Skip to content

support new arch, Expo 53+#59

Open
song-react wants to merge 4 commits intoprisma:mainfrom
song-react:main
Open

support new arch, Expo 53+#59
song-react wants to merge 4 commits intoprisma:mainfrom
song-react:main

Conversation

@song-react
Copy link

@song-react song-react commented Nov 23, 2025

Summary by CodeRabbit

  • New Features

    • Added support for React Native's new architecture with automatic fallback to legacy architecture.
  • Bug Fixes

    • Enhanced initialization safety checks to prevent crashes from uninitialized components.
    • Improved simulator and device build compatibility.
  • Chores

    • Optimized migration bundling during the build process.
    • Improved memory management for internal callbacks.

@coderabbitai
Copy link

coderabbitai bot commented Mar 14, 2026

Walkthrough

This PR enhances null-safety checks in Android, updates C++ lambda capture semantics, adds platform-specific header compilation for iOS, refactors iOS Prisma module for new React Native architecture support, and adjusts the build script for migrations copying.

Changes

Cohort / File(s) Summary
Android null-safety
android/src/main/java/com/prisma/PrismaModule.java
Enhanced install() method with guarded null-checks for JSContext pointer and JSCallInvokerHolder retrieval, adding fallback logic to obtain the invoker from CatalystInstance if direct access is unavailable.
Build configuration
copy-migrations.sh
Simplified migrations directory copying by directly sourcing from prisma/migrations instead of a generic top-level migrations folder, removing explicit mkdir step.
C++ header compilation
cpp/QueryEngineHostObject.h
Added conditional compilation guard (TARGET_OS_SIMULATOR) for query_engine header inclusion to resolve platform-specific paths at compile time.
C++ lambda captures
cpp/react-native-prisma.cpp
Adjusted lambda capture semantics: changed js_log_callback and queryEngineHostObject from by-reference to by-value captures in log callback and Promise executor lambdas.
iOS new-arch integration
ios/Prisma.mm
Major refactor introducing new React Native architecture support: reworked TurboModule installation to use runtimeExecutor-based path with fallback to legacy RCTCxxBridge, added helper functions for runtime installation and bridge resolution, and replaced NativePrismaSpecJSI with new PrismaCxxTurboModule implementation.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding support for React Native's new architecture and Expo 53+. Multiple files demonstrate architectural shifts toward new-arch support, particularly in iOS TurboModule implementation and runtime integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can enable review details to help with troubleshooting, context usage and more.

Enable the reviews.review_details setting to include review details such as the model used, the time taken for each step and more in the review comments.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d89adffc-66b7-4720-a069-b88a786e04fb

📥 Commits

Reviewing files that changed from the base of the PR and between 78fa48b and 28e3245.

📒 Files selected for processing (5)
  • android/src/main/java/com/prisma/PrismaModule.java
  • copy-migrations.sh
  • cpp/QueryEngineHostObject.h
  • cpp/react-native-prisma.cpp
  • ios/Prisma.mm

