react-native-nitro-modules 0.5.0 → 0.7.0
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/NitroModules.podspec +3 -1
- package/README.md +33 -14
- package/android/gradle.properties +5 -5
- package/android/src/main/cpp/JNIOnLoad.cpp +8 -0
- package/android/src/main/cpp/core/ByteBufferArrayBuffer.hpp +47 -0
- package/android/src/main/cpp/core/JAnyMap.hpp +193 -0
- package/android/src/main/cpp/core/JAnyValue.hpp +191 -0
- package/android/src/main/cpp/core/JArrayBuffer.hpp +124 -0
- package/android/src/main/cpp/core/JHybridObject.hpp +11 -4
- package/android/src/main/cpp/core/JPromise.hpp +88 -0
- package/android/src/main/cpp/platform/NitroLogger.cpp +36 -0
- package/android/src/main/cpp/registry/JHybridObjectInitializer.hpp +1 -1
- package/android/src/main/cpp/registry/JHybridObjectRegistry.cpp +1 -1
- package/android/src/main/cpp/registry/JHybridObjectRegistry.hpp +1 -1
- package/android/src/main/cpp/utils/{JNISharedPtr.h → JNISharedPtr.hpp} +13 -1
- package/android/src/main/java/com/margelo/nitro/JNIOnLoad.java +27 -0
- package/android/src/main/java/com/margelo/nitro/NitroModulesPackage.java +1 -9
- package/android/src/main/java/com/margelo/nitro/core/AnyMap.kt +80 -0
- package/android/src/main/java/com/margelo/nitro/core/AnyValue.kt +164 -0
- package/android/src/main/java/com/margelo/nitro/core/ArrayBuffer.kt +115 -0
- package/android/src/main/java/com/margelo/nitro/{HybridObject.kt → core/HybridObject.kt} +12 -15
- package/android/src/main/java/com/margelo/nitro/{HybridObjectInitializer.java → core/HybridObjectInitializer.java} +1 -1
- package/android/src/main/java/com/margelo/nitro/{HybridObjectRegistry.java → core/HybridObjectRegistry.java} +6 -4
- package/android/src/main/java/com/margelo/nitro/core/Promise.kt +115 -0
- package/cpp/core/AnyMap.hpp +2 -2
- package/cpp/core/ArrayBuffer.cpp +5 -5
- package/cpp/core/ArrayBuffer.hpp +7 -9
- package/cpp/core/HybridFunction.hpp +120 -46
- package/cpp/core/HybridObject.cpp +17 -6
- package/cpp/core/HybridObject.hpp +32 -8
- package/cpp/jsi/JSICache.cpp +5 -5
- package/cpp/jsi/JSICache.hpp +1 -3
- package/cpp/jsi/JSIConverter+AnyMap.hpp +2 -2
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +2 -2
- package/cpp/jsi/JSIConverter+Function.hpp +7 -6
- package/cpp/jsi/JSIConverter+HybridObject.hpp +6 -5
- package/cpp/jsi/JSIConverter+Optional.hpp +1 -1
- package/cpp/jsi/JSIConverter+Promise.hpp +5 -4
- package/cpp/jsi/JSIConverter+Tuple.hpp +1 -1
- package/cpp/jsi/JSIConverter+UnorderedMap.hpp +1 -1
- package/cpp/jsi/JSIConverter+Variant.hpp +1 -3
- package/cpp/jsi/JSIConverter+Vector.hpp +1 -1
- package/cpp/jsi/JSIConverter.hpp +11 -11
- package/cpp/jsi/JSPromise.cpp +2 -2
- package/cpp/platform/NitroLogger.hpp +67 -0
- package/cpp/prototype/HybridObjectPrototype.cpp +2 -2
- package/cpp/prototype/Prototype.hpp +43 -22
- package/cpp/registry/HybridObjectRegistry.cpp +4 -4
- package/cpp/threading/Dispatcher.cpp +4 -3
- package/cpp/threading/ThreadPool.cpp +2 -2
- package/cpp/turbomodule/NativeNitroModules.cpp +26 -8
- package/cpp/turbomodule/NativeNitroModules.h +1 -0
- package/cpp/turbomodule/NativeNitroModules.hpp +2 -0
- package/cpp/utils/TypeInfo.hpp +14 -0
- package/ios/core/AnyMapHolder.hpp +91 -0
- package/ios/core/AnyMapHolder.swift +316 -0
- package/ios/core/ArrayBufferHolder.hpp +7 -5
- package/ios/core/ArrayBufferHolder.swift +22 -7
- package/ios/core/HybridContext.hpp +3 -3
- package/ios/core/Promise.swift +20 -0
- package/ios/core/PromiseHolder.hpp +15 -11
- package/ios/platform/NitroLogger.mm +36 -0
- package/ios/platform/ThreadUtils.cpp +1 -1
- package/ios/turbomodule/NitroModuleOnLoad.mm +2 -1
- package/ios/utils/SwiftClosure.hpp +63 -0
- package/ios/utils/SwiftClosure.swift +58 -0
- package/lib/AnyMap.d.ts +3 -0
- package/lib/HybridObject.d.ts +15 -0
- package/lib/NativeNitroModules.d.ts +2 -0
- package/lib/NitroModules.d.ts +18 -0
- package/lib/NitroModules.js +24 -0
- package/lib/commonjs/NativeNitroModules.js.map +1 -1
- package/lib/commonjs/NitroModules.js +24 -0
- package/lib/commonjs/NitroModules.js.map +1 -1
- package/lib/commonjs/package.json +1 -0
- package/lib/module/AnyMap.js +1 -1
- package/lib/module/HybridObject.js +1 -1
- package/lib/module/ModuleNotFoundError.js +2 -0
- package/lib/module/ModuleNotFoundError.js.map +1 -1
- package/lib/module/NativeNitroModules.js +2 -0
- package/lib/module/NativeNitroModules.js.map +1 -1
- package/lib/module/NativeNitroModules.web.js +2 -0
- package/lib/module/NativeNitroModules.web.js.map +1 -1
- package/lib/module/NitroModules.js +26 -0
- package/lib/module/NitroModules.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -12
- package/src/AnyMap.ts +3 -0
- package/src/HybridObject.ts +15 -0
- package/src/NativeNitroModules.ts +5 -0
- package/src/NitroModules.ts +24 -0
- package/android/src/main/cpp/core/JHybridObject.cpp +0 -8
- package/cpp/templates/IsInPack.hpp +0 -21
- package/cpp/utils/NitroLogger.hpp +0 -58
- package/ios/utils/ClosureWrapper.swift +0 -45
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
package com.margelo.nitro.core
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.Keep
|
|
4
|
+
import com.facebook.jni.HybridData
|
|
5
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
6
|
+
import dalvik.annotation.optimization.CriticalNative
|
|
7
|
+
import java.nio.ByteBuffer
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An ArrayBuffer instance shared between native (Kotlin/C++) and JS.
|
|
11
|
+
*
|
|
12
|
+
* A `ByteBuffer` will be used as the underlying `ArrayBuffer`'s data,
|
|
13
|
+
* which has to remain valid for as long as the `ArrayBuffer` is alive.
|
|
14
|
+
*/
|
|
15
|
+
@Suppress("KotlinJniMissingFunction")
|
|
16
|
+
@Keep
|
|
17
|
+
@DoNotStrip
|
|
18
|
+
class ArrayBuffer {
|
|
19
|
+
/**
|
|
20
|
+
* Holds the native C++ instance of the `ArrayBuffer`.
|
|
21
|
+
*/
|
|
22
|
+
private val mHybridData: HybridData
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether this `ArrayBuffer` is an **owning-**, or a **non-owning-** `ArrayBuffer`.
|
|
26
|
+
* - **Owning** ArrayBuffers can safely be held in memory for longer, and accessed at any point.
|
|
27
|
+
* - **Non-owning** ArrayBuffers can not be held in memory for longer, and can only be safely
|
|
28
|
+
* accessed within the synchronous function's scope (aka on the JS Thread). Once you switch Threads,
|
|
29
|
+
* data access is not safe anymore. If you need to access data longer, copy the data.
|
|
30
|
+
*/
|
|
31
|
+
val isOwner: Boolean
|
|
32
|
+
get() = getIsOwner()
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether this `ArrayBuffer` is holding a `ByteBuffer`, or not.
|
|
36
|
+
* - If the `ArrayBuffer` holds a `ByteBuffer`, `getBuffer(false)` can safely be called to
|
|
37
|
+
* get shared access to the underlying data, without performing any copies.
|
|
38
|
+
* - If the `ArrayBuffer` doesn't hold a `ByteBuffer`, it can still be accessed via `getBuffer(false)`,
|
|
39
|
+
* but the returned `ByteBuffer` is only valid as long as it's parent `ArrayBuffer` is alive.
|
|
40
|
+
*/
|
|
41
|
+
val isByteBuffer: Boolean
|
|
42
|
+
get() = getIsByteBuffer()
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the size of bytes in this `ArrayBuffer`.
|
|
46
|
+
*/
|
|
47
|
+
val size: Int
|
|
48
|
+
get() = getBufferSize()
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get a `ByteBuffer` that holds- or wraps- the underlying data.
|
|
52
|
+
* - If this `ArrayBuffer` has been created from Kotlin/Java, it is already holding a
|
|
53
|
+
* `ByteBuffer` (`isByteBuffer == true`). In this case, the returned buffer is safe to access,
|
|
54
|
+
* even after the `ArrayBuffer` has already been destroyed in JS.
|
|
55
|
+
* - If this `ArrayBuffer` has been created elsewhere (C++/JS), it is not holding a
|
|
56
|
+
* `ByteBuffer` (`isByteBuffer == false`). In this case, the returned buffer will either be a copy
|
|
57
|
+
* of the data (`copyIfNeeded == true`), or just wrapping the data (`copyIfNeeded == false`).
|
|
58
|
+
*
|
|
59
|
+
* @param copyIfNeeded If this `ArrayBuffer` is not holding a `ByteBuffer` (`isByteBuffer == false`),
|
|
60
|
+
* the foreign data needs to be either _wrapped_, or _copied_ to be represented as a `ByteBuffer`.
|
|
61
|
+
* This flag controls that behaviour.
|
|
62
|
+
*/
|
|
63
|
+
fun getBuffer(copyIfNeeded: Boolean): ByteBuffer {
|
|
64
|
+
return getByteBuffer(copyIfNeeded)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a new **owning-** `ArrayBuffer` that holds the given `ByteBuffer`.
|
|
69
|
+
* The `ByteBuffer` needs to remain valid for as long as the `ArrayBuffer` is alive.
|
|
70
|
+
*/
|
|
71
|
+
constructor(byteBuffer: ByteBuffer) {
|
|
72
|
+
if (!byteBuffer.isDirect) {
|
|
73
|
+
throw Error("ArrayBuffers can only be created from direct ByteBuffers, " +
|
|
74
|
+
"and the given ByteBuffer is not direct!")
|
|
75
|
+
}
|
|
76
|
+
mHybridData = initHybrid(byteBuffer)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a new **non-owning-** `ArrayBuffer` that holds foreign data, potentially coming from JS.
|
|
81
|
+
*/
|
|
82
|
+
@Suppress("unused")
|
|
83
|
+
private constructor(hybridData: HybridData) {
|
|
84
|
+
mHybridData = hybridData
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private external fun initHybrid(buffer: ByteBuffer): HybridData
|
|
88
|
+
private external fun getByteBuffer(copyIfNeeded: Boolean): ByteBuffer
|
|
89
|
+
@CriticalNative
|
|
90
|
+
private external fun getIsOwner(): Boolean
|
|
91
|
+
@CriticalNative
|
|
92
|
+
private external fun getIsByteBuffer(): Boolean
|
|
93
|
+
@CriticalNative
|
|
94
|
+
private external fun getBufferSize(): Int
|
|
95
|
+
|
|
96
|
+
companion object {
|
|
97
|
+
/**
|
|
98
|
+
* Copy the given `ArrayBuffer` into a new **owning** `ArrayBuffer`.
|
|
99
|
+
*/
|
|
100
|
+
fun copyOf(other: ArrayBuffer): ArrayBuffer {
|
|
101
|
+
// 1. Create a new buffer with the same size as the other
|
|
102
|
+
val newBuffer = ByteBuffer.allocateDirect(other.size)
|
|
103
|
+
// 2. Prepare the source buffer
|
|
104
|
+
val originalBuffer = other.getBuffer(false)
|
|
105
|
+
originalBuffer.rewind()
|
|
106
|
+
// 3. Copy over the source buffer into the new buffer
|
|
107
|
+
newBuffer.put(originalBuffer)
|
|
108
|
+
// 4. Rewind both buffers again to index 0
|
|
109
|
+
newBuffer.rewind()
|
|
110
|
+
originalBuffer.rewind()
|
|
111
|
+
// 5. Create a new `ArrayBuffer`
|
|
112
|
+
return ArrayBuffer(newBuffer)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package com.margelo.nitro
|
|
1
|
+
package com.margelo.nitro.core
|
|
2
2
|
|
|
3
3
|
import androidx.annotation.Keep
|
|
4
4
|
import com.facebook.jni.HybridData
|
|
@@ -9,7 +9,6 @@ import com.facebook.proguard.annotations.DoNotStrip
|
|
|
9
9
|
*/
|
|
10
10
|
@Keep
|
|
11
11
|
@DoNotStrip
|
|
12
|
-
@Suppress("KotlinJniMissingFunction")
|
|
13
12
|
abstract class HybridObject {
|
|
14
13
|
/**
|
|
15
14
|
* Get the memory size of the Kotlin instance (plus any external heap allocations),
|
|
@@ -29,22 +28,20 @@ abstract class HybridObject {
|
|
|
29
28
|
*/
|
|
30
29
|
@get:DoNotStrip
|
|
31
30
|
@get:Keep
|
|
32
|
-
abstract val memorySize:
|
|
31
|
+
abstract val memorySize: Long
|
|
33
32
|
|
|
34
33
|
/**
|
|
35
|
-
*
|
|
34
|
+
* Holds the native C++ instance.
|
|
35
|
+
* In `HybridObject`, the C++ instance is a sub-class of `JHybridObject`, such as one of it's specs.
|
|
36
|
+
* This is `null`, until `updateNative(..)` is called.
|
|
36
37
|
*/
|
|
37
|
-
|
|
38
|
-
@Keep
|
|
39
|
-
val mHybridData: HybridData
|
|
38
|
+
private var mHybridData: HybridData? = null
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
companion object {
|
|
48
|
-
private const val TAG = "HybridObject"
|
|
40
|
+
/**
|
|
41
|
+
* Must be called in the constructor of a subclass of `HybridObject`, to initialize the C++
|
|
42
|
+
* `JHybridObject` with a subclass of it.
|
|
43
|
+
*/
|
|
44
|
+
protected fun updateNative(hybridData: HybridData) {
|
|
45
|
+
mHybridData = hybridData
|
|
49
46
|
}
|
|
50
47
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
package com.margelo.nitro;
|
|
1
|
+
package com.margelo.nitro.core;
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import com.margelo.nitro.JNIOnLoad;
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A registry that holds initializers for HybridObjects.
|
|
@@ -8,12 +8,14 @@ import java.util.function.Supplier;
|
|
|
8
8
|
* @noinspection JavaJniMissingFunction
|
|
9
9
|
*/
|
|
10
10
|
public class HybridObjectRegistry {
|
|
11
|
+
static {
|
|
12
|
+
JNIOnLoad.initializeNativeNitro();
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* Registers the given HybridObject in the `HybridObjectRegistry`.
|
|
13
17
|
* It will be uniquely identified via it's `hybridObjectName`, and can be initialized from
|
|
14
18
|
* JS using `NitroModules.createHybridObject<T>(name)` - which will call the `constructorFn` here.
|
|
15
19
|
*/
|
|
16
20
|
public static native void registerHybridObjectConstructor(String hybridObjectName, HybridObjectInitializer initializer);
|
|
17
|
-
|
|
18
|
-
private static final String TAG = "HybridObjectRegistry";
|
|
19
21
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
package com.margelo.nitro.core
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.Keep
|
|
4
|
+
import com.facebook.jni.HybridData
|
|
5
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
6
|
+
import kotlinx.coroutines.CoroutineScope
|
|
7
|
+
import kotlinx.coroutines.Dispatchers
|
|
8
|
+
import kotlinx.coroutines.async
|
|
9
|
+
import kotlinx.coroutines.launch
|
|
10
|
+
import kotlin.concurrent.thread
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Represents a Promise that can be passed to JS.
|
|
14
|
+
*
|
|
15
|
+
* Create a new Promise with the following APIs:
|
|
16
|
+
* - [Promise.async]` { ... }` - Creates a new Promise that runs the given suspending function in a Kotlin coroutine scope.
|
|
17
|
+
* - [Promise.parallel]` { ... }` - Creates a new Promise that runs the given code in a parallel Thread.
|
|
18
|
+
* - [Promise.resolved]`(..)` - Creates a new already resolved Promise.
|
|
19
|
+
* - [Promise.rejected]`(..)` - Creates a new already rejected Promise.
|
|
20
|
+
* - [Promise]`()` - Creates a new Promise with fully manual control over the `resolve(..)`/`reject(..)` functions.
|
|
21
|
+
*/
|
|
22
|
+
@Suppress("KotlinJniMissingFunction")
|
|
23
|
+
@Keep
|
|
24
|
+
@DoNotStrip
|
|
25
|
+
class Promise<T> {
|
|
26
|
+
private val mHybridData: HybridData
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new Promise with fully manual control over the `resolve(..)`/`reject(..)` functions
|
|
30
|
+
*/
|
|
31
|
+
init {
|
|
32
|
+
mHybridData = initHybrid()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolves the Promise with the given result.
|
|
37
|
+
* Any `onResolved` listeners will be invoked.
|
|
38
|
+
*/
|
|
39
|
+
fun resolve(result: T) {
|
|
40
|
+
nativeResolve(result as Any)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Rejects the Promise with the given error.
|
|
45
|
+
* Any `onRejected` listeners will be invoked.
|
|
46
|
+
*/
|
|
47
|
+
fun reject(error: Error) {
|
|
48
|
+
nativeReject(error.toString())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// C++ functions
|
|
52
|
+
private external fun nativeResolve(result: Any)
|
|
53
|
+
private external fun nativeReject(error: String)
|
|
54
|
+
private external fun initHybrid(): HybridData
|
|
55
|
+
|
|
56
|
+
companion object {
|
|
57
|
+
private val defaultScope = CoroutineScope(Dispatchers.Default)
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates a new Promise that asynchronously runs the given suspending function [run]
|
|
61
|
+
* on the given coroutine scope [scope].
|
|
62
|
+
*
|
|
63
|
+
* If [scope] is omitted, the [Dispatchers.Default] scope will be used.
|
|
64
|
+
*
|
|
65
|
+
* When the suspending function returns, the Promise gets resolved. If the suspending
|
|
66
|
+
* function throws, the Promise gets rejected.
|
|
67
|
+
*/
|
|
68
|
+
fun <T> async(scope: CoroutineScope = defaultScope, run: suspend () -> T): Promise<T> {
|
|
69
|
+
val promise = Promise<T>()
|
|
70
|
+
scope.launch {
|
|
71
|
+
try {
|
|
72
|
+
val result = run()
|
|
73
|
+
promise.resolve(result)
|
|
74
|
+
} catch (e: Error) {
|
|
75
|
+
promise.reject(e)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return promise
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates a new Promise that runs the given function [run] on a separate
|
|
83
|
+
* Thread.
|
|
84
|
+
*
|
|
85
|
+
* When the function returns, the Promise gets resolved. If the
|
|
86
|
+
* function throws, the Promise gets rejected.
|
|
87
|
+
*/
|
|
88
|
+
fun <T> parallel(run: () -> T): Promise<T> {
|
|
89
|
+
val promise = Promise<T>()
|
|
90
|
+
thread {
|
|
91
|
+
try {
|
|
92
|
+
val result = run()
|
|
93
|
+
promise.resolve(result)
|
|
94
|
+
} catch (e: Error) {
|
|
95
|
+
promise.reject(e)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return promise
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Creates a new Promise that is already resolved with the given result.
|
|
103
|
+
*/
|
|
104
|
+
fun <T> resolved(result: T): Promise<T> {
|
|
105
|
+
return Promise<T>().apply { resolve(result) }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Creates a new Promise that is already rejected with the given error.
|
|
110
|
+
*/
|
|
111
|
+
fun <T> rejected(error: Error): Promise<T> {
|
|
112
|
+
return Promise<T>().apply { reject(error) }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
package/cpp/core/AnyMap.hpp
CHANGED
|
@@ -49,13 +49,13 @@ public:
|
|
|
49
49
|
|
|
50
50
|
public:
|
|
51
51
|
/**
|
|
52
|
-
* Create a new shared_ptr instance of AnyMap.
|
|
52
|
+
* Create a new `shared_ptr` instance of AnyMap.
|
|
53
53
|
*/
|
|
54
54
|
static std::shared_ptr<AnyMap> make() {
|
|
55
55
|
return std::make_shared<AnyMap>();
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Create a new shared_ptr instance of AnyMap with the given amount of spaces pre-allocated.
|
|
58
|
+
* Create a new `shared_ptr` instance of AnyMap with the given amount of spaces pre-allocated.
|
|
59
59
|
*/
|
|
60
60
|
static std::shared_ptr<AnyMap> make(size_t size) {
|
|
61
61
|
return std::make_shared<AnyMap>(size);
|
package/cpp/core/ArrayBuffer.cpp
CHANGED
|
@@ -17,18 +17,18 @@ using namespace facebook;
|
|
|
17
17
|
|
|
18
18
|
// 1. ArrayBuffer
|
|
19
19
|
|
|
20
|
-
std::shared_ptr<ArrayBuffer> ArrayBuffer::makeBuffer(uint8_t* data, size_t size, DeleteFn deleteFunc
|
|
21
|
-
return std::make_shared<NativeArrayBuffer>(data, size, deleteFunc
|
|
20
|
+
std::shared_ptr<ArrayBuffer> ArrayBuffer::makeBuffer(uint8_t* data, size_t size, DeleteFn&& deleteFunc) {
|
|
21
|
+
return std::make_shared<NativeArrayBuffer>(data, size, std::move(deleteFunc));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// 2. NativeArrayBuffer
|
|
25
25
|
|
|
26
|
-
NativeArrayBuffer::NativeArrayBuffer(uint8_t* data, size_t size, DeleteFn deleteFunc
|
|
27
|
-
: ArrayBuffer(), _data(data), _size(size), _deleteFunc(deleteFunc)
|
|
26
|
+
NativeArrayBuffer::NativeArrayBuffer(uint8_t* data, size_t size, DeleteFn&& deleteFunc)
|
|
27
|
+
: ArrayBuffer(), _data(data), _size(size), _deleteFunc(std::move(deleteFunc)) {}
|
|
28
28
|
|
|
29
29
|
NativeArrayBuffer::~NativeArrayBuffer() {
|
|
30
30
|
if (_deleteFunc != nullptr) {
|
|
31
|
-
_deleteFunc(
|
|
31
|
+
_deleteFunc();
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
package/cpp/core/ArrayBuffer.hpp
CHANGED
|
@@ -15,7 +15,7 @@ namespace margelo::nitro {
|
|
|
15
15
|
|
|
16
16
|
using namespace facebook;
|
|
17
17
|
|
|
18
|
-
using DeleteFn = void
|
|
18
|
+
using DeleteFn = std::function<void()>;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Represents a raw byte buffer that can be read from-, and
|
|
@@ -38,7 +38,7 @@ public:
|
|
|
38
38
|
ArrayBuffer(const ArrayBuffer&) = delete;
|
|
39
39
|
ArrayBuffer(ArrayBuffer&&) = delete;
|
|
40
40
|
virtual ~ArrayBuffer() = default;
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
public:
|
|
43
43
|
/**
|
|
44
44
|
* Returns whether this `ArrayBuffer` is actually owning the data,
|
|
@@ -46,13 +46,13 @@ public:
|
|
|
46
46
|
* memory that we didn't allocate, or from JS - which can be deleted at any point).
|
|
47
47
|
*/
|
|
48
48
|
virtual bool isOwner() const noexcept = 0;
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
public:
|
|
51
51
|
/**
|
|
52
52
|
* Create a new `NativeArrayBuffer` that wraps the given data (without copy) of the given size,
|
|
53
|
-
* and calls `deleteFunc`
|
|
53
|
+
* and calls `deleteFunc` in which `data` should be deleted.
|
|
54
54
|
*/
|
|
55
|
-
static std::shared_ptr<ArrayBuffer> makeBuffer(uint8_t* data, size_t size, DeleteFn deleteFunc
|
|
55
|
+
static std::shared_ptr<ArrayBuffer> makeBuffer(uint8_t* data, size_t size, DeleteFn&& deleteFunc);
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -74,9 +74,9 @@ public:
|
|
|
74
74
|
* and will only delete it once this `ArrayBuffer` gets deleted.
|
|
75
75
|
*
|
|
76
76
|
* Once this `ArrayBuffer` goes out of scope, `deleteFunc` will be called.
|
|
77
|
-
* The caller is responsible for deleting the memory (`data`
|
|
77
|
+
* The caller is responsible for deleting the memory (`data`) here.
|
|
78
78
|
*/
|
|
79
|
-
NativeArrayBuffer(uint8_t* data, size_t size, DeleteFn deleteFunc
|
|
79
|
+
NativeArrayBuffer(uint8_t* data, size_t size, DeleteFn&& deleteFunc);
|
|
80
80
|
~NativeArrayBuffer();
|
|
81
81
|
|
|
82
82
|
public:
|
|
@@ -84,8 +84,6 @@ public:
|
|
|
84
84
|
size_t size() const override;
|
|
85
85
|
bool isOwner() const noexcept override;
|
|
86
86
|
|
|
87
|
-
double something();
|
|
88
|
-
|
|
89
87
|
private:
|
|
90
88
|
uint8_t* _data;
|
|
91
89
|
size_t _size;
|
|
@@ -26,19 +26,19 @@ namespace margelo::nitro {
|
|
|
26
26
|
using namespace facebook;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Represents the
|
|
29
|
+
* Represents the kind of a function - it can be either a normal function ("METHOD"),
|
|
30
30
|
* or a property ("GETTER" + "SETTER")
|
|
31
31
|
*/
|
|
32
|
-
enum class
|
|
32
|
+
enum class FunctionKind { METHOD, GETTER, SETTER };
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Represents a Hybrid Function.
|
|
36
36
|
*/
|
|
37
37
|
class HybridFunction final {
|
|
38
38
|
private:
|
|
39
|
-
std::string _name;
|
|
40
|
-
size_t _paramCount;
|
|
41
39
|
jsi::HostFunctionType _function;
|
|
40
|
+
size_t _paramCount;
|
|
41
|
+
std::string _name;
|
|
42
42
|
|
|
43
43
|
public:
|
|
44
44
|
// getters
|
|
@@ -69,30 +69,14 @@ public:
|
|
|
69
69
|
* and assumes that the object this is called on has a proper `this` configured.
|
|
70
70
|
* The object's `this` needs to be a `NativeState`.
|
|
71
71
|
*/
|
|
72
|
-
template <typename
|
|
73
|
-
static inline HybridFunction createHybridFunction(const std::string& name, ReturnType (
|
|
74
|
-
jsi::HostFunctionType hostFunction = [name, method,
|
|
72
|
+
template <typename THybrid, typename ReturnType, typename... Args>
|
|
73
|
+
static inline HybridFunction createHybridFunction(const std::string& name, ReturnType (THybrid::*method)(Args...), FunctionKind kind) {
|
|
74
|
+
jsi::HostFunctionType hostFunction = [name, method, kind](/* JS Runtime */ jsi::Runtime& runtime,
|
|
75
75
|
/* HybridObject */ const jsi::Value& thisValue,
|
|
76
76
|
/* JS arguments */ const jsi::Value* args,
|
|
77
77
|
/* argument size */ size_t count) -> jsi::Value {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!thisValue.isObject()) [[unlikely]] {
|
|
81
|
-
throw jsi::JSError(runtime, "Cannot call hybrid function " + name + "(...) - `this` is not bound!");
|
|
82
|
-
}
|
|
83
|
-
#endif
|
|
84
|
-
jsi::Object thisObject = thisValue.getObject(runtime);
|
|
85
|
-
|
|
86
|
-
#if DEBUG
|
|
87
|
-
if (!thisObject.hasNativeState<Derived>(runtime)) [[unlikely]] {
|
|
88
|
-
if (thisObject.hasNativeState(runtime)) {
|
|
89
|
-
throw jsi::JSError(runtime, "Cannot call hybrid function " + name + "(...) - `this` has a NativeState, but it's the wrong type!");
|
|
90
|
-
} else {
|
|
91
|
-
throw jsi::JSError(runtime, "Cannot call hybrid function " + name + "(...) - `this` does not have a NativeState!");
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
#endif
|
|
95
|
-
std::shared_ptr<Derived> hybridInstance = thisObject.getNativeState<Derived>(runtime);
|
|
78
|
+
// 1. Get actual `HybridObject` instance from `thisValue` (it's stored as `NativeState`)
|
|
79
|
+
std::shared_ptr<THybrid> hybridInstance = getHybridObjectNativeState<THybrid>(runtime, thisValue, kind, name);
|
|
96
80
|
|
|
97
81
|
// 2. Make sure the given arguments match, either with a static size, or with potentially optional arguments size.
|
|
98
82
|
constexpr size_t optionalArgsCount = trailing_optionals_count_v<Args...>;
|
|
@@ -101,47 +85,62 @@ public:
|
|
|
101
85
|
bool isWithinArgsRange = (count >= minArgsCount && count <= maxArgsCount);
|
|
102
86
|
if (!isWithinArgsRange) [[unlikely]] {
|
|
103
87
|
// invalid amount of arguments passed!
|
|
104
|
-
std::string
|
|
88
|
+
std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
|
|
105
89
|
if constexpr (minArgsCount == maxArgsCount) {
|
|
106
90
|
// min and max args length is the same, so we don't have any optional parameters. fixed count
|
|
107
|
-
throw jsi::JSError(runtime,
|
|
108
|
-
|
|
91
|
+
throw jsi::JSError(runtime, "`" + funcName + "` expected " + std::to_string(maxArgsCount) + " arguments, but received " +
|
|
92
|
+
std::to_string(count) + "!");
|
|
109
93
|
} else {
|
|
110
94
|
// min and max args length are different, so we have optional parameters - variable length arguments.
|
|
111
|
-
throw jsi::JSError(runtime,
|
|
95
|
+
throw jsi::JSError(runtime, "`" + funcName + "` expected between " + std::to_string(minArgsCount) + " and " +
|
|
112
96
|
std::to_string(maxArgsCount) + " arguments, but received " + std::to_string(count) + "!");
|
|
113
97
|
}
|
|
114
98
|
}
|
|
115
99
|
|
|
116
|
-
// 3. Actually call method - either raw JSI method, or by going through `JSIConverter<T>` first.
|
|
117
100
|
try {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return (hybridInstance->*method)(runtime, thisValue, args, count);
|
|
122
|
-
} else {
|
|
123
|
-
// Call the actual method with JSI values as arguments and return a JSI value again.
|
|
124
|
-
// Internally, this method converts the JSI values to C++ values.
|
|
125
|
-
return callMethod(hybridInstance.get(), method, runtime, args, count, std::index_sequence_for<Args...>{});
|
|
126
|
-
}
|
|
101
|
+
// 3. Actually call the method with JSI values as arguments and return a JSI value again.
|
|
102
|
+
// Internally, this method converts the JSI values to C++ values using `JSIConverter<T>`.
|
|
103
|
+
return callMethod(hybridInstance.get(), method, runtime, args, count, std::index_sequence_for<Args...>{});
|
|
127
104
|
} catch (const std::exception& exception) {
|
|
128
105
|
// Some exception was thrown - add method name information and re-throw as `JSError`.
|
|
129
|
-
std::string
|
|
106
|
+
std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
|
|
130
107
|
std::string message = exception.what();
|
|
131
|
-
|
|
132
|
-
throw jsi::JSError(runtime, hybridObjectName + "." + name + suffix + ": " + message);
|
|
108
|
+
throw jsi::JSError(runtime, funcName + ": " + message);
|
|
133
109
|
} catch (...) {
|
|
134
110
|
// Some unknown exception was thrown - add method name information and re-throw as `JSError`.
|
|
135
|
-
std::string
|
|
111
|
+
std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
|
|
136
112
|
std::string errorName = TypeInfo::getCurrentExceptionName();
|
|
137
|
-
|
|
138
|
-
throw jsi::JSError(runtime, hybridObjectName + "." + name + suffix + " threw an unknown " + errorName + " error.");
|
|
113
|
+
throw jsi::JSError(runtime, "`" + funcName + "` threw an unknown " + errorName + " error.");
|
|
139
114
|
}
|
|
140
115
|
};
|
|
141
116
|
|
|
142
117
|
return HybridFunction(std::move(hostFunction), sizeof...(Args), name);
|
|
143
118
|
}
|
|
144
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Create a new `HybridFunction` that can be called from JS.
|
|
122
|
+
* Unlike `createHybridFunction(...)`, this method does **not** perform any argument parsing or size checking.
|
|
123
|
+
* It is a raw-, untyped JSI method, and the user is expected to manually handle arguments and return values.
|
|
124
|
+
*/
|
|
125
|
+
template <typename Derived>
|
|
126
|
+
static inline HybridFunction createRawHybridFunction(const std::string& name, size_t expectedArgumentsCount,
|
|
127
|
+
jsi::Value (Derived::*method)(jsi::Runtime& runtime, const jsi::Value& thisArg,
|
|
128
|
+
const jsi::Value* args, size_t count)) {
|
|
129
|
+
jsi::HostFunctionType hostFunction = [name, method](/* JS Runtime */ jsi::Runtime& runtime,
|
|
130
|
+
/* HybridObject */ const jsi::Value& thisValue,
|
|
131
|
+
/* JS arguments */ const jsi::Value* args,
|
|
132
|
+
/* argument size */ size_t count) -> jsi::Value {
|
|
133
|
+
// 1. Get actual `HybridObject` instance from `thisValue` (it's stored as `NativeState`)
|
|
134
|
+
std::shared_ptr<Derived> hybridInstance = getHybridObjectNativeState<Derived>(runtime, thisValue, FunctionKind::METHOD, name);
|
|
135
|
+
|
|
136
|
+
// 2. Call the raw JSI method using raw JSI Values. Exceptions are also expected to be handled by the user.
|
|
137
|
+
Derived* pointer = hybridInstance.get();
|
|
138
|
+
return (pointer->*method)(runtime, thisValue, args, count);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return HybridFunction(std::move(hostFunction), expectedArgumentsCount, name);
|
|
142
|
+
}
|
|
143
|
+
|
|
145
144
|
private:
|
|
146
145
|
/**
|
|
147
146
|
* Calls the given method on the given instance with the given `jsi::Value` arguments by converting them to the desired target types.
|
|
@@ -159,7 +158,82 @@ private:
|
|
|
159
158
|
} else {
|
|
160
159
|
// It's returning some C++ type, we need to convert that to a JSI value now.
|
|
161
160
|
ReturnType result = (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, Is < argsSize ? args[Is] : defaultValue)...);
|
|
162
|
-
return JSIConverter<
|
|
161
|
+
return JSIConverter<ReturnType>::toJSI(runtime, std::forward<ReturnType>(result));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private:
|
|
166
|
+
/**
|
|
167
|
+
* Get the `NativeState` of the given `value`.
|
|
168
|
+
*/
|
|
169
|
+
template <typename THybrid>
|
|
170
|
+
static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value, FunctionKind funcKind,
|
|
171
|
+
const std::string& funcName) {
|
|
172
|
+
// 1. Convert jsi::Value to jsi::Object
|
|
173
|
+
#ifndef NDEBUG
|
|
174
|
+
if (!value.isObject()) [[unlikely]] {
|
|
175
|
+
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) + " - `this` is not bound!");
|
|
176
|
+
}
|
|
177
|
+
#endif
|
|
178
|
+
jsi::Object object = value.getObject(runtime);
|
|
179
|
+
|
|
180
|
+
// 2. Check if it even has any kind of `NativeState`
|
|
181
|
+
#ifndef NDEBUG
|
|
182
|
+
if (!object.hasNativeState(runtime)) [[unlikely]] {
|
|
183
|
+
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
|
|
184
|
+
" - `this` does not have a NativeState! Suggestions:\n"
|
|
185
|
+
"- Did you accidentally destructure the `HybridObject` and lose a reference to the original object?\n"
|
|
186
|
+
"- Did you call `dispose()` on the `HybridObject` before?"
|
|
187
|
+
"- Did you accidentally call `" +
|
|
188
|
+
funcName + "` on the prototype directly?\n");
|
|
189
|
+
}
|
|
190
|
+
#endif
|
|
191
|
+
|
|
192
|
+
// 3. Get `NativeState` from the jsi::Object and check if it is non-null
|
|
193
|
+
std::shared_ptr<jsi::NativeState> nativeState = object.getNativeState(runtime);
|
|
194
|
+
#ifndef NDEBUG
|
|
195
|
+
if (nativeState == nullptr) [[unlikely]] {
|
|
196
|
+
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
|
|
197
|
+
" - `this`'s `NativeState` is `nullptr`, "
|
|
198
|
+
"did you accidentally call `dispose()` on this object?");
|
|
199
|
+
}
|
|
200
|
+
#endif
|
|
201
|
+
|
|
202
|
+
// 4. Try casting it to our desired target type.
|
|
203
|
+
std::shared_ptr<THybrid> hybridInstance = std::dynamic_pointer_cast<THybrid>(nativeState);
|
|
204
|
+
#ifndef NDEBUG
|
|
205
|
+
if (hybridInstance == nullptr) [[unlikely]] {
|
|
206
|
+
throw jsi::JSError(runtime, "Cannot " + getHybridFuncDebugInfo<THybrid>(funcKind, funcName) +
|
|
207
|
+
" - `this` has a NativeState, but it's the wrong type!");
|
|
208
|
+
}
|
|
209
|
+
#endif
|
|
210
|
+
return hybridInstance;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private:
|
|
214
|
+
template <typename THybrid>
|
|
215
|
+
static inline std::string getHybridFuncFullName(FunctionKind kind, const std::string& registrationName,
|
|
216
|
+
THybrid* hybridInstance = nullptr) {
|
|
217
|
+
std::string typeName = hybridInstance != nullptr ? hybridInstance->getName() : TypeInfo::getFriendlyTypenameNoNamespace<THybrid>();
|
|
218
|
+
switch (kind) {
|
|
219
|
+
case FunctionKind::METHOD:
|
|
220
|
+
return typeName + "." + registrationName + "(...)";
|
|
221
|
+
case FunctionKind::GETTER:
|
|
222
|
+
case FunctionKind::SETTER:
|
|
223
|
+
return typeName + "." + registrationName;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
template <typename THybrid>
|
|
227
|
+
static inline std::string getHybridFuncDebugInfo(FunctionKind kind, const std::string& registrationName,
|
|
228
|
+
THybrid* hybridInstance = nullptr) {
|
|
229
|
+
auto funcName = getHybridFuncFullName<THybrid>(kind, registrationName, hybridInstance);
|
|
230
|
+
switch (kind) {
|
|
231
|
+
case FunctionKind::METHOD:
|
|
232
|
+
return "call hybrid function `" + funcName + "`";
|
|
233
|
+
case FunctionKind::GETTER:
|
|
234
|
+
return "get hybrid property `" + funcName + "`";
|
|
235
|
+
case FunctionKind::SETTER:
|
|
236
|
+
return "set hybrid property `" + funcName + "`";
|
|
163
237
|
}
|
|
164
238
|
}
|
|
165
239
|
};
|