Skip to content

Commit e4e76d8

Browse files
authored
Merge pull request #120 from mkitti/independent_JNI
To summarize the net changes: 1. Library loading and core JVM initialization, and destroy are moved into the JNI module. This means __init__() no longer loads the dynamic library. This is entirely done on init() 2. penv is no longer a global in JavaCall and passed by default to ccall. All JNI calls from JavaCall do not require a JNIEnv pointer to be passed since that is handled entirely within JNI module now. penv can still be passed in as a named argument. 3. JavaCall.findjvm() just returns a tuple of Strings describing the library or libraries to load rather than setting a global. 4. Tests no longer use ccall directly but now refer to the wrapped versions in the JNI module. As a side effect and for unknown reasons, JavaCall.init() and some functions now work on x86 Windows. This is somehow related to storing jnienv as a global.
2 parents b6ed1e3 + ef12eb7 commit e4e76d8

File tree

9 files changed

+509
-459
lines changed

9 files changed

+509
-459
lines changed

appveyor.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
environment:
22
matrix:
3+
- julia_version: 1.0
4+
platform: x64 # 64-bit
35
- julia_version: 1
4-
- julia_version: 1.3
5-
- julia_version: 1.4
6+
platform: x64 # 64-bit
7+
- julia_version: 1
8+
JAVA_HOME: C:\Program Files (x86)\Java\jdk1.8.0
9+
platform: x86 # 32-bit
610
- julia_version: nightly
7-
8-
platform:
9-
- x86 # 32-bit
10-
- x64 # 64-bit
11+
platform: x64 # 64-bit
1112

1213
# # Uncomment the following lines to allow failures on nightly julia
1314
# # (tests will run but not make your overall status red)

src/JNI.jl

Lines changed: 425 additions & 341 deletions
Large diffs are not rendered by default.

src/JavaCall.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export JavaObject, JavaMetaClass,
1111
# using Sys: iswindows, islinux, isunix, isapple
1212

1313
import DataStructures: OrderedSet
14-
import Libdl
1514
using Dates
1615

1716
@static if Sys.iswindows()
@@ -30,6 +29,8 @@ include("core.jl")
3029
include("convert.jl")
3130
include("reflect.jl")
3231

32+
Base.@deprecate_binding jnifunc JavaCall.JNI.jniref[]
33+
3334
function __init__()
3435
global JULIA_COPY_STACKS = get(ENV, "JULIA_COPY_STACKS", "") ("1", "yes")
3536
if ! Sys.iswindows()
@@ -44,8 +45,6 @@ function __init__()
4445
"Calling the JVM may result in undefined behavior.")
4546
end
4647
end
47-
findjvm()
48-
global create = Libdl.dlsym(libjvm, :JNI_CreateJavaVM)
4948
end
5049

5150

src/convert.jl

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,23 @@ convert(::Type{JObject}, str::AbstractString) = convert(JObject, JString(str))
44
#Cast java object from S to T . Needed for polymorphic calls
55
function convert(::Type{JavaObject{T}}, obj::JavaObject{S}) where {T,S}
66
if isConvertible(T, S) #Safe static cast
7-
ptr = JNI.NewLocalRef(penv, obj.ptr)
7+
ptr = JNI.NewLocalRef(obj.ptr)
88
ptr === C_NULL && geterror()
99
return JavaObject{T}(ptr)
1010
end
1111
isnull(obj) && throw(ArgumentError("Cannot convert NULL"))
12-
realClass = JNI.GetObjectClass(penv, obj.ptr)
12+
realClass = JNI.GetObjectClass(obj.ptr)
1313
if isConvertible(T, realClass) #dynamic cast
14-
ptr = JNI.NewLocalRef(penv, obj.ptr)
14+
ptr = JNI.NewLocalRef(obj.ptr)
1515
ptr === C_NULL && geterror()
1616
return JavaObject{T}(ptr)
1717
end
1818
throw(JavaCallError("Cannot cast java object from $S to $T"))
1919
end
2020

2121
#Is java type convertible from S to T.
22-
isConvertible(T, S) = JNI.IsAssignableFrom(penv, metaclass(S).ptr, metaclass(T).ptr) == JNI_TRUE
23-
isConvertible(T, S::Ptr{Nothing} ) = JNI.IsAssignableFrom(penv, S, metaclass(T).ptr) == JNI_TRUE
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
2424

2525
unsafe_convert(::Type{Ptr{Nothing}}, cls::JavaMetaClass) = cls.ptr
2626

