javap

How to disassemble and decompile Scala code (javap, scalac, jad)

Table of Contents1 - Problem2 - Solution3 - Using javap4 - Using scalac print options5 - Use a decompiler6 - Discussion7 - See Also8 - Sponsored by ...

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 14.6, “How to disassemble and decompile Scala code.”

Back to top

Problem

In the process of learning Scala, or trying to understand a particular problem, you want to examine the source code and bytecode the Scala compiler generates from your original source code.

Back to top

Solution

You can use several different approaches to see how your Scala source code is translated:

Java’s javap command doesn’t show private members (by default)

As I was just reminded, Java’s javap command doesn’t show private members by default. You have to use the -p option of javap to see private members.

I was just reminded of that when using the Scala REPL. Given this Person class with a private constructor field named name:

class Person(private var name: String)

javap without the -p option shows this:

You can run the `javap` command inside the Scala REPL

As shown in the image, I was just reminded that you can run javap inside the Scala REPL. The REPL help command shows some other things you can do:

scala> :help

All commands can be abbreviated, e.g., :he instead of :help.
:edit <id>|<line>        edit history
:help [command]          print this summary or command-specific help
:history [num]           show the history (optional num is commands to show)
:h? <string>             search the history
:imports [name name ...] show import history, identifying sources of names
:implicits [-v]          show the implicits in scope
:javap <path|class>      disassemble a file or class name
:line <id>|<line>        place line(s) at the end of history
:load <path>             interpret lines in a file
:paste [-raw] [path]     enter paste mode or paste a file
:power                   enable power user mode
:quit                    exit the interpreter
:replay [options]        reset the repl and replay all previous commands
:require <path>          add a jar to the classpath
:reset [options]         reset the repl to its initial state, forgetting all session entries
:save <path>             save replayable session to a file
:sh <command line>       run a shell command (result is implicitly => List[String])
:settings <options>      update compiler options, if possible; see reset
:silent                  disable/enable automatic printing of results
:type [-v] <expr>        display the type of an expression without evaluating it
:kind [-v] <type>        display the kind of a type. see also :help kind
:warnings                show the suppressed warnings from the most recent line which had any

More Scala/javap examples

Here are a couple more javap examples in the Scala REPL that help to show what details are available:

scala> class Person (var name: String, var age: Int)
defined class Person

scala> :javap -public Person
Compiled from "<console>"
public class $line3.$read$$iw$$iw$Person {
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  public $line3.$read$$iw$$iw$Person(java.lang.String, int);
}

scala> :javap -private Person
Compiled from "<console>"
public class $line3.$read$$iw$$iw$Person {
  private java.lang.String name;
  private int age;
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  public $line3.$read$$iw$$iw$Person(java.lang.String, int);
}

javap help

Finally, here’s the output from the javap -help command that helps to explain that output:

> javap -help
Usage: javap <options> <classes>
where possible options include:
  -help  --help  -?        Print this usage message
  -version                 Version information
  -v  -verbose             Print additional information
  -l                       Print line number and local variable tables
  -public                  Show only public classes and members
  -protected               Show protected/public classes and members
  -package                 Show package/protected/public classes
                           and members (default)
  -p  -private             Show all classes and members
  -c                       Disassemble the code
  -s                       Print internal type signatures
  -sysinfo                 Show system info (path, size, date, MD5 hash)
                           of class being processed
  -constants               Show final constants
  -classpath <path>        Specify where to find user class files
  -cp <path>               Specify where to find user class files
  -bootclasspath <path>    Override location of bootstrap class files

If you ever want to use javap inside the Scala REPL, I hope this information is helpful.

A look at how the Scala `lazy val` syntax gets converted into Java code (bytecode)

Table of Contents1 - A little `lazy val` conversion example2 - A second `lazy val` conversion example3 - One more `lazy val` conversion example4 - The end

I don’t have any major conclusions to share in this blog post, but ... what I was curious about is how Scala implements “lazy val” fields. That is, when the Scala code I write is translated into a .class file and bytecode that a JVM can understand, what does that resulting code look like?

Converting a Scala class file to decompiled Java source code

As a Scala newbie, I'm curious about how the process of converting a Scala class back to Java source code works. What I really want to see is how my Scala source code is converted to Java source code. Besides plain old curiosity, I think that understanding more about how Scala works can also be very important to my understanding of Scala (such as the apply() method, and so on).