Skip to content

Commit

Permalink
Improve @jcall macro
Browse files Browse the repository at this point in the history
  • Loading branch information
mkitti committed Aug 15, 2023
1 parent 28dc646 commit bfc6165
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 14 deletions.
2 changes: 2 additions & 0 deletions src/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 21 additions & 14 deletions src/jcall_macro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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

Expand Down
55 changes: 55 additions & 0 deletions test/jcall_macro.jl
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,8 @@ end
end
end

include("jcall_macro.jl")

end

# Test downstream dependencies
Expand Down

0 comments on commit bfc6165

Please sign in to comment.