@@ -71,8 +71,8 @@ for (x, y, z) in [(:jboolean, :(JNI.NewBooleanArray), :(JNI.SetBooleanArrayRegio
7171
function convert_arg(argtype::Type{Array{$x,1}}, arg)
7272
carg = convert(argtype, arg)
7373
sz=length(carg)
74-
arrayptr = $y(penv, sz)
75-
$z(penv, arrayptr, 0, sz, carg)
74+
arrayptr = $y(sz)
75+
$z(arrayptr, 0, sz, carg)
7676
return carg, arrayptr
7777
end
7878
end
@@ -83,9 +83,9 @@ function convert_arg(argtype::Type{Array{T,1}}, arg) where T<:JavaObject
8383
carg = convert(argtype, arg)
8484
sz = length(carg)
8585
init = carg[1]
86-
arrayptr = JNI.NewObjectArray(penv, sz, metaclass(T).ptr, init.ptr)
86+
arrayptr = JNI.NewObjectArray(sz, metaclass(T).ptr, init.ptr)
8787
for i=2:sz
88-
JNI.SetObjectArrayElement(penv, arrayptr, i-1, carg[i].ptr)
88+
JNI.SetObjectArrayElement(arrayptr, i-1, carg[i].ptr)
8989
end
9090
return carg, arrayptr
9191
end
@@ -104,24 +104,24 @@ for (x, y, z) in [(:jboolean, :(JNI.GetBooleanArrayElements), :(JNI.ReleaseBoole
104104
(:jdouble, :(JNI.GetDoubleArrayElements), :(JNI.ReleaseDoubleArrayElements)) ]
105105
m = quote
106106
function convert_result(rettype::Type{Array{$(x),1}}, result)
107-
sz = JNI.GetArrayLength(penv, result)
108-
arr = $y(penv, result, Ptr{jboolean}(C_NULL))
107+
sz = JNI.GetArrayLength(result)
108+
arr = $y(result, Ptr{jboolean}(C_NULL))
109109
jl_arr::Array = unsafe_wrap(Array, arr, Int(sz))
110110
jl_arr = deepcopy(jl_arr)
111-
$z(penv, result, arr, Int32(0))
111+
$z(result, arr, Int32(0))
112112
return jl_arr
113113
end
114114
end
115115
eval(m)
116116
end
117117

118118
function convert_result(rettype::Type{Array{JavaObject{T},1}}, result) where T
119-
sz = JNI.GetArrayLength(penv, result)
119+
sz = JNI.GetArrayLength(result)
120120

121121
ret = Array{JavaObject{T}}(undef, sz)
122122

123123
for i=1:sz
124-
a=JNI.GetObjectArrayElement(penv, result, i-1)
124+
a=JNI.GetObjectArrayElement(result, i-1)
125125
ret[i] = JavaObject{T}(a)
126126
end
127127
return ret
@@ -130,33 +130,33 @@ end
130130

131131
# covers return types like Vector{Vector{T}}
132132
function convert_result(rettype::Type{Array{T,1}}, result) where T
133-
sz = JNI.GetArrayLength(penv, result)
133+
sz = JNI.GetArrayLength(result)
134134

135135
ret = Array{T}(undef, sz)
136136

137137
for i=1:sz
138-
a=JNI.GetObjectArrayElement(penv, result, i-1)
138+
a=JNI.GetObjectArrayElement(result, i-1)
139139
ret[i] = convert_result(T, a)
140140
end
141141
return ret
142142
end
143143

144144

145145
function convert_result(rettype::Type{Array{JavaObject{T},2}}, result) where T
146-
sz = JNI.GetArrayLength(penv, result)
146+
sz = JNI.GetArrayLength(result)
147147
if sz == 0
148148
return Array{T}(undef, 0,0)
149149
end
150-
a_1 = JNI.GetObjectArrayElement(penv, result, 0)
151-
sz_1 = JNI.GetArrayLength(penv, a_1)
150+
a_1 = JNI.GetObjectArrayElement(result, 0)
151+
sz_1 = JNI.GetArrayLength(a_1)
152152
ret = Array{JavaObject{T}}(undef, sz, sz_1)
153153
for i=1:sz
154-
a = JNI.GetObjectArrayElement(penv, result, i-1)
154+
a = JNI.GetObjectArrayElement(result, i-1)
155155
# check that size of the current subarray is the same as for the first one
156-
sz_a = JNI.GetArrayLength(penv, a)
156+
sz_a = JNI.GetArrayLength(a)
157157
@assert(sz_a == sz_1, "Size of $(i)th subrarray is $sz_a, but size of the 1st subarray was $sz_1")
158158
for j=1:sz_1
159-
x = JNI.GetObjectArrayElement(penv, a, j-1)
159+
x = JNI.GetObjectArrayElement(a, j-1)
160160
ret[i, j] = JavaObject{T}(x)
161161
end
162162
end
@@ -166,17 +166,17 @@ end
166166

167167
# matrices of primitive types and other arrays
168168
function convert_result(rettype::Type{Array{T,2}}, result) where T
169-
sz = JNI.GetArrayLength(penv, result)
169+
sz = JNI.GetArrayLength(result)
170170
if sz == 0
171171
return Array{T}(undef, 0,0)
172172
end
173-
a_1 = JNI.GetObjectArrayElement(penv, result, 0)
174-
sz_1 = JNI.GetArrayLength(penv, a_1)
173+
a_1 = JNI.GetObjectArrayElement(result, 0)
174+
sz_1 = JNI.GetArrayLength(a_1)
175175
ret = Array{T}(undef, sz, sz_1)
176176
for i=1:sz
177-
a = JNI.GetObjectArrayElement(penv, result, i-1)
177+
a = JNI.GetObjectArrayElement(result, i-1)
178178
# check that size of the current subarray is the same as for the first one
179-
sz_a = JNI.GetArrayLength(penv, a)
179+
sz_a = JNI.GetArrayLength(a)
180180
@assert(sz_a == sz_1, "Size of $(i)th subrarray is $sz_a, but size of the 1st subarray was $sz_1")
181181
ret[i, :] = convert_result(Vector{T}, a)
182182
end
@@ -252,10 +252,10 @@ end
252252
function unsafe_string(jstr::JString) #jstr must be a jstring obtained via a JNI call
253253
if isnull(jstr); return ""; end #Return empty string to keep type stability. But this is questionable
254254
pIsCopy = Array{jboolean}(undef, 1)
255-
#buf::Ptr{UInt8} = JNI.GetStringUTFChars(penv, jstr.ptr, pIsCopy)
256-
buf = JNI.GetStringUTFChars(penv, jstr.ptr, pIsCopy)
255+
#buf::Ptr{UInt8} = JNI.GetStringUTFChars(jstr.ptr, pIsCopy)
256+
buf = JNI.GetStringUTFChars(jstr.ptr, pIsCopy)
257257
s = unsafe_string(buf)
258-
JNI.ReleaseStringUTFChars(penv, jstr.ptr, buf)
258+
JNI.ReleaseStringUTFChars(jstr.ptr, buf)
259259
return s
260260
end
261261

@@ -269,11 +269,11 @@ for (x, y, z) in [(:jboolean, :(JNI.GetBooleanArrayElements), :(JNI.ReleaseBoole
269269
(:jdouble, :(JNI.GetDoubleArrayElements), :(JNI.ReleaseDoubleArrayElements)) ]
270270
m = quote
271271
function convert(::Type{Array{$(x),1}}, obj::JObject)
272-
sz = JNI.GetArrayLength(penv, obj.ptr)
273-
arr = $y(penv, obj.ptr, Ptr{jboolean}(C_NULL))
272+
sz = JNI.GetArrayLength(obj.ptr)
273+
arr = $y(obj.ptr, Ptr{jboolean}(C_NULL))
274274
jl_arr::Array = unsafe_wrap(Array, arr, Int(sz))
275275
jl_arr = deepcopy(jl_arr)
276-
$z(penv, obj.ptr, arr, Int32(0))
276+
$z(obj.ptr, arr, Int32(0))
277277
return jl_arr
278278
end
279279
end
@@ -282,10 +282,10 @@ end
282282

283283

284284
function convert(::Type{Array{T, 1}}, obj::JObject) where T
285-
sz = JNI.GetArrayLength(penv, obj.ptr)
285+
sz = JNI.GetArrayLength(obj.ptr)
286286
ret = Array{T}(undef, sz)
287287
for i=1:sz
288-
ptr = JNI.GetObjectArrayElement(penv, obj.ptr, i-1)
288+
ptr = JNI.GetObjectArrayElement(obj.ptr, i-1)
289289
ret[i] = convert(T, JObject(ptr))
290290
end
291291
return ret

src/core.jl

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ JavaObject{T}() where {T} = JavaObject{T}((),)
2525

2626
function deleteref(x::JavaObject)
2727
if x.ptr == C_NULL; return; end
28-
if (penv==C_NULL); return; end
29-
JNI.DeleteLocalRef(penv, x.ptr)
28+
if !JNI.is_env_loaded(); return; end;
29+
JNI.DeleteLocalRef(x.ptr)
3030
x.ptr=C_NULL #Safety in case this function is called direcly, rather than at finalize
3131
return
3232
end
@@ -69,7 +69,7 @@ const JClassLoader = JavaObject{Symbol("java.lang.ClassLoader")}
6969
const JString = JavaObject{Symbol("java.lang.String")}
7070

7171
function JString(str::AbstractString)
72-
jstring = JNI.NewStringUTF(penv, String(str))
72+
jstring = JNI.NewStringUTF(String(str))
7373
if jstring == C_NULL
7474
geterror()
7575
else
@@ -107,7 +107,7 @@ end
107107
function jnew(T::Symbol, argtypes::Tuple, args...)
108108
assertroottask_or_goodenv()
109109
sig = method_signature(Nothing, argtypes...)
110-
jmethodId = JNI.GetMethodID(penv, metaclass(T).ptr, String("<init>"), sig)
110+
jmethodId = JNI.GetMethodID(metaclass(T).ptr, String("<init>"), sig)
111111
if jmethodId == C_NULL
112112
throw(JavaCallError("No constructor for $T with signature $sig"))
113113
end
@@ -119,7 +119,7 @@ function jcall(typ::Type{JavaObject{T}}, method::AbstractString, rettype::Type,
119119
args... ) where T
120120
assertroottask_or_goodenv()
121121
sig = method_signature(rettype, argtypes...)
122-
jmethodId = JNI.GetStaticMethodID(penv, metaclass(T).ptr, String(method), sig)
122+
jmethodId = JNI.GetStaticMethodID(metaclass(T).ptr, String(method), sig)
123123
jmethodId==C_NULL && geterror(true)
124124
_jcall(metaclass(T), jmethodId, C_NULL, rettype, argtypes, args...)
125125
end
@@ -128,21 +128,21 @@ end
128128
function jcall(obj::JavaObject, method::AbstractString, rettype::Type, argtypes::Tuple, args... )
129129
assertroottask_or_goodenv()
130130
sig = method_signature(rettype, argtypes...)
131-
jmethodId = JNI.GetMethodID(penv, metaclass(obj).ptr, String(method), sig)
131+
jmethodId = JNI.GetMethodID(metaclass(obj).ptr, String(method), sig)
132132
jmethodId==C_NULL && geterror(true)
133133
_jcall(obj, jmethodId, C_NULL, rettype, argtypes, args...)
134134
end
135135

136136
function jfield(typ::Type{JavaObject{T}}, field::AbstractString, fieldType::Type) where T
137137
assertroottask_or_goodenv()
138-
jfieldID = JNI.GetStaticFieldID(penv, metaclass(T).ptr, String(field), signature(fieldType))
138+
jfieldID = JNI.GetStaticFieldID(metaclass(T).ptr, String(field), signature(fieldType))
139139
jfieldID==C_NULL && geterror(true)
140140
_jfield(metaclass(T), jfieldID, fieldType)
141141
end
142142

143143
function jfield(obj::JavaObject, field::AbstractString, fieldType::Type)
144144
assertroottask_or_goodenv()
145-
jfieldID = JNI.GetFieldID(penv, metaclass(obj).ptr, String(field), signature(fieldType))
145+
jfieldID = JNI.GetFieldID(metaclass(obj).ptr, String(field), signature(fieldType))
146146
jfieldID==C_NULL && geterror(true)
147147
_jfield(obj, jfieldID, fieldType)
148148
end
@@ -159,7 +159,7 @@ for (x, y, z) in [(:jboolean, :(JNI.GetBooleanField), :(JNI.GetStaticBooleanFiel
159159
m = quote
160160
function _jfield(obj, jfieldID::Ptr{Nothing}, fieldType::Type{$(x)})
161161
callmethod = ifelse( typeof(obj)<:JavaObject, $y , $z )
162-
result = callmethod(penv, obj.ptr, jfieldID)
162+
result = callmethod(obj.ptr, jfieldID)
163163
result==C_NULL && geterror()
164164
return convert_result(fieldType, result)
165165
end
@@ -169,7 +169,7 @@ end
169169

170170
function _jfield(obj, jfieldID::Ptr{Nothing}, fieldType::Type)
171171
callmethod = ifelse( typeof(obj)<:JavaObject, JNI.GetObjectField , JNI.GetStaticObjectField )
172-
result = callmethod(penv, obj.ptr, jfieldID)
172+
result = callmethod(obj.ptr, jfieldID)
173173
result==C_NULL && geterror()
174174
return convert_result(fieldType, result)
175175
end
@@ -195,7 +195,7 @@ for (x, y, z) in [(:jboolean, :(JNI.CallBooleanMethodA), :(JNI.CallStaticBoolean
195195
@assert jmethodId != C_NULL
196196
isnull(obj) && throw(JavaCallError("Attempt to call method on Java NULL"))
197197
savedArgs, convertedArgs = convert_args(argtypes, args...)
198-
result = callmethod(penv, obj.ptr, jmethodId, convertedArgs)
198+
result = callmethod(obj.ptr, jmethodId, convertedArgs)
199199
result==C_NULL && geterror()
200200
result == nothing && (return)
201201
return convert_result(rettype, result)
@@ -219,7 +219,7 @@ function _jcall(obj, jmethodId::Ptr{Nothing}, callmethod::Union{Function,Ptr{Not
219219
@assert jmethodId != C_NULL
220220
isnull(obj) && error("Attempt to call method on Java NULL")
221221
savedArgs, convertedArgs = convert_args(argtypes, args...)
222-
result = callmethod(penv, obj.ptr, jmethodId, convertedArgs)
222+
result = callmethod(obj.ptr, jmethodId, convertedArgs)
223223
result==C_NULL && geterror()
224224
return convert_result(rettype, result)
225225
end
@@ -229,7 +229,7 @@ global const _jmc_cache = Dict{Symbol, JavaMetaClass}()
229229

230230
function _metaclass(class::Symbol)
231231
jclass=javaclassname(class)
232-
jclassptr = JNI.FindClass(penv, jclass)
232+
jclassptr = JNI.FindClass(jclass)
233233
jclassptr == C_NULL && throw(JavaCallError("Class Not Found $jclass"))
234234
return JavaMetaClass(class, jclassptr)
235235
end
@@ -247,21 +247,21 @@ metaclass(::JavaObject{T}) where {T} = metaclass(T)
247247
javaclassname(class::Symbol) = replace(string(class), "."=>"/")
248248

249249
function geterror(allow=false)
250-
isexception = JNI.ExceptionCheck(penv)
250+
isexception = JNI.ExceptionCheck()
251251

252252
if isexception == JNI_TRUE
253-
jthrow = JNI.ExceptionOccurred(penv)
253+
jthrow = JNI.ExceptionOccurred()
254254
jthrow==C_NULL && throw(JavaCallError("Java Exception thrown, but no details could be retrieved from the JVM"))
255-
JNI.ExceptionDescribe(penv ) #Print java stackstrace to stdout
256-
JNI.ExceptionClear(penv )
257-
jclass = JNI.FindClass(penv, "java/lang/Throwable")
255+
JNI.ExceptionDescribe() #Print java stackstrace to stdout
256+
JNI.ExceptionClear()
257+
jclass = JNI.FindClass("java/lang/Throwable")
258258
jclass==C_NULL && throw(JavaCallError("Java Exception thrown, but no details could be retrieved from the JVM"))
259-
jmethodId=JNI.GetMethodID(penv, jclass, "toString", "()Ljava/lang/String;")
259+
jmethodId=JNI.GetMethodID(jclass, "toString", "()Ljava/lang/String;")
260260
jmethodId==C_NULL && throw(JavaCallError("Java Exception thrown, but no details could be retrieved from the JVM"))
261-
res = JNI.CallObjectMethodA(penv, jthrow, jmethodId, Int[])
261+
res = JNI.CallObjectMethodA(jthrow, jmethodId, Int[])
262262
res==C_NULL && throw(JavaCallError("Java Exception thrown, but no details could be retrieved from the JVM"))
263263
msg = unsafe_string(JString(res))
264-
JNI.DeleteLocalRef(penv, jthrow)
264+
JNI.DeleteLocalRef(jthrow)
265265
throw(JavaCallError(string("Error calling Java: ",msg)))
266266
else
267267
if allow==false

src/jnienv.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ JNINativeInterface() = JNINativeInterface(repeat([C_NULL],233)...)
309309
struct JNIEnv
310310
JNINativeInterface_::Ptr{JNINativeInterface}
311311
end
312+
JNIEnv() = JNIEnv(C_NULL)
312313

313314
struct JNIInvokeInterface #struct JNIInvokeInterface_ {
314315
reserved0::Ptr{Nothing} #void *reserved0;
@@ -325,6 +326,7 @@ struct JNIInvokeInterface #struct JNIInvokeInterface_ {
325326

326327
AttachCurrentThreadAsDaemon::Ptr{Nothing} #jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args);
327328
end
329+
JNIInvokeInterface() = JNIInvokeInterface(repeat([C_NULL],8)...)
328330

329331
struct JavaVM
330332
JNIInvokeInterface_::Ptr{JNIInvokeInterface}

0 commit comments

Comments
 (0)