diff --git a/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI-Cxx/include/RuntimeScheduler.h b/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI-Cxx/include/RuntimeScheduler.h index 6115e87..5ab6f17 100644 --- a/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI-Cxx/include/RuntimeScheduler.h +++ b/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI-Cxx/include/RuntimeScheduler.h @@ -45,12 +45,22 @@ private: std::atomic refCount{1}; public: + // Xcode 27 (clang build 18xxxxxx+) warns when the constructors of a + // SWIFT_SHARED_REFERENCE type are not annotated; Xcode 26.x (Swift 6.2) + // rejects the annotation on constructors as an error. Apply it only on + // toolchains that understand it. See https://github.com/expo/expo/issues/50067 +#if defined(__apple_build_version__) && __apple_build_version__ >= 18000000 +#define EXPO_RUNTIME_SCHEDULER_CTOR_RETAINED SWIFT_RETURNS_RETAINED +#else +#define EXPO_RUNTIME_SCHEDULER_CTOR_RETAINED +#endif + /** Constructs a scheduler bound to a host-provided native RuntimeScheduler. `scheduleTask` dispatches through `fn`, which the host implements against the real react::RuntimeScheduler. */ - SWIFT_RETURNS_RETAINED RuntimeScheduler(void *scheduler, ScheduleFn fn) noexcept + EXPO_RUNTIME_SCHEDULER_CTOR_RETAINED RuntimeScheduler(void *scheduler, ScheduleFn fn) noexcept : nativeScheduler(scheduler), scheduleFn(fn) {} /** @@ -58,7 +68,7 @@ public: caller's thread — intended for standalone runtimes (e.g. tests) that have no React scheduler. */ - SWIFT_RETURNS_RETAINED RuntimeScheduler() {} + EXPO_RUNTIME_SCHEDULER_CTOR_RETAINED RuntimeScheduler() {} RuntimeScheduler(const RuntimeScheduler &) = delete; diff --git a/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift b/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift index 3d796ee..5d700c3 100644 --- a/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift +++ b/node_modules/expo-modules-jsi/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift @@ -4,6 +4,22 @@ internal import ExpoModulesJSI_Cxx import Foundation internal import jsi +/// Carries a call-scoped raw pointer into the `@JavaScriptActor` closures below. +/// +/// Upstream marks these locals `nonisolated(unsafe)`, which Swift 6.3 (Xcode 27) accepts but +/// Swift 6.2.x (Xcode 26) still rejects with "sending 'ptr' risks causing data races". A +/// `@unchecked Sendable` box is exempt from region checking on both compilers. The pointers +/// are read-only, call-scoped inputs consumed synchronously inside `assumeIsolated`, so this +/// is exactly as sound as the upstream annotation. See https://github.com/expo/expo/issues/50067 +internal struct UnsafeSendableBox: @unchecked Sendable { + let value: Value + + @inline(__always) + init(_ value: Value) { + self.value = value + } +} + /// A Swift wrapper around a JavaScript runtime. Provides access to a JavaScript execution environment, allowing you to evaluate /// JavaScript code, create and manipulate JavaScript objects, functions, and values, and bridge between Swift and JavaScript. /// @@ -185,12 +201,12 @@ open class JavaScriptRuntime: Equatable, Identifiable, @unchecked Sendable { resultPtr: UnsafeMutablePointer ) -> Bool { let propertyName = String(cString: propertyName) - nonisolated(unsafe) let resultPtr = resultPtr + let resultPtr = UnsafeSendableBox(resultPtr) return withGuaranteedContext(context) { (context: HostObjectContext, runtime) in return JavaScriptActor.assumeIsolated { return forwardingSwiftErrorsToJS(runtime: runtime) { - try context.get(propertyName).writeJSIValue(to: resultPtr) + try context.get(propertyName).writeJSIValue(to: resultPtr.value) } } } @@ -774,19 +790,19 @@ private func createFunctionClosure( // synchronous call, so the `nonisolated(unsafe)` capture is sound. This removes a per-call class // allocation + retain/release + dealloc that profiling showed dominating the no-op `@JS` host-call // floor. - nonisolated(unsafe) let thisPtr = thisPtr - nonisolated(unsafe) let argumentsPtr = argumentsPtr - nonisolated(unsafe) let resultPtr = resultPtr + let thisPtr = UnsafeSendableBox(thisPtr) + let argumentsPtr = UnsafeSendableBox(argumentsPtr) + let resultPtr = UnsafeSendableBox(resultPtr) // See `withGuaranteedContext` for why neither the context nor the runtime is retained here, and // why the result is written to the caller's slot instead of being returned. return withGuaranteedContext(context) { (context: HostFunctionContext, runtime) in return JavaScriptActor.assumeIsolated { return forwardingSwiftErrorsToJS(runtime: runtime) { - let this = UnsafeMutablePointer(mutating: thisPtr).move() - let arguments = JavaScriptValuesBuffer(runtime, start: argumentsPtr, count: argumentsCount) + let this = UnsafeMutablePointer(mutating: thisPtr.value).move() + let arguments = JavaScriptValuesBuffer(runtime, start: argumentsPtr.value, count: argumentsCount) let thisValue = JavaScriptValue(runtime, this) - try context.call(thisValue, consume arguments).writeJSIValue(to: resultPtr) + try context.call(thisValue, consume arguments).writeJSIValue(to: resultPtr.value) } } } @@ -817,18 +833,18 @@ private func createFunctionClosure( // handed in as a borrowed `JavaScriptUnownedValue` pointing straight at the C++-owned `this` slot: // it is not moved out and no owning `JavaScriptValue` is allocated, so the closure avoids the // per-call `weak`-runtime form/destroy and heap object that the owning `this` pays. - nonisolated(unsafe) let thisPtr = thisPtr - nonisolated(unsafe) let argumentsPtr = argumentsPtr - nonisolated(unsafe) let resultPtr = resultPtr + let thisPtr = UnsafeSendableBox(thisPtr) + let argumentsPtr = UnsafeSendableBox(argumentsPtr) + let resultPtr = UnsafeSendableBox(resultPtr) // See `withGuaranteedContext` for why neither the context nor the runtime is retained here, and // why the result is written to the caller's slot instead of being returned. return withGuaranteedContext(context) { (context: UnownedThisHostFunctionContext, runtime) in return JavaScriptActor.assumeIsolated { return forwardingSwiftErrorsToJS(runtime: runtime) { - let arguments = JavaScriptValuesBuffer(runtime, start: argumentsPtr, count: argumentsCount) - let thisValue = JavaScriptUnownedValue(runtime.pointee, thisPtr) - try context.call(thisValue, consume arguments).writeJSIValue(to: resultPtr) + let arguments = JavaScriptValuesBuffer(runtime, start: argumentsPtr.value, count: argumentsCount) + let thisValue = JavaScriptUnownedValue(runtime.pointee, thisPtr.value) + try context.call(thisValue, consume arguments).writeJSIValue(to: resultPtr.value) } } }