How do I invoke a Java method from perl6
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
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();