final class Box {
private[this] final var TMP:Object = null
final def put(o:Object) {
TMP = o
}
final def get() = {
val tmp = TMP
TMP = null
tmp
}
}
class Client(box:Box) {
var count = 0
final def run(cycles:Int) {
if (cycles > 0) {
box.put(new Object)
if (box.get() != null) count += 1
run(cycles - 1)
}
}
}
object Main {
def main(args:Array[String]) {
val c = new Client(new Box)
val N = 1000000000
c.run(N)
assert(c.count == N, c.count)
}
}
$ java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) Server VM (build 20.1-b02, mixed mode)
$ scala -version
Scala code runner version 2.9.1.final -- Copyright 2002-2011, LAMP/EPFL
$ sbt run
[error] (run-main) java.lang.AssertionError: assertion failed: 999982178
java.lang.AssertionError: assertion failed: 999982178
at scala.Predef$.assert(Predef.scala:103)
at Main$.main(Bug.scala:33)
at Main.main(Bug.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
java.lang.RuntimeException: Nonzero exit code: 1
at scala.sys.package$.error(package.scala:27)
This issue is reproducible 100% of the time and the test passes if I
omit the 'final' modifier for TMP.
So, who is the culprit? JVM or Scala?
Thanks,
Sébastien
The only difference in the byte code (at least as printed by javap) is the final modifier on the field, and removing that fixes it.
Regards,
Roland
Roland Kuhn
Typesafe – The software stack for applications that scale.
twitter: @rolandkuhn
unfortunately, the JVM does allow that. This is used upon deserialization where no constructor is called and where the final fields have to be set. With generated bytecode it is possible to write to final fields after object construction. See Cliff Click's blog post at [1]. He also mentions that code may "behave differently" when the JIT kicks in.
> I haven't checked the JVM spec, but it seems it shouldn't accept bytecode that assigns to a final variable outside of a constructor
Dominik
[1] http://www.azulsystems.com/blog/cliff/2011-10-17-writing-to-final-fields-via-reflection
Roland Kuhn
Typesafe – The software stack for applications that scale.
twitter: @rolandkuhn
See java.lang.System.setOut/In/Err for examples of methods that modify final fields.
Yeah it is not using reflection, but does modify final fields (System.out/in/err). Just another example of the fun that can be had.
There seems to be a mismatch between spec and compiler as to what
final means on mutable variables. The compiler does not let you
override a mutable variable, but the spec does not have that
restriction. So we need to clear that up at some point.
But my fix works independently of the resolution of this problem.
Cheers
-- Martin
--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967