-
-
Notifications
You must be signed in to change notification settings - Fork 51
JProxy update #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JProxy update #112
Conversation
created init_current_vm function
Adding new conversions to convert.jl
array arg conversion did not work for empty arrays adding JConstructor
need to remove references to JavaObject from proxy.jl
## Major TODOs and Caveats | ||
* Currently, only a low level interface is available, via `jcall`. As a result, this package is best suited for writing libraries that wrap around existing java packages. Writing user code direcly using this interface might be a bit tedious at present. A high level interface using reflection will eventually be built. |
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.
This section of the documentation should probably be rewritten
src/JavaCall.jl
Outdated
@jimport, jcall, jfield, isnull, | ||
getname, getclass, listmethods, getreturntype, getparametertypes, classforname, | ||
narrow | ||
narrow, JProxy, @class, interfacehas, staticproxy |
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.
I'm not sure JProxy is the best name here. Might be worth brainstorming a few alternatives
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.
Maybe "JavaObject", because you use it like a Java object?
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.
Yes, that is the most relevant name, the only trouble is that we already have a JavaObject type in JavaCall, so we'll have figure out a transition. However, I think the macro should be called @java
.
test/runtests.jl
Outdated
import Dates | ||
using Base.GC: gc | ||
|
||
const pxyptr = JavaCall.pxyptr |
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.
doesnt seem used below?
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.
Correct, I had used that in a println in an earlier version of the test code and I forgot to remove it
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.
These tests will be moved to a separate sub-package.
|
||
location(source) = replace(string(source.file), r"^.*/([^/]*)$" => s"\1") * ":" * string(source.line) * ": " | ||
|
||
macro verbose(args...) |
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.
These seem debug aids. Are they needed now? In general, we should use the Base logging for output.
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.
yes, @verbose is just for debugging the project itself and should eventually be removed or commented out. I left some of the logging commented out so I could uncomment it at need (and I did have to uncomment some of them sometimes :) )
jstr.ptr, buf) | ||
jstr, buf) | ||
return s | ||
end |
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.
@aviks Could we incorporate the changes to this file convert.jl into JavaCall proper?
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.
Sure. Is the current code likely a bug?
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.
I think it was just quoted out of context. What I meant is that most of the changes to convert.jl seem acceptable to merge. It will have to be updated for the new JNI module though.
src/core.jl
Outdated
jprimitive = Union{jboolean, jchar, jshort, jfloat, jdouble, jint, jlong} | ||
|
||
struct JavaMetaClass{T} | ||
mutable struct JavaMetaClass{T} |
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.
Why is it necessary to change this to a mutable struct? I haven't look very hard for the answer, but it might save time if someone could explain it.
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.
Might it make sense to have mutable and unmutable versions of JavaMetaClass?
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.
I'll look around -- I'm pretty sure I had a good reason for that. But maybe not :)
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.
Because the finalizer on line 84 nulls out ptr for safety. If we can guarantee that deleteref will only ever be called from finalize, it can stay immutable I think.
src/jvm.jl
Outdated
jvm = unsafe_load(pjvm) | ||
global jvmfunc = unsafe_load(jvm.JNIInvokeInterface_) | ||
global jnifunc = unsafe_load(jnienv.JNINativeInterface_) #The JNI Function table | ||
initProxy() |
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.
We should revert the changes to this file for now.
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.
Looks like init_current_vm() needs initProxy() as well -- unless you're going to remove that call from _init()
|
||
public String testArrayArgs(Object[] i) { | ||
return "java.lang.Object[]"; | ||
} |
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.
The changes to Test.java and the binaries should be reverted for now and moved into a separate sub-package.
src/convert.jl
Outdated
convert(::AbstractString, str::Type{JString}) = unsafe_string(str) | ||
convert(::Type{JString}, str::AbstractString) = JString(str) | ||
convert(::Type{JObject}, str::AbstractString) = convert(JObject, JString(str)) | ||
convert(::Type{JavaObject{Symbol("java.lang.Double")}}, n::Real) = jnew(Symbol("java.lang.Double"), (jdouble,), Float64(n)) | ||
convert(::Type{JavaObject{Symbol("java.lang.Float")}}, n::Real) = jnew(Symbol("java.lang.Float"), (jfloat,), Float32(n)) | ||
convert(::Type{JavaObject{Symbol("java.lang.Long")}}, n::Real) = jnew(Symbol("java.lang.Long"), (jlong,), Int64(n)) | ||
convert(::Type{JavaObject{Symbol("java.lang.Integer")}}, n::Real) = jnew(Symbol("java.lang.Integer"), (jint,), Int32(n)) | ||
convert(::Type{JavaObject{Symbol("java.lang.Short")}}, n::Real) = jnew(Symbol("java.lang.Short"), (jshort,), Int16(n)) | ||
convert(::Type{JavaObject{Symbol("java.lang.Byte")}}, n::Real) = jnew(Symbol("java.lang.Byte"), (jbyte,), Int8(n)) | ||
convert(::Type{JavaObject{Symbol("java.lang.Character")}}, n::Real) = jnew(Symbol("java.lang.Character"), (jchar,), Char(n)) | ||
convert(::Type{JavaObject{:int}}, n) = convert(jint, n) | ||
convert(::Type{JavaObject{:long}}, n) = convert(jlong, n) | ||
convert(::Type{JavaObject{:byte}}, n) = convert(jbyte, n) | ||
convert(::Type{JavaObject{:boolean}}, n) = convert(jboolean, n) | ||
convert(::Type{JavaObject{:char}}, n) = convert(jchar, n) | ||
convert(::Type{JavaObject{:short}}, n) = convert(jshort, n) | ||
convert(::Type{JavaObject{:float}}, n) = convert(jfloat, n) | ||
convert(::Type{JavaObject{:double}}, n) = convert(jdouble, n) | ||
convert(::Type{JavaObject{:void}}, n) = convert(jvoid, n) | ||
convert(::Type{JavaObject{T}}, ::Nothing) where T = jnull |
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.
These convert methods seem fine to merge.
function convert(::Type{JavaObject{T}}, obj::JavaObject{S}) where {T,S} | ||
if isConvertible(T, S) #Safe static cast | ||
ptr = ccall(jnifunc.NewLocalRef, Ptr{Nothing}, (Ptr{JNIEnv}, Ptr{Nothing}), penv, obj.ptr) | ||
ptr === C_NULL && geterror() | ||
return JavaObject{T}(ptr) | ||
if isnull(obj) | ||
return JavaObject{T}(obj.ptr) | ||
elseif isConvertible(T, S) #Safe static cast | ||
return JavaObject{T}(obj.ptr) | ||
end | ||
isnull(obj) && throw(ArgumentError("Cannot convert NULL")) | ||
realClass = ccall(jnifunc.GetObjectClass, Ptr{Nothing}, (Ptr{JNIEnv}, Ptr{Nothing} ), penv, obj.ptr) | ||
if isConvertible(T, realClass) #dynamic cast | ||
ptr = ccall(jnifunc.NewLocalRef, Ptr{Nothing}, (Ptr{JNIEnv}, Ptr{Nothing}), penv, obj.ptr) | ||
ptr === C_NULL && geterror() | ||
return JavaObject{T}(ptr) | ||
return JavaObject{T}(obj.ptr) | ||
end | ||
throw(JavaCallError("Cannot cast java object from $S to $T")) | ||
end |
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.
This allows for null objects. We could probably merge this.
src/core.jl
Outdated
const JString = JavaObject{Symbol("java.lang.String")} | ||
const jnull = JavaObject(nothing) | ||
|
||
JavaObject(ptr::Ptr{Nothing}) = ptr == C_NULL ? JavaObject(ptr) : JavaObject{Symbol(getclassname(getclass(ptr)))}(ptr) |
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.
This depends on functions in proxy.jl at the moment, so we will probably move it into proxy.jl
Folks, do you need any more information from me in order to tie this off? |
Hi @zot, thanks for checking in. JavaCall has undergone some major structural changes since a few weeks ago, so this will need to be updated again. Next on my list is to improve JavaCall's local/global reference management. However, I think the reorganization will make things much more manageable moving forward. For example, all the Do we have your permission to continue developing this? My plan remains making this a separate package which has JavaCall as a dependency. |
Of course! JavaCall belongs to you guys -- I wrote the proxy code in order to contribute to the project. Use and modify it as you see fit, I'd just like to see it merged in one form or another... |
@zot , @aviks I split the proxy code off into its own
The easiest way I found to load |
JProxies/doc/index.md
Outdated
* Create static proxies to access static members | ||
Primitive types and strings are converted to Julia objects on field accesses and method returns and converted back to Java types when sent as arguments to Java methods. | ||
*NOTE: Because of this, if you need to call Java methods on a string that you got from Java, you'll have to use `JProxy(str)` to convert the Julia string to a proxied Java string* | ||
To invoke static methods, set static to true (see below). |
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.
I wrote this before I added the @class
macro -- I think it should change to this:
To invoke static methods, use @class (see below).
JProxies/doc/index.md
Outdated
julia> JProxy(@jimport(java.lang.System)).getName() | ||
"java.lang.System" | ||
julia> JProxy(@jimport(java.lang.System);static=true).out.println("hello") |
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.
I'd change this to
julia> @class(java.lang.System).out.println("hello")
Can we just make this a submodule for the JavaCall module, so doing |
We could do that although the direction I was heading in was to spin it out so that it can live as its own package. Currently, you could import using JProxies
JProxies.init() Under the hood that will still use JavaCall. You made a point once that there was a lot of the proxy code to support which could make future maintenance more difficult. Dividing the code clearly like this is beneficial for maintainability I think. There is still some code I actually want to move into JavaCall itself. For example, |
That sounds like a good direction to me. That way people who just want to use JavaCall don't need to load in JProxy and people who want JProxy don't need to worry about JavaCall.
Yes, I wrote some useful utility code that could move into JavaCall but I wanted to keep my changes to the base package to a minimum so I kept as much as I could in proxy.jl. |
Is this dying on the vine? |
Most of the changes to core JavaCall were merged in b5692cc a few months ago. We're preparing for a JavaCall 0.8 release. Taro just set compatibility with 0.8. It looks like we have conflicts to fix here. The |
Add a JProxy subpackage |
I have updated the
JProxy
code from @zot from PR #91 . Tests are now passing and there are no conflicts.