expo-modules-core 3.0.13 → 3.0.15
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 +18 -0
- package/android/build.gradle +2 -2
- package/android/src/main/cpp/ExpoModulesHostObject.cpp +4 -0
- package/android/src/main/java/expo/modules/kotlin/KClassExtensions.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructorFactory.kt +3 -2
- package/android/src/main/java/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +4 -4
- package/android/src/main/java/expo/modules/kotlin/defaultmodules/CoreModule.kt +17 -12
- package/android/src/main/java/expo/modules/kotlin/defaultmodules/FilePermissionModule.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/devtools/OkHttpExtensions.kt +6 -2
- package/android/src/main/java/expo/modules/kotlin/events/KModuleEventEmitterWrapper.kt +4 -2
- package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +0 -30
- package/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt +20 -18
- package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptTypedArray.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +3 -4
- package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObject.kt +4 -0
- package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObjectTypeConverter.kt +3 -1
- package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedRef.kt +4 -0
- package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -7
- package/android/src/main/java/expo/modules/kotlin/types/Either.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +25 -18
- package/android/src/main/java/expo/modules/kotlin/types/TypedArrayTypeConverter.kt +16 -37
- package/android/src/main/java/expo/modules/kotlin/types/io/FileTypeConverter.kt +2 -0
- package/android/src/main/java/expo/modules/kotlin/views/ExpoView.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +13 -13
- package/android/src/main/java/expo/modules/kotlin/views/ViewTypeConverter.kt +1 -3
- package/ios/Core/Convertibles/Convertibles+Color.swift +8 -4
- package/ios/Core/Views/SwiftUI/Convertibles+SwiftUI.swift +5 -2
- package/ios/FileSystemUtilities/FileSystemUtilities.swift +3 -11
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 3.0.15 — 2025-09-10
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [Android] Fixes `JNI detected error in application: obj == null` in `ExpoModulesHostObject::get`. ([#39523](https://github.com/expo/expo/pull/39523) by [@lukmccall](https://github.com/lukmccall))
|
|
18
|
+
|
|
19
|
+
## 3.0.14 — 2025-09-08
|
|
20
|
+
|
|
21
|
+
### 🐛 Bug fixes
|
|
22
|
+
|
|
23
|
+
- [Android] Fix type check in the `SharedRef` converter. ([#39446](https://github.com/expo/expo/pull/39446) by [@lukmccall](https://github.com/lukmccall))
|
|
24
|
+
|
|
25
|
+
### 💡 Others
|
|
26
|
+
|
|
27
|
+
- improve startup performance by not relying on kotlin reflection. ([#39389](https://github.com/expo/expo/pull/39389) by [@ACHP](https://github.com/ACHP))
|
|
28
|
+
|
|
13
29
|
## 3.0.13 — 2025-09-04
|
|
14
30
|
|
|
15
31
|
### 💡 Others
|
|
@@ -21,6 +37,8 @@
|
|
|
21
37
|
### 💡 Others
|
|
22
38
|
|
|
23
39
|
- Use new LongLivedObject.h and CallbackWrapper.h headers namespace ([#39344](https://github.com/expo/expo/pull/39344) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
|
40
|
+
- [iOS] Added `Color` convertible support without passing `AppContext`. ([#39183](https://github.com/expo/expo/pull/39183) by [@aleqsio](https://github.com/aleqsio))
|
|
41
|
+
- Extracted the read permission check from `FileSystemUtilities` ([#39210](https://github.com/expo/expo/pull/39210) by [@kosmydel](https://github.com/kosmydel))
|
|
24
42
|
|
|
25
43
|
## 3.0.11 — 2025-09-02
|
|
26
44
|
|
package/android/build.gradle
CHANGED
|
@@ -25,7 +25,7 @@ if (shouldIncludeCompose) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
group = 'host.exp.exponent'
|
|
28
|
-
version = '3.0.
|
|
28
|
+
version = '3.0.15'
|
|
29
29
|
|
|
30
30
|
def isExpoModulesCoreTests = {
|
|
31
31
|
Gradle gradle = getGradle()
|
|
@@ -75,7 +75,7 @@ android {
|
|
|
75
75
|
defaultConfig {
|
|
76
76
|
consumerProguardFiles 'proguard-rules.pro'
|
|
77
77
|
versionCode 1
|
|
78
|
-
versionName "3.0.
|
|
78
|
+
versionName "3.0.15"
|
|
79
79
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
80
80
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
|
|
81
81
|
|
|
@@ -28,6 +28,10 @@ ExpoModulesHostObject::~ExpoModulesHostObject() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
jsi::Value ExpoModulesHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &name) {
|
|
31
|
+
if (installer->wasDeallocated()) {
|
|
32
|
+
return jsi::Value::undefined();
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
auto cName = name.utf8(runtime);
|
|
32
36
|
|
|
33
37
|
if (UniqueJSIObject &cachedObject = modulesCache[cName]) {
|
|
@@ -8,3 +8,6 @@ val <T : Any> KClass<T>.fastPrimaryConstructor: KFunction<T>?
|
|
|
8
8
|
// If class only has one constructor, use it as a primary constructor.
|
|
9
9
|
// Otherwise, try to find the primary constructor using kotlin reflection.
|
|
10
10
|
get() = constructors.singleOrNull() ?: primaryConstructor
|
|
11
|
+
|
|
12
|
+
fun KClass<*>.fastIsSupperClassOf(jClass: Class<*>) =
|
|
13
|
+
this.javaObjectType.isAssignableFrom(jClass) || this.java.isAssignableFrom(jClass)
|
|
@@ -8,7 +8,8 @@ import kotlin.reflect.KParameter
|
|
|
8
8
|
*/
|
|
9
9
|
class ObjectConstructorFactory {
|
|
10
10
|
fun <T : Any> get(clazz: KClass<T>): ObjectConstructor<T> =
|
|
11
|
-
tryToUseDefaultConstructor(clazz.java)
|
|
11
|
+
tryToUseDefaultConstructor(clazz.java)
|
|
12
|
+
?: tryToUseDefaultKotlinConstructor(clazz)
|
|
12
13
|
?: useUnsafeAllocator(clazz.java)
|
|
13
14
|
|
|
14
15
|
private fun <T> tryToUseDefaultConstructor(clazz: Class<T>): ObjectConstructor<T>? {
|
|
@@ -21,7 +22,7 @@ class ObjectConstructorFactory {
|
|
|
21
22
|
ObjectConstructor {
|
|
22
23
|
ctor.newInstance() as T
|
|
23
24
|
}
|
|
24
|
-
} catch (
|
|
25
|
+
} catch (_: NoSuchMethodException) {
|
|
25
26
|
null
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -10,7 +10,8 @@ import expo.modules.kotlin.functions.SyncFunctionComponent
|
|
|
10
10
|
import expo.modules.kotlin.objects.ObjectDefinitionBuilder
|
|
11
11
|
import expo.modules.kotlin.objects.PropertyComponentBuilderWithThis
|
|
12
12
|
import expo.modules.kotlin.sharedobjects.SharedObject
|
|
13
|
-
import expo.modules.kotlin.sharedobjects.
|
|
13
|
+
import expo.modules.kotlin.sharedobjects.isSharedObjectClass
|
|
14
|
+
import expo.modules.kotlin.sharedobjects.isSharedRefClass
|
|
14
15
|
import expo.modules.kotlin.traits.Trait
|
|
15
16
|
import expo.modules.kotlin.types.AnyType
|
|
16
17
|
import expo.modules.kotlin.types.TypeConverterProvider
|
|
@@ -19,7 +20,6 @@ import expo.modules.kotlin.types.toAnyType
|
|
|
19
20
|
import expo.modules.kotlin.types.toArgsArray
|
|
20
21
|
import expo.modules.kotlin.types.toReturnType
|
|
21
22
|
import kotlin.reflect.KClass
|
|
22
|
-
import kotlin.reflect.full.isSubclassOf
|
|
23
23
|
|
|
24
24
|
class ClassComponentBuilder<SharedObjectType : Any>(
|
|
25
25
|
private val appContext: AppContext,
|
|
@@ -33,8 +33,8 @@ class ClassComponentBuilder<SharedObjectType : Any>(
|
|
|
33
33
|
|
|
34
34
|
fun buildClass(): ClassDefinitionData {
|
|
35
35
|
val hasOwnerType = ownerClass != Unit::class
|
|
36
|
-
val isSharedObject = hasOwnerType && ownerClass.
|
|
37
|
-
val isSharedRef = hasOwnerType && ownerClass.
|
|
36
|
+
val isSharedObject = hasOwnerType && ownerClass.isSharedObjectClass()
|
|
37
|
+
val isSharedRef = hasOwnerType && ownerClass.isSharedRefClass()
|
|
38
38
|
|
|
39
39
|
if (eventsDefinition != null && isSharedObject) {
|
|
40
40
|
listOf("__expo_onStartListeningToEvent" to SharedObject::onStartListeningToEvent, "__expo_onStopListeningToEvent" to SharedObject::onStopListeningToEvent)
|
|
@@ -19,16 +19,19 @@ class CoreModule : Module() {
|
|
|
19
19
|
|
|
20
20
|
override fun definition() = ModuleDefinition {
|
|
21
21
|
Property("expoModulesCoreVersion") {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
val version = BuildConfig.EXPO_MODULES_CORE_VERSION
|
|
23
|
+
val (major, minor, patch) = version
|
|
24
|
+
.split("-")
|
|
25
|
+
.first()
|
|
26
|
+
.split(".")
|
|
27
|
+
.map { it.toInt() }
|
|
28
|
+
|
|
29
|
+
return@Property mapOf(
|
|
30
|
+
"version" to version,
|
|
31
|
+
"major" to major,
|
|
32
|
+
"minor" to minor,
|
|
33
|
+
"patch" to patch
|
|
34
|
+
)
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
Property("cacheDir") {
|
|
@@ -47,7 +50,7 @@ class CoreModule : Module() {
|
|
|
47
50
|
Function("uuidv5") { name: String, namespace: String ->
|
|
48
51
|
val namespaceUUID = try {
|
|
49
52
|
UUID.fromString(namespace)
|
|
50
|
-
} catch (
|
|
53
|
+
} catch (_: IllegalArgumentException) {
|
|
51
54
|
throw InvalidNamespaceException(namespace)
|
|
52
55
|
}
|
|
53
56
|
return@Function uuidv5(namespaceUUID, name).toString()
|
|
@@ -57,7 +60,9 @@ class CoreModule : Module() {
|
|
|
57
60
|
val holder = runtimeContext.registry.getModuleHolder(moduleName)
|
|
58
61
|
?: return@Function null
|
|
59
62
|
|
|
60
|
-
val viewManagerDefinition = holder
|
|
63
|
+
val viewManagerDefinition = holder
|
|
64
|
+
.definition
|
|
65
|
+
.viewManagerDefinitions[viewName ?: DEFAULT_MODULE_VIEW]
|
|
61
66
|
?: return@Function null
|
|
62
67
|
|
|
63
68
|
val validAttributes = viewManagerDefinition
|
|
@@ -22,7 +22,7 @@ open class FilePermissionModule : FilePermissionModuleInterface, InternalModule
|
|
|
22
22
|
getInternalPaths(context)
|
|
23
23
|
.firstOrNull { dir -> canonicalPath.startsWith("$dir/") || dir == canonicalPath }
|
|
24
24
|
?.let { EnumSet.of(Permission.READ, Permission.WRITE) }
|
|
25
|
-
} catch (
|
|
25
|
+
} catch (_: IOException) {
|
|
26
26
|
EnumSet.noneOf(Permission::class.java)
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -31,7 +31,7 @@ fun Headers.toSingleMap(): Map<String, String> {
|
|
|
31
31
|
*/
|
|
32
32
|
suspend inline fun Request.await(okHttpClient: OkHttpClient): Response {
|
|
33
33
|
return suspendCancellableCoroutine { callback ->
|
|
34
|
-
|
|
34
|
+
val responseCallback = object : Callback {
|
|
35
35
|
override fun onResponse(call: Call, response: Response) {
|
|
36
36
|
callback.resume(response)
|
|
37
37
|
}
|
|
@@ -42,6 +42,10 @@ suspend inline fun Request.await(okHttpClient: OkHttpClient): Response {
|
|
|
42
42
|
}
|
|
43
43
|
callback.resumeWithException(e)
|
|
44
44
|
}
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
okHttpClient
|
|
48
|
+
.newCall(this)
|
|
49
|
+
.enqueue(responseCallback)
|
|
46
50
|
}
|
|
47
51
|
}
|
|
@@ -95,16 +95,18 @@ open class KEventEmitterWrapper(
|
|
|
95
95
|
|
|
96
96
|
override fun emit(viewId: Int, eventName: String, eventBody: WritableMap?, coalescingKey: Short?) {
|
|
97
97
|
val context = reactContextHolder.get() ?: return
|
|
98
|
+
val uiEvent = UIEvent(surfaceId = -1, viewId, eventName, eventBody, coalescingKey)
|
|
98
99
|
UIManagerHelper.getEventDispatcherForReactTag(context, viewId)
|
|
99
|
-
?.dispatchEvent(
|
|
100
|
+
?.dispatchEvent(uiEvent)
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
override fun emit(view: View, eventName: String, eventBody: WritableMap?, coalescingKey: Short?) {
|
|
103
104
|
val context = reactContextHolder.get() ?: return
|
|
104
105
|
val surfaceId = UIManagerHelper.getSurfaceId(view)
|
|
105
106
|
val viewId = view.id
|
|
107
|
+
val uiEvent = UIEvent(surfaceId, viewId, eventName, eventBody, coalescingKey)
|
|
106
108
|
UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
|
|
107
|
-
?.dispatchEvent(
|
|
109
|
+
?.dispatchEvent(uiEvent)
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
private class UIEvent(
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
package expo.modules.kotlin.functions
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.bridge.ReadableArray
|
|
4
3
|
import expo.modules.kotlin.AppContext
|
|
5
4
|
import expo.modules.kotlin.exception.ArgumentCastException
|
|
6
5
|
import expo.modules.kotlin.exception.CodedException
|
|
7
6
|
import expo.modules.kotlin.exception.InvalidArgsNumberException
|
|
8
7
|
import expo.modules.kotlin.exception.exceptionDecorator
|
|
9
|
-
import expo.modules.kotlin.iterator
|
|
10
8
|
import expo.modules.kotlin.jni.ExpectedType
|
|
11
9
|
import expo.modules.kotlin.jni.JavaScriptObject
|
|
12
10
|
import expo.modules.kotlin.jni.decorators.JSDecoratorsBridgingObject
|
|
13
|
-
import expo.modules.kotlin.recycle
|
|
14
11
|
import expo.modules.kotlin.types.AnyType
|
|
15
12
|
import kotlin.reflect.KClass
|
|
16
13
|
import kotlin.reflect.KType
|
|
@@ -61,33 +58,6 @@ abstract class AnyFunction(
|
|
|
61
58
|
return@run desiredArgsTypes.size - nonNullableArgIndex
|
|
62
59
|
}
|
|
63
60
|
|
|
64
|
-
/**
|
|
65
|
-
* Tries to convert arguments from RN representation to expected types.
|
|
66
|
-
*
|
|
67
|
-
* @return An array of converted arguments
|
|
68
|
-
* @throws `CodedException` if conversion isn't possible
|
|
69
|
-
*/
|
|
70
|
-
@Throws(CodedException::class)
|
|
71
|
-
protected fun convertArgs(args: ReadableArray): Array<out Any?> {
|
|
72
|
-
if (requiredArgumentsCount > args.size() || args.size() > desiredArgsTypes.size) {
|
|
73
|
-
throw InvalidArgsNumberException(args.size(), desiredArgsTypes.size, requiredArgumentsCount)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
val finalArgs = arrayOfNulls<Any?>(desiredArgsTypes.size)
|
|
77
|
-
val argIterator = args.iterator()
|
|
78
|
-
for (index in 0 until args.size()) {
|
|
79
|
-
val desiredType = desiredArgsTypes[index]
|
|
80
|
-
argIterator.next().recycle {
|
|
81
|
-
exceptionDecorator({ cause ->
|
|
82
|
-
ArgumentCastException(desiredType.kType, index, type.toString(), cause)
|
|
83
|
-
}) {
|
|
84
|
-
finalArgs[index] = desiredType.convert(this)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return finalArgs
|
|
89
|
-
}
|
|
90
|
-
|
|
91
61
|
/**
|
|
92
62
|
* Tries to convert arguments from [Any]? to expected types.
|
|
93
63
|
*
|
|
@@ -12,6 +12,10 @@ interface Destructible {
|
|
|
12
12
|
|
|
13
13
|
@DoNotStrip
|
|
14
14
|
class JNIDeallocator(shouldCreateDestructorThread: Boolean = true) : AutoCloseable {
|
|
15
|
+
private inner class DeallocatorThread : Thread("Expo JNI deallocator") {
|
|
16
|
+
override fun run() = deallocator()
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
/**
|
|
16
20
|
* A [PhantomReference] queue managed by JVM
|
|
17
21
|
*/
|
|
@@ -27,24 +31,7 @@ class JNIDeallocator(shouldCreateDestructorThread: Boolean = true) : AutoCloseab
|
|
|
27
31
|
* to not store invalid references to every created object.
|
|
28
32
|
*/
|
|
29
33
|
private val destructorThread = if (shouldCreateDestructorThread) {
|
|
30
|
-
|
|
31
|
-
override fun run() {
|
|
32
|
-
while (!isInterrupted) {
|
|
33
|
-
try {
|
|
34
|
-
// Referent of PhantomReference were garbage collected so we can remove it from our registry.
|
|
35
|
-
// Note that we don't have to call `deallocate` method - it was called [com.facebook.jni.HybridData].
|
|
36
|
-
val current = referenceQueue.remove()
|
|
37
|
-
synchronized(this@JNIDeallocator) {
|
|
38
|
-
destructorMap.remove(current)
|
|
39
|
-
}
|
|
40
|
-
} catch (e: InterruptedException) {
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}.also {
|
|
46
|
-
it.start()
|
|
47
|
-
}
|
|
34
|
+
DeallocatorThread().apply { start() }
|
|
48
35
|
} else {
|
|
49
36
|
null
|
|
50
37
|
}
|
|
@@ -80,6 +67,21 @@ class JNIDeallocator(shouldCreateDestructorThread: Boolean = true) : AutoCloseab
|
|
|
80
67
|
destructorMap.values.mapNotNull { it.get() }
|
|
81
68
|
}
|
|
82
69
|
|
|
70
|
+
private fun Thread.deallocator() {
|
|
71
|
+
while (!isInterrupted) {
|
|
72
|
+
try {
|
|
73
|
+
// Referent of PhantomReference were garbage collected so we can remove it from our registry.
|
|
74
|
+
// Note that we don't have to call `deallocate` method - it was called [com.facebook.jni.HybridData].
|
|
75
|
+
val current = referenceQueue.remove()
|
|
76
|
+
synchronized(this@JNIDeallocator) {
|
|
77
|
+
destructorMap.remove(current)
|
|
78
|
+
}
|
|
79
|
+
} catch (_: InterruptedException) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
83
85
|
override fun close() {
|
|
84
86
|
deallocate()
|
|
85
87
|
}
|
|
@@ -30,7 +30,7 @@ class JavaScriptTypedArray @DoNotStrip constructor(hybridData: HybridData) :
|
|
|
30
30
|
|
|
31
31
|
override val kind: TypedArrayKind by lazy {
|
|
32
32
|
val rawKind = getRawKind()
|
|
33
|
-
TypedArrayKind.
|
|
33
|
+
TypedArrayKind.entries.first { it.value == rawKind }
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
override val length: Int by lazy {
|
|
@@ -32,18 +32,17 @@ class RecordTypeConverter<T : Record>(
|
|
|
32
32
|
private val propertyDescriptors: Map<KProperty1<out Any, *>, PropertyDescriptor> by lazy {
|
|
33
33
|
(type.classifier as KClass<*>)
|
|
34
34
|
.memberProperties
|
|
35
|
-
.
|
|
36
|
-
val fieldAnnotation = property.findAnnotation<Field>() ?: return@
|
|
35
|
+
.mapNotNull { property ->
|
|
36
|
+
val fieldAnnotation = property.findAnnotation<Field>() ?: return@mapNotNull null
|
|
37
37
|
val typeConverter = converterProvider.obtainTypeConverter(property.returnType)
|
|
38
38
|
|
|
39
|
-
return@
|
|
39
|
+
return@mapNotNull property to PropertyDescriptor(
|
|
40
40
|
typeConverter,
|
|
41
41
|
fieldAnnotation,
|
|
42
42
|
isRequired = property.findAnnotation<Required>() != null,
|
|
43
43
|
validators = getValidators(property)
|
|
44
44
|
)
|
|
45
45
|
}
|
|
46
|
-
.filterNotNull()
|
|
47
46
|
.toMap()
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -8,6 +8,7 @@ import expo.modules.kotlin.jni.JavaScriptWeakObject
|
|
|
8
8
|
import expo.modules.kotlin.logger
|
|
9
9
|
import expo.modules.kotlin.types.JSTypeConverter
|
|
10
10
|
import expo.modules.kotlin.weak
|
|
11
|
+
import kotlin.reflect.KClass
|
|
11
12
|
|
|
12
13
|
@DoNotStrip
|
|
13
14
|
open class SharedObject(runtimeContext: RuntimeContext? = null) {
|
|
@@ -83,3 +84,6 @@ open class SharedObject(runtimeContext: RuntimeContext? = null) {
|
|
|
83
84
|
return 0
|
|
84
85
|
}
|
|
85
86
|
}
|
|
87
|
+
|
|
88
|
+
fun KClass<*>.isSharedObjectClass() =
|
|
89
|
+
SharedObject::class.java.isAssignableFrom(this.java)
|
package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObjectTypeConverter.kt
CHANGED
|
@@ -3,6 +3,7 @@ package expo.modules.kotlin.sharedobjects
|
|
|
3
3
|
import com.facebook.react.bridge.Dynamic
|
|
4
4
|
import expo.modules.kotlin.AppContext
|
|
5
5
|
import expo.modules.kotlin.exception.IncorrectRefTypeException
|
|
6
|
+
import expo.modules.kotlin.fastIsSupperClassOf
|
|
6
7
|
import expo.modules.kotlin.jni.CppType
|
|
7
8
|
import expo.modules.kotlin.jni.ExpectedType
|
|
8
9
|
import expo.modules.kotlin.toStrongReference
|
|
@@ -73,7 +74,8 @@ class SharedRefTypeConverter<T : SharedRef<*>>(
|
|
|
73
74
|
val ref = sharedRef.ref ?: return sharedRef
|
|
74
75
|
val sharedRefClass = sharedRefType?.classifier as? KClass<*>
|
|
75
76
|
?: return sharedRef
|
|
76
|
-
|
|
77
|
+
|
|
78
|
+
if (sharedRefClass.fastIsSupperClassOf(ref.javaClass)) {
|
|
77
79
|
return sharedRef
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -3,6 +3,7 @@ package expo.modules.kotlin.sharedobjects
|
|
|
3
3
|
import expo.modules.core.interfaces.DoNotStrip
|
|
4
4
|
import expo.modules.kotlin.AppContext
|
|
5
5
|
import expo.modules.kotlin.RuntimeContext
|
|
6
|
+
import kotlin.reflect.KClass
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Shared object (ref) that holds a strong reference to any native object. Allows passing references
|
|
@@ -29,3 +30,6 @@ inline fun <reified RefType> SharedRef<*>.cast(): SharedRef<RefType>? {
|
|
|
29
30
|
|
|
30
31
|
return null
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
fun KClass<*>.isSharedRefClass() =
|
|
35
|
+
SharedRef::class.java.isAssignableFrom(this.java)
|
|
@@ -21,7 +21,6 @@ import expo.modules.kotlin.typedarray.Uint16Array
|
|
|
21
21
|
import expo.modules.kotlin.typedarray.Uint32Array
|
|
22
22
|
import expo.modules.kotlin.typedarray.Uint8Array
|
|
23
23
|
import expo.modules.kotlin.typedarray.Uint8ClampedArray
|
|
24
|
-
import expo.modules.kotlin.types.AnyTypeProvider.typesMap
|
|
25
24
|
import java.io.File
|
|
26
25
|
import java.net.URI
|
|
27
26
|
import java.net.URL
|
|
@@ -90,7 +89,7 @@ class EmptyKType(
|
|
|
90
89
|
|
|
91
90
|
object AnyTypeProvider {
|
|
92
91
|
@PublishedApi
|
|
93
|
-
internal val typesMap = buildMap
|
|
92
|
+
internal val typesMap = buildMap {
|
|
94
93
|
listOf(
|
|
95
94
|
Int::class,
|
|
96
95
|
Float::class,
|
|
@@ -312,11 +311,8 @@ class AnyType(
|
|
|
312
311
|
) {
|
|
313
312
|
|
|
314
313
|
private val converter: TypeConverter<*> by lazy {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
} else {
|
|
318
|
-
TypeConverterProviderImpl.obtainTypeConverter(kType)
|
|
319
|
-
}
|
|
314
|
+
converterProvider?.obtainTypeConverter(kType)
|
|
315
|
+
?: TypeConverterProviderImpl.obtainTypeConverter(kType)
|
|
320
316
|
}
|
|
321
317
|
|
|
322
318
|
fun convert(value: Any?, appContext: AppContext? = null, forceConversion: Boolean = false): Any? {
|
|
@@ -40,7 +40,10 @@ class EnumTypeConverter(
|
|
|
40
40
|
|
|
41
41
|
override fun convertFromDynamic(value: Dynamic, context: AppContext?, forceConversion: Boolean): Enum<*> {
|
|
42
42
|
if (primaryConstructor.parameters.isEmpty()) {
|
|
43
|
-
return convertEnumWithoutParameter(
|
|
43
|
+
return convertEnumWithoutParameter(
|
|
44
|
+
value.asString() ?: throw DynamicCastException(String::class),
|
|
45
|
+
enumConstants
|
|
46
|
+
)
|
|
44
47
|
} else if (primaryConstructor.parameters.size == 1) {
|
|
45
48
|
return convertEnumWithParameter(
|
|
46
49
|
value,
|
|
@@ -93,23 +96,7 @@ class EnumTypeConverter(
|
|
|
93
96
|
filed.isAccessible = true
|
|
94
97
|
|
|
95
98
|
val parameterType = filed.type
|
|
96
|
-
val jsUnwrapValue =
|
|
97
|
-
if (parameterType == String::class.java) {
|
|
98
|
-
jsValue.asString()
|
|
99
|
-
} else {
|
|
100
|
-
jsValue.asInt()
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
if (parameterType == String::class.java) {
|
|
104
|
-
jsValue as String
|
|
105
|
-
} else {
|
|
106
|
-
if (jsValue is Double) {
|
|
107
|
-
jsValue.toInt()
|
|
108
|
-
} else {
|
|
109
|
-
jsValue as Int
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
99
|
+
val jsUnwrapValue = jsValue.unwrapValue(parameterType)
|
|
113
100
|
|
|
114
101
|
return requireNotNull(
|
|
115
102
|
enumConstants.find {
|
|
@@ -117,4 +104,24 @@ class EnumTypeConverter(
|
|
|
117
104
|
}
|
|
118
105
|
) { "Couldn't convert '$jsValue' to ${enumClass.simpleName} where $parameterName is the enum parameter" }
|
|
119
106
|
}
|
|
107
|
+
|
|
108
|
+
private fun Any.unwrapValue(parameterType: Class<*>): Any? {
|
|
109
|
+
if (this is Dynamic) {
|
|
110
|
+
if (parameterType == String::class.java) {
|
|
111
|
+
return asString()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return asInt()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (parameterType == String::class.java) {
|
|
118
|
+
return this as String
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this is Double) {
|
|
122
|
+
return toInt()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return this as Int
|
|
126
|
+
}
|
|
120
127
|
}
|
|
@@ -17,59 +17,38 @@ import expo.modules.kotlin.typedarray.Uint32Array
|
|
|
17
17
|
import expo.modules.kotlin.typedarray.Uint8Array
|
|
18
18
|
import expo.modules.kotlin.typedarray.Uint8ClampedArray
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
open class BaseTypeArrayConverter<T : TypedArray>(
|
|
21
|
+
val typedArrayWrapper: (rawArray: JavaScriptTypedArray) -> T
|
|
22
|
+
) : NonNullableTypeConverter<T>() {
|
|
21
23
|
override fun convertNonNullable(value: Any, context: AppContext?, forceConversion: Boolean): T = wrapJavaScriptTypedArray(value as JavaScriptTypedArray)
|
|
22
24
|
override fun getCppRequiredTypes(): ExpectedType = ExpectedType(CppType.TYPED_ARRAY)
|
|
23
25
|
override fun isTrivial() = false
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray): T = typedArrayWrapper(value)
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
class Int8ArrayTypeConverter() : BaseTypeArrayConverter<Int8Array>()
|
|
29
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Int8Array(value)
|
|
30
|
-
}
|
|
30
|
+
class Int8ArrayTypeConverter() : BaseTypeArrayConverter<Int8Array>(::Int8Array)
|
|
31
31
|
|
|
32
|
-
class Int16ArrayTypeConverter() : BaseTypeArrayConverter<Int16Array>()
|
|
33
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Int16Array(value)
|
|
34
|
-
}
|
|
32
|
+
class Int16ArrayTypeConverter() : BaseTypeArrayConverter<Int16Array>(::Int16Array)
|
|
35
33
|
|
|
36
|
-
class Int32ArrayTypeConverter() : BaseTypeArrayConverter<Int32Array>()
|
|
37
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Int32Array(value)
|
|
38
|
-
}
|
|
34
|
+
class Int32ArrayTypeConverter() : BaseTypeArrayConverter<Int32Array>(::Int32Array)
|
|
39
35
|
|
|
40
|
-
class Uint8ArrayTypeConverter() : BaseTypeArrayConverter<Uint8Array>()
|
|
41
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Uint8Array(value)
|
|
42
|
-
}
|
|
36
|
+
class Uint8ArrayTypeConverter() : BaseTypeArrayConverter<Uint8Array>(::Uint8Array)
|
|
43
37
|
|
|
44
|
-
class Uint8ClampedArrayTypeConverter() : BaseTypeArrayConverter<Uint8ClampedArray>()
|
|
45
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Uint8ClampedArray(value)
|
|
46
|
-
}
|
|
38
|
+
class Uint8ClampedArrayTypeConverter() : BaseTypeArrayConverter<Uint8ClampedArray>(::Uint8ClampedArray)
|
|
47
39
|
|
|
48
|
-
class Uint16ArrayTypeConverter() : BaseTypeArrayConverter<Uint16Array>()
|
|
49
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Uint16Array(value)
|
|
50
|
-
}
|
|
40
|
+
class Uint16ArrayTypeConverter() : BaseTypeArrayConverter<Uint16Array>(::Uint16Array)
|
|
51
41
|
|
|
52
|
-
class Uint32ArrayTypeConverter() : BaseTypeArrayConverter<Uint32Array>()
|
|
53
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Uint32Array(value)
|
|
54
|
-
}
|
|
42
|
+
class Uint32ArrayTypeConverter() : BaseTypeArrayConverter<Uint32Array>(::Uint32Array)
|
|
55
43
|
|
|
56
|
-
class Float32ArrayTypeConverter() : BaseTypeArrayConverter<Float32Array>()
|
|
57
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Float32Array(value)
|
|
58
|
-
}
|
|
44
|
+
class Float32ArrayTypeConverter() : BaseTypeArrayConverter<Float32Array>(::Float32Array)
|
|
59
45
|
|
|
60
|
-
class Float64ArrayTypeConverter() : BaseTypeArrayConverter<Float64Array>()
|
|
61
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = Float64Array(value)
|
|
62
|
-
}
|
|
46
|
+
class Float64ArrayTypeConverter() : BaseTypeArrayConverter<Float64Array>(::Float64Array)
|
|
63
47
|
|
|
64
|
-
class BigInt64ArrayTypeConverter() : BaseTypeArrayConverter<BigInt64Array>()
|
|
65
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = BigInt64Array(value)
|
|
66
|
-
}
|
|
48
|
+
class BigInt64ArrayTypeConverter() : BaseTypeArrayConverter<BigInt64Array>(::BigInt64Array)
|
|
67
49
|
|
|
68
|
-
class BigUint64ArrayTypeConverter() : BaseTypeArrayConverter<BigUint64Array>()
|
|
69
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray) = BigUint64Array(value)
|
|
70
|
-
}
|
|
50
|
+
class BigUint64ArrayTypeConverter() : BaseTypeArrayConverter<BigUint64Array>(::BigUint64Array)
|
|
71
51
|
|
|
72
|
-
class TypedArrayTypeConverter() : BaseTypeArrayConverter<TypedArray>() {
|
|
73
|
-
override fun wrapJavaScriptTypedArray(value: JavaScriptTypedArray): TypedArray = value
|
|
52
|
+
class TypedArrayTypeConverter() : BaseTypeArrayConverter<TypedArray>({ it }) {
|
|
74
53
|
override fun isTrivial() = true
|
|
75
54
|
}
|
|
@@ -2,6 +2,7 @@ package expo.modules.kotlin.types.io
|
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.Dynamic
|
|
4
4
|
import expo.modules.kotlin.AppContext
|
|
5
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
5
6
|
import expo.modules.kotlin.jni.CppType
|
|
6
7
|
import expo.modules.kotlin.jni.ExpectedType
|
|
7
8
|
import expo.modules.kotlin.types.DynamicAwareTypeConverters
|
|
@@ -10,6 +11,7 @@ import java.io.File
|
|
|
10
11
|
class FileTypeConverter : DynamicAwareTypeConverters<File>() {
|
|
11
12
|
override fun convertFromDynamic(value: Dynamic, context: AppContext?, forceConversion: Boolean): File {
|
|
12
13
|
val path = value.asString()
|
|
14
|
+
?: throw Exceptions.IllegalArgument("Cannot convert ${value.type} to File")
|
|
13
15
|
return File(path)
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -28,7 +28,9 @@ class ViewManagerWrapperDelegate(internal var moduleHolder: ModuleHolder<*>, int
|
|
|
28
28
|
fun onViewDidUpdateProps(view: View) {
|
|
29
29
|
definition.onViewDidUpdateProps?.let {
|
|
30
30
|
try {
|
|
31
|
-
exceptionDecorator(
|
|
31
|
+
exceptionDecorator(
|
|
32
|
+
{ exception -> OnViewDidUpdatePropsException(view.javaClass.kotlin, exception) }
|
|
33
|
+
) {
|
|
32
34
|
it.invoke(view)
|
|
33
35
|
}
|
|
34
36
|
} catch (exception: Throwable) {
|
|
@@ -103,17 +105,15 @@ class ViewManagerWrapperDelegate(internal var moduleHolder: ModuleHolder<*>, int
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
}
|
|
108
|
+
fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? = buildMap {
|
|
109
|
+
definition
|
|
110
|
+
.callbacksDefinition
|
|
111
|
+
?.names
|
|
112
|
+
?.forEach {
|
|
113
|
+
put(
|
|
114
|
+
normalizeEventName(it),
|
|
115
|
+
mapOf("registrationName" to it)
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -19,9 +19,7 @@ class ViewTypeConverter<T : View>(
|
|
|
19
19
|
|
|
20
20
|
val viewTag = value as Int
|
|
21
21
|
val view = appContext.findView<T>(viewTag)
|
|
22
|
-
|
|
23
|
-
throw Exceptions.ViewNotFound(type.classifier as KClass<*>, viewTag)
|
|
24
|
-
}
|
|
22
|
+
?: throw Exceptions.ViewNotFound(type.classifier as KClass<*>, viewTag)
|
|
25
23
|
|
|
26
24
|
return view
|
|
27
25
|
}
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
extension UIColor: Convertible {
|
|
4
4
|
public static func convert(from value: Any?, appContext: AppContext) throws -> Self {
|
|
5
|
+
return try convert(from: value)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
public static func convert(from value: Any?) throws -> Self {
|
|
5
9
|
// swiftlint:disable force_cast
|
|
6
10
|
if let value = value as? String {
|
|
7
11
|
if let namedColorComponents = namedColors[value] {
|
|
@@ -30,10 +34,10 @@ extension UIColor: Convertible {
|
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
if let appearances = opaqueValue["dynamic"] as? [String: Any],
|
|
33
|
-
let lightColor = try appearances["light"].map({ try UIColor.convert(from: $0
|
|
34
|
-
let darkColor = try appearances["dark"].map({ try UIColor.convert(from: $0
|
|
35
|
-
let highContrastLightColor = try appearances["highContrastLight"].map({ try UIColor.convert(from: $0
|
|
36
|
-
let highContrastDarkColor = try appearances["highContrastDark"].map({ try UIColor.convert(from: $0
|
|
37
|
+
let lightColor = try appearances["light"].map({ try UIColor.convert(from: $0) }),
|
|
38
|
+
let darkColor = try appearances["dark"].map({ try UIColor.convert(from: $0) }) {
|
|
39
|
+
let highContrastLightColor = try appearances["highContrastLight"].map({ try UIColor.convert(from: $0) })
|
|
40
|
+
let highContrastDarkColor = try appearances["highContrastDark"].map({ try UIColor.convert(from: $0) })
|
|
37
41
|
|
|
38
42
|
#if os(iOS) || os(tvOS)
|
|
39
43
|
let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import SwiftUI
|
|
4
4
|
|
|
5
5
|
extension Color: Convertible {
|
|
6
|
-
public static func convert(from value: Any
|
|
6
|
+
public static func convert(from value: Any?) throws -> Color {
|
|
7
7
|
// Simply reuse the logic from UIColor
|
|
8
|
-
if let uiColor = try? UIColor.convert(from: value
|
|
8
|
+
if let uiColor = try? UIColor.convert(from: value) {
|
|
9
9
|
return Color(uiColor)
|
|
10
10
|
}
|
|
11
11
|
// Context-dependent colors
|
|
@@ -14,6 +14,9 @@ extension Color: Convertible {
|
|
|
14
14
|
}
|
|
15
15
|
throw Conversions.ConvertingException<Color>(value)
|
|
16
16
|
}
|
|
17
|
+
public static func convert(from value: Any?, appContext: AppContext) throws -> Color {
|
|
18
|
+
return try convert(from: value)
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
private static func colorFromName(_ name: String) -> Color? {
|
|
19
22
|
switch name {
|
|
@@ -59,7 +59,7 @@ public struct FileSystemUtilities {
|
|
|
59
59
|
return getExternalPathPermissions(path, appContext)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
public static func getInternalPathPermissions(_ appContext: AppContext?, for url: URL) -> [FileSystemPermissionFlags] {
|
|
63
63
|
guard let appContext else {
|
|
64
64
|
return [.none]
|
|
65
65
|
}
|
|
@@ -88,17 +88,9 @@ public struct FileSystemUtilities {
|
|
|
88
88
|
if appContext?.config.scoped ?? false && url.path.contains("ExponentExperienceData") {
|
|
89
89
|
return []
|
|
90
90
|
}
|
|
91
|
-
var filePermissions: [FileSystemPermissionFlags] = []
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if FileManager.default.isWritableFile(atPath: url.path) {
|
|
98
|
-
filePermissions.append(.write)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return filePermissions
|
|
92
|
+
// Defer permission checks for external paths to the underlying system at the time of file operations
|
|
93
|
+
return [.read, .write]
|
|
102
94
|
}
|
|
103
95
|
|
|
104
96
|
private static func getAppGroupSharedDirectories(_ appContext: AppContext) -> [String] {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.15",
|
|
4
4
|
"description": "The core of Expo Modules architecture",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@testing-library/react-native": "^13.2.0",
|
|
66
|
-
"expo-module-scripts": "^5.0.
|
|
66
|
+
"expo-module-scripts": "^5.0.7"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "87186d10c8239c6469e055417e67bd4d0ed63efb"
|
|
69
69
|
}
|