From bfc6165b97d6b2af77255f958b4b8da07df5a90b Mon Sep 17 00:00:00 2001 From: "markkitt@gmail.com" Date: Tue, 15 Aug 2023 05:00:21 -0400 Subject: [PATCH] Improve @jcall macro --- src/core.jl | 2 ++ src/jcall_macro.jl | 35 +++++++++++++++++------------ test/jcall_macro.jl | 55 +++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 2 ++ 4 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 test/jcall_macro.jl diff --git a/src/core.jl b/src/core.jl index d04121a..9e51814 100644 --- a/src/core.jl +++ b/src/core.jl @@ -428,6 +428,8 @@ function jfield(ref, field::AbstractString) _jfield(_jcallable(ref), jfieldID, fieldType) end +jfield(ref, field::Symbol) = jfield(ref, String(field)) + function get_field_id(typ::Type{JavaObject{T}}, field::AbstractString, fieldType::Type) where T @checknull JNI.GetStaticFieldID(Ptr(metaclass(T)), String(field), signature(fieldType)) end diff --git a/src/jcall_macro.jl b/src/jcall_macro.jl index 3ae7087..e984ea3 100644 --- a/src/jcall_macro.jl +++ b/src/jcall_macro.jl @@ -3,18 +3,25 @@ macro jcall(expr) end function jcall_macro_lower(func, rettype, types, args, nreq) - @info "args: " func rettype types args nreq func.head func.args - obj = func.args[2] - if obj isa Expr && obj.head == :. - obj = quote - jfield($(obj.args[1]), string($(obj.args[2]))) - end + @debug "args: " func rettype types args nreq + jtypes = Expr(:tuple, esc.(types)...) + jargs = Expr(:tuple, esc.(args)...) + jret = esc(rettype) + if func isa Expr + @debug "func" func.head func.args + obj = resolve_dots(func.args[2]) + f = string(func.args[1].value) + return :(jcall($(esc(obj)), $f, $jret, $jtypes, ($jargs)...)) + elseif func isa QuoteNode + return :($(esc(func.value))($jtypes, ($jargs)...)) end - f = string(func.args[1].value) - jtypes = Expr(:tuple, types...) - jret = rettype - quote - jcall($(esc(obj)), $f, $jret, $jtypes, $args...) +end + +function resolve_dots(obj) + if obj isa Expr && obj.head == :. + return :(jfield($(resolve_dots(obj.args[1])), string($(obj.args[2])))) + else + return obj end end @@ -23,10 +30,10 @@ end jcall_macro_parse(expression) `jcall_macro_parse` is an implementation detail of `@jcall -it takes an expression like `:(printf("%d"::Cstring, value::Cuint)::Cvoid)` +it takes an expression like `:(System.out.println("Hello"::JString)::Nothing)` returns: a tuple of `(function_name, return_type, arg_types, args)` The above input outputs this: - (:printf, :Cvoid, [:Cstring, :Cuint], ["%d", :value]) + (:(System.out.println), Nothing, [:JString], ["Hello]) """ function jcall_macro_parse(expr::Expr) # setup and check for errors @@ -49,7 +56,7 @@ function jcall_macro_parse(expr::Expr) elseif f isa Symbol QuoteNode(f) else - throw(ArgumentError("@jcall function name must be a symbol, a `.` node (e.g. `libc.printf`) or an interpolated function pointer (with `\$`)")) + throw(ArgumentError("@jcall function name must be a symbol or a `.` node (e.g. `System.out.println`)")) end end diff --git a/test/jcall_macro.jl b/test/jcall_macro.jl new file mode 100644 index 0000000..44c3912 --- /dev/null +++ b/test/jcall_macro.jl @@ -0,0 +1,55 @@ +using Test +using JavaCall + +@testset "jcall macro" begin + JavaCall.isloaded() || JavaCall.init(["-Djava.class.path=$(@__DIR__)"]) + System = @jimport java.lang.System + version_from_macro = @jcall System.getProperty("java.version"::JString)::JString + version_from_func = jcall(System, "getProperty", JString, (JString,), "java.version") + @test version_from_macro == version_from_func + @test "bar" == @jcall System.getProperty("foo"::JString, "bar"::JString)::JString + @test 0x00 == @jcall System.out.checkError()::jboolean + rettype = jboolean + @test 0x00 == @jcall System.out.checkError()::rettype + jstr = JString + @test version_from_func == @jcall System.getProperty("java.version"::jstr)::jstr + + T = @jimport Test + @test 10 == @jcall T.testShort(10::jshort)::jshort + @test 10 == @jcall T.testInt(10::jint)::jint + @test 10 == @jcall T.testLong(10::jlong)::jlong + @test typemax(jint) == @jcall T.testInt(typemax(jint)::jint)::jint + @test typemax(jlong) == @jcall T.testLong(typemax(jlong)::jlong)::jlong + @test "Hello Java" == @jcall T.testString("Hello Java"::JString)::JString + @test Float64(10.02) == @jcall T.testDouble(10.02::jdouble)::jdouble + @test Float32(10.02) == @jcall T.testFloat(10.02::jfloat)::jfloat + @test floatmax(jdouble) == @jcall T.testDouble(floatmax(jdouble)::jdouble)::jdouble + @test floatmax(jfloat) == @jcall T.testFloat(floatmax(jfloat)::jfloat)::jfloat + c=JString(C_NULL) + @test isnull(c) + @test "" == @jcall T.testString(c::JString)::JString + a = rand(10^7) + @test [@jcall(T.testDoubleArray(a::Array{jdouble,1})::jdouble) + for i in 1:10][1] ≈ sum(a) + a = nothing + + jlm = @jimport "java.lang.Math" + @test 1.0 ≈ @jcall jlm.sin((pi/2)::jdouble)::jdouble + @test 1.0 ≈ @jcall jlm.min(1::jdouble, 2::jdouble)::jdouble + @test 1 == @jcall jlm.abs((-1)::jint)::jint + + @testset "jcall macro instance_methods_1" begin + jnu = @jimport java.net.URL + gurl = @jcall jnu("https://en.wikipedia.org"::JString)::jnu + @test "en.wikipedia.org"== @jcall gurl.getHost()::JString + jni = @jimport java.net.URI + guri = @jcall gurl.toURI()::jni + @test typeof(guri)==jni + + h=@jcall guri.hashCode()::jint + @test typeof(h)==jint + end + + jlist = @jimport java.util.ArrayList + @test 0x01 == @jcall jlist().add(JObject(C_NULL)::JObject)::jboolean +end diff --git a/test/runtests.jl b/test/runtests.jl index 3e67968..2234d60 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -438,6 +438,8 @@ end end end +include("jcall_macro.jl") + end # Test downstream dependencies