Skip to content

Commit

Permalink
Add @keep and "Keep everything"
Browse files Browse the repository at this point in the history
Keep everything will decide whether R8 should keep all public
symbols by default. If unchecked, you can now use @keep to
choose what to keep.
  • Loading branch information
romainguy committed Aug 22, 2024
1 parent 2cc1f36 commit 59b7dc0
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 20 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repositories {
maven("https://packages.jetbrains.team/maven/p/kpm/public/")
}

version = "1.4.7"
version = "1.5.0"
val baseName = "Kotlin Explorer"

kotlin {
Expand Down
37 changes: 35 additions & 2 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/Disassembly.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ suspend fun buildAndRun(

val path = directory.resolve("KotlinExplorer.kt")
Files.writeString(path, source)
writeSupportFiles(directory)

val kotlinc = KotlinCompiler(toolPaths, directory).compile(path)

Expand Down Expand Up @@ -101,7 +102,8 @@ suspend fun buildAndDisassemble(
onOat: (CodeContent) -> Unit,
onLogs: (AnnotatedString) -> Unit,
onStatusUpdate: (String, Float) -> Unit,
optimize: Boolean
optimize: Boolean,
keepEverything: Boolean
) = coroutineScope {
val ui = currentCoroutineContext()

Expand All @@ -117,6 +119,7 @@ suspend fun buildAndDisassemble(

val path = directory.resolve("KotlinExplorer.kt")
Files.writeString(path, source)
writeSupportFiles(directory)

val kotlinc = KotlinCompiler(toolPaths, directory).compile(path)

Expand Down Expand Up @@ -149,7 +152,7 @@ suspend fun buildAndDisassemble(

val dexCompiler = DexCompiler(toolPaths, directory, r8rules)

val dex = dexCompiler.buildDex(optimize)
val dex = dexCompiler.buildDex(optimize, keepEverything)

if (dex.exitCode != 0) {
updater.addJob(launch(ui) {
Expand Down Expand Up @@ -271,6 +274,36 @@ private fun cleanupClasses(directory: Path) {
}
}

private fun writeSupportFiles(directory: Path) {
Files.writeString(
directory.resolve("Keep.kt"),
"""
import java.lang.annotation.ElementType.ANNOTATION_TYPE
import java.lang.annotation.ElementType.CONSTRUCTOR
import java.lang.annotation.ElementType.FIELD
import java.lang.annotation.ElementType.METHOD
import java.lang.annotation.ElementType.PACKAGE
import java.lang.annotation.ElementType.TYPE
@Retention(AnnotationRetention.BINARY)
@Target(
AnnotationTarget.FILE,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.CLASS,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FIELD
)
@Suppress("DEPRECATED_JAVA_ANNOTATION", "SupportAnnotationUsage")
@java.lang.annotation.Target(PACKAGE, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, METHOD, FIELD)
public annotation class Keep
""".trimIndent()
)
}

private fun showError(error: String) = buildAnnotatedString {
withStyle(SpanStyle(ErrorColor)) {
append(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.compose.ui.input.key.Key.Companion.C
import androidx.compose.ui.input.key.Key.Companion.D
import androidx.compose.ui.input.key.Key.Companion.F
import androidx.compose.ui.input.key.Key.Companion.G
import androidx.compose.ui.input.key.Key.Companion.K
import androidx.compose.ui.input.key.Key.Companion.L
import androidx.compose.ui.input.key.Key.Companion.O
import androidx.compose.ui.input.key.Key.Companion.P
Expand Down Expand Up @@ -438,7 +439,8 @@ private fun FrameWindowScope.MainMenu(
onOatUpdate,
onLogsUpdate,
onStatusUpdate,
explorerState.optimize
explorerState.optimize,
explorerState.keepEverything
)
}
}
Expand Down Expand Up @@ -508,6 +510,7 @@ private fun FrameWindowScope.MainMenu(
)
Separator()
MenuCheckboxItem("Optimize with R8", CtrlShift(O), explorerState::optimize)
MenuCheckboxItem("Keep everything", CtrlShift(K), explorerState::keepEverything)
MenuCheckboxItem("Build on Startup", shortcut = null, explorerState::autoBuildOnStartup)
MenuItem(
"Build & Disassemble",
Expand Down
11 changes: 10 additions & 1 deletion src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import kotlin.io.path.readLines
private const val AndroidHome = "ANDROID_HOME"
private const val KotlinHome = "KOTLIN_HOME"
private const val Optimize = "OPTIMIZE"
private const val KeepEverything = "KEEP_EVERYTHING"
private const val R8Rules = "R8_RULES"
private const val AutoBuildOnStartup = "AUTO_BUILD_ON_STARTUP"
private const val Presentation = "PRESENTATION"
Expand Down Expand Up @@ -55,6 +56,7 @@ class ExplorerState {
var kotlinHome by StringState(KotlinHome, System.getenv("KOTLIN_HOME") ?: System.getProperty("user.home"))
var toolPaths by mutableStateOf(createToolPaths())
var optimize by BooleanState(Optimize, true)
var keepEverything by BooleanState(KeepEverything, true)
var r8Rules by StringState(R8Rules, "")
var autoBuildOnStartup by BooleanState(AutoBuildOnStartup, false)
var presentationMode by BooleanState(Presentation, false)
Expand Down Expand Up @@ -151,5 +153,12 @@ private fun readSettings(file: Path): MutableMap<String, String> {
private fun readSourceCode(toolPaths: ToolPaths) = if (toolPaths.sourceFile.exists()) {
Files.readString(toolPaths.sourceFile)
} else {
"fun square(a: Int): Int {\n return a * a\n}\n"
"""
// NOTE: If Build > Keep everything is *not* checked, used the @Keep
// annotation to keep the classes/methods/etc. you want to disassemble
fun square(a: Int): Int {
return a * a
}
""".trimIndent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@

package dev.romainguy.kotlin.explorer.build

import dev.romainguy.kotlin.explorer.ProcessResult
import dev.romainguy.kotlin.explorer.ToolPaths
import dev.romainguy.kotlin.explorer.process
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.*

class DexCompiler(private val toolPaths: ToolPaths, private val outputDirectory: Path, private val r8rules: String) {
suspend fun buildDex(optimize: Boolean) = process(*buildDexCommand(optimize), directory = outputDirectory)
suspend fun buildDex(optimize: Boolean, keepEverything: Boolean): ProcessResult {
return process(*buildDexCommand(optimize, keepEverything), directory = outputDirectory)
}

suspend fun dumpDex() = process(
toolPaths.dexdump.toString(),
Expand All @@ -33,8 +36,8 @@ class DexCompiler(private val toolPaths: ToolPaths, private val outputDirectory:
)

@OptIn(ExperimentalPathApi::class)
private fun buildDexCommand(optimize: Boolean): Array<String> {
writeR8Rules()
private fun buildDexCommand(optimize: Boolean, keepEverything: Boolean): Array<String> {
writeR8Rules(keepEverything)

return buildList {
add("java")
Expand Down Expand Up @@ -69,20 +72,47 @@ class DexCompiler(private val toolPaths: ToolPaths, private val outputDirectory:
}.toTypedArray()
}

private fun writeR8Rules() {
private fun writeR8Rules(keepEverything: Boolean) {
// Match $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt
Files.writeString(
outputDirectory.resolve("rules.txt"),
"""
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify
-dontobfuscate
-keep,allowoptimization class !kotlin.**,!kotlinx.** {
<methods>;
buildString {
append(
"""
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify
-dontobfuscate
""".trimIndent()
)
if (keepEverything) {
append(
"""
-keep,allowoptimization class !kotlin.**,!kotlinx.** {
<methods >;
}
""".trimIndent()
)
} else {
append(
"""
-keep,allowobfuscation @interface Keep
-keep @Keep class * {*;}
-keepclasseswithmembers class * {
@Keep <methods>;
}
-keepclasseswithmembers class * {
@Keep <fields>;
}
-keepclasseswithmembers class * {
@Keep <init>(...);
}
""".trimIndent()
)
}
""".trimIndent() + r8rules
append(r8rules)
}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ class KotlinCompiler(private val toolPaths: ToolPaths, private val outputDirecto
private fun buildCompileCommand(file: Path): Array<String> {
val command = mutableListOf(
toolPaths.kotlinc.toString(),
file.toString(),
"-Xmulti-platform",
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions",
"-classpath",
(toolPaths.kotlinLibs + listOf(toolPaths.platform)).joinToString(":") { jar -> jar.toString() }
(toolPaths.kotlinLibs + listOf(toolPaths.platform)).joinToString(":") { jar -> jar.toString() },
file.toString(),
file.parent.resolve("Keep.kt").toString()
)

return command.toTypedArray()
Expand Down

0 comments on commit 59b7dc0

Please sign in to comment.