expo-modules-core 1.12.3 → 1.12.5
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 +12 -0
- package/android/build.gradle +2 -2
- package/android/src/main/cpp/JSIContext.cpp +53 -8
- package/android/src/main/cpp/JSIContext.h +1 -1
- package/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt +52 -3
- package/android/src/main/java/expo/modules/kotlin/devtools/ExpoRequestCdpInterceptor.kt +14 -4
- package/android/src/main/java/expo/modules/kotlin/devtools/cdp/CdpNetworkTypes.kt +9 -4
- package/ios/Legacy/Services/EXReactNativeAdapter.mm +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.12.5 — 2024-05-01
|
|
14
|
+
|
|
15
|
+
_This version does not introduce any user-facing changes._
|
|
16
|
+
|
|
17
|
+
## 1.12.4 — 2024-04-29
|
|
18
|
+
|
|
19
|
+
### 🐛 Bug fixes
|
|
20
|
+
|
|
21
|
+
- [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))
|
|
22
|
+
- [Android] Fixed crashes during the deallocation of shared objects. ([#28491](https://github.com/expo/expo/pull/28491) by [@lukmccall](https://github.com/lukmccall))
|
|
23
|
+
- [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))
|
|
24
|
+
|
|
13
25
|
## 1.12.3 — 2024-04-26
|
|
14
26
|
|
|
15
27
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'host.exp.exponent'
|
|
4
|
-
version = '1.12.
|
|
4
|
+
version = '1.12.5'
|
|
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.
|
|
66
|
+
versionName "1.12.5"
|
|
67
67
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
|
|
68
68
|
|
|
69
69
|
testInstrumentationRunner "expo.modules.TestRunner"
|
|
@@ -29,6 +29,42 @@ namespace expo {
|
|
|
29
29
|
|
|
30
30
|
#endif
|
|
31
31
|
|
|
32
|
+
/*
|
|
33
|
+
* A wrapper for a global reference that can be deallocated on any thread.
|
|
34
|
+
* It should be used with smart pointer. That structure can't be copied or moved.
|
|
35
|
+
*/
|
|
36
|
+
template <typename T>
|
|
37
|
+
class ThreadSafeJNIGlobalRef {
|
|
38
|
+
public:
|
|
39
|
+
ThreadSafeJNIGlobalRef(jobject globalRef) : globalRef(globalRef) {}
|
|
40
|
+
ThreadSafeJNIGlobalRef(const ThreadSafeJNIGlobalRef &other) = delete;
|
|
41
|
+
ThreadSafeJNIGlobalRef(ThreadSafeJNIGlobalRef &&other) = delete;
|
|
42
|
+
ThreadSafeJNIGlobalRef &operator=(const ThreadSafeJNIGlobalRef &other) = delete;
|
|
43
|
+
ThreadSafeJNIGlobalRef &operator=(ThreadSafeJNIGlobalRef &&other) = delete;
|
|
44
|
+
|
|
45
|
+
void use(std::function<void(jni::alias_ref<T> globalRef)> &&action) {
|
|
46
|
+
if (globalRef == nullptr) {
|
|
47
|
+
throw std::runtime_error("ThreadSafeJNIGlobalRef: globalRef is null");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
jni::ThreadScope::WithClassLoader([this, action = std::move(action)]() {
|
|
51
|
+
jni::alias_ref<jobject> aliasRef = jni::wrap_alias(globalRef);
|
|
52
|
+
jni::alias_ref<T> jsiContextRef = jni::static_ref_cast<T>(aliasRef);
|
|
53
|
+
action(jsiContextRef);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
~ThreadSafeJNIGlobalRef() {
|
|
58
|
+
if (globalRef != nullptr) {
|
|
59
|
+
jni::ThreadScope::WithClassLoader([this] {
|
|
60
|
+
jni::Environment::current()->DeleteGlobalRef(this->globalRef);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
jobject globalRef;
|
|
66
|
+
};
|
|
67
|
+
|
|
32
68
|
jni::local_ref<JSIContext::jhybriddata>
|
|
33
69
|
JSIContext::initHybrid(jni::alias_ref<jhybridobject> jThis) {
|
|
34
70
|
return makeCxxInstance(jThis);
|
|
@@ -137,13 +173,17 @@ void JSIContext::prepareRuntime() {
|
|
|
137
173
|
|
|
138
174
|
EventEmitter::installClass(runtime);
|
|
139
175
|
|
|
176
|
+
auto threadSafeRef = std::make_shared<ThreadSafeJNIGlobalRef<JSIContext::javaobject>>(
|
|
177
|
+
jni::Environment::current()->NewGlobalRef(javaPart_.get())
|
|
178
|
+
);
|
|
179
|
+
|
|
140
180
|
SharedObject::installBaseClass(
|
|
141
181
|
runtime,
|
|
142
182
|
// We can't predict the order of deallocation of the JSIContext and the SharedObject.
|
|
143
183
|
// So we need to pass a new ref to retain the JSIContext to make sure it's not deallocated before the SharedObject.
|
|
144
|
-
[
|
|
145
|
-
jni::
|
|
146
|
-
JSIContext::deleteSharedObject(
|
|
184
|
+
[threadSafeRef = std::move(threadSafeRef)](const SharedObject::ObjectId objectId) {
|
|
185
|
+
threadSafeRef->use([objectId](jni::alias_ref<JSIContext::javaobject> globalRef) {
|
|
186
|
+
JSIContext::deleteSharedObject(globalRef, objectId);
|
|
147
187
|
});
|
|
148
188
|
}
|
|
149
189
|
);
|
|
@@ -169,7 +209,8 @@ void JSIContext::prepareRuntime() {
|
|
|
169
209
|
jni::local_ref<JavaScriptModuleObject::javaobject>
|
|
170
210
|
JSIContext::callGetJavaScriptModuleObjectMethod(const std::string &moduleName) const {
|
|
171
211
|
if (javaPart_ == nullptr) {
|
|
172
|
-
throw std::runtime_error(
|
|
212
|
+
throw std::runtime_error(
|
|
213
|
+
"getJavaScriptModuleObject: JSIContext was prepared to be deallocated.");
|
|
173
214
|
}
|
|
174
215
|
|
|
175
216
|
const static auto method = expo::JSIContext::javaClassLocal()
|
|
@@ -270,7 +311,7 @@ void JSIContext::registerSharedObject(
|
|
|
270
311
|
}
|
|
271
312
|
|
|
272
313
|
void JSIContext::deleteSharedObject(
|
|
273
|
-
jni::
|
|
314
|
+
jni::alias_ref<JSIContext::javaobject> javaObject,
|
|
274
315
|
int objectId
|
|
275
316
|
) {
|
|
276
317
|
if (javaObject == nullptr) {
|
|
@@ -324,13 +365,17 @@ void JSIContext::jniSetNativeStateForSharedObject(
|
|
|
324
365
|
int id,
|
|
325
366
|
jni::alias_ref<JavaScriptObject::javaobject> jsObject
|
|
326
367
|
) {
|
|
368
|
+
auto threadSafeRef = std::make_shared<ThreadSafeJNIGlobalRef<JSIContext::javaobject>>(
|
|
369
|
+
jni::Environment::current()->NewGlobalRef(javaPart_.get())
|
|
370
|
+
);
|
|
371
|
+
|
|
327
372
|
auto nativeState = std::make_shared<expo::SharedObject::NativeState>(
|
|
328
373
|
id,
|
|
329
374
|
// We can't predict the order of deallocation of the JSIContext and the SharedObject.
|
|
330
375
|
// So we need to pass a new ref to retain the JSIContext to make sure it's not deallocated before the SharedObject.
|
|
331
|
-
[
|
|
332
|
-
jni::
|
|
333
|
-
JSIContext::deleteSharedObject(
|
|
376
|
+
[threadSafeRef = std::move(threadSafeRef)](const SharedObject::ObjectId objectId) {
|
|
377
|
+
threadSafeRef->use([objectId](jni::alias_ref<JSIContext::javaobject> globalRef) {
|
|
378
|
+
JSIContext::deleteSharedObject(globalRef, objectId);
|
|
334
379
|
});
|
|
335
380
|
}
|
|
336
381
|
);
|
package/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt
CHANGED
|
@@ -6,6 +6,11 @@ import android.util.Log
|
|
|
6
6
|
import okhttp3.Interceptor
|
|
7
7
|
import okhttp3.Request
|
|
8
8
|
import okhttp3.Response
|
|
9
|
+
import okhttp3.ResponseBody
|
|
10
|
+
import okhttp3.ResponseBody.Companion.asResponseBody
|
|
11
|
+
import okio.GzipSource
|
|
12
|
+
import okio.buffer
|
|
13
|
+
import java.io.IOException
|
|
9
14
|
|
|
10
15
|
private const val TAG = "ExpoNetworkInspector"
|
|
11
16
|
|
|
@@ -31,7 +36,9 @@ class ExpoNetworkInspectOkHttpNetworkInterceptor : Interceptor {
|
|
|
31
36
|
it.priorResponse = response
|
|
32
37
|
}
|
|
33
38
|
} else {
|
|
34
|
-
|
|
39
|
+
val body = peekResponseBody(response)
|
|
40
|
+
delegate.didReceiveResponse(requestId, request, response, body)
|
|
41
|
+
body?.close()
|
|
35
42
|
}
|
|
36
43
|
} catch (e: Exception) {
|
|
37
44
|
Log.e(TAG, "Failed to send network request CDP event", e)
|
|
@@ -62,9 +69,18 @@ class ExpoNetworkInspectOkHttpAppInterceptor : Interceptor {
|
|
|
62
69
|
* The delegate to dispatch network request events
|
|
63
70
|
*/
|
|
64
71
|
internal interface ExpoNetworkInspectOkHttpInterceptorsDelegate {
|
|
65
|
-
fun willSendRequest(
|
|
72
|
+
fun willSendRequest(
|
|
73
|
+
requestId: String,
|
|
74
|
+
request: Request,
|
|
75
|
+
redirectResponse: Response?
|
|
76
|
+
)
|
|
66
77
|
|
|
67
|
-
fun didReceiveResponse(
|
|
78
|
+
fun didReceiveResponse(
|
|
79
|
+
requestId: String,
|
|
80
|
+
request: Request,
|
|
81
|
+
response: Response,
|
|
82
|
+
body: ResponseBody?
|
|
83
|
+
)
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
/**
|
|
@@ -74,3 +90,36 @@ internal class RedirectResponse {
|
|
|
74
90
|
var requestId: String? = null
|
|
75
91
|
var priorResponse: Response? = null
|
|
76
92
|
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Peek response body that could send to CDP delegate.
|
|
96
|
+
* Also uncompress gzip payload if necessary since OkHttp [Interceptor] does not uncompress payload for you.
|
|
97
|
+
* @return null if the response body exceeds [byteCount]
|
|
98
|
+
*/
|
|
99
|
+
internal fun peekResponseBody(
|
|
100
|
+
response: Response,
|
|
101
|
+
byteCount: Long = ExpoNetworkInspectOkHttpNetworkInterceptor.MAX_BODY_SIZE
|
|
102
|
+
): ResponseBody? {
|
|
103
|
+
val body = response.body ?: return null
|
|
104
|
+
val peeked = body.source().peek()
|
|
105
|
+
try {
|
|
106
|
+
if (peeked.request(byteCount + 1)) {
|
|
107
|
+
// When the request() returns true,
|
|
108
|
+
// it means the source have more available bytes then [byteCount].
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
} catch (_: IOException) {}
|
|
112
|
+
|
|
113
|
+
val encoding = response.header("Content-Encoding")
|
|
114
|
+
val source = when {
|
|
115
|
+
encoding.equals("gzip", ignoreCase = true) ->
|
|
116
|
+
GzipSource(peeked).buffer().apply {
|
|
117
|
+
request(byteCount)
|
|
118
|
+
}
|
|
119
|
+
else -> peeked
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
val buffer = okio.Buffer()
|
|
123
|
+
buffer.write(source, minOf(byteCount, source.buffer.size))
|
|
124
|
+
return buffer.asResponseBody(body.contentType(), buffer.size)
|
|
125
|
+
}
|
|
@@ -13,6 +13,7 @@ import kotlinx.coroutines.Dispatchers
|
|
|
13
13
|
import kotlinx.coroutines.launch
|
|
14
14
|
import okhttp3.Request
|
|
15
15
|
import okhttp3.Response
|
|
16
|
+
import okhttp3.ResponseBody
|
|
16
17
|
import java.math.BigDecimal
|
|
17
18
|
import java.math.RoundingMode
|
|
18
19
|
|
|
@@ -38,7 +39,11 @@ object ExpoRequestCdpInterceptor : ExpoNetworkInspectOkHttpInterceptorsDelegate
|
|
|
38
39
|
|
|
39
40
|
//region ExpoNetworkInspectOkHttpInterceptorsDelegate implementations
|
|
40
41
|
|
|
41
|
-
override fun willSendRequest(
|
|
42
|
+
override fun willSendRequest(
|
|
43
|
+
requestId: String,
|
|
44
|
+
request: Request,
|
|
45
|
+
redirectResponse: Response?
|
|
46
|
+
) {
|
|
42
47
|
val now = BigDecimal(System.currentTimeMillis() / 1000.0).setScale(3, RoundingMode.CEILING)
|
|
43
48
|
|
|
44
49
|
val params = RequestWillBeSentParams(now, requestId, request, redirectResponse)
|
|
@@ -48,14 +53,19 @@ object ExpoRequestCdpInterceptor : ExpoNetworkInspectOkHttpInterceptorsDelegate
|
|
|
48
53
|
dispatchEvent(Event("Network.requestWillBeSentExtraInfo", params2))
|
|
49
54
|
}
|
|
50
55
|
|
|
51
|
-
override fun didReceiveResponse(
|
|
56
|
+
override fun didReceiveResponse(
|
|
57
|
+
requestId: String,
|
|
58
|
+
request: Request,
|
|
59
|
+
response: Response,
|
|
60
|
+
body: ResponseBody?
|
|
61
|
+
) {
|
|
52
62
|
val now = BigDecimal(System.currentTimeMillis() / 1000.0).setScale(3, RoundingMode.CEILING)
|
|
53
63
|
|
|
54
64
|
val params = ResponseReceivedParams(now, requestId, request, response)
|
|
55
65
|
dispatchEvent(Event("Network.responseReceived", params))
|
|
56
66
|
|
|
57
|
-
if (
|
|
58
|
-
val params2 = ExpoReceivedResponseBodyParams(now, requestId, request, response)
|
|
67
|
+
if (body != null) {
|
|
68
|
+
val params2 = ExpoReceivedResponseBodyParams(now, requestId, request, response, body)
|
|
59
69
|
dispatchEvent(Event("Expo(Network.receivedResponseBody)", params2))
|
|
60
70
|
}
|
|
61
71
|
|
|
@@ -227,15 +227,20 @@ data class ExpoReceivedResponseBodyParams(
|
|
|
227
227
|
var body: String,
|
|
228
228
|
var base64Encoded: Boolean
|
|
229
229
|
) : JsonSerializable {
|
|
230
|
-
constructor(
|
|
230
|
+
constructor(
|
|
231
|
+
now: BigDecimal,
|
|
232
|
+
requestId: RequestId,
|
|
233
|
+
request: okhttp3.Request,
|
|
234
|
+
response: okhttp3.Response,
|
|
235
|
+
body: okhttp3.ResponseBody
|
|
236
|
+
) : this(
|
|
231
237
|
requestId = requestId,
|
|
232
238
|
body = "",
|
|
233
239
|
base64Encoded = false
|
|
234
240
|
) {
|
|
235
|
-
val
|
|
236
|
-
val contentType = rawBody.contentType()
|
|
241
|
+
val contentType = body.contentType()
|
|
237
242
|
val isText = contentType?.type == "text" || (contentType?.type == "application" && contentType.subtype == "json")
|
|
238
|
-
val bodyString = if (isText)
|
|
243
|
+
val bodyString = if (isText) body.string() else body.source().readByteString().base64()
|
|
239
244
|
|
|
240
245
|
this.body = bodyString
|
|
241
246
|
this.base64Encoded = !isText
|
|
@@ -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 =
|
|
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 =
|
|
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.
|
|
3
|
+
"version": "1.12.5",
|
|
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": "
|
|
47
|
+
"gitHead": "470464ab5c25ae8bd45eb2a94d221ce51a8b2560"
|
|
48
48
|
}
|