@bennyblader/ddk-rn 1.0.0-rc4 → 1.0.0-rc5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DdkRn.podspec CHANGED
@@ -17,7 +17,7 @@ Pod::Spec.new do |s|
17
17
 
18
18
  s.source_files = "ios/**/*.{h,m,mm,swift}", "ios/generated/**/*.{h,m,mm}", "cpp/**/*.{hpp,cpp,c,h}", "cpp/**/*.{hpp,cpp,c,h}"
19
19
  s.vendored_frameworks = "ios/DdkRn.xcframework"
20
- s.dependency "uniffi-bindgen-react-native", "0.31.0-3"
20
+ s.dependency "uniffi-bindgen-react-native", "0.31.0-5"
21
21
 
22
22
  # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
23
23
  # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
package/README.md CHANGED
@@ -296,19 +296,6 @@ there, and a dozen transaction operations are methods on a record here
296
296
  - iOS: Set `RCT_NEW_ARCH_ENABLED=1`
297
297
  - Android: Set `newArchEnabled=true` in `gradle.properties`
298
298
 
299
- 2. **Android C++ builds need an unreleased ubrn fix.** The generated
300
- `android/CMakeLists.txt` resolves the ubrn package with
301
- `require.resolve('uniffi-bindgen-react-native/package.json')`, which throws
302
- `ERR_PACKAGE_PATH_NOT_EXPORTED` because ubrn's `exports` map doesn't expose
303
- that subpath. The include dir silently becomes `/cpp/includes` and the build
304
- dies with `'UniffiCallInvoker.h' file not found`. This repo patches it
305
- (`patches/uniffi-bindgen-react-native@0.31.0-3.patch`, applied by
306
- `pnpm install`), but a pnpm patch only applies to this repo — building an
307
- Android app against the published package hits the original error until ubrn
308
- ships the fix ([#404](https://github.com/jhugman/uniffi-bindgen-react-native/issues/404),
309
- fixed on `main`, release tracked in
310
- [#421](https://github.com/jhugman/uniffi-bindgen-react-native/issues/421)).
311
-
312
299
  ## Troubleshooting
313
300
 
314
301
  ### iOS Build Issues
@@ -1,14 +1,14 @@
1
1
  // Generated by uniffi-bindgen-react-native
2
2
  package com.ddkrn
3
3
 
4
- import com.facebook.react.TurboReactPackage
4
+ import com.facebook.react.BaseReactPackage
5
5
  import com.facebook.react.bridge.NativeModule
6
6
  import com.facebook.react.bridge.ReactApplicationContext
7
7
  import com.facebook.react.module.model.ReactModuleInfo
8
8
  import com.facebook.react.module.model.ReactModuleInfoProvider
9
9
  import java.util.HashMap
10
10
 
11
- class DdkRnPackage : TurboReactPackage() {
11
+ class DdkRnPackage : BaseReactPackage() {
12
12
  override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
13
13
  return if (name == DdkRnModule.NAME) {
14
14
  DdkRnModule(reactContext)
package/cpp/ddk_ffi.cpp CHANGED
@@ -862,18 +862,58 @@ template <> struct Bridging<RustBuffer> {
862
862
  static RustBuffer fromJs(jsi::Runtime &rt, std::shared_ptr<CallInvoker>,
863
863
  const jsi::Value &value) {
864
864
  try {
865
+ auto obj = value.asObject(rt);
866
+
867
+ // Adoption vs copy. Two kinds of view arrive here:
868
+ //
869
+ // * Library-owned views from `rustbuffer_alloc`. Codegen allocates one,
870
+ // fills it in place, and ships it as an argument — and never frees a
871
+ // lowered argument, while the view's backing `CMutableBuffer` is
872
+ // non-owning (its destructor leaves `data` alone). Such a view carries
873
+ // a capacity hint (stamped by `rustbuffer_alloc` in the wrapper). We
874
+ // *adopt* it: hand the existing allocation to the callee, which frees
875
+ // it. Copying instead would orphan the allocation and leak one whole
876
+ // payload per call.
877
+ // * Ordinary JS-owned arrays, which carry no hint. These are *copied*
878
+ // into a fresh library allocation — they are not ours to give away.
879
+ //
880
+ // On adoption we reset the hint to 0 so a later `rustbuffer_free(view)` is
881
+ // a no-op rather than a double free.
882
+ if (obj.hasProperty(rt, uniffi_jsi::kUbrnRustCapacity)) {
883
+ auto capacity = static_cast<uint64_t>(
884
+ obj.getProperty(rt, uniffi_jsi::kUbrnRustCapacity).asNumber());
885
+ if (capacity == 0) {
886
+ // Hint present but zeroed: the view was already adopted by a previous
887
+ // call and its memory has been freed. Reading it would be a
888
+ // use-after-free, so refuse rather than hand over a dangling pointer.
889
+ throw jsi::JSError(
890
+ rt,
891
+ "RustBuffer argument was already consumed by a previous FFI call");
892
+ }
893
+ auto arrayBuffer =
894
+ obj.getPropertyAsObject(rt, "buffer").getArrayBuffer(rt);
895
+ auto byteOffset =
896
+ static_cast<size_t>(obj.getProperty(rt, "byteOffset").asNumber());
897
+ auto byteLength =
898
+ static_cast<size_t>(obj.getProperty(rt, "byteLength").asNumber());
899
+ obj.setProperty(rt, uniffi_jsi::kUbrnRustCapacity, jsi::Value(0));
900
+ return RustBuffer{
901
+ .capacity = capacity,
902
+ .len = static_cast<uint64_t>(byteLength),
903
+ .data = arrayBuffer.data(rt) + byteOffset,
904
+ };
905
+ }
906
+
865
907
  auto buffer = uniffi_jsi::Bridging<jsi::ArrayBuffer>::value_to_arraybuffer(rt, value);
866
908
  auto bytes = ForeignBytes{
867
909
  .len = static_cast<int32_t>(buffer.length(rt)),
868
910
  .data = buffer.data(rt),
869
911
  };
870
912
 
871
- // This buffer is constructed from foreign bytes. Rust scaffolding copies
872
- // the bytes, to make the RustBuffer.
913
+ // No hint: an ordinary JS-owned array. Rust scaffolding copies the bytes
914
+ // to make the RustBuffer; the copy is destroyed when the callee
915
+ // deserializes its arguments.
873
916
  auto buf = rustbuffer_from_bytes(bytes);
874
- // Once it leaves this function, the buffer is immediately passed back
875
- // into Rust, where it's used to deserialize into the Rust versions of the
876
- // arguments. At that point, the copy is destroyed.
877
917
  return buf;
878
918
  } catch (const std::logic_error &e) {
879
919
  throw jsi::JSError(rt, e.what());
@@ -3117,14 +3157,24 @@ NativeDdkFfi::NativeDdkFfi(
3117
3157
  throw jsi::JSError(rt, "rustbuffer_alloc failed: alloc returned null");
3118
3158
  }
3119
3159
  // Non-owning view over Rust-allocated memory; CMutableBuffer's destructor
3120
- // is the default and does not free `rb.data`. JS must call rustbuffer_free
3121
- // explicitly before dropping the reference.
3160
+ // is the default and does not free `rb.data`. The allocation is released
3161
+ // either by an explicit `rustbuffer_free(view)` or by being adopted when
3162
+ // the view is lowered as an FFI argument.
3122
3163
  auto payload = std::make_shared<uniffi_jsi::CMutableBuffer>(
3123
3164
  rb.data, static_cast<size_t>(rb.capacity));
3124
3165
  // Wrap as Uint8Array so JS can index/assign bytes directly.
3125
- return jsi::Value(
3126
- rt, uniffi_jsi::arraybufferToUint8Array(
3127
- rt, jsi::ArrayBuffer(rt, payload)));
3166
+ auto view = uniffi_jsi::arraybufferToUint8Array(
3167
+ rt, jsi::ArrayBuffer(rt, payload));
3168
+ // Stamp the capacity so the argument-lowering path
3169
+ // (`Bridging<RustBuffer>::fromJs`) recognises this view as
3170
+ // library-owned and adopts the allocation instead of copying it.
3171
+ // Without the stamp, a lowered argument is copied and this
3172
+ // allocation is orphaned — one leaked payload per call.
3173
+ if (rb.capacity > 0) {
3174
+ view.setProperty(rt, uniffi_jsi::kUbrnRustCapacity,
3175
+ jsi::Value(static_cast<double>(rb.capacity)));
3176
+ }
3177
+ return jsi::Value(rt, view);
3128
3178
  }
3129
3179
  );
3130
3180
 
@@ -3158,6 +3208,12 @@ NativeDdkFfi::NativeDdkFfi(
3158
3208
  capacity = static_cast<size_t>(
3159
3209
  view.getProperty(rt, uniffi_jsi::kUbrnRustCapacity).asNumber());
3160
3210
  }
3211
+ // A zero capacity marks a view already adopted as an FFI argument
3212
+ // (its hint was reset to 0) and freed by the callee. Freeing again
3213
+ // would be a double free, so this is a no-op.
3214
+ if (capacity == 0) {
3215
+ return jsi::Value::undefined();
3216
+ }
3161
3217
  auto buffer = view.getPropertyAsObject(rt, "buffer").getArrayBuffer(rt);
3162
3218
  auto byteOffset =
3163
3219
  static_cast<size_t>(view.getProperty(rt, "byteOffset").asNumber());
@@ -8,7 +8,7 @@
8
8
  <key>BinaryPath</key>
9
9
  <string>libddk_ffi.a</string>
10
10
  <key>LibraryIdentifier</key>
11
- <string>ios-arm64</string>
11
+ <string>ios-arm64-simulator</string>
12
12
  <key>LibraryPath</key>
13
13
  <string>libddk_ffi.a</string>
14
14
  <key>SupportedArchitectures</key>
@@ -17,12 +17,14 @@
17
17
  </array>
18
18
  <key>SupportedPlatform</key>
19
19
  <string>ios</string>
20
+ <key>SupportedPlatformVariant</key>
21
+ <string>simulator</string>
20
22
  </dict>
21
23
  <dict>
22
24
  <key>BinaryPath</key>
23
25
  <string>libddk_ffi.a</string>
24
26
  <key>LibraryIdentifier</key>
25
- <string>ios-arm64-simulator</string>
27
+ <string>ios-arm64</string>
26
28
  <key>LibraryPath</key>
27
29
  <string>libddk_ffi.a</string>
28
30
  <key>SupportedArchitectures</key>
@@ -31,8 +33,6 @@
31
33
  </array>
32
34
  <key>SupportedPlatform</key>
33
35
  <string>ios</string>
34
- <key>SupportedPlatformVariant</key>
35
- <string>simulator</string>
36
36
  </dict>
37
37
  </array>
38
38
  <key>CFBundlePackageType</key>