How to recognize boxing / unboxing in decompiled Scala code?

In the accepted best answer to this question , there is a clear explanation of why boxing occurs.

However, if I decompile the code (using a java decompiler), I do not see the use of scala.runtime.BoxesRunTime. Also, if I look at the code (using JProfiler), I don't see any BoxesRunTime instances.

So how can I see boxing / unboxing proof?

+8
scala boxing
source share
2 answers

In this code:

class Foo[T] { def bar(i: T) = i } object Main { def main(args: Array[String]) { val f = new Foo[Int] f.bar(5) } } 

The bar call should first insert an integer. Compilation using Scala 2.8.1 and using:

 javap -c -l -private -verbose -classpath <dir> Main$ 

to see that the bytecode created for the main method of the main class gives:

 public void main(java.lang.String[]); ... 9: iconst_5 10: invokestatic #24; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; 13: invokevirtual #28; //Method Foo.bar:(Ljava/lang/Object;)Ljava/lang/Object; 16: pop 17: return ... 

You can call BoxesRunTime before calling bar .

BoxesRunTime is an object that contains boxing methods for primitive types, so there must be exactly one instance as a whole. The trick here is that this particular file in the library was written in Java, and conversions are static methods. For this reason, there are no instances at runtime, although using it in Scala code looks as if it were an object.

You should probably look for nested primitives (like java.lang.Integer) with JProfile, although I'm not sure how the JVM works, and whether it can really rewrite the code at runtime and optimize it to avoid boxing. As far as I know, he should not apply specialization (but I believe that the CLR). A few micro-businesses with and without boxing are another way to find out what happens at runtime.

EDIT:

The above assumes that the type parameter was not annotated with the @specialized annotation. In this case, boxing / unpacking can be avoided. Certain classes in the standard library are specialized. See this file .

+7
source share

Given the following Test.scala program:

 object Test { def main(args:Array[String]) { val list = List(1,5,15) val res = list.map(e => e*2).filter(e => e>10) } } 

If I compile with scalac -Xprint:jvm Test.scala , I get this snippet suggesting that specialization is happening (sorry for the wide paste):

 package <empty> { final class Test extends java.lang.Object with ScalaObject { def main(args: Array[java.lang.String]): Unit = { val list: List = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 5, 15})); val res: List = list.map({ (new Test$$anonfun$1(): Function1) }, immutable.this.List.canBuildFrom()).$asInstanceOf[scala.collection.TraversableLike]().filter({ (new Test$$anonfun$2(): Function1) }).$asInstanceOf[List](); () }; def this(): object Test = { Test.super.this(); () } }; @SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp { final def apply(e: Int): Int = Test$$anonfun$1.this.apply$mcII$sp(e); <specialized> def apply$mcII$sp(v1: Int): Int = v1.*(2); final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Int.box(Test$$anonfun$1.this.apply(scala.Int.unbox(v1))); def this(): Test$$anonfun$1 = { Test$$anonfun$1.super.this(); () } }; @SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$2 extends scala.runtime.AbstractFunction1$mcZI$sp { final def apply(e: Int): Boolean = Test$$anonfun$2.this.apply$mcZI$sp(e); <specialized> def apply$mcZI$sp(v1: Int): Boolean = v1.>(10); final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Boolean.box(Test$$anonfun$2.this.apply(scala.Int.unbox(v1))); def this(): Test$$anonfun$2 = { Test$$anonfun$2.super.this(); () } } } 

Maybe why you don't see any evidence of boxing in bytecode ...

+1
source share

All Articles