Skip to content

Commit 8294a61

Browse files
committed
java2smali v1.0
1 parent a569708 commit 8294a61

File tree

6 files changed

+233
-0
lines changed

6 files changed

+233
-0
lines changed

META-INF/plugin.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<idea-plugin version="2">
2+
<id>org.ollide.java2smali</id>
3+
<name>java2smali</name>
4+
<version>1.0</version>
5+
<vendor email="[email protected]" url="https://github.com/ollide">ollide</vendor>
6+
7+
<description><![CDATA[
8+
Simple plugin to easily compile a Java file to smali.
9+
]]></description>
10+
11+
<change-notes><![CDATA[
12+
<b>version 1.0</b><br>
13+
initial release
14+
]]>
15+
</change-notes>
16+
17+
<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Build+Number+Ranges for description -->
18+
<idea-version since-build="123.72"/>
19+
20+
<application-components>
21+
<!-- Add your application components here -->
22+
</application-components>
23+
24+
<project-components>
25+
<!-- Add your project components here -->
26+
</project-components>
27+
28+
<actions>
29+
<action id="generateSmaliCode" class="org.ollide.java2smali.GenerateAction" text="Compile to smali"
30+
description="Creates and shows a smali version of this Java file">
31+
<add-to-group group-id="BuildMenu" anchor="after" relative-to-action="Compile"/>
32+
</action>
33+
</actions>
34+
35+
<extensions defaultExtensionNs="com.intellij">
36+
<!-- Add your extensions here -->
37+
</extensions>
38+
</idea-plugin>

lib/baksmali-2.0.3.jar

999 KB
Binary file not shown.

lib/dx-19.0.1.jar

