Skip to content

Commit d0f3368

Browse files
committed
Merge branch 'release/2.0.0'
2 parents 75d6dff + 25d3132 commit d0f3368

File tree

10 files changed

+322
-110
lines changed

10 files changed

+322
-110
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
language: java
2-
jdk: oraclejdk8
2+
jdk: openjdk11
33

44
before_cache:
55
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ intellij-java2smali
33

44
[![Travis Build Status](https://travis-ci.org/ollide/intellij-java2smali.svg?branch=develop)](https://travis-ci.org/ollide/intellij-java2smali)
55
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/htqnq07bveqh8elv/branch/develop?svg=true)](https://ci.appveyor.com/project/ollide/intellij-java2smali)
6-
[![Quality Gate status](https://sonarcloud.io/api/badges/gate?key=org.ollide:intellij-java2smali)](https://sonarcloud.io/dashboard?id=org.ollide%3Aintellij-java2smali)
6+
[![Quality Gate status](https://sonarcloud.io/api/project_badges/measure?project=org.ollide%3Aintellij-java2smali&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.ollide%3Aintellij-java2smali)
77

88
Simple plugin for IntelliJ IDEA & Android Studio to easily compile Java & Kotlin files to smali.
99

@@ -37,6 +37,14 @@ Build the plugin with Gradle:
3737

3838
The plugin can be found in `/build/distributions/java2smali-$VERSION.zip`.
3939

40+
## Troubleshooting ##
41+
42+
To enable debug logging of the plugin, select `Help -> Diagnostic Tools -> Debug Log Settings…`
43+
and add the following line:
44+
```
45+
#org.ollide.java2smali
46+
```
47+
4048
## Third-Party Libraries & Credits ##
4149

4250
- [dx](http://developer.android.com/tools/help/index.html#tools-platform) from the Android platform-tools is used to create a .dex version of the compiled Java (.class) file

build.gradle

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ buildscript {
22
repositories {
33
jcenter()
44
}
5+
6+
dependencies {
7+
classpath 'com.github.ben-manes:gradle-versions-plugin:0.28.0'
8+
}
59
}
610

711
plugins {
8-
id 'org.jetbrains.intellij' version '0.2.13'
9-
id 'org.jetbrains.kotlin.jvm' version '1.1.2'
10-
id 'org.sonarqube' version '2.5'
12+
id 'org.jetbrains.intellij' version '0.4.18'
13+
id 'org.jetbrains.kotlin.jvm' version '1.3.71'
14+
id 'org.sonarqube' version '2.8'
1115
}
1216

17+
apply plugin: 'com.github.ben-manes.versions'
1318
apply plugin: 'org.jetbrains.intellij'
1419
apply plugin: 'java'
1520
apply plugin: 'kotlin'
@@ -21,14 +26,13 @@ repositories {
2126
dependencies {
2227
compile fileTree(dir: 'lib', include: '*.jar')
2328

24-
compile 'org.jetbrains.kotlin:kotlin-runtime'
25-
compile 'org.jetbrains.kotlin:kotlin-stdlib'
29+
compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
2630

27-
compile 'org.smali:baksmali:2.2.1'
31+
compile 'org.smali:baksmali:2.4.0'
2832
}
2933

3034
intellij {
31-
version = 'IC-143.2370.31'
35+
version = 'IC-2020.1'
3236

3337
downloadSources false
3438
updateSinceUntilBuild false
@@ -41,12 +45,15 @@ intellij {
4145
username 'ollide'
4246
password JETBRAINS_PASSWORD
4347
}
48+
49+
plugins = ['java']
4450
}
4551

4652
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
4753
kotlinOptions {
48-
jvmTarget = '1.6'
49-
apiVersion = '1.0'
54+
jvmTarget = '1.8'
55+
apiVersion = '1.2'
56+
languageVersion = '1.2'
5057
}
5158
}
5259

@@ -61,4 +68,4 @@ sonarqube {
6168
}
6269
}
6370

64-
version = '1.6'
71+
version = '2.0.0'

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

src/main/kotlin/org/ollide/java2smali/CompilerCallback.kt

Lines changed: 0 additions & 76 deletions
This file was deleted.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package org.ollide.java2smali
2+
3+
import com.intellij.openapi.command.WriteCommandAction
4+
import com.intellij.openapi.diagnostic.Logger
5+
import com.intellij.openapi.fileEditor.OpenFileDescriptor
6+
import com.intellij.openapi.module.Module
7+
import com.intellij.openapi.project.Project
8+
import com.intellij.openapi.roots.*
9+
import com.intellij.openapi.vfs.LocalFileSystem
10+
import com.intellij.openapi.vfs.VirtualFile
11+
import com.intellij.openapi.vfs.VirtualFileManager
12+
import com.intellij.psi.PsiClassOwner
13+
import com.intellij.psi.PsiManager
14+
import com.intellij.task.ProjectTaskManager
15+
import com.intellij.util.SmartList
16+
import com.intellij.util.containers.OrderedSet
17+
import com.intellij.util.io.URLUtil
18+
import java.io.File
19+
import java.io.IOException
20+
import java.nio.file.Paths
21+
22+
class DexCompiler(private val vFile: VirtualFile, private val project: Project, private val module: Module) {
23+
24+
fun run() {
25+
buildModule {
26+
onProjectBuildComplete()
27+
}
28+
}
29+
30+
/**
31+
* To create a dex or smali file from the virtual file, we ne a compiled .class file
32+
* of the given virtual file to be present.
33+
*
34+
* Project structures and builds vary a lot (Android, Java, Kotlin, directory structure),
35+
* so instead of using the CompileManager, we trigger a general build task.
36+
*/
37+
private fun buildModule(callback: () -> Unit) {
38+
val projectTaskManager = ProjectTaskManager.getInstance(project)
39+
val buildTask = projectTaskManager.createModulesBuildTask(module, true, true, true)
40+
41+
val supportProjectTaskManager = SupportProjectTaskManager(projectTaskManager)
42+
supportProjectTaskManager.run(buildTask).onSuccess {
43+
if (it.hasErrors()) {
44+
LOG.warn("Module build failed, aborting dex/smali build.")
45+
} else {
46+
callback()
47+
}
48+
}
49+
}
50+
51+
private fun onProjectBuildComplete() {
52+
val file = PsiManager.getInstance(project).findFile(vFile) as PsiClassOwner
53+
54+
val fileOutputDirectory = getFileOutputDirectory(file)
55+
fileOutputDirectory.refresh(false, false)
56+
57+
val fileName = vFile.nameWithoutExtension
58+
val dexFilePath = Paths.get(fileOutputDirectory.path, fileName + DEX_EXTENSION).toString()
59+
60+
// CLASS -> DEX
61+
val targetFiles = getClassFiles(fileOutputDirectory, fileName)
62+
compileDexFile(targetFiles, dexFilePath)
63+
64+
// DEX -> SMALI
65+
val outputDir = getSourceRootFile().path
66+
WriteCommandAction.runWriteCommandAction(project) {
67+
Dex2SmaliHelper.disassembleDexFile(dexFilePath, outputDir)
68+
69+
// we've created the smali file(s) in our source file's directory
70+
// refresh directory synchronously and access children to let IDEA detect the file(s)
71+
val parent = vFile.parent
72+
parent.refresh(false, false)
73+
parent.children
74+
}
75+
76+
// get a VirtualFile by the IO path
77+
val smaliPath = vFile.path.substringBeforeLast('.') + SMALI_EXTENSION
78+
val virtualDexFile = LocalFileSystem.getInstance().findFileByIoFile(File(smaliPath)) ?: return
79+
80+
// use the VirtualFile to show the smali file in IDEA editor
81+
val openFileDescriptor = OpenFileDescriptor(project, virtualDexFile)
82+
openFileDescriptor.navigate(true)
83+
}
84+
85+
private fun getClassFiles(fileOutputDirectory: VirtualFile, fileName: String): Array<String> {
86+
val children = fileOutputDirectory.children ?: arrayOf()
87+
return children.filter {
88+
val baseName = it.nameWithoutExtension
89+
(baseName == fileName || baseName.startsWith("$fileName$")) && it.extension == CLASS
90+
}.map {
91+
it.path
92+
}.toTypedArray()
93+
}
94+
95+
private fun getFileOutputDirectory(file: PsiClassOwner): VirtualFile {
96+
// determine whether this is a production or test file
97+
val isProduction = module.getModuleScope(false).contains(vFile)
98+
99+
val pkg = file.packageName.replace('.', File.separatorChar)
100+
101+
// find the general output directory of the file's module (target, app/build/intermediates/javac/$variant/classes, ...)
102+
val possibleOutputDirectories = findModuleOutputDirectories(isProduction)
103+
LOG.debug("Possible output directories: ", possibleOutputDirectories.joinToString(","))
104+
105+
val virtualFileManager = VirtualFileManager.getInstance().getFileSystem(URLUtil.FILE_PROTOCOL)
106+
107+
val fileOutputDirectory = possibleOutputDirectories
108+
.asSequence()
109+
.map {
110+
val classFile = vFile.nameWithoutExtension + CLASS_EXTENSION
111+
val path = Paths.get(it, pkg, classFile).toString()
112+
virtualFileManager.refreshAndFindFileByPath(path)?.parent
113+
}
114+
.firstOrNull { it != null }
115+
116+
LOG.debug("Found output directory: $fileOutputDirectory")
117+
return fileOutputDirectory ?: throw IllegalStateException("Output directory not found")
118+
}
119+
120+
/**
121+
* @see <a href="https://github.com/JetBrains/intellij-community/blob/master/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java">intellij-community/CompilerPaths.java</a>
122+
*/
123+
private fun findModuleOutputDirectories(production: Boolean): OrderedSet<String> {
124+
val outputPaths: MutableList<String> = mutableListOf()
125+
126+
val compilerExtension = CompilerModuleExtension.getInstance(module)
127+
if (production) {
128+
compilerExtension?.compilerOutputPath?.path?.let { outputPaths.add(it) }
129+
} else {
130+
compilerExtension?.compilerOutputPathForTests?.path?.let { outputPaths.add(it) }
131+
}
132+
133+
val moduleRootManager = ModuleRootManager.getInstance(module)
134+
for (handlerFactory in OrderEnumerationHandler.EP_NAME.extensions) {
135+
if (handlerFactory.isApplicable(module)) {
136+
val handler = handlerFactory.createHandler(module)
137+
val outputUrls: List<String> = SmartList()
138+
handler.addCustomModuleRoots(OrderRootType.CLASSES, moduleRootManager, outputUrls, production, !production)
139+
for (outputUrl in outputUrls) {
140+
outputPaths.add(VirtualFileManager.extractPath(outputUrl).replace('/', File.separatorChar))
141+
}
142+
}
143+
}
144+
return OrderedSet(outputPaths)
145+
}
146+
147+
private fun compileDexFile(compiledPaths: Array<String>, dexFile: String) {
148+
try {
149+
Class2DexHelper.dexClassFile(compiledPaths, dexFile)
150+
} catch (e: IOException) {
151+
e.printStackTrace()
152+
return
153+
}
154+
}
155+
156+
private fun getSourceRootFile(): VirtualFile {
157+
return ProjectRootManager.getInstance(project).fileIndex.getSourceRootForFile(vFile) as VirtualFile
158+
}
159+
160+
companion object {
161+
162+
private val LOG = Logger.getInstance(DexCompiler::class.java)
163+
164+
const val CLASS_EXTENSION = ".class"
165+
const val DEX_EXTENSION = ".dex"
166+
const val SMALI_EXTENSION = ".smali"
167+
const val CLASS = "class"
168+
}
169+
170+
}

0 commit comments

Comments
 (0)