java: .class file decompiler plugin integrated

This commit is contained in:
Roman Shevchenko
2014-06-20 19:01:22 +02:00
parent 321dc15447
commit a206a6f053
33 changed files with 753 additions and 1 deletions

1
.idea/modules.xml generated
View File

@@ -82,6 +82,7 @@
<module fileurl="file://$PROJECT_DIR$/plugins/IntelliLang/intellilang-jps-plugin/intellilang-jps-plugin.iml" filepath="$PROJECT_DIR$/plugins/IntelliLang/intellilang-jps-plugin/intellilang-jps-plugin.iml" group="jps" />
<module fileurl="file://$PROJECT_DIR$/java/java-analysis-api/java-analysis-api.iml" filepath="$PROJECT_DIR$/java/java-analysis-api/java-analysis-api.iml" group="java" />
<module fileurl="file://$PROJECT_DIR$/java/java-analysis-impl/java-analysis-impl.iml" filepath="$PROJECT_DIR$/java/java-analysis-impl/java-analysis-impl.iml" group="java" />
<module fileurl="file://$PROJECT_DIR$/plugins/java-decompiler/java-decompiler.iml" filepath="$PROJECT_DIR$/plugins/java-decompiler/java-decompiler.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/plugins/java-i18n/java-i18n.iml" filepath="$PROJECT_DIR$/plugins/java-i18n/java-i18n.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/java/java-impl/java-impl.iml" filepath="$PROJECT_DIR$/java/java-impl/java-impl.iml" group="java" />
<module fileurl="file://$PROJECT_DIR$/java/java-indexing-api/java-indexing-api.iml" filepath="$PROJECT_DIR$/java/java-indexing-api/java-indexing-api.iml" group="java" />

View File

