Skip to content

Conversation

mkitti
Copy link
Member

@mkitti mkitti commented Apr 13, 2020

I have updated the JProxy code from @zot from PR #91 . Tests are now passing and there are no conflicts.

Bill Burdick and others added 30 commits September 8, 2018 15:28
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.
Copy link
Collaborator

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
Copy link
Collaborator

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

Copy link
Contributor

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?

Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesnt seem used below?

Copy link
Contributor

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

Copy link
Member Author

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...)
Copy link
Collaborator

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.

Copy link
Contributor

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
Copy link
Member Author

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?

Copy link
Collaborator

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?

Copy link
Member Author

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}
Copy link
Member Author

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.

Copy link
Member Author

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?

Copy link
Contributor

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 :)

Copy link
Contributor

@zot zot Apr 28, 2020

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()
Copy link
Member Author

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.

Copy link
Contributor

@zot zot Jun 18, 2020

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[]";
}
Copy link
Member Author

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
Comment on lines 1 to 20
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
Copy link
Member Author

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.

Comment on lines 23 to 34
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
Copy link
Member Author

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)
Copy link
Member Author

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

@zot
Copy link
Contributor

zot commented May 31, 2020

Folks, do you need any more information from me in order to tie this off?

@mkitti
Copy link
Member Author

mkitti commented May 31, 2020

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 ccall should be wrapped into the new JNI submodule.

Do we have your permission to continue developing this? My plan remains making this a separate package which has JavaCall as a dependency.

@zot
Copy link
Contributor

zot commented May 31, 2020

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...

@mkitti
Copy link
Member Author

mkitti commented Jun 30, 2020

@zot , @aviks I split the proxy code off into its own JProxies subpackage.

  1. I switched most of the JNI calls over to the JavaCall.JNI module which wraps the ccalls
  2. I disabled most of the memory management code since I want to use JNI.PushLocalFrame and JNI.PopLocalFrame in the future which already does the job of tracking local references and marking them for garbage collection.
  3. All tests in both packages are passing.

The easiest way I found to load JProxies is to symlink the JProxies folder into ~/.julia/dev and then use Pkg.develop. More work is required, but I think this puts us on a more sustainable path.

* 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).
Copy link
Contributor

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).

julia> JProxy(@jimport(java.lang.System)).getName()
"java.lang.System"
julia> JProxy(@jimport(java.lang.System);static=true).out.println("hello")
Copy link
Contributor

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")

@aviks
Copy link
Collaborator

aviks commented Jul 1, 2020

Can we just make this a submodule for the JavaCall module, so doing using JavaCall; using JavaCall.JProxy gets you the new behaviour?

@mkitti
Copy link
Member Author

mkitti commented Jul 2, 2020

Can we just make this a submodule for the JavaCall module, so doing using JavaCall; using JavaCall.JProxy gets you the new behaviour?

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 JProxies without explicitly importing JavaCall.

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, listfields probably belongs in reflect.jl.

@zot
Copy link
Contributor

zot commented Jul 5, 2020

Can we just make this a submodule for the JavaCall module, so doing using JavaCall; using JavaCall.JProxy gets you the new behaviour?

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 JProxies without explicitly importing JavaCall.

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.

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.

There is still some code I actually want to move into JavaCall itself. For example, listfields probably belongs in reflect.jl.

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.

@zot
Copy link
Contributor

zot commented Nov 1, 2020

Is this dying on the vine?

@mkitti
Copy link
Member Author

mkitti commented Nov 2, 2020

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 jnull thing should be tested again.

@mkitti
Copy link
Member Author

mkitti commented Nov 2, 2020

Add a JProxy subpackage

@mkitti mkitti merged commit 4625a51 into JuliaInterop:master Nov 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants