Open-source project
romainguy/kotlin-explorer avatar
romainguy/kotlin-explorer

Kotlin Explorer: a three pane workbench for Kotlin bytecode, DEX and OAT

Desktop tool to quickly explore disassembled Kotlin code.

972 stars38 forksKotlinApache-2.0

At a glance

What is it?
Kotlin Explorer is a desktop application that compiles Kotlin source you type and shows the result as Java bytecode, Android DEX bytecode and ahead of time native assembly in parallel panes. Two of those three outputs only appear if a real Android device or emulator is reachable over adb, which is the constraint that decides whether the tool is useful to you.
Who is it for?
Adopt Kotlin Explorer if you write Kotlin and want to see what the compiler and R8 actually emit, especially on the Android side where DEX and OAT output make the difference between theory and the shipped artefact. Do not plan on it for reverse engineering an existing application, since the workflow starts from source you type in the app.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 138 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Type Kotlin on the left, read three artefacts on the right

Kotlin Explorer is a desktop application for looking at what a Kotlin compilation produces. The interface is three panes. You type valid Kotlin code in the left pane, and the application compiles it and displays the results in the two panes on the right.

By default, the middle pane shows Android DEX bytecode and the right panel shows the native assembly produced by ahead of time compilation. You can control which panels are visible from the View menu, which matters because the source pane stays the same either way. The middle pane also shows Java bytecode as one of the outputs the tool can produce, alongside DEX and OAT.

The reason the panes are side by side rather than separate windows is that the tool is built for comparison. The source, the bytecode and the assembly are three views of one compilation, and features such as Sync Lines tie a line in one pane to the corresponding line in another. For someone learning what a language construct costs at the bytecode level, that alignment is the entire value proposition; a text editor and a command line compiler give you the same information but no correspondence between them.

Build & Disassemble is the whole loop

After launching the application, the interaction is short. You type code in the left pane, then trigger a compilation with *Build > Build & Disassemble*, or with the keyboard shortcut, which is `Cmd-Shift-D` on macOS and `Ctrl-Shift-D` on Linux and Windows.

The menu items around that command shape the workflow. *Build > Run* compiles the Kotlin source and runs it locally, sending any output to the logs panel, which is how you check behaviour alongside the disassembly. *Build > Build on Startup* launches a compilation automatically when the application starts, which saves a keystroke on every launch and is the setting to turn on when you are iterating. *Build > Optimize with R8* turns on R8 optimizations, and it changes what you can see, which is the subject of its own section below.

There is also a set of inspection aids for the assembly output. Clicking a jump instruction draws an arrow to the jump destination. The tool reports the number of instructions and branches per method. Clicking a disassembled instruction or a register highlights every occurrence of it. Inline aarch64 documentation is available under *View > Show Logs & Documentation*, which puts architecture reference material next to the instruction you are reading instead of in a separate window.

DEX and OAT output need a reachable Android device

The README marks this as important, and it is the point that decides whether the tool works for you at all. DEX bytecode and OAT assembly are only displayed if an Android device or emulator can be reached successfully via adb, and the device has to be recent enough to host the `oatdump` tool on its system image.

That constraint shapes the whole installation. This is a desktop application, but two of its three outputs are produced on the phone or emulator, not on the machine running the UI. The device is not an optional extra for the headline feature, it is a prerequisite, and the age requirement on the system image means an old test device will not do.

The Java bytecode output does not have this requirement. If you are working on plain JVM Kotlin and only care about the class files, the tool can produce them without any Android involvement. If your interest is in what Android ships, the DEX and OAT panes are where the information lives, and they need the adb route to be working before you start.

Running it from source needs three paths set up

Kotlin Explorer is run with a Gradle task:

bash
./gradlew jvmRun

The application then has to be told where to find the Android SDK and the Kotlin compiler. Unless you have already set `$ANDROID_HOME` and `$KOTLIN_HOME` properly, the application asks you to enter those paths interactively.

For `$ANDROID_HOME`, use the root of the Android SDK, the directory that contains `build-tools/` and `platform-tools/` and similar. The README notes that Android Studio for macOS stores this in `$HOME/Library/Android/sdk`. For `$KOTLIN_HOME`, use the root of your Kotlin installation, and the README says that directory should contain `bin/kotlinc` and `lib/kotlin-stdlib-*.jar`.

There is a third requirement that is easy to overlook: `java` and `javap` must both be in your `$PATH`. Since `javap` is the disassembler that produces the Java bytecode view, its absence removes one of the outputs rather than causing an obvious error.

The repository layout explains the build. It is a Kotlin project with build.gradle.kts, settings.gradle.kts and gradle.properties at the root, a compose-desktop.pro file and a compose-stability.config, and src/ plus scripts/ directories. The Compose desktop setup is what produces a native desktop application rather than a web or mobile one.

R8 optimization removes the source line numbers