764 KB
Binary file not shown.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.ollide.java2smali;
2+
3+
import com.android.dx.command.dexer.Main;
4+
5+
import java.io.IOException;
6+
7+
public class Class2DexHelper {
8+
9+
/**
10+
* Uses the dx tool from the Android Build Tools (19.0.1) to create
11+
* a .dex version of a compiled java file (.class)
12+
*
13+
* @param inputClassFilePath full path to the compiled .class file
14+
* @param outputDexPath this will be the dex output file's path and name
15+
* @throws IOException
16+
*/
17+
public static void dexClassFile(String inputClassFilePath, String outputDexPath) throws IOException {
18+
Main.Arguments arguments = new Main.Arguments();
19+
arguments.outName = outputDexPath;
20+
arguments.strictNameCheck = false;
21+
arguments.fileNames = new String[]{inputClassFilePath};
22+
23+
Main.run(arguments);
24+
}
25+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.ollide.java2smali;
2+
3+
import org.jf.baksmali.baksmali;
4+
import org.jf.baksmali.baksmaliOptions;
5+
import org.jf.dexlib2.DexFileFactory;
6+
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
7+
8+
import java.io.File;
9+
import java.io.IOException;
10+
11+
public class Dex2SmaliHelper {
12+
13+
/**
14+
* Uses baksmali, an disassembler for Android's dex format.
15+
* Source code and more information: https://bitbucket.org/JesusFreke/smali/overview
16+
*
17+
* @param dexFilePath
18+
* @param outputDir
19+
* @throws IOException
20+
*/
21+
public static void disassembleDexFile(String dexFilePath, String outputDir) throws IOException {
22+
DexBackedDexFile dexBackedDexFile = DexFileFactory.loadDexFile(new File(dexFilePath), 19);
23+
baksmaliOptions options = new baksmaliOptions();
24+
options.outputDirectory = outputDir;
25+
26+
// default value -1 will lead to an exception
27+
// this setup is copied from Baksmali project
28+
options.jobs = Runtime.getRuntime().availableProcessors();
29+
if (options.jobs > 6) {
30+
options.jobs = 6;
31+
}
32+
33+
baksmali.disassembleDexFile(dexBackedDexFile, options);
34+
}
35+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package org.ollide.java2smali;
2+
3+
import com.intellij.openapi.actionSystem.AnAction;
4+
import com.intellij.openapi.actionSystem.AnActionEvent;
5+
import com.intellij.openapi.actionSystem.LangDataKeys;
6+
import com.intellij.openapi.actionSystem.PlatformDataKeys;
7+
import com.intellij.openapi.compiler.CompileContext;
8+
import com.intellij.openapi.compiler.CompileStatusNotification;
9+
import com.intellij.openapi.compiler.CompilerManager;
10+
import com.intellij.openapi.editor.Editor;
11+
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
12+
import com.intellij.openapi.module.Module;
13+
import com.intellij.openapi.project.Project;
14+
import com.intellij.openapi.roots.ProjectRootManager;
15+
import com.intellij.openapi.vfs.LocalFileSystem;
16+
import com.intellij.openapi.vfs.VirtualFile;
17+
import com.intellij.psi.PsiClass;
18+
import com.intellij.psi.PsiElement;
19+
import com.intellij.psi.PsiFile;
20+
import com.intellij.psi.PsiJavaFile;
21+
import com.intellij.psi.util.PsiTreeUtil;
22+
23+
import java.io.File;
24+
import java.io.IOException;
25+
26+
public class GenerateAction extends AnAction {
27+
28+
private static final String CLASS_EXTENSION = ".class";
29+
private static final String DEX_EXTENSION = ".dex";
30+
private static final String JAVA_EXTENSION = ".java";
31+
private static final String SMALI_EXTENSION = ".smali";
32+
33+
public void actionPerformed(AnActionEvent e) {
34+
PsiJavaFile javaFile = (PsiJavaFile) getPsiClassFromContext(e).getContainingFile();
35+
36+
Project p = e.getProject();
37+
Module module = ProjectRootManager.getInstance(p).getFileIndex().getModuleForFile(javaFile.getVirtualFile());
38+
39+
// Compile the javaFile's module
40+
CompilerCallback compilerCallback = new CompilerCallback(javaFile);
41+
CompilerManager.getInstance(p).compile(module, compilerCallback);
42+
}
43+
44+
@Override
45+
public void update(AnActionEvent e) {
46+
PsiClass psiClass = getPsiClassFromContext(e);
47+
e.getPresentation().setEnabled(psiClass != null && psiClass.getContainingFile() instanceof PsiJavaFile);
48+
}
49+
50+
private PsiClass getPsiClassFromContext(AnActionEvent e) {
51+
PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
52+
Editor editor = e.getData(PlatformDataKeys.EDITOR);
53+
54+
if (psiFile == null || editor == null) {
55+
return null;
56+
}
57+
58+
int offset = editor.getCaretModel().getOffset();
59+
PsiElement elementAt = psiFile.findElementAt(offset);
60+
return PsiTreeUtil.getParentOfType(elementAt, PsiClass.class);
61+
}
62+
63+
private static VirtualFile getSourceRootFile(PsiFile file) {
64+
return ProjectRootManager.getInstance(file.getProject()).getFileIndex().getSourceRootForFile(file.getVirtualFile());
65+
}
66+
67+
/**
68+
* This is the Callback class which is called when the module has been compiled.
69+
*/
70+
private static class CompilerCallback implements CompileStatusNotification {
71+
72+
private final PsiJavaFile javaFile;
73+
74+
public CompilerCallback(PsiJavaFile file) {
75+
this.javaFile = file;
76+
}
77+
78+
public void finished(boolean b, int i, int i2, CompileContext compileContext) {
79+
VirtualFile[] outputDirectories = compileContext.getAllOutputDirectories();
80+
if (outputDirectories != null && outputDirectories.length > 0) {
81+
String compileDirPath = outputDirectories[0].getPath();
82+
String compiledFilePath = getCompiledClassFilePath(compileDirPath);
83+
84+
String dexFile = compiledFilePath + DEX_EXTENSION;
85+
// CLASS -> DEX
86+
try {
87+
Class2DexHelper.dexClassFile(compiledFilePath, dexFile);
88+
} catch (IOException e) {
89+
e.printStackTrace();
90+
return;
91+
}
92+
93+
// DEX -> SMALI
94+
String outputDir = getSourceRootFile(javaFile).getPath();
95+
try {
96+
Dex2SmaliHelper.disassembleDexFile(dexFile, outputDir);
97+
} catch (IOException e) {
98+
e.printStackTrace();
99+
return;
100+
}
101+
102+
// we've created the smali file in our source file's directory
103+
// refresh directory synchronously to let IDEA detect the file
104+
javaFile.getVirtualFile().getParent().refresh(false, false);
105+
106+
// get a VirtualFile by the IO path
107+
String smaliPath = javaFile.getVirtualFile().getPath().replace(JAVA_EXTENSION, SMALI_EXTENSION);
108+
VirtualFile virtualDexFile = LocalFileSystem.getInstance().findFileByIoFile(new File(smaliPath));
109+
if (virtualDexFile == null) {
110+
// create smali file failed
111+
return;
112+
}
113+
114+
// use the VirtualFile to show the smali file in IDEA editor
115+
OpenFileDescriptor openFileDescriptor = new OpenFileDescriptor(javaFile.getProject(), virtualDexFile);
116+
openFileDescriptor.navigate(true);
117+
}
118+
}
119+
120+
private String getCompiledClassFilePath(String dirPath) {
121+
String packageName = javaFile.getPackageName();
122+
String[] packages = packageName.split("\\.");
123+
124+
StringBuilder sb = new StringBuilder(dirPath);
125+
for (String p : packages) {
126+
sb.append(File.separator);
127+
sb.append(p);
128+
}
129+
sb.append(File.separator);
130+
sb.append(javaFile.getContainingFile().getVirtualFile().getNameWithoutExtension());
131+
sb.append(CLASS_EXTENSION);
132+
return sb.toString();
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)