JuliaInterop / JuliaInterop/JavaCall.jl
Call methods of a class generated in runtime
Nessuno ha ancora preso questa issue.
- Lingua principale
- Julia
- Stelle
- 125
- Fork
- 53
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Descrizione
jcall fails with NoSuchMethodError when calling methods of a dynamically compiled class.
I use InMemoryJavaCompiler to compile a new class and create its instance. (InMemoryJavaCompiler is based on javax.tools.*, so quite standard approach).
const JInMemoryJavaCompiler = @jimport org.mdkt.compiler.InMemoryJavaCompiler
function mkclass(name::String, src::String)
jcompiler = jcall(JInMemoryJavaCompiler, "newInstance", JInMemoryJavaCompiler, ())
return jcall(jcompiler, "compile", JClass, (JString, JString), name, src)
end
function instantiate(name::String, src::String)
jclass = mkclass(name, src)
return jcall(jclass, "newInstance", JObject, ())
end
init()
name = "julia.compiled.Hello"
src = """
package julia.compiled;
public class Hello {
public String hello(String name) {
return "Hello, " + name;
}
public void goodbye() {
System.out.println("Goodbye!");
}
}
"""
obj = instantiate(name, src)
The resulting object looks like the ordinary one and I can see the methods I defined:
julia> listmethods(obj)
11-element Vector{JMethod}:
java.lang.String hello(java.lang.String)
void goodbye()
void wait(long)
void wait(long, int)
void wait()
boolean equals(java.lang.Object)
java.lang.String toString()
int hashCode()
java.lang.Class getClass()
void notify()
void notifyAll()
However, calling them with jcall fails, even though calling the inherited methods works fine:
julia> jcall(obj, "hello", JString, (JString,), "Bob")
Exception in thread "main" java.lang.NoSuchMethodError: hello
ERROR: JavaCall.JavaCallError("Error calling Java: java.lang.NoSuchMethodError: hello")
Stacktrace:
[1] geterror(allow::Bool)
@ JavaCall ~/.julia/packages/JavaCall/MlduK/src/core.jl:418
[2] jcall(obj::JObject, method::String, rettype::Type, argtypes::Tuple{DataType}, args::String)
@ JavaCall ~/.julia/packages/JavaCall/MlduK/src/core.jl:244
[3] top-level scope
@ REPL[13]:1
julia> jcall(obj, "goodbye", Nothing, ())
Exception in thread "main" java.lang.NoSuchMethodError: goodbye
ERROR: JavaCall.JavaCallError("Error calling Java: java.lang.NoSuchMethodError: goodbye")
Stacktrace:
[1] geterror(allow::Bool)
@ JavaCall ~/.julia/packages/JavaCall/MlduK/src/core.jl:418
[2] jcall(::JObject, ::String, ::Type, ::Tuple{})
@ JavaCall ~/.julia/packages/JavaCall/MlduK/src/core.jl:244
[3] top-level scope
@ REPL[14]:1
julia> jcall(obj, "equals", jboolean, (JObject,), obj)
0x01
What's even more confusing, these new methods can actually be called using the reflection:
function jcall2(jobj::JavaObject, name::String, ret_type, arg_types, args...)
jclass = getclass(jobj)
jargs = [a for a in convert.(arg_types, args)] # convert to Vector
meth = jcall(jclass, "getMethod", JMethod, (JString, Vector{JClass}), name, getclass.(jargs))
return meth(jobj, jargs...)
end
julia> jcall2(obj, "hello", JString, (JString,), "Bob")
"Hello, Bob"
julia> jcall2(obj, "goodbye", Nothing, ())
Goodbye!
Although it fails with NoSuchMethodException for the inherited method ¯_(ツ)_/¯:
julia> jcall2(obj, "equals", jboolean, (JObject,), obj)
Exception in thread "main" java.lang.NoSuchMethodException: julia.compiled.Hello.equals(julia.compiled.Hello)
at java.base/java.lang.Class.getMethod(Class.java:2108)
ERROR: JavaCall.JavaCallError("Error calling Java: java.lang.NoSuchMethodException: julia.compiled.Hello.equals(julia.compiled.Hello)")
Stacktrace:
[1] geterror(allow::Bool)
@ JavaCall ~/.julia/packages/JavaCall/MlduK/src/core.jl:418
[2] geterror
@ ~/.julia/packages/JavaCall/MlduK/src/core.jl:403 [inlined]
[3] _jcall(::JClass, ::Ptr{Nothing}, ::Ptr{Nothing}, ::Type, ::Tuple{DataType, DataType}, ::String, ::Vararg{Any})
@ JavaCall ~/.julia/packages/JavaCall/MlduK/src/core.jl:373
[4] jcall(::JClass, ::String, ::Type, ::Tuple{DataType, DataType}, ::String, ::Vararg{Any})
@ JavaCall ~/.julia/packages/JavaCall/MlduK/src/core.jl:245
[5] jcall2(jobj::JObject, name::String, ret_type::Type, arg_types::Tuple{DataType}, args::JObject)
@ Main ./REPL[17]:4
[6] top-level scope
@ REPL[18]:1
The code above is executable from the Spark#new-api branch, but honestly I hope that I'm just doing some stupid mistake that a keen eye can catch just from the description.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Riproduci il fallimento con l’esempio InMemoryJavaCompiler dell’issue, quindi inizia in core.jl di JavaCall intorno a jcall e alle righe segnalate 244 e 373. Confronta il dispatch ordinario con il percorso jcall2 basato sulla reflection; il lavoro è completato quando è possibile chiamare tramite jcall i metodi delle classi compilate dinamicamente, mentre i metodi ereditati continuano a funzionare.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- java, julia
- Ambito
- tooling
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 25/100