The most consequential feature for what you can see is *Build > Optimize with R8*. The README is explicit that turning it on affects the ability to see corresponding source line numbers in the bytecode and DEX outputs, and that the Sync Lines feature may require R8 optimizations to be turned off to work properly.

That is a real trade-off. Without R8, the output looks like what a normal build produces, with the line mapping intact and the code recognisable. With R8, the output looks like what actually ships in an Android application, which is the point of the tool for anyone studying what their code becomes after shrinking and optimisation.

By default, all public classes and members are kept, which lets you analyse them in the disassembly panels. The README warns that keeping everything is not representative of what R8 will do on an actual application, and gives you two ways to get a more honest picture. You can disable the keep everything feature and use the `@Keep` annotation to choose an entry point leading to the disassembly you want. Or you can create a `fun main()` entry point and call your code from there.

That last route carries a warning worth remembering: be careful not to use constants when calling other methods, since this may lead to aggressive optimization by R8. Inlining a constant can remove the call you were trying to observe.

Presentation Mode and Sync Lines for reading the output

Two display features shape how the panes are used in practice.

*View > Sync Lines* synchronizes the current line in the source, bytecode and DEX panels. This is the feature that makes the three pane layout pay off, because you move through one view and the others follow. The README notes that it may require R8 optimizations to be turned off to work properly, which links it back to the previous section: a faithful, optimised view and a line aligned view do not always coexist.

*View > Presentation Mode* increases the font size to make the content more visible when projected. That is a small detail with a clear audience, since disassembly is dense, monospaced text that is difficult to read from a screen at the back of a room. It tells you who the tool is also used by, people explaining generated code to a group rather than only people debugging it alone.

The logs and documentation panel is toggled separately under *View > Show Logs & Documentation*, and it holds both the output of a local run and the inline aarch64 reference material. Since the OAT pane is ARM 64 bit assembly, having architecture documentation in the same window removes the usual round trip to a separate reference.

Where Kotlin Explorer is the wrong tool

The workflow has a firm starting point: you type the Kotlin source into the app. The README describes no way to load an existing binary, a compiled APK from a device, or a decompiled jar, and no such feature is mentioned anywhere in the documentation. So if your goal is reverse engineering an application you do not have the source for, this is not the tool, no matter how good the disassembly view is.

The Android requirement is the second limit. Two of the three outputs depend on a reachable device with a recent enough system image, which means a laptop without an Android device or emulator nearby gets Java bytecode and nothing else, and a CI environment gets the same.

There is a third, softer limit. The tool is opinionated about the pipeline: it compiles with the Kotlin compiler you point it at, optionally runs R8, and then produces DEX and OAT on a device. It is a workbench for one compilation path, not a general bytecode analysis suite. For the narrower job of looking at what a Kotlin snippet becomes, that focus is an advantage. For a mixed set of questions about arbitrary class files, the command line tools are a better fit, and for comparing Kotlin and Java performance a profiler is the right instrument.

That comparison is the real alternative to consider. The same information is available through the JDK's `javap`, the Kotlin compiler's own tooling, and Android's build tools, and those give you text you can diff, grep and pipe. Kotlin Explorer gives you the same data with alignment, cross referencing and architecture documentation in one window. The trade is scriptability against comprehension.

Editorial conclusion

Adopt Kotlin Explorer if you write Kotlin and want to see what the compiler and R8 actually emit, especially on the Android side where DEX and OAT output make the difference between theory and the shipped artefact. Do not plan on it for reverse engineering an existing application, since the workflow starts from source you type in the app. Before you start, confirm that an Android device or emulator reachable through adb is recent enough to host oatdump, set ANDROID_HOME and KOTLIN_HOME, and check that the Apache-2.0 licence at the repository root fits your use, with v1.6.7 released on 2026-06-01 and the last push on 2026-06-07.

Frequently asked questions

How do I run Kotlin Explorer from source?

Run ./gradlew jvmRun from the repository. The application needs the Android SDK and Kotlin compiler locations, supplied through $ANDROID_HOME and $KOTLIN_HOME or entered interactively, and it needs java and javap on your $PATH.

Why do I not see DEX or OAT output in Kotlin Explorer?

Those two outputs are only displayed if an Android device or emulator can be reached via adb, and the device has to be recent enough to host the oatdump tool on its system image.

How does R8 affect what Kotlin Explorer shows?

Turning on Build > Optimize with R8 affects the ability to see corresponding source line numbers in the bytecode and DEX outputs, and Sync Lines may require R8 optimizations to be off to work properly.

Can Kotlin Explorer show the R8 output of a real application?

By default all public classes and members are kept, which is not representative of a real application. You can disable that and use the @Keep annotation to pick an entry point, or create a fun main() entry point, while avoiding constants in calls since those may trigger aggressive optimisation.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. romainguy/kotlin-explorer on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/romainguy-kotlin-explorer.svg)](https://hysenlabs.com/projects/romainguy-kotlin-explorer)

Was this useful?