expo-modules-core 1.12.16 → 1.12.17
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 +5 -42
- package/android/src/main/cpp/JSIContext.h +11 -0
- package/android/src/main/cpp/ThreadSafeJNIGlobalRef.h +49 -0
- package/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift +1 -1
- package/ios/Legacy/Services/EXReactNativeAdapter.mm +0 -10
- package/ios/Tests/FileSystemLegacyUtilitiesSpec.swift +29 -0
- package/ios/Utilities.swift +29 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.12.17 — 2024-06-27
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [Android] Reduce the number of global references to JSIContext. ([#29936](https://github.com/expo/expo/pull/29936) by [@lukmccall](https://github.com/lukmccall))
|
|
18
|
+
- Fixed `getPathPermissions` permission error for local path with spaces on iOS 16 and older. ([#29958](https://github.com/expo/expo/pull/29958) by [@kudo](https://github.com/kudo))
|
|
19
|
+
- [iOS] Fixed broken `addUIBlock` and `executeUIBlock` on New Architecture mode. ([#30030](https://github.com/expo/expo/pull/30030) by [@kudo](https://github.com/kudo))
|
|
20
|
+
|
|
21
|
+
### 💡 Others
|
|
22
|
+
|
|
23
|
+
- [iOS] Exposed `Utilities` class for Expo Modules common tasks. ([#29945](https://github.com/expo/expo/pull/29945) by [@kudo](https://github.com/kudo))
|
|
24
|
+
|
|
13
25
|
## 1.12.16 — 2024-06-20
|
|
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.17'
|
|
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.17"
|
|
67
67
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
|
|
68
68
|
|
|
69
69
|
testInstrumentationRunner "expo.modules.TestRunner"
|
|
@@ -29,42 +29,6 @@ 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
|
-
|
|
68
32
|
jni::local_ref<JSIContext::jhybriddata>
|
|
69
33
|
JSIContext::initHybrid(jni::alias_ref<jhybridobject> jThis) {
|
|
70
34
|
return makeCxxInstance(jThis);
|
|
@@ -90,7 +54,10 @@ void JSIContext::registerNatives() {
|
|
|
90
54
|
}
|
|
91
55
|
|
|
92
56
|
JSIContext::JSIContext(jni::alias_ref<jhybridobject> jThis)
|
|
93
|
-
: javaPart_(jni::make_global(jThis))
|
|
57
|
+
: javaPart_(jni::make_global(jThis)),
|
|
58
|
+
threadSafeJThis(std::make_shared<ThreadSafeJNIGlobalRef<JSIContext::javaobject>>(
|
|
59
|
+
jni::Environment::current()->NewGlobalRef(javaPart_.get())
|
|
60
|
+
)) {}
|
|
94
61
|
|
|
95
62
|
JSIContext::~JSIContext() {
|
|
96
63
|
if (runtimeHolder) {
|
|
@@ -368,15 +335,11 @@ void JSIContext::jniSetNativeStateForSharedObject(
|
|
|
368
335
|
int id,
|
|
369
336
|
jni::alias_ref<JavaScriptObject::javaobject> jsObject
|
|
370
337
|
) {
|
|
371
|
-
auto threadSafeRef = std::make_shared<ThreadSafeJNIGlobalRef<JSIContext::javaobject>>(
|
|
372
|
-
jni::Environment::current()->NewGlobalRef(javaPart_.get())
|
|
373
|
-
);
|
|
374
|
-
|
|
375
338
|
auto nativeState = std::make_shared<expo::SharedObject::NativeState>(
|
|
376
339
|
id,
|
|
377
340
|
// We can't predict the order of deallocation of the JSIContext and the SharedObject.
|
|
378
341
|
// So we need to pass a new ref to retain the JSIContext to make sure it's not deallocated before the SharedObject.
|
|
379
|
-
[threadSafeRef =
|
|
342
|
+
[threadSafeRef = threadSafeJThis](const SharedObject::ObjectId objectId) {
|
|
380
343
|
threadSafeRef->use([objectId](jni::alias_ref<JSIContext::javaobject> globalRef) {
|
|
381
344
|
JSIContext::deleteSharedObject(globalRef, objectId);
|
|
382
345
|
});
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include "JavaReferencesCache.h"
|
|
10
10
|
#include "JSReferencesCache.h"
|
|
11
11
|
#include "JNIDeallocator.h"
|
|
12
|
+
#include "ThreadSafeJNIGlobalRef.h"
|
|
12
13
|
|
|
13
14
|
#include <fbjni/fbjni.h>
|
|
14
15
|
#include <jsi/jsi.h>
|
|
@@ -149,10 +150,20 @@ public:
|
|
|
149
150
|
|
|
150
151
|
private:
|
|
151
152
|
friend HybridBase;
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* We store two global references to the Java part of the JSIContext.
|
|
156
|
+
* However, one is wrapped in additional abstraction to make it thread-safe,
|
|
157
|
+
* which increase the access time. For most operations, we should use the bare reference.
|
|
158
|
+
* Only for operations that are executed on different threads that aren't attached to JVM,
|
|
159
|
+
* we should use the thread-safe reference.
|
|
160
|
+
*/
|
|
152
161
|
jni::global_ref<JSIContext::javaobject> javaPart_;
|
|
162
|
+
std::shared_ptr<ThreadSafeJNIGlobalRef<JSIContext::javaobject>> threadSafeJThis;
|
|
153
163
|
|
|
154
164
|
bool wasDeallocated_ = false;
|
|
155
165
|
|
|
166
|
+
|
|
156
167
|
explicit JSIContext(jni::alias_ref<jhybridobject> jThis);
|
|
157
168
|
|
|
158
169
|
inline jni::local_ref<JavaScriptModuleObject::javaobject>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright © 2021-present 650 Industries, Inc. (aka Expo)
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <fbjni/fbjni.h>
|
|
6
|
+
#include <android/log.h>
|
|
7
|
+
|
|
8
|
+
namespace jni = facebook::jni;
|
|
9
|
+
|
|
10
|
+
namespace expo {
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* A wrapper for a global reference that can be deallocated on any thread.
|
|
14
|
+
* It should be used with smart pointer. That structure can't be copied or moved.
|
|
15
|
+
*/
|
|
16
|
+
template<typename T>
|
|
17
|
+
class ThreadSafeJNIGlobalRef {
|
|
18
|
+
public:
|
|
19
|
+
ThreadSafeJNIGlobalRef(jobject globalRef) : globalRef(globalRef) {}
|
|
20
|
+
ThreadSafeJNIGlobalRef(const ThreadSafeJNIGlobalRef &other) = delete;
|
|
21
|
+
ThreadSafeJNIGlobalRef(ThreadSafeJNIGlobalRef &&other) = delete;
|
|
22
|
+
ThreadSafeJNIGlobalRef &operator=(const ThreadSafeJNIGlobalRef &other) = delete;
|
|
23
|
+
ThreadSafeJNIGlobalRef &operator=(ThreadSafeJNIGlobalRef &&other) = delete;
|
|
24
|
+
|
|
25
|
+
void use(std::function<void(jni::alias_ref<T> globalRef)> &&action) {
|
|
26
|
+
if (globalRef == nullptr) {
|
|
27
|
+
__android_log_print(ANDROID_LOG_WARN, "ExpoModulesCore", "ThreadSafeJNIGlobalRef was used after deallocation.");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
jni::ThreadScope::WithClassLoader([this, action = std::move(action)]() {
|
|
32
|
+
jni::alias_ref<jobject> aliasRef = jni::wrap_alias(globalRef);
|
|
33
|
+
jni::alias_ref<T> jsiContextRef = jni::static_ref_cast<T>(aliasRef);
|
|
34
|
+
action(jsiContextRef);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
~ThreadSafeJNIGlobalRef() {
|
|
39
|
+
if (globalRef != nullptr) {
|
|
40
|
+
jni::ThreadScope::WithClassLoader([this] {
|
|
41
|
+
jni::Environment::current()->DeleteGlobalRef(this->globalRef);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
jobject globalRef;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
} // namespace expo
|
|
@@ -82,7 +82,7 @@ public class FileSystemLegacyUtilities: NSObject, EXInternalModule, EXFileSystem
|
|
|
82
82
|
|
|
83
83
|
@objc
|
|
84
84
|
public func getPathPermissions(_ path: String) -> EXFileSystemPermissionFlags {
|
|
85
|
-
guard let url =
|
|
85
|
+
guard let url = convertToUrl(string: path) else {
|
|
86
86
|
return []
|
|
87
87
|
}
|
|
88
88
|
let permissionsForInternalDirectories = getInternalPathPermissions(url)
|
|
@@ -292,12 +292,7 @@ EX_REGISTER_MODULE();
|
|
|
292
292
|
|
|
293
293
|
dispatch_async(RCTGetUIManagerQueue(), ^{
|
|
294
294
|
[uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
|
295
|
-
#if RCT_NEW_ARCH_ENABLED
|
|
296
|
-
UIView<RCTComponentViewProtocol> *componentView = [uiManager viewForReactTag:(NSNumber *)viewId];
|
|
297
|
-
UIView *view = [(ExpoFabricViewObjC *)componentView contentView];
|
|
298
|
-
#else
|
|
299
295
|
UIView *view = [uiManager viewForReactTag:(NSNumber *)viewId];
|
|
300
|
-
#endif
|
|
301
296
|
block(view);
|
|
302
297
|
}];
|
|
303
298
|
});
|
|
@@ -309,12 +304,7 @@ EX_REGISTER_MODULE();
|
|
|
309
304
|
|
|
310
305
|
dispatch_async(RCTGetUIManagerQueue(), ^{
|
|
311
306
|
[uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
|
312
|
-
#if RCT_NEW_ARCH_ENABLED
|
|
313
|
-
UIView<RCTComponentViewProtocol> *componentView = [uiManager viewForReactTag:(NSNumber *)viewId];
|
|
314
|
-
UIView *view = [(ExpoFabricViewObjC *)componentView contentView];
|
|
315
|
-
#else
|
|
316
307
|
UIView *view = [uiManager viewForReactTag:(NSNumber *)viewId];
|
|
317
|
-
#endif
|
|
318
308
|
block(view);
|
|
319
309
|
}];
|
|
320
310
|
[uiManager setNeedsLayout];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesTestCore
|
|
4
|
+
|
|
5
|
+
@testable import ExpoModulesCore
|
|
6
|
+
|
|
7
|
+
final class FileSystemLegacyUtilitiesSpec: ExpoSpec {
|
|
8
|
+
override class func spec() {
|
|
9
|
+
let fsUtils = FileSystemLegacyUtilities()
|
|
10
|
+
|
|
11
|
+
describe("getPathPermissions") {
|
|
12
|
+
it("should return read/write permissions for filePath with `file:` scheme") {
|
|
13
|
+
let dirUrl = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
|
14
|
+
let fileUrl = dirUrl.appendingPathComponent("dir/test.txt")
|
|
15
|
+
let filePath = fileUrl.absoluteString
|
|
16
|
+
expect(filePath.starts(with: "file:")) == true
|
|
17
|
+
expect(fsUtils.getPathPermissions(filePath)) == [.read, .write]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
it("should return read/write permissions for filePath without `file:` scheme") {
|
|
21
|
+
let dirUrl = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
|
22
|
+
let fileUrl = dirUrl.appendingPathComponent("dir/test.txt")
|
|
23
|
+
let filePath = fileUrl.path
|
|
24
|
+
expect(filePath.starts(with: "file:")) == false
|
|
25
|
+
expect(fsUtils.getPathPermissions(filePath)) == [.read, .write]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/ios/Utilities.swift
CHANGED
|
@@ -60,11 +60,37 @@ internal func isFileUrlPath(_ path: String) -> Bool {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
internal func convertToUrl(string value: String) -> URL? {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
let url: URL?
|
|
64
|
+
if #available(iOS 17, *) {
|
|
65
|
+
// URL(string:) supports RFC 3986 as URLComponents from iOS 17
|
|
66
|
+
url = URL(string: value)
|
|
67
|
+
} else if #available(iOS 16, *) {
|
|
68
|
+
// URLComponents parses and constructs URLs according to RFC 3986.
|
|
69
|
+
// For some unusual urls URL(string:) will fail incorrectly
|
|
70
|
+
url = URLComponents(string: value)?.url ?? URL(string: value)
|
|
71
|
+
} else {
|
|
72
|
+
// URLComponents on iOS 15 and lower does not well support RFC 3986.
|
|
73
|
+
// We have to fallback URL(fileURLWithPath:) first.
|
|
74
|
+
url = value.hasPrefix("/")
|
|
75
|
+
? URL(fileURLWithPath: value)
|
|
76
|
+
: URLComponents(string: value)?.url ?? URL(string: value)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
guard let url else {
|
|
66
80
|
return nil
|
|
67
81
|
}
|
|
68
82
|
// If it has no scheme, we assume it was the file path which needs to be recreated to be recognized as the file url.
|
|
69
83
|
return url.scheme != nil ? url : URL(fileURLWithPath: value)
|
|
70
84
|
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
A collection of utility functions for various Expo Modules common tasks.
|
|
88
|
+
*/
|
|
89
|
+
public struct Utilities {
|
|
90
|
+
/**
|
|
91
|
+
Converts a `String` to a `URL`.
|
|
92
|
+
*/
|
|
93
|
+
public static func urlFrom(string: String) -> URL? {
|
|
94
|
+
return convertToUrl(string: string)
|
|
95
|
+
}
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.17",
|
|
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": "09b2d97bbc0f70f7c811ff9b6c9ad8808c5ad84b"
|
|
48
48
|
}
|