Open yuvaldeg opened 4 years ago
Scala is relatively new, will investigate
In reality it has an object (i.e. the kernel accesses an object). I've created following simple test program:
package boxed
class Logic {
def run(): Unit = {
var result = 5
val closure = () => {
result += 1
}
closure()
println(result)
}
}
object Main {
def main(args: Array[String]): Unit = {
new Logic().run()
}
}
Here's the decompiled output:
$ javap -c target/scala-2.12/classes/boxed/Logic.class
Compiled from "Main.scala"
public class boxed.Logic {
public void run();
Code:
0: iconst_5
1: invokestatic #21 // Method scala/runtime/IntRef.create:(I)Lscala/runtime/IntRef;
4: astore_1
5: aload_1
6: invokedynamic #42, 0 // InvokeDynamic #0:apply$mcV$sp:(Lscala/runtime/IntRef;)Lscala/runtime/java8/JFunction0$mcV$sp;
11: astore_2
12: aload_2
13: invokeinterface #46, 1 // InterfaceMethod scala/Function0.apply$mcV$sp:()V
18: getstatic #52 // Field scala/Predef$.MODULE$:Lscala/Predef$;
21: aload_1
22: getfield #56 // Field scala/runtime/IntRef.elem:I
25: invokestatic #62 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
28: invokevirtual #66 // Method scala/Predef$.println:(Ljava/lang/Object;)V
31: return
public static final void $anonfun$run$1(scala.runtime.IntRef);
Code:
0: aload_0
1: aload_0
2: getfield #56 // Field scala/runtime/IntRef.elem:I
5: iconst_1
6: iadd
7: putfield #56 // Field scala/runtime/IntRef.elem:I
10: return
public boxed.Logic();
Code:
0: aload_0
1: invokespecial #76 // Method java/lang/Object."<init>":()V
4: return
}
As you see, it created an instance of IntRef
and uses it as a container for var result: Int
. The reason is that in Java platform you can't access different stack frame. In Java language you can do something that looks like accessing different stack frame, but you're restricted to effectively final variables. The way it works is that Java copies the values to new object, so then they can be accessed safely. In case of mutable variables you can't use the same trick, as modifying one copy would render the other copy stale and invalid. Therefore you need to box any stack allocated value and place it on managed heap. Then you can safely share an immutable copy of reference to that mutable object.
If you look here https://github.com/scala/scala/tree/2.13.x/src/library/scala/runtime then you'll see plenty of mutable or lazy boxed primitive types. Maybe you could detect that and provide more informative exception message? Like, detect that object type is from scala.runtime
package and then show message like: "Detected object of class from scala.runtime package. Did you use boxed primitive from within kernel?"
Consider this simple test case that has no Java objects in the Kernel:
Code generation still fails, saying: "Using java objects inside kernels is not supported", with this stacktrace: