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.
Files changed (98) hide show
  1. package/NitroModules.podspec +3 -1
  2. package/README.md +33 -14
  3. package/android/gradle.properties +5 -5
  4. package/android/src/main/cpp/JNIOnLoad.cpp +8 -0
  5. package/android/src/main/cpp/core/ByteBufferArrayBuffer.hpp +47 -0
  6. package/android/src/main/cpp/core/JAnyMap.hpp +193 -0
  7. package/android/src/main/cpp/core/JAnyValue.hpp +191 -0
  8. package/android/src/main/cpp/core/JArrayBuffer.hpp +124 -0
  9. package/android/src/main/cpp/core/JHybridObject.hpp +11 -4
  10. package/android/src/main/cpp/core/JPromise.hpp +88 -0
  11. package/android/src/main/cpp/platform/NitroLogger.cpp +36 -0
  12. package/android/src/main/cpp/registry/JHybridObjectInitializer.hpp +1 -1
  13. package/android/src/main/cpp/registry/JHybridObjectRegistry.cpp +1 -1
  14. package/android/src/main/cpp/registry/JHybridObjectRegistry.hpp +1 -1
  15. package/android/src/main/cpp/utils/{JNISharedPtr.h → JNISharedPtr.hpp} +13 -1
  16. package/android/src/main/java/com/margelo/nitro/JNIOnLoad.java +27 -0
  17. package/android/src/main/java/com/margelo/nitro/NitroModulesPackage.java +1 -9
  18. package/android/src/main/java/com/margelo/nitro/core/AnyMap.kt +80 -0
  19. package/android/src/main/java/com/margelo/nitro/core/AnyValue.kt +164 -0
  20. package/android/src/main/java/com/margelo/nitro/core/ArrayBuffer.kt +115 -0
  21. package/android/src/main/java/com/margelo/nitro/{HybridObject.kt → core/HybridObject.kt} +12 -15
  22. package/android/src/main/java/com/margelo/nitro/{HybridObjectInitializer.java → core/HybridObjectInitializer.java} +1 -1
  23. package/android/src/main/java/com/margelo/nitro/{HybridObjectRegistry.java → core/HybridObjectRegistry.java} +6 -4
  24. package/android/src/main/java/com/margelo/nitro/core/Promise.kt +115 -0
  25. package/cpp/core/AnyMap.hpp +2 -2
  26. package/cpp/core/ArrayBuffer.cpp +5 -5
  27. package/cpp/core/ArrayBuffer.hpp +7 -9
  28. package/cpp/core/HybridFunction.hpp +120 -46
  29. package/cpp/core/HybridObject.cpp +17 -6
  30. package/cpp/core/HybridObject.hpp +32 -8
  31. package/cpp/jsi/JSICache.cpp +5 -5
  32. package/cpp/jsi/JSICache.hpp +1 -3
  33. package/cpp/jsi/JSIConverter+AnyMap.hpp +2 -2
  34. package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +2 -2
  35. package/cpp/jsi/JSIConverter+Function.hpp +7 -6
  36. package/cpp/jsi/JSIConverter+HybridObject.hpp +6 -5
  37. package/cpp/jsi/JSIConverter+Optional.hpp +1 -1
  38. package/cpp/jsi/JSIConverter+Promise.hpp +5 -4
  39. package/cpp/jsi/JSIConverter+Tuple.hpp +1 -1
  40. package/cpp/jsi/JSIConverter+UnorderedMap.hpp +1 -1
  41. package/cpp/jsi/JSIConverter+Variant.hpp +1 -3
  42. package/cpp/jsi/JSIConverter+Vector.hpp +1 -1
  43. package/cpp/jsi/JSIConverter.hpp +11 -11
  44. package/cpp/jsi/JSPromise.cpp +2 -2
  45. package/cpp/platform/NitroLogger.hpp +67 -0
  46. package/cpp/prototype/HybridObjectPrototype.cpp +2 -2
  47. package/cpp/prototype/Prototype.hpp +43 -22
  48. package/cpp/registry/HybridObjectRegistry.cpp +4 -4
  49. package/cpp/threading/Dispatcher.cpp +4 -3
  50. package/cpp/threading/ThreadPool.cpp +2 -2
  51. package/cpp/turbomodule/NativeNitroModules.cpp +26 -8
  52. package/cpp/turbomodule/NativeNitroModules.h +1 -0
  53. package/cpp/turbomodule/NativeNitroModules.hpp +2 -0
  54. package/cpp/utils/TypeInfo.hpp +14 -0
  55. package/ios/core/AnyMapHolder.hpp +91 -0
  56. package/ios/core/AnyMapHolder.swift +316 -0
  57. package/ios/core/ArrayBufferHolder.hpp +7 -5
  58. package/ios/core/ArrayBufferHolder.swift +22 -7
  59. package/ios/core/HybridContext.hpp +3 -3
  60. package/ios/core/Promise.swift +20 -0
  61. package/ios/core/PromiseHolder.hpp +15 -11
  62. package/ios/platform/NitroLogger.mm +36 -0
  63. package/ios/platform/ThreadUtils.cpp +1 -1
  64. package/ios/turbomodule/NitroModuleOnLoad.mm +2 -1
  65. package/ios/utils/SwiftClosure.hpp +63 -0
  66. package/ios/utils/SwiftClosure.swift +58 -0
  67. package/lib/AnyMap.d.ts +3 -0
  68. package/lib/HybridObject.d.ts +15 -0
  69. package/lib/NativeNitroModules.d.ts +2 -0
  70. package/lib/NitroModules.d.ts +18 -0
  71. package/lib/NitroModules.js +24 -0
  72. package/lib/commonjs/NativeNitroModules.js.map +1 -1
  73. package/lib/commonjs/NitroModules.js +24 -0
  74. package/lib/commonjs/NitroModules.js.map +1 -1
  75. package/lib/commonjs/package.json +1 -0
  76. package/lib/module/AnyMap.js +1 -1
  77. package/lib/module/HybridObject.js +1 -1
  78. package/lib/module/ModuleNotFoundError.js +2 -0
  79. package/lib/module/ModuleNotFoundError.js.map +1 -1
  80. package/lib/module/NativeNitroModules.js +2 -0
  81. package/lib/module/NativeNitroModules.js.map +1 -1
  82. package/lib/module/NativeNitroModules.web.js +2 -0
  83. package/lib/module/NativeNitroModules.web.js.map +1 -1
  84. package/lib/module/NitroModules.js +26 -0
  85. package/lib/module/NitroModules.js.map +1 -1
  86. package/lib/module/index.js +2 -0
  87. package/lib/module/index.js.map +1 -1
  88. package/lib/module/package.json +1 -0
  89. package/lib/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +13 -12
  91. package/src/AnyMap.ts +3 -0
  92. package/src/HybridObject.ts +15 -0
  93. package/src/NativeNitroModules.ts +5 -0
  94. package/src/NitroModules.ts +24 -0
  95. package/android/src/main/cpp/core/JHybridObject.cpp +0 -8
  96. package/cpp/templates/IsInPack.hpp +0 -21
  97. package/cpp/utils/NitroLogger.hpp +0 -58
  98. package/ios/utils/ClosureWrapper.swift +0 -45
