tdlib / td

Cross-platform library for building Telegram clients
https://core.telegram.org/tdlib
Boost Software License 1.0
7.23k stars 1.47k forks source link

FATAL ERROR in native method: Can't find class #2564

Closed santoszv closed 1 year ago

santoszv commented 1 year ago

I am having problems using TDLib on Linux and Java 11/17. Next is the output:

[0.240s][info][class,load] org.drinkless.tdlib.Client source: file:/root/trexishere-all.jar [0.241s][info][class,load] org.drinkless.tdlib.TdApi$Object source: file:/root/trexishere-all.jar [0.241s][info][class,load] org.drinkless.tdlib.TdApi$Function source: file:/root/trexishere-all.jar [0.242s][info][class,load] org.drinkless.tdlib.TdApi$GetOption source: file:/root/trexishere-all.jar [0.242s][info][class,load] java.lang.UnsatisfiedLinkError source: jrt:/java.base [0.243s][info][class,load] org.drinkless.tdlib.Client$ResponseReceiver source: file:/root/trexishere-all.jar [0.244s][info][class,load] org.drinkless.tdlib.TdApi$KeyboardButton source: file:/root/trexishere-all.jar [0.245s][info][class,load] org.drinkless.tdlib.TdApi$InlineKeyboardButton source: file:/root/trexishere-all.jar [0.245s][info][class,load] org.drinkless.tdlib.TdApi$PageBlockTableCell source: file:/root/trexishere-all.jar [0.246s][info][class,load] org.drinkless.tdlib.TdApi$SetLogVerbosityLevel source: file:/root/trexishere-all.jar [ 1][t 0][1691304839.988271713][tl_jni_object.cpp:39] Can't find class [Call set_package_name/TdApi$SetLogVerbosityLevel] FATAL ERROR in native method: Can't find class [Call set_package_name/TdApi$SetLogVerbosityLevel] at org.drinkless.tdlib.Client.nativeClientExecute(Native Method) at org.drinkless.tdlib.Client.execute(Client.java:106) at tdlib.TDLibKt.initTDLib(TDLib.kt:19) at trex_is_here.TRexIsHereKt.main(TRexIsHere.kt:19) at trex_is_here.TRexIsHereKt.main(TRexIsHere.kt) Aborted

This output is the same across Gentoo Linux and Ubuntu Linux 22. Far as I can understand, TDLib is loaded correctly beacuse org.drinkless.tdlib.Client is loaded due to auto client_class = td::jni::get_jclass(env, PACKAGE_NAME "/Client"); in example/java/td_jni.cpp and the problem starts after JNI_OnLoad returns.

Thanks on advance.

levlam commented 1 year ago

You must add classes org.drinkless.tdlib.Client and org.drinkless.tdlib.TdApi to your project without changing their package.

santoszv commented 1 year ago

You must add classes org.drinkless.tdlib.Client and org.drinkless.tdlib.TdApi to your project without changing their package.

I am lost, does this line is wrong?

[0.240s][info][class,load] org.drinkless.tdlib.Client source: file:/root/trexishere-all.jar

This class was loaded as org.drinkless.tdlib.Client far as I know, because this JAR is working fine on MacOS and Windows. Just Linux is the problem.

levlam commented 1 year ago

Sorry. It looks like you are tried to call a TDLib method before the library was loaded, for example, from another static block.

levlam commented 1 year ago

From the error it can be seen that Client.nativeClientExecute is being called before call to the JNI_OnLoad method of the tdjni library.

santoszv commented 1 year ago

From the error it can be seen that Client.nativeClientExecute is being called before call to the JNI_OnLoad method of the tdjni library.

I did:

1) Commented static block with System.loadLibrary in org.drinkless.tdlib.TdApi 2) Commented static block with System.loadLibrary in org.drinkless.tdlib.Client 3) Changed main method (written in Kotlin) to:

println("java.library.path = ${System.getProperty("java.library.path")}")
println("SZV: KOTLIN: BEFORE LOAD LIBRARY")
System.loadLibrary("tdjni")
println("SZV: KOTLIN: AFTER LOAD LIBRARY")
println("SZV: KOTLIN: BEFORE Client.execute")
Client.execute(TdApi.SetLogVerbosityLevel(0))
println("SZV: KOTLIN: AFTER Client.execute")

4) Modified example/java/td_jni.cpp for printing when JNI_OnLoad is invoked 5) Ran with this command line java -verbose:class -Djava.library.path=. -jar trexishere-all.jar

Output:

java.library.path = .
SZV: KOTLIN: BEFORE LOAD LIBRARY
SZV: C++: JNI_OnLoad called
[0.359s][info][class,load] org.drinkless.tdlib.Client source: file:/home/santoszv/trexishere-all.jar
[0.360s][info][class,load] org.drinkless.tdlib.TdApi$Object source: file:/home/santoszv/trexishere-all.jar
[0.360s][info][class,load] org.drinkless.tdlib.TdApi$Function source: file:/home/santoszv/trexishere-all.jar
[0.361s][info][class,load] org.drinkless.tdlib.TdApi$GetOption source: file:/home/santoszv/trexishere-all.jar
[0.362s][info][class,load] org.drinkless.tdlib.Client$ResponseReceiver source: file:/home/santoszv/trexishere-all.jar
[0.363s][info][class,load] org.drinkless.tdlib.TdApi$KeyboardButton source: file:/home/santoszv/trexishere-all.jar
[0.364s][info][class,load] org.drinkless.tdlib.TdApi$InlineKeyboardButton source: file:/home/santoszv/trexishere-all.jar
[0.364s][info][class,load] org.drinkless.tdlib.TdApi$PageBlockTableCell source: file:/home/santoszv/trexishere-all.jar
SZV: C++: JNI_OnLoad returning
SZV: KOTLIN: AFTER LOAD LIBRARY
SZV: KOTLIN: BEFORE Client.execute
[0.365s][info][class,load] org.drinkless.tdlib.TdApi$SetLogVerbosityLevel source: file:/home/santoszv/trexishere-all.jar
[ 1][t 0][1691323335.595601797][tl_jni_object.cpp:39]   Can't find class [Call set_package_name/TdApi$SetLogVerbosityLevel]
FATAL ERROR in native method: Can't find class [Call set_package_name/TdApi$SetLogVerbosityLevel]
    at org.drinkless.tdlib.Client.nativeClientExecute(Native Method)
    at org.drinkless.tdlib.Client.execute(Client.java:106)
    at trex_is_here.TRexIsHereKt.main(TRexIsHere.kt:23)
    at trex_is_here.TRexIsHereKt.main(TRexIsHere.kt)
Aborted

Somewhere something goes bad just in Linux (I am running fine this JAR on MacOS and Windows). The Call set_package_name seems weird, unfornately my expertise does not include debugging C++, I can't help with anything more.

levlam commented 1 year ago

Thank you. This is a very good way to debug the issue. Could you attach the td_jni.cpp file after the changes?

santoszv commented 1 year ago

Thank you. This is a very good way to debug the issue. Could you attach the td_jni.cpp file after the changes?

I just added some cout, this is the diff with Git.

diff --git a/example/java/td_jni.cpp b/example/java/td_jni.cpp
index fb7554c74..2dc8f964e 100644
--- a/example/java/td_jni.cpp
+++ b/example/java/td_jni.cpp
@@ -14,6 +14,9 @@
 #include <string>
 #include <utility>

+#include <iostream>
+
+
 namespace td_jni {

 static td::td_api::object_ptr<td::td_api::Function> fetch_function(JNIEnv *env, jobject function) {
@@ -176,6 +179,8 @@ static jint register_native(JavaVM *vm) {
 }  // namespace td_jni

 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
+  std::cout << "SZV: C++: JNI_OnLoad called" << std::endl;
   static jint jni_version = td_jni::register_native(vm);  // call_once
+  std::cout << "SZV: C++: JNI_OnLoad returning" << std::endl;
   return jni_version;
 }
santoszv commented 1 year ago

An update about this issue, I recompiled TDLib with differents options and found this:

Seems like splitting code is doing something wrong for Java.

levlam commented 1 year ago

Ah, this makes sense. Yes, code splitting doesn't work now for the Java binding.

santoszv commented 1 year ago

Ah, this makes sense. Yes, code splitting doesn't work now for the Java binding.

Build instruction on https://tdlib.github.io/td/build.html doesn't say anything about this. Maybe a warning is enough to avoid using code splitting in Java/Kotlin.

levlam commented 1 year ago

This was a recently introduced bug.

I have just pushed to Github a fix for it. Thnak you for the report.