Skip to content

Commit caab713

Browse files
authored
Merge pull request #124 from mkitti/javaref
Create JavaRef, JavaLocalRef, and JavaGlobalRef to hold references and manage memory
2 parents 8d33e56 + 54c274e commit caab713

File tree

8 files changed

+243
-107
lines changed

8 files changed

+243
-107
lines changed

Project.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,18 @@ version = "0.7.4"
66
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
77
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
88
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
9-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
109
WinReg = "1b915085-20d7-51cf-bf83-8f477d6f5128"
1110

1211
[compat]
13-
julia = "1"
1412
DataStructures = "0.17"
1513
WinReg = "0.3.1"
14+
julia = "1"
15+
16+
[extras]
17+
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
18+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
19+
Taro = "61d0e4fa-4e73-5030-88a9-ae4c27b203dd"
20+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
21+
22+
[targets]
23+
test = ["Taro", "Pkg", "Test","DataFrames"]

src/JNI.jl

Lines changed: 71 additions & 59 deletions
Large diffs are not rendered by default.

src/JavaCall.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ using Dates
2626
end
2727

2828

29-
import Base: convert, unsafe_convert, unsafe_string
29+
import Base: convert, unsafe_convert, unsafe_string, Ptr
3030

3131
JULIA_COPY_STACKS = false
3232

src/convert.jl

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
1+
convert(::Type{Ptr{Nothing}}, obj::JavaObject{T}) where T = Ptr(obj)
2+
13
convert(::Type{JString}, str::AbstractString) = JString(str)
24
convert(::Type{JObject}, str::AbstractString) = convert(JObject, JString(str))
35

46
#Cast java object from S to T . Needed for polymorphic calls
57
function convert(::Type{JavaObject{T}}, obj::JavaObject{S}) where {T,S}
68
if isConvertible(T, S) #Safe static cast
7-
ptr = JNI.NewLocalRef(obj.ptr)
9+
ptr = JNI.NewLocalRef(Ptr(obj))
810
ptr === C_NULL && geterror()
911
return JavaObject{T}(ptr)
1012
end
1113
isnull(obj) && throw(ArgumentError("Cannot convert NULL"))
12-
realClass = JNI.GetObjectClass(obj.ptr)
14+
realClass = JNI.GetObjectClass(Ptr(obj))
1315
if isConvertible(T, realClass) #dynamic cast
14-
ptr = JNI.NewLocalRef(obj.ptr)
16+
ptr = JNI.NewLocalRef(Ptr(obj))
1517
ptr === C_NULL && geterror()
1618
return JavaObject{T}(ptr)
1719
end
1820
throw(JavaCallError("Cannot cast java object from $S to $T"))
1921
end
2022

2123
#Is java type convertible from S to T.
22-
isConvertible(T, S) = JNI.IsAssignableFrom(metaclass(S).ptr, metaclass(T).ptr) == JNI_TRUE
23-
isConvertible(T, S::Ptr{Nothing} ) = JNI.IsAssignableFrom(S, metaclass(T).ptr) == JNI_TRUE
24+
isConvertible(T, S) = JNI.IsAssignableFrom(Ptr(metaclass(S)), Ptr(metaclass(T))) == JNI_TRUE
25+
isConvertible(T, S::Ptr{Nothing} ) = JNI.IsAssignableFrom(S, Ptr(metaclass(T))) == JNI_TRUE
2426

25-
unsafe_convert(::Type{Ptr{Nothing}}, cls::JavaMetaClass) = cls.ptr
27+
unsafe_convert(::Type{Ptr{Nothing}}, cls::JavaMetaClass) = Ptr(cls)
28+
unsafe_convert(::Type{Ptr{Nothing}}, obj::JavaObject) = Ptr(obj)
29+
unsafe_convert(::Type{Ptr{Nothing}}, ref::JavaRef) = Ptr(ref)
2630

2731
# Get the JNI/C type for a particular Java type
2832
function real_jtype(rettype)
@@ -88,10 +92,10 @@ function convert_arg(argtype::Type{Array{T,1}}, arg) where T<:JavaObject
8892
carg = convert(argtype, arg)
8993
sz = length(carg)
9094
init = carg[1]
91-
arrayptr = JNI.NewObjectArray(sz, metaclass(T).ptr, init.ptr)
95+
arrayptr = JNI.NewObjectArray(sz, Ptr(metaclass(T)), Ptr(init))
9296
arrayptr === C_NULL && geterror()
9397
for i=2:sz
94-
JNI.SetObjectArrayElement(arrayptr, i-1, carg[i].ptr)
98+
JNI.SetObjectArrayElement(arrayptr, i-1, Ptr(carg[i]))
9599
end
96100
return carg, JavaObject{argtype}(arrayptr)
97101
end
@@ -258,10 +262,10 @@ end
258262
function unsafe_string(jstr::JString) #jstr must be a jstring obtained via a JNI call
259263
if isnull(jstr); return ""; end #Return empty string to keep type stability. But this is questionable
260264
pIsCopy = Array{jboolean}(undef, 1)
261-
#buf::Ptr{UInt8} = JNI.GetStringUTFChars(jstr.ptr, pIsCopy)
262-
buf = JNI.GetStringUTFChars(jstr.ptr, pIsCopy)
265+
#buf::Ptr{UInt8} = JNI.GetStringUTFChars(Ptr(jstr), pIsCopy)
266+
buf = JNI.GetStringUTFChars(Ptr(jstr), pIsCopy)
263267
s = unsafe_string(buf)
264-
JNI.ReleaseStringUTFChars(jstr.ptr, buf)
268+
JNI.ReleaseStringUTFChars(Ptr(jstr), buf)
265269
return s
266270
end
267271

