Skip to content

Commit 6405ef3

Browse files
ahnlabbabreadjoaocmdmkitti
authored
Raw JNI arrays (#144)
* Create unsafe _jnicall to simplify error checks * add JNIArray, returned java arrays w/o conversion * Basic mutable JNIArray functionality * Better interface, finalizer, fixes * add conversion and basic tests * Add support for short to conventional_name Co-authored-by: João David <[email protected]> * replace `isnothing` for backwards compatibility * Use src/core.jl from master branch * Rename JNIArray to JNIVector in case Java ever gets builtin nd arrays Co-authored-by: André Breda <[email protected]> Co-authored-by: João David <[email protected]> Co-authored-by: [email protected] <[email protected]>
1 parent bf9ae91 commit 6405ef3

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

src/JavaCall.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module JavaCall
77
Base.Experimental.@optlevel 1
88
end
99

10-
export JavaObject, JavaMetaClass,
10+
export JavaObject, JavaMetaClass, JNIVector,
1111
jint, jlong, jbyte, jboolean, jchar, jshort, jfloat, jdouble, jvoid,
1212
JObject, JClass, JMethod, JConstructor, JField, JString,
1313
JavaRef, JavaLocalRef, JavaGlobalRef, JavaNullRef,
@@ -39,6 +39,7 @@ include("jvm.jl")
3939
include("core.jl")
4040
include("convert.jl")
4141
include("reflect.jl")
42+
include("jniarray.jl")
4243

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

src/jniarray.jl

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
jniname(::Type{jboolean}) = "Boolean"
2+
jniname(::Type{jbyte}) = "Byte"
3+
jniname(::Type{jchar}) = "Char"
4+
jniname(::Type{jshort}) = "Short"
5+
jniname(::Type{jint}) = "Int"
6+
jniname(::Type{jlong}) = "Long"
7+
jniname(::Type{jfloat}) = "Float"
8+
jniname(::Type{jdouble}) = "Double"
9+
10+
11+
mutable struct JNIVector{T} <: AbstractVector{T}
12+
ref::JavaRef
13+
arr::Union{Nothing,Vector{T}}
14+
function JNIVector{T}(ref) where T
15+
j = new{T}(ref, nothing)
16+
finalizer(deleteref, j)
17+
return j
18+
end
19+
end
20+
21+
Base.getindex(jarr::JNIVector, args...) = getindex(jarr.arr, args...)
22+
Base.setindex!(jarr::JNIVector, args...) = setindex!(jarr.arr, args...)
23+
Base.size(jarr::JNIVector, args...; kwargs...) = size(jarr.arr, args...; kwargs...)
24+
25+
function deleteref(x::JNIVector{T}) where T
26+
if x.arr !== nothing
27+
release_elements(x)
28+
end
29+
deleteref(x.ref)
30+
x.ref = J_NULL
31+
end
32+
33+
signature(::Type{JNIVector{T}}) where T = string("[", signature(T))
34+
jvalue(jarr::JNIVector) = jarr.ref.ptr
35+
JNIVector{T}(ptr::Ptr{Nothing}) where {T} = JNIVector{T}(JavaLocalRef(ptr))
36+
37+
function convert(::Type{JNIVector{T}}, vec::Vector{T}) where {T}
38+
arr = JNIVector{T}(length(vec))
39+
arr .= vec
40+
return arr
41+
end
42+
43+
JNIVector(vec::Vector{T}) where {T} = convert(JNIVector{T}, vec)
44+
45+
for primitive in [:jboolean, :jchar, :jbyte, :jshort, :jint, :jlong, :jfloat, :jdouble]
46+
name = jniname(eval(primitive))
47+
get_elements = :(JNI.$(Symbol("Get$(name)ArrayElements")))
48+
release_elements = :(JNI.$(Symbol("Release$(name)ArrayElements")))
49+
new_array = :(JNI.$(Symbol("New$(name)Array")))
50+
m = quote
51+
function get_elements!(jarr::JNIVector{$primitive})
52+
sz = Int(JNI.GetArrayLength(jarr.ref.ptr))
53+
jarr.arr = unsafe_wrap(Array, $get_elements(jarr.ref.ptr, Ptr{jboolean}(C_NULL)), sz)
54+
jarr
55+
end
56+
JNIVector{$primitive}(sz::Int) = get_elements!(JNIVector{$primitive}($new_array(sz)))
57+
function release_elements(arg::JNIVector{$primitive})
58+
$release_elements(arg.ref.ptr, pointer(arg.arr), jint(0))
59+
arg.arr = nothing
60+
end
61+
function convert_result(::Type{JNIVector{$primitive}}, ptr)
62+
get_elements!(JNIVector{$primitive}(ptr))
63+
end
64+
function convert_arg(argtype::Type{Vector{$primitive}}, arg::JNIVector{$primitive})
65+
release_elements(arg)
66+
return arg, arg
67+
end
68+
function convert_arg(argtype::Type{JNIVector{$primitive}}, arg::JNIVector{$primitive})
69+
release_elements(arg)
70+
return arg, arg
71+
end
72+
function cleanup_arg(jarr::JNIVector{$primitive})
73+
get_elements!(jarr)
74+
end
75+
end
76+
eval(m)
77+
end

test/runtests.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,27 @@ end
226226
@test size(jcall(T, "testStringArray2D", Array{JString,2}, ())) == (2,2)
227227
end
228228

229+
@testset "jni_arrays_1" begin
230+
j_u_arrays = @jimport java.util.Arrays
231+
arr = jint[10,20,30,40,50,60]
232+
jniarr = JNIVector(arr)
233+
@test length(arr) == length(jniarr)
234+
@test size(arr) == size(jniarr)
235+
@test all(arr .== jniarr)
236+
@test 3 == jcall(j_u_arrays, "binarySearch", jint, (JNIVector{jint}, jint), jniarr, 40)
237+
@test "[10, 20, 30, 40, 50, 60]" == jcall(j_u_arrays, "toString", JString, (JavaCall.JNIVector{jint},), jniarr)
238+
239+
JCharBuffer = @jimport(java.nio.CharBuffer)
240+
buf = jcall(JCharBuffer, "wrap", JCharBuffer, (JNIVector{jchar},), JNIVector(jchar.(collect("array"))))
241+
@test "array" == jcall(buf, "toString", JString, ())
242+
243+
# Ensure JNIVectors are garbage collected properly
244+
for i in 1:100000
245+
a = JNIVector(jchar[j == i ? 0 : 1 for j in 1:10000])
246+
buf = jcall(JCharBuffer, "wrap", JCharBuffer, (JNIVector{jchar},), a)
247+
end
248+
end
249+
229250
@testset "dates_1" begin
230251
jd = @jimport(java.util.Date)(())
231252
jcal = @jimport(java.util.GregorianCalendar)(())

0 commit comments

Comments
 (0)