I wonder how variables marked as final are interpreted by Groovy (in 1.8.0, 1.8.1). I know that it makes sense in Java and it is possible to improve the performance and -- of course -- help to avoid stupid mistakes. I would like to learn if final may help the java compiler to optimize a program written in Groovy. I wonder if Groovy transformers preserve the final markings for variables.
It doesn't appear that groovyc will inline final variables the way javac does. I created two test scripts, one using final and one not:
final String message = "Hello World"
println message
String message = "Hello World"
println message
javap -c produced the same output for both classes:
0: invokestatic #18; //Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
3: astore_1
4: ldc #58; //String Hello World
6: astore_2
7: aload_1
8: ldc #59; //int 1
10: aaload
11: aload_0
12: aload_2
13: invokeinterface #63, 3; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;Ljava/lang/Object;)Ljava/lang/Object;
18: areturn
19: nop
javac optimized away the astore/aload:
Without final:
0: ldc #2; //String Hello World
2: astore_1
3: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
With final:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String Hello World
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
All that said, if performance is paramount, Groovy is a poor choice to begin with. Inlining final variables won't save you from the overhead of using reflection for method calls.
As Justin has said, if the optimisations that the compiler performs for final variables are important to you, then you shouldn't be using Groovy.
However, if the performance of Groovy is good enough, then it is still useful to mark variables final for two reasons:
Protecting your class' invariants, i.e. making sure that a value cannot be changed after object construction. Java enforces this at compile-time, Groovy only enforces this at runtime, but this is better than silently allowing an immutable value to be changed
Documentation. Users of your class can easily see which values they are allowed to change
Original post: Bug is open
not yet. but it might in a future, so I still mark them if appropriate.
https://issues.apache.org/jira/browse/GROOVY-1628
Update post: Bug is "Fixed" in 2.5.x
GROOVY-1628 - Inconsistent checking of final has been marked as "Fixed" on 2015-03-13 with "Fix Version/s" set to "2.5.0-beta-1".
Related
I am reading the programming for the Java Virtual Machine by Joshua Engel, and I tried typing in one of the examples in the book. I understand that the book uses Oolong, and I am using Jasmin, however for such a simple example, and because the syntax of Oolong and Jasmin are so similar, I don't see why this doesn't work. As a side note, I haven't been able to find anything about the difference in syntax between Oolong and Jasmin. Could anyone point me towards a resource like this?
Here's the code, exactly as it is in the book:
.class Test
.method static run()I
bipush 9
bipush 6
imul
ireturn
.end method
And when I run Jasmin on this, I get the following errors:
Test.j:2: Warning - Syntax error.
.method
^
Test.j:2: Error - Couldn't repair and continue parse.
.method
^
So what is it that I'm doing wrong?
Jasmin requires .super keyword after .class.
The following code should compile fine:
.class Test
.super java/lang/Object
.method static run()I
bipush 9
bipush 6
imul
ireturn
.end method
I have a following code snippet in Kotlin:
val pair: Pair<Char,Char> = 'z' to 'z'
val comparison = pair.first.compareTo(pair.second)
println(comparison)
It fails at the second line with the following exception when I try to run it:
java.lang.ClassCastException: java.lang.Character cannot be cast to java.lang.Number
IDE (IntelliJ) doesn't complain about any wrong type. The issue is somehow related to the fact that Chars are coming from Pair<Char, Char> because 'z'.compareTo('z') works fine. Do you know how Kotlin resolves the following call?
I'm using Kotlin 1.0.4
TL;DR Apparently, this is a compiler bug.
The reason for this behavior lies in the bytecode that the Kotlin compiler generates for these two calls. (If you use IntelliJ IDEA, you can inspect the bytecode using the bytecode viewing tool).
First, the bytecode generated for the 'z'.compareTo('z') call is:
LINENUMBER 10 L3
BIPUSH 122
BIPUSH 122
INVOKESTATIC kotlin/jvm/internal/Intrinsics.compare (II)I
It calls kotlin.jvm.internal.Intrisics.compare() that compares two Ints, and the Chars are pushed to the stack directly as Ints (BIPUSH means push byte as integer).
But if you look at the bytecode for pair.first.compareTo(pair.second), you will find something like this:
ALOAD 1
INVOKEVIRTUAL kotlin/Pair.getFirst ()Ljava/lang/Object;
CHECKCAST java/lang/Number
INVOKEVIRTUAL java/lang/Number.intValue ()I
ALOAD 1
INVOKEVIRTUAL kotlin/Pair.getSecond ()Ljava/lang/Object;
CHECKCAST java/lang/Number
INVOKEVIRTUAL java/lang/Number.intValue ()I
INVOKESTATIC kotlin/jvm/internal/Intrinsics.compare (II)I
It calls kotlin.jvm.internal.Intrisics.compare, too, but here's what it tries to do before:
Get the component from the pair (the ALOAD 1 and INVOKEVIRTUAL ... lines)
Check that the object can be cast to Number (CHECKCAST ...)
Take java.lang.Number.intValue() (INVOKEVIRTUAL ...)
The second and the third lines are the culprit, Char is not a Number. It simply looks like the compiler generated incorrect bytecode for this comparison (it would be correct for the Number types, seems like Char is just not handled separately).
There is an issue about this in the Kotlin issue tracker, it will likely be fixed in future releases.
To fix the call in your code for now, you can convert the Chars manually before the call:
pair.first.toInt().compareTo(pair.second.toInt())
use java::util::zip::CRC32:from<java>;
my $crc = CRC32.new();
for 'Hello, Java'.encode('utf-8') {
$crc.'method/update/(B)V'($_);
}
say $crc.getValue();
sadly, this does not work
Method 'method/update/(B)V' not found for invocant of class 'java.util.zip.CRC32'
This code is available at the following links. It is the only example I've been able to find
Rakudo Perl 6 on the JVM (slides)
Perl 6 Advent Calendar: Day 03 – Rakudo Perl 6 on the JVM
Final answer
Combining the code cleanups explained in the Your answer cleaned up section below with Pepe Schwarz's improvements mentioned in the Expectation alert section below we get:
use java::util::zip::CRC32:from<Java>;
my $crc = CRC32.new();
for 'Hello, Java'.encode('utf-8').list {
$crc.update($_);
}
say $crc.getValue();
Your answer cleaned up
use v6;
use java::util::zip::CRC32:from<Java>;
my $crc = CRC32.new();
for 'Hello, Java'.encode('utf-8').list { # Appended `.list`
$crc.'method/update/(I)V'($_);
}
say $crc.getValue();
One important changed bit is the appended .list.
The 'Hello, Java'.encode('utf-8') fragment returns an object, a utf8. That object returns just one value (itself) to the for statement. So the for iterates just once, passing the object to the code block with the update line in it.
Iterating just once could make sense if the update line was .'method/update/([B)V', which maps to a Java method that expects a buffer of 8 bit ints, which is essentially what a Perl 6 utf8 is. However, that would require some support Perl 6 code (presumably in the core compiler) to marshal (automagically convert) the Perl 6 utf8 into a Java buf[] and if that code ever existed/worked it sure isn't working when I test with the latest Rakudo.
But if one appends a judicious .list as shown above and changes the code block to match, things work out.
First, the .list results in the for statement iterating over a series of integers.
Second, like you, I called the Integer arg version of the Java method (.'method/update/(I)V') instead of the original buffer arg version and the code then worked correctly. (This means that the binary representation of the unsigned 8 bit integers returned from the Perl 6 utf8 object is either already exactly what the Java method expects or is automagically marshaled for you.)
Another required change is that the from<java> needs to be from<Java> per your comment below -- thanks.
Expectation alert
As of Jan 2015:
Merely using the JVM backend for Rakudo/NQP (i.e. running pure P6 code on a JVM) still needs more hardening before it can be officially declared ready for production use. (This is in addition to the all round hardening that the entire P6 ecosystem is expected to undergo this year.) The JVM backend will hopefully get there in 2015 -- it will hopefully be part of the initial official launch of Perl 6 being ready for production use this year -- but that's going to largely depend on demand and on there being more devs using it and contributing patches.
P6 code calling Java code is an additional project. Pepe Schwarz has made great progress in the last couple months in getting up to speed, learning the codebase and landing commits. He has already implemented the obviously nicer shortname calling shown at the start of this answer and completed a lot more of the marshaling logic for converting between P6 and Java types and is actively soliciting feedback and requests for specific improvements.
The code which is responsible for this area of Java interop is found in the class org.perl6.nqp.runtime.BootJavaInterop. It suggests that the overloaded methods are identified by the string method/<name>/<descriptor>. The descriptor is computed in function org.objectweb.asm.Type#getMethodDescriptor. That jar is available through maven from http://mvnrepository.com/artifact/asm/asm.
import java.util.zip.CRC32
import org.objectweb.asm.Type
object MethodSignatures {
def printSignature(cls: Class[_], method: String, params: Class[_]): Unit = {
val m = cls.getMethod(method, params)
val d = Type.getMethodDescriptor(m)
println(m)
println(s"\t$d")
}
def main(args: Array[String]) {
val cls = classOf[CRC32]
# see https://docs.oracle.com/javase/8/docs/api/java/util/zip/CRC32.html
val ab = classOf[Array[Byte]]
val i = classOf[Int]
printSignature(cls, "update", ab)
printSignature(cls, "update", i)
}
}
This prints
public void java.util.zip.CRC32.update(byte[])
([B)V
public void java.util.zip.CRC32.update(int)
(I)V
Since I want to call the update(int) variant of this overloaded method, the correct method invocation (on line 5 of the example program) is
$crc.'method/update/(I)V'($_);
This crashes with
This representation can not unbox to a native int
finally, for some reason I do not understand, changing the same line to
$crc.'method/update/(I)V'($_.Int);
fixes that and the example runs fine.
The final version of the code is
use v6;
use java::util::zip::CRC32:from<java>;
my $crc = CRC32.new();
for 'Hello, Java'.encode('utf-8') {
$crc.'method/update/(I)V'($_.Int);
}
say $crc.getValue();
I got this to work on Perl 6.c with following modification (Jan 4, 2018):
use v6;
use java::util::zip::CRC32:from<JavaRuntime>;
my $crc = CRC32.new();
for 'Hello, Java'.encode('utf-8').list {
$crc.update($_);
}
say $crc.getValue();
Resulting in:
% perl6-j --version
This is Rakudo version 2017.12-79-g6f36b02 built on JVM
implementing Perl 6.c.
% perl6-j crcjava.p6
1072431491
I filed the following bug on September 28th, 2009. Sadly, I still did not get any response and the final version of the specification still is incorrect. Is this really a bug? If not, why not? If yes, what should I do?
The section that contains the bug is 5.4.5 (Method overriding): http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5 in combination with the description of the INVOKEVIRTUAL opcode: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokevirtual
According to 5.4.5 m1 can override m2 even if m1 is private. This can happen if creating .class files manually or combining the .class from two compilations.
In my example I have classes A and B with B extends A. I compiled these classes so that A contains a public method named f and B contains a private method, also named f (by first declaring both methods public, compiling, copying A.class to a safe place, removing the declaration of f in A and changing to private in B, then compile B and using the saved version of A.class).
When now running this, my current Oracle JVM outputs A (meaning the method f in A is invoked). According to the specification, B should be the output (meaning the method f in B should be invoked).
EDIT: Actually, B.f should be resolved. Invocation may fail because of access right checks for the resolved method, if the caller is not B. However, I believe the method resolution part is wrong.
I think that the definition in 5.4.5 should check the access rights of m1, not only m2.
public class A {
public void f();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String A
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
public class B extends A {
private void f();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String B
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
Thanks,
Carsten
Your issue has been addressed, finally. The current version of the Java 8 JVM specification contains the required clarification:
5.4.5 Overriding
An instance method mC declared in class C overrides another instance method mA
declared in class A iff either mC is the same as mA, or all of the following are true:
C is a subclass of A.
mC has the same name and descriptor as mA.
mC is not marked ACC_PRIVATE.
One of the following is true:
mA is marked ACC_PUBLIC; or is marked ACC_PROTECTED; or is marked neither
ACC_PUBLIC nor ACC_PROTECTED nor ACC_PRIVATE and A belongs to the same
run-time package as C.
mC overrides a method m' (m' distinct from mC and mA) such that m' overrides mA.
There is another addition in §4.10.1.5 “Type Checking Abstract and Native Methods”:
private methods and static methods are orthogonal to dynamic method dispatch,
so they never override other methods (§5.4.5).
Less than five years for a fix, that’s fast compared to some other issues…
The docs say to use f.call.apply(arguments), but that seems to only work for object methods, not functions.
testapply.dart:
#!/usr/bin/env dart
say(a, b, c) {
print("${a}!");
print("${b}!");
print("${c}!");
}
main() {
var args = [1, 2, 3];
say.call.apply(args);
}
Trace:
$ dart testapply.dart
'/Users/andrew/Desktop/testapply.dart': Error: line 11 pos 2: Unresolved identifier 'Function 'say': static.'
say.call.apply(args);
^
Is there a way to do LISP (apply f args) without using objects?
Alternatively, is there a way to dynamically wrap an arbitrary function in an object so that it can be applied using f.call.apply(arguments)?
Alternatively, can Dart curry?
The documentation page you refer to says,
This functionality is not yet implemented, but is specified as part of version 0.07 of the Dart Programming Language Specification. Hopefully it will find its way into our implementations in due course, though it may be quite a while.
That may or may not be the issue...