@@ -275,11 +279,11 @@ for (x, y, z) in [(:jboolean, :(JNI.GetBooleanArrayElements), :(JNI.ReleaseBoole
275279
(:jdouble, :(JNI.GetDoubleArrayElements), :(JNI.ReleaseDoubleArrayElements)) ]
276280
m = quote
277281
function convert(::Type{Array{$(x),1}}, obj::JObject)
278-
sz = JNI.GetArrayLength(obj.ptr)
279-
arr = $y(obj.ptr, Ptr{jboolean}(C_NULL))
282+
sz = JNI.GetArrayLength(Ptr(obj))
283+
arr = $y(Ptr(obj), Ptr{jboolean}(C_NULL))
280284
jl_arr::Array = unsafe_wrap(Array, arr, Int(sz))
281285
jl_arr = deepcopy(jl_arr)
282-
$z(obj.ptr, arr, Int32(0))
286+
$z(Ptr(obj), arr, Int32(0))
283287
return jl_arr
284288
end
285289
end
@@ -288,10 +292,10 @@ end
288292

289293

290294
function convert(::Type{Array{T, 1}}, obj::JObject) where T
291-
sz = JNI.GetArrayLength(obj.ptr)
295+
sz = JNI.GetArrayLength(Ptr(obj))
292296
ret = Array{T}(undef, sz)
293297
for i=1:sz
294-
ptr = JNI.GetObjectArrayElement(obj.ptr, i-1)
298+
ptr = JNI.GetObjectArrayElement(Ptr(obj), i-1)
295299
ret[i] = convert(T, JObject(ptr))
296300
end
297301
return ret

src/core.jl

Lines changed: 118 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,99 @@
1-
struct JavaMetaClass{T}
1+
"""
2+
JavaRef is abstract parent for JavaLocalRef, JavaGlobalRef, and JavaNullRef in the JavaCall Module
3+
4+
It is distinct from its parent type, JavaCall.JNI.AbstractJavaRef, since its use is defined in
5+
JavaCall itself rather than the JNI submodule.
6+
"""
7+
abstract type JavaRef <: JNI.AbstractJavaRef end
8+
9+
"""
10+
JavaLocalRef is a JavaRef that is meant to be used with local variables in a function call.
11+
After the function call these references may be freed and garbage collected. See note about
12+
JNI memory management below.
13+
14+
This is the default reference type returned from the JNI.
15+
16+
Use this with JNI.PushLocalFrame / JNI.PopLocalFrame for memory management.
17+
Also see JNI.EnsureLocalCapacity.
18+
19+
The internal pointer should be deleted using JNI.DeleteLocalRef
20+
"""
21+
struct JavaLocalRef <: JavaRef
222
ptr::Ptr{Nothing}
323
end
424

5-
#The metaclass, sort of equivalent to a the
6-
JavaMetaClass(T, ptr) = JavaMetaClass{T}(ptr)
25+
"""
26+
JavaGlobalRef is a JavaRef that is meant to be used with global variables that live beyond
27+
a single function call.
28+
"""
29+
struct JavaGlobalRef <: JavaRef
30+
ptr::Ptr{Nothing}
31+
end
32+
33+
"""
34+
JavaNullRef is a JavaRef that serves as a placeholder to mark where references have already been deleted.
735
8-
mutable struct JavaObject{T}
36+
See J_NULL
37+
"""
38+
struct JavaNullRef <: JavaRef
939
ptr::Ptr{Nothing}
40+
JavaNullRef() = new(C_NULL)
41+
end
42+
43+
""" Constant JavaNullRef """
44+
const J_NULL = JavaNullRef()
45+
46+
Ptr(ref::JavaRef) = ref.ptr
47+
Ptr{Nothing}(ref::JavaRef) = ref.ptr
48+
49+
JavaLocalRef(ref::JavaRef) = JavaLocalRef(JNI.NewLocalRef(Ptr(ref)))
50+
JavaGlobalRef(ref::JavaRef) = JavaGlobalRef(JNI.NewGlobalRef(Ptr(ref)))
51+
52+
# _deleteref does local/global reference deletion without null or state checking
53+
_deleteref(ref::JavaLocalRef ) = JNI.DeleteLocalRef( Ptr(ref))
54+
_deleteref(ref::JavaGlobalRef) = JNI.DeleteGlobalRef(Ptr(ref))
55+
56+
"""
57+
deleteref deletes a JavaRef using either JNI.DeleteLocalRef or JNI.DeleteGlobalRef
58+
"""
59+
function deleteref(x::JavaRef)
60+
if x.ptr == C_NULL; return; end
61+
if !JNI.is_env_loaded(); return; end;
62+
_deleteref(x)
63+
return
64+
end
65+
66+
"""
67+
JavaMetaClass represents meta information about a Java class
68+
69+
These are usually cached in _jmc_cache and are meant to live
70+
as long as the cache is valid.
71+
"""
72+
struct JavaMetaClass{T} <: JNI.AbstractJavaRef
73+
ref::JavaRef
74+
end
75+
76+
#The metaclass, sort of equivalent to a the
77+
JavaMetaClass(T, ref::JavaRef) = JavaMetaClass{T}(ref)
78+
JavaMetaClass(T, ptr::Ptr{Nothing}) = JavaMetaClass{T}(JavaGlobalRef(ptr))
79+
80+
ref(mc::JavaMetaClass{T}) where T = mc.ref
81+
Ptr(mc::JavaMetaClass{T}) where T = Ptr(mc.ref)
82+
Ptr{Nothing}(mc::JavaMetaClass{T}) where T = Ptr(mc.ref)
83+
84+
"""
85+
JavaObject{T} is the main JavaCall type representing either an instance
86+
or a static class
87+
88+
T is usually a symbol referring a Java class name
89+
"""
90+
mutable struct JavaObject{T} <: JNI.AbstractJavaRef
91+
ref::JavaRef
1092

1193
#This below is ugly. Once we stop supporting 0.5, this can be replaced by
1294
# function JavaObject{T}(ptr) where T
13-
function JavaObject{T}(ptr) where T
14-
j = new{T}(ptr)
95+
function JavaObject{T}(ref) where T
96+
j = new{T}(ref)
1597
finalizer(deleteref, j)
1698
return j
1799
end
@@ -20,17 +102,28 @@ mutable struct JavaObject{T}
20102
JavaObject{T}(argtypes::Tuple, args...) where {T} = jnew(T, argtypes, args...)
21103
end
22104

105+
# JavaObject Construction
23106
JavaObject(T, ptr) = JavaObject{T}(ptr)
24107
JavaObject{T}() where {T} = JavaObject{T}((),)
108+
JavaObject{T}(ptr::Ptr{Nothing}) where {T} = JavaObject{T}(JavaLocalRef(ptr))
25109

26-
function deleteref(x::JavaObject)
27-
if x.ptr == C_NULL; return; end
28-
if !JNI.is_env_loaded(); return; end;
29-
JNI.DeleteLocalRef(x.ptr)
30-
x.ptr=C_NULL #Safety in case this function is called direcly, rather than at finalize
31-
return
32-
end
110+
# JavaObject Reference Management
111+
ref(x::JavaObject{T}) where T = x.ref
112+
copyref(x::JavaObject{T}) where T = JavaObject{T}(JavaLocalRef(x.ref))
113+
deleteref(x::JavaObject{T}) where T = ( deleteref(x.ref); x.ref = J_NULL )
114+
115+
# Obtain the underlying pointer for a JavaObject
116+
Ptr(x::JavaObject{T}) where T = Ptr(x.ref)
117+
Ptr{Nothing}(x::JavaObject{T}) where T = Ptr(x.ref)
33118

119+
"""
120+
jglobal(x::JavaObject) creates a new JavaGlobalRef and deletes the prior JavaRef
121+
"""
122+
function jglobal(x::JavaObject)
123+
gref = JavaGlobalRef(JNI.NewGlobalRef(Ptr(x)))
124+
deleteref(x.ref)
125+
x.ref = gref
126+
end
34127

35128
"""
36129
```
@@ -44,7 +137,7 @@ Checks if the passed JavaObject is null or not
44137
### Returns
45138
true if the passed object is null else false
46139
"""
47-
isnull(obj::JavaObject) = obj.ptr == C_NULL
140+
isnull(obj::JavaObject) = Ptr(obj) == C_NULL
48141
isnull(obj::Ptr{Nothing}) = obj == C_NULL
49142

50143
"""
@@ -59,7 +152,7 @@ Checks if the passed JavaMetaClass is null or not
59152
### Returns
60153
true if the passed object is null else false
61154
"""
62-
isnull(obj::JavaMetaClass) = obj.ptr == C_NULL
155+
isnull(obj::JavaMetaClass) = Ptr(obj) == C_NULL
63156

64157
const JClass = JavaObject{Symbol("java.lang.Class")}
65158
const JObject = JavaObject{Symbol("java.lang.Object")}
@@ -82,7 +175,7 @@ jvalue(v::Integer)::JNI.jvalue = JNI.jvalue(v)
82175
jvalue(v::Float32) = jvalue(reinterpret(Int32, v))
83176
jvalue(v::Float64) = jvalue(reinterpret(Int64, v))
84177
jvalue(v::Ptr) = jvalue(Int(v))
85-
jvalue(v::JavaObject) = jvalue(v.ptr)
178+
jvalue(v::JavaObject) = jvalue(Ptr(v))
86179

87180

88181
function _jimport(juliaclass)
@@ -108,7 +201,7 @@ end
108201
function jnew(T::Symbol, argtypes::Tuple, args...)
109202
assertroottask_or_goodenv()
110203
sig = method_signature(Nothing, argtypes...)
111-
jmethodId = JNI.GetMethodID(metaclass(T).ptr, String("<init>"), sig)
204+
jmethodId = JNI.GetMethodID(Ptr(metaclass(T)), String("<init>"), sig)
112205
if jmethodId == C_NULL
113206
throw(JavaCallError("No constructor for $T with signature $sig"))
114207
end
@@ -120,7 +213,7 @@ function jcall(typ::Type{JavaObject{T}}, method::AbstractString, rettype::Type,
120213
args... ) where T
121214
assertroottask_or_goodenv()
122215
sig = method_signature(rettype, argtypes...)
123-
jmethodId = JNI.GetStaticMethodID(metaclass(T).ptr, String(method), sig)
216+
jmethodId = JNI.GetStaticMethodID(Ptr(metaclass(T)), String(method), sig)
124217
jmethodId==C_NULL && geterror(true)
125218
_jcall(metaclass(T), jmethodId, C_NULL, rettype, argtypes, args...)
126219
end
@@ -129,21 +222,21 @@ end
129222
function jcall(obj::JavaObject, method::AbstractString, rettype::Type, argtypes::Tuple, args... )
130223
assertroottask_or_goodenv()
131224
sig = method_signature(rettype, argtypes...)
132-
jmethodId = JNI.GetMethodID(metaclass(obj).ptr, String(method), sig)
225+
jmethodId = JNI.GetMethodID(Ptr(metaclass(obj)), String(method), sig)
133226
jmethodId==C_NULL && geterror(true)
134227
_jcall(obj, jmethodId, C_NULL, rettype, argtypes, args...)
135228
end
136229

137230
function jfield(typ::Type{JavaObject{T}}, field::AbstractString, fieldType::Type) where T
138231
assertroottask_or_goodenv()
139-
jfieldID = JNI.GetStaticFieldID(metaclass(T).ptr, String(field), signature(fieldType))
232+
jfieldID = JNI.GetStaticFieldID(Ptr(metaclass(T)), String(field), signature(fieldType))
140233
jfieldID==C_NULL && geterror(true)
141234
_jfield(metaclass(T), jfieldID, fieldType)
142235
end
143236

144237
function jfield(obj::JavaObject, field::AbstractString, fieldType::Type)
145238
assertroottask_or_goodenv()
146-
jfieldID = JNI.GetFieldID(metaclass(obj).ptr, String(field), signature(fieldType))
239+
jfieldID = JNI.GetFieldID(Ptr(metaclass(obj)), String(field), signature(fieldType))
147240
jfieldID==C_NULL && geterror(true)
148241
_jfield(obj, jfieldID, fieldType)
149242
end
@@ -160,7 +253,7 @@ for (x, y, z) in [(:jboolean, :(JNI.GetBooleanField), :(JNI.GetStaticBooleanFiel
160253
m = quote
161254
function _jfield(obj, jfieldID::Ptr{Nothing}, fieldType::Type{$(x)})
162255
callmethod = ifelse( typeof(obj)<:JavaObject, $y , $z )
163-
result = callmethod(obj.ptr, jfieldID)
256+
result = callmethod(Ptr(obj), jfieldID)
164257
result==C_NULL && geterror()
165258
return convert_result(fieldType, result)
166259
end
@@ -170,7 +263,7 @@ end
170263

171264
function _jfield(obj, jfieldID::Ptr{Nothing}, fieldType::Type)
172265
callmethod = ifelse( typeof(obj)<:JavaObject, JNI.GetObjectField , JNI.GetStaticObjectField )
173-
result = callmethod(obj.ptr, jfieldID)
266+
result = callmethod(Ptr(obj), jfieldID)
174267
result==C_NULL && geterror()
175268
return convert_result(fieldType, result)
176269
end
@@ -197,7 +290,7 @@ for (x, y, z) in [(:jboolean, :(JNI.CallBooleanMethodA), :(JNI.CallStaticBoolean
197290
isnull(obj) && throw(JavaCallError("Attempt to call method on Java NULL"))
198291
savedArgs, convertedArgs = convert_args(argtypes, args...)
199292
GC.@preserve savedArgs begin
200-
result = callmethod(obj.ptr, jmethodId, Array{JNI.jvalue}(jvalue.(convertedArgs)))
293+
result = callmethod(Ptr(obj), jmethodId, Array{JNI.jvalue}(jvalue.(convertedArgs)))
201294
end
202295
deleteref.(filter(x->isa(x,JavaObject),convertedArgs))
203296
result==C_NULL && geterror()
@@ -224,7 +317,7 @@ function _jcall(obj, jmethodId::Ptr{Nothing}, callmethod::Union{Function,Ptr{Not
224317
isnull(obj) && error("Attempt to call method on Java NULL")
225318
savedArgs, convertedArgs = convert_args(argtypes, args...)
226319
GC.@preserve savedArgs begin
227-
result = callmethod(obj.ptr, jmethodId, Array{JNI.jvalue}(jvalue.(convertedArgs)))
320+
result = callmethod(Ptr(obj), jmethodId, Array{JNI.jvalue}(jvalue.(convertedArgs)))
228321
end
229322
deleteref.(filter(x->isa(x,JavaObject),convertedArgs))
230323
result==C_NULL && geterror()

src/jnienv.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct JNINativeInterface #struct JNINativeInterface_ {
1515
FromReflectedField::Ptr{Nothing} #jfieldID ( *FromReflectedField)(JNIEnv *env, jobject field);
1616
ToReflectedMethod::Ptr{Nothing} #jobject ( *ToReflectedMethod) (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);
1717

18-
GetSuperClass::Ptr{Nothing} #jclass ( *GetSuperclass) (JNIEnv *env, jclass sub);
18+
GetSuperclass::Ptr{Nothing} #jclass ( *GetSuperclass) (JNIEnv *env, jclass sub);
1919
IsAssignableFrom::Ptr{Nothing} #jboolean ( *IsAssignableFrom) (JNIEnv *env, jclass sub, jclass sup);
2020

2121
ToReflectedField::Ptr{Nothing} #jobject ( *ToReflectedField)(JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);

src/make_jni2.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ function decl_arg_type(t, s)
4141
end
4242
elseif t == "jsize" #|| t == "jint" || t == "jlong" || t == "jshort" || t == "jbyte"
4343
return Integer
44+
elseif t == "jobject"
45+
return "jobject_arg"
46+
elseif t == "jobjectArray"
47+
return "jobjectArray_arg"
4448
end
4549

4650
if t == "void"

0 commit comments

Comments
 (0)