-
-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add prototype jcall macro * Improve @jcall macro * Remove nreq from macro, restore 1.0 compat
- Loading branch information
Showing
5 changed files
with
151 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
macro jcall(expr) | ||
return jcall_macro_lower(jcall_macro_parse(expr)...) | ||
end | ||
|
||
function jcall_macro_lower(func, rettype, types, args) | ||
@debug "args: " func rettype types args | ||
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 | ||
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 | ||
|
||
# @jcall implementation, based on Base.@ccall | ||
""" | ||
jcall_macro_parse(expression) | ||
`jcall_macro_parse` is an implementation detail of `@jcall | ||
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: | ||
(:(System.out.println), Nothing, [:JString], ["Hello]) | ||
""" | ||
function jcall_macro_parse(expr::Expr) | ||
# setup and check for errors | ||
if !Meta.isexpr(expr, :(::)) | ||
throw(ArgumentError("@jcall needs a function signature with a return type")) | ||
end | ||
rettype = expr.args[2] | ||
|
||
call = expr.args[1] | ||
if !Meta.isexpr(call, :call) | ||
throw(ArgumentError("@jcall has to take a function call")) | ||
end | ||
|
||
# get the function symbols | ||
func = let f = call.args[1] | ||
if Meta.isexpr(f, :.) | ||
:(($(f.args[2]), $(f.args[1]))) | ||
elseif Meta.isexpr(f, :$) | ||
f | ||
elseif f isa Symbol | ||
QuoteNode(f) | ||
else | ||
throw(ArgumentError("@jcall function name must be a symbol or a `.` node (e.g. `System.out.println`)")) | ||
end | ||
end | ||
|
||
# detect varargs | ||
varargs = nothing | ||
argstart = 2 | ||
callargs = call.args | ||
if length(callargs) >= 2 && Meta.isexpr(callargs[2], :parameters) | ||
argstart = 3 | ||
varargs = callargs[2].args | ||
end | ||
|
||
# collect args and types | ||
args = [] | ||
types = [] | ||
|
||
function pusharg!(arg) | ||
if !Meta.isexpr(arg, :(::)) | ||
throw(ArgumentError("args in @jcall need type annotations. '$arg' doesn't have one.")) | ||
end | ||
push!(args, arg.args[1]) | ||
push!(types, arg.args[2]) | ||
end | ||
|
||
for i in argstart:length(callargs) | ||
pusharg!(callargs[i]) | ||
end | ||
|
||
return func, rettype, types, args | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -438,6 +438,8 @@ end | |
end | ||
end | ||
|
||
include("jcall_macro.jl") | ||
|
||
end | ||
|
||
# Test downstream dependencies | ||
|
2b8c3f1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JuliaRegistrator register
2b8c3f1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Registration pull request created: JuliaRegistries/General/89671
After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.
This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via: