Skip to content

Commit

Permalink
java2smali v1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ollide committed Jan 23, 2014
1 parent a569708 commit 8294a61
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 0 deletions.
38 changes: 38 additions & 0 deletions META-INF/plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<idea-plugin version="2">
<id>org.ollide.java2smali</id>
<name>java2smali</name>
<version>1.0</version>
<vendor email="[email protected]" url="https://github.com/ollide">ollide</vendor>

<description><![CDATA[
Simple plugin to easily compile a Java file to smali.
]]></description>

<change-notes><![CDATA[
<b>version 1.0</b><br>
initial release
]]>
</change-notes>

<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Build+Number+Ranges for description -->
<idea-version since-build="123.72"/>

<application-components>
<!-- Add your application components here -->
</application-components>

<project-components>
<!-- Add your project components here -->
</project-components>

<actions>
<action id="generateSmaliCode" class="org.ollide.java2smali.GenerateAction" text="Compile to smali"
description="Creates and shows a smali version of this Java file">
<add-to-group group-id="BuildMenu" anchor="after" relative-to-action="Compile"/>
</action>
</actions>

<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
</extensions>
</idea-plugin>
Binary file added lib/baksmali-2.0.3.jar
Binary file not shown.
Binary file added lib/dx-19.0.1.jar
Binary file not shown.
25 changes: 25 additions & 0 deletions src/org/ollide/java2smali/Class2DexHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.ollide.java2smali;

import com.android.dx.command.dexer.Main;

import java.io.IOException;

public class Class2DexHelper {

/**
* Uses the dx tool from the Android Build Tools (19.0.1) to create
* a .dex version of a compiled java file (.class)
*
* @param inputClassFilePath full path to the compiled .class file
* @param outputDexPath this will be the dex output file's path and name
* @throws IOException
*/
public static void dexClassFile(String inputClassFilePath, String outputDexPath) throws IOException {
Main.Arguments arguments = new Main.Arguments();
arguments.outName = outputDexPath;
arguments.strictNameCheck = false;
arguments.fileNames = new String[]{inputClassFilePath};

Main.run(arguments);
}
}
35 changes: 35 additions & 0 deletions src/org/ollide/java2smali/Dex2SmaliHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.ollide.java2smali;

import org.jf.baksmali.baksmali;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;

import java.io.File;
import java.io.IOException;

public class Dex2SmaliHelper {

/**
* Uses baksmali, an disassembler for Android's dex format.
* Source code and more information: https://bitbucket.org/JesusFreke/smali/overview
*
* @param dexFilePath
* @param outputDir
* @throws IOException
*/
public static void disassembleDexFile(String dexFilePath, String outputDir) throws IOException {
DexBackedDexFile dexBackedDexFile = DexFileFactory.loadDexFile(new File(dexFilePath), 19);
baksmaliOptions options = new baksmaliOptions();
options.outputDirectory = outputDir;

// default value -1 will lead to an exception
// this setup is copied from Baksmali project
options.jobs = Runtime.getRuntime().availableProcessors();
if (options.jobs > 6) {
options.jobs = 6;
}

baksmali.disassembleDexFile(dexBackedDexFile, options);
}
}
135 changes: 135 additions & 0 deletions src/org/ollide/java2smali/GenerateAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package org.ollide.java2smali;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileStatusNotification;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.util.PsiTreeUtil;

import java.io.File;
import java.io.IOException;

public class GenerateAction extends AnAction {

private static final String CLASS_EXTENSION = ".class";
private static final String DEX_EXTENSION = ".dex";
private static final String JAVA_EXTENSION = ".java";
private static final String SMALI_EXTENSION = ".smali";

public void actionPerformed(AnActionEvent e) {
PsiJavaFile javaFile = (PsiJavaFile) getPsiClassFromContext(e).getContainingFile();

Project p = e.getProject();
Module module = ProjectRootManager.getInstance(p).getFileIndex().getModuleForFile(javaFile.getVirtualFile());

// Compile the javaFile's module
CompilerCallback compilerCallback = new CompilerCallback(javaFile);
CompilerManager.getInstance(p).compile(module, compilerCallback);
}

@Override
public void update(AnActionEvent e) {
PsiClass psiClass = getPsiClassFromContext(e);
e.getPresentation().setEnabled(psiClass != null && psiClass.getContainingFile() instanceof PsiJavaFile);
}

private PsiClass getPsiClassFromContext(AnActionEvent e) {
PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
Editor editor = e.getData(PlatformDataKeys.EDITOR);

if (psiFile == null || editor == null) {
return null;
}

int offset = editor.getCaretModel().getOffset();
PsiElement elementAt = psiFile.findElementAt(offset);
return PsiTreeUtil.getParentOfType(elementAt, PsiClass.class);
}

private static VirtualFile getSourceRootFile(PsiFile file) {
return ProjectRootManager.getInstance(file.getProject()).getFileIndex().getSourceRootForFile(file.getVirtualFile());
}

/**
* This is the Callback class which is called when the module has been compiled.
*/
private static class CompilerCallback implements CompileStatusNotification {

private final PsiJavaFile javaFile;

public CompilerCallback(PsiJavaFile file) {
this.javaFile = file;
}

public void finished(boolean b, int i, int i2, CompileContext compileContext) {
VirtualFile[] outputDirectories = compileContext.getAllOutputDirectories();
if (outputDirectories != null && outputDirectories.length > 0) {
String compileDirPath = outputDirectories[0].getPath();
String compiledFilePath = getCompiledClassFilePath(compileDirPath);

String dexFile = compiledFilePath + DEX_EXTENSION;
// CLASS -> DEX
try {
Class2DexHelper.dexClassFile(compiledFilePath, dexFile);
} catch (IOException e) {
e.printStackTrace();
return;
}

// DEX -> SMALI
String outputDir = getSourceRootFile(javaFile).getPath();
try {
Dex2SmaliHelper.disassembleDexFile(dexFile, outputDir);
} catch (IOException e) {
e.printStackTrace();
return;
}

// we've created the smali file in our source file's directory
// refresh directory synchronously to let IDEA detect the file
javaFile.getVirtualFile().getParent().refresh(false, false);

// get a VirtualFile by the IO path
String smaliPath = javaFile.getVirtualFile().getPath().replace(JAVA_EXTENSION, SMALI_EXTENSION);
VirtualFile virtualDexFile = LocalFileSystem.getInstance().findFileByIoFile(new File(smaliPath));
if (virtualDexFile == null) {
// create smali file failed
return;
}

// use the VirtualFile to show the smali file in IDEA editor
OpenFileDescriptor openFileDescriptor = new OpenFileDescriptor(javaFile.getProject(), virtualDexFile);
openFileDescriptor.navigate(true);
}
}

private String getCompiledClassFilePath(String dirPath) {
String packageName = javaFile.getPackageName();
String[] packages = packageName.split("\\.");

StringBuilder sb = new StringBuilder(dirPath);
for (String p : packages) {
sb.append(File.separator);
sb.append(p);
}
sb.append(File.separator);
sb.append(javaFile.getContainingFile().getVirtualFile().getNameWithoutExtension());
sb.append(CLASS_EXTENSION);
return sb.toString();
}
}
}

0 comments on commit 8294a61

Please sign in to comment.