expo-modules-core 1.12.4 → 1.12.6

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,12 +10,23 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 1.12.6 — 2024-05-01
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Android] Fix `field operation on NULL object` when reloading the app. ([#28555](https://github.com/expo/expo/pull/28555) by [@lukmccall](https://github.com/lukmccall))
18
+
19
+ ## 1.12.5 — 2024-05-01
20
+
21
+ _This version does not introduce any user-facing changes._
22
+
13
23
  ## 1.12.4 — 2024-04-29
14
24
 
15
25
  ### 🐛 Bug fixes
16
26
 
17
27
  - [Android] Fixed gziped payload does not correctly shown in network inspector. ([#28486](https://github.com/expo/expo/pull/28486) by [@kudo](https://github.com/kudo))
18
28
  - [Android] Fixed crashes during the deallocation of shared objects. ([#28491](https://github.com/expo/expo/pull/28491) by [@lukmccall](https://github.com/lukmccall))
29
+ - [iOS] Fix accessing the view registry to avoid infinite loop crash. ([#28531](https://github.com/expo/expo/pull/28531) by [@tsapeta](https://github.com/tsapeta))
19
30
 
20
31
  ## 1.12.3 — 2024-04-26
21
32
 
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'host.exp.exponent'
4
- version = '1.12.4'
4
+ version = '1.12.6'
5
5
 
6
6
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
7
  apply from: expoModulesCorePlugin
@@ -63,7 +63,7 @@ android {
63
63
  defaultConfig {
64
64
  consumerProguardFiles 'proguard-rules.pro'
65
65
  versionCode 1
66
- versionName "1.12.4"
66
+ versionName "1.12.6"
67
67
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
68
68
 
69
69
  testInstrumentationRunner "expo.modules.TestRunner"
@@ -41,6 +41,13 @@ jsi::Value ExpoModulesHostObject::get(jsi::Runtime &runtime, const jsi::PropName
41
41
  // Create a lazy object for the specific module. It defers initialization of the final module object.
42
42
  LazyObject::Shared moduleLazyObject = std::make_shared<LazyObject>(
43
43
  [this, cName](jsi::Runtime &rt) {
44
+ // Check if the installer has been deallocated.
45
+ // If so, return nullptr to avoid a "field operation on NULL object" crash.
46
+ // As it's probably the best we can do in this case.
47
+ if (installer->wasDeallocated()) {
48
+ return std::shared_ptr<jsi::Object>(nullptr);
49
+ }
50
+
44
51
  auto module = installer->getModule(cName);
45
52
  return module->cthis()->getJSIObject(rt);
46
53
  });
@@ -359,6 +359,7 @@ void JSIContext::prepareForDeallocation() {
359
359
  runtimeHolder.reset();
360
360
  jniDeallocator.reset();
361
361
  javaPart_.reset();
362
+ wasDeallocated_ = true;
362
363
  }
363
364
 
364
365
  void JSIContext::jniSetNativeStateForSharedObject(
@@ -386,6 +387,10 @@ void JSIContext::jniSetNativeStateForSharedObject(
386
387
  ->setNativeState(runtimeHolder->get(), std::move(nativeState));
387
388
  }
388
389
 
390
+ bool JSIContext::wasDeallocated() const {
391
+ return wasDeallocated_;
392
+ }
393
+
389
394
  thread_local std::unordered_map<uintptr_t, JSIContext *> jsiContexts;
390
395
 
391
396
  void bindJSIContext(const jsi::Runtime &runtime, JSIContext *jsiContext) {
@@ -145,10 +145,14 @@ public:
145
145
 
146
146
  void prepareForDeallocation();
147
147
 
148
+ bool wasDeallocated() const;
149
+
148
150
  private:
149
151
  friend HybridBase;
150
152
  jni::global_ref<JSIContext::javaobject> javaPart_;
151
153
 
154
+ bool wasDeallocated_ = false;
155
+
152
156
  explicit JSIContext(jni::alias_ref<jhybridobject> jThis);
153
157
 
154
158
  inline jni::local_ref<JavaScriptModuleObject::javaobject>
@@ -27,7 +27,6 @@ object EmulatorUtilities {
27
27
  Build.BOOTLOADER.lowercase(Locale.ROOT).contains("nox") ||
28
28
  Build.HARDWARE.lowercase(Locale.ROOT).contains("nox") ||
29
29
  Build.PRODUCT.lowercase(Locale.ROOT).contains("nox") ||
30
- Build.SERIAL.lowercase(Locale.ROOT).contains("nox") ||
31
30
  (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
32
31
  }
33
32
  }
@@ -0,0 +1,34 @@
1
+ package expo.modules.kotlin
2
+
3
+ import android.content.Intent
4
+ import android.os.Build
5
+ import android.os.Bundle
6
+ import android.os.Parcelable
7
+ import java.io.Serializable
8
+
9
+ inline fun <reified T : Parcelable> Intent.safeGetParcelableExtra(name: String): T? {
10
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
11
+ getParcelableExtra(name, T::class.java)
12
+ } else {
13
+ @Suppress("DEPRECATION")
14
+ getParcelableExtra(name)
15
+ }
16
+ }
17
+
18
+ inline fun <reified T : Parcelable> Bundle.safeGetParcelable(name: String): T? {
19
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
20
+ getParcelable(name, T::class.java)
21
+ } else {
22
+ @Suppress("DEPRECATION")
23
+ getParcelable(name)
24
+ }
25
+ }
26
+
27
+ inline fun <reified T : Serializable> Bundle.safeGetSerializable(name: String): T? {
28
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
29
+ getSerializable(name, T::class.java)
30
+ } else {
31
+ @Suppress("DEPRECATION")
32
+ getSerializable(name) as T?
33
+ }
34
+ }
@@ -363,7 +363,7 @@ class AppContext(
363
363
  }
364
364
 
365
365
  internal fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
366
- activityResultsManager.onActivityResult(activity, requestCode, resultCode, data)
366
+ activityResultsManager.onActivityResult(requestCode, resultCode, data)
367
367
  registry.post(
368
368
  EventName.ON_ACTIVITY_RESULT,
369
369
  activity,
@@ -79,6 +79,7 @@ fun Promise.toBridgePromise(): com.facebook.react.bridge.Promise {
79
79
  expoPromise.reject(code ?: unknownCode, message, throwable)
80
80
  }
81
81
 
82
+ @Deprecated("Use reject(code, message, throwable) instead")
82
83
  override fun reject(message: String?) {
83
84
  expoPromise.reject(unknownCode, message, null)
84
85
  }
@@ -48,7 +48,7 @@ class ActivityResultsManager(
48
48
 
49
49
  // region Lifecycle
50
50
 
51
- fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
51
+ fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
52
52
  registry.dispatchResult(requestCode, resultCode, data)
53
53
  }
54
54
 
@@ -25,10 +25,10 @@ import androidx.lifecycle.LifecycleOwner
25
25
  import expo.modules.core.utilities.ifNull
26
26
  import expo.modules.kotlin.AppContext
27
27
  import expo.modules.kotlin.providers.CurrentActivityProvider
28
+ import expo.modules.kotlin.safeGetParcelable
29
+ import expo.modules.kotlin.safeGetParcelableExtra
28
30
  import java.io.Serializable
29
- import java.util.*
30
- import kotlin.collections.ArrayList
31
- import kotlin.collections.HashMap
31
+ import java.util.Random
32
32
 
33
33
  private const val TAG = "ActivityResultRegistry"
34
34
 
@@ -116,8 +116,9 @@ class AppContextActivityResultRegistry(
116
116
  ?: arrayOfNulls(0)
117
117
  ActivityCompat.requestPermissions(activity, permissions, requestCode)
118
118
  }
119
+
119
120
  StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST -> {
120
- val request: IntentSenderRequest = intent.getParcelableExtra(StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST)!!
121
+ val request = intent.safeGetParcelableExtra<IntentSenderRequest>(StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST)!!
121
122
  try {
122
123
  // startIntentSenderForResult path
123
124
  ActivityCompat.startIntentSenderForResult(
@@ -141,6 +142,7 @@ class AppContextActivityResultRegistry(
141
142
  }
142
143
  }
143
144
  }
145
+
144
146
  else -> {
145
147
  // startActivityForResult path
146
148
  ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle)
@@ -181,10 +183,14 @@ class AppContextActivityResultRegistry(
181
183
 
182
184
  // 1. No callbacks registered yet, other path would take care of the results
183
185
  @Suppress("UNCHECKED_CAST")
184
- val callbacksAndContract: CallbacksAndContract<I, O> = (keyToCallbacksAndContract[key] ?: return@LifecycleEventObserver) as CallbacksAndContract<I, O>
186
+ val callbacksAndContract: CallbacksAndContract<I, O> = (
187
+ keyToCallbacksAndContract[key]
188
+ ?: return@LifecycleEventObserver
189
+ ) as CallbacksAndContract<I, O>
185
190
 
186
191
  // 2. There are results to be delivered to the callbacks
187
- pendingResults.getParcelable<ActivityResult>(key)?.let {
192
+ val activityResult = pendingResults.safeGetParcelable<ActivityResult>(key)
193
+ activityResult?.let {
188
194
  pendingResults.remove(key)
189
195
 
190
196
  @Suppress("UNCHECKED_CAST")
@@ -200,9 +206,11 @@ class AppContextActivityResultRegistry(
200
206
  }
201
207
  }
202
208
  }
209
+
203
210
  Lifecycle.Event.ON_DESTROY -> {
204
211
  unregister(key)
205
212
  }
213
+
206
214
  else -> Unit
207
215
  }
208
216
  }
@@ -274,7 +282,7 @@ class AppContextActivityResultRegistry(
274
282
  }
275
283
  keyToCallbacksAndContract.remove(key)
276
284
  if (pendingResults.containsKey(key)) {
277
- Log.w(TAG, "Dropping pending result for request $key : ${pendingResults.getParcelable<ActivityResult>(key)}")
285
+ Log.w(TAG, "Dropping pending result for request $key : ${pendingResults.safeGetParcelable<ActivityResult>(key)}")
278
286
  pendingResults.remove(key)
279
287
  }
280
288
  keyToLifecycleContainers[key]?.let {
@@ -7,6 +7,7 @@ import android.os.Parcel
7
7
  import android.text.format.DateUtils
8
8
  import android.util.Base64
9
9
  import androidx.core.os.bundleOf
10
+ import expo.modules.kotlin.safeGetSerializable
10
11
  import java.io.Serializable
11
12
  import java.util.*
12
13
  import kotlin.collections.ArrayList
@@ -64,7 +65,7 @@ class DataPersistor(context: Context) {
64
65
  bundle
65
66
  .keySet()
66
67
  .associateWith { key ->
67
- bundle.getSerializable(key) ?: throw IllegalStateException("For a key '$key' there should be a serializable class available")
68
+ bundle.safeGetSerializable<Serializable>(key) ?: throw IllegalStateException("For a key '$key' there should be a serializable class available")
68
69
  }
69
70
  }
70
71
  }
@@ -82,7 +83,7 @@ class DataPersistor(context: Context) {
82
83
  }
83
84
 
84
85
  fun retrieveSerializable(key: String): Serializable? {
85
- return retrievedData.getSerializable(key)
86
+ return retrievedData.safeGetSerializable<Serializable>(key)
86
87
  }
87
88
 
88
89
  fun persist() {
@@ -61,15 +61,15 @@ object ExpoRequestCdpInterceptor : ExpoNetworkInspectOkHttpInterceptorsDelegate
61
61
  ) {
62
62
  val now = BigDecimal(System.currentTimeMillis() / 1000.0).setScale(3, RoundingMode.CEILING)
63
63
 
64
- val params = ResponseReceivedParams(now, requestId, request, response)
64
+ val params = ResponseReceivedParams(now, requestId, response)
65
65
  dispatchEvent(Event("Network.responseReceived", params))
66
66
 
67
67
  if (body != null) {
68
- val params2 = ExpoReceivedResponseBodyParams(now, requestId, request, response, body)
68
+ val params2 = ExpoReceivedResponseBodyParams(requestId, body)
69
69
  dispatchEvent(Event("Expo(Network.receivedResponseBody)", params2))
70
70
  }
71
71
 
72
- val params3 = LoadingFinishedParams(now, requestId, request, response)
72
+ val params3 = LoadingFinishedParams(now, requestId, response)
73
73
  dispatchEvent(Event("Network.loadingFinished", params3))
74
74
  }
75
75
 
@@ -183,7 +183,7 @@ data class ResponseReceivedParams(
183
183
  val response: Response,
184
184
  val hasExtraInfo: Boolean = false
185
185
  ) : JsonSerializable {
186
- constructor(now: BigDecimal, requestId: RequestId, request: okhttp3.Request, okhttpResponse: okhttp3.Response) : this(
186
+ constructor(now: BigDecimal, requestId: RequestId, okhttpResponse: okhttp3.Response) : this(
187
187
  requestId = requestId,
188
188
  timestamp = now,
189
189
  type = ResourceType.fromMimeType(okhttpResponse.header("Content-Type", "") ?: ""),
@@ -207,7 +207,7 @@ data class LoadingFinishedParams(
207
207
  val timestamp: MonotonicTime,
208
208
  val encodedDataLength: Long
209
209
  ) : JsonSerializable {
210
- constructor(now: BigDecimal, requestId: RequestId, request: okhttp3.Request, response: okhttp3.Response) : this(
210
+ constructor(now: BigDecimal, requestId: RequestId, response: okhttp3.Response) : this(
211
211
  requestId = requestId,
212
212
  timestamp = now,
213
213
  encodedDataLength = response.body?.contentLength() ?: 0
@@ -228,10 +228,7 @@ data class ExpoReceivedResponseBodyParams(
228
228
  var base64Encoded: Boolean
229
229
  ) : JsonSerializable {
230
230
  constructor(
231
- now: BigDecimal,
232
231
  requestId: RequestId,
233
- request: okhttp3.Request,
234
- response: okhttp3.Response,
235
232
  body: okhttp3.ResponseBody
236
233
  ) : this(
237
234
  requestId = requestId,
@@ -65,4 +65,9 @@ class Exceptions {
65
65
  ) : CodedException(
66
66
  message = "Expected to run on $expectedThreadName thread, but was run on $currentThreadName"
67
67
  )
68
+
69
+ /**
70
+ * An exception to throw when the root view is missing.
71
+ */
72
+ class MissingRootView : CodedException(message = "The root view is missing")
68
73
  }
@@ -88,8 +88,8 @@ open class JavaScriptObject @DoNotStrip internal constructor(@DoNotStrip private
88
88
 
89
89
  // Needed to handle untyped null value
90
90
  // Without it setProperty(name, null) won't work
91
- fun setProperty(name: String, `null`: Nothing?) = unsetProperty(name)
92
- operator fun set(name: String, `null`: Nothing?) = unsetProperty(name)
91
+ fun setProperty(name: String, @Suppress("UNUSED_PARAMETER") `null`: Nothing?) = unsetProperty(name)
92
+ operator fun set(name: String, @Suppress("UNUSED_PARAMETER") `null`: Nothing?) = unsetProperty(name)
93
93
 
94
94
  fun defineProperty(
95
95
  name: String,
@@ -130,7 +130,7 @@ open class JavaScriptObject @DoNotStrip internal constructor(@DoNotStrip private
130
130
  // Needed to handle untyped null value
131
131
  fun defineProperty(
132
132
  name: String,
133
- `null`: Nothing?,
133
+ @Suppress("UNUSED_PARAMETER") `null`: Nothing?,
134
134
  options: List<PropertyDescriptor> = emptyList()
135
135
  ) = defineJSObjectProperty(name, null, options.toCppOptions())
136
136
 
@@ -10,7 +10,7 @@ import kotlin.reflect.KType
10
10
 
11
11
  sealed class DeferredValue
12
12
 
13
- object IncompatibleValue : DeferredValue()
13
+ data object IncompatibleValue : DeferredValue()
14
14
 
15
15
  class UnconvertedValue(
16
16
  private val unconvertedValue: Any,
@@ -73,16 +73,16 @@ open class Either<FirstType : Any, SecondType : Any>(
73
73
  }
74
74
 
75
75
  @JvmName("isFirstType")
76
- fun `is`(type: KClass<FirstType>): Boolean = `is`(0)
76
+ fun `is`(@Suppress("UNUSED_PARAMETER") type: KClass<FirstType>): Boolean = `is`(0)
77
77
 
78
78
  @JvmName("isSecondType")
79
- fun `is`(type: KClass<SecondType>): Boolean = `is`(1)
79
+ fun `is`(@Suppress("UNUSED_PARAMETER") type: KClass<SecondType>): Boolean = `is`(1)
80
80
 
81
81
  @JvmName("getFirstType")
82
- fun get(type: KClass<FirstType>): FirstType = get(0) as FirstType
82
+ fun get(@Suppress("UNUSED_PARAMETER") type: KClass<FirstType>): FirstType = get(0) as FirstType
83
83
 
84
84
  @JvmName("getSecondType")
85
- fun get(type: KClass<SecondType>): SecondType = get(1) as SecondType
85
+ fun get(@Suppress("UNUSED_PARAMETER") type: KClass<SecondType>): SecondType = get(1) as SecondType
86
86
 
87
87
  fun first(): FirstType = get(0) as FirstType
88
88
 
@@ -96,10 +96,10 @@ open class EitherOfThree<FirstType : Any, SecondType : Any, ThirdType : Any>(
96
96
  types: List<KType>
97
97
  ) : Either<FirstType, SecondType>(bareValue, deferredValue, types) {
98
98
  @JvmName("isThirdType")
99
- fun `is`(type: KClass<ThirdType>): Boolean = `is`(2)
99
+ fun `is`(@Suppress("UNUSED_PARAMETER") type: KClass<ThirdType>): Boolean = `is`(2)
100
100
 
101
101
  @JvmName("getThirdType")
102
- fun get(type: KClass<ThirdType>) = get(3) as ThirdType
102
+ fun get(@Suppress("UNUSED_PARAMETER") type: KClass<ThirdType>) = get(3) as ThirdType
103
103
 
104
104
  fun third(): ThirdType = get(3) as ThirdType
105
105
  }
@@ -111,10 +111,10 @@ class EitherOfFour<FirstType : Any, SecondType : Any, ThirdType : Any, FourthTyp
111
111
  types: List<KType>
112
112
  ) : EitherOfThree<FirstType, SecondType, ThirdType>(bareValue, deferredValue, types) {
113
113
  @JvmName("isFourthType")
114
- fun `is`(type: KClass<FourthType>): Boolean = `is`(3)
114
+ fun `is`(@Suppress("UNUSED_PARAMETER") type: KClass<FourthType>): Boolean = `is`(3)
115
115
 
116
116
  @JvmName("getFourthType")
117
- fun get(type: KClass<FourthType>): FourthType = get(3) as FourthType
117
+ fun get(@Suppress("UNUSED_PARAMETER") type: KClass<FourthType>): FourthType = get(3) as FourthType
118
118
 
119
119
  fun fourth(): FourthType = get(3) as FourthType
120
120
  }
@@ -296,7 +296,7 @@ EX_REGISTER_MODULE();
296
296
  UIView<RCTComponentViewProtocol> *componentView = [uiManager viewForReactTag:(NSNumber *)viewId];
297
297
  UIView *view = [(ExpoFabricViewObjC *)componentView contentView];
298
298
  #else
299
- UIView *view = viewRegistry[viewId];
299
+ UIView *view = [uiManager viewForReactTag:(NSNumber *)viewId];
300
300
  #endif
301
301
  block(view);
302
302
  }];
@@ -313,7 +313,7 @@ EX_REGISTER_MODULE();
313
313
  UIView<RCTComponentViewProtocol> *componentView = [uiManager viewForReactTag:(NSNumber *)viewId];
314
314
  UIView *view = [(ExpoFabricViewObjC *)componentView contentView];
315
315
  #else
316
- UIView *view = viewRegistry[viewId];
316
+ UIView *view = [uiManager viewForReactTag:(NSNumber *)viewId];
317
317
  #endif
318
318
  block(view);
319
319
  }];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "1.12.4",
3
+ "version": "1.12.6",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -44,5 +44,5 @@
44
44
  "@testing-library/react-hooks": "^7.0.1",
45
45
  "expo-module-scripts": "^3.0.0"
46
46
  },
47
- "gitHead": "4a7cf0d0baf6dfc595d93f604945d2142e705a36"
47
+ "gitHead": "87bd0dfa8b784a834191195ca174a00dd30ee5b7"
48
48
  }