expo-modules-core 3.0.12 → 3.0.14

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 CHANGED
@@ -10,6 +10,22 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 3.0.14 — 2025-09-08
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Android] Fix type check in the `SharedRef` converter. ([#39446](https://github.com/expo/expo/pull/39446) by [@lukmccall](https://github.com/lukmccall))
18
+
19
+ ### 💡 Others
20
+
21
+ - improve startup performance by not relying on kotlin reflection. ([#39389](https://github.com/expo/expo/pull/39389) by [@ACHP](https://github.com/ACHP))
22
+
23
+ ## 3.0.13 — 2025-09-04
24
+
25
+ ### 💡 Others
26
+
27
+ - [Android] Remove some usage of `kotlin.reflect.full.*`. ([#39385](https://github.com/expo/expo/pull/39385) by [@lukmccall](https://github.com/lukmccall))
28
+
13
29
  ## 3.0.12 — 2025-09-03
14
30
 
15
31
  ### 💡 Others
@@ -25,7 +25,7 @@ if (shouldIncludeCompose) {
25
25
  }
26
26
 
27
27
  group = 'host.exp.exponent'
28
- version = '3.0.12'
28
+ version = '3.0.14'
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.12"
78
+ versionName "3.0.14"
79
79
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
80
80
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
81
81
 
@@ -1,13 +1,12 @@
1
- package expo.modules.kotlin.modules
1
+ package expo.modules.kotlin
2
2
 
3
3
  import expo.modules.kotlin.types.Enumerable
4
4
  import kotlin.reflect.KProperty1
5
5
  import kotlin.reflect.full.declaredMemberProperties
6
- import kotlin.reflect.full.primaryConstructor
7
6
 
8
- internal fun <T> convertEnumToString(enumValue: T): String where T : Enumerable, T : Enum<T> {
9
- val enumClass = enumValue::class
10
- val primaryConstructor = enumClass.primaryConstructor
7
+ fun <T> T.convertToString(): String where T : Enum<T>, T : Enumerable {
8
+ val enumClass = this::class
9
+ val primaryConstructor = enumClass.fastPrimaryConstructor
11
10
  if (primaryConstructor?.parameters?.size == 1) {
12
11
  val parameterName = primaryConstructor.parameters.first().name
13
12
  val parameterProperty = enumClass
@@ -18,8 +17,8 @@ internal fun <T> convertEnumToString(enumValue: T): String where T : Enumerable,
18
17
  require(parameterProperty.returnType.classifier == String::class) { "The enum parameter has to be a string." }
19
18
 
20
19
  @Suppress("UNCHECKED_CAST")
21
- return (parameterProperty as KProperty1<T, String>).get(enumValue)
20
+ return (parameterProperty as KProperty1<T, String>).get(this)
22
21
  }
23
22
 
24
- return enumValue.name
23
+ return this.name
25
24
  }
@@ -0,0 +1,13 @@
1
+ package expo.modules.kotlin
2
+
3
+ import kotlin.reflect.KClass
4
+ import kotlin.reflect.KFunction
5
+ import kotlin.reflect.full.primaryConstructor
6
+
7
+ val <T : Any> KClass<T>.fastPrimaryConstructor: KFunction<T>?
8
+ // If class only has one constructor, use it as a primary constructor.
9
+ // Otherwise, try to find the primary constructor using kotlin reflection.
10
+ get() = constructors.singleOrNull() ?: primaryConstructor
11
+
12
+ fun KClass<*>.fastIsSupperClassOf(jClass: Class<*>) =
13
+ this.javaObjectType.isAssignableFrom(jClass) || this.java.isAssignableFrom(jClass)
@@ -3,16 +3,15 @@ package expo.modules.kotlin
3
3
  import com.facebook.react.bridge.ReadableArray
4
4
  import com.facebook.react.bridge.ReadableMap
5
5
  import com.facebook.react.bridge.ReadableType
6
- import kotlin.reflect.KType
7
- import kotlin.reflect.full.createType
6
+ import kotlin.reflect.KClass
8
7
 
9
- fun ReadableType.toKType(): KType {
8
+ fun ReadableType.toKClass(): KClass<*> {
10
9
  return when (this) {
11
- ReadableType.Null -> Any::class.createType(nullable = true)
12
- ReadableType.Boolean -> Boolean::class.createType()
13
- ReadableType.Number -> Number::class.createType()
14
- ReadableType.String -> String::class.createType()
15
- ReadableType.Map -> ReadableMap::class.createType()
16
- ReadableType.Array -> ReadableArray::class.createType()
10
+ ReadableType.Null -> Any::class
11
+ ReadableType.Boolean -> Boolean::class
12
+ ReadableType.Number -> Number::class
13
+ ReadableType.String -> String::class
14
+ ReadableType.Map -> ReadableMap::class
15
+ ReadableType.Array -> ReadableArray::class
17
16
  }
18
17
  }
@@ -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.SharedRef
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.isSubclassOf(SharedObject::class)
37
- val isSharedRef = hasOwnerType && ownerClass.isSubclassOf(SharedRef::class)
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)
@@ -71,8 +71,8 @@ inline fun <reified T : CodedException> errorCodeOf(): String =
71
71
  CodedException.inferCode(T::class.java)
72
72
 
73
73
  internal class IncompatibleArgTypeException(
74
- argumentType: KType,
75
- desiredType: KType,
74
+ argumentType: KClass<*>,
75
+ desiredType: KClass<*>,
76
76
  cause: Throwable? = null
77
77
  ) : CodedException(
78
78
  message = "Argument type '$argumentType' is not compatible with expected type '$desiredType'.",
@@ -3,6 +3,7 @@ package expo.modules.kotlin.modules
3
3
  import android.os.Bundle
4
4
  import expo.modules.kotlin.AppContext
5
5
  import expo.modules.kotlin.RuntimeContext
6
+ import expo.modules.kotlin.convertToString
6
7
  import expo.modules.kotlin.providers.AppContextProvider
7
8
  import expo.modules.kotlin.tracing.trace
8
9
  import expo.modules.kotlin.types.Enumerable
@@ -44,11 +45,11 @@ abstract class Module : AppContextProvider {
44
45
  }
45
46
 
46
47
  fun <T> sendEvent(enum: T, body: Bundle? = Bundle.EMPTY) where T : Enumerable, T : Enum<T> {
47
- moduleEventEmitter?.emit(convertEnumToString(enum), body)
48
+ moduleEventEmitter?.emit(enum.convertToString(), body)
48
49
  }
49
50
 
50
51
  fun <T> sendEvent(enum: T, body: Map<String, Any?>? = null) where T : Enumerable, T : Enum<T> {
51
- moduleEventEmitter?.emit(convertEnumToString(enum), body)
52
+ moduleEventEmitter?.emit(enum.convertToString(), body)
52
53
  }
53
54
 
54
55
  open fun converters(): TypeConverterProvider? = null
@@ -7,7 +7,9 @@ import expo.modules.kotlin.Promise
7
7
  import expo.modules.kotlin.component6
8
8
  import expo.modules.kotlin.component7
9
9
  import expo.modules.kotlin.component8
10
+ import expo.modules.kotlin.convertToString
10
11
  import expo.modules.kotlin.events.EventsDefinition
12
+ import expo.modules.kotlin.fastPrimaryConstructor
11
13
  import expo.modules.kotlin.functions.AsyncFunctionComponent
12
14
  import expo.modules.kotlin.functions.AsyncFunctionBuilder
13
15
  import expo.modules.kotlin.functions.AsyncFunctionWithPromiseComponent
@@ -18,14 +20,12 @@ import expo.modules.kotlin.jni.JavaScriptModuleObject
18
20
  import expo.modules.kotlin.jni.decorators.JSDecoratorsBridgingObject
19
21
  import expo.modules.kotlin.modules.Module
20
22
  import expo.modules.kotlin.modules.ModuleDefinitionBuilder
21
- import expo.modules.kotlin.modules.convertEnumToString
22
23
  import expo.modules.kotlin.types.Enumerable
23
24
  import expo.modules.kotlin.types.TypeConverterProvider
24
25
  import expo.modules.kotlin.types.enforceType
25
26
  import expo.modules.kotlin.types.toArgsArray
26
27
  import expo.modules.kotlin.types.toReturnType
27
28
  import kotlin.reflect.full.declaredMemberProperties
28
- import kotlin.reflect.full.primaryConstructor
29
29
 
30
30
  /**
31
31
  * Base class for other definitions representing an object, such as `ModuleDefinition`.
@@ -449,7 +449,7 @@ open class ObjectDefinitionBuilder(
449
449
  }
450
450
 
451
451
  inline fun <reified T> Events() where T : Enumerable, T : Enum<T> {
452
- val primaryConstructor = T::class.primaryConstructor
452
+ val primaryConstructor = T::class.fastPrimaryConstructor
453
453
  val events = if (primaryConstructor?.parameters?.size == 1) {
454
454
  val parameterName = primaryConstructor.parameters.first().name
455
455
 
@@ -487,7 +487,7 @@ open class ObjectDefinitionBuilder(
487
487
  * Creates module's lifecycle listener that is called right after the first event listener is added for given event.
488
488
  */
489
489
  fun <T> OnStartObserving(enum: T, body: () -> Unit) where T : Enumerable, T : Enum<T> {
490
- OnStartObserving(convertEnumToString(enum), body)
490
+ OnStartObserving(enum.convertToString(), body)
491
491
  }
492
492
 
493
493
  /**
@@ -520,7 +520,7 @@ open class ObjectDefinitionBuilder(
520
520
  * Creates module's lifecycle listener that is called right after all event listeners are removed for given event.
521
521
  */
522
522
  fun <T> OnStopObserving(enum: T, body: () -> Unit) where T : Enumerable, T : Enum<T> {
523
- OnStopObserving(convertEnumToString(enum), body)
523
+ OnStopObserving(enum.convertToString(), body)
524
524
  }
525
525
 
526
526
  /**
@@ -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)
@@ -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
@@ -10,7 +11,6 @@ import expo.modules.kotlin.types.NonNullableTypeConverter
10
11
  import kotlin.reflect.KClass
11
12
  import kotlin.reflect.KType
12
13
  import kotlin.reflect.KTypeProjection
13
- import kotlin.reflect.full.isSuperclassOf
14
14
 
15
15
  class SharedObjectTypeConverter<T : SharedObject>(
16
16
  val type: KType
@@ -74,7 +74,8 @@ class SharedRefTypeConverter<T : SharedRef<*>>(
74
74
  val ref = sharedRef.ref ?: return sharedRef
75
75
  val sharedRefClass = sharedRefType?.classifier as? KClass<*>
76
76
  ?: return sharedRef
77
- if (sharedRefClass.isSuperclassOf(ref.javaClass.kotlin)) {
77
+
78
+ if (sharedRefClass.fastIsSupperClassOf(ref.javaClass)) {
78
79
  return sharedRef
79
80
  }
80
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)
@@ -5,12 +5,11 @@ import expo.modules.kotlin.AppContext
5
5
  import expo.modules.kotlin.exception.DynamicCastException
6
6
  import expo.modules.kotlin.exception.EnumNoSuchValueException
7
7
  import expo.modules.kotlin.exception.IncompatibleArgTypeException
8
+ import expo.modules.kotlin.fastPrimaryConstructor
8
9
  import expo.modules.kotlin.jni.ExpectedType
9
10
  import expo.modules.kotlin.logger
10
- import expo.modules.kotlin.toKType
11
+ import expo.modules.kotlin.toKClass
11
12
  import kotlin.reflect.KClass
12
- import kotlin.reflect.full.createType
13
- import kotlin.reflect.full.primaryConstructor
14
13
 
15
14
  class EnumTypeConverter(
16
15
  private val enumClass: KClass<Enum<*>>
@@ -23,7 +22,9 @@ class EnumTypeConverter(
23
22
  }
24
23
  }
25
24
 
26
- private val primaryConstructor = requireNotNull(enumClass.primaryConstructor) {
25
+ private val primaryConstructor = requireNotNull(
26
+ enumClass.fastPrimaryConstructor
27
+ ) {
27
28
  "Cannot convert js value to enum without the primary constructor"
28
29
  }
29
30
 
@@ -48,7 +49,7 @@ class EnumTypeConverter(
48
49
  )
49
50
  }
50
51
 
51
- throw IncompatibleArgTypeException(value.type.toKType(), enumClass.createType())
52
+ throw IncompatibleArgTypeException(value.type.toKClass(), enumClass)
52
53
  }
53
54
 
54
55
  override fun convertFromAny(value: Any, context: AppContext?, forceConversion: Boolean): Enum<*> {
@@ -62,7 +63,7 @@ class EnumTypeConverter(
62
63
  )
63
64
  }
64
65
 
65
- throw IncompatibleArgTypeException(value::class.createType(), enumClass.createType())
66
+ throw IncompatibleArgTypeException(value::class, enumClass)
66
67
  }
67
68
 
68
69
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "3.0.12",
3
+ "version": "3.0.14",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -65,5 +65,5 @@
65
65
  "@testing-library/react-native": "^13.2.0",
66
66
  "expo-module-scripts": "^5.0.6"
67
67
  },
68
- "gitHead": "f97e6d1cae0cb14d5eed1b08fad6d12a7144af3a"
68
+ "gitHead": "d087a2182086089f23bcee65e27434f21db50128"
69
69
  }