From a1d12c63d98946db138c702b889a5b686df1ff59 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Fri, 6 Sep 2024 16:20:02 +0200 Subject: [PATCH] fix: Fix bridge not being used for callbacks (#106) * fix: Also support more types * fix: I guess `ArrayBufferHolder` now works bidirectionally * fix: Fix header includes --- .../src/screens/HybridObjectTestsScreen.tsx | 11 +- .../src/autolinking/createSwiftCxxBridge.ts | 2 +- .../src/syntax/swift/SwiftCxxBridgedType.ts | 32 +++++- .../src/syntax/swift/SwiftCxxTypeHelper.ts | 28 +++-- .../ios/HybridSwiftKotlinTestObject.swift | 4 +- .../ios/NitroImage-Swift-Cxx-Bridge.hpp | 107 +++++++++++++----- .../HybridSwiftKotlinTestObjectSpecSwift.hpp | 2 +- .../HybridSwiftKotlinTestObjectSpec.swift | 2 +- .../HybridSwiftKotlinTestObjectSpecCxx.swift | 10 +- .../c++/HybridSwiftKotlinTestObjectSpec.hpp | 2 +- .../src/specs/TestObject.nitro.ts | 4 +- 11 files changed, 149 insertions(+), 55 deletions(-) diff --git a/example/src/screens/HybridObjectTestsScreen.tsx b/example/src/screens/HybridObjectTestsScreen.tsx index 6ba51f074..46262f401 100644 --- a/example/src/screens/HybridObjectTestsScreen.tsx +++ b/example/src/screens/HybridObjectTestsScreen.tsx @@ -1,13 +1,22 @@ import * as React from 'react' import { StyleSheet, View, Text, ScrollView, Button } from 'react-native' -import { HybridTestObject } from 'react-native-nitro-image' +import { + HybridKotlinTestObject, + HybridTestObject, +} from 'react-native-nitro-image' import { getTests, type TestRunner } from '../getTests' import { SafeAreaView } from 'react-native-safe-area-context' import { logPrototypeChain } from '../logPrototypeChain' logPrototypeChain(HybridTestObject) +HybridKotlinTestObject.doSomeStuff((p, str, buf) => { + console.log(p) + console.log(str) + console.log(buf) +}) + const allTests = getTests() interface TestState { diff --git a/packages/nitrogen/src/autolinking/createSwiftCxxBridge.ts b/packages/nitrogen/src/autolinking/createSwiftCxxBridge.ts index 3be52f40f..458ec5209 100644 --- a/packages/nitrogen/src/autolinking/createSwiftCxxBridge.ts +++ b/packages/nitrogen/src/autolinking/createSwiftCxxBridge.ts @@ -32,7 +32,7 @@ export function createSwiftCxxBridge(): SourceFile[] { const requiredImports = bridges.flatMap((b) => b.requiredIncludes) const includes = requiredImports - .map((i) => includeHeader(i)) + .map((i) => includeHeader(i, false)) .filter(isNotDuplicate) const forwardDeclarations = requiredImports .map((i) => i.forwardDeclaration) diff --git a/packages/nitrogen/src/syntax/swift/SwiftCxxBridgedType.ts b/packages/nitrogen/src/syntax/swift/SwiftCxxBridgedType.ts index 2bdd409d8..35766a5e7 100644 --- a/packages/nitrogen/src/syntax/swift/SwiftCxxBridgedType.ts +++ b/packages/nitrogen/src/syntax/swift/SwiftCxxBridgedType.ts @@ -251,6 +251,12 @@ export class SwiftCxxBridgedType implements BridgedType<'swift', 'c++'> { return this.type.getCode(language) } } + case 'array-buffer': + if (this.isBridgingToDirectCppTarget) { + return this.type.getCode(language) + } else { + return `ArrayBufferHolder` + } case 'string': { switch (language) { case 'c++': @@ -316,8 +322,18 @@ export class SwiftCxxBridgedType implements BridgedType<'swift', 'c++'> { } case 'array-buffer': { switch (language) { + case 'swift': + if (this.isBridgingToDirectCppTarget) { + return `ArrayBufferHolder(${cppParameterName})` + } else { + return cppParameterName + } case 'c++': - return `ArrayBufferHolder(${cppParameterName})` + if (this.isBridgingToDirectCppTarget) { + return cppParameterName + } else { + return `ArrayBufferHolder(${cppParameterName})` + } default: return cppParameterName } @@ -444,7 +460,7 @@ case ${i}: const returnType = funcType.returnType.getCode('swift') const signature = `(${paramsSignature.join(', ')}) -> ${returnType}` const paramsForward = funcType.parameters.map((p) => { - const bridged = new SwiftCxxBridgedType(p, true) + const bridged = new SwiftCxxBridgedType(p) return bridged.parseFromSwiftToCpp(p.escapedName, 'swift') }) @@ -537,8 +553,18 @@ case ${i}: } case 'array-buffer': { switch (language) { + case 'swift': + if (this.isBridgingToDirectCppTarget) { + return `${swiftParameterName}.getArrayBuffer()` + } else { + return swiftParameterName + } case 'c++': - return `${swiftParameterName}.getArrayBuffer()` + if (this.isBridgingToDirectCppTarget) { + return swiftParameterName + } else { + return `${swiftParameterName}.getArrayBuffer()` + } default: return swiftParameterName } diff --git a/packages/nitrogen/src/syntax/swift/SwiftCxxTypeHelper.ts b/packages/nitrogen/src/syntax/swift/SwiftCxxTypeHelper.ts index 016a90c95..fbc30f61e 100644 --- a/packages/nitrogen/src/syntax/swift/SwiftCxxTypeHelper.ts +++ b/packages/nitrogen/src/syntax/swift/SwiftCxxTypeHelper.ts @@ -46,6 +46,7 @@ export function createSwiftCxxHelpers(type: Type): SwiftCxxHelper | undefined { */ function createCxxOptionalSwiftHelper(type: OptionalType): SwiftCxxHelper { const actualType = type.getCode('c++') + const bridgedType = new SwiftCxxBridgedType(type) const name = escapeCppName(actualType) return { cxxType: actualType, @@ -57,7 +58,7 @@ function createCxxOptionalSwiftHelper(type: OptionalType): SwiftCxxHelper { space: 'system', language: 'c++', }, - ...type.getRequiredImports(), + ...bridgedType.getRequiredImports(), ], cxxCode: ` /** @@ -76,6 +77,7 @@ inline ${actualType} create_${name}(const ${type.wrappingType.getCode('c++')}& v */ function createCxxVectorSwiftHelper(type: ArrayType): SwiftCxxHelper { const actualType = type.getCode('c++') + const bridgedType = new SwiftCxxBridgedType(type) const name = escapeCppName(actualType) return { cxxType: actualType, @@ -87,7 +89,7 @@ function createCxxVectorSwiftHelper(type: ArrayType): SwiftCxxHelper { space: 'system', language: 'c++', }, - ...type.getRequiredImports(), + ...bridgedType.getRequiredImports(), ], cxxCode: ` /** @@ -108,6 +110,7 @@ inline ${actualType} create_${name}(size_t size) { */ function createCxxUnorderedMapSwiftHelper(type: RecordType): SwiftCxxHelper { const actualType = type.getCode('c++') + const bridgedType = new SwiftCxxBridgedType(type) const name = escapeCppName(actualType) const keyType = type.keyType.getCode('c++') return { @@ -120,7 +123,7 @@ function createCxxUnorderedMapSwiftHelper(type: RecordType): SwiftCxxHelper { space: 'system', language: 'c++', }, - ...type.getRequiredImports(), + ...bridgedType.getRequiredImports(), ], cxxCode: ` /** @@ -149,6 +152,7 @@ inline std::vector<${keyType}> get_${name}_keys(const ${name}& map) { */ function createCxxFunctionSwiftHelper(type: FunctionType): SwiftCxxHelper { const actualType = type.getCode('c++') + const bridgedType = new SwiftCxxBridgedType(type) const returnBridge = new SwiftCxxBridgedType(type.returnType) const paramsSignature = type.parameters.map((p) => { if (p.canBePassedByReference) { @@ -157,6 +161,11 @@ function createCxxFunctionSwiftHelper(type: FunctionType): SwiftCxxHelper { return `${p.getCode('c++')} ${p.escapedName}` } }) + const callCppFuncParamsSignature = type.parameters.map((p) => { + const bridge = new SwiftCxxBridgedType(p) + const cppType = bridge.getTypeCode('c++') + return `${cppType} ${p.escapedName}` + }) const callParamsForward = type.parameters.map((p) => { const bridge = new SwiftCxxBridgedType(p) return bridge.parseFromSwiftToCpp(p.escapedName, 'c++') @@ -219,7 +228,7 @@ function createCxxFunctionSwiftHelper(type: FunctionType): SwiftCxxHelper { space: 'system', language: 'c++', }, - ...type.getRequiredImports(), + ...bridgedType.getRequiredImports(), ], cxxCode: ` /** @@ -234,7 +243,7 @@ public: explicit ${wrapperName}(const ${actualType}& func): function(func) {} explicit ${wrapperName}(${actualType}&& func): function(std::move(func)) {} - ${callFuncReturnType} call(${paramsSignature.join(', ')}) const { + ${callFuncReturnType} call(${callCppFuncParamsSignature.join(', ')}) const { ${callCppFuncBody} } @@ -258,6 +267,7 @@ inline std::shared_ptr<${wrapperName}> share_${name}(const ${name}& value) { */ function createCxxVariantSwiftHelper(type: VariantType): SwiftCxxHelper { const actualType = type.getCode('c++') + const bridgedType = new SwiftCxxBridgedType(type) const name = escapeCppName(actualType) const createFunctions = type.variants .map((t) => { @@ -290,7 +300,7 @@ inline ${t.getCode('c++')} get_${name}_${i}(const ${actualType}& variant) { space: 'system', language: 'c++', }, - ...type.getRequiredImports(), + ...bridgedType.getRequiredImports(), ], cxxCode: ` /** @@ -308,6 +318,7 @@ ${getFunctions} */ function createCxxTupleSwiftHelper(type: TupleType): SwiftCxxHelper { const actualType = type.getCode('c++') + const bridgedType = new SwiftCxxBridgedType(type) const name = escapeCppName(actualType) const typesSignature = type.itemTypes .map((t, i) => { @@ -326,7 +337,7 @@ function createCxxTupleSwiftHelper(type: TupleType): SwiftCxxHelper { space: 'system', language: 'c++', }, - ...type.getRequiredImports(), + ...bridgedType.getRequiredImports(), ], cxxCode: ` /** @@ -345,6 +356,7 @@ inline ${actualType} create_${name}(${typesSignature}) { */ function createCxxPromiseSwiftHelper(type: PromiseType): SwiftCxxHelper { const resultingType = type.resultingType.getCode('c++') + const bridgedType = new SwiftCxxBridgedType(type) const actualType = `PromiseHolder<${resultingType}>` const name = escapeCppName(actualType) return { @@ -357,7 +369,7 @@ function createCxxPromiseSwiftHelper(type: PromiseType): SwiftCxxHelper { space: 'system', language: 'c++', }, - ...type.getRequiredImports(), + ...bridgedType.getRequiredImports(), ], cxxCode: ` /** diff --git a/packages/react-native-nitro-image/ios/HybridSwiftKotlinTestObject.swift b/packages/react-native-nitro-image/ios/HybridSwiftKotlinTestObject.swift index 7c62bc596..43db8401a 100644 --- a/packages/react-native-nitro-image/ios/HybridSwiftKotlinTestObject.swift +++ b/packages/react-native-nitro-image/ios/HybridSwiftKotlinTestObject.swift @@ -13,9 +13,9 @@ class HybridSwiftKotlinTestObject : HybridSwiftKotlinTestObjectSpec { args.callback() } - func doSomeStuff(withEnum callback: @escaping ((Powertrain) -> Void)) throws { + func doSomeStuff(withEnum callback: @escaping ((Powertrain, String, ArrayBufferHolder) -> Void)) throws { DispatchQueue.global().asyncAfter(deadline: .now() + 2) { - callback(.gas) + callback(.gas, "HELLO!", .allocate(size: 2048)) } } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImage-Swift-Cxx-Bridge.hpp b/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImage-Swift-Cxx-Bridge.hpp index b6d6c406e..bbb5f7b10 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImage-Swift-Cxx-Bridge.hpp +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImage-Swift-Cxx-Bridge.hpp @@ -9,8 +9,14 @@ #pragma once // Forward declarations of C++ defined types +// Forward declaration of `ArrayBufferHolder` to properly resolve imports. +namespace NitroModules { class ArrayBufferHolder; } +// Forward declaration of `ArrayBuffer` to properly resolve imports. +namespace NitroModules { class ArrayBuffer; } // Forward declaration of `Car` to properly resolve imports. namespace margelo::nitro::image { struct Car; } +// Forward declaration of `HybridTestObjectSpecSwift` to properly resolve imports. +namespace margelo::nitro::image { class HybridTestObjectSpecSwift; } // Forward declaration of `HybridTestObjectSpec` to properly resolve imports. namespace margelo::nitro::image { class HybridTestObjectSpec; } // Forward declaration of `OldEnum` to properly resolve imports. @@ -21,21 +27,60 @@ namespace margelo::nitro::image { struct Person; } namespace margelo::nitro::image { enum class Powertrain; } // Include C++ defined types -#include "Car.hpp" -#include "HybridTestObjectSpec.hpp" -#include "OldEnum.hpp" -#include "Person.hpp" -#include "Powertrain.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#if __has_include("Car.hpp") + #include "Car.hpp" +#endif +#if __has_include("HybridTestObjectSpec.hpp") + #include "HybridTestObjectSpec.hpp" +#endif +#if __has_include("HybridTestObjectSpecSwift.hpp") + #include "HybridTestObjectSpecSwift.hpp" +#endif +#if __has_include("OldEnum.hpp") + #include "OldEnum.hpp" +#endif +#if __has_include("Person.hpp") + #include "Person.hpp" +#endif +#if __has_include("Powertrain.hpp") + #include "Powertrain.hpp" +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif +#if __has_include() + #include +#endif /** * Contains specialized versions of C++ templated types so they can be accessed from Swift, @@ -55,7 +100,7 @@ namespace margelo::nitro::image::bridge::swift { explicit Func_void_std__string_Wrapper(const std::function& func): function(func) {} explicit Func_void_std__string_Wrapper(std::function&& func): function(std::move(func)) {} - void call(const std::string& path) const { + void call(std::string path) const { function(path); } @@ -422,31 +467,31 @@ namespace margelo::nitro::image::bridge::swift { } /** - * Specialized version of `std::function`. + * Specialized version of `std::function&)>`. */ - using Func_void_Powertrain = std::function; + using Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_ = std::function& /* buf */)>; /** - * Wrapper class for a `std::function`, this can be used from Swift. + * Wrapper class for a `std::function& / * buf * /)>`, this can be used from Swift. */ - class Func_void_Powertrain_Wrapper { + class Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer__Wrapper { public: - explicit Func_void_Powertrain_Wrapper(const std::function& func): function(func) {} - explicit Func_void_Powertrain_Wrapper(std::function&& func): function(std::move(func)) {} + explicit Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer__Wrapper(const std::function& /* buf */)>& func): function(func) {} + explicit Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer__Wrapper(std::function& /* buf */)>&& func): function(std::move(func)) {} - void call(Powertrain value) const { - function(static_cast(value)); + void call(int value, std::string str, ArrayBufferHolder buf) const { + function(static_cast(value), str, buf.getArrayBuffer()); } - std::function function; + std::function& /* buf */)> function; }; - inline Func_void_Powertrain create_Func_void_Powertrain(void* closureHolder, void(*call)(void* /* closureHolder */, int), void(*destroy)(void*)) { + inline Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_ create_Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_(void* closureHolder, void(*call)(void* /* closureHolder */, int, std::string, ArrayBufferHolder), void(*destroy)(void*)) { std::shared_ptr sharedClosureHolder(closureHolder, destroy); - return Func_void_Powertrain([sharedClosureHolder, call](Powertrain value) -> void { - call(sharedClosureHolder.get(), static_cast(value)); + return Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_([sharedClosureHolder, call](Powertrain value, const std::string& str, const std::shared_ptr& buf) -> void { + call(sharedClosureHolder.get(), static_cast(value), str, ArrayBufferHolder(buf)); }); } - inline std::shared_ptr share_Func_void_Powertrain(const Func_void_Powertrain& value) { - return std::make_shared(value); + inline std::shared_ptr share_Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_(const Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_& value) { + return std::make_shared(value); } /** @@ -497,7 +542,7 @@ namespace margelo::nitro::image::bridge::swift { explicit Func_void_Person_Wrapper(const std::function& func): function(func) {} explicit Func_void_Person_Wrapper(std::function&& func): function(std::move(func)) {} - void call(const Person& p) const { + void call(Person p) const { function(p); } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridSwiftKotlinTestObjectSpecSwift.hpp b/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridSwiftKotlinTestObjectSpecSwift.hpp index 0a3cb2b8e..d037c060c 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridSwiftKotlinTestObjectSpecSwift.hpp +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridSwiftKotlinTestObjectSpecSwift.hpp @@ -249,7 +249,7 @@ namespace margelo::nitro::image { auto __result = _swiftPart.getCarAsync(); return __result.getFuture(); } - inline void doSomeStuff(const std::function& withEnum) override { + inline void doSomeStuff(const std::function& /* buf */)>& withEnum) override { _swiftPart.doSomeStuff(withEnum); } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpec.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpec.swift index 4bb272d19..27b07a90f 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpec.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpec.swift @@ -61,7 +61,7 @@ public protocol HybridSwiftKotlinTestObjectSpec: HybridObjectSpec { func getNumberAsync() throws -> Promise func getStringAsync() throws -> Promise func getCarAsync() throws -> Promise - func doSomeStuff(withEnum: @escaping ((_ value: Powertrain) -> Void)) throws -> Void + func doSomeStuff(withEnum: @escaping ((_ value: Powertrain, _ str: String, _ buf: ArrayBufferHolder) -> Void)) throws -> Void } public extension HybridSwiftKotlinTestObjectSpec { diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpecCxx.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpecCxx.swift index deee20d7a..3e8a39b0a 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpecCxx.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridSwiftKotlinTestObjectSpecCxx.swift @@ -496,12 +496,12 @@ public final class HybridSwiftKotlinTestObjectSpecCxx { } @inline(__always) - public func doSomeStuff(withEnum: bridge.Func_void_Powertrain) -> Void { + public func doSomeStuff(withEnum: bridge.Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_) -> Void { do { - try self.implementation.doSomeStuff(withEnum: { () -> ((Powertrain) -> Void) in - let shared = bridge.share_Func_void_Powertrain(withEnum) - return { (value: Powertrain) -> Void in - shared.pointee.call(value) + try self.implementation.doSomeStuff(withEnum: { () -> ((Powertrain, String, ArrayBufferHolder) -> Void) in + let shared = bridge.share_Func_void_Powertrain_std__string_std__shared_ptr_ArrayBuffer_(withEnum) + return { (value: Powertrain, str: String, buf: ArrayBufferHolder) -> Void in + shared.pointee.call(value.rawValue, std.string(str), buf) } }()) return diff --git a/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridSwiftKotlinTestObjectSpec.hpp b/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridSwiftKotlinTestObjectSpec.hpp index 561781043..0b990848a 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridSwiftKotlinTestObjectSpec.hpp +++ b/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridSwiftKotlinTestObjectSpec.hpp @@ -115,7 +115,7 @@ namespace margelo::nitro::image { virtual std::future getNumberAsync() = 0; virtual std::future getStringAsync() = 0; virtual std::future getCarAsync() = 0; - virtual void doSomeStuff(const std::function& withEnum) = 0; + virtual void doSomeStuff(const std::function& /* buf */)>& withEnum) = 0; protected: // Hybrid Setup diff --git a/packages/react-native-nitro-image/src/specs/TestObject.nitro.ts b/packages/react-native-nitro-image/src/specs/TestObject.nitro.ts index 201cd51e1..6a031eaa2 100644 --- a/packages/react-native-nitro-image/src/specs/TestObject.nitro.ts +++ b/packages/react-native-nitro-image/src/specs/TestObject.nitro.ts @@ -147,7 +147,9 @@ export interface SwiftKotlinTestObject extends HybridObject<{ ios: 'swift' }> { getStringAsync(): Promise getCarAsync(): Promise - doSomeStuff(withEnum: (value: Powertrain) => void): void + doSomeStuff( + withEnum: (value: Powertrain, str: string, buf: ArrayBuffer) => void + ): void } export interface KotlinTestObject extends HybridObject<{ android: 'kotlin' }> {