IDEA-127182 (passing line number map to debugger)
authorRoman Shevchenko <roman.shevchenko@jetbrains.com>
Fri, 17 Oct 2014 19:35:16 +0000 (21:35 +0200)
committerRoman Shevchenko <roman.shevchenko@jetbrains.com>
Fri, 17 Oct 2014 19:35:16 +0000 (21:35 +0200)
java/debugger/openapi/src/com/intellij/debugger/PositionManager.java
platform/util/resources/misc/registry.properties
plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java
plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java
plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/ContextUnit.java
plugins/java-decompiler/plugin/java-decompiler-plugin.iml
plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.java
plugins/java-decompiler/plugin/test/org/jetbrains/java/decompiler/IdeaDecompilerTest.java
plugins/java-decompiler/plugin/testData/LineNumbers.class [new file with mode: 0644]
plugins/java-decompiler/plugin/testData/LineNumbers.java [new file with mode: 0644]

index 8b5c3d768c58b49031363b03d6dcbc89b30155e5..11382bce570109954c82b4ab13a9732b323e2a9e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 package com.intellij.debugger;
 
 import com.intellij.debugger.requests.ClassPrepareRequestor;
+import com.intellij.openapi.util.Key;
 import com.sun.jdi.Location;
 import com.sun.jdi.ReferenceType;
 import com.sun.jdi.request.ClassPrepareRequest;
@@ -31,6 +32,12 @@ import java.util.List;
  * @see com.intellij.debugger.engine.JSR45PositionManager
  */
 public interface PositionManager {
+  /**
+   * A mapping between lines contained in a byte code and actual source lines
+   * (placed into a user data of a VirtualFile for a .class file).
+   */
+  Key<int[]> LINE_NUMBERS_MAPPING_KEY = Key.create("line.numbers.mapping.key");
+
   /**
    * Returns the source position corresponding to the specified bytecode location.
    *
index 57502412093f45d48ed5913c54ec7e1a779e08c7..85b24fda75135ba02cf5140a42e9ce52cd1f9680 100644 (file)
@@ -453,7 +453,11 @@ new.css.schema.enabled=true
 editor.disable.rtl=false
 editor.disable.rtl.description=Disables RTL support in editor (which is broken now anyway)
 
+decompiler.use.line.mapping=false
+decompiler.use.line.mapping.description=Maps original to decompiled line numbers when stepping in debugger.
 decompiler.use.line.table=false
+decompiler.use.line.table.description=Rearranges decompiled text to match original line numbers
+
 ide.transparency.mode.for.windows=false
 ide.transparency.mode.for.windows.description=Allow to add transparency to floating windows
 ide.new.welcome.screen=false
index a486d9f83ee130a05cff6dff1b5425cfca45d3d1..08ec6835a878f47d03fd9e28e396e666ce3b36b8 100644 (file)
@@ -181,7 +181,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
   }
 
   @Override
-  public void saveClassFile(String path, String qualifiedName, String entryName, String content) {
+  public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
     File file = new File(getAbsolutePath(path), entryName);
     try {
       Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF8");
index 1e6a7d4262a5f324128cef9e044127f8842f629d..269f1c57d4ca87119f28e6c377666ba50fcf2266 100644 (file)
@@ -22,7 +22,7 @@ public interface IResultSaver {
 
   void copyFile(String source, String path, String entryName);
 
-  void saveClassFile(String path, String qualifiedName, String entryName, String content);
+  void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping);
 
   void createArchive(String path, String archiveName, Manifest manifest);
 
index 819ae6446d074890435971fb1207d58d3c877e25..850c3d84888d95343abd8a3c297baac58783a8c3 100644 (file)
@@ -15,6 +15,8 @@
  */
 package org.jetbrains.java.decompiler.struct;
 
+import org.jetbrains.java.decompiler.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
 import org.jetbrains.java.decompiler.main.extern.IResultSaver;
 import org.jetbrains.java.decompiler.struct.lazy.LazyLoader;
 import org.jetbrains.java.decompiler.struct.lazy.LazyLoader.Link;
@@ -112,7 +114,11 @@ public class ContextUnit {
           if (entryName != null) {
             String content = decompiledData.getClassContent(cl);
             if (content != null) {
-              resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content);
+              int[] mapping = null;
+              if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {
+                mapping = DecompilerContext.getBytecodeSourceMapper().getOriginalLinesMapping();
+              }
+              resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content, mapping);
             }
           }
         }
index 6b5b607979f4f353d80395d6b28c8ca318767260..352da6d54947f1b85d446c3d754b1776660b13b1 100644 (file)
@@ -14,6 +14,7 @@
     <orderEntry type="module" module-name="java-psi-impl" />
     <orderEntry type="module" module-name="util" />
     <orderEntry type="module" module-name="platform-impl" />
+    <orderEntry type="module" module-name="debugger-openapi" />
     <orderEntry type="module" module-name="testFramework-java" scope="TEST" />
   </component>
 </module>
\ No newline at end of file
index d08ea993f01283f73d0a08788746581bc8132ddb..58bdcb832fe2eccd7ffd9525da7c28a301982ca2 100644 (file)
@@ -15,6 +15,7 @@
  */
 package org.jetbrains.java.decompiler;
 
+import com.intellij.debugger.PositionManager;
 import com.intellij.icons.AllIcons;
 import com.intellij.ide.highlighter.JavaFileType;
 import com.intellij.ide.plugins.PluginManagerCore;
@@ -146,13 +147,27 @@ public class IdeaDecompiler extends ClassFileDecompilers.Light {
       MyBytecodeProvider provider = new MyBytecodeProvider(files);
       MyResultSaver saver = new MyResultSaver();
 
-      myOptions.put(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS, Registry.is("decompiler.use.line.table") ? "1" : "0");
+      if (Registry.is("decompiler.use.line.mapping")) {
+        myOptions.put(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1");
+        myOptions.put(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS, "0");
+      }
+      else if (Registry.is("decompiler.use.line.table")) {
+        myOptions.put(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "0");
+        myOptions.put(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS, "1");
+      }
+      else {
+        myOptions.put(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "0");
+        myOptions.put(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS, "0");
+      }
+
       BaseDecompiler decompiler = new BaseDecompiler(provider, saver, myOptions, myLogger);
       for (String path : files.keySet()) {
         decompiler.addSpace(new File(path), true);
       }
       decompiler.decompileContext();
 
+      file.putUserData(PositionManager.LINE_NUMBERS_MAPPING_KEY, saver.myMapping);
+
       return saver.myResult;
     }
     catch (Exception e) {
@@ -190,11 +205,13 @@ public class IdeaDecompiler extends ClassFileDecompilers.Light {
 
   private static class MyResultSaver implements IResultSaver {
     private String myResult = "";
+    private int[] myMapping = null;
 
     @Override
-    public void saveClassFile(String path, String qualifiedName, String entryName, String content) {
+    public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
       if (myResult.isEmpty()) {
         myResult = content;
+        myMapping = mapping;
       }
     }
 
index c8e275cdfb22aeb3c8e9ca59c140f1cd9bde5822..c7e25f4faae4fe00034106455433e3b98cf0e9c6 100644 (file)
@@ -17,9 +17,12 @@ package org.jetbrains.java.decompiler;
 
 import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPassFactory;
 import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
+import com.intellij.debugger.PositionManager;
 import com.intellij.openapi.application.PluginPathManager;
 import com.intellij.openapi.fileTypes.StdFileTypes;
 import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.util.registry.RegistryValue;
 import com.intellij.openapi.vfs.StandardFileSystems;
 import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -119,7 +122,7 @@ public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase {
 
   private static VirtualFile getTestFile(String name) {
     String path = PluginPathManager.getPluginHomePath("java-decompiler") + "/plugin/testData/" + name;
-    VirtualFile file = StandardFileSystems.local().findFileByPath(path);
+    VirtualFile file = StandardFileSystems.local().refreshAndFindFileByPath(path);
     assertNotNull(path, file);
     return file;
   }
@@ -135,4 +138,24 @@ public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase {
   private int offset(int line, int column) {
     return myFixture.getEditor().getDocument().getLineStartOffset(line - 1) + column - 1;
   }
+
+  public void testLineNumberMapping() {
+    RegistryValue value = Registry.get("decompiler.use.line.mapping");
+    boolean old = value.asBoolean();
+    try {
+      value.setValue(true);
+
+      VirtualFile file = getTestFile("LineNumbers.class");
+      assertNull(file.getUserData(PositionManager.LINE_NUMBERS_MAPPING_KEY));
+
+      new IdeaDecompiler().getText(file);
+
+      int[] mapping = file.getUserData(PositionManager.LINE_NUMBERS_MAPPING_KEY);
+      assertNotNull(mapping);
+      assertEquals(20, mapping.length);
+    }
+    finally {
+      value.setValue(old);
+    }
+  }
 }
diff --git a/plugins/java-decompiler/plugin/testData/LineNumbers.class b/plugins/java-decompiler/plugin/testData/LineNumbers.class
new file mode 100644 (file)
index 0000000..b83654d
Binary files /dev/null and b/plugins/java-decompiler/plugin/testData/LineNumbers.class differ
diff --git a/plugins/java-decompiler/plugin/testData/LineNumbers.java b/plugins/java-decompiler/plugin/testData/LineNumbers.java
new file mode 100644 (file)
index 0000000..c2143bc
--- /dev/null
@@ -0,0 +1,16 @@
+class LineNumbers {
+  void m(double a, double b, double c) {
+    double d = b*b - 4*a*c;
+    if (d < 0) {
+      System.out.println("No roots.");
+    } else if (d == 0) {
+      double x = -b / 2*a;
+      System.out.println("x=" + x);
+    } else {
+      d = Math.sqrt(d);
+      double x1 = -b - d / 2*a;
+      double x2 = -b + d / 2*a;
+      System.out.println("x1=" + x1 + " x2=" + x2);
+    }
+  }
+}