expo-modules-core 1.1.0 → 1.2.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 (59) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/android/CMakeLists.txt +9 -25
  3. package/android/ExpoModulesCorePlugin.gradle +0 -1
  4. package/android/build.gradle +29 -23
  5. package/android/legacy/CMakeLists.txt +2 -0
  6. package/android/src/fabric/CMakeLists.txt +12 -9
  7. package/android/src/main/cpp/JavaReferencesCache.cpp +6 -0
  8. package/android/src/main/cpp/JavaReferencesCache.h +20 -6
  9. package/android/src/main/cpp/JavaScriptModuleObject.cpp +9 -9
  10. package/android/src/main/cpp/JavaScriptModuleObject.h +5 -3
  11. package/android/src/main/cpp/JavaScriptRuntime.cpp +4 -0
  12. package/android/src/main/cpp/MethodMetadata.cpp +13 -2
  13. package/android/src/main/cpp/types/CppType.h +12 -11
  14. package/android/src/main/cpp/types/FrontendConverter.cpp +23 -0
  15. package/android/src/main/cpp/types/FrontendConverter.h +15 -0
  16. package/android/src/main/cpp/types/FrontendConverterProvider.cpp +1 -0
  17. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +3 -0
  18. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +8 -2
  19. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +35 -24
  20. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +1 -0
  21. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +4 -0
  22. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +1 -4
  23. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +30 -0
  24. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +5 -1
  25. package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverter.kt +1 -0
  26. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +6 -0
  27. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +2 -0
  28. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +2 -0
  29. package/android/src/main/java/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +56 -6
  30. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +73 -1
  31. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +20 -16
  32. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +2 -2
  33. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
  34. package/build/NativeViewManagerAdapter.native.js +19 -1
  35. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  36. package/build/SyntheticPlatformEmitter.d.ts +1 -1
  37. package/build/SyntheticPlatformEmitter.d.ts.map +1 -1
  38. package/build/SyntheticPlatformEmitter.js +1 -1
  39. package/build/SyntheticPlatformEmitter.js.map +1 -1
  40. package/ios/Fabric/ExpoFabricView.swift +6 -6
  41. package/ios/Fabric/ExpoFabricViewObjC.h +1 -1
  42. package/ios/Fabric/ExpoFabricViewObjC.mm +3 -9
  43. package/ios/Swift/Conversions.swift +5 -2
  44. package/ios/Swift/ExpoBridgeModule.swift +23 -2
  45. package/ios/Swift/Functions/AnyFunction.swift +4 -0
  46. package/ios/Swift/Functions/ConcurrentFunctionDefinition.swift +271 -0
  47. package/ios/Swift/Functions/SyncFunctionComponent.swift +8 -4
  48. package/ios/Swift/Objects/ObjectDefinitionBuilder.swift +25 -0
  49. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +6 -0
  50. package/ios/Swift/Views/ViewModuleWrapper.swift +7 -9
  51. package/ios/Tests/FunctionSpec.swift +26 -2
  52. package/package.json +2 -2
  53. package/src/NativeViewManagerAdapter.native.tsx +23 -2
  54. package/src/SyntheticPlatformEmitter.ts +1 -1
  55. package/build/SyntheticPlatformEmitter.web.d.ts +0 -6
  56. package/build/SyntheticPlatformEmitter.web.d.ts.map +0 -1
  57. package/build/SyntheticPlatformEmitter.web.js +0 -6
  58. package/build/SyntheticPlatformEmitter.web.js.map +0 -1
  59. package/src/SyntheticPlatformEmitter.web.ts +0 -5
