Call methods of a class generated in runtime

オープン
#166 コメント 12 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
25/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
停滞
技術スタック
java, julia
領域
tooling

調査の方向性

issue の InMemoryJavaCompiler の例で失敗を再現し、その後 JavaCall の core.jl で jcall 周辺と報告されている 244 行目および 373 行目から調べ始めてください。通常の dispatch と reflection ベースの jcall2 パスを比較してください。動的にコンパイルされたクラスのメソッドを jcall 経由で呼び出せ、継承されたメソッドも引き続き動作すれば完了です。

索引モデルが issue の本文から書いたものです。

説明

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.

主要言語
Julia
スター
125
フォーク
53
PR マージ指標
30日以内にマージされた PR はありません

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

JuliaInterop/JavaCall.jl のほかの issue

JuliaInterop/JavaCall.jl の issue をすべて見る

似ている issue

Julia の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。