@@ -545,6 +545,15 @@ public def layoutCommunityPlugins(String home) {
}
}
}
pluginDir("java-decompiler") {
dir("lib") {
jar("java-decompiler.jar") {
module("java-decompiler")
}
fileset(file: "$home/plugins/java-decompiler/lib/fernflower.jar")
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 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.
@@ -281,3 +281,4 @@ jetbrainsLibrary("JPS")
jetbrainsLibrary("Maven Embedder")
jetbrainsLibrary("tcServiceMessages")
jetbrainsLibrary("optimizedFileManager.jar")
jetbrainsLibrary("fernflower.jar")

View File

@@ -100,6 +100,7 @@
<orderEntry type="module" module-name="java-tests" scope="TEST" />
<orderEntry type="module" module-name="community-tests" scope="TEST" />
<orderEntry type="module" module-name="jira" />
<orderEntry type="module" module-name="java-decompiler" />
</component>
</module>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="lang-api" />
<orderEntry type="module" module-name="java-psi-api" />
<orderEntry type="module" module-name="java-psi-impl" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="testFramework-java" scope="TEST" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/lib/fernflower.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>

Binary file not shown.

View File

@@ -0,0 +1,20 @@
<idea-plugin version="2">
<id>org.jetbrains.java.decompiler</id>
<name>Java Bytecode Decompiler</name>
<description>
The plugin extends standard IDEA .class file viewer with powerful Fernflower decompiler -
no more dull "{ /* compiled code */ }" in method bodies!
</description>
<version>0.1</version>
<vendor email="support@jetbrains.com" url="http://www.jetbrains.com">JetBrains</vendor>
<idea-version since-build="135"/>
<module value="com.intellij.modules.java"/>
<extensions defaultExtensionNs="com.intellij">
<psi.classFileDecompiler implementation="org.jetbrains.java.decompiler.IdeaDecompiler" order="last"/>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,159 @@
/*
* 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.
* 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.jetbrains.java.decompiler;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.DefaultProjectFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.compiled.ClassFileDecompilers;
import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.util.containers.ContainerUtil;
import de.fernflower.main.decompiler.IdeDecompiler;
import de.fernflower.main.extern.IBytecodeProvider;
import de.fernflower.main.extern.IDecompilatSaver;
import de.fernflower.main.extern.IFernflowerLogger;
import de.fernflower.main.extern.IFernflowerPreferences;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.Manifest;
public class IdeaDecompiler extends ClassFileDecompilers.Light {
private static final String BANNER =
"//\n" +
"// Source code recreated from a .class file by IntelliJ IDEA\n" +
"// (powered by Fernflower decompiler)\n" +
"//\n\n";
private final IFernflowerLogger myLogger = new IdeaLogger();
private final HashMap<String, Object> myOptions = new HashMap<String, Object>();
public IdeaDecompiler() {
myOptions.put(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR, "0");
myOptions.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1");
myOptions.put(IFernflowerPreferences.REMOVE_SYNTHETIC, "1");
myOptions.put(IFernflowerPreferences.REMOVE_BRIDGE, "1");
myOptions.put(IFernflowerPreferences.LITERALS_AS_IS, "1");
myOptions.put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "1");
Project project = DefaultProjectFactory.getInstance().getDefaultProject();
CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(project).getCurrentSettings();
CommonCodeStyleSettings.IndentOptions options = settings.getIndentOptions(JavaFileType.INSTANCE);
myOptions.put(IFernflowerPreferences.INDENT_STRING, StringUtil.repeat(" ", options.INDENT_SIZE));
}
@Override
public boolean accepts(@NotNull VirtualFile file) {
return true;
}
@NotNull
@Override
public CharSequence getText(@NotNull VirtualFile file) {
if ("package-info.class".equals(file.getName())) {
return ClsFileImpl.decompile(file); //fixme
}
try {
Map<String, VirtualFile> files = ContainerUtil.newLinkedHashMap();
files.put(file.getPath(), file);
String mask = file.getNameWithoutExtension() + "$";
for (VirtualFile child : file.getParent().getChildren()) {
if (child.getNameWithoutExtension().startsWith(mask) && file.getFileType() == StdFileTypes.CLASS) {
files.put(child.getPath(), child);
}
}
MyByteCodeProvider provider = new MyByteCodeProvider(files);
MyResultSaver saver = new MyResultSaver();
IdeDecompiler decompiler = new IdeDecompiler(provider, saver, myLogger, myOptions);
for (String path : files.keySet()) {
decompiler.addSpace(new File(path), true);
}
decompiler.decompileContext();
return BANNER + saver.myResult;
}
catch (Exception e) {
Logger.getInstance(IdeaDecompiler.class).error(file.getPath(), e);
return ClsFileImpl.decompile(file);
}
}
private static class MyByteCodeProvider implements IBytecodeProvider {
private final Map<String, VirtualFile> myFiles;
private MyByteCodeProvider(@NotNull Map<String, VirtualFile> files) {
myFiles = files;
}
@Override
public InputStream getBytecodeStream(String externalPath, String internalPath) {
try {
return myFiles.get(externalPath).getInputStream();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static class MyResultSaver implements IDecompilatSaver {
private String myResult = "";
@Override
public void saveClassFile(String path, String qualifiedName, String entryName, String content) {
if (myResult.isEmpty()) {
myResult = content;
}
}
@Override
public void copyFile(String source, String destPath, String destFileName) { }
@Override
public void saveFolder(String path) { }
@Override
public void saveFile(String path, String filename, String content) { }
@Override
public void createArchive(String path, String archiveName, Manifest manifest) { }
@Override
public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { }
@Override
public void saveEntry(String path, String archiveName, String entryName, String content) { }
@Override
public void copyEntry(String source, String destPath, String archiveName, String entry) { }
@Override
public void closeArchive(String path, String archiveName) { }
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.
* 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.jetbrains.java.decompiler;
import com.intellij.openapi.diagnostic.Logger;
import de.fernflower.main.extern.IFernflowerLogger;
public class IdeaLogger implements IFernflowerLogger {
private final static Logger LOG = Logger.getInstance(IdeaDecompiler.class);
public static class InternalException extends RuntimeException {
public InternalException(String message, Throwable cause) {
super(message, cause);
}
}
@Override
public void writeMessage(String message, int severity) {
if (severity >= ERROR) LOG.error(message);
else if (severity == WARNING) LOG.warn(message);
else if (severity == INFO) LOG.info(message);
else LOG.debug(message);
}
@Override
public void writeMessage(String message, Throwable t) {
if (t instanceof InternalException) throw (InternalException)t;
else throw new InternalException(message, t);
}
@Override
public void startClass(String className) {
LOG.debug("processing class " + className);
}
@Override
public void endClass() {
LOG.debug("... class processed");
}
@Override
public void startWriteClass(String className) {
LOG.debug("writing class " + className);
}
@Override
public void endWriteClass() {
LOG.debug("... class written");
}
@Override
public void startMethod(String method) {
LOG.debug("processing method " + method);
}
@Override
public void endMethod() {
LOG.debug("... method processed");
}
@Override
public int getSeverity() {
return WARNING;
}
@Override
public void setSeverity(int ignore) { }
}

View File

@@ -0,0 +1,110 @@
/*
* 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.
* 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.jetbrains.java.decompiler;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
public class IdeaDecompilerTest extends LightCodeInsightFixtureTestCase {
public void testSimple() {
String path = getRtJarPath() + "!/java/lang/String.class";
VirtualFile file = StandardFileSystems.jar().findFileByPath(path);
assertNotNull(path, file);
CharSequence text = new IdeaDecompiler().getText(file);
assertNotNull(text);
String decompiled = text.toString();
assertTrue(decompiled, decompiled.contains("public final class String"));
assertTrue(decompiled, decompiled.contains("@deprecated"));
assertTrue(decompiled, decompiled.contains("private static class CaseInsensitiveComparator"));
assertFalse(decompiled, decompiled.contains("{ /* compiled code */ }"));
assertFalse(decompiled, decompiled.contains("synthetic"));
}
public void testEnum() { doTestDecompiler(); }
public void testDeprecations() { doTestDecompiler(); }
public void testExtendsList() { doTestDecompiler(); }
public void testParameters() { doTestDecompiler(); }
public void testConstants() { doTestDecompiler(); }
private void doTestDecompiler() {
String name = PluginPathManager.getPluginHomePath("java-decompiler") + "/testData/" + getName().substring(4);
String path = name + ".class";
VirtualFile file = StandardFileSystems.local().findFileByPath(path);
assertNotNull(path, file);
file.getParent().getChildren();
file.getParent().refresh(false, true);
try {
CharSequence text = new IdeaDecompiler().getText(file);
assertNotNull(text);
String expected = FileUtil.loadFile(new File(name + ".txt"));
assertEquals(expected, text.toString());
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
public void testStubCompatibilityRt() {
String path = getRtJarPath() + "!/";
VirtualFile dir = StandardFileSystems.jar().findFileByPath(path);
assertNotNull(path, dir);
doTestStubCompatibility(dir);
}
public void testStubCompatibilityIdea() {
String path = PathManager.getHomePath() + "/out/classes/production";
VirtualFile dir = StandardFileSystems.local().findFileByPath(path);
assertNotNull(path, dir);
doTestStubCompatibility(dir);
}
private void doTestStubCompatibility(VirtualFile root) {
VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
if (!file.isDirectory() && file.getFileType() == StdFileTypes.CLASS && !file.getName().contains("$")) {
PsiFile clsFile = getPsiManager().findFile(file);
assertNotNull(file.getPath(), clsFile);
String decompiled = ((ClsFileImpl)clsFile).getMirror().getText();
assertTrue(file.getPath(), decompiled.contains(file.getNameWithoutExtension()));
}
return true;
}
});
}
private static String getRtJarPath() {
String home = System.getProperty("java.home");
return SystemInfo.isAppleJvm ? FileUtil.toCanonicalPath(home + "/../Classes/classes.jar") : home + "/lib/rt.jar";
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,47 @@
public class Constants {
static final boolean T = true;
static final boolean F = false;
static final char C0 = '\n';
static final char C1 = 'a';
static final char C2 = 512;
static final byte BMin = Byte.MIN_VALUE;
static final byte BMax = Byte.MAX_VALUE;
static final short SMin = Short.MIN_VALUE;
static final short SMax = Short.MAX_VALUE;
static final int IMin = Integer.MIN_VALUE;
static final int IMax = Integer.MAX_VALUE;
static final long LMin = Long.MIN_VALUE;
static final long LMax = Long.MAX_VALUE;
static final float FNan = Float.NaN;
static final float FNeg = Float.NEGATIVE_INFINITY;
static final float FPos = Float.POSITIVE_INFINITY;
static final float FMin = Float.MIN_VALUE;
static final float FMax = Float.MAX_VALUE;
static final double DNan = Double.NaN;
static final double DNeg = Double.NEGATIVE_INFINITY;
static final double DPos = Double.POSITIVE_INFINITY;
static final double DMin = Double.MIN_VALUE;
static final double DMax = Double.MAX_VALUE;
static @interface A {
Class<?> value();
}
@A(byte.class) void m1() { }
@A(char.class) void m2() { }
@A(double.class) void m3() { }
@A(float.class) void m4() { }
@A(int.class) void m5() { }
@A(long.class) void m6() { }
@A(short.class) void m7() { }
@A(boolean.class) void m8() { }
@A(void.class) void m9() { }
@A(java.util.Date.class) void m10() { }
}

View File

@@ -0,0 +1,82 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.util.Date;
public class Constants {
static final boolean T = true;
static final boolean F = false;
static final char C0 = '\n';
static final char C1 = 'a';
static final char C2 = 'Ȁ';
static final byte BMin = -128;
static final byte BMax = 127;
static final short SMin = -32768;
static final short SMax = 32767;
static final int IMin = -2147483648;
static final int IMax = 2147483647;
static final long LMin = -9223372036854775808L;
static final long LMax = 9223372036854775807L;
static final float FNan = 0.0F / 0.0;
static final float FNeg = -1.0F / 0.0;
static final float FPos = 1.0F / 0.0;
static final float FMin = 1.4E-45F;
static final float FMax = 3.4028235E38F;
static final double DNan = 0.0D / 0.0;
static final double DNeg = -1.0D / 0.0;
static final double DPos = 1.0D / 0.0;
static final double DMin = 4.9E-324D;
static final double DMax = 1.7976931348623157E308D;
public Constants() {
}
@Constants(byte.class)
void m1() {
}
@Constants(char.class)
void m2() {
}
@Constants(double.class)
void m3() {
}
@Constants(float.class)
void m4() {
}
@Constants(int.class)
void m5() {
}
@Constants(long.class)
void m6() {
}
@Constants(short.class)
void m7() {
}
@Constants(boolean.class)
void m8() {
}
@Constants(void.class)
void m9() {
}
@Constants(Date.class)
void m10() {
}
@interface A {
Class<?> value();
}
}

Binary file not shown.

View File

@@ -0,0 +1,19 @@
public class Deprecations {
/** @deprecated */
public int byComment;
@Deprecated
public int byAnno;
/** @deprecated */
public void byComment() { }
@Deprecated
public void byAnno() { }
/** @deprecated */
public static class ByComment { }
@Deprecated
public static class ByAnno { }
}

View File

@@ -0,0 +1,42 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
public class Deprecations {
/** @deprecated */
public int byComment;
/** @deprecated */
@Deprecated
public int byAnno;
public Deprecations() {
}
/** @deprecated */
public void byComment() {
}
/** @deprecated */
@Deprecated
public void byAnno() {
}
/** @deprecated */
@Deprecated
public static class ByAnno {
public ByAnno() {
}
}
/** @deprecated */
public static class ByComment {
public ByComment() {
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,19 @@
public enum Enum {
E1,
E2() {
@Override
public void m() { }
},
E3("-"),
E4("+") {
@Override
public void m() { }
};
public void m() { }
private String s;
private Enum() { this("?"); }
private Enum(@Deprecated String s) { this.s = s; }
}

View File

@@ -0,0 +1,33 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
public enum Enum {
E1,
E2 {
public void m() {
}
},
E3("-"),
E4("+") {
public void m() {
}
};
private String s;
public void m() {
}
private Enum() {
this((String)"?");
}
private Enum(@Deprecated String var3) {
this.s = var3;
}
}

Binary file not shown.

View File

@@ -0,0 +1,9 @@
public class ExtendsList {
static <T extends Comparable<? super T>> T m1(T t) {
return null;
}
static <T extends Object & Comparable<? super T>> T m2(T t) {
return null;
}
}

View File

@@ -0,0 +1,19 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
public class ExtendsList {
public ExtendsList() {
}
static <T extends Comparable<? super T>> T m1(T var0) {
return null;
}
static <T extends Object & Comparable<? super T>> T m2(T var0) {
return null;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,23 @@
public class Parameters {
Parameters(@Deprecated int p01) { }
void m1(@Deprecated int p02) { }
static void m2(@Deprecated int p03) { }
class C1 {
C1(@Deprecated int p11) { }
void m(@Deprecated int p12) { }
}
static class C2 {
C2(@Deprecated int p21) { }
void m1(@Deprecated int p22) { }
static void m2(@Deprecated int p23) { }
}
void local() {
class Local {
Local(@Deprecated int p31) { }
void m(@Deprecated int p32) { }
}
}
}

View File

@@ -0,0 +1,50 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
public class Parameters {
Parameters(@Deprecated int var1) {
}
void m1(@Deprecated int var1) {
}
static void m2(@Deprecated int var0) {
}
void local() {
class Local {
Local(@Deprecated int var2) {
}
void m(@Deprecated int var1) {
}
}
}
static class C2 {
C2(@Deprecated int var1) {
}
void m1(@Deprecated int var1) {
}
static void m2(@Deprecated int var0) {
}
}
class C1 {
C1(@Deprecated int var2) {
}
void m(@Deprecated int var1) {
}
}
}