@hot-updater/react-native 1.0.8 → 1.1.1
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/HotUpdater.podspec +14 -2
- package/Package.swift +24 -3
- package/android/build.gradle +2 -6
- package/android/hot-updater-fingerprint.gradle +135 -0
- package/android/src/main/cpp/HotUpdaterRecovery.cpp +291 -21
- package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +176 -15
- package/android/src/main/java/com/hotupdater/ArtifactFileResolver.kt +20 -8
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +145 -31
- package/android/src/main/java/com/hotupdater/CohortService.kt +10 -2
- package/android/src/main/java/com/hotupdater/FileManagerService.kt +3 -1
- package/android/src/main/java/com/hotupdater/HotUpdaterConfig.kt +249 -21
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +8 -4
- package/android/src/main/java/com/hotupdater/HotUpdaterHeldContent.kt +92 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +319 -113
- package/android/src/main/java/com/hotupdater/HotUpdaterInstance.kt +87 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterInstanceEnvironment.kt +323 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterMainThread.kt +46 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterProcess.kt +30 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryAttribution.kt +493 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +1541 -159
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoverySlots.kt +137 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +4 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterRuntimeInstance.kt +53 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterUpdateCheck.kt +149 -0
- package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +12 -2
- package/android/src/main/java/com/hotupdater/NativeConfigUtils.kt +5 -6
- package/android/src/main/java/com/hotupdater/NativeUpdateCheck.kt +172 -0
- package/android/src/main/java/com/hotupdater/NativeUpdateChecker.kt +1007 -0
- package/android/src/main/java/com/hotupdater/NativeUpdateEventQueue.kt +111 -0
- package/android/src/main/java/com/hotupdater/OkHttpUpdateCheckTransport.kt +88 -0
- package/android/src/main/java/com/hotupdater/ReleaseCatalog.kt +491 -0
- package/android/src/main/java/com/hotupdater/ReleaseCatalogClient.kt +405 -0
- package/android/src/main/java/com/hotupdater/ReleaseSelection.kt +474 -0
- package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +32 -10
- package/android/src/main/java/com/hotupdater/StrictJson.kt +392 -0
- package/android/src/main/java/com/hotupdater/VersionedPreferencesService.kt +24 -9
- package/android/src/main/java/com/hotupdater/WeakIdentitySet.kt +76 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/BitReader.java +314 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/BrotliError.java +47 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/BrotliInputStream.java +174 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/BrotliRuntimeException.java +21 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/Context.java +58 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/Decode.java +1574 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/Decoder.java +72 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/Dictionary.java +103 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/DictionaryData.java +82 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/Huffman.java +143 -0
- package/{LICENSE → android/src/main/java/com/hotupdater/vendor/brotli/dec/LICENSE} +6 -20
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/README.md +39 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/State.java +100 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/Transform.java +240 -0
- package/android/src/main/java/com/hotupdater/vendor/brotli/dec/Utils.java +163 -0
- package/android/src/main/resources/META-INF/LICENSE-brotli +19 -0
- package/android/src/newarch/HotUpdater.kt +53 -15
- package/android/src/newarch/HotUpdaterModule.kt +42 -17
- package/android/src/newarch/JavaScriptExceptionHooks.kt +45 -8
- package/android/src/oldarch/HotUpdater.kt +51 -12
- package/android/src/oldarch/HotUpdaterModule.kt +43 -17
- package/android/src/oldarch/HotUpdaterSpec.kt +2 -0
- package/android/src/oldarch/JavaScriptExceptionHooks.kt +25 -16
- package/ios/HotUpdater/Interface/HotUpdaterInterface.h +13 -0
- package/ios/HotUpdater/Internal/ArtifactFileResolver.swift +38 -7
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +10 -4
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +186 -48
- package/ios/HotUpdater/Internal/CohortService.swift +8 -0
- package/ios/HotUpdater/Internal/HotUpdater.mm +821 -71
- package/ios/HotUpdater/Internal/HotUpdaterAsync.swift +7 -0
- package/ios/HotUpdater/Internal/HotUpdaterConfig.swift +208 -25
- package/ios/HotUpdater/Internal/HotUpdaterFingerprintHash.m +10 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +940 -261
- package/ios/HotUpdater/Internal/HotUpdaterInstanceEnvironment.swift +492 -0
- package/ios/HotUpdater/Internal/HotUpdaterInstanceRouter.swift +83 -0
- package/ios/HotUpdater/Internal/HotUpdaterInterface.swift +28 -1
- package/ios/HotUpdater/Internal/HotUpdaterLaunchRecovery.swift +1553 -0
- package/ios/HotUpdater/Internal/HotUpdaterNativeUpdates.swift +353 -0
- package/ios/HotUpdater/Internal/LaunchPreparationLock.swift +54 -0
- package/ios/HotUpdater/Internal/NativeUpdateCheck.swift +2051 -0
- package/ios/HotUpdater/Internal/ReleaseCatalog.swift +634 -0
- package/ios/HotUpdater/Internal/ReleaseCatalogCacheService.swift +1 -1
- package/ios/HotUpdater/Internal/ReleaseSelection.swift +482 -0
- package/ios/HotUpdater/Internal/SignatureVerifier.swift +27 -10
- package/ios/HotUpdater/Internal/StrictJson.swift +363 -0
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +4 -2
- package/ios/HotUpdater/Internal/UpdateFailure.swift +2 -0
- package/ios/HotUpdater/Package.swift +11 -0
- package/ios/HotUpdater/Public/HotUpdater/HotUpdater.h +4 -0
- package/ios/HotUpdater/Public/HotUpdater.h +21 -2
- package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +66 -0
- package/ios/Plugins/HotUpdaterFingerprintPlugin/HotUpdaterFingerprintPlugin.swift +45 -0
- package/ios/scripts/generate-fingerprint-hash.zsh +85 -0
- package/lib/commonjs/checkForUpdate.js +3 -1
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/index.js +9 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +24 -79
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/nativeUpdateEvents.js +66 -0
- package/lib/commonjs/nativeUpdateEvents.js.map +1 -0
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/checkForUpdate.js +3 -1
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/index.js +10 -13
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +19 -73
- package/lib/module/native.js.map +1 -1
- package/lib/module/nativeUpdateEvents.js +61 -0
- package/lib/module/nativeUpdateEvents.js.map +1 -0
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -14
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +9 -46
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/nativeUpdateEvents.d.ts +12 -0
- package/lib/typescript/commonjs/nativeUpdateEvents.d.ts.map +1 -0
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +19 -3
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -14
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +9 -46
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/nativeUpdateEvents.d.ts +12 -0
- package/lib/typescript/module/nativeUpdateEvents.d.ts.map +1 -0
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +19 -3
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/checkForUpdate.ts +7 -1
- package/src/index.ts +9 -17
- package/src/native.ts +19 -91
- package/src/nativeUpdateEvents.ts +77 -0
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +20 -3
- package/android/libs/README.md +0 -50
- package/android/libs/hot-updater-brotli-dec-1.2.0.jar +0 -0
- package/android/src/bridge/BridgeReactInstance.kt +0 -38
- package/android/src/bridgeless/BridgeReactInstance.kt +0 -24
- package/android/src/main/java/com/hotupdater/ContentAppearedLog.kt +0 -31
- package/android/src/main/java/com/hotupdater/ReactIntegrationManagerBase.kt +0 -34
- package/android/src/newarch/ReactIntegrationManager.kt +0 -212
- package/android/src/oldarch/ReactIntegrationManager.kt +0 -120
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
#import <React/RCTReloadCommand.h>
|
|
5
5
|
#import <React/RCTLog.h>
|
|
6
6
|
|
|
7
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
8
|
+
#import <React/RCTFabricSurface.h>
|
|
9
|
+
#import <React/RCTSurfacePresenter.h>
|
|
10
|
+
#else
|
|
11
|
+
#import <React/RCTBridge+Private.h>
|
|
12
|
+
#import <React/RCTModuleData.h>
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
#include <atomic>
|
|
16
|
+
#include <cstdint>
|
|
17
|
+
#include <cstring>
|
|
18
|
+
#include <mutex>
|
|
7
19
|
#include <fcntl.h>
|
|
8
20
|
#include <limits.h>
|
|
9
21
|
#include <signal.h>
|
|
@@ -22,9 +34,10 @@ typedef NSObject<HotUpdaterConfigInterface> HotUpdaterConfig;
|
|
|
22
34
|
#import "HotUpdater-Swift.h"
|
|
23
35
|
#endif
|
|
24
36
|
|
|
37
|
+
// The process's impl, which HotUpdaterInstance uses too.
|
|
25
38
|
static HotUpdaterImpl *HotUpdaterMakeImpl(void)
|
|
26
39
|
{
|
|
27
|
-
return [
|
|
40
|
+
return [HotUpdaterNativeUpdateBridge sharedImpl];
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
static NSString *HotUpdaterAppVersion(void)
|
|
@@ -36,6 +49,12 @@ static HotUpdaterConfig *HotUpdaterSharedConfig(void)
|
|
|
36
49
|
{
|
|
37
50
|
return [HotUpdaterConfig shared];
|
|
38
51
|
}
|
|
52
|
+
|
|
53
|
+
// The class methods share the selectors of HotUpdaterNativeUpdatesInterface.
|
|
54
|
+
static Class HotUpdaterNativeUpdates(void)
|
|
55
|
+
{
|
|
56
|
+
return [HotUpdaterNativeUpdateBridge class];
|
|
57
|
+
}
|
|
39
58
|
#endif
|
|
40
59
|
|
|
41
60
|
@interface HotUpdater (InternalSharedImpl)
|
|
@@ -43,14 +62,75 @@ static HotUpdaterConfig *HotUpdaterSharedConfig(void)
|
|
|
43
62
|
@end
|
|
44
63
|
|
|
45
64
|
extern "C" NSString *HotUpdaterBuildMinBundleId(void);
|
|
65
|
+
extern "C" NSString *HotUpdaterBuildFingerprintHash(void);
|
|
46
66
|
|
|
47
67
|
namespace {
|
|
48
68
|
constexpr size_t kHotUpdaterMaxBundleIdLength = 128;
|
|
49
69
|
const int kHotUpdaterSignals[] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP};
|
|
50
70
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
// One slot per instance whose launch a native crash may count against, slot 0
|
|
72
|
+
// being main's (HotUpdaterRecoveryCoordinator in Swift assigns them and keeps
|
|
73
|
+
// the rule below in step). A slot's marker path is set once and never moves.
|
|
74
|
+
// An instance beyond the last slot gets no marker from a native crash; its
|
|
75
|
+
// unfinished launch still rolls back through launchInProgress.
|
|
76
|
+
constexpr int kHotUpdaterCrashSlotCount = 8;
|
|
77
|
+
// Every slot whose launch is unverified writes its marker.
|
|
78
|
+
constexpr sig_atomic_t kHotUpdaterUnattributedCrash = -1;
|
|
79
|
+
|
|
80
|
+
struct HotUpdaterCrashSlotData {
|
|
81
|
+
// Empty while the slot is free.
|
|
82
|
+
char markerPath[PATH_MAX];
|
|
83
|
+
char bundleId[kHotUpdaterMaxBundleIdLength];
|
|
84
|
+
// The launch started and its first content hasn't appeared.
|
|
85
|
+
int shouldRollback;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// A sequence lock: a crash on any thread may read a slot while another thread
|
|
89
|
+
// rewrites it. The writer makes the sequence odd while it copies, and the
|
|
90
|
+
// handler keeps a copy only when the sequence was even and unchanged across
|
|
91
|
+
// it.
|
|
92
|
+
// The published copy of HotUpdaterCrashSlotData. Every field is atomic,
|
|
93
|
+
// copied with relaxed loads and stores, so a reader racing a writer reads
|
|
94
|
+
// defined (if mixed) values, which the sequence then rejects.
|
|
95
|
+
struct HotUpdaterAtomicCrashSlotData {
|
|
96
|
+
std::atomic<char> markerPath[PATH_MAX];
|
|
97
|
+
std::atomic<char> bundleId[kHotUpdaterMaxBundleIdLength];
|
|
98
|
+
std::atomic<int> shouldRollback;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
struct HotUpdaterCrashSlotBuffer {
|
|
102
|
+
std::atomic<uint32_t> sequence;
|
|
103
|
+
HotUpdaterAtomicCrashSlotData data;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Two buffers per slot: the writer fills the one not published, then
|
|
107
|
+
// publishes it. A crash during a rewrite, even on the writer's own thread,
|
|
108
|
+
// still reads the previous whole copy.
|
|
109
|
+
// `published`: bit 0 is the published buffer, bit 1 says anything was
|
|
110
|
+
// published at all (a freshly started slot has neither).
|
|
111
|
+
constexpr uint32_t kPublishedIndexMask = 1U;
|
|
112
|
+
constexpr uint32_t kPublishedOnceFlag = 2U;
|
|
113
|
+
|
|
114
|
+
struct HotUpdaterCrashSlot {
|
|
115
|
+
std::atomic<uint32_t> published;
|
|
116
|
+
HotUpdaterCrashSlotBuffer buffers[2];
|
|
117
|
+
};
|
|
118
|
+
// C++11, as old React Native builds compile it: the handler needs a
|
|
119
|
+
// lock-free sequence.
|
|
120
|
+
static_assert(ATOMIC_INT_LOCK_FREE == 2 && sizeof(uint32_t) == sizeof(int), "the crash handler needs a lock-free sequence");
|
|
121
|
+
static_assert(ATOMIC_CHAR_LOCK_FREE == 2, "the crash handler needs lock-free slot bytes");
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
// Zero-initialized: static storage. Every slot starts free.
|
|
125
|
+
HotUpdaterCrashSlot gHotUpdaterCrashSlots[kHotUpdaterCrashSlotCount];
|
|
126
|
+
// The sequence lock takes one writer at a time; every rewrite of a slot holds
|
|
127
|
+
// this lock. Never taken by the signal handler.
|
|
128
|
+
std::mutex gHotUpdaterCrashSlotWriters;
|
|
129
|
+
// A JavaScript fatal about to crash the process is one instance's: only its
|
|
130
|
+
// slot writes (none when it has no slot). Otherwise kHotUpdaterUnattributedCrash.
|
|
131
|
+
// Lock-free (the static_assert above), so the handler on any thread reads a
|
|
132
|
+
// whole value published by another.
|
|
133
|
+
std::atomic<int> gHotUpdaterAttributedSlot(kHotUpdaterUnattributedCrash);
|
|
54
134
|
struct sigaction gHotUpdaterPreviousActions[NSIG];
|
|
55
135
|
__weak RCTBridge *gHotUpdaterBridge = nil;
|
|
56
136
|
|
|
@@ -110,13 +190,147 @@ void HotUpdaterSafeCopy(char *destination, size_t destinationSize, const char *s
|
|
|
110
190
|
destination[index] = '\0';
|
|
111
191
|
}
|
|
112
192
|
|
|
113
|
-
void
|
|
193
|
+
void HotUpdaterStoreBytes(std::atomic<char> *destination, const char *source, size_t size)
|
|
194
|
+
{
|
|
195
|
+
for (size_t index = 0; index < size; ++index) {
|
|
196
|
+
destination[index].store(source[index], std::memory_order_relaxed);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
void HotUpdaterLoadBytes(const std::atomic<char> *source, char *destination, size_t size)
|
|
201
|
+
{
|
|
202
|
+
for (size_t index = 0; index < size; ++index) {
|
|
203
|
+
destination[index] = source[index].load(std::memory_order_relaxed);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// The strings first, the flag last with release: a reader that loads the flag
|
|
208
|
+
// with acquire and finds it asking for rollback sees the whole strings, even
|
|
209
|
+
// when it interrupts this copy (see HotUpdaterReadFirstCrashSlotWrite).
|
|
210
|
+
void HotUpdaterStoreCrashSlotData(HotUpdaterAtomicCrashSlotData &destination, const HotUpdaterCrashSlotData &source)
|
|
114
211
|
{
|
|
115
|
-
|
|
212
|
+
HotUpdaterStoreBytes(destination.markerPath, source.markerPath, sizeof(source.markerPath));
|
|
213
|
+
HotUpdaterStoreBytes(destination.bundleId, source.bundleId, sizeof(source.bundleId));
|
|
214
|
+
destination.shouldRollback.store(source.shouldRollback, std::memory_order_release);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
void HotUpdaterLoadCrashSlotData(const HotUpdaterAtomicCrashSlotData &source, HotUpdaterCrashSlotData &destination)
|
|
218
|
+
{
|
|
219
|
+
HotUpdaterLoadBytes(source.markerPath, destination.markerPath, sizeof(destination.markerPath));
|
|
220
|
+
HotUpdaterLoadBytes(source.bundleId, destination.bundleId, sizeof(destination.bundleId));
|
|
221
|
+
destination.shouldRollback = source.shouldRollback.load(std::memory_order_relaxed);
|
|
222
|
+
// A copy taken mid-rewrite is rejected by the sequence, but the strings must
|
|
223
|
+
// still end inside their buffers.
|
|
224
|
+
destination.markerPath[sizeof(destination.markerPath) - 1] = '\0';
|
|
225
|
+
destination.bundleId[sizeof(destination.bundleId) - 1] = '\0';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
constexpr int kHotUpdaterKeepShouldRollback = -1;
|
|
229
|
+
|
|
230
|
+
// Rewrites a slot; a null path or bundle id, or kHotUpdaterKeepShouldRollback,
|
|
231
|
+
// keeps the current value. The new fields are prepared first, so the window a
|
|
232
|
+
// crash skips stays one copy long.
|
|
233
|
+
void HotUpdaterRewriteCrashSlot(HotUpdaterCrashSlot &slot,
|
|
234
|
+
const char *markerPath,
|
|
235
|
+
const char *bundleId,
|
|
236
|
+
int shouldRollback)
|
|
237
|
+
{
|
|
238
|
+
std::lock_guard<std::mutex> writer(gHotUpdaterCrashSlotWriters);
|
|
239
|
+
const uint32_t state = slot.published.load(std::memory_order_relaxed);
|
|
240
|
+
const uint32_t current = state & kPublishedIndexMask;
|
|
241
|
+
HotUpdaterCrashSlotData published;
|
|
242
|
+
// The writers are serialized, so the published buffer is stable here.
|
|
243
|
+
HotUpdaterLoadCrashSlotData(slot.buffers[current].data, published);
|
|
244
|
+
HotUpdaterCrashSlotData next = published;
|
|
245
|
+
if (markerPath) {
|
|
246
|
+
HotUpdaterSafeCopy(next.markerPath, sizeof(next.markerPath), markerPath);
|
|
247
|
+
}
|
|
248
|
+
if (bundleId) {
|
|
249
|
+
HotUpdaterSafeCopy(next.bundleId, sizeof(next.bundleId), bundleId);
|
|
250
|
+
}
|
|
251
|
+
if (shouldRollback != kHotUpdaterKeepShouldRollback) {
|
|
252
|
+
next.shouldRollback = shouldRollback != 0 ? 1 : 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if ((state & kPublishedOnceFlag) != 0 && std::strcmp(published.markerPath, next.markerPath) == 0 &&
|
|
256
|
+
std::strcmp(published.bundleId, next.bundleId) == 0) {
|
|
257
|
+
// Same marker and bundle, only the flag changes (a launch charged or
|
|
258
|
+
// verified): flip it in the published buffer with one atomic store, so
|
|
259
|
+
// no crash ever falls inside a copy and reads the old flag.
|
|
260
|
+
slot.buffers[current].data.shouldRollback.store(next.shouldRollback, std::memory_order_release);
|
|
116
261
|
return;
|
|
117
262
|
}
|
|
118
263
|
|
|
119
|
-
|
|
264
|
+
HotUpdaterCrashSlotBuffer &buffer = slot.buffers[current ^ 1U];
|
|
265
|
+
const uint32_t sequence = buffer.sequence.load(std::memory_order_relaxed);
|
|
266
|
+
buffer.sequence.store(sequence + 1, std::memory_order_relaxed);
|
|
267
|
+
std::atomic_thread_fence(std::memory_order_release);
|
|
268
|
+
HotUpdaterStoreCrashSlotData(buffer.data, next);
|
|
269
|
+
buffer.sequence.store(sequence + 2, std::memory_order_release);
|
|
270
|
+
slot.published.store((current ^ 1U) | kPublishedOnceFlag, std::memory_order_release);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Copies a consistent snapshot of the slot, or returns false while it is
|
|
274
|
+
// being rewritten.
|
|
275
|
+
bool HotUpdaterReadCrashSlotBuffer(const HotUpdaterCrashSlotBuffer &buffer, HotUpdaterCrashSlotData &snapshot)
|
|
276
|
+
{
|
|
277
|
+
const uint32_t before = buffer.sequence.load(std::memory_order_acquire);
|
|
278
|
+
if ((before & 1U) != 0) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
HotUpdaterLoadCrashSlotData(buffer.data, snapshot);
|
|
282
|
+
std::atomic_thread_fence(std::memory_order_acquire);
|
|
283
|
+
return buffer.sequence.load(std::memory_order_relaxed) == before;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// The slot's first write, before anything was published, into the buffer
|
|
287
|
+
// that was all zero: whole, or interrupted (a crash on the writer's own
|
|
288
|
+
// thread, or another thread's crash) once its flag asks for rollback, since
|
|
289
|
+
// the strings were stored before it. Nothing to write from otherwise.
|
|
290
|
+
bool HotUpdaterReadFirstCrashSlotWrite(const HotUpdaterCrashSlotBuffer &buffer, HotUpdaterCrashSlotData &snapshot)
|
|
291
|
+
{
|
|
292
|
+
if (HotUpdaterReadCrashSlotBuffer(buffer, snapshot)) {
|
|
293
|
+
return snapshot.markerPath[0] != '\0';
|
|
294
|
+
}
|
|
295
|
+
const int shouldRollback = buffer.data.shouldRollback.load(std::memory_order_acquire);
|
|
296
|
+
if (shouldRollback == 0) {
|
|
297
|
+
// Not asking for rollback yet: a marker would change nothing.
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
HotUpdaterLoadBytes(buffer.data.markerPath, snapshot.markerPath, sizeof(snapshot.markerPath));
|
|
301
|
+
HotUpdaterLoadBytes(buffer.data.bundleId, snapshot.bundleId, sizeof(snapshot.bundleId));
|
|
302
|
+
snapshot.markerPath[sizeof(snapshot.markerPath) - 1] = '\0';
|
|
303
|
+
snapshot.bundleId[sizeof(snapshot.bundleId) - 1] = '\0';
|
|
304
|
+
snapshot.shouldRollback = shouldRollback;
|
|
305
|
+
return snapshot.markerPath[0] != '\0';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// A snapshot to write the slot's marker from, or false when there is none.
|
|
309
|
+
// Before anything was published, only the first write is read, never the
|
|
310
|
+
// unwritten published buffer. After, only the published buffer, retried while
|
|
311
|
+
// it is rewritten.
|
|
312
|
+
bool HotUpdaterReadCrashSlot(const HotUpdaterCrashSlot &slot, HotUpdaterCrashSlotData &snapshot)
|
|
313
|
+
{
|
|
314
|
+
for (int attempt = 0; attempt < 4; ++attempt) {
|
|
315
|
+
const uint32_t state = slot.published.load(std::memory_order_acquire);
|
|
316
|
+
const uint32_t published = state & kPublishedIndexMask;
|
|
317
|
+
if ((state & kPublishedOnceFlag) == 0) {
|
|
318
|
+
return HotUpdaterReadFirstCrashSlotWrite(slot.buffers[published ^ 1U], snapshot);
|
|
319
|
+
}
|
|
320
|
+
if (HotUpdaterReadCrashSlotBuffer(slot.buffers[published], snapshot)) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
void HotUpdaterWriteCrashMarker(const HotUpdaterCrashSlotData &slot)
|
|
328
|
+
{
|
|
329
|
+
if (slot.markerPath[0] == '\0') {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
int fd = open(slot.markerPath, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
120
334
|
if (fd < 0) {
|
|
121
335
|
return;
|
|
122
336
|
}
|
|
@@ -128,11 +342,11 @@ void HotUpdaterWriteCrashMarker()
|
|
|
128
342
|
constexpr char suffix[] = "}\n";
|
|
129
343
|
|
|
130
344
|
write(fd, prefix, sizeof(prefix) - 1);
|
|
131
|
-
if (
|
|
132
|
-
write(fd,
|
|
345
|
+
if (slot.bundleId[0] != '\0') {
|
|
346
|
+
write(fd, slot.bundleId, HotUpdaterSafeStringLength(slot.bundleId, kHotUpdaterMaxBundleIdLength));
|
|
133
347
|
}
|
|
134
348
|
write(fd, middle, sizeof(middle) - 1);
|
|
135
|
-
if (
|
|
349
|
+
if (slot.shouldRollback != 0) {
|
|
136
350
|
write(fd, trueLiteral, sizeof(trueLiteral) - 1);
|
|
137
351
|
} else {
|
|
138
352
|
write(fd, falseLiteral, sizeof(falseLiteral) - 1);
|
|
@@ -141,6 +355,28 @@ void HotUpdaterWriteCrashMarker()
|
|
|
141
355
|
close(fd);
|
|
142
356
|
}
|
|
143
357
|
|
|
358
|
+
// Main's slot (0) writes once main launched, as it always has, with
|
|
359
|
+
// `shouldRollback: false` after its first content. A named slot writes only
|
|
360
|
+
// while its launch is unverified (started, first content not yet shown). While
|
|
361
|
+
// a JavaScript fatal of one instance takes the process down, only its slot
|
|
362
|
+
// writes.
|
|
363
|
+
void HotUpdaterWriteCrashMarkers()
|
|
364
|
+
{
|
|
365
|
+
const int attributedSlot = gHotUpdaterAttributedSlot.load(std::memory_order_acquire);
|
|
366
|
+
for (int index = 0; index < kHotUpdaterCrashSlotCount; ++index) {
|
|
367
|
+
if (attributedSlot != kHotUpdaterUnattributedCrash && attributedSlot != index) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
HotUpdaterCrashSlotData slot;
|
|
371
|
+
if (!HotUpdaterReadCrashSlot(gHotUpdaterCrashSlots[index], slot) || slot.markerPath[0] == '\0') {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (index == 0 || slot.shouldRollback != 0) {
|
|
375
|
+
HotUpdaterWriteCrashMarker(slot);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
144
380
|
void HotUpdaterSignalHandler(int signum, siginfo_t *info, void *context);
|
|
145
381
|
|
|
146
382
|
void HotUpdaterForwardToPreviousHandler(int signum, siginfo_t *info, void *context)
|
|
@@ -172,29 +408,64 @@ void HotUpdaterForwardToPreviousHandler(int signum, siginfo_t *info, void *conte
|
|
|
172
408
|
|
|
173
409
|
void HotUpdaterSignalHandler(int signum, siginfo_t *info, void *context)
|
|
174
410
|
{
|
|
175
|
-
|
|
411
|
+
HotUpdaterWriteCrashMarkers();
|
|
176
412
|
HotUpdaterForwardToPreviousHandler(signum, info, context);
|
|
177
413
|
}
|
|
178
414
|
} // namespace
|
|
179
415
|
|
|
180
|
-
|
|
416
|
+
// Once per process. Called on the main thread.
|
|
417
|
+
extern "C" void HotUpdaterInstallCrashSignalHandlers(void)
|
|
181
418
|
{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
419
|
+
// Once per process, whichever instance asks first: a second sigaction
|
|
420
|
+
// would record this handler as its own previous action and recurse.
|
|
421
|
+
static dispatch_once_t onceToken;
|
|
422
|
+
dispatch_once(&onceToken, ^{
|
|
423
|
+
|
|
424
|
+
struct sigaction action {};
|
|
425
|
+
action.sa_sigaction = HotUpdaterSignalHandler;
|
|
426
|
+
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
|
427
|
+
sigemptyset(&action.sa_mask);
|
|
428
|
+
|
|
429
|
+
for (int signum : kHotUpdaterSignals) {
|
|
430
|
+
sigaction(signum, &action, &gHotUpdaterPreviousActions[signum]);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
}
|
|
188
434
|
|
|
189
|
-
|
|
190
|
-
|
|
435
|
+
extern "C" void HotUpdaterUpdateCrashSlot(NSInteger index,
|
|
436
|
+
NSString *crashMarkerPath,
|
|
437
|
+
NSString * _Nullable bundleId,
|
|
438
|
+
BOOL shouldRollback)
|
|
439
|
+
{
|
|
440
|
+
if (index < 0 || index >= kHotUpdaterCrashSlotCount) {
|
|
441
|
+
return;
|
|
191
442
|
}
|
|
443
|
+
HotUpdaterRewriteCrashSlot(gHotUpdaterCrashSlots[index],
|
|
444
|
+
crashMarkerPath.UTF8String ?: "",
|
|
445
|
+
bundleId.UTF8String ?: "",
|
|
446
|
+
shouldRollback ? 1 : 0);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// kHotUpdaterUnattributedCrash, a slot index, or any other negative value for
|
|
450
|
+
// an instance without a slot (then a native crash writes no marker).
|
|
451
|
+
extern "C" void HotUpdaterAttributeCrashToSlot(NSInteger index)
|
|
452
|
+
{
|
|
453
|
+
gHotUpdaterAttributedSlot.store(static_cast<int>(index), std::memory_order_release);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Legacy entry points of the single main slot.
|
|
457
|
+
extern "C" void HotUpdaterInstallSignalHandlers(NSString *crashMarkerPath)
|
|
458
|
+
{
|
|
459
|
+
HotUpdaterRewriteCrashSlot(gHotUpdaterCrashSlots[0],
|
|
460
|
+
crashMarkerPath.UTF8String ?: "",
|
|
461
|
+
nullptr,
|
|
462
|
+
kHotUpdaterKeepShouldRollback);
|
|
463
|
+
HotUpdaterInstallCrashSignalHandlers();
|
|
192
464
|
}
|
|
193
465
|
|
|
194
466
|
extern "C" void HotUpdaterUpdateSignalLaunchState(NSString * _Nullable bundleId, BOOL shouldRollback)
|
|
195
467
|
{
|
|
196
|
-
|
|
197
|
-
gHotUpdaterShouldRollback = shouldRollback ? 1 : 0;
|
|
468
|
+
HotUpdaterRewriteCrashSlot(gHotUpdaterCrashSlots[0], nullptr, bundleId.UTF8String ?: "", shouldRollback ? 1 : 0);
|
|
198
469
|
}
|
|
199
470
|
|
|
200
471
|
extern "C" BOOL HotUpdaterPerformRecoveryReload(void)
|
|
@@ -205,7 +476,7 @@ extern "C" BOOL HotUpdaterPerformRecoveryReload(void)
|
|
|
205
476
|
HotUpdaterImpl *impl = [HotUpdater sharedImpl];
|
|
206
477
|
[impl resetLaunchPreparation];
|
|
207
478
|
|
|
208
|
-
NSURL *bundleURL = [impl bundleURLWithBundle:
|
|
479
|
+
NSURL *bundleURL = [impl bundleURLWithBundle:HotUpdaterSharedConfig().bundle];
|
|
209
480
|
if (!bundleURL) {
|
|
210
481
|
RCTLogWarn(@"[HotUpdater.mm] Failed to resolve bundle URL for recovery reload");
|
|
211
482
|
return;
|
|
@@ -243,10 +514,67 @@ extern "C" void *HotUpdaterCopyMinBundleId(void)
|
|
|
243
514
|
return (__bridge_retained void *)HotUpdaterGetMinBundleId();
|
|
244
515
|
}
|
|
245
516
|
|
|
517
|
+
// The fingerprint the build embedded, or NULL for an app without one.
|
|
518
|
+
extern "C" void *HotUpdaterCopyBuildFingerprintHash(void)
|
|
519
|
+
{
|
|
520
|
+
NSString *fingerprintHash = HotUpdaterBuildFingerprintHash();
|
|
521
|
+
return fingerprintHash.length > 0
|
|
522
|
+
? (__bridge_retained void *)fingerprintHash
|
|
523
|
+
: NULL;
|
|
524
|
+
}
|
|
525
|
+
|
|
246
526
|
|
|
247
527
|
// Define Notification names used for observing Swift Core
|
|
248
528
|
NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUpdaterUpdateProgressDidChange";
|
|
249
529
|
|
|
530
|
+
#pragma mark - Runtime Attribution
|
|
531
|
+
|
|
532
|
+
// Crash recovery (HotUpdaterRecoveryHub in Swift) asks which instance a view,
|
|
533
|
+
// a surface, or a failing JavaScript thread belongs to. Each React Native
|
|
534
|
+
// runtime has its own module, which knows its instance once its bundle URL is
|
|
535
|
+
// known, and its runtime's surface presenter.
|
|
536
|
+
|
|
537
|
+
// Posted once a runtime's module knows its instance, so surfaces that started
|
|
538
|
+
// before it existed can be attributed.
|
|
539
|
+
static NSString *const HotUpdaterRuntimeDidResolveNotification = @"HotUpdaterRuntimeDidResolve";
|
|
540
|
+
static NSString *const HotUpdaterRuntimeModuleKey = @"HotUpdater.runtimeModule";
|
|
541
|
+
// The bridgeless runtime's JavaScript thread, where RCTFatal reports its
|
|
542
|
+
// JavaScript fatal errors.
|
|
543
|
+
static NSString *const HotUpdaterRuntimeThreadName = @"com.facebook.react.runtime.JavaScript";
|
|
544
|
+
|
|
545
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
546
|
+
// Old architecture: the context of the ExceptionsManager's method queue, where
|
|
547
|
+
// RCTFatal reports that bridge's JavaScript fatals.
|
|
548
|
+
static char kHotUpdaterRuntimeQueueKey;
|
|
549
|
+
|
|
550
|
+
static void HotUpdaterReleaseRuntimeQueueReference(void *reference)
|
|
551
|
+
{
|
|
552
|
+
CFRelease(reference);
|
|
553
|
+
}
|
|
554
|
+
#endif
|
|
555
|
+
|
|
556
|
+
// What a JavaScript thread's dictionary, or an old-architecture runtime's
|
|
557
|
+
// ExceptionsManager queue, holds: its runtime's module, weakly.
|
|
558
|
+
@interface HotUpdaterRuntimeModuleReference : NSObject
|
|
559
|
+
@property (nonatomic, weak) HotUpdater *module;
|
|
560
|
+
@end
|
|
561
|
+
|
|
562
|
+
@implementation HotUpdaterRuntimeModuleReference
|
|
563
|
+
@end
|
|
564
|
+
|
|
565
|
+
static id HotUpdaterRuntimeIdentityOfModule(HotUpdater *module);
|
|
566
|
+
|
|
567
|
+
// Every module that joined a runtime, weakly.
|
|
568
|
+
static NSHashTable<HotUpdater *> *HotUpdaterRuntimeModules(void)
|
|
569
|
+
{
|
|
570
|
+
static NSHashTable<HotUpdater *> *modules;
|
|
571
|
+
static dispatch_once_t onceToken;
|
|
572
|
+
dispatch_once(&onceToken, ^{
|
|
573
|
+
modules = [NSHashTable weakObjectsHashTable];
|
|
574
|
+
});
|
|
575
|
+
return modules;
|
|
576
|
+
}
|
|
577
|
+
|
|
250
578
|
@interface HotUpdaterRecoverySignalBridge : NSObject
|
|
251
579
|
@end
|
|
252
580
|
|
|
@@ -265,14 +593,23 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
|
|
|
265
593
|
@end
|
|
266
594
|
|
|
267
595
|
@interface HotUpdater () <RCTInitializing>
|
|
596
|
+
- (HotUpdaterImpl *)resolvedInstanceImpl;
|
|
597
|
+
- (NSURL *)runtimeBundleURL;
|
|
598
|
+
- (BOOL)isRuntimeOfSurfacePresenter:(id)surfacePresenter rootTag:(NSNumber *)rootTag;
|
|
268
599
|
@end
|
|
269
600
|
|
|
270
601
|
@implementation HotUpdater {
|
|
271
602
|
bool hasListeners;
|
|
603
|
+
// The impl of this module's React Native runtime, once its bundle URL is known.
|
|
604
|
+
HotUpdaterImpl *_instanceImpl;
|
|
605
|
+
// Bridgeless: this runtime's surface presenter, which knows every surface
|
|
606
|
+
// of this runtime by its root tag.
|
|
607
|
+
__weak id _surfacePresenter;
|
|
272
608
|
}
|
|
273
609
|
|
|
274
610
|
@synthesize bridge = _bridge;
|
|
275
611
|
@synthesize moduleRegistry = _moduleRegistry;
|
|
612
|
+
@synthesize bundleManager = _bundleManager;
|
|
276
613
|
|
|
277
614
|
+ (BOOL)requiresMainQueueSetup {
|
|
278
615
|
return YES;
|
|
@@ -299,7 +636,12 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
|
|
|
299
636
|
- (void)setBridge:(RCTBridge *)bridge
|
|
300
637
|
{
|
|
301
638
|
[super setBridge:bridge];
|
|
302
|
-
|
|
639
|
+
// Main's recovery reload reloads this bridge: only main's runtime sets
|
|
640
|
+
// it, never a named runtime's that started after main's. With main
|
|
641
|
+
// only, every bridge is main's, as before.
|
|
642
|
+
if (!bridge || [HotUpdaterNativeUpdates() implForBundleURL:bridge.bundleURL] == [HotUpdater sharedImpl]) {
|
|
643
|
+
gHotUpdaterBridge = bridge;
|
|
644
|
+
}
|
|
303
645
|
[self configureExceptionsManagerWithBridge:bridge];
|
|
304
646
|
}
|
|
305
647
|
|
|
@@ -329,7 +671,44 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
|
|
|
329
671
|
|
|
330
672
|
id exceptionsManager = [bridge moduleForClass:[RCTExceptionsManager class]];
|
|
331
673
|
[self applyExceptionsManagerReloadLimit:exceptionsManager];
|
|
674
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
675
|
+
[self noteRuntimeQueuesOfBridge:bridge];
|
|
676
|
+
#endif
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
680
|
+
// Old architecture: RCTFatal reports a JavaScript fatal on the
|
|
681
|
+
// ExceptionsManager's method queue, and an error of the bridge itself on its
|
|
682
|
+
// JavaScript thread. Both belong to this module's bridge, so both remember
|
|
683
|
+
// this module, which tells crash recovery whose fatal it is.
|
|
684
|
+
- (void)noteRuntimeQueuesOfBridge:(RCTBridge *)bridge
|
|
685
|
+
{
|
|
686
|
+
HotUpdaterRuntimeModuleReference *reference = [HotUpdaterRuntimeModuleReference new];
|
|
687
|
+
reference.module = self;
|
|
688
|
+
|
|
689
|
+
#pragma clang diagnostic push
|
|
690
|
+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
691
|
+
// Modules get the batched bridge, which keeps the module data.
|
|
692
|
+
RCTBridge *batchedBridge = [bridge respondsToSelector:@selector(moduleDataForName:)] ? bridge : bridge.batchedBridge;
|
|
693
|
+
RCTModuleData *exceptionsManager = [batchedBridge respondsToSelector:@selector(moduleDataForName:)]
|
|
694
|
+
? [batchedBridge moduleDataForName:@"ExceptionsManager"]
|
|
695
|
+
: nil;
|
|
696
|
+
dispatch_queue_t queue = exceptionsManager.methodQueue;
|
|
697
|
+
#pragma clang diagnostic pop
|
|
698
|
+
// Only a queue of this bridge alone.
|
|
699
|
+
if (queue && queue != dispatch_get_main_queue() && queue != RCTJSThread) {
|
|
700
|
+
dispatch_queue_set_specific(queue,
|
|
701
|
+
&kHotUpdaterRuntimeQueueKey,
|
|
702
|
+
(__bridge_retained void *)reference,
|
|
703
|
+
HotUpdaterReleaseRuntimeQueueReference);
|
|
704
|
+
}
|
|
705
|
+
if ([batchedBridge respondsToSelector:@selector(dispatchBlock:queue:)]) {
|
|
706
|
+
[batchedBridge dispatchBlock:^{
|
|
707
|
+
[NSThread currentThread].threadDictionary[HotUpdaterRuntimeModuleKey] = reference;
|
|
708
|
+
} queue:RCTJSThread];
|
|
709
|
+
}
|
|
332
710
|
}
|
|
711
|
+
#endif
|
|
333
712
|
|
|
334
713
|
- (void)configureExceptionsManagerWithModuleRegistry:(id)moduleRegistry
|
|
335
714
|
{
|
|
@@ -384,6 +763,132 @@ RCT_EXPORT_MODULE();
|
|
|
384
763
|
return _sharedImpl;
|
|
385
764
|
}
|
|
386
765
|
|
|
766
|
+
#pragma mark - Instance Routing
|
|
767
|
+
|
|
768
|
+
// The impl of the instance whose bundle this module's React Native runtime
|
|
769
|
+
// runs. Each runtime has its own module, and JavaScript never names its
|
|
770
|
+
// instance, so the runtime's bundle URL tells them apart. Resolved on first
|
|
771
|
+
// use: the URL isn't set yet in -init, and React Native also creates a module
|
|
772
|
+
// that never joins a runtime.
|
|
773
|
+
- (HotUpdaterImpl *)instanceImpl {
|
|
774
|
+
@synchronized (self) {
|
|
775
|
+
if (_instanceImpl) {
|
|
776
|
+
return _instanceImpl;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
781
|
+
NSURL *bundleURL = self.bundleManager.bundleURL;
|
|
782
|
+
#else
|
|
783
|
+
NSURL *bundleURL = self.bridge.bundleURL;
|
|
784
|
+
#endif
|
|
785
|
+
if (!bundleURL) {
|
|
786
|
+
// Not cached: the next call resolves again once the URL is set.
|
|
787
|
+
return [HotUpdater sharedImpl];
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
HotUpdaterImpl *impl = [HotUpdaterNativeUpdates() implForBundleURL:bundleURL];
|
|
791
|
+
BOOL resolvedNow = NO;
|
|
792
|
+
@synchronized (self) {
|
|
793
|
+
if (!_instanceImpl) {
|
|
794
|
+
_instanceImpl = impl;
|
|
795
|
+
resolvedNow = YES;
|
|
796
|
+
}
|
|
797
|
+
impl = _instanceImpl;
|
|
798
|
+
}
|
|
799
|
+
if (resolvedNow) {
|
|
800
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:HotUpdaterRuntimeDidResolveNotification
|
|
801
|
+
object:self
|
|
802
|
+
userInfo:@{
|
|
803
|
+
@"impl" : impl,
|
|
804
|
+
@"bundleURL" : bundleURL,
|
|
805
|
+
@"runtime" : HotUpdaterRuntimeIdentityOfModule(self) ?: self,
|
|
806
|
+
}];
|
|
807
|
+
}
|
|
808
|
+
return impl;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// The impl once this runtime's bundle URL is known, else nil: attribution
|
|
812
|
+
// must not take instanceImpl's fallback to main for a runtime it can't tell.
|
|
813
|
+
- (HotUpdaterImpl *)resolvedInstanceImpl {
|
|
814
|
+
@synchronized (self) {
|
|
815
|
+
if (_instanceImpl) {
|
|
816
|
+
return _instanceImpl;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return [self runtimeBundleURL] ? [self instanceImpl] : nil;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// The bundle this module's runtime runs, which tells an error of an earlier
|
|
823
|
+
// launch's runtime from one of the instance's current launch.
|
|
824
|
+
- (NSURL *)runtimeBundleURL {
|
|
825
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
826
|
+
return self.bundleManager.bundleURL;
|
|
827
|
+
#else
|
|
828
|
+
return self.bridge.bundleURL;
|
|
829
|
+
#endif
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// RCTInstance hands each module it creates its runtime's surface presenter.
|
|
833
|
+
- (void)setSurfacePresenter:(id)surfacePresenter {
|
|
834
|
+
@synchronized (self) {
|
|
835
|
+
_surfacePresenter = surfacePresenter;
|
|
836
|
+
}
|
|
837
|
+
NSHashTable<HotUpdater *> *modules = HotUpdaterRuntimeModules();
|
|
838
|
+
@synchronized (modules) {
|
|
839
|
+
[modules addObject:self];
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Whether `surfacePresenter` is this runtime's, or, without one, whether
|
|
844
|
+
// this runtime's presenter has the surface of `rootTag`. Root tags are unique
|
|
845
|
+
// in the process, so no other runtime's presenter has it.
|
|
846
|
+
- (BOOL)isRuntimeOfSurfacePresenter:(id)surfacePresenter rootTag:(NSNumber *)rootTag {
|
|
847
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
848
|
+
RCTSurfacePresenter *presenter = nil;
|
|
849
|
+
@synchronized (self) {
|
|
850
|
+
presenter = _surfacePresenter;
|
|
851
|
+
}
|
|
852
|
+
if (!presenter) {
|
|
853
|
+
return NO;
|
|
854
|
+
}
|
|
855
|
+
if (surfacePresenter) {
|
|
856
|
+
return presenter == surfacePresenter;
|
|
857
|
+
}
|
|
858
|
+
return rootTag && [presenter surfaceForRootTag:rootTag.integerValue] != nil;
|
|
859
|
+
#else
|
|
860
|
+
return NO;
|
|
861
|
+
#endif
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// React Native asks for the module's JSI object on the runtime's JavaScript
|
|
865
|
+
// thread, which is where RCTFatal reports that runtime's JavaScript fatals.
|
|
866
|
+
- (void)noteRuntimeThread {
|
|
867
|
+
NSThread *thread = [NSThread currentThread];
|
|
868
|
+
if (![thread.name isEqualToString:HotUpdaterRuntimeThreadName]) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
HotUpdaterRuntimeModuleReference *reference = [HotUpdaterRuntimeModuleReference new];
|
|
872
|
+
reference.module = self;
|
|
873
|
+
thread.threadDictionary[HotUpdaterRuntimeModuleKey] = reference;
|
|
874
|
+
// Resolves now, while the bundle URL is known, for the surfaces that
|
|
875
|
+
// started before this module existed.
|
|
876
|
+
[self resolvedInstanceImpl];
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Reloading reloads main's React host or the whole app, so a runtime of a
|
|
880
|
+
// named instance refuses it until each React host can reload its own.
|
|
881
|
+
- (BOOL)rejectReloadOfNamedInstance:(RCTPromiseRejectBlock)reject {
|
|
882
|
+
if ([self instanceImpl] == [HotUpdater sharedImpl]) {
|
|
883
|
+
return NO;
|
|
884
|
+
}
|
|
885
|
+
reject(@"RELOAD_UNSUPPORTED",
|
|
886
|
+
@"HotUpdater.reload() and HotUpdater.reloadProcess() are not supported in a React Native runtime of a named "
|
|
887
|
+
@"HotUpdaterInstance yet. Reload its React Native host from native code.",
|
|
888
|
+
nil);
|
|
889
|
+
return YES;
|
|
890
|
+
}
|
|
891
|
+
|
|
387
892
|
#pragma mark - React Native Constants (Keep getMinBundleId, delegate others)
|
|
388
893
|
|
|
389
894
|
// Returns the bundle ID of the bundle this build ships (MIN_BUNDLE_ID)
|
|
@@ -395,31 +900,34 @@ RCT_EXPORT_MODULE();
|
|
|
395
900
|
return @{
|
|
396
901
|
@"MIN_BUNDLE_ID": [self getMinBundleId] ?: [NSNull null],
|
|
397
902
|
@"APP_VERSION": HotUpdaterAppVersion() ?: [NSNull null],
|
|
398
|
-
@"CHANNEL": [[
|
|
399
|
-
@"DEFAULT_CHANNEL": [[
|
|
400
|
-
@"FINGERPRINT_HASH": [[
|
|
903
|
+
@"CHANNEL": [[self instanceImpl] getChannel] ?: [NSNull null],
|
|
904
|
+
@"DEFAULT_CHANNEL": [[self instanceImpl] getDefaultChannel] ?: [NSNull null],
|
|
905
|
+
@"FINGERPRINT_HASH": [[self instanceImpl] getFingerprintHash] ?: [NSNull null]
|
|
401
906
|
};
|
|
402
907
|
}
|
|
403
908
|
|
|
404
909
|
|
|
405
910
|
// Get bundleURL with default bundle using singleton
|
|
406
911
|
+ (NSURL *)bundleURL {
|
|
407
|
-
return [
|
|
912
|
+
return [self bundleURLWithBundle:[NSBundle mainBundle]];
|
|
408
913
|
}
|
|
409
914
|
|
|
410
915
|
// Get bundleURL with specific bundle using singleton
|
|
411
916
|
+ (NSURL *)bundleURLWithBundle:(NSBundle *)bundle {
|
|
917
|
+
// Record the bundle before the first sharedImpl, whose storage isolation
|
|
918
|
+
// key reads the bundle's HOT_UPDATER_* Info.plist keys.
|
|
919
|
+
HotUpdaterSharedConfig().bundle = bundle;
|
|
412
920
|
return [[HotUpdater sharedImpl] bundleURLWithBundle:bundle];
|
|
413
921
|
}
|
|
414
922
|
|
|
415
923
|
// Get bundleURL with default bundle using instance impl
|
|
416
924
|
- (NSURL *)bundleURL {
|
|
417
|
-
return [
|
|
925
|
+
return [HotUpdater bundleURLWithBundle:[NSBundle mainBundle]];
|
|
418
926
|
}
|
|
419
927
|
|
|
420
928
|
// Get bundleURL with specific bundle using instance impl
|
|
421
929
|
- (NSURL *)bundleURLWithBundle:(NSBundle *)bundle {
|
|
422
|
-
return [
|
|
930
|
+
return [HotUpdater bundleURLWithBundle:bundle];
|
|
423
931
|
}
|
|
424
932
|
|
|
425
933
|
+ (void)configureWithFingerprintHash:(NSString *)fingerprintHash
|
|
@@ -434,6 +942,9 @@ RCT_EXPORT_MODULE();
|
|
|
434
942
|
|
|
435
943
|
- (void)handleDownloadProgress:(NSNotification *)notification {
|
|
436
944
|
if (!hasListeners) return;
|
|
945
|
+
// Every impl posts its progress here; this runtime's JavaScript gets
|
|
946
|
+
// only its own instance's.
|
|
947
|
+
if (notification.object != [self instanceImpl]) return;
|
|
437
948
|
|
|
438
949
|
NSDictionary *userInfo = notification.userInfo;
|
|
439
950
|
NSNumber *progressNum = userInfo[@"progress"];
|
|
@@ -478,11 +989,14 @@ RCT_EXPORT_MODULE();
|
|
|
478
989
|
- (void)reload:(RCTPromiseResolveBlock)resolve
|
|
479
990
|
reject:(RCTPromiseRejectBlock)reject {
|
|
480
991
|
RCTLogInfo(@"[HotUpdater.mm] HotUpdater requested a reload");
|
|
992
|
+
if ([self rejectReloadOfNamedInstance:reject]) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
481
995
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
482
996
|
@try {
|
|
483
997
|
HotUpdaterImpl *impl = [HotUpdater sharedImpl];
|
|
484
998
|
[impl resetLaunchPreparation];
|
|
485
|
-
NSURL *bundleURL = [impl bundleURLWithBundle:
|
|
999
|
+
NSURL *bundleURL = [impl bundleURLWithBundle:HotUpdaterSharedConfig().bundle];
|
|
486
1000
|
RCTLogInfo(@"[HotUpdater.mm] Reloading with bundle URL: %@", bundleURL);
|
|
487
1001
|
if (bundleURL) {
|
|
488
1002
|
RCTReloadCommandSetBundleURL(bundleURL);
|
|
@@ -543,7 +1057,7 @@ RCT_EXPORT_MODULE();
|
|
|
543
1057
|
paramDict[@"requestTimeout"] = @(params.requestTimeout().value());
|
|
544
1058
|
}
|
|
545
1059
|
|
|
546
|
-
HotUpdaterImpl *impl = [
|
|
1060
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
547
1061
|
[impl updateBundle:paramDict resolver:resolve rejecter:reject];
|
|
548
1062
|
}
|
|
549
1063
|
|
|
@@ -583,63 +1097,63 @@ RCT_EXPORT_MODULE();
|
|
|
583
1097
|
paramDict[@"selection"] = params.selection();
|
|
584
1098
|
}
|
|
585
1099
|
|
|
586
|
-
HotUpdaterImpl *impl = [
|
|
1100
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
587
1101
|
[impl installArtifact:paramDict resolver:resolve rejecter:reject];
|
|
588
1102
|
}
|
|
589
1103
|
|
|
590
1104
|
- (NSNumber *)acceptReleaseCatalog:(NSDictionary *)params {
|
|
591
|
-
return @([[
|
|
1105
|
+
return @([[self instanceImpl] acceptReleaseCatalog:params]);
|
|
592
1106
|
}
|
|
593
1107
|
|
|
594
1108
|
- (NSDictionary *)getActiveUpdateState {
|
|
595
|
-
return [[
|
|
1109
|
+
return [[self instanceImpl] getActiveUpdateState];
|
|
596
1110
|
}
|
|
597
1111
|
|
|
598
1112
|
- (void)getReleaseCatalogCache:(NSString *)partition
|
|
599
1113
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
600
1114
|
reject:(RCTPromiseRejectBlock)reject {
|
|
601
|
-
[[
|
|
1115
|
+
[[self instanceImpl] getReleaseCatalogCache:partition resolver:resolve rejecter:reject];
|
|
602
1116
|
}
|
|
603
1117
|
|
|
604
1118
|
- (void)setReleaseCatalogCache:(NSString *)partition
|
|
605
1119
|
payload:(NSString *)payload
|
|
606
1120
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
607
1121
|
reject:(RCTPromiseRejectBlock)reject {
|
|
608
|
-
[[
|
|
1122
|
+
[[self instanceImpl] setReleaseCatalogCache:partition payload:payload resolver:resolve rejecter:reject];
|
|
609
1123
|
}
|
|
610
1124
|
|
|
611
1125
|
- (void)removeReleaseCatalogCache:(NSString *)partition
|
|
612
1126
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
613
1127
|
reject:(RCTPromiseRejectBlock)reject {
|
|
614
|
-
[[
|
|
1128
|
+
[[self instanceImpl] removeReleaseCatalogCache:partition resolver:resolve rejecter:reject];
|
|
615
1129
|
}
|
|
616
1130
|
|
|
617
1131
|
- (NSNumber *)isReleaseSelectionCurrent:(NSDictionary *)params {
|
|
618
|
-
return @([[
|
|
1132
|
+
return @([[self instanceImpl] isReleaseSelectionCurrent:params]);
|
|
619
1133
|
}
|
|
620
1134
|
|
|
621
1135
|
- (void)commitReleaseSelection:(NSDictionary *)params
|
|
622
1136
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
623
1137
|
reject:(RCTPromiseRejectBlock)reject {
|
|
624
|
-
[[
|
|
1138
|
+
[[self instanceImpl] commitReleaseSelection:params resolver:resolve rejecter:reject];
|
|
625
1139
|
}
|
|
626
1140
|
|
|
627
1141
|
- (NSDictionary *)notifyAppReady {
|
|
628
1142
|
NSLog(@"[HotUpdater.mm] notifyAppReady called");
|
|
629
|
-
HotUpdaterImpl *impl = [
|
|
1143
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
630
1144
|
return [impl notifyAppReady];
|
|
631
1145
|
}
|
|
632
1146
|
|
|
633
1147
|
- (NSArray<NSString *> *)getCrashHistory {
|
|
634
1148
|
NSLog(@"[HotUpdater.mm] getCrashHistory called");
|
|
635
|
-
HotUpdaterImpl *impl = [
|
|
1149
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
636
1150
|
NSArray<NSString *> *crashHistory = [impl getCrashHistory];
|
|
637
1151
|
return crashHistory ?: @[];
|
|
638
1152
|
}
|
|
639
1153
|
|
|
640
1154
|
- (NSNumber *)clearCrashHistory {
|
|
641
1155
|
NSLog(@"[HotUpdater.mm] clearCrashHistory called");
|
|
642
|
-
HotUpdaterImpl *impl = [
|
|
1156
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
643
1157
|
BOOL result = [impl clearCrashHistory];
|
|
644
1158
|
return @(result);
|
|
645
1159
|
}
|
|
@@ -650,18 +1164,22 @@ RCT_EXPORT_MODULE();
|
|
|
650
1164
|
}
|
|
651
1165
|
|
|
652
1166
|
- (NSString * _Nullable)getStorageItem:(NSString *)key {
|
|
653
|
-
HotUpdaterImpl *impl = [
|
|
1167
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
654
1168
|
return [impl getStorageItem:key];
|
|
655
1169
|
}
|
|
656
1170
|
|
|
657
1171
|
- (void)setStorageItem:(NSString *)key value:(NSString * _Nullable)value {
|
|
658
|
-
HotUpdaterImpl *impl = [
|
|
1172
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
659
1173
|
[impl setStorageItem:key value:value];
|
|
660
1174
|
}
|
|
661
1175
|
|
|
1176
|
+
- (NSString * _Nullable)takeNativeUpdateEvents {
|
|
1177
|
+
return [HotUpdaterNativeUpdates() takeEventsWithImpl:[self instanceImpl]];
|
|
1178
|
+
}
|
|
1179
|
+
|
|
662
1180
|
- (NSString * _Nullable)getBaseURL {
|
|
663
1181
|
NSLog(@"[HotUpdater.mm] getBaseURL called");
|
|
664
|
-
HotUpdaterImpl *impl = [
|
|
1182
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
665
1183
|
return [impl getBaseURL];
|
|
666
1184
|
}
|
|
667
1185
|
|
|
@@ -677,21 +1195,22 @@ RCT_EXPORT_MODULE();
|
|
|
677
1195
|
|
|
678
1196
|
- (NSString * _Nullable)getBundleId {
|
|
679
1197
|
NSLog(@"[HotUpdater.mm] getBundleId called");
|
|
680
|
-
HotUpdaterImpl *impl = [
|
|
1198
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
681
1199
|
return [impl getBundleId];
|
|
682
1200
|
}
|
|
683
1201
|
|
|
684
1202
|
- (NSDictionary<NSString *, id> *)getManifest {
|
|
685
1203
|
NSLog(@"[HotUpdater.mm] getManifest called");
|
|
686
|
-
HotUpdaterImpl *impl = [
|
|
1204
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
687
1205
|
NSDictionary<NSString *, id> *manifest = [impl getManifest];
|
|
688
1206
|
return manifest ?: @{};
|
|
689
1207
|
}
|
|
690
1208
|
|
|
691
1209
|
- (void)resetChannel:(RCTPromiseResolveBlock)resolve
|
|
692
1210
|
reject:(RCTPromiseRejectBlock)reject {
|
|
693
|
-
|
|
694
|
-
|
|
1211
|
+
[HotUpdaterNativeUpdates() resetChannelWithImpl:[self instanceImpl]
|
|
1212
|
+
resolver:resolve
|
|
1213
|
+
rejecter:reject];
|
|
695
1214
|
}
|
|
696
1215
|
|
|
697
1216
|
- (facebook::react::ModuleConstants<JS::NativeHotUpdater::Constants::Builder>)constantsToExport {
|
|
@@ -699,7 +1218,7 @@ RCT_EXPORT_MODULE();
|
|
|
699
1218
|
}
|
|
700
1219
|
|
|
701
1220
|
- (facebook::react::ModuleConstants<JS::NativeHotUpdater::Constants::Builder>)getConstants {
|
|
702
|
-
HotUpdaterImpl *impl = [
|
|
1221
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
703
1222
|
return facebook::react::typedConstants<JS::NativeHotUpdater::Constants::Builder>({
|
|
704
1223
|
.MIN_BUNDLE_ID = [self getMinBundleId],
|
|
705
1224
|
.APP_VERSION = HotUpdaterAppVersion(),
|
|
@@ -716,11 +1235,14 @@ RCT_EXPORT_MODULE();
|
|
|
716
1235
|
RCT_EXPORT_METHOD(reload:(RCTPromiseResolveBlock)resolve
|
|
717
1236
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
718
1237
|
RCTLogInfo(@"[HotUpdater.mm] HotUpdater requested a reload");
|
|
1238
|
+
if ([self rejectReloadOfNamedInstance:reject]) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
719
1241
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
720
1242
|
@try {
|
|
721
1243
|
HotUpdaterImpl *impl = [HotUpdater sharedImpl];
|
|
722
1244
|
[impl resetLaunchPreparation];
|
|
723
|
-
NSURL *bundleURL = [impl bundleURLWithBundle:
|
|
1245
|
+
NSURL *bundleURL = [impl bundleURLWithBundle:HotUpdaterSharedConfig().bundle];
|
|
724
1246
|
RCTLogInfo(@"[HotUpdater.mm] Reloading with bundle URL: %@", bundleURL);
|
|
725
1247
|
if (bundleURL) {
|
|
726
1248
|
RCTReloadCommandSetBundleURL(bundleURL);
|
|
@@ -748,7 +1270,7 @@ RCT_EXPORT_METHOD(updateBundle:(NSDictionary *)params
|
|
|
748
1270
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
749
1271
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
750
1272
|
NSLog(@"[HotUpdater.mm] updateBundle called. params: %@", params);
|
|
751
|
-
HotUpdaterImpl *impl = [
|
|
1273
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
752
1274
|
[impl updateBundle:params resolver:resolve rejecter:reject];
|
|
753
1275
|
}
|
|
754
1276
|
|
|
@@ -756,63 +1278,63 @@ RCT_EXPORT_METHOD(installArtifact:(NSDictionary *)params
|
|
|
756
1278
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
757
1279
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
758
1280
|
NSLog(@"[HotUpdater.mm] installArtifact called.");
|
|
759
|
-
HotUpdaterImpl *impl = [
|
|
1281
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
760
1282
|
[impl installArtifact:params resolver:resolve rejecter:reject];
|
|
761
1283
|
}
|
|
762
1284
|
|
|
763
1285
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(acceptReleaseCatalog:(NSDictionary *)params) {
|
|
764
|
-
return @([[
|
|
1286
|
+
return @([[self instanceImpl] acceptReleaseCatalog:params]);
|
|
765
1287
|
}
|
|
766
1288
|
|
|
767
1289
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getActiveUpdateState) {
|
|
768
|
-
return [[
|
|
1290
|
+
return [[self instanceImpl] getActiveUpdateState];
|
|
769
1291
|
}
|
|
770
1292
|
|
|
771
1293
|
RCT_EXPORT_METHOD(getReleaseCatalogCache:(NSString *)partition
|
|
772
1294
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
773
1295
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
774
|
-
[[
|
|
1296
|
+
[[self instanceImpl] getReleaseCatalogCache:partition resolver:resolve rejecter:reject];
|
|
775
1297
|
}
|
|
776
1298
|
|
|
777
1299
|
RCT_EXPORT_METHOD(setReleaseCatalogCache:(NSString *)partition
|
|
778
1300
|
payload:(NSString *)payload
|
|
779
1301
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
780
1302
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
781
|
-
[[
|
|
1303
|
+
[[self instanceImpl] setReleaseCatalogCache:partition payload:payload resolver:resolve rejecter:reject];
|
|
782
1304
|
}
|
|
783
1305
|
|
|
784
1306
|
RCT_EXPORT_METHOD(removeReleaseCatalogCache:(NSString *)partition
|
|
785
1307
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
786
1308
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
787
|
-
[[
|
|
1309
|
+
[[self instanceImpl] removeReleaseCatalogCache:partition resolver:resolve rejecter:reject];
|
|
788
1310
|
}
|
|
789
1311
|
|
|
790
1312
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isReleaseSelectionCurrent:(NSDictionary *)params) {
|
|
791
|
-
return @([[
|
|
1313
|
+
return @([[self instanceImpl] isReleaseSelectionCurrent:params]);
|
|
792
1314
|
}
|
|
793
1315
|
|
|
794
1316
|
RCT_EXPORT_METHOD(commitReleaseSelection:(NSDictionary *)params
|
|
795
1317
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
796
1318
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
797
|
-
[[
|
|
1319
|
+
[[self instanceImpl] commitReleaseSelection:params resolver:resolve rejecter:reject];
|
|
798
1320
|
}
|
|
799
1321
|
|
|
800
1322
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(notifyAppReady) {
|
|
801
1323
|
NSLog(@"[HotUpdater.mm] notifyAppReady called");
|
|
802
|
-
HotUpdaterImpl *impl = [
|
|
1324
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
803
1325
|
return [impl notifyAppReady];
|
|
804
1326
|
}
|
|
805
1327
|
|
|
806
1328
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCrashHistory) {
|
|
807
1329
|
NSLog(@"[HotUpdater.mm] getCrashHistory called");
|
|
808
|
-
HotUpdaterImpl *impl = [
|
|
1330
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
809
1331
|
NSArray<NSString *> *crashHistory = [impl getCrashHistory];
|
|
810
1332
|
return crashHistory ?: @[];
|
|
811
1333
|
}
|
|
812
1334
|
|
|
813
1335
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(clearCrashHistory) {
|
|
814
1336
|
NSLog(@"[HotUpdater.mm] clearCrashHistory called");
|
|
815
|
-
HotUpdaterImpl *impl = [
|
|
1337
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
816
1338
|
BOOL result = [impl clearCrashHistory];
|
|
817
1339
|
return @(result);
|
|
818
1340
|
}
|
|
@@ -823,18 +1345,22 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getInstallId) {
|
|
|
823
1345
|
}
|
|
824
1346
|
|
|
825
1347
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getStorageItem:(NSString *)key) {
|
|
826
|
-
HotUpdaterImpl *impl = [
|
|
1348
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
827
1349
|
return [impl getStorageItem:key];
|
|
828
1350
|
}
|
|
829
1351
|
|
|
830
1352
|
RCT_EXPORT_METHOD(setStorageItem:(NSString *)key value:(NSString * _Nullable)value) {
|
|
831
|
-
HotUpdaterImpl *impl = [
|
|
1353
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
832
1354
|
[impl setStorageItem:key value:value];
|
|
833
1355
|
}
|
|
834
1356
|
|
|
1357
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(takeNativeUpdateEvents) {
|
|
1358
|
+
return [HotUpdaterNativeUpdates() takeEventsWithImpl:[self instanceImpl]];
|
|
1359
|
+
}
|
|
1360
|
+
|
|
835
1361
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBaseURL) {
|
|
836
1362
|
NSLog(@"[HotUpdater.mm] getBaseURL called");
|
|
837
|
-
HotUpdaterImpl *impl = [
|
|
1363
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
838
1364
|
return [impl getBaseURL];
|
|
839
1365
|
}
|
|
840
1366
|
|
|
@@ -850,21 +1376,22 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCohort) {
|
|
|
850
1376
|
|
|
851
1377
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBundleId) {
|
|
852
1378
|
NSLog(@"[HotUpdater.mm] getBundleId called");
|
|
853
|
-
HotUpdaterImpl *impl = [
|
|
1379
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
854
1380
|
return [impl getBundleId];
|
|
855
1381
|
}
|
|
856
1382
|
|
|
857
1383
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getManifest) {
|
|
858
1384
|
NSLog(@"[HotUpdater.mm] getManifest called");
|
|
859
|
-
HotUpdaterImpl *impl = [
|
|
1385
|
+
HotUpdaterImpl *impl = [self instanceImpl];
|
|
860
1386
|
NSDictionary<NSString *, id> *manifest = [impl getManifest];
|
|
861
1387
|
return manifest ?: @{};
|
|
862
1388
|
}
|
|
863
1389
|
|
|
864
1390
|
RCT_EXPORT_METHOD(resetChannel:(RCTPromiseResolveBlock)resolve
|
|
865
1391
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
866
|
-
|
|
867
|
-
|
|
1392
|
+
[HotUpdaterNativeUpdates() resetChannelWithImpl:[self instanceImpl]
|
|
1393
|
+
resolver:resolve
|
|
1394
|
+
rejecter:reject];
|
|
868
1395
|
}
|
|
869
1396
|
|
|
870
1397
|
- (NSDictionary *)constantsToExport {
|
|
@@ -883,9 +1410,232 @@ RCT_EXPORT_METHOD(resetChannel:(RCTPromiseResolveBlock)resolve
|
|
|
883
1410
|
|
|
884
1411
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
885
1412
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
1413
|
+
[self noteRuntimeThread];
|
|
886
1414
|
return std::make_shared<facebook::react::NativeHotUpdaterSpecJSI>(params);
|
|
887
1415
|
}
|
|
888
1416
|
#endif
|
|
889
1417
|
|
|
890
1418
|
|
|
891
1419
|
@end
|
|
1420
|
+
|
|
1421
|
+
#pragma mark - Runtime Attribution (C)
|
|
1422
|
+
|
|
1423
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
1424
|
+
// The bundle URL an RCTHost or RCTInstance runs, which neither exposes:
|
|
1425
|
+
// read through KVC from what RN 0.85 keeps (RCTHost's `_bundleURL` and
|
|
1426
|
+
// `_bundleManager`, RCTInstance's `_bridgeModuleDecorator.bundleManager`).
|
|
1427
|
+
// Nil when another React Native version keeps it elsewhere.
|
|
1428
|
+
static NSURL *HotUpdaterBundleURLOfRuntime(id object)
|
|
1429
|
+
{
|
|
1430
|
+
for (NSString *keyPath in @[ @"bundleURL", @"bundleManager.bundleURL", @"bridgeModuleDecorator.bundleManager.bundleURL" ]) {
|
|
1431
|
+
@try {
|
|
1432
|
+
id value = [object valueForKeyPath:keyPath];
|
|
1433
|
+
if ([value isKindOfClass:[NSURL class]]) {
|
|
1434
|
+
return value;
|
|
1435
|
+
}
|
|
1436
|
+
} @catch (NSException *exception) {
|
|
1437
|
+
// Not a key of this object.
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
return nil;
|
|
1441
|
+
}
|
|
1442
|
+
#endif
|
|
1443
|
+
|
|
1444
|
+
// The impl whose runtime `object` belongs to: a Fabric surface or root view,
|
|
1445
|
+
// an RCTInstance or RCTHost (by its surface presenter), an old-architecture
|
|
1446
|
+
// root view or bridge (by its bundle URL). Nil when no runtime claims it.
|
|
1447
|
+
// `runtimeBundleURL`, when given, receives the bundle the runtime runs.
|
|
1448
|
+
// The object that stands for one React Native runtime, which crash recovery
|
|
1449
|
+
// keeps the launch it runs by: the module (bridgeless), or the bridge the app
|
|
1450
|
+
// created (old architecture; a module, a root view and a load failure each
|
|
1451
|
+
// reach it, and its batched bridge points back to it).
|
|
1452
|
+
static id HotUpdaterRuntimeIdentityOfBridge(id bridge)
|
|
1453
|
+
{
|
|
1454
|
+
if ([bridge respondsToSelector:NSSelectorFromString(@"parentBridge")]) {
|
|
1455
|
+
id parent = [bridge valueForKey:@"parentBridge"];
|
|
1456
|
+
if (parent) {
|
|
1457
|
+
return parent;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
return bridge;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
static id HotUpdaterRuntimeIdentityOfModule(HotUpdater *module)
|
|
1464
|
+
{
|
|
1465
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
1466
|
+
return module;
|
|
1467
|
+
#else
|
|
1468
|
+
return HotUpdaterRuntimeIdentityOfBridge(module.bridge);
|
|
1469
|
+
#endif
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// `runtimeIdentity`, when given, receives the runtime's identity (see
|
|
1473
|
+
// HotUpdaterRuntimeIdentityOfBridge) when one told the runtime apart.
|
|
1474
|
+
static HotUpdaterImpl *HotUpdaterImplForRootObject(id object, NSURL **runtimeBundleURL, id *runtimeIdentity = nullptr)
|
|
1475
|
+
{
|
|
1476
|
+
if (!object) {
|
|
1477
|
+
return nil;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
1481
|
+
id surfacePresenter = nil;
|
|
1482
|
+
NSNumber *rootTag = nil;
|
|
1483
|
+
if ([object respondsToSelector:@selector(surfacePresenter)]) {
|
|
1484
|
+
surfacePresenter = [object valueForKey:@"surfacePresenter"];
|
|
1485
|
+
} else if ([object respondsToSelector:NSSelectorFromString(@"rootViewTag")]) {
|
|
1486
|
+
id tag = [object valueForKey:@"rootViewTag"];
|
|
1487
|
+
rootTag = [tag isKindOfClass:[NSNumber class]] ? tag : nil;
|
|
1488
|
+
} else if ([object isKindOfClass:[UIView class]] &&
|
|
1489
|
+
[object isKindOfClass:NSClassFromString(@"RCTRootComponentView")]) {
|
|
1490
|
+
rootTag = @(((UIView *)object).tag);
|
|
1491
|
+
}
|
|
1492
|
+
if (surfacePresenter || rootTag) {
|
|
1493
|
+
NSHashTable<HotUpdater *> *table = HotUpdaterRuntimeModules();
|
|
1494
|
+
NSArray<HotUpdater *> *modules;
|
|
1495
|
+
@synchronized (table) {
|
|
1496
|
+
modules = table.allObjects;
|
|
1497
|
+
}
|
|
1498
|
+
for (HotUpdater *module in modules) {
|
|
1499
|
+
if ([module isRuntimeOfSurfacePresenter:surfacePresenter rootTag:rootTag]) {
|
|
1500
|
+
HotUpdaterImpl *impl = [module resolvedInstanceImpl];
|
|
1501
|
+
if (impl) {
|
|
1502
|
+
if (runtimeBundleURL) {
|
|
1503
|
+
*runtimeBundleURL = [module runtimeBundleURL];
|
|
1504
|
+
}
|
|
1505
|
+
if (runtimeIdentity) {
|
|
1506
|
+
*runtimeIdentity = HotUpdaterRuntimeIdentityOfModule(module);
|
|
1507
|
+
}
|
|
1508
|
+
return impl;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (!surfacePresenter) {
|
|
1513
|
+
return nil;
|
|
1514
|
+
}
|
|
1515
|
+
// An RCTInstance or RCTHost whose bundle failed to load before its
|
|
1516
|
+
// runtime created a module: its bundle URL tells whose it is.
|
|
1517
|
+
NSURL *bundleURL = HotUpdaterBundleURLOfRuntime(object);
|
|
1518
|
+
if (!bundleURL) {
|
|
1519
|
+
return nil;
|
|
1520
|
+
}
|
|
1521
|
+
if (runtimeBundleURL) {
|
|
1522
|
+
*runtimeBundleURL = bundleURL;
|
|
1523
|
+
}
|
|
1524
|
+
return [HotUpdaterNativeUpdates() implForBundleURL:bundleURL];
|
|
1525
|
+
}
|
|
1526
|
+
#endif
|
|
1527
|
+
|
|
1528
|
+
RCTBridge *bridge = nil;
|
|
1529
|
+
if ([object isKindOfClass:[RCTBridge class]]) {
|
|
1530
|
+
bridge = object;
|
|
1531
|
+
} else if ([object respondsToSelector:@selector(bridge)]) {
|
|
1532
|
+
id candidate = [object valueForKey:@"bridge"];
|
|
1533
|
+
bridge = [candidate isKindOfClass:[RCTBridge class]] ? candidate : nil;
|
|
1534
|
+
}
|
|
1535
|
+
NSURL *bundleURL = bridge.bundleURL;
|
|
1536
|
+
if (!bundleURL) {
|
|
1537
|
+
return nil;
|
|
1538
|
+
}
|
|
1539
|
+
if (runtimeBundleURL) {
|
|
1540
|
+
*runtimeBundleURL = bundleURL;
|
|
1541
|
+
}
|
|
1542
|
+
if (runtimeIdentity) {
|
|
1543
|
+
*runtimeIdentity = HotUpdaterRuntimeIdentityOfBridge(bridge);
|
|
1544
|
+
}
|
|
1545
|
+
return [HotUpdaterNativeUpdates() implForBundleURL:bundleURL];
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
extern "C" void *HotUpdaterCopyImplForRootObject(id object)
|
|
1549
|
+
{
|
|
1550
|
+
HotUpdaterImpl *impl = HotUpdaterImplForRootObject(object, nullptr);
|
|
1551
|
+
return impl ? (__bridge_retained void *)impl : NULL;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// The bundle the runtime `object` belongs to runs, or NULL.
|
|
1555
|
+
extern "C" void *HotUpdaterCopyBundleURLForRootObject(id object)
|
|
1556
|
+
{
|
|
1557
|
+
NSURL *bundleURL = nil;
|
|
1558
|
+
HotUpdaterImplForRootObject(object, &bundleURL);
|
|
1559
|
+
return bundleURL ? (__bridge_retained void *)bundleURL : NULL;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// The identity of the runtime `object` belongs to, or NULL when none told it
|
|
1563
|
+
// apart. Crash recovery keeps the launch each runtime runs by it.
|
|
1564
|
+
extern "C" void *HotUpdaterCopyRuntimeIdentityForRootObject(id object)
|
|
1565
|
+
{
|
|
1566
|
+
id identity = nil;
|
|
1567
|
+
HotUpdaterImplForRootObject(object, nullptr, &identity);
|
|
1568
|
+
return identity ? (__bridge_retained void *)identity : NULL;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// The impl of the runtime whose JavaScript thread, or old-architecture
|
|
1572
|
+
// ExceptionsManager queue, calls this, or NULL.
|
|
1573
|
+
extern "C" void *HotUpdaterCopyRuntimeImpl(void)
|
|
1574
|
+
{
|
|
1575
|
+
HotUpdaterRuntimeModuleReference *reference =
|
|
1576
|
+
[NSThread currentThread].threadDictionary[HotUpdaterRuntimeModuleKey];
|
|
1577
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
1578
|
+
if (!reference) {
|
|
1579
|
+
reference = (__bridge HotUpdaterRuntimeModuleReference *)dispatch_get_specific(&kHotUpdaterRuntimeQueueKey);
|
|
1580
|
+
}
|
|
1581
|
+
#endif
|
|
1582
|
+
HotUpdaterImpl *impl = [reference.module resolvedInstanceImpl];
|
|
1583
|
+
return impl ? (__bridge_retained void *)impl : NULL;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
// The identity of the runtime whose JavaScript thread, or old-architecture
|
|
1587
|
+
// ExceptionsManager queue, calls this, or NULL.
|
|
1588
|
+
extern "C" void *HotUpdaterCopyRuntimeIdentity(void)
|
|
1589
|
+
{
|
|
1590
|
+
HotUpdaterRuntimeModuleReference *reference =
|
|
1591
|
+
[NSThread currentThread].threadDictionary[HotUpdaterRuntimeModuleKey];
|
|
1592
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
1593
|
+
if (!reference) {
|
|
1594
|
+
reference = (__bridge HotUpdaterRuntimeModuleReference *)dispatch_get_specific(&kHotUpdaterRuntimeQueueKey);
|
|
1595
|
+
}
|
|
1596
|
+
#endif
|
|
1597
|
+
HotUpdater *module = reference.module;
|
|
1598
|
+
id identity = module ? HotUpdaterRuntimeIdentityOfModule(module) : nil;
|
|
1599
|
+
return identity ? (__bridge_retained void *)identity : NULL;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// The bundle the runtime whose JavaScript thread, or old-architecture
|
|
1603
|
+
// ExceptionsManager queue, calls this runs, or NULL.
|
|
1604
|
+
extern "C" void *HotUpdaterCopyRuntimeBundleURL(void)
|
|
1605
|
+
{
|
|
1606
|
+
HotUpdaterRuntimeModuleReference *reference =
|
|
1607
|
+
[NSThread currentThread].threadDictionary[HotUpdaterRuntimeModuleKey];
|
|
1608
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
1609
|
+
if (!reference) {
|
|
1610
|
+
reference = (__bridge HotUpdaterRuntimeModuleReference *)dispatch_get_specific(&kHotUpdaterRuntimeQueueKey);
|
|
1611
|
+
}
|
|
1612
|
+
#endif
|
|
1613
|
+
NSURL *bundleURL = [reference.module runtimeBundleURL];
|
|
1614
|
+
return bundleURL ? (__bridge_retained void *)bundleURL : NULL;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
// Whether a started view is shown: an old-architecture root view in a window,
|
|
1618
|
+
// or a running Fabric surface whose view is in a window. A surface's `view`
|
|
1619
|
+
// getter would create its view, so this reads the one it already has.
|
|
1620
|
+
extern "C" BOOL HotUpdaterIsViewShown(id object)
|
|
1621
|
+
{
|
|
1622
|
+
if ([object isKindOfClass:[UIView class]]) {
|
|
1623
|
+
return ((UIView *)object).window != nil;
|
|
1624
|
+
}
|
|
1625
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
1626
|
+
if ([object isKindOfClass:[RCTFabricSurface class]]) {
|
|
1627
|
+
RCTFabricSurface *surface = object;
|
|
1628
|
+
if (surface.stage != RCTSurfaceStageRunning) {
|
|
1629
|
+
return NO;
|
|
1630
|
+
}
|
|
1631
|
+
id view = nil;
|
|
1632
|
+
@try {
|
|
1633
|
+
view = [surface valueForKey:@"_view"];
|
|
1634
|
+
} @catch (NSException *exception) {
|
|
1635
|
+
return NO;
|
|
1636
|
+
}
|
|
1637
|
+
return [view isKindOfClass:[UIView class]] && ((UIView *)view).window != nil;
|
|
1638
|
+
}
|
|
1639
|
+
#endif
|
|
1640
|
+
return NO;
|
|
1641
|
+
}
|