Skip to content
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

Crash in android release mode during runtime in example app #314

Closed
3 of 5 tasks
hannojg opened this issue Nov 9, 2024 · 14 comments · Fixed by #324
Closed
3 of 5 tasks

Crash in android release mode during runtime in example app #314

hannojg opened this issue Nov 9, 2024 · 14 comments · Fixed by #324
Labels
nitro-core Issue is related to the Nitro Modules core runtime/C++ codebase

Comments

@hannojg
Copy link
Contributor

hannojg commented Nov 9, 2024

What's happening?

Running the example app in release mode causes a crash

Reproduceable Code

  • Build example in release for android
  • run tests
  • crash

Relevant log output

2024-11-09 10:22:41.486  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  Bridgeless mode is enabled
2024-11-09 10:22:41.488  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  Running "NitroExample"
2024-11-09 10:22:41.498  5023-5080  NitroModules            com.margelo.nitroexample             I  Loading NitroModules C++ library...
2024-11-09 10:22:41.500  5023-5080  NitroModules            com.margelo.nitroexample             I  Successfully loaded NitroModules C++ library!
2024-11-09 10:22:41.501  5023-5080  HybridImageFactorySpec  com.margelo.nitroexample             I  Loading NitroImage C++ library...
2024-11-09 10:22:41.502  5023-5080  HybridImageFactorySpec  com.margelo.nitroexample             I  Successfully loaded NitroImage C++ library!
2024-11-09 10:22:41.502  5023-5080  HybridTest...KotlinSpec com.margelo.nitroexample             I  Loading NitroImage C++ library...
2024-11-09 10:22:41.503  5023-5080  HybridTest...KotlinSpec com.margelo.nitroexample             I  Successfully loaded NitroImage C++ library!
2024-11-09 10:22:41.503  5023-5080  HybridBaseSpec          com.margelo.nitroexample             I  Loading NitroImage C++ library...
2024-11-09 10:22:41.504  5023-5080  HybridBaseSpec          com.margelo.nitroexample             I  Successfully loaded NitroImage C++ library!
2024-11-09 10:22:41.504  5023-5080  HybridChildSpec         com.margelo.nitroexample             I  Loading NitroImage C++ library...
2024-11-09 10:22:41.504  5023-5080  HybridChildSpec         com.margelo.nitroexample             I  Successfully loaded NitroImage C++ library!
2024-11-09 10:22:41.504  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  Prototype chain of Child:
2024-11-09 10:22:41.505  5023-5080  ReactNativeJS           com.margelo.nitroexample             I    Child �[2m(0 props)�[0m
2024-11-09 10:22:41.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I     ∟ {} �[2m(1 props)�[0m
2024-11-09 10:22:41.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I        ∟ {} �[2m(1 props)�[0m
2024-11-09 10:22:41.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I           ∟ {} �[2m(4 props)�[0m
2024-11-09 10:22:41.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I              ∟ {} �[2m(0 props)�[0m
2024-11-09 10:22:41.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I                 ∟ {} �[2m(0 props)�[0m
2024-11-09 10:22:41.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  10
2024-11-09 10:22:41.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  20
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  30
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  Prototype chain of TestObjectCpp:
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I    TestObjectCpp �[2m(0 props)�[0m
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I     ∟ {} �[2m(1 props)�[0m
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I        ∟ {} �[2m(57 props)�[0m
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I           ∟ {} �[2m(4 props)�[0m
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I              ∟ {} �[2m(0 props)�[0m
2024-11-09 10:22:41.521  5023-5080  ReactNativeJS           com.margelo.nitroexample             I                 ∟ {} �[2m(0 props)�[0m
2024-11-09 10:22:41.585  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  Showing Tests for HybridObject "TestObjectCpp"
2024-11-09 10:22:41.712  5023-5023  Compatibil...geReporter com.margelo.nitroexample             D  Compat change id reported: 210923482; UID 10193; state: ENABLED
2024-11-09 10:22:42.733  5023-5023  Compatibil...geReporter com.margelo.nitroexample             D  Compat change id reported: 171228096; UID 10193; state: ENABLED
2024-11-09 10:22:42.797  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  Showing Tests for HybridObject "TestObjectCpp"
2024-11-09 10:22:43.153  5023-5048  EGL_emulation           com.margelo.nitroexample             D  app_time_stats: avg=1048.23ms min=588.69ms max=1507.77ms count=2
2024-11-09 10:22:44.281  5023-5080  Compatibil...geReporter com.margelo.nitroexample             D  Compat change id reported: 289878283; UID 10193; state: ENABLED
2024-11-09 10:22:44.314  5023-5048  EGL_emulation           com.margelo.nitroexample             D  app_time_stats: avg=192.53ms min=6.79ms max=993.85ms count=6
2024-11-09 10:22:44.358  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  Showing Tests for HybridObject "TestObjectCpp"
2024-11-09 10:22:44.519  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  ⏳ Test "HybridObject.prototype is valid" started...
2024-11-09 10:22:44.520  5023-5080  ReactNativeJS           com.margelo.nitroexample             I  ✅ Test "HybridObject.prototype is valid" passed!
2024-11-09 10:22:44.520  5023-5080  libc                    com.margelo.nitroexample             A  Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x3b637ffff0 in tid 5080 (mqt_v_js), pid 5023 (lo.nitroexample)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A  Cmdline: com.margelo.nitroexample
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A  pid: 5023, tid: 5080, name: mqt_v_js  >>> com.margelo.nitroexample <<<
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #00 pc 00000000000b176c  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #01 pc 000000000005f520  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libNitroModules.so (offset 0x5f8000) (std::__ndk1::shared_ptr<margelo::nitro::HybridObject> margelo::nitro::HybridFunction::getHybridObjectNativeState<margelo::nitro::HybridObject>(facebook::jsi::Runtime&, facebook::jsi::Value const&, margelo::nitro::FunctionKind, std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&)+84) (BuildId: a0d6a70708ebb7b54d7cb0cc61d47975c4d45e5a)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #02 pc 000000000005eec0  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libNitroModules.so (offset 0x5f8000) (margelo::nitro::HybridFunction margelo::nitro::HybridFunction::createHybridFunction<margelo::nitro::HybridObject, std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > >(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > (margelo::nitro::HybridObject::*)(), margelo::nitro::FunctionKind)::'lambda'(facebook::jsi::Runtime&, facebook::jsi::Value const&, facebook::jsi::Value const*, unsigned long)::operator()(facebook::jsi::Runtime&, facebook::jsi::Value const&, facebook::jsi::Value const*, unsigned long) const+84) (BuildId: a0d6a70708ebb7b54d7cb0cc61d47975c4d45e5a)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #03 pc 00000000000bb7f0  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #04 pc 00000000000bb450  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #05 pc 00000000000c2528  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #06 pc 00000000000d2874  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #07 pc 00000000000d4224  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #08 pc 00000000000d38e0  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #09 pc 00000000000c2e38  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #10 pc 0000000000136c90  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #11 pc 00000000000c2528  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #12 pc 00000000000d2874  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #13 pc 00000000000d4224  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #14 pc 00000000000d38e0  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #15 pc 00000000000c2628  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #16 pc 00000000000c0f04  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #17 pc 0000000000109254  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #18 pc 00000000000af970  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libhermes.so (offset 0x838000) (BuildId: e70878b706682e356226475d11c138e044ca065f)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #19 pc 00000000004de8ac  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libreactnative.so (offset 0xba8000) (facebook::react::RuntimeScheduler_Modern::performMicrotaskCheckpoint(facebook::jsi::Runtime&)+60) (BuildId: f7df2c604e5072c2)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #20 pc 00000000004ddf14  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libreactnative.so (offset 0xba8000) (facebook::react::RuntimeScheduler_Modern::runEventLoopTick(facebook::jsi::Runtime&, facebook::react::Task&, std::__ndk1::chrono::time_point<std::__ndk1::chrono::steady_clock, std::__ndk1::chrono::duration<long long, std::__ndk1::ratio<1l, 1000000000l> > >)+128) (BuildId: f7df2c604e5072c2)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #21 pc 00000000004de26c  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libreactnative.so (offset 0xba8000) (facebook::react::RuntimeScheduler_Modern::runEventLoop(facebook::jsi::Runtime&, bool)+144) (BuildId: f7df2c604e5072c2)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #22 pc 000000000034a468  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libreactnative.so (offset 0xba8000) (BuildId: f7df2c604e5072c2)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #23 pc 000000000051bdf4  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libreactnative.so (offset 0xba8000) (BuildId: f7df2c604e5072c2)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #24 pc 0000000000019804  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libfbjni.so (offset 0x808000) (facebook::jni::detail::MethodWrapper<void (facebook::jni::JNativeRunnable::*)(), &(facebook::jni::JNativeRunnable::run()), facebook::jni::JNativeRunnable, void>::dispatch(facebook::jni::alias_ref<facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<facebook::jni::JNativeRunnable, facebook::jni::JRunnable>::JavaPart, facebook::jni::JRunnable, void>::_javaobject*>)+72) (BuildId: a22242831a7971267de570e06121acb588ce64cd)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #25 pc 0000000000019744  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/base.apk!libfbjni.so (offset 0x808000) (facebook::jni::detail::FunctionWrapper<void (*)(facebook::jni::alias_ref<facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<facebook::jni::JNativeRunnable, facebook::jni::JRunnable>::JavaPart, facebook::jni::JRunnable, void>::_javaobject*>), facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<facebook::jni::JNativeRunnable, facebook::jni::JRunnable>::JavaPart, facebook::jni::JRunnable, void>::_javaobject*, void>::call(_JNIEnv*, _jobject*, void (*)(facebook::jni::alias_ref<facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<facebook::jni::JNativeRunnable, facebook::jni::JRunnable>::JavaPart, facebook::jni::JRunnable, void>::_javaobject*>))+60) (BuildId: a22242831a7971267de570e06121acb588ce64cd)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #26 pc 0000000000025344  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/oat/arm64/base.odex (art_jni_trampoline+116)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #29 pc 00000000002ba80c  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/oat/arm64/base.vdex (com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage+0)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #33 pc 00000000002badb2  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/oat/arm64/base.vdex (com.facebook.react.bridge.queue.MessageQueueThreadImpl.lambda$startNewBackgroundThread$2+70)
2024-11-09 10:22:44.920  5099-5099  DEBUG                   pid-5099                             A        #35 pc 00000000002ba8a4  /data/app/~~zz4h7X7D_XggNeymJ1k82w==/com.margelo.nitroexample-wVR229i9TnWsolzHcppGUg==/oat/arm64/base.vdex (com.facebook.react.bridge.queue.MessageQueueThreadImpl$$ExternalSyntheticLambda1.run+4)

Device

android emulator / device doesn't matter

Nitro Modules Version

0.15.0

Nitrogen Version

0.15.0

Can you reproduce this issue in the Nitro Example app here?

Yes, I can reproduce the same issue in the Example app here

Additional information

@hannojg hannojg added the nitro-core Issue is related to the Nitro Modules core runtime/C++ codebase label Nov 9, 2024
@hannojg hannojg changed the title Crash in android release mode during runtime when building release variant Crash in android release mode during runtime in example app Nov 11, 2024
@hannojg
Copy link
Contributor Author

hannojg commented Nov 11, 2024

Update: at first i thought this would be only an issue when building react native from source, but I just realized that its always happening in release mode on android (for the example app at least)

@mrousavy
Copy link
Owner

  • This only happens in release builds, not in debug
  • This crash does not happen in the Expensify app (RN 0.75) is that correct? Only in NitroExample (RN 0.76)?

@mrousavy
Copy link
Owner

Hanno can you try to run each test individually to find out which test is actually crashing?

@mrousavy
Copy link
Owner

And second thing we can try is to edit NitroDefines.hpp to just always set NITRO_DEBUG to 0/undef'd, even in debug mode.

Then run the app in debug and see if it still crashes. If no, then maybe there is something happening in Hermes with NativeState.

@hannojg
Copy link
Contributor Author

hannojg commented Nov 11, 2024

sure, let me test!

@hannojg
Copy link
Contributor Author

hannojg commented Nov 11, 2024

Its the first two tests that are crashing:

  • HyrbidObject.prototype is valid
  • HyrbidObject.prototype.prototype is valid

The other tests seem to all work:

untitled.mp4

@mrousavy
Copy link
Owner

Hm, could this be a Hermes bug? 🙈

The code that this test essentially runs is:

const prototype = Object.getPrototypeOf(testObject)
typeof prototype === 'object'
prototype[simpleFunc] != null || Object.keys(prototype).includes("simpleFunc")

Where testObject is an object created by Nitro via Object.create(XXX) (XXX being the prototype we want to test here) with NativeState.

cc @tmikov anything I'm doing wrong here?

@hannojg
Copy link
Contributor Author

hannojg commented Nov 11, 2024

@mrousavy if i remove NITRO_DEBUG and run in debug mode the same tests are crashing!

@mrousavy
Copy link
Owner

wait whaaat

This function didn't throw when NITRO_DEBUG was true, so why should it suddenly fail if we remove those checks?

template <typename THybrid>
static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value, FunctionKind funcKind,
const std::string& funcName) {
// 1. Convert jsi::Value to jsi::Object
#ifdef NITRO_DEBUG
if (!value.isObject()) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this` is not bound! Suggestions:\n"
"- Did you accidentally destructure the `HybridObject`? (`const { " +
funcName +
" } = ...`)\n"
"- Did you call `dispose()` on the `HybridObject` before?"
"- Did you accidentally call `" +
funcName + "` on the prototype directly?");
}
#endif
jsi::Object object = value.getObject(runtime);
// 2. Check if it even has any kind of `NativeState`
#ifdef NITRO_DEBUG
if (!object.hasNativeState(runtime)) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this` does not have a NativeState! Suggestions:\n"
"- Did you accidentally destructure the `HybridObject`? (`const { " +
funcName +
" } = ...`)\n"
"- Did you call `dispose()` on the `HybridObject` before?"
"- Did you accidentally call `" +
funcName + "` on the prototype directly?");
}
#endif
// 3. Get `NativeState` from the jsi::Object and check if it is non-null
std::shared_ptr<jsi::NativeState> nativeState = object.getNativeState(runtime);
#ifdef NITRO_DEBUG
if (nativeState == nullptr) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this`'s `NativeState` is `nullptr`, "
"did you accidentally call `dispose()` on this object?");
}
#endif
// 4. Try casting it to our desired target type.
std::shared_ptr<THybrid> hybridInstance = std::dynamic_pointer_cast<THybrid>(nativeState);
#ifdef NITRO_DEBUG
if (hybridInstance == nullptr) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this` has a NativeState, but it's the wrong type!");
}
#endif
return hybridInstance;
}

..or do you think there's some kind of flattening going on? The object has a single __type property in debug, which is not available on release:

#ifdef NITRO_DEBUG
// 7. Assign a private __type property for debugging - this will be used so users know it's not just an empty object.
object.setProperty(runtime, "__type", jsi::String::createFromUtf8(runtime, "NativeState<" + std::string(_name) + ">"));
#endif

@hannojg
Copy link
Contributor Author

hannojg commented Nov 11, 2024

I think this isn't the place where its crashing:

template <typename THybrid>
static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value, FunctionKind funcKind,
const std::string& funcName) {
// 1. Convert jsi::Value to jsi::Object
#ifdef NITRO_DEBUG
if (!value.isObject()) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this` is not bound! Suggestions:\n"
"- Did you accidentally destructure the `HybridObject`? (`const { " +
funcName +
" } = ...`)\n"
"- Did you call `dispose()` on the `HybridObject` before?"
"- Did you accidentally call `" +
funcName + "` on the prototype directly?");
}
#endif
jsi::Object object = value.getObject(runtime);
// 2. Check if it even has any kind of `NativeState`
#ifdef NITRO_DEBUG
if (!object.hasNativeState(runtime)) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this` does not have a NativeState! Suggestions:\n"
"- Did you accidentally destructure the `HybridObject`? (`const { " +
funcName +
" } = ...`)\n"
"- Did you call `dispose()` on the `HybridObject` before?"
"- Did you accidentally call `" +
funcName + "` on the prototype directly?");
}
#endif
// 3. Get `NativeState` from the jsi::Object and check if it is non-null
std::shared_ptr<jsi::NativeState> nativeState = object.getNativeState(runtime);
#ifdef NITRO_DEBUG
if (nativeState == nullptr) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this`'s `NativeState` is `nullptr`, "
"did you accidentally call `dispose()` on this object?");
}
#endif
// 4. Try casting it to our desired target type.
std::shared_ptr<THybrid> hybridInstance = std::dynamic_pointer_cast<THybrid>(nativeState);
#ifdef NITRO_DEBUG
if (hybridInstance == nullptr) [[unlikely]] {
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
" - `this` has a NativeState, but it's the wrong type!");
}
#endif
return hybridInstance;
}

The place where its actually crashing is here:

CleanShot 2024-11-11 at 15 48 30

CleanShot 2024-11-11 at 15 48 50

@mrousavy
Copy link
Owner

mrousavy commented Nov 11, 2024

Yes I know that it's crashing in getNativeState because apparently hasNativeState is false here.

I'm trying to figure out why hasNativeState is false; for that I was wondering where we actually have a different code-path than before (NITRO_DEBUG on vs off).
The only place where there is actually a different code-path is where I set a __type property on the object that has a NativeState or Prototype - only here we have a different code-path in NITRO_DEBUG than in release.

Is there maybe something happening with the NativeState when the object does not have any properties?
You can check this by reverting all changes again and just removing those 4 lines here and run it again:

#ifdef NITRO_DEBUG
// 7. Assign a private __type property for debugging - this will be used so users know it's not just an empty object.
object.setProperty(runtime, "__type", jsi::String::createFromUtf8(runtime, "NativeState<" + std::string(_name) + ">"));
#endif

@hannojg
Copy link
Contributor Author

hannojg commented Nov 11, 2024

Its still working in debug with the mentioned lines removed 🤔

@mrousavy
Copy link
Owner

mrousavy commented Nov 12, 2024

Hm, maybe it is trying to call toString() on the Prototype which does not have a bound this/NativeState?

@mrousavy
Copy link
Owner

mrousavy commented Nov 12, 2024

I finally figured it out after hours of debugging.

Context

In the Example app, we run tests and each tests then logs the result of the test.
The first two tests of the example app tested the prototype of a HybridObject - so that's Object.getPrototypeOf(hybridObject).

All HybridObjects can be logged by calling hybridObject.toString(), and toString() is a method defined on the highest Prototype of the HybridObject prototype chain - HybridObject itself.

The way it works is simple - it gets the NativeState, unwraps it to the C++ class HybridObject, and calls HybridObject::toString() - which just returns the C++ _name.

This is a problem though - for HybridObjects (objects with NativeState) this works, but if we call it on the prototype directly it does not have a NativeState - because it's the prototype, not an actual instance of it.

toString() throws in debug

Calling toString() on an unbound prototype (the prototype itself, without NativeState) this throws , and in release it fully crashes the app.

The reason this worked before in debug is because of this code;

try {
if ('toString' in value) {
const string = value.toString()
if (string !== '[object Object]') return string
}
return `{ ${value} ${Object.keys(value).join(', ')} }`
} catch {
// toString() threw - maybe because we accessed it on a prototype.
return `{ [Object] ${Object.keys(value).join(', ')} }`
}

We caught the error, and just returned { [Object] ... } in this case. The error was silently swallowed.

Why assert(...) didn't catch this in release

Well, dumb mistake on my end - assert is compiled out in release.

Why value.toString() worked

Apparently this also threw, which silently caught the error. No idea why.

Solution

Now, I added some checks to my toString() function in the example to prevent calling toString() on an unbound prototype (so a object without NativeState).
This now no longer crashes and just logs something different for prototypes, which is absolutely desireable.

Alternatively I could've made toString() work without NativeState, but that would've been a lot of code complexity, and potentially also a minor performance hit for an absolutely unnecessary feature; who in their right mind would stringify an object's prototype in a release build?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
nitro-core Issue is related to the Nitro Modules core runtime/C++ codebase
Projects
None yet
2 participants