expo-modules-core 1.10.0 → 1.11.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/CHANGELOG.md +22 -0
- package/ExpoModulesCore.podspec +4 -3
- package/android/CMakeLists.txt +1 -1
- package/android/build.gradle +2 -2
- package/android/proguard-rules.pro +6 -2
- package/android/src/fabric/CMakeLists.txt +2 -1
- package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +5 -0
- package/android/src/main/cpp/JSIInteropModuleRegistry.h +4 -0
- package/android/src/main/cpp/MethodMetadata.cpp +13 -0
- package/android/src/main/cpp/types/CppType.h +9 -8
- package/android/src/main/cpp/types/ExpectedType.cpp +3 -0
- package/android/src/main/cpp/types/FrontendConverter.cpp +27 -0
- package/android/src/main/cpp/types/FrontendConverter.h +15 -0
- package/android/src/main/cpp/types/FrontendConverterProvider.cpp +1 -0
- package/android/src/main/cpp/types/JNIToJSIConverter.cpp +93 -6
- package/android/src/main/cpp/types/JNIToJSIConverter.h +6 -0
- package/android/src/main/java/expo/modules/adapters/react/permissions/PermissionsService.kt +11 -1
- package/android/src/main/java/expo/modules/kotlin/AppContext.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +9 -8
- package/android/src/main/java/expo/modules/kotlin/events/KModuleEventEmitterWrapper.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +19 -10
- package/android/src/main/java/expo/modules/kotlin/functions/BaseAsyncFunctionComponent.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +1 -0
- package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptFunction.kt +8 -1
- package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +8 -0
- package/android/src/main/java/expo/modules/kotlin/types/ByteArrayTypeConverter.kt +13 -0
- package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverter.kt +2 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/types/UnitTypeConverter.kt +15 -0
- package/android/src/main/java/expo/modules/kotlin/types/folly/FollyDynamicExtensionConverter.kt +44 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +1 -1
- package/build/EventEmitter.d.ts.map +1 -1
- package/build/EventEmitter.js +0 -4
- package/build/EventEmitter.js.map +1 -1
- package/build/NativeModulesProxy.d.ts +4 -0
- package/build/NativeModulesProxy.d.ts.map +1 -1
- package/build/NativeModulesProxy.js +4 -0
- package/build/NativeModulesProxy.js.map +1 -1
- package/common/cpp/fabric/ExpoViewEventEmitter.h +1 -1
- package/common/cpp/fabric/ExpoViewProps.cpp +2 -1
- package/ios/Arguments/AnyArgument.swift +31 -5
- package/ios/Arguments/Convertible.swift +6 -0
- package/ios/Arguments/Enumerable.swift +6 -0
- package/ios/DynamicTypes/DynamicArrayType.swift +1 -1
- package/ios/DynamicTypes/DynamicDataType.swift +28 -0
- package/ios/DynamicTypes/DynamicDictionaryType.swift +65 -0
- package/ios/DynamicTypes/DynamicOptionalType.swift +1 -1
- package/ios/DynamicTypes/DynamicSharedObjectType.swift +2 -2
- package/ios/DynamicTypes/DynamicType.swift +15 -3
- package/ios/Fabric/ExpoFabricViewObjC.mm +0 -1
- package/ios/FileSystemUtilities/FileSystemUtilities.swift +100 -0
- package/ios/Interfaces/FileSystem/EXFileSystemInterface.h +0 -1
- package/ios/JSI/EXJSIConversions.mm +14 -0
- package/ios/JSI/EXJavaScriptRuntime.mm +35 -3
- package/ios/JSI/JavaScriptValue.swift +7 -1
- package/ios/Records/Field.swift +1 -1
- package/ios/SharedObjects/SharedObject.swift +6 -0
- package/ios/Tests/BlobConvertiblesSpec.swift +94 -0
- package/ios/Tests/ClassComponentSpec.swift +1 -1
- package/ios/Tests/ConstantsSpec.swift +1 -1
- package/ios/Tests/ConvertiblesSpec.swift +1 -1
- package/ios/Tests/CoreModuleSpec.swift +1 -1
- package/ios/Tests/DynamicTypeSpec.swift +1 -1
- package/ios/Tests/EitherSpec.swift +1 -1
- package/ios/Tests/EnumerableSpec.swift +1 -1
- package/ios/Tests/ExceptionsSpec.swift +2 -2
- package/ios/Tests/ExpoModulesSpec.swift +4 -4
- package/ios/Tests/ExpoRequestCdpInterceptorSpec.swift +4 -4
- package/ios/Tests/FunctionSpec.swift +7 -7
- package/ios/Tests/FunctionWithConvertiblesSpec.swift +1 -1
- package/ios/Tests/JavaScriptObjectSpec.swift +1 -1
- package/ios/Tests/JavaScriptRuntimeSpec.swift +1 -1
- package/ios/Tests/ModuleEventListenersSpec.swift +1 -1
- package/ios/Tests/ModuleRegistrySpec.swift +1 -1
- package/ios/Tests/PersistentFileLogSpec.swift +32 -34
- package/ios/Tests/PropertyComponentSpec.swift +1 -1
- package/ios/Tests/RecordSpec.swift +5 -5
- package/ios/Tests/SharedObjectRegistrySpec.swift +1 -1
- package/ios/Tests/SharedRefSpec.swift +1 -1
- package/ios/Tests/TypedArraysSpec.swift +1 -1
- package/ios/Tests/ViewDefinitionSpec.swift +1 -1
- package/ios/TypedArrays/AnyTypedArray.swift +7 -0
- package/ios/Views/ViewDefinition.swift +5 -1
- package/package.json +2 -2
- package/src/EventEmitter.ts +0 -3
- package/src/NativeModulesProxy.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,28 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.11.0 — 2023-12-12
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Added support for React Native 0.73.0. ([#24971](https://github.com/expo/expo/pull/24971), [#25453](https://github.com/expo/expo/pull/25453) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
|
18
|
+
- Added `Data <-> Uint8Array` convertible on iOS. ([#25726](https://github.com/expo/expo/pull/25726) by [@kudo](https://github.com/kudo))
|
|
19
|
+
- Added `ByteArray <-> Uint8Array` convertible on Android. ([#25727](https://github.com/expo/expo/pull/25727) by [@kudo](https://github.com/kudo))
|
|
20
|
+
|
|
21
|
+
### 🐛 Bug fixes
|
|
22
|
+
|
|
23
|
+
- [Android] Prevent the app from crashing during reloading when an unfinished promise tries to execute.
|
|
24
|
+
- [Android] Fix `JavaScriptFunction` not working when the return type wasn't provided. ([#25688](https://github.com/expo/expo/pull/25688) by [@lukmccall](https://github.com/lukmccall))
|
|
25
|
+
- [Android] Fix requesting only `WRITE_SETTINGS` rejecting promise even if the permission was granted. ([#25732](https://github.com/expo/expo/pull/25732) by [@lukmccall](https://github.com/lukmccall))
|
|
26
|
+
- [Android] Fix functions that are scheduled on the main thread weren't being called as soon as possible. ([#25757](https://github.com/expo/expo/pull/25757) by [@lukmccall](https://github.com/lukmccall))
|
|
27
|
+
|
|
28
|
+
### 💡 Others
|
|
29
|
+
|
|
30
|
+
- [iOS] Made dynamic types creation faster. ([#25390](https://github.com/expo/expo/pull/25390) by [@tsapeta](https://github.com/tsapeta))
|
|
31
|
+
- [iOS] Add `FileSystemUtilities` to replace legacy interfaces. ([#25495](https://github.com/expo/expo/pull/25495) by [@alanhughes](https://github.com/alanjhughes))
|
|
32
|
+
- Bump C++ compiler setting to C++20. ([#25548](https://github.com/expo/expo/pull/25548) by [@kudo](https://github.com/kudo))
|
|
33
|
+
- Marked `NativeModulesProxy` as deprecated in favor of `requireNativeModule` and `requireOptionalNativeModule`. ([#25666](https://github.com/expo/expo/pull/25666) by [@tsapeta](https://github.com/tsapeta))
|
|
34
|
+
|
|
13
35
|
## 1.10.0 — 2023-11-14
|
|
14
36
|
|
|
15
37
|
### 🛠 Breaking changes
|
package/ExpoModulesCore.podspec
CHANGED
|
@@ -13,8 +13,8 @@ reactNativeMinorVersion = reactNativeVersion.split('.')[1].to_i
|
|
|
13
13
|
|
|
14
14
|
fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
|
15
15
|
fabric_compiler_flags = '-DRN_FABRIC_ENABLED -DRCT_NEW_ARCH_ENABLED'
|
|
16
|
-
folly_version = '
|
|
17
|
-
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
16
|
+
folly_version = '2022.05.16.00'
|
|
17
|
+
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_CFG_NO_COROUTINES=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
18
18
|
|
|
19
19
|
Pod::Spec.new do |s|
|
|
20
20
|
s.name = 'ExpoModulesCore'
|
|
@@ -46,7 +46,7 @@ Pod::Spec.new do |s|
|
|
|
46
46
|
s.pod_target_xcconfig = {
|
|
47
47
|
'USE_HEADERMAP' => 'YES',
|
|
48
48
|
'DEFINES_MODULE' => 'YES',
|
|
49
|
-
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++
|
|
49
|
+
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20',
|
|
50
50
|
'SWIFT_COMPILATION_MODE' => 'wholemodule',
|
|
51
51
|
'HEADER_SEARCH_PATHS' => header_search_paths.join(' '),
|
|
52
52
|
"FRAMEWORK_SEARCH_PATHS" => "\"${PODS_CONFIGURATION_BUILD_DIR}/React-hermes\"",
|
|
@@ -56,6 +56,7 @@ Pod::Spec.new do |s|
|
|
|
56
56
|
'"${PODS_CONFIGURATION_BUILD_DIR}/ExpoModulesCore/Swift Compatibility Header"',
|
|
57
57
|
'"$(PODS_ROOT)/Headers/Private/React-bridging/react/bridging"',
|
|
58
58
|
'"$(PODS_CONFIGURATION_BUILD_DIR)/React-bridging/react_bridging.framework/Headers"',
|
|
59
|
+
'"$(PODS_ROOT)/Headers/Private/Yoga"',
|
|
59
60
|
]
|
|
60
61
|
if fabric_enabled && ENV['USE_FRAMEWORKS']
|
|
61
62
|
user_header_search_paths << "\"$(PODS_ROOT)/DoubleConversion\""
|
package/android/CMakeLists.txt
CHANGED
package/android/build.gradle
CHANGED
|
@@ -5,7 +5,7 @@ apply plugin: 'kotlin-android'
|
|
|
5
5
|
apply plugin: 'maven-publish'
|
|
6
6
|
|
|
7
7
|
group = 'host.exp.exponent'
|
|
8
|
-
version = '1.
|
|
8
|
+
version = '1.11.0'
|
|
9
9
|
|
|
10
10
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
11
11
|
if (expoModulesCorePlugin.exists()) {
|
|
@@ -143,7 +143,7 @@ android {
|
|
|
143
143
|
defaultConfig {
|
|
144
144
|
consumerProguardFiles 'proguard-rules.pro'
|
|
145
145
|
versionCode 1
|
|
146
|
-
versionName "1.
|
|
146
|
+
versionName "1.11.0"
|
|
147
147
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
|
|
148
148
|
|
|
149
149
|
testInstrumentationRunner "expo.modules.TestRunner"
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
-keepclassmembers class * {
|
|
3
2
|
@expo.modules.core.interfaces.ExpoProp *;
|
|
4
3
|
}
|
|
@@ -16,7 +15,12 @@
|
|
|
16
15
|
*;
|
|
17
16
|
}
|
|
18
17
|
-keepclassmembers enum * implements expo.modules.kotlin.types.Enumerable {
|
|
19
|
-
*;
|
|
18
|
+
*;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
-keep,allowoptimization,allowobfuscation class * extends expo.modules.kotlin.modules.Module {
|
|
22
|
+
public <init>();
|
|
23
|
+
public expo.modules.kotlin.modules.ModuleDefinitionData definition();
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
-keepclassmembers class * implements expo.modules.kotlin.views.ExpoView {
|
|
@@ -12,7 +12,7 @@ add_library(fabric STATIC
|
|
|
12
12
|
include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
|
|
13
13
|
|
|
14
14
|
target_compile_options(fabric PRIVATE
|
|
15
|
-
"-std=c++
|
|
15
|
+
"-std=c++20"
|
|
16
16
|
${folly_FLAGS}
|
|
17
17
|
)
|
|
18
18
|
|
|
@@ -43,6 +43,7 @@ target_link_libraries(fabric
|
|
|
43
43
|
ReactAndroid::react_render_debug
|
|
44
44
|
ReactAndroid::react_render_graphics
|
|
45
45
|
ReactAndroid::react_render_mapbuffer
|
|
46
|
+
ReactAndroid::react_utils
|
|
46
47
|
ReactAndroid::rrc_view
|
|
47
48
|
ReactAndroid::runtimeexecutor
|
|
48
49
|
ReactAndroid::yoga
|
|
@@ -30,6 +30,7 @@ void JSIInteropModuleRegistry::registerNatives() {
|
|
|
30
30
|
makeNativeMethod("global", JSIInteropModuleRegistry::global),
|
|
31
31
|
makeNativeMethod("createObject", JSIInteropModuleRegistry::createObject),
|
|
32
32
|
makeNativeMethod("drainJSEventLoop", JSIInteropModuleRegistry::drainJSEventLoop),
|
|
33
|
+
makeNativeMethod("wasDeallocated", JSIInteropModuleRegistry::jniWasDeallocated),
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -188,4 +189,8 @@ void JSIInteropModuleRegistry::registerSharedObject(
|
|
|
188
189
|
);
|
|
189
190
|
method(javaPart_, std::move(native), std::move(js));
|
|
190
191
|
}
|
|
192
|
+
|
|
193
|
+
void JSIInteropModuleRegistry::jniWasDeallocated() {
|
|
194
|
+
wasDeallocated = true;
|
|
195
|
+
}
|
|
191
196
|
} // namespace expo
|
|
@@ -109,6 +109,8 @@ public:
|
|
|
109
109
|
std::shared_ptr<JavaScriptRuntime> runtimeHolder;
|
|
110
110
|
std::unique_ptr<JSReferencesCache> jsRegistry;
|
|
111
111
|
jni::global_ref<JNIDeallocator::javaobject> jniDeallocator;
|
|
112
|
+
|
|
113
|
+
bool wasDeallocated = false;
|
|
112
114
|
private:
|
|
113
115
|
friend HybridBase;
|
|
114
116
|
jni::global_ref<JSIInteropModuleRegistry::javaobject> javaPart_;
|
|
@@ -123,5 +125,7 @@ private:
|
|
|
123
125
|
inline jni::local_ref<JavaScriptModuleObject::javaobject> callGetCoreModuleObject() const;
|
|
124
126
|
|
|
125
127
|
inline bool callHasModule(const std::string &moduleName) const;
|
|
128
|
+
|
|
129
|
+
void jniWasDeallocated();
|
|
126
130
|
};
|
|
127
131
|
} // namespace expo
|
|
@@ -71,6 +71,10 @@ jni::local_ref<JavaCallback::JavaPart> createJavaCallbackFromJSIFunction(
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
jsi::Value arg = jsi::valueFromDynamic(strongWrapper2->runtime(), responses);
|
|
74
|
+
auto enhancedArg = decorateValueForDynamicExtension(strongWrapper2->runtime(), arg);
|
|
75
|
+
if (enhancedArg) {
|
|
76
|
+
arg = std::move(*enhancedArg);
|
|
77
|
+
}
|
|
74
78
|
if (!isRejectCallback) {
|
|
75
79
|
strongWrapper2->callback().call(
|
|
76
80
|
strongWrapper2->runtime(),
|
|
@@ -308,6 +312,15 @@ jsi::Function MethodMetadata::toAsyncFunction(
|
|
|
308
312
|
const jsi::Value *args,
|
|
309
313
|
size_t count
|
|
310
314
|
) -> jsi::Value {
|
|
315
|
+
/**
|
|
316
|
+
* Halt execution during cleaning phase as modules and js context will be deallocated soon.
|
|
317
|
+
* The output of this method doesn't matter.
|
|
318
|
+
* We added that check to prevent the app from crashing when users reload their apps.
|
|
319
|
+
*/
|
|
320
|
+
if (moduleRegistry->wasDeallocated) {
|
|
321
|
+
return jsi::Value::undefined();
|
|
322
|
+
}
|
|
323
|
+
|
|
311
324
|
JNIEnv *env = jni::Environment::current();
|
|
312
325
|
|
|
313
326
|
/**
|
|
@@ -19,13 +19,14 @@ enum CppType {
|
|
|
19
19
|
JS_VALUE = 1 << 7,
|
|
20
20
|
READABLE_ARRAY = 1 << 8,
|
|
21
21
|
READABLE_MAP = 1 << 9,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
UINT8_TYPED_ARRAY = 1 << 10,
|
|
23
|
+
TYPED_ARRAY = 1 << 11,
|
|
24
|
+
PRIMITIVE_ARRAY = 1 << 12,
|
|
25
|
+
LIST = 1 << 13,
|
|
26
|
+
MAP = 1 << 14,
|
|
27
|
+
VIEW_TAG = 1 << 15,
|
|
28
|
+
SHARED_OBJECT_ID = 1 << 16,
|
|
29
|
+
JS_FUNCTION = 1 << 17,
|
|
30
|
+
ANY = 1 << 18
|
|
30
31
|
};
|
|
31
32
|
} // namespace expo
|
|
@@ -70,6 +70,9 @@ std::string ExpectedType::getJClassString(bool allowsPrimitives) {
|
|
|
70
70
|
if (type == CppType::READABLE_MAP) {
|
|
71
71
|
return "com/facebook/react/bridge/ReadableNativeMap";
|
|
72
72
|
}
|
|
73
|
+
if (type == CppType::UINT8_TYPED_ARRAY) {
|
|
74
|
+
return "[B";
|
|
75
|
+
}
|
|
73
76
|
if (type == CppType::TYPED_ARRAY) {
|
|
74
77
|
return "expo/modules/kotlin/jni/JavaScriptTypedArray";
|
|
75
78
|
}
|
|
@@ -153,6 +153,33 @@ bool ReadableNativeMapArrayFrontendConverter::canConvert(
|
|
|
153
153
|
return value.isObject();
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
jobject ByteArrayFrontendConverter::convert(
|
|
157
|
+
jsi::Runtime &rt,
|
|
158
|
+
JNIEnv *env,
|
|
159
|
+
JSIInteropModuleRegistry *moduleRegistry,
|
|
160
|
+
const jsi::Value &value
|
|
161
|
+
) const {
|
|
162
|
+
auto typedArray = TypedArray(rt, value.getObject(rt));
|
|
163
|
+
size_t length = typedArray.byteLength(rt);
|
|
164
|
+
auto byteArray = jni::JArrayByte::newArray(length);
|
|
165
|
+
byteArray->setRegion(0, length, static_cast<const signed char *>(typedArray.getRawPointer(rt)));
|
|
166
|
+
return byteArray.release();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
bool ByteArrayFrontendConverter::canConvert(
|
|
170
|
+
jsi::Runtime &rt,
|
|
171
|
+
const jsi::Value &value
|
|
172
|
+
) const {
|
|
173
|
+
if (value.isObject()) {
|
|
174
|
+
auto object = value.getObject(rt);
|
|
175
|
+
if (isTypedArray(rt, object)) {
|
|
176
|
+
auto typedArray = TypedArray(rt, object);
|
|
177
|
+
return typedArray.getKind(rt) == TypedArrayKind::Uint8Array;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
156
183
|
jobject TypedArrayFrontendConverter::convert(
|
|
157
184
|
jsi::Runtime &rt,
|
|
158
185
|
JNIEnv *env,
|
|
@@ -160,6 +160,21 @@ public:
|
|
|
160
160
|
bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
|
|
161
161
|
};
|
|
162
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Converter from js Uint8Array to [java.lang.Byte] array.
|
|
165
|
+
*/
|
|
166
|
+
class ByteArrayFrontendConverter : public FrontendConverter {
|
|
167
|
+
public:
|
|
168
|
+
jobject convert(
|
|
169
|
+
jsi::Runtime &rt,
|
|
170
|
+
JNIEnv *env,
|
|
171
|
+
JSIInteropModuleRegistry *moduleRegistry,
|
|
172
|
+
const jsi::Value &value
|
|
173
|
+
) const override;
|
|
174
|
+
|
|
175
|
+
bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
|
|
176
|
+
};
|
|
177
|
+
|
|
163
178
|
/**
|
|
164
179
|
* Converter from js type array to [expo.modules.kotlin.jni.JavaScriptTypedArray].
|
|
165
180
|
*/
|
|
@@ -16,6 +16,7 @@ void FrontendConverterProvider::createConverters() {
|
|
|
16
16
|
RegisterConverter(CppType::FLOAT, FloatFrontendConverter);
|
|
17
17
|
RegisterConverter(CppType::DOUBLE, DoubleFrontendConverter);
|
|
18
18
|
RegisterConverter(CppType::BOOLEAN, BooleanFrontendConverter);
|
|
19
|
+
RegisterConverter(CppType::UINT8_TYPED_ARRAY, ByteArrayFrontendConverter);
|
|
19
20
|
RegisterConverter(CppType::TYPED_ARRAY, TypedArrayFrontendConverter);
|
|
20
21
|
RegisterConverter(CppType::JS_OBJECT, JavaScriptObjectFrontendConverter);
|
|
21
22
|
RegisterConverter(CppType::JS_VALUE, JavaScriptValueFrontendConverter);
|
|
@@ -11,6 +11,44 @@
|
|
|
11
11
|
|
|
12
12
|
namespace react = facebook::react;
|
|
13
13
|
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
// This value should be synced with the value in **FollyDynamicExtensionConverter.kt**
|
|
17
|
+
constexpr char DYNAMIC_EXTENSION_PREFIX[] = "__expo_dynamic_extension__#";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create an JavaScript Uint8Array instance from Java ByteArray.
|
|
21
|
+
*/
|
|
22
|
+
jsi::Value createUint8Array(jsi::Runtime &rt, jni::alias_ref<jni::JArrayByte> byteArray) {
|
|
23
|
+
auto arrayBufferCtor = rt.global().getPropertyAsFunction(rt, "ArrayBuffer");
|
|
24
|
+
auto arrayBufferObject = arrayBufferCtor.callAsConstructor(rt, static_cast<int>(byteArray->size())).getObject(rt);
|
|
25
|
+
auto arrayBuffer = arrayBufferObject.getArrayBuffer(rt);
|
|
26
|
+
byteArray->getRegion(0, byteArray->size(), reinterpret_cast<signed char *>(arrayBuffer.data(rt)));
|
|
27
|
+
|
|
28
|
+
auto uint8ArrayCtor = rt.global().getPropertyAsFunction(rt, "Uint8Array");
|
|
29
|
+
auto uint8Array = uint8ArrayCtor.callAsConstructor(rt, arrayBufferObject).getObject(rt);
|
|
30
|
+
return uint8Array;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Convert a string with FollyDynamicExtensionConverter support.
|
|
35
|
+
*/
|
|
36
|
+
std::optional<jsi::Value> convertStringToFollyDynamicIfNeeded(jsi::Runtime &rt, const std::string& string) {
|
|
37
|
+
if (!string.starts_with(DYNAMIC_EXTENSION_PREFIX)) {
|
|
38
|
+
return std::nullopt;
|
|
39
|
+
}
|
|
40
|
+
auto converterClass = jni::findClassLocal("expo/modules/kotlin/types/folly/FollyDynamicExtensionConverter");
|
|
41
|
+
const auto getInstanceMethod = converterClass->getStaticMethod<jni::JObject(std::string)>("get");
|
|
42
|
+
jni::local_ref<jni::JObject> instance = getInstanceMethod(converterClass, string);
|
|
43
|
+
|
|
44
|
+
if (instance->isInstanceOf(jni::JArrayByte::javaClassStatic())) {
|
|
45
|
+
return createUint8Array(rt, jni::static_ref_cast<jni::JArrayByte>(instance));
|
|
46
|
+
}
|
|
47
|
+
return std::nullopt;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
} // namespace
|
|
51
|
+
|
|
14
52
|
namespace expo {
|
|
15
53
|
|
|
16
54
|
jsi::Value convert(
|
|
@@ -34,10 +72,9 @@ jsi::Value convert(
|
|
|
34
72
|
return {(double) jni::static_ref_cast<jni::JLong>(value)->value()};
|
|
35
73
|
}
|
|
36
74
|
if (env->IsInstanceOf(unpackedValue, cache->getJClass("java/lang/String").clazz)) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
75
|
+
std::string string = jni::static_ref_cast<jni::JString>(value)->toStdString();
|
|
76
|
+
auto enhancedValue = convertStringToFollyDynamicIfNeeded(rt, string);
|
|
77
|
+
return enhancedValue ? std::move(*enhancedValue) : jsi::String::createFromUtf8(rt, string);
|
|
41
78
|
}
|
|
42
79
|
if (env->IsInstanceOf(unpackedValue, cache->getJClass("java/lang/Boolean").clazz)) {
|
|
43
80
|
return {(bool) jni::static_ref_cast<jni::JBoolean>(value)->value()};
|
|
@@ -52,7 +89,12 @@ jsi::Value convert(
|
|
|
52
89
|
auto dynamic = jni::static_ref_cast<react::WritableNativeArray::javaobject>(value)
|
|
53
90
|
->cthis()
|
|
54
91
|
->consume();
|
|
55
|
-
|
|
92
|
+
auto arg = jsi::valueFromDynamic(rt, dynamic);
|
|
93
|
+
auto enhancedArg = decorateValueForDynamicExtension(rt, arg);
|
|
94
|
+
if (enhancedArg) {
|
|
95
|
+
arg = std::move(*enhancedArg);
|
|
96
|
+
}
|
|
97
|
+
return arg;
|
|
56
98
|
}
|
|
57
99
|
if (env->IsInstanceOf(
|
|
58
100
|
unpackedValue,
|
|
@@ -61,7 +103,12 @@ jsi::Value convert(
|
|
|
61
103
|
auto dynamic = jni::static_ref_cast<react::WritableNativeMap::javaobject>(value)
|
|
62
104
|
->cthis()
|
|
63
105
|
->consume();
|
|
64
|
-
|
|
106
|
+
auto arg = jsi::valueFromDynamic(rt, dynamic);
|
|
107
|
+
auto enhancedArg = decorateValueForDynamicExtension(rt, arg);
|
|
108
|
+
if (enhancedArg) {
|
|
109
|
+
arg = std::move(*enhancedArg);
|
|
110
|
+
}
|
|
111
|
+
return arg;
|
|
65
112
|
}
|
|
66
113
|
if (env->IsInstanceOf(unpackedValue, JavaScriptModuleObject::javaClassStatic().get())) {
|
|
67
114
|
auto anonymousObject = jni::static_ref_cast<JavaScriptModuleObject::javaobject>(value)
|
|
@@ -106,4 +153,44 @@ jsi::Value convert(
|
|
|
106
153
|
|
|
107
154
|
return jsi::Value::undefined();
|
|
108
155
|
}
|
|
156
|
+
|
|
157
|
+
std::optional<jsi::Value> decorateValueForDynamicExtension(jsi::Runtime &rt, const jsi::Value &value) {
|
|
158
|
+
if (value.isString()) {
|
|
159
|
+
std::string string = value.getString(rt).utf8(rt);
|
|
160
|
+
return convertStringToFollyDynamicIfNeeded(rt, string);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (value.isObject()) {
|
|
164
|
+
auto jsObject = value.getObject(rt);
|
|
165
|
+
if (jsObject.isArray(rt)) {
|
|
166
|
+
bool changed = false;
|
|
167
|
+
auto jsArray = jsObject.getArray(rt);
|
|
168
|
+
size_t length = jsArray.length(rt);
|
|
169
|
+
for (size_t i = 0; i < length; ++i) {
|
|
170
|
+
auto converted = decorateValueForDynamicExtension(rt, jsArray.getValueAtIndex(rt, i));
|
|
171
|
+
if (converted) {
|
|
172
|
+
jsArray.setValueAtIndex(rt, i, std::move(*converted));
|
|
173
|
+
changed = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return changed ? std::make_optional<jsi::Value>(std::move(jsArray)) : std::nullopt;
|
|
177
|
+
} else {
|
|
178
|
+
bool changed = false;
|
|
179
|
+
auto propNames = jsObject.getPropertyNames(rt);
|
|
180
|
+
size_t length = propNames.length(rt);
|
|
181
|
+
for (size_t i = 0; i < length; ++i) {
|
|
182
|
+
auto propName = propNames.getValueAtIndex(rt, i).getString(rt);
|
|
183
|
+
auto converted = decorateValueForDynamicExtension(rt, jsObject.getProperty(rt, propName));
|
|
184
|
+
if (converted) {
|
|
185
|
+
jsObject.setProperty(rt, propName, std::move(*converted));
|
|
186
|
+
changed = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return changed ? std::make_optional<jsi::Value>(std::move(jsObject)) : std::nullopt;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return std::nullopt;
|
|
194
|
+
}
|
|
195
|
+
|
|
109
196
|
} // namespace expo
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
#include <fbjni/fbjni.h>
|
|
8
8
|
#include <jsi/jsi.h>
|
|
9
|
+
#include <optional>
|
|
9
10
|
|
|
10
11
|
namespace jni = facebook::jni;
|
|
11
12
|
namespace jsi = facebook::jsi;
|
|
@@ -19,4 +20,9 @@ jsi::Value convert(
|
|
|
19
20
|
jni::local_ref<jobject> value
|
|
20
21
|
);
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Decorate jsi::Value with FollyDynamicExtensionConverter support.
|
|
25
|
+
*/
|
|
26
|
+
std::optional<jsi::Value> decorateValueForDynamicExtension(jsi::Runtime &rt, const jsi::Value &value);
|
|
27
|
+
|
|
22
28
|
} // namespace expo
|
|
@@ -64,7 +64,7 @@ open class PermissionsService(val context: Context) : InternalModule, Permission
|
|
|
64
64
|
getPermissions(
|
|
65
65
|
PermissionsResponseListener { permissionsMap: MutableMap<String, PermissionsResponse> ->
|
|
66
66
|
val areAllGranted = permissionsMap.all { (_, response) -> response.status == PermissionsStatus.GRANTED }
|
|
67
|
-
val areAllDenied = permissionsMap.all { (_, response) -> response.status == PermissionsStatus.DENIED }
|
|
67
|
+
val areAllDenied = permissionsMap.isNotEmpty() && permissionsMap.all { (_, response) -> response.status == PermissionsStatus.DENIED }
|
|
68
68
|
val canAskAgain = permissionsMap.all { (_, response) -> response.canAskAgain }
|
|
69
69
|
|
|
70
70
|
promise.resolve(
|
|
@@ -113,6 +113,11 @@ open class PermissionsService(val context: Context) : InternalModule, Permission
|
|
|
113
113
|
|
|
114
114
|
@Throws(IllegalStateException::class)
|
|
115
115
|
override fun askForPermissions(responseListener: PermissionsResponseListener, vararg permissions: String) {
|
|
116
|
+
if (permissions.isEmpty()) {
|
|
117
|
+
responseListener.onResult(mutableMapOf())
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
116
121
|
if (permissions.contains(Manifest.permission.WRITE_SETTINGS)) {
|
|
117
122
|
val permissionsToAsk = permissions.toMutableList().apply { remove(Manifest.permission.WRITE_SETTINGS) }.toTypedArray()
|
|
118
123
|
val newListener = PermissionsResponseListener {
|
|
@@ -136,6 +141,11 @@ open class PermissionsService(val context: Context) : InternalModule, Permission
|
|
|
136
141
|
addToAskedPermissionsCache(arrayOf(Manifest.permission.WRITE_SETTINGS))
|
|
137
142
|
askForWriteSettingsPermissionFirst()
|
|
138
143
|
} else {
|
|
144
|
+
// User only ask for `WRITE_SETTINGS`, we can already return response
|
|
145
|
+
if (permissionsToAsk.isEmpty()) {
|
|
146
|
+
newListener.onResult(mutableMapOf())
|
|
147
|
+
return
|
|
148
|
+
}
|
|
139
149
|
askForManifestPermissions(permissionsToAsk, newListener)
|
|
140
150
|
}
|
|
141
151
|
} else {
|
|
@@ -295,6 +295,9 @@ class AppContext(
|
|
|
295
295
|
modulesQueue.cancel(ContextDestroyedException())
|
|
296
296
|
mainQueue.cancel(ContextDestroyedException())
|
|
297
297
|
backgroundCoroutineScope.cancel(ContextDestroyedException())
|
|
298
|
+
if (::jsiInterop.isInitialized) {
|
|
299
|
+
jsiInterop.wasDeallocated()
|
|
300
|
+
}
|
|
298
301
|
jniDeallocator.deallocate()
|
|
299
302
|
logger.info("✅ AppContext was destroyed")
|
|
300
303
|
}
|
|
@@ -15,7 +15,7 @@ import expo.modules.kotlin.tracing.trace
|
|
|
15
15
|
import kotlinx.coroutines.launch
|
|
16
16
|
import kotlin.reflect.KClass
|
|
17
17
|
|
|
18
|
-
class ModuleHolder(val module:
|
|
18
|
+
class ModuleHolder<T : Module>(val module: T) {
|
|
19
19
|
val definition = module.definition()
|
|
20
20
|
|
|
21
21
|
val name get() = definition.name
|
|
@@ -12,11 +12,11 @@ import java.lang.ref.WeakReference
|
|
|
12
12
|
|
|
13
13
|
class ModuleRegistry(
|
|
14
14
|
private val appContext: WeakReference<AppContext>
|
|
15
|
-
) : Iterable<ModuleHolder
|
|
15
|
+
) : Iterable<ModuleHolder<*>> {
|
|
16
16
|
@PublishedApi
|
|
17
|
-
internal val registry = mutableMapOf<String, ModuleHolder
|
|
17
|
+
internal val registry = mutableMapOf<String, ModuleHolder<*>>()
|
|
18
18
|
|
|
19
|
-
fun register(module:
|
|
19
|
+
fun <T : Module> register(module: T) = trace("ModuleRegistry.register(${module.javaClass})") {
|
|
20
20
|
module._appContext = requireNotNull(appContext.get()) { "Cannot create a module for invalid app context." }
|
|
21
21
|
|
|
22
22
|
val holder = ModuleHolder(module)
|
|
@@ -56,12 +56,13 @@ class ModuleRegistry(
|
|
|
56
56
|
return registry.values.find { it.module is T }?.module as? T
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
fun getModuleHolder(name: String): ModuleHolder
|
|
59
|
+
fun getModuleHolder(name: String): ModuleHolder<*>? = registry[name]
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
@Suppress("UNCHECKED_CAST")
|
|
62
|
+
fun <T : Module> getModuleHolder(module: T): ModuleHolder<T>? =
|
|
63
|
+
registry.values.find { it.module === module } as? ModuleHolder<T>
|
|
63
64
|
|
|
64
|
-
fun <T : View> getModuleHolder(viewClass: Class<T>): ModuleHolder
|
|
65
|
+
fun <T : View> getModuleHolder(viewClass: Class<T>): ModuleHolder<*>? {
|
|
65
66
|
return registry.firstNotNullOfOrNull { (_, holder) ->
|
|
66
67
|
if (holder.definition.viewManagerDefinition?.viewType == viewClass) {
|
|
67
68
|
holder
|
|
@@ -91,7 +92,7 @@ class ModuleRegistry(
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
override fun iterator(): Iterator<ModuleHolder
|
|
95
|
+
override fun iterator(): Iterator<ModuleHolder<*>> = registry.values.iterator()
|
|
95
96
|
|
|
96
97
|
fun cleanUp() {
|
|
97
98
|
registry.clear()
|
|
@@ -17,7 +17,7 @@ import java.lang.ref.WeakReference
|
|
|
17
17
|
* But because of that, we had to create a wrapper for EventEmitter.
|
|
18
18
|
*/
|
|
19
19
|
class KModuleEventEmitterWrapper(
|
|
20
|
-
private val moduleHolder: ModuleHolder
|
|
20
|
+
private val moduleHolder: ModuleHolder<*>,
|
|
21
21
|
legacyEventEmitter: expo.modules.core.interfaces.services.EventEmitter,
|
|
22
22
|
reactContextHolder: WeakReference<ReactApplicationContext>
|
|
23
23
|
) : KEventEmitterWrapper(legacyEventEmitter, reactContextHolder) {
|
|
@@ -50,7 +50,7 @@ abstract class AnyFunction(
|
|
|
50
50
|
/**
|
|
51
51
|
* A minimum number of arguments the functions needs which equals to `argumentsCount` reduced by the number of trailing optional arguments.
|
|
52
52
|
*/
|
|
53
|
-
|
|
53
|
+
private val requiredArgumentsCount = run {
|
|
54
54
|
val nonNullableArgIndex = desiredArgsTypes
|
|
55
55
|
.reversed()
|
|
56
56
|
.indexOfFirst { !it.kType.isMarkedNullable }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package expo.modules.kotlin.functions
|
|
2
2
|
|
|
3
|
+
import android.view.View
|
|
3
4
|
import com.facebook.react.bridge.ReadableArray
|
|
4
5
|
import expo.modules.BuildConfig
|
|
5
6
|
import expo.modules.kotlin.AppContext
|
|
@@ -22,7 +23,7 @@ abstract class AsyncFunction(
|
|
|
22
23
|
desiredArgsTypes: Array<AnyType>
|
|
23
24
|
) : BaseAsyncFunctionComponent(name, desiredArgsTypes) {
|
|
24
25
|
|
|
25
|
-
override fun call(holder: ModuleHolder
|
|
26
|
+
override fun call(holder: ModuleHolder<*>, args: ReadableArray, promise: Promise) {
|
|
26
27
|
val queue = when (queue) {
|
|
27
28
|
Queues.MAIN -> holder.module.appContext.mainQueue
|
|
28
29
|
Queues.DEFAULT -> null
|
|
@@ -85,24 +86,32 @@ abstract class AsyncFunction(
|
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
dispatchOnQueue(appContext, functionBody)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private fun dispatchOnQueue(appContext: AppContext, block: () -> Unit) {
|
|
94
|
+
when (queue) {
|
|
95
|
+
Queues.DEFAULT -> {
|
|
96
|
+
appContext.modulesQueue.launch {
|
|
97
|
+
block()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Queues.MAIN -> {
|
|
102
|
+
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED && desiredArgsTypes.any { it.inheritFrom<View>() }) {
|
|
90
103
|
// On certain occasions, invoking a function on a view could lead to an error
|
|
91
104
|
// because of the asynchronous communication between the JavaScript and native components.
|
|
92
105
|
// In such cases, the native view may not have been mounted yet,
|
|
93
106
|
// but the JavaScript code has already received the future tag of the view.
|
|
94
107
|
// To avoid this issue, we have decided to temporarily utilize
|
|
95
108
|
// the UIManagerModule for dispatching functions on the main thread.
|
|
96
|
-
appContext.dispatchOnMainUsingUIManager(
|
|
97
|
-
return
|
|
109
|
+
appContext.dispatchOnMainUsingUIManager(block)
|
|
110
|
+
return
|
|
98
111
|
}
|
|
99
112
|
|
|
100
113
|
appContext.mainQueue.launch {
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
appContext.modulesQueue.launch {
|
|
105
|
-
functionBody()
|
|
114
|
+
block()
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
117
|
}
|
|
@@ -17,7 +17,7 @@ abstract class BaseAsyncFunctionComponent(
|
|
|
17
17
|
|
|
18
18
|
protected var queue = Queues.DEFAULT
|
|
19
19
|
|
|
20
|
-
abstract fun call(holder: ModuleHolder
|
|
20
|
+
abstract fun call(holder: ModuleHolder<*>, args: ReadableArray, promise: Promise)
|
|
21
21
|
|
|
22
22
|
fun runOnQueue(queue: Queues) = apply {
|
|
23
23
|
this.queue = queue
|
|
@@ -22,7 +22,7 @@ class SuspendFunctionComponent(
|
|
|
22
22
|
private val body: suspend CoroutineScope.(args: Array<out Any?>) -> Any?
|
|
23
23
|
) : BaseAsyncFunctionComponent(name, desiredArgsTypes) {
|
|
24
24
|
|
|
25
|
-
override fun call(holder: ModuleHolder
|
|
25
|
+
override fun call(holder: ModuleHolder<*>, args: ReadableArray, promise: Promise) {
|
|
26
26
|
val appContext = holder.module.appContext
|
|
27
27
|
val queue = when (queue) {
|
|
28
28
|
Queues.MAIN -> appContext.mainQueue
|
|
@@ -24,6 +24,7 @@ enum class CppType(val clazz: KClass<*>, val value: Int = nextValue()) {
|
|
|
24
24
|
JS_VALUE(JavaScriptValue::class),
|
|
25
25
|
READABLE_ARRAY(ReadableArray::class),
|
|
26
26
|
READABLE_MAP(ReadableMap::class),
|
|
27
|
+
UINT8_TYPED_ARRAY(ByteArray::class),
|
|
27
28
|
TYPED_ARRAY(TypedArray::class),
|
|
28
29
|
PRIMITIVE_ARRAY(Array::class),
|
|
29
30
|
LIST(List::class),
|
|
@@ -68,6 +68,11 @@ class JSIInteropModuleRegistry(appContext: AppContext) : Destructible {
|
|
|
68
68
|
*/
|
|
69
69
|
external fun drainJSEventLoop()
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Informs C++ that runtime was deallocated.
|
|
73
|
+
*/
|
|
74
|
+
external fun wasDeallocated()
|
|
75
|
+
|
|
71
76
|
/**
|
|
72
77
|
* Returns a `JavaScriptModuleObject` that is a bridge between [expo.modules.kotlin.modules.Module]
|
|
73
78
|
* and HostObject exported via JSI.
|