@@ -49,8 +49,8 @@ HybridObject::HybridObject(const char* name) : HybridObjectPrototype(), _name(na
49
49
  _instanceId = getId(name);
50
50
  uint32_t alive = incrementAliveInstancesAndGet(_name);
51
51
  uint32_t totalObjects = getTotalAliveInstances();
52
- Logger::log(TAG, "(MEMORY) ✅ Creating %s (#%i)... (Total %s(s): %i | Total HybridObjects: %i)", _name, _instanceId, _name, alive,
53
- totalObjects);
52
+ Logger::log(LogLevel::Info, TAG, "(MEMORY) ✅ Creating %s (#%i)... (Total %s(s): %i | Total HybridObjects: %i)", _name, _instanceId,
53
+ _name, alive, totalObjects);
54
54
  #endif
55
55
  }
56
56
 
@@ -58,8 +58,8 @@ HybridObject::~HybridObject() {
58
58
  #if LOG_MEMORY_ALLOCATIONS
59
59
  uint32_t alive = decrementAliveInstancesAndGet(_name);
60
60
  uint32_t totalObjects = getTotalAliveInstances();
61
- Logger::log(TAG, "(MEMORY) ❌ Deleting %s (#%i)... (Total %s(s): %i | Total HybridObjects: %i) ", _name, _instanceId, _name, alive,
62
- totalObjects);
61
+ Logger::log(LogLevel::Info, TAG, "(MEMORY) ❌ Deleting %s (#%i)... (Total %s(s): %i | Total HybridObjects: %i) ", _name, _instanceId,
62
+ _name, alive, totalObjects);
63
63
  #endif
64
64
  }
65
65
 
@@ -75,11 +75,22 @@ bool HybridObject::equals(std::shared_ptr<HybridObject> other) {
75
75
  return this == other.get();
76
76
  }
77
77
 
