Skip to content

Commit

Permalink
Add prototype jcall macro
Browse files Browse the repository at this point in the history
  • Loading branch information
mkitti committed Mar 21, 2022
1 parent 63026e4 commit 28dc646
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/JavaCall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export JavaObject, JavaMetaClass, JNIVector,
jint, jlong, jbyte, jboolean, jchar, jshort, jfloat, jdouble, jvoid,
JObject, JClass, JMethod, JConstructor, JField, JString,
JavaRef, JavaLocalRef, JavaGlobalRef, JavaNullRef,
@jimport, jcall, jfield, jlocalframe, isnull,
@jimport, @jcall, jcall, jfield, jlocalframe, isnull,
getname, getclass, listmethods, getreturntype, getparametertypes, classforname,
listfields, gettype,
narrow
Expand Down Expand Up @@ -40,6 +40,7 @@ include("core.jl")
include("convert.jl")
include("reflect.jl")
include("jniarray.jl")
include("jcall_macro.jl")

Base.@deprecate_binding jnifunc JavaCall.JNI.jniref[]

Expand Down
95 changes: 95 additions & 0 deletions src/jcall_macro.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
macro jcall(expr)
return jcall_macro_lower(jcall_macro_parse(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
end
f = string(func.args[1].value)
jtypes = Expr(:tuple, types...)
jret = rettype
quote
jcall($(esc(obj)), $f, $jret, $jtypes, $args...)
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 `:(printf("%d"::Cstring, value::Cuint)::Cvoid)`
returns: a tuple of `(function_name, return_type, arg_types, args)`
The above input outputs this:
(:printf, :Cvoid, [:Cstring, :Cuint], ["%d", :value])
"""
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, a `.` node (e.g. `libc.printf`) or an interpolated function pointer (with `\$`)"))
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
# Do we need this in JavaCall?
# add any varargs if necessary
nreq = 0
if !isnothing(varargs)
if length(args) == 0
throw(ArgumentError("C ABI prohibits vararg without one required argument"))
end
nreq = length(args)
for a in varargs
pusharg!(a)
end
end

return func, rettype, types, args, nreq
end

0 comments on commit 28dc646

Please sign in to comment.