Comment on lines +125 to +135
if (jsContextPointer == 0) {
throw new RuntimeException("JSI runtime pointer is null. Make sure Hermes is enabled and the runtime is initialized before calling install().");
}
CallInvokerHolderImpl jsCallInvokerHolder;
if (context.getJSCallInvokerHolder() != null) {
jsCallInvokerHolder = (CallInvokerHolderImpl) context.getJSCallInvokerHolder();
} else if (context.getCatalystInstance() != null) {
jsCallInvokerHolder = (CallInvokerHolderImpl) context.getCatalystInstance().getJSCallInvokerHolder();
} else {
throw new RuntimeException("JSCallInvokerHolder is not available yet.");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Null-safety improvements are good, but the fallback path may still NPE.

The null checks for jsContextPointer and getJSCallInvokerHolder() are correct. However, on line 132, context.getCatalystInstance().getJSCallInvokerHolder() may also return null even when getCatalystInstance() is non-null, leading to an NPE during the cast.

🛠️ Proposed fix to add null check
     if (context.getJSCallInvokerHolder() != null) {
       jsCallInvokerHolder = (CallInvokerHolderImpl) context.getJSCallInvokerHolder();
     } else if (context.getCatalystInstance() != null) {
-      jsCallInvokerHolder = (CallInvokerHolderImpl) context.getCatalystInstance().getJSCallInvokerHolder();
+      var holder = context.getCatalystInstance().getJSCallInvokerHolder();
+      if (holder == null) {
+        throw new RuntimeException("JSCallInvokerHolder from CatalystInstance is null.");
+      }
+      jsCallInvokerHolder = (CallInvokerHolderImpl) holder;
     } else {
       throw new RuntimeException("JSCallInvokerHolder is not available yet.");
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (jsContextPointer == 0) {
throw new RuntimeException("JSI runtime pointer is null. Make sure Hermes is enabled and the runtime is initialized before calling install().");
}
CallInvokerHolderImpl jsCallInvokerHolder;
if (context.getJSCallInvokerHolder() != null) {
jsCallInvokerHolder = (CallInvokerHolderImpl) context.getJSCallInvokerHolder();
} else if (context.getCatalystInstance() != null) {
jsCallInvokerHolder = (CallInvokerHolderImpl) context.getCatalystInstance().getJSCallInvokerHolder();
} else {
throw new RuntimeException("JSCallInvokerHolder is not available yet.");
}
if (jsContextPointer == 0) {
throw new RuntimeException("JSI runtime pointer is null. Make sure Hermes is enabled and the runtime is initialized before calling install().");
}
CallInvokerHolderImpl jsCallInvokerHolder;
if (context.getJSCallInvokerHolder() != null) {
jsCallInvokerHolder = (CallInvokerHolderImpl) context.getJSCallInvokerHolder();
} else if (context.getCatalystInstance() != null) {
var holder = context.getCatalystInstance().getJSCallInvokerHolder();
if (holder == null) {
throw new RuntimeException("JSCallInvokerHolder from CatalystInstance is null.");
}
jsCallInvokerHolder = (CallInvokerHolderImpl) holder;
} else {
throw new RuntimeException("JSCallInvokerHolder is not available yet.");
}

rm -rf "$MIGRATIONS_TARGET/migrations"
mkdir "$MIGRATIONS_TARGET/migrations"
cp -r ${SRCROOT}/../migrations ${MIGRATIONS_TARGET}
cp -r ${SRCROOT}/../prisma/migrations "${MIGRATIONS_TARGET}/migrations"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Quote the source path to prevent word splitting.

If SRCROOT contains spaces, this command will fail. Quote the variable expansion for robustness.

🛠️ Proposed fix
-cp -r ${SRCROOT}/../prisma/migrations "${MIGRATIONS_TARGET}/migrations"
+cp -r "${SRCROOT}/../prisma/migrations" "${MIGRATIONS_TARGET}/migrations"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cp -r ${SRCROOT}/../prisma/migrations "${MIGRATIONS_TARGET}/migrations"
cp -r "${SRCROOT}/../prisma/migrations" "${MIGRATIONS_TARGET}/migrations"
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 8-8: Double quote to prevent globbing and word splitting.

(SC2086)

Comment on lines +5 to +10
#include <TargetConditionals.h>
#if TARGET_OS_SIMULATOR
#include "../engines/ios/QueryEngine.xcframework/ios-arm64_x86_64-simulator/Headers/query_engine.h"
#else
#include "../engines/ios/QueryEngine.xcframework/ios-arm64/Headers/query_engine.h"
#endif
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing platform guard will break non-Apple builds.

<TargetConditionals.h> is an Apple-only header. This code compiles on Android where this header doesn't exist, causing a build failure. Additionally, cpp/react-native-prisma.cpp:5 still uses #include "query_engine.h" directly, creating inconsistency.

Wrap the Apple-specific conditional in a platform check:

🐛 Proposed fix to add platform guard
+#if defined(__APPLE__)
 `#include` <TargetConditionals.h>
 `#if` TARGET_OS_SIMULATOR
 `#include` "../engines/ios/QueryEngine.xcframework/ios-arm64_x86_64-simulator/Headers/query_engine.h"
 `#else`
 `#include` "../engines/ios/QueryEngine.xcframework/ios-arm64/Headers/query_engine.h"
 `#endif`
+#else
+#include "query_engine.h"
+#endif
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#include <TargetConditionals.h>
#if TARGET_OS_SIMULATOR
#include "../engines/ios/QueryEngine.xcframework/ios-arm64_x86_64-simulator/Headers/query_engine.h"
#else
#include "../engines/ios/QueryEngine.xcframework/ios-arm64/Headers/query_engine.h"
#endif
`#if` defined(__APPLE__)
`#include` <TargetConditionals.h>
`#if` TARGET_OS_SIMULATOR
`#include` "../engines/ios/QueryEngine.xcframework/ios-arm64_x86_64-simulator/Headers/query_engine.h"
`#else`
`#include` "../engines/ios/QueryEngine.xcframework/ios-arm64/Headers/query_engine.h"
`#endif`
`#else`
`#include` "query_engine.h"
`#endif`
🧰 Tools
🪛 Clang (14.0.6)

[error] 5-5: 'TargetConditionals.h' file not found

(clang-diagnostic-error)

Comment on lines +70 to 80
if (bridge && [bridge respondsToSelector:@selector(runtimeExecutor)]) {
facebook::react::RuntimeExecutor executor = RCTRuntimeExecutorFromBridge(bridge);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
executor([&](facebook::jsi::Runtime &runtime) {
InstallInRuntime(runtime, bridge.jsCallInvoker);
*okPtr = YES;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
return @(ok);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potential deadlock if InstallInRuntime throws.

If InstallInRuntime throws an exception inside the executor block, dispatch_semaphore_signal is never called, causing dispatch_semaphore_wait to block indefinitely.

🐛 Proposed fix using try-catch
     if (bridge && [bridge respondsToSelector:`@selector`(runtimeExecutor)]) {
         facebook::react::RuntimeExecutor executor = RCTRuntimeExecutorFromBridge(bridge);
         dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+        __block NSException *caughtException = nil;
         executor([&](facebook::jsi::Runtime &runtime) {
-          InstallInRuntime(runtime, bridge.jsCallInvoker);
-          *okPtr = YES;
+          `@try` {
+            InstallInRuntime(runtime, bridge.jsCallInvoker);
+            *okPtr = YES;
+          } `@catch` (NSException *e) {
+            caughtException = e;
+          }
           dispatch_semaphore_signal(sema);
         });
         dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+        if (caughtException) {
+          `@throw` caughtException;
+        }
         return @(ok);
     }

Note: C++ exceptions from JSI may need different handling (e.g., std::exception_ptr). Consider using a C++ try-catch block instead if JSI throws C++ exceptions.

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.

1 participant