78
+ jsi::Value HybridObject::disposeRaw(jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) {
79
+ // 1. Dispose any resources - this might be overridden by child classes to perform manual cleanup.
80
+ dispose();
81
+ // 2. Remove the NativeState from `this`
82
+ jsi::Object thisObject = thisArg.asObject(runtime);
83
+ thisObject.setNativeState(runtime, nullptr);
84
+
85
+ return jsi::Value::undefined();
86
+ }
87
+
78
88
  void HybridObject::loadHybridMethods() {
79
89
  registerHybrids(this, [](Prototype& prototype) {
80
90
  prototype.registerHybridGetter("name", &HybridObject::getName);
81
- prototype.registerHybridMethod("toString", &HybridObject::toString);
82
91
  prototype.registerHybridMethod("equals", &HybridObject::equals);
92
+ prototype.registerHybridMethod("toString", &HybridObject::toString);
93
+ prototype.registerRawHybridMethod("dispose", 0, &HybridObject::disposeRaw);
83
94
  });
84
95
  }
85
96
 
@@ -112,7 +123,7 @@ jsi::Value HybridObject::toObject(jsi::Runtime& runtime) {
112
123
  // 6. Set memory size so Hermes GC knows about actual memory
113
124
  object.setExternalMemoryPressure(runtime, getExternalMemorySize());
114
125
 
115
- #if DEBUG
126
+ #ifndef NDEBUG
116
127
  // 7. Assign a private __type property for debugging - this will be used so users know it's not just an empty object.
117
128
  object.setProperty(runtime, "__type", jsi::String::createFromUtf8(runtime, "NativeState<" + std::string(_name) + ">"));
118
129
  #endif
@@ -23,7 +23,7 @@ using namespace facebook;
23
23
  *
24
24
  * The new class can then be passed to JS using the `JSIConverter<HybridObject>`.
25
25
  */
26
- class HybridObject : public jsi::NativeState, public HybridObjectPrototype, public std::enable_shared_from_this<HybridObject> {
26
+ class HybridObject : public virtual jsi::NativeState, public HybridObjectPrototype, public std::enable_shared_from_this<HybridObject> {
27
27
  public:
28
28
  /**
29
29
  * Create a new instance of a `HybridObject`.
@@ -38,11 +38,17 @@ public:
38
38
  /**
39
39
  * HybridObjects cannot be copied.
40
40
  */
41
- HybridObject(const HybridObject& copy) = delete;
41
+ HybridObject(const HybridObject& copy) = default;
42
42
  /**
43
43
  * HybridObjects cannot be moved.
44
44
  */
45
- HybridObject(HybridObject&& move) = delete;
45
+ HybridObject(HybridObject&& move) = default;
46
+ /**
47
+ * HybridObjects cannot be default-constructed!
48
+ */
49
+ HybridObject() {
50
+ throw std::runtime_error("Cannot default-construct HybridObject!");
51
+ }
46
52
 
47
53
  public:
48
54
  /**
@@ -60,7 +66,7 @@ public:
60
66
  */
61
67
  template <typename Derived>
62
68
  std::shared_ptr<Derived> shared() {
63
- return std::static_pointer_cast<Derived>(shared_from_this());
69
+ return std::dynamic_pointer_cast<Derived>(shared_from_this());
64
70
  }
65
71
 
66
72
  public:
@@ -68,10 +74,6 @@ public:
68
74
  * Get the HybridObject's name
69
75
  */
70
76
  std::string getName();
71
- /**
72
- * Get a string representation of this HostObject, useful for logging or debugging.
73
- */
74
- virtual std::string toString();
75
77
  /**
76
78
  * Compare this HybridObject for reference equality to the other HybridObject.
77
79
  *
@@ -79,6 +81,28 @@ public:
79
81
  * they might still be the same `HybridObject` - in this case `equals(other)` will return true.
80
82
  */
81
83
  bool equals(std::shared_ptr<HybridObject> other);
84
+ /**
85
+ * Get a string representation of this `HybridObject` - useful for logging or debugging.
86
+ */
87
+ virtual std::string toString();
88
+ /**
89
+ * Eagerly- (and manually-) dispose all native resources this `HybridObject` holds.
90
+ * This method can only be manually called from JS using `dispose()`.
91
+ *
92
+ * If this method is never manually called, a `HybridObject` is expected to disposes it's
93
+ * resources as usual via the object's destructor (`~HybridObject()`, `deinit` or `finalize()`).
94
+ *
95
+ * By default, this method does nothing. It can be overridden to perform actual disposing/cleanup
96
+ * if required.
97
+ */
98
+ virtual void dispose() {}
99
+
100
+ private:
101
+ /**
102
+ * The actual `dispose()` function from JS.
103
+ * This needs to be a raw JSI function as we remove the NativeState here.
104
+ */
105
+ jsi::Value disposeRaw(jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count);
82
106
 
83
107
  protected:
84
108
  /**
@@ -24,7 +24,7 @@ inline void destroyReferences(const std::vector<BorrowingReference<T>>& referenc
24
24
  }
25
25
 
26
26
  JSICache::~JSICache() {
27
- Logger::log(TAG, "Destroying JSICache...");
27
+ Logger::log(LogLevel::Info, TAG, "Destroying JSICache...");
28
28
  std::unique_lock lock(_mutex);
29
29
 
30
30
  destroyReferences(_objectCache);
@@ -43,19 +43,19 @@ JSICacheReference JSICache::getOrCreateCache(jsi::Runtime& runtime) {
43
43
  // It's still alive! Return it
44
44
  return JSICacheReference(strong);
45
45
  }
46
- Logger::log(TAG, "JSICache was created, but it is no longer strong!");
46
+ Logger::log(LogLevel::Warning, TAG, "JSICache was created, but it is no longer strong!");
47
47
  }
48
48
 
49
- #if DEBUG
49
+ #ifndef NDEBUG
50
50
  if (runtime.global().hasProperty(runtime, CACHE_PROP_NAME)) [[unlikely]] {
51
51
  throw std::runtime_error("The Runtime \"" + getRuntimeId(runtime) + "\" already has a global cache! (\"" + CACHE_PROP_NAME + "\")");
52
52
  }
53
53
  #endif
54
54
 
55
55
  // Cache doesn't exist yet.
56
- Logger::log(TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime));
56
+ Logger::log(LogLevel::Info, TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime).c_str());
57
57
  // Create new cache
58
- auto nativeState = std::shared_ptr<JSICache>(new JSICache(&runtime));
58
+ auto nativeState = std::shared_ptr<JSICache>(new JSICache());
59
59
  // Wrap it in a jsi::Value using NativeState
60
60
  jsi::Object cache(runtime);
61
61
  cache.setNativeState(runtime, nativeState);
@@ -38,12 +38,11 @@ public:
38
38
  ~JSICache();
39
39
 
40
40
  public:
41
- JSICache() = delete;
42
41
  JSICache(const JSICache&) = delete;
43
42
  JSICache(JSICache&&) = delete;
44
43
 
45
44
  private:
46
- explicit JSICache(jsi::Runtime* runtime) : _runtime(runtime) {}
45
+ JSICache() = default;
47
46
 
48
47
  public:
49
48
  /**
@@ -59,7 +58,6 @@ private:
59
58
  friend class JSICacheReference;
60
59
 
61
60
  private:
62
- jsi::Runtime* _runtime;
63
61
  std::mutex _mutex;
64
62
  std::vector<BorrowingReference<jsi::Object>> _objectCache;
65
63
  std::vector<BorrowingReference<jsi::Function>> _functionCache;
@@ -27,7 +27,7 @@ using namespace facebook;
27
27
 
28
28
  // AnyValue <> Record<K, V>
29
29
  template <>
30
- struct JSIConverter<AnyValue> {
30
+ struct JSIConverter<AnyValue> final {
31
31
  static inline AnyValue fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
32
32
  return JSIConverter<AnyValue::variant>::fromJSI(runtime, arg);
33
33
  }
@@ -41,7 +41,7 @@ struct JSIConverter<AnyValue> {
41
41
 
42
42
  // AnyMap <> Record<K, V>
43
43
  template <>
44
- struct JSIConverter<std::shared_ptr<AnyMap>> {
44
+ struct JSIConverter<std::shared_ptr<AnyMap>> final {
45
45
  static inline std::shared_ptr<AnyMap> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46
46
  jsi::Object object = arg.asObject(runtime);
47
47
  jsi::Array propNames = object.getPropertyNames(runtime);
@@ -28,11 +28,11 @@ using namespace facebook;
28
28
 
29
29
  // MutableBuffer <> ArrayBuffer
30
30
  template <typename T>
31
- struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer>>> {
31
+ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer>>> final {
32
32
  static inline std::shared_ptr<ArrayBuffer> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
33
33
  jsi::Object object = arg.asObject(runtime);
34
34
 
35
- #if DEBUG
35
+ #ifndef NDEBUG
36
36
  if (!object.isArrayBuffer(runtime)) [[unlikely]] {
37
37
  throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) +
38
38
  "\" is not an ArrayBuffer! "
@@ -27,7 +27,7 @@ using namespace facebook;
27
27
 
28
28
  // [](Args...) -> T {} <> (Args...) => T
29
29
  template <typename ReturnType, typename... Args>
30
- struct JSIConverter<std::function<ReturnType(Args...)>> {
30
+ struct JSIConverter<std::function<ReturnType(Args...)>> final {
31
31
  // std::future<T> -> T
32
32
  using ResultingType = future_type_v<ReturnType>;
33
33
 
@@ -47,7 +47,8 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
47
47
  std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
48
48
  if (!dispatcher) {
49
49
  if constexpr (std::is_void_v<ResultingType>) {
50
- Logger::log("JSIConverter", "Tried calling void(..) function, but the JS Dispatcher has already been deleted by JS!");
50
+ Logger::log(LogLevel::Error, "JSIConverter",
51
+ "Tried calling void(..) function, but the JS Dispatcher has already been deleted by JS!");
51
52
  return;
52
53
  } else {
53
54
  throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
@@ -68,8 +69,8 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
68
69
  }
69
70
 
70
71
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::function<ReturnType(Args...)>& function) {
71
- jsi::HostFunctionType jsFunction = [function](jsi::Runtime& runtime, const jsi::Value& thisValue,
72
- const jsi::Value* args, size_t count) -> jsi::Value {
72
+ jsi::HostFunctionType jsFunction = [function](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* args,
73
+ size_t count) -> jsi::Value {
73
74
  if (count != sizeof...(Args)) [[unlikely]] {
74
75
  throw jsi::JSError(runtime, "Function expected " + std::to_string(sizeof...(Args)) + " arguments, but received " +
75
76
  std::to_string(count) + "!");
@@ -95,7 +96,7 @@ private:
95
96
  if (!function) {
96
97
  if constexpr (std::is_void_v<ResultingType>) {
97
98
  // runtime has already been deleted. since this returns void, we can just ignore it being deleted.
98
- Logger::log("JSIConverter", "Tried calling void(..) function, but it has already been deleted by JS!");
99
+ Logger::log(LogLevel::Error, "JSIConverter", "Tried calling void(..) function, but it has already been deleted by JS!");
99
100
  return;
100
101
  } else {
101
102
  // runtime has already been deleted, but we are expecting a return value - throw an error in this case.
@@ -123,7 +124,7 @@ private:
123
124
  } else {
124
125
  // it is a custom type, parse it to a JS value
125
126
  ReturnType result = function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
126
- return JSIConverter<ReturnType>::toJSI(runtime, result);
127
+ return JSIConverter<ReturnType>::toJSI(runtime, std::forward<ReturnType>(result));
127
128
  }
128
129
  }
129
130
  };
@@ -20,11 +20,11 @@ using namespace facebook;
20
20
 
21
21
  // HybridObject(NativeState) <> {}
22
22
  template <typename T>
23
- struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>> {
23
+ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>> final {
24
24
  using TPointee = typename T::element_type;
25
25
 
26
26
  static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
27
- #if DEBUG
27
+ #ifndef NDEBUG
28
28
  if (!arg.isObject()) [[unlikely]] {
29
29
  if (arg.isUndefined()) [[unlikely]] {
30
30
  throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
@@ -36,7 +36,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
36
36
  #endif
37
37
  jsi::Object object = arg.asObject(runtime);
38
38
 
39
- #if DEBUG
39
+ #ifndef NDEBUG
40
40
  if (!object.hasNativeState<TPointee>(runtime)) [[unlikely]] {
41
41
  if (!object.hasNativeState(runtime)) [[unlikely]] {
42
42
  std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
@@ -47,7 +47,8 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
47
47
  }
48
48
  }
49
49
  #endif
50
- return object.getNativeState<TPointee>(runtime);
50
+ std::shared_ptr<jsi::NativeState> nativeState = object.getNativeState(runtime);
51
+ return std::dynamic_pointer_cast<TPointee>(nativeState);
51
52
  }
52
53
 
53
54
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const T& arg) {
@@ -60,7 +61,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
60
61
  // It's a HybridObject - use it's internal constructor which caches jsi::Objects for proper memory management!
61
62
  return arg->toObject(runtime);
62
63
  } else {
63
- // It's any other kind of jsi::HostObject - just create it as normal. This will not have a prototype then!
64
+ // It's any other kind of jsi::NativeState - just create it as normal. This will not have a prototype then!
64
65
  jsi::Object object(runtime);
65
66
  object.setNativeState(runtime, arg);
66
67
  return object;
@@ -21,7 +21,7 @@ using namespace facebook;
21
21
 
22
22
  // std::optional<T> <> T | undefined
23
23
  template <typename TInner>
24
- struct JSIConverter<std::optional<TInner>> {
24
+ struct JSIConverter<std::optional<TInner>> final {
25
25
  static inline std::optional<TInner> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
26
26
  if (arg.isUndefined() || arg.isNull()) {
27
27
  return std::nullopt;
@@ -30,7 +30,7 @@ using namespace facebook;
30
30
 
31
31
  // std::future<T> <> Promise<T>
32
32
  template <typename TResult>
33
- struct JSIConverter<std::future<TResult>> {
33
+ struct JSIConverter<std::future<TResult>> final {
34
34
  static inline std::future<TResult> fromJSI(jsi::Runtime&, const jsi::Value&) {
35
35
  throw std::runtime_error("Promise cannot be converted to a native type - it needs to be awaited first!");
36
36
  }
@@ -49,7 +49,8 @@ struct JSIConverter<std::future<TResult>> {
49
49
  // the async function completed successfully, get a JS Dispatcher so we can resolve on JS Thread
50
50
  std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
51
51
  if (!dispatcher) {
52
- Logger::log("JSIConverter", "Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed.");
52
+ Logger::log(LogLevel::Error, "JSIConverter",
53
+ "Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed!");
53
54
  return;
54
55
  }
55
56
 
@@ -62,7 +63,7 @@ struct JSIConverter<std::future<TResult>> {
62
63
  } else {
63
64
  // it's returning a custom type, convert it to a jsi::Value
64
65
  TResult result = sharedFuture->get();
65
- jsi::Value jsResult = JSIConverter<TResult>::toJSI(runtime, result);
66
+ jsi::Value jsResult = JSIConverter<TResult>::toJSI(runtime, std::forward<TResult>(result));
66
67
  promise->resolve(runtime, std::move(jsResult));
67
68
  }
68
69
  } catch (const std::exception& exception) {
@@ -84,7 +85,7 @@ struct JSIConverter<std::future<TResult>> {
84
85
  });
85
86
  }
86
87
 
87
- static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
88
+ static inline bool canConvert(jsi::Runtime&, const jsi::Value&) {
88
89
  throw std::runtime_error("jsi::Value of type Promise cannot be converted to std::future yet!");
89
90
  }
90
91
  };
@@ -23,7 +23,7 @@ using namespace facebook;
23
23
 
24
24
  // std::tuple<A, B, C> <> [A, B, C]
25
25
  template <typename... Types>
26
- struct JSIConverter<std::tuple<Types...>> {
26
+ struct JSIConverter<std::tuple<Types...>> final {
27
27
  static inline std::tuple<Types...> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
28
28
  jsi::Object object = value.asObject(runtime);
29
29
  jsi::Array array = object.asArray(runtime);
@@ -23,7 +23,7 @@ using namespace facebook;
23
23
 
24
24
  // std::unordered_map<std::string, T> <> Record<string, T>
25
25
  template <typename ValueType>
26
- struct JSIConverter<std::unordered_map<std::string, ValueType>> {
26
+ struct JSIConverter<std::unordered_map<std::string, ValueType>> final {
27
27
  static inline std::unordered_map<std::string, ValueType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
28
28
  jsi::Object object = arg.asObject(runtime);
29
29
  jsi::Array propertyNames = object.getPropertyNames(runtime);
@@ -18,7 +18,6 @@ struct JSIConverter;
18
18
  #include "JSIConverter.hpp"
19
19
 
20
20
  #include "AnyMap.hpp"
21
- #include "IsInPack.hpp"
22
21
  #include "TypeInfo.hpp"
23
22
  #include <jsi/jsi.h>
24
23
  #include <memory>
@@ -30,8 +29,7 @@ using namespace facebook;
30
29
 
31
30
  // std::variant<A, B, C> <> A | B | C
32
31
  template <typename... Types>
33
- struct JSIConverter<std::variant<Types...>> {
34
-
32
+ struct JSIConverter<std::variant<Types...>> final {
35
33
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
36
34
  // Check each type in `Types...` to make sure we can convert `jsi::Value` to one of those.
37
35
  return (JSIConverter<Types>::canConvert(runtime, value) || ...);
@@ -22,7 +22,7 @@ using namespace facebook;
22
22
 
23
23
  // std::vector<T> <> T[]
24
24
  template <typename ElementType>
25
- struct JSIConverter<std::vector<ElementType>> {
25
+ struct JSIConverter<std::vector<ElementType>> final {
26
26
  static inline std::vector<ElementType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
27
27
  jsi::Array array = arg.asObject(runtime).asArray(runtime);
28
28
  size_t length = array.size(runtime);
@@ -22,7 +22,7 @@ using namespace facebook;
22
22
  * The JSIConverter<T> class can convert any type from and to a jsi::Value.
23
23
  * It uses templates to statically create fromJSI/toJSI methods, and will throw compile-time errors
24
24
  * if a given type is not convertable.
25
- * Value types, custom types (HostObjects), and even functions with any number of arguments/types are supported.
25
+ * Value types, custom types (HybridObject), and even functions with any number of arguments/types are supported.
26
26
  * This type can be extended by just creating a new template for JSIConverter in a header.
27
27
  */
28
28
  template <typename T, typename Enable = void>
@@ -61,7 +61,7 @@ private:
61
61
 
62
62
  // int <> number
63
63
  template <>
64
- struct JSIConverter<int> {
64
+ struct JSIConverter<int> final {
65
65
  static inline int fromJSI(jsi::Runtime&, const jsi::Value& arg) {
66
66
  return static_cast<int>(arg.asNumber());
67
67
  }
@@ -75,11 +75,11 @@ struct JSIConverter<int> {
75
75
 
76
76
  // std::monostate <> null
77
77
  template <>
78
- struct JSIConverter<std::monostate> {
79
- static inline std::monostate fromJSI(jsi::Runtime&, const jsi::Value& arg) {
78
+ struct JSIConverter<std::monostate> final {
79
+ static inline std::monostate fromJSI(jsi::Runtime&, const jsi::Value&) {
80
80
  return std::monostate();
81
81
  }
82
- static inline jsi::Value toJSI(jsi::Runtime&, std::monostate arg) {
82
+ static inline jsi::Value toJSI(jsi::Runtime&, std::monostate) {
83
83
  return jsi::Value::null();
84
84
  }
85
85
  static inline bool canConvert(jsi::Runtime&, const jsi::Value& value) {
@@ -89,7 +89,7 @@ struct JSIConverter<std::monostate> {
89
89
 
90
90
  // double <> number
91
91
  template <>
92
- struct JSIConverter<double> {
92
+ struct JSIConverter<double> final {
93
93
  static inline double fromJSI(jsi::Runtime&, const jsi::Value& arg) {
94
94
  return arg.asNumber();
95
95
  }
@@ -103,7 +103,7 @@ struct JSIConverter<double> {
103
103
 
104
104
  // float <> number
105
105
  template <>
106
- struct JSIConverter<float> {
106
+ struct JSIConverter<float> final {
107
107
  static inline float fromJSI(jsi::Runtime&, const jsi::Value& arg) {
108
108
  return static_cast<float>(arg.asNumber());
109
109
  }
@@ -117,7 +117,7 @@ struct JSIConverter<float> {
117
117
 
118
118
  // int64_t <> BigInt
119
119
  template <>
120
- struct JSIConverter<int64_t> {
120
+ struct JSIConverter<int64_t> final {
121
121
  static inline double fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
122
122
  return arg.asBigInt(runtime).asInt64(runtime);
123
123
  }
@@ -135,7 +135,7 @@ struct JSIConverter<int64_t> {
135
135
 
136
136
  // uint64_t <> BigInt
137
137
  template <>
138
- struct JSIConverter<uint64_t> {
138
+ struct JSIConverter<uint64_t> final {
139
139
  static inline double fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
140
140
  return arg.asBigInt(runtime).asUint64(runtime);
141
141
  }
@@ -153,7 +153,7 @@ struct JSIConverter<uint64_t> {
153
153
 
154
154
  // bool <> boolean
155
155
  template <>
156
- struct JSIConverter<bool> {
156
+ struct JSIConverter<bool> final {
157
157
  static inline bool fromJSI(jsi::Runtime&, const jsi::Value& arg) {
158
158
  return arg.asBool();
159
159
  }
@@ -167,7 +167,7 @@ struct JSIConverter<bool> {
167
167
 
168
168
  // std::string <> string
169
169
  template <>
170
- struct JSIConverter<std::string> {
170
+ struct JSIConverter<std::string> final {
171
171
  static inline std::string fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
172
172
  return arg.asString(runtime).utf8(runtime);
173
173
  }
@@ -38,7 +38,7 @@ void JSPromise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
38
38
  OwningLock<jsi::Function> lock = _resolver.lock();
39
39
 
40
40
  if (!_resolver) {
41
- Logger::log(TAG, "Promise resolver function has already been deleted! Ignoring call..");
41
+ Logger::log(LogLevel::Error, TAG, "Promise resolver function has already been deleted! Ignoring call..");
42
42
  return;
43
43
  }
44
44
  _resolver->call(runtime, std::move(result));
@@ -48,7 +48,7 @@ void JSPromise::reject(jsi::Runtime& runtime, std::string message) {
48
48
  OwningLock<jsi::Function> lock = _rejecter.lock();
49
49
 
50
50
  if (!_rejecter) {
51
- Logger::log(TAG, "Promise rejecter function has already been deleted! Ignoring call..");
51
+ Logger::log(LogLevel::Error, TAG, "Promise rejecter function has already been deleted! Ignoring call..");
52
52
  return;
53
53
  }
54
54
  jsi::JSError error(runtime, message);
@@ -0,0 +1,67 @@
1
+ //
2
+ // Created by Marc Rousavy on 05.03.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ #include <cstdarg>
8
+ #include <cstdio>
9
+ #include <iostream>
10
+ #include <sstream>
11
+ #include <string>
12
+ #include <type_traits>
13
+
14
+ namespace margelo::nitro {
15
+
16
+ enum class LogLevel { Debug, Info, Warning, Error };
17
+
18
+ class Logger final {
19
+ private:
20
+ Logger() = delete;
21
+
22
+ public:
23
+ template <typename... Args>
24
+ static void log(LogLevel level, const char* tag, const char* format, Args... args) {
25
+ #ifndef NDEBUG
26
+ // 1. Make sure args can be passed to sprintf(..)
27
+ static_assert(all_are_trivially_copyable<Args...>(), "All arguments passed to Logger::log(..) must be trivially copyable! "
28
+ "Did you try to pass a complex type, like std::string?");
29
+
30
+ // 2. Format all arguments in the message
31
+ std::string message = formatString(format, args...);
32
+
33
+ // 3. Call the platform specific log function
34
+ nativeLog(level, tag, message);
35
+ #endif
36
+ }
37
+
38
+ static void nativeLog(LogLevel level, const char* tag, const std::string& string);
39
+
40
+ private:
41
+ template <typename... Args>
42
+ static std::string formatString(const char* format, Args... args) {
43
+ #pragma clang diagnostic push
44
+ #pragma clang diagnostic ignored "-Wformat-security"
45
+ int size = snprintf(nullptr, 0, format, args...) + 1; // Extra space for '\0'
46
+ if (size <= 0) {
47
+ return "Error during formatting.";
48
+ }
49
+ std::unique_ptr<char[]> buf(new char[size]);
50
+ snprintf(buf.get(), size, format, args...);
51
+ return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
52
+ #pragma clang diagnostic pop
53
+ }
54
+
55
+ // Overloaded functions to convert std::string to C-style string
56
+ template <typename T>
57
+ static constexpr bool is_trivially_copyable() {
58
+ return std::is_trivially_copyable<T>::value;
59
+ }
60
+
61
+ template <typename... Args>
62
+ static constexpr bool all_are_trivially_copyable() {
63
+ return (is_trivially_copyable<Args>() && ...);
64
+ }
65
+ };
66
+
67
+ } // namespace margelo::nitro
@@ -30,7 +30,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
30
30
 
31
31
  // 2. We didn't find the given prototype in cache (either it's a new prototype, or a new runtime),
32
32
  // so we need to create it. First, we need some helper methods from JS
33
- Logger::log(TAG, "Creating new JS prototype for C++ instance type \"%s\"...", prototype->getNativeInstanceId().name());
33
+ Logger::log(LogLevel::Info, TAG, "Creating new JS prototype for C++ instance type \"%s\"...", prototype->getNativeInstanceId().name());
34
34
  jsi::Object objectConstructor = runtime.global().getPropertyAsObject(runtime, "Object");
35
35
  jsi::Function objectCreate = objectConstructor.getPropertyAsFunction(runtime, "create");
36
36
  jsi::Function objectDefineProperty = objectConstructor.getPropertyAsFunction(runtime, "defineProperty");
@@ -69,7 +69,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
69
69
  prototypeCache.emplace(prototype->getNativeInstanceId(), cachedObject);
70
70
 
71
71
  // 7. In DEBUG, add a __type info to the prototype object.
72
- #if DEBUG
72
+ #ifndef NDEBUG
73
73
  auto typeName = "Prototype<" + std::string(prototype->getNativeInstanceId().name()) + ">";
74
74
  cachedObject->setProperty(runtime, "__type", jsi::String::createFromUtf8(runtime, typeName));
75
75
  #endif