add "Decompile" button to Kotlin Bytecode toolwindow
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="java-decompiler-plugin">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/ideaSDK/plugins/java-decompiler/lib/java-decompiler.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/ideaSDK/sources/sources.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/ideaSDK/sources/sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
|
</component>
|
||||||
@@ -25,6 +25,7 @@ New features:
|
|||||||
- [KT-11704](https://youtrack.jetbrains.com/issue/KT-11704) Support file path references inside of Kotlin string literals
|
- [KT-11704](https://youtrack.jetbrains.com/issue/KT-11704) Support file path references inside of Kotlin string literals
|
||||||
- [KT-12092](https://youtrack.jetbrains.com/issue/KT-12092) Implement bean references in @Qualifier annotations
|
- [KT-12092](https://youtrack.jetbrains.com/issue/KT-12092) Implement bean references in @Qualifier annotations
|
||||||
- [KT-12175](https://youtrack.jetbrains.com/issue/KT-12175) Don't enforce empty line after one-line constructor
|
- [KT-12175](https://youtrack.jetbrains.com/issue/KT-12175) Don't enforce empty line after one-line constructor
|
||||||
|
- Added "Decompile" button to Kotlin bytecode toolwindow
|
||||||
|
|
||||||
Issues fixed:
|
Issues fixed:
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
<orderEntry type="module" module-name="tests-common" scope="TEST" />
|
<orderEntry type="module" module-name="tests-common" scope="TEST" />
|
||||||
<orderEntry type="library" scope="TEST" name="kotlin-test" level="project" />
|
<orderEntry type="library" scope="TEST" name="kotlin-test" level="project" />
|
||||||
<orderEntry type="module" module-name="lint-idea" scope="PROVIDED" />
|
<orderEntry type="module" module-name="lint-idea" scope="PROVIDED" />
|
||||||
|
<orderEntry type="library" name="java-decompiler-plugin" level="project" />
|
||||||
<orderEntry type="module" module-name="formatter" />
|
<orderEntry type="module" module-name="formatter" />
|
||||||
<orderEntry type="module" module-name="idea-maven" />
|
<orderEntry type="module" module-name="idea-maven" />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<idea-plugin>
|
||||||
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
|
<applicationService serviceInterface="org.jetbrains.kotlin.idea.internal.KotlinDecompilerService"
|
||||||
|
serviceImplementation="org.jetbrains.kotlin.idea.internal.KotlinDecompilerServiceImpl"/>
|
||||||
|
<writingAccessProvider implementation="org.jetbrains.kotlin.idea.internal.DecompiledFileWritingAccessProvider"/>
|
||||||
|
</extensions>
|
||||||
|
</idea-plugin>
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
<depends optional="true" config-file="coverage.xml">Coverage</depends>
|
<depends optional="true" config-file="coverage.xml">Coverage</depends>
|
||||||
<depends optional="true" config-file="i18n.xml">com.intellij.java-i18n</depends>
|
<depends optional="true" config-file="i18n.xml">com.intellij.java-i18n</depends>
|
||||||
<depends optional="true" config-file="injection.xml">org.intellij.intelliLang</depends>
|
<depends optional="true" config-file="injection.xml">org.intellij.intelliLang</depends>
|
||||||
|
<depends optional="true" config-file="decompiler.xml">org.jetbrains.java.decompiler</depends>
|
||||||
|
|
||||||
<!-- ULTIMATE-PLUGIN-PLACEHOLDER -->
|
<!-- ULTIMATE-PLUGIN-PLACEHOLDER -->
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.internal;
|
|||||||
import com.intellij.ide.highlighter.JavaFileType;
|
import com.intellij.ide.highlighter.JavaFileType;
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Document;
|
import com.intellij.openapi.editor.Document;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.editor.EditorFactory;
|
import com.intellij.openapi.editor.EditorFactory;
|
||||||
@@ -26,12 +27,14 @@ import com.intellij.openapi.editor.ScrollType;
|
|||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.ui.Messages;
|
||||||
import com.intellij.openapi.util.Computable;
|
import com.intellij.openapi.util.Computable;
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
import com.intellij.openapi.wm.ToolWindow;
|
import com.intellij.openapi.wm.ToolWindow;
|
||||||
import com.intellij.util.Alarm;
|
import com.intellij.util.Alarm;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.java.decompiler.IdeaLogger;
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
|
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories;
|
import org.jetbrains.kotlin.codegen.ClassBuilderFactories;
|
||||||
@@ -54,12 +57,16 @@ import org.jetbrains.kotlin.utils.StringsKt;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
|
public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
|
||||||
|
private final Logger LOG = Logger.getInstance(KotlinBytecodeToolWindow.class);
|
||||||
|
|
||||||
private static final int UPDATE_DELAY = 1000;
|
private static final int UPDATE_DELAY = 1000;
|
||||||
private static final String DEFAULT_TEXT = "/*\n" +
|
private static final String DEFAULT_TEXT = "/*\n" +
|
||||||
"Generated bytecode for Kotlin source file.\n" +
|
"Generated bytecode for Kotlin source file.\n" +
|
||||||
@@ -152,8 +159,9 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
|
|||||||
private final JCheckBox enableInline;
|
private final JCheckBox enableInline;
|
||||||
private final JCheckBox enableOptimization;
|
private final JCheckBox enableOptimization;
|
||||||
private final JCheckBox enableAssertions;
|
private final JCheckBox enableAssertions;
|
||||||
|
private final JButton decompile;
|
||||||
|
|
||||||
public KotlinBytecodeToolWindow(Project project, ToolWindow toolWindow) {
|
public KotlinBytecodeToolWindow(final Project project, ToolWindow toolWindow) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
myProject = project;
|
myProject = project;
|
||||||
this.toolWindow = toolWindow;
|
this.toolWindow = toolWindow;
|
||||||
@@ -165,10 +173,31 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
|
|||||||
JPanel optionPanel = new JPanel(new FlowLayout());
|
JPanel optionPanel = new JPanel(new FlowLayout());
|
||||||
add(optionPanel, BorderLayout.NORTH);
|
add(optionPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
decompile = new JButton("Decompile");
|
||||||
|
if (KotlinDecompilerService.Companion.getInstance() != null) {
|
||||||
|
optionPanel.add(decompile);
|
||||||
|
decompile.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
Location location = Location.fromEditor(FileEditorManager.getInstance(myProject).getSelectedTextEditor(), myProject);
|
||||||
|
KtFile file = location.getKFile();
|
||||||
|
if (file != null) {
|
||||||
|
try {
|
||||||
|
KotlinDecompilerAdapterKt.showDecompiledCode(file);
|
||||||
|
}
|
||||||
|
catch (IdeaLogger.InternalException ex) {
|
||||||
|
LOG.info(ex);
|
||||||
|
Messages.showErrorDialog(myProject, "Failed to decompile " + file.getName() + ": " + ex, "Kotlin Bytecode Decompiler");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*TODO: try to extract default parameter from compiler options*/
|
/*TODO: try to extract default parameter from compiler options*/
|
||||||
enableInline = new JCheckBox("Enable inline", true);
|
enableInline = new JCheckBox("Inline", true);
|
||||||
enableOptimization = new JCheckBox("Enable optimization", true);
|
enableOptimization = new JCheckBox("Optimization", true);
|
||||||
enableAssertions = new JCheckBox("Enable assertions", true);
|
enableAssertions = new JCheckBox("Assertions", true);
|
||||||
optionPanel.add(enableInline);
|
optionPanel.add(enableInline);
|
||||||
optionPanel.add(enableOptimization);
|
optionPanel.add(enableOptimization);
|
||||||
optionPanel.add(enableAssertions);
|
optionPanel.add(enableAssertions);
|
||||||
@@ -193,50 +222,7 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
|
|||||||
) {
|
) {
|
||||||
GenerationState state;
|
GenerationState state;
|
||||||
try {
|
try {
|
||||||
ResolutionFacade resolutionFacade = ResolutionUtils.getResolutionFacade(jetFile);
|
state = compileSingleFile(jetFile, enableInline, enableAssertions, enableOptimization);
|
||||||
|
|
||||||
BindingContext bindingContextForFile = resolutionFacade.analyzeFullyAndGetResult(Collections.singletonList(jetFile)).getBindingContext();
|
|
||||||
|
|
||||||
kotlin.Pair<BindingContext, List<KtFile>> result = DebuggerUtils.INSTANCE.analyzeInlinedFunctions(
|
|
||||||
resolutionFacade, bindingContextForFile, jetFile, !enableInline
|
|
||||||
);
|
|
||||||
|
|
||||||
BindingContext bindingContext = result.getFirst();
|
|
||||||
List<KtFile> toProcess = result.getSecond();
|
|
||||||
|
|
||||||
GenerationState.GenerateClassFilter generateClassFilter = new GenerationState.GenerateClassFilter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGeneratePackagePart(@NotNull KtFile file) {
|
|
||||||
return file == jetFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldAnnotateClass(@NotNull KtClassOrObject processingClassOrObject) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateClass(@NotNull KtClassOrObject processingClassOrObject) {
|
|
||||||
return processingClassOrObject.getContainingKtFile() == jetFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateScript(@NotNull KtScript script) {
|
|
||||||
return script.getContainingKtFile() == jetFile;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
state = new GenerationState(jetFile.getProject(), ClassBuilderFactories.TEST,
|
|
||||||
resolutionFacade.getModuleDescriptor(), bindingContext,
|
|
||||||
toProcess,
|
|
||||||
!enableAssertions,
|
|
||||||
!enableAssertions,
|
|
||||||
generateClassFilter,
|
|
||||||
!enableInline,
|
|
||||||
!enableOptimization,
|
|
||||||
/*useTypeTableInSerializer=*/false);
|
|
||||||
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
|
|
||||||
}
|
}
|
||||||
catch (ProcessCanceledException e) {
|
catch (ProcessCanceledException e) {
|
||||||
throw e;
|
throw e;
|
||||||
@@ -273,6 +259,60 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
|
|||||||
return answer.toString();
|
return answer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static GenerationState compileSingleFile(
|
||||||
|
final KtFile ktFile,
|
||||||
|
boolean enableInline,
|
||||||
|
boolean enableAssertions,
|
||||||
|
boolean enableOptimization
|
||||||
|
) {
|
||||||
|
ResolutionFacade resolutionFacade = ResolutionUtils.getResolutionFacade(ktFile);
|
||||||
|
|
||||||
|
BindingContext bindingContextForFile = resolutionFacade.analyzeFullyAndGetResult(Collections.singletonList(ktFile)).getBindingContext();
|
||||||
|
|
||||||
|
kotlin.Pair<BindingContext, List<KtFile>> result = DebuggerUtils.INSTANCE.analyzeInlinedFunctions(
|
||||||
|
resolutionFacade, bindingContextForFile, ktFile, !enableInline
|
||||||
|
);
|
||||||
|
|
||||||
|
BindingContext bindingContext = result.getFirst();
|
||||||
|
List<KtFile> toProcess = result.getSecond();
|
||||||
|
|
||||||
|
GenerationState.GenerateClassFilter generateClassFilter = new GenerationState.GenerateClassFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldGeneratePackagePart(@NotNull KtFile file) {
|
||||||
|
return file == ktFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldAnnotateClass(@NotNull KtClassOrObject processingClassOrObject) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldGenerateClass(@NotNull KtClassOrObject processingClassOrObject) {
|
||||||
|
return processingClassOrObject.getContainingKtFile() == ktFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldGenerateScript(@NotNull KtScript script) {
|
||||||
|
return script.getContainingKtFile() == ktFile;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GenerationState state = new GenerationState(ktFile.getProject(), ClassBuilderFactories.TEST,
|
||||||
|
resolutionFacade.getModuleDescriptor(), bindingContext,
|
||||||
|
toProcess,
|
||||||
|
!enableAssertions,
|
||||||
|
!enableAssertions,
|
||||||
|
generateClassFilter,
|
||||||
|
!enableInline,
|
||||||
|
!enableOptimization,
|
||||||
|
/*useTypeTableInSerializer=*/false);
|
||||||
|
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
private static Pair<Integer, Integer> mapLines(String text, int startLine, int endLine) {
|
private static Pair<Integer, Integer> mapLines(String text, int startLine, int endLine) {
|
||||||
int byteCodeLine = 0;
|
int byteCodeLine = 0;
|
||||||
int byteCodeStartLine = -1;
|
int byteCodeStartLine = -1;
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.kotlin.idea.internal
|
||||||
|
|
||||||
|
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.vfs.VfsUtil
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileManager
|
||||||
|
import com.intellij.openapi.vfs.WritingAccessProvider
|
||||||
|
import com.intellij.openapi.vfs.ex.dummy.DummyFileSystem
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
|
fun showDecompiledCode(sourceFile: KtFile) {
|
||||||
|
val decompilerService = KotlinDecompilerService.getInstance() ?: return
|
||||||
|
val decompiledCode = decompilerService.decompile(sourceFile)
|
||||||
|
|
||||||
|
runWriteAction {
|
||||||
|
val root = getOrCreateDummyRoot()
|
||||||
|
val decompiledFileName = FileUtil.getNameWithoutExtension(sourceFile.name) + ".decompiled.java"
|
||||||
|
val result = DummyFileSystem.getInstance().createChildFile(null, root, decompiledFileName)
|
||||||
|
VfsUtil.saveText(result, decompiledCode)
|
||||||
|
|
||||||
|
OpenFileDescriptor(sourceFile.project, result).navigate(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val KOTLIN_DECOMPILED_FOLDER = "kotlinDecompiled"
|
||||||
|
val KOTLIN_DECOMPILED_ROOT = "dummy://$KOTLIN_DECOMPILED_FOLDER"
|
||||||
|
|
||||||
|
fun getOrCreateDummyRoot(): VirtualFile =
|
||||||
|
VirtualFileManager.getInstance().refreshAndFindFileByUrl(KOTLIN_DECOMPILED_ROOT) ?:
|
||||||
|
DummyFileSystem.getInstance().createRoot(KOTLIN_DECOMPILED_FOLDER)
|
||||||
|
|
||||||
|
class DecompiledFileWritingAccessProvider : WritingAccessProvider() {
|
||||||
|
override fun isPotentiallyWritable(file: VirtualFile): Boolean {
|
||||||
|
if (file.fileSystem is DummyFileSystem && file.parent.name == KOTLIN_DECOMPILED_FOLDER) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun requestWriting(vararg files: VirtualFile): Collection<VirtualFile> = emptyList()
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.kotlin.idea.internal
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
|
interface KotlinDecompilerService {
|
||||||
|
fun decompile(file: KtFile): String
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getInstance(): KotlinDecompilerService? {
|
||||||
|
return ServiceManager.getService(KotlinDecompilerService::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.kotlin.idea.internal
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtilRt
|
||||||
|
import org.jetbrains.java.decompiler.IdeaLogger
|
||||||
|
import org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler
|
||||||
|
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider
|
||||||
|
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences
|
||||||
|
import org.jetbrains.java.decompiler.main.extern.IResultSaver
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.io.File
|
||||||
|
import java.util.jar.Manifest
|
||||||
|
|
||||||
|
class KotlinDecompilerServiceImpl : KotlinDecompilerService {
|
||||||
|
override fun decompile(file: KtFile): String {
|
||||||
|
val generationState = KotlinBytecodeToolWindow.compileSingleFile(file, true, true, true)
|
||||||
|
|
||||||
|
val bytecodeMap = hashMapOf<File, ByteArray>()
|
||||||
|
generationState.factory.asList().filter { FileUtilRt.extensionEquals(it.relativePath, "class") }.forEach {
|
||||||
|
bytecodeMap[File("/${it.relativePath}").absoluteFile] = it.asByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
val bytecodeProvider = KotlinBytecodeProvider(bytecodeMap)
|
||||||
|
val resultSaver = KotlinResultSaver()
|
||||||
|
val options = hashMapOf<String, Any>(
|
||||||
|
IFernflowerPreferences.REMOVE_BRIDGE to "0"
|
||||||
|
)
|
||||||
|
val decompiler = BaseDecompiler(bytecodeProvider, resultSaver, options, IdeaLogger())
|
||||||
|
for (path in bytecodeMap.keys) {
|
||||||
|
decompiler.addSpace(path, true)
|
||||||
|
}
|
||||||
|
decompiler.decompileContext()
|
||||||
|
return resultSaver.resultText
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinBytecodeProvider(val bytecodeMap: Map<File, ByteArray>) : IBytecodeProvider {
|
||||||
|
override fun getBytecode(externalPath: String?, internalPath: String?): ByteArray? {
|
||||||
|
return bytecodeMap[File(externalPath)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinResultSaver : IResultSaver {
|
||||||
|
private val decompiledText = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
val resultText: String
|
||||||
|
get() {
|
||||||
|
decompiledText.values.singleOrNull()?.let { return it }
|
||||||
|
return buildString {
|
||||||
|
for ((filename, content) in decompiledText) {
|
||||||
|
appendln("// $filename")
|
||||||
|
append(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveFolder(path: String?) { }
|
||||||
|
|
||||||
|
override fun closeArchive(path: String?, archiveName: String?) { }
|
||||||
|
|
||||||
|
override fun copyEntry(source: String?, path: String?, archiveName: String?, entry: String?) { }
|
||||||
|
|
||||||
|
override fun createArchive(path: String?, archiveName: String?, manifest: Manifest?) { }
|
||||||
|
|
||||||
|
override fun saveClassFile(path: String?, qualifiedName: String?, entryName: String?, content: String?, mapping: IntArray?) {
|
||||||
|
if (entryName != null && content != null) {
|
||||||
|
decompiledText[entryName] = content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun copyFile(source: String?, path: String?, entryName: String?) { }
|
||||||
|
|
||||||
|
override fun saveClassEntry(path: String?, archiveName: String?, qualifiedName: String?, entryName: String?, content: String?) { }
|
||||||
|
|
||||||
|
override fun saveDirEntry(path: String?, archiveName: String?, entryName: String?) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user