package/CHANGELOG.md CHANGED
@@ -10,6 +10,35 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 1.2.0 — 2023-02-03
14
+
15
+ ### 🎉 New features
16
+
17
+ - View-related DSL functions do not require providing the view's type in function parameters on Android. ([#20751](https://github.com/expo/expo/pull/20751) by [@lukmccall](https://github.com/lukmccall))
18
+ - Add support for the `Long` type as function parameters on Android. ([#20787](https://github.com/expo/expo/pull/20787) by [@lukmccall](https://github.com/lukmccall))
19
+ - [Android] Added experimental support for building the function result from the object definition. ([#20864](https://github.com/expo/expo/pull/20864) by [@lukmccall](https://github.com/lukmccall))
20
+ - Removed boost dependency which needs extra downloading on Android. ([#21000](https://github.com/expo/expo/pull/21000) by [@kudo](https://github.com/kudo))
21
+
22
+ ### 🐛 Bug fixes
23
+
24
+ - Fix view prop setter not being called when its new value is `null` or `undefined`. ([#20755](https://github.com/expo/expo/pull/20755) & [#20766](https://github.com/expo/expo/pull/20766) by [@tsapeta](https://github.com/tsapeta) & [@lukmccall](https://github.com/lukmccall))
25
+ - Fixed "Tried to register two views with the same name" error on fast refresh. ([#20788](https://github.com/expo/expo/pull/20788) by [@tsapeta](https://github.com/tsapeta))
26
+
27
+ ### 💡 Others
28
+
29
+ - On Android bump `compileSdkVersion` and `targetSdkVersion` to `33`. ([#20721](https://github.com/expo/expo/pull/20721) by [@lukmccall](https://github.com/lukmccall))
30
+
31
+ ## 1.1.1 — 2023-01-06
32
+
33
+ ### 🎉 New features
34
+
35
+ - Added support for concurrent (async/await) functions in Swift. ([#20645](https://github.com/expo/expo/pull/20645) by [@tsapeta](https://github.com/tsapeta))
36
+ - [iOS] Added experimental support for building the function result from the object definition. ([#20623](https://github.com/expo/expo/pull/20623) by [@tsapeta](https://github.com/tsapeta))
37
+
38
+ ### 🐛 Bug fixes
39
+
40
+ - Fixed boost build error on Android. ([#20719](https://github.com/expo/expo/pull/20719) by [@kudo](https://github.com/kudo))
41
+
13
42
  ## 1.1.0 — 2022-12-30
14
43
 
15
44
  ### 🎉 New features
@@ -6,7 +6,9 @@ set(CMAKE_VERBOSE_MAKEFILE ON)
6
6
  set(CMAKE_CXX_STANDARD 17)
7
7
  set(PACKAGE_NAME "expo-modules-core")
8
8
  set(BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
9
- set(ignoreMe "${PROJECT_BUILD_DIR} ${REACT_ANDROID_BUILD_DIR} ${REACT_NATIVE_TARGET_VERSION} ${REACT_ANDROID_DIR} ${HERMES_HEADER_DIR}")
9
+ set(ignoreMe "${PROJECT_BUILD_DIR} ${REACT_ANDROID_BUILD_DIR} ${REACT_ANDROID_DIR} ${HERMES_HEADER_DIR} ${BOOST_VERSION}")
10
+
11
+ string(APPEND CMAKE_CXX_FLAGS " -DREACT_NATIVE_TARGET_VERSION=${REACT_NATIVE_TARGET_VERSION}")
10
12
 
11
13
  if (${NATIVE_DEBUG})
12
14
  set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
@@ -76,26 +78,12 @@ endif ()
76
78
 
77
79
  if(${UNIT_TEST})
78
80
  if(${USE_HERMES})
79
- file(GLOB HERMES_SO_DIR "${BUILD_DIR}/third-party-ndk/hermes/jni/${ANDROID_ABI}")
80
- find_library(
81
- JSEXECUTOR_LIB
82
- hermes
83
- PATHS ${HERMES_SO_DIR}
84
- NO_CMAKE_FIND_ROOT_PATH
85
- )
86
- set(JSEXECUTOR_INCLUDE ${HERMES_HEADER_DIR} ${HERMES_HEADER_DIR}/API ${HERMES_HEADER_DIR}/public)
81
+ set(JSEXECUTOR_LIB ReactAndroid::hermes_executor)
87
82
  else()
88
- find_library(
89
- JSEXECUTOR_LIB
90
- jscexecutor
91
- PATHS ${LIBRN_DIR}
92
- NO_CMAKE_FIND_ROOT_PATH
93
- )
94
- set(JSEXECUTOR_INCLUDE "")
83
+ set(JSEXECUTOR_LIB ReactAndroid::jscexecutor)
95
84
  endif()
96
85
  else()
97
86
  set(JSEXECUTOR_LIB "")
98
- set(JSEXECUTOR_INCLUDE "")
99
87
  endif()
100
88
 
101
89
  # find libraries
@@ -118,22 +106,18 @@ target_include_directories(
118
106
 
119
107
  # header only imports from turbomodule, e.g. CallInvokerHolder.h
120
108
  "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
121
-
122
- "${BUILD_DIR}/third-party-ndk/boost/boost_${BOOST_VERSION}"
123
109
  "${COMMON_DIR}"
124
- "${JSEXECUTOR_INCLUDE}"
125
110
  "${SRC_DIR}/fabric"
126
111
  )
127
112
 
128
113
  # linking
129
114
 
115
+ include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
116
+
130
117
  target_compile_options(
131
118
  ${PACKAGE_NAME}
132
- PRIVATE -DFOLLY_NO_CONFIG=1
133
- -DFOLLY_HAVE_CLOCK_GETTIME=1
134
- -DFOLLY_HAVE_MEMRCHR=1
135
- -DFOLLY_USE_LIBCPP=1
136
- -DFOLLY_MOBILE=1
119
+ PRIVATE
120
+ ${folly_FLAGS}
137
121
  )
138
122
 
139
123
  target_link_libraries(
@@ -74,7 +74,6 @@ class LegacyReactNativeLibsExtractionPlugin implements Plugin<Project> {
74
74
  def reactProperties = new Properties()
75
75
  new File("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
76
76
  def FOLLY_VERSION = reactProperties.getProperty("FOLLY_VERSION")
77
- def BOOST_VERSION = reactProperties.getProperty("BOOST_VERSION")
78
77
  def DOUBLE_CONVERSION_VERSION = reactProperties.getProperty("DOUBLE_CONVERSION_VERSION")
79
78
  def REACT_NATIVE_VERSION = System.getenv("REACT_NATIVE_OVERRIDE_VERSION") ?: reactProperties.getProperty("VERSION_NAME")
80
79
  def REACT_NATIVE_TARGET_VERSION = REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
@@ -6,7 +6,7 @@ apply plugin: 'maven-publish'
6
6
  apply plugin: "de.undercouch.download"
7
7
 
8
8
  group = 'host.exp.exponent'
9
- version = '1.1.0'
9
+ version = '1.2.0'
10
10
 
11
11
  buildscript {
12
12
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -150,24 +150,13 @@ afterEvaluate {
150
150
  }
151
151
 
152
152
  android {
153
- compileSdkVersion safeExtGet("compileSdkVersion", 31)
153
+ compileSdkVersion safeExtGet("compileSdkVersion", 33)
154
154
 
155
- if (REACT_NATIVE_BUILD_FROM_SOURCE) {
156
- if (rootProject.hasProperty("ndkPath")) {
157
- ndkPath rootProject.ext.ndkPath
158
- }
159
- if (rootProject.hasProperty("ndkVersion")) {
160
- ndkVersion rootProject.ext.ndkVersion
161
- }
162
- } else {
163
- // This ndkVersion should align to prebuilt react-native building ndkVersion.
164
- // Because the unwind library is different before NDK r23 and after,
165
- // if we build this library with newer NDK, C++ exceptions are incompatible between these libraries.
166
- // For example, C++ exceptions throwing from hermes are not catchable from this library.
167
- // Ref: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#unwinding
168
- //
169
- // TODO: Revisit this version when upgrade react-native 0.71 (Expo SDK 48)
170
- ndkVersion = "21.4.7075529"
155
+ if (rootProject.hasProperty("ndkPath")) {
156
+ ndkPath rootProject.ext.ndkPath
157
+ }
158
+ if (rootProject.hasProperty("ndkVersion")) {
159
+ ndkVersion rootProject.ext.ndkVersion
171
160
  }
172
161
 
173
162
  compileOptions {
@@ -181,10 +170,10 @@ android {
181
170
 
182
171
  defaultConfig {
183
172
  minSdkVersion safeExtGet("minSdkVersion", 21)
184
- targetSdkVersion safeExtGet("targetSdkVersion", 31)
173
+ targetSdkVersion safeExtGet("targetSdkVersion", 33)
185
174
  consumerProguardFiles 'proguard-rules.pro'
186
175
  versionCode 1
187
- versionName "1.1.0"
176
+ versionName "1.2.0"
188
177
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
189
178
 
190
179
  testInstrumentationRunner "expo.modules.TestRunner"
@@ -300,7 +289,7 @@ dependencies {
300
289
  //noinspection GradleDynamicVersion
301
290
  implementation 'com.facebook.react:react-native:+'
302
291
 
303
- compileOnly 'com.facebook.fbjni:fbjni:0.2.2'
292
+ compileOnly 'com.facebook.fbjni:fbjni:0.3.0'
304
293
 
305
294
  testImplementation 'androidx.test:core:1.4.0'
306
295
  testImplementation 'junit:junit:4.13.1'
@@ -404,10 +393,27 @@ def prepareBoost = tasks.register('prepareBoost', Copy) {
404
393
  }
405
394
  }
406
395
 
396
+ void nativeBuildDependsOn(project, dependsOnTask, buildTypesIncludes) {
397
+ def buildTasks = project.tasks.findAll { task ->
398
+ def taskName = task.name
399
+ if (taskName.contains("Clean")) { return false }
400
+ if (taskName.contains("externalNative") || taskName.contains("CMake") || taskName.contains("generateJsonModel")) {
401
+ if (buildTypesIncludes == null) { return true }
402
+ for (buildType in buildTypesIncludes) {
403
+ if (taskName.contains(buildType)) { return true }
404
+ }
405
+ }
406
+ return false
407
+ }
408
+ buildTasks.forEach { task -> task.dependsOn(dependsOnTask) }
409
+ }
410
+
407
411
  afterEvaluate {
408
- preBuild.dependsOn(prepareBoost)
412
+ if (REACT_NATIVE_TARGET_VERSION < 71) {
413
+ nativeBuildDependsOn(project, prepareBoost, null)
414
+ }
409
415
  if (USE_HERMES) {
410
- preBuild.dependsOn(prepareHermes)
416
+ nativeBuildDependsOn(project, prepareHermes, null)
411
417
  if (hasHermesProject && !prebuiltHermesCacheHit) {
412
418
  prepareHermes.dependsOn(":ReactAndroid:hermes-engine:assembleDebug")
413
419
  }
@@ -9,6 +9,8 @@ set(PACKAGE_NAME "expo-modules-core")
9
9
  set(BUILD_DIR ${CMAKE_SOURCE_DIR}/../build)
10
10
  set(ignoreMe "${PROJECT_BUILD_DIR} ${REACT_ANDROID_BUILD_DIR} ${REACT_ANDROID_DIR} ${HERMES_HEADER_DIR}")
11
11
 
12
+ string(APPEND CMAKE_CXX_FLAGS " -DREACT_NATIVE_TARGET_VERSION=${REACT_NATIVE_TARGET_VERSION}")
13
+
12
14
  if (${NATIVE_DEBUG})
13
15
  set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
14
16
  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
@@ -9,24 +9,27 @@ add_library(fabric STATIC
9
9
  ${SOURCES}
10
10
  )
11
11
 
12
+ include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
13
+
12
14
  target_compile_options(fabric PRIVATE
13
15
  "-std=c++17"
14
- -DFOLLY_NO_CONFIG=1
15
- -DFOLLY_HAVE_CLOCK_GETTIME=1
16
- -DFOLLY_HAVE_MEMRCHR=1
17
- -DFOLLY_USE_LIBCPP=1
18
- -DFOLLY_MOBILE=1
16
+ ${folly_FLAGS}
19
17
  )
20
18
 
19
+ find_package(ReactAndroid REQUIRED CONFIG)
20
+
21
+ find_package(fbjni REQUIRED CONFIG)
22
+
23
+ get_target_property(INCLUDE_fabricjni
24
+ ReactAndroid::fabricjni
25
+ INTERFACE_INCLUDE_DIRECTORIES)
26
+
21
27
  target_include_directories(fabric PRIVATE
22
28
  "${REACT_NATIVE_DIR}/ReactCommon"
23
29
  "${COMMON_FABRIC_DIR}"
30
+ "${INCLUDE_fabricjni}/react/fabric"
24
31
  )
25
32
 
26
- find_package(ReactAndroid REQUIRED CONFIG)
27
-
28
- find_package(fbjni REQUIRED CONFIG)
29
-
30
33
  target_link_libraries(fabric
31
34
  CommonSettings
32
35
  fbjni::fbjni
@@ -2,6 +2,8 @@
2
2
 
3
3
  #include "JavaReferencesCache.h"
4
4
 
5
+ #include <vector>
6
+
5
7
  namespace expo {
6
8
  std::shared_ptr<JavaReferencesCache> JavaReferencesCache::instance() {
7
9
  static std::shared_ptr<JavaReferencesCache> singleton{new JavaReferencesCache};
@@ -21,6 +23,10 @@ void JavaReferencesCache::loadJClasses(JNIEnv *env) {
21
23
  {"<init>", "(I)V"}
22
24
  });
23
25
 
26
+ loadJClass(env, "java/lang/Long", {
27
+ {"<init>", "(J)V"}
28
+ });
29
+
24
30
  loadJClass(env, "java/lang/Float", {
25
31
  {"<init>", "(F)V"}
26
32
  });
@@ -3,7 +3,6 @@
3
3
  #pragma once
4
4
 
5
5
  #include <fbjni/fbjni.h>
6
- #include "boost/functional/hash.hpp"
7
6
 
8
7
  #include <memory>
9
8
  #include <unordered_map>
@@ -11,11 +10,26 @@
11
10
  namespace jni = facebook::jni;
12
11
 
13
12
  namespace expo {
14
- using MethodHashMap = std::unordered_map<
15
- std::pair<std::string, std::string>,
16
- jmethodID,
17
- boost::hash<std::pair<std::string, std::string>>
18
- >;
13
+
14
+ template <typename T>
15
+ inline void hash_combine(std::size_t& seed, const T& v)
16
+ {
17
+ std::hash<T> hasher;
18
+ // Reference from: https://github.com/boostorg/container_hash/blob/boost-1.76.0/include/boost/container_hash/hash.hpp
19
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
20
+ }
21
+
22
+ struct pairhash {
23
+ template <typename A, typename B>
24
+ std::size_t operator()(const std::pair<A, B>& v) const {
25
+ std::size_t seed = 0;
26
+ hash_combine(seed, v.first);
27
+ hash_combine(seed, v.second);
28
+ return seed;
29
+ }
30
+ };
31
+
32
+ using MethodHashMap = std::unordered_map<std::pair<std::string, std::string>, jmethodID, pairhash>;
19
33
 
20
34
  /**
21
35
  * Singleton registry used to store references to often used Java classes.
@@ -42,13 +42,15 @@ void JavaScriptModuleObject::registerNatives() {
42
42
  }
43
43
 
44
44
  std::shared_ptr<jsi::Object> JavaScriptModuleObject::getJSIObject(jsi::Runtime &runtime) {
45
- if (jsiObject == nullptr) {
46
- auto hostObject = std::make_shared<JavaScriptModuleObject::HostObject>(this);
47
- jsiObject = std::make_shared<jsi::Object>(
48
- jsi::Object::createFromHostObject(runtime, hostObject));
45
+ if (auto object = jsiObject.lock()) {
46
+ return object;
49
47
  }
50
48
 
51
- return jsiObject;
49
+ auto hostObject = std::make_shared<JavaScriptModuleObject::HostObject>(this);
50
+ auto object = std::make_shared<jsi::Object>(
51
+ jsi::Object::createFromHostObject(runtime, hostObject));
52
+ jsiObject = object;
53
+ return object;
52
54
  }
53
55
 
54
56
  void JavaScriptModuleObject::exportConstants(
@@ -143,9 +145,8 @@ JavaScriptModuleObject::HostObject::HostObject(
143
145
  * Clears all the JSI references held by the `JavaScriptModuleObject`.
144
146
  */
145
147
  JavaScriptModuleObject::HostObject::~HostObject() {
146
- if (jsModule->jsiObject != nullptr) {
147
- jsModule->jsiObject.reset();
148
- }
148
+ jObjectRef.reset();
149
+ jsModule->jsiObject.reset();
149
150
  jsModule->methodsMetadata.clear();
150
151
  jsModule->constants.clear();
151
152
  jsModule->properties.clear();
@@ -236,5 +237,4 @@ JavaScriptModuleObject::JavaScriptModuleObject(jni::alias_ref<jhybridobject> jTh
236
237
  : javaPart_(jni::make_global(jThis)) {
237
238
  longLivedObjectCollection_ = std::make_shared<react::LongLivedObjectCollection>();
238
239
  }
239
-
240
240
  } // namespace expo
@@ -105,7 +105,7 @@ public:
105
105
  public:
106
106
  HostObject(JavaScriptModuleObject *);
107
107
 
108
- ~HostObject() override;
108
+ ~HostObject() override;
109
109
 
110
110
  jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override;
111
111
 
@@ -113,6 +113,7 @@ public:
113
113
 
114
114
  std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
115
115
 
116
+ jni::global_ref<jobject> jObjectRef;
116
117
  private:
117
118
  JavaScriptModuleObject *jsModule;
118
119
  };
@@ -125,8 +126,10 @@ private:
125
126
  /**
126
127
  * A reference to the `JavaScriptModuleObject::HostObject`.
127
128
  * Simple we cached that value to return the same object each time.
129
+ * It's a weak reference because the JS runtime holds the actual object.
130
+ * Doing that allows the runtime to deallocate jsi::Object if it's not needed anymore.
128
131
  */
129
- std::shared_ptr<jsi::Object> jsiObject = nullptr;
132
+ std::weak_ptr<jsi::Object> jsiObject;
130
133
  jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_;
131
134
 
132
135
  /**
@@ -149,6 +152,5 @@ private:
149
152
  * The `LongLivedObjectCollection` to hold `LongLivedObject` (callbacks or promises) for this module.
150
153
  */
151
154
  std::shared_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_;
152
-
153
155
  };
154
156
  } // namespace expo
@@ -15,7 +15,11 @@
15
15
 
16
16
  #else
17
17
 
18
+ #if REACT_NATIVE_TARGET_VERSION >= 71
19
+ #include <jsc/JSCRuntime.h>
20
+ #else
18
21
  #include <jsi/JSCRuntime.h>
22
+ #endif // REACT_NATIVE_TARGET_VERSION >= 71
19
23
 
20
24
  #endif
21
25
 
@@ -164,7 +164,7 @@ MethodMetadata::MethodMetadata(
164
164
  args(args),
165
165
  isAsync(isAsync),
166
166
  jBodyReference(std::move(jBodyReference)),
167
- longLivedObjectCollection_(longLivedObjectCollection) {
167
+ longLivedObjectCollection_(std::move(longLivedObjectCollection)) {
168
168
  argTypes.reserve(args);
169
169
  for (size_t i = 0; i < args; i++) {
170
170
  auto expectedType = expectedArgTypes->getElement(i);
@@ -186,7 +186,7 @@ MethodMetadata::MethodMetadata(
186
186
  isAsync(isAsync),
187
187
  argTypes(std::move(expectedArgTypes)),
188
188
  jBodyReference(std::move(jBodyReference)),
189
- longLivedObjectCollection_(longLivedObjectCollection) {
189
+ longLivedObjectCollection_(std::move(longLivedObjectCollection)) {
190
190
  }
191
191
 
192
192
  std::shared_ptr<jsi::Function> MethodMetadata::toJSFunction(
@@ -270,6 +270,9 @@ jsi::Value MethodMetadata::callSync(
270
270
  if (env->IsInstanceOf(unpackedResult, cache->getJClass("java/lang/Integer").clazz)) {
271
271
  return {jni::static_ref_cast<jni::JInteger>(result)->value()};
272
272
  }
273
+ if (env->IsInstanceOf(unpackedResult, cache->getJClass("java/lang/Long").clazz)) {
274
+ return {(double) jni::static_ref_cast<jni::JLong>(result)->value()};
275
+ }
273
276
  if (env->IsInstanceOf(unpackedResult, cache->getJClass("java/lang/String").clazz)) {
274
277
  return jsi::String::createFromUtf8(
275
278
  rt,
@@ -300,6 +303,14 @@ jsi::Value MethodMetadata::callSync(
300
303
  ->consume();
301
304
  return jsi::valueFromDynamic(rt, dynamic);
302
305
  }
306
+ if (env->IsInstanceOf(unpackedResult, JavaScriptModuleObject::javaClassStatic().get())) {
307
+ auto anonymousObject = jni::static_ref_cast<JavaScriptModuleObject::javaobject>(result)
308
+ ->cthis();
309
+ anonymousObject->jsiInteropModuleRegistry = moduleRegistry;
310
+ auto hostObject = std::make_shared<JavaScriptModuleObject::HostObject>(anonymousObject);
311
+ hostObject->jObjectRef = jni::make_global(result);
312
+ return jsi::Object::createFromHostObject(rt, hostObject);
313
+ }
303
314
 
304
315
  return jsi::Value::undefined();
305
316
  }
@@ -11,16 +11,17 @@ enum CppType {
11
11
  NONE = 0,
12
12
  DOUBLE = 1 << 0,
13
13
  INT = 1 << 1,
14
- FLOAT = 1 << 2,
15
- BOOLEAN = 1 << 3,
16
- STRING = 1 << 4,
17
- JS_OBJECT = 1 << 5,
18
- JS_VALUE = 1 << 6,
19
- READABLE_ARRAY = 1 << 7,
20
- READABLE_MAP = 1 << 8,
21
- TYPED_ARRAY = 1 << 9,
22
- PRIMITIVE_ARRAY = 1 << 10,
23
- LIST = 1 << 11,
24
- MAP = 1 << 12
14
+ LONG = 1 << 2,
15
+ FLOAT = 1 << 3,
16
+ BOOLEAN = 1 << 4,
17
+ STRING = 1 << 5,
18
+ JS_OBJECT = 1 << 6,
19
+ JS_VALUE = 1 << 7,
20
+ READABLE_ARRAY = 1 << 8,
21
+ READABLE_MAP = 1 << 9,
22
+ TYPED_ARRAY = 1 << 10,
23
+ PRIMITIVE_ARRAY = 1 << 11,
24
+ LIST = 1 << 12,
25
+ MAP = 1 << 13
25
26
  };
26
27
  } // namespace expo
@@ -40,6 +40,23 @@ bool IntegerFrontendConverter::canConvert(jsi::Runtime &rt, const jsi::Value &va
40
40
  return value.isNumber();
41
41
  }
42
42
 
43
+ jobject LongFrontendConverter::convert(
44
+ jsi::Runtime &rt,
45
+ JNIEnv *env,
46
+ JSIInteropModuleRegistry *moduleRegistry,
47
+ const jsi::Value &value
48
+ ) const {
49
+ auto &longClass = JavaReferencesCache::instance()
50
+ ->getJClass("java/lang/Long");
51
+ jmethodID longConstructor = longClass.getMethod("<init>", "(J)V");
52
+ return env->NewObject(longClass.clazz, longConstructor,
53
+ static_cast<jlong>(value.getNumber()));
54
+ }
55
+
56
+ bool LongFrontendConverter::canConvert(jsi::Runtime &rt, const jsi::Value &value) const {
57
+ return value.isNumber();
58
+ }
59
+
43
60
  jobject FloatFrontendConverter::convert(
44
61
  jsi::Runtime &rt,
45
62
  JNIEnv *env,
@@ -293,6 +310,12 @@ jobject PrimitiveArrayFrontendConverter::convert(
293
310
  &JNIEnv::SetIntArrayRegion
294
311
  );
295
312
  }
313
+ if (parameterType == CppType::LONG) {
314
+ return _createPrimitiveArray(
315
+ &JNIEnv::NewLongArray,
316
+ &JNIEnv::SetLongArrayRegion
317
+ );
318
+ }
296
319
  if (parameterType == CppType::DOUBLE) {
297
320
  return _createPrimitiveArray(
298
321
  &JNIEnv::NewDoubleArray,
@@ -55,6 +55,21 @@ public:
55
55
  bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
56
56
  };
57
57
 
58
+ /**
59
+ * Converter from js number to [java.lang.Long].
60
+ */
61
+ class LongFrontendConverter : public FrontendConverter {
62
+ public:
63
+ jobject convert(
64
+ jsi::Runtime &rt,
65
+ JNIEnv *env,
66
+ JSIInteropModuleRegistry *moduleRegistry,
67
+ const jsi::Value &value
68
+ ) const override;
69
+
70
+ bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
71
+ };
72
+
58
73
  /**
59
74
  * Converter from js number to [java.lang.Float].
60
75
  */
@@ -12,6 +12,7 @@ void FrontendConverterProvider::createConverters() {
12
12
  #define RegisterConverter(type, clazz) simpleConverters.insert({type, std::make_shared<clazz>()})
13
13
  RegisterConverter(CppType::NONE, UnknownFrontendConverter);
14
14
  RegisterConverter(CppType::INT, IntegerFrontendConverter);
15
+ RegisterConverter(CppType::LONG, LongFrontendConverter);
15
16
  RegisterConverter(CppType::FLOAT, FloatFrontendConverter);
16
17
  RegisterConverter(CppType::DOUBLE, DoubleFrontendConverter);
17
18
  RegisterConverter(CppType::BOOLEAN, BooleanFrontendConverter);
@@ -1,6 +1,7 @@
1
1
  package expo.modules.kotlin
2
2
 
3
3
  import com.facebook.react.bridge.Dynamic
4
+ import com.facebook.react.bridge.DynamicFromObject
4
5
 
5
6
  inline fun <T> Dynamic.recycle(block: Dynamic.() -> T): T {
6
7
  try {
@@ -9,3 +10,5 @@ inline fun <T> Dynamic.recycle(block: Dynamic.() -> T): T {
9
10
  this.recycle()
10
11
  }
11
12
  }
13
+
14
+ val DynamicNull = DynamicFromObject(null)
@@ -85,8 +85,14 @@ internal class MissingTypeConverter(
85
85
  message = "Cannot find type converter for '$forType'.",
86
86
  )
87
87
 
88
- internal class InvalidArgsNumberException(received: Int, expected: Int) :
89
- CodedException(message = "Received $received arguments, but $expected was expected.")
88
+ internal class InvalidArgsNumberException(received: Int, expected: Int, required: Int = expected) :
89
+ CodedException(
90
+ message = if (required < expected) {
91
+ "Received $received arguments, but $expected was expected and at least $required is required"
92
+ } else {
93
+ "Received $received arguments, but $expected was expected"
94
+ }
95
+ )
90
96
 
91
97
  internal class MethodNotFoundException :
92
98
  CodedException(message = "Method does not exist.")
@@ -22,6 +22,20 @@ abstract class AnyFunction(
22
22
  ) {
23
23
  internal val argsCount get() = desiredArgsTypes.size
24
24
 
25
+ /**
26
+ * A minimum number of arguments the functions needs which equals to `argumentsCount` reduced by the number of trailing optional arguments.
27
+ */
28
+ internal val requiredArgumentsCount = run {
29
+ val nonNullableArgIndex = desiredArgsTypes
30
+ .reversed()
31
+ .indexOfFirst { !it.kType.isMarkedNullable }
32
+ if (nonNullableArgIndex < 0) {
33
+ return@run desiredArgsTypes.size
34
+ }
35
+
36
+ return@run desiredArgsTypes.size - nonNullableArgIndex
37
+ }
38
+
25
39
  /**
26
40
  * Tries to convert arguments from RN representation to expected types.
27
41
  *
@@ -30,23 +44,22 @@ abstract class AnyFunction(
30
44
  */
31
45
  @Throws(CodedException::class)
32
46
  protected fun convertArgs(args: ReadableArray): Array<out Any?> {
33
- if (desiredArgsTypes.size != args.size()) {
34
- throw InvalidArgsNumberException(args.size(), desiredArgsTypes.size)
47
+ if (requiredArgumentsCount > args.size() || args.size() > desiredArgsTypes.size) {
48
+ throw InvalidArgsNumberException(args.size(), desiredArgsTypes.size, requiredArgumentsCount)
35
49
  }
36
50
 
37
51
  val finalArgs = Array<Any?>(desiredArgsTypes.size) { null }
38
52
  val argIterator = args.iterator()
39
- desiredArgsTypes
40
- .withIndex()
41
- .forEach { (index, desiredType) ->
42
- argIterator.next().recycle {
43
- exceptionDecorator({ cause ->
44
- ArgumentCastException(desiredType.kType, index, type, cause)
45
- }) {
46
- finalArgs[index] = desiredType.convert(this)
47
- }
53
+ for (index in 0 until args.size()) {
54
+ val desiredType = desiredArgsTypes[index]
55
+ argIterator.next().recycle {
56
+ exceptionDecorator({ cause ->
57
+ ArgumentCastException(desiredType.kType, index, type, cause)
58
+ }) {
59
+ finalArgs[index] = desiredType.convert(this)
48
60
  }
49
61
  }
62
+ }
50
63
  return finalArgs
51
64
  }
52
65
 
@@ -58,30 +71,28 @@ abstract class AnyFunction(
58
71
  */
59
72
  @Throws(CodedException::class)
60
73
  protected fun convertArgs(args: Array<Any?>): Array<out Any?> {
61
- if (desiredArgsTypes.size != args.size) {
62
- throw InvalidArgsNumberException(args.size, desiredArgsTypes.size)
74
+ if (requiredArgumentsCount > args.size || args.size > desiredArgsTypes.size) {
75
+ throw InvalidArgsNumberException(args.size, desiredArgsTypes.size, requiredArgumentsCount)
63
76
  }
64
77
 
65
78
  val finalArgs = Array<Any?>(desiredArgsTypes.size) { null }
66
79
  val argIterator = args.iterator()
67
- desiredArgsTypes
68
- .withIndex()
69
- .forEach { (index, desiredType) ->
70
- val element = argIterator.next()
71
-
72
- exceptionDecorator({ cause ->
73
- ArgumentCastException(desiredType.kType, index, ReadableType.String, cause)
74
- }) {
75
- finalArgs[index] = desiredType.convert(element)
76
- }
80
+ for (index in args.indices) {
81
+ val element = argIterator.next()
82
+ val desiredType = desiredArgsTypes[index]
83
+ exceptionDecorator({ cause ->
84
+ ArgumentCastException(desiredType.kType, index, ReadableType.String, cause)
85
+ }) {
86
+ finalArgs[index] = desiredType.convert(element)
77
87
  }
88
+ }
78
89
  return finalArgs
79
90
  }
80
91
 
81
92
  /**
82
93
  * Attaches current function to the provided js object.
83
94
  */
84
- internal abstract fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject)
95
+ abstract fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject)
85
96
 
86
97
  fun getCppRequiredTypes(): List<ExpectedType> {
87
98
  return desiredArgsTypes.map { it.getCppRequiredTypes() }