react-native-unistyles 3.3.0 → 3.5.0

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.
Files changed (216) hide show
  1. package/README.md +26 -10
  2. package/android/build.gradle +12 -1
  3. package/android/src/main/cxx/NativeUnistylesModule.cpp +8 -4
  4. package/android/src/main/cxx/NativeUnistylesModule.h +5 -1
  5. package/components/native/ActivityIndicator/package.json +1 -1
  6. package/cxx/common/UnistylesConstants.h +1 -0
  7. package/cxx/converters/TransformOriginConverter.cpp +106 -0
  8. package/cxx/core/StyleSheetRegistry.cpp +9 -9
  9. package/cxx/core/StyleSheetRegistry.h +3 -3
  10. package/cxx/core/UnistyleWrapper.h +13 -1
  11. package/cxx/core/UnistylesRegistry.cpp +226 -54
  12. package/cxx/core/UnistylesRegistry.h +64 -7
  13. package/cxx/hybridObjects/HybridShadowRegistry.cpp +128 -11
  14. package/cxx/hybridObjects/HybridShadowRegistry.h +20 -0
  15. package/cxx/hybridObjects/HybridStyleSheet.cpp +20 -12
  16. package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +11 -1
  17. package/cxx/hybridObjects/HybridUnistylesRuntime.h +4 -2
  18. package/cxx/parser/Parser.cpp +30 -3
  19. package/cxx/parser/Parser.h +1 -0
  20. package/cxx/shadowTree/NativeProps.h +88 -0
  21. package/cxx/shadowTree/ShadowLeafUpdate.h +8 -0
  22. package/cxx/shadowTree/ShadowTrafficController.h +18 -35
  23. package/cxx/shadowTree/ShadowTreeDiagnostics.cpp +339 -0
  24. package/cxx/shadowTree/ShadowTreeDiagnostics.h +18 -0
  25. package/cxx/shadowTree/ShadowTreeManager.cpp +14 -148
  26. package/cxx/shadowTree/ShadowTreeManager.h +2 -6
  27. package/diagnostics/package.json +6 -0
  28. package/ios/UnistylesModuleOnLoad.mm +9 -3
  29. package/lib/commonjs/components/native/ActivityIndicator.native.js +10 -0
  30. package/lib/commonjs/components/native/ActivityIndicator.native.js.map +1 -0
  31. package/lib/commonjs/components/native/Animated.js +2 -0
  32. package/lib/commonjs/components/native/Animated.js.map +1 -1
  33. package/lib/commonjs/components/native/NativeText.js +6 -0
  34. package/lib/commonjs/components/native/NativeText.js.map +1 -1
  35. package/lib/commonjs/components/native/NativeText.native.js +17 -1
  36. package/lib/commonjs/components/native/NativeText.native.js.map +1 -1
  37. package/lib/commonjs/components/native/NativeView.js +6 -0
  38. package/lib/commonjs/components/native/NativeView.js.map +1 -1
  39. package/lib/commonjs/components/native/NativeView.native.js +22 -3
  40. package/lib/commonjs/components/native/NativeView.native.js.map +1 -1
  41. package/lib/commonjs/components/native/Pressable.js +9 -3
  42. package/lib/commonjs/components/native/Pressable.js.map +1 -1
  43. package/lib/commonjs/components/native/Pressable.native.js +2 -0
  44. package/lib/commonjs/components/native/Pressable.native.js.map +1 -1
  45. package/lib/commonjs/components/native/TouchableHighlight.native.js +10 -0
  46. package/lib/commonjs/components/native/TouchableHighlight.native.js.map +1 -0
  47. package/lib/commonjs/core/createUnistylesActivityIndicator.js +41 -0
  48. package/lib/commonjs/core/createUnistylesActivityIndicator.js.map +1 -0
  49. package/lib/commonjs/core/createUnistylesElement.js +59 -14
  50. package/lib/commonjs/core/createUnistylesElement.js.map +1 -1
  51. package/lib/commonjs/core/createUnistylesTouchableHighlight.js +58 -0
  52. package/lib/commonjs/core/createUnistylesTouchableHighlight.js.map +1 -0
  53. package/lib/commonjs/core/index.js +14 -0
  54. package/lib/commonjs/core/index.js.map +1 -1
  55. package/lib/commonjs/diagnostics/index.js +13 -0
  56. package/lib/commonjs/diagnostics/index.js.map +1 -0
  57. package/lib/commonjs/diagnostics/types.js +2 -0
  58. package/lib/commonjs/diagnostics/types.js.map +1 -0
  59. package/lib/commonjs/diagnostics/verify.js +19 -0
  60. package/lib/commonjs/diagnostics/verify.js.map +1 -0
  61. package/lib/commonjs/diagnostics/verify.native.js +12 -0
  62. package/lib/commonjs/diagnostics/verify.native.js.map +1 -0
  63. package/lib/commonjs/index.js +38 -0
  64. package/lib/commonjs/index.js.map +1 -1
  65. package/lib/commonjs/mocks.js +41 -0
  66. package/lib/commonjs/mocks.js.map +1 -1
  67. package/lib/commonjs/reanimated/variant/useUpdateVariantColor.native.js +8 -0
  68. package/lib/commonjs/reanimated/variant/useUpdateVariantColor.native.js.map +1 -1
  69. package/lib/commonjs/specs/ShadowRegistry/index.js +97 -1
  70. package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
  71. package/lib/commonjs/specs/StyleSheet/index.js +5 -1
  72. package/lib/commonjs/specs/StyleSheet/index.js.map +1 -1
  73. package/lib/commonjs/specs/UnistylesRuntime/index.js.map +1 -1
  74. package/lib/commonjs/web/convert/object/boxShadow.js +2 -2
  75. package/lib/commonjs/web/convert/object/boxShadow.js.map +1 -1
  76. package/lib/commonjs/web/index.js.map +1 -1
  77. package/lib/commonjs/web/registry.js +3 -0
  78. package/lib/commonjs/web/registry.js.map +1 -1
  79. package/lib/module/components/native/ActivityIndicator.native.js +6 -0
  80. package/lib/module/components/native/ActivityIndicator.native.js.map +1 -0
  81. package/lib/module/components/native/Animated.js +3 -0
  82. package/lib/module/components/native/Animated.js.map +1 -1
  83. package/lib/module/components/native/NativeText.js +1 -1
  84. package/lib/module/components/native/NativeText.js.map +1 -1
  85. package/lib/module/components/native/NativeText.native.js +15 -1
  86. package/lib/module/components/native/NativeText.native.js.map +1 -1
  87. package/lib/module/components/native/NativeView.js +1 -0
  88. package/lib/module/components/native/NativeView.js.map +1 -1
  89. package/lib/module/components/native/NativeView.native.js +19 -2
  90. package/lib/module/components/native/NativeView.native.js.map +1 -1
  91. package/lib/module/components/native/Pressable.js +9 -3
  92. package/lib/module/components/native/Pressable.js.map +1 -1
  93. package/lib/module/components/native/Pressable.native.js +2 -0
  94. package/lib/module/components/native/Pressable.native.js.map +1 -1
  95. package/lib/module/components/native/TouchableHighlight.native.js +6 -0
  96. package/lib/module/components/native/TouchableHighlight.native.js.map +1 -0
  97. package/lib/module/core/createUnistylesActivityIndicator.js +35 -0
  98. package/lib/module/core/createUnistylesActivityIndicator.js.map +1 -0
  99. package/lib/module/core/createUnistylesElement.js +58 -13
  100. package/lib/module/core/createUnistylesElement.js.map +1 -1
  101. package/lib/module/core/createUnistylesTouchableHighlight.js +52 -0
  102. package/lib/module/core/createUnistylesTouchableHighlight.js.map +1 -0
  103. package/lib/module/core/index.js +2 -0
  104. package/lib/module/core/index.js.map +1 -1
  105. package/lib/module/diagnostics/index.js +5 -0
  106. package/lib/module/diagnostics/index.js.map +1 -0
  107. package/lib/module/diagnostics/types.js +2 -0
  108. package/lib/module/diagnostics/types.js.map +1 -0
  109. package/lib/module/diagnostics/verify.js +14 -0
  110. package/lib/module/diagnostics/verify.js.map +1 -0
  111. package/lib/module/diagnostics/verify.native.js +8 -0
  112. package/lib/module/diagnostics/verify.native.js.map +1 -0
  113. package/lib/module/index.js +2 -0
  114. package/lib/module/index.js.map +1 -1
  115. package/lib/module/mocks.js +41 -0
  116. package/lib/module/mocks.js.map +1 -1
  117. package/lib/module/reanimated/variant/useUpdateVariantColor.native.js +9 -1
  118. package/lib/module/reanimated/variant/useUpdateVariantColor.native.js.map +1 -1
  119. package/lib/module/specs/ShadowRegistry/index.js +97 -1
  120. package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
  121. package/lib/module/specs/StyleSheet/index.js +6 -1
  122. package/lib/module/specs/StyleSheet/index.js.map +1 -1
  123. package/lib/module/specs/UnistylesRuntime/index.js.map +1 -1
  124. package/lib/module/web/convert/object/boxShadow.js +2 -2
  125. package/lib/module/web/convert/object/boxShadow.js.map +1 -1
  126. package/lib/module/web/index.js.map +1 -1
  127. package/lib/module/web/registry.js +3 -0
  128. package/lib/module/web/registry.js.map +1 -1
  129. package/lib/typescript/src/components/native/ActivityIndicator.native.d.ts +2 -0
  130. package/lib/typescript/src/components/native/ActivityIndicator.native.d.ts.map +1 -0
  131. package/lib/typescript/src/components/native/Animated.d.ts +16 -36
  132. package/lib/typescript/src/components/native/Animated.d.ts.map +1 -1
  133. package/lib/typescript/src/components/native/NativeText.d.ts +1 -1
  134. package/lib/typescript/src/components/native/NativeText.d.ts.map +1 -1
  135. package/lib/typescript/src/components/native/NativeText.native.d.ts.map +1 -1
  136. package/lib/typescript/src/components/native/NativeView.d.ts +1 -0
  137. package/lib/typescript/src/components/native/NativeView.d.ts.map +1 -1
  138. package/lib/typescript/src/components/native/NativeView.native.d.ts +2 -2
  139. package/lib/typescript/src/components/native/NativeView.native.d.ts.map +1 -1
  140. package/lib/typescript/src/components/native/Pressable.d.ts +4 -2
  141. package/lib/typescript/src/components/native/Pressable.d.ts.map +1 -1
  142. package/lib/typescript/src/components/native/Pressable.native.d.ts +5 -2
  143. package/lib/typescript/src/components/native/Pressable.native.d.ts.map +1 -1
  144. package/lib/typescript/src/components/native/TouchableHighlight.native.d.ts +2 -0
  145. package/lib/typescript/src/components/native/TouchableHighlight.native.d.ts.map +1 -0
  146. package/lib/typescript/src/core/createUnistylesActivityIndicator.d.ts +3 -0
  147. package/lib/typescript/src/core/createUnistylesActivityIndicator.d.ts.map +1 -0
  148. package/lib/typescript/src/core/createUnistylesElement.d.ts.map +1 -1
  149. package/lib/typescript/src/core/createUnistylesTouchableHighlight.d.ts +3 -0
  150. package/lib/typescript/src/core/createUnistylesTouchableHighlight.d.ts.map +1 -0
  151. package/lib/typescript/src/core/index.d.ts +2 -0
  152. package/lib/typescript/src/core/index.d.ts.map +1 -1
  153. package/lib/typescript/src/core/useUnistyles.d.ts +1 -1
  154. package/lib/typescript/src/diagnostics/index.d.ts +3 -0
  155. package/lib/typescript/src/diagnostics/index.d.ts.map +1 -0
  156. package/lib/typescript/src/diagnostics/types.d.ts +19 -0
  157. package/lib/typescript/src/diagnostics/types.d.ts.map +1 -0
  158. package/lib/typescript/src/diagnostics/verify.d.ts +3 -0
  159. package/lib/typescript/src/diagnostics/verify.d.ts.map +1 -0
  160. package/lib/typescript/src/diagnostics/verify.native.d.ts +3 -0
  161. package/lib/typescript/src/diagnostics/verify.native.d.ts.map +1 -0
  162. package/lib/typescript/src/index.d.ts +4 -1
  163. package/lib/typescript/src/index.d.ts.map +1 -1
  164. package/lib/typescript/src/reanimated/variant/useUpdateVariantColor.native.d.ts.map +1 -1
  165. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts +5 -1
  166. package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
  167. package/lib/typescript/src/specs/ShadowRegistry/types.d.ts +3 -0
  168. package/lib/typescript/src/specs/ShadowRegistry/types.d.ts.map +1 -1
  169. package/lib/typescript/src/specs/StyleSheet/index.d.ts +4 -1
  170. package/lib/typescript/src/specs/StyleSheet/index.d.ts.map +1 -1
  171. package/lib/typescript/src/specs/UnistylesRuntime/index.d.ts +2 -2
  172. package/lib/typescript/src/specs/UnistylesRuntime/index.d.ts.map +1 -1
  173. package/lib/typescript/src/types/breakpoints.d.ts +1 -1
  174. package/lib/typescript/src/types/breakpoints.d.ts.map +1 -1
  175. package/lib/typescript/src/types/core.d.ts +7 -2
  176. package/lib/typescript/src/types/core.d.ts.map +1 -1
  177. package/lib/typescript/src/types/stylesheet.d.ts +3 -3
  178. package/lib/typescript/src/types/stylesheet.d.ts.map +1 -1
  179. package/lib/typescript/src/web/index.d.ts.map +1 -1
  180. package/lib/typescript/src/web/registry.d.ts.map +1 -1
  181. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_UnistylesNativeMiniRuntime.kt +0 -2
  182. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_std__vector_UnistyleDependency__UnistylesNativeMiniRuntime.kt +0 -2
  183. package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/HybridNativePlatformSpec.kt +2 -0
  184. package/package.json +35 -26
  185. package/plugin/index.d.ts +4 -4
  186. package/plugin/index.js +21 -5
  187. package/src/components/native/ActivityIndicator.native.tsx +5 -0
  188. package/src/components/native/Animated.tsx +18 -1
  189. package/src/components/native/NativeText.native.tsx +18 -1
  190. package/src/components/native/NativeText.tsx +1 -1
  191. package/src/components/native/NativeView.native.tsx +22 -2
  192. package/src/components/native/NativeView.tsx +2 -0
  193. package/src/components/native/Pressable.native.tsx +10 -2
  194. package/src/components/native/Pressable.tsx +15 -4
  195. package/src/components/native/TouchableHighlight.native.tsx +5 -0
  196. package/src/core/createUnistylesActivityIndicator.tsx +47 -0
  197. package/src/core/createUnistylesElement.tsx +45 -14
  198. package/src/core/createUnistylesTouchableHighlight.tsx +69 -0
  199. package/src/core/index.ts +2 -0
  200. package/src/diagnostics/index.ts +3 -0
  201. package/src/diagnostics/types.ts +25 -0
  202. package/src/diagnostics/verify.native.ts +7 -0
  203. package/src/diagnostics/verify.ts +13 -0
  204. package/src/index.ts +4 -1
  205. package/src/mocks.ts +41 -0
  206. package/src/reanimated/variant/useUpdateVariantColor.native.ts +7 -1
  207. package/src/specs/ShadowRegistry/index.ts +128 -1
  208. package/src/specs/ShadowRegistry/types.ts +3 -0
  209. package/src/specs/StyleSheet/index.ts +10 -2
  210. package/src/specs/UnistylesRuntime/index.ts +7 -2
  211. package/src/types/breakpoints.ts +5 -3
  212. package/src/types/core.ts +7 -28
  213. package/src/types/stylesheet.ts +3 -3
  214. package/src/web/convert/object/boxShadow.ts +2 -2
  215. package/src/web/index.ts +2 -1
  216. package/src/web/registry.ts +3 -0
@@ -1,6 +1,7 @@
1
1
  #include "UnistylesRegistry.h"
2
2
  #include "UnistylesState.h"
3
3
  #include "Parser.h"
4
+ #include "NativeProps.h"
4
5
 
5
6
  using namespace margelo::nitro::unistyles;
6
7
  using namespace facebook;
@@ -8,7 +9,13 @@ using namespace facebook::react;
8
9
 
9
10
  std::atomic<int> core::UnistylesRegistry::_nextStyleSheetTag{0};
10
11
 
11
- void core::UnistylesRegistry::registerTheme(jsi::Runtime& rt, std::string name, jsi::Value& theme) {
12
+ void core::UnistylesRegistry::registerTheme(jsi::Runtime& rt, uint64_t generation, std::string name, jsi::Value& theme) {
13
+ std::lock_guard<std::mutex> lock(this->_ownershipMutex);
14
+
15
+ if (!this->isCurrent(generation)) {
16
+ throw std::runtime_error(helpers::RUNTIME_REPLACED_ERROR);
17
+ }
18
+
12
19
  auto& state = this->getState();
13
20
 
14
21
  state._jsThemes.emplace(name, std::move(theme));
@@ -45,7 +52,7 @@ void core::UnistylesRegistry::createState() {
45
52
  this->_state = std::make_unique<UnistylesState>();
46
53
  }
47
54
 
48
- void core::UnistylesRegistry::updateTheme(jsi::Runtime& rt, std::string& themeName, jsi::Function&& callback) {
55
+ void core::UnistylesRegistry::updateTheme(jsi::Runtime& rt, uint64_t generation, std::string& themeName, jsi::Function&& callback) {
49
56
  auto& state = this->getState();
50
57
  auto it = state._jsThemes.find(themeName);
51
58
 
@@ -55,42 +62,103 @@ void core::UnistylesRegistry::updateTheme(jsi::Runtime& rt, std::string& themeNa
55
62
 
56
63
  helpers::assertThat(rt, result.isObject(), "Unistyles: Returned theme is not an object. Please check your updateTheme function.");
57
64
 
58
- it->second = result.asObject(rt);
65
+ auto theme = result.asObject(rt);
66
+ std::lock_guard<std::mutex> lock(this->_ownershipMutex);
67
+
68
+ if (!this->isCurrent(generation)) {
69
+ throw std::runtime_error(helpers::RUNTIME_REPLACED_ERROR);
70
+ }
71
+
72
+ it->second = std::move(theme);
59
73
  }
60
74
 
61
75
  void core::UnistylesRegistry::linkShadowNodeWithUnistyle(
62
76
  jsi::Runtime& rt,
63
- const ShadowNodeFamily* shadowNodeFamily,
77
+ uint64_t generation,
78
+ const std::shared_ptr<const ShadowNodeFamily>& shadowNodeFamily,
64
79
  std::vector<std::shared_ptr<UnistyleData>>& unistylesData,
65
80
  std::optional<folly::dynamic> initialScopedUpdate
66
81
  ) {
67
- this->trafficController.withLock([this, &rt, &unistylesData, shadowNodeFamily, &initialScopedUpdate](){
82
+ // released after unlocking, so a family destructor never runs within the lock
83
+ ReleasedFamilies releasedFamilies;
84
+ std::lock_guard<std::mutex> lock(this->_ownershipMutex);
85
+
86
+ if (!this->isCurrent(generation)) {
87
+ return;
88
+ }
89
+
90
+ this->trafficController.withLock([this, &unistylesData, &shadowNodeFamily, &initialScopedUpdate, &releasedFamilies](){
91
+ auto family = shadowNodeFamily.get();
92
+ auto it = this->_shadowRegistry.find(family);
93
+
68
94
  // Clear suspension state if this family was previously suspended
69
- if (_suspendedFamilies.erase(shadowNodeFamily) > 0) {
70
- auto* mutableFamily = const_cast<ShadowNodeFamily*>(shadowNodeFamily);
71
- mutableFamily->nativeProps_DEPRECATED.reset();
95
+ // native props stay, they hold values from theme changes that React doesn't know about
96
+ if (it != this->_shadowRegistry.end() && it->second.isSuspended) {
72
97
  // Clear old registry entries to prevent stale UnistyleData accumulation
73
- this->_shadowRegistry.erase(shadowNodeFamily);
74
- // Remove any stale traffic controller entry (e.g. from a theme change during suspension)
75
- this->trafficController.removeShadowNode(shadowNodeFamily);
98
+ // and any stale traffic controller entry (e.g. from a theme change during suspension)
99
+ this->forgetFamilyUnsafe(family, releasedFamilies);
76
100
  }
77
101
 
78
- std::for_each(unistylesData.begin(), unistylesData.end(), [this, shadowNodeFamily](std::shared_ptr<UnistyleData> unistyleData){
79
- this->_shadowRegistry[shadowNodeFamily].emplace_back(unistyleData);
80
- });
102
+ auto& linkedFamily = this->_shadowRegistry[family];
103
+
104
+ linkedFamily.family = shadowNodeFamily;
105
+ linkedFamily.unistyles.insert(linkedFamily.unistyles.end(), unistylesData.begin(), unistylesData.end());
81
106
 
82
107
  // Required for scoped themes to apply on initial mount
83
108
  if (initialScopedUpdate.has_value()) {
84
- shadow::ShadowLeafUpdates updates;
109
+ this->trafficController.setUpdate(shadowNodeFamily, std::move(*initialScopedUpdate));
110
+ }
85
111
 
86
- updates.emplace(shadowNodeFamily, std::move(*initialScopedUpdate));
87
- this->trafficController.setUpdates(updates);
112
+ // families unmounted without unlink (eg. while frozen) are pinned by us only
113
+ // sweep them once the registry doubles, so it's amortized O(1) per link
114
+ if (this->_shadowRegistry.size() >= this->_sweepThreshold) {
115
+ this->sweepUnownedFamiliesUnsafe(releasedFamilies);
116
+ this->_sweepThreshold = std::max(MIN_SWEEP_THRESHOLD, this->_shadowRegistry.size() * 2);
88
117
  }
89
118
  });
90
119
  }
91
120
 
121
+ bool core::UnistylesRegistry::isOwnedOnlyByUnistyles(const LinkedFamily& linkedFamily) noexcept {
122
+ // shadow nodes own their family, so if nothing but our pin owns it, the family is not part of any
123
+ // shadow tree anymore. React Native can't get it back, as it only keeps weak references beside shadow nodes
124
+ return linkedFamily.family.use_count() == 1;
125
+ }
126
+
127
+ void core::UnistylesRegistry::forgetFamilyUnsafe(const ShadowNodeFamily* shadowNodeFamily, ReleasedFamilies& releasedFamilies) {
128
+ auto it = this->_shadowRegistry.find(shadowNodeFamily);
129
+
130
+ if (it != this->_shadowRegistry.end()) {
131
+ releasedFamilies.emplace_back(std::move(it->second.family));
132
+ this->_shadowRegistry.erase(it);
133
+ }
134
+
135
+ if (auto pendingFamily = this->trafficController.removeShadowNode(shadowNodeFamily)) {
136
+ releasedFamilies.emplace_back(std::move(pendingFamily));
137
+ }
138
+ }
139
+
140
+ void core::UnistylesRegistry::sweepUnownedFamiliesUnsafe(ReleasedFamilies& releasedFamilies) {
141
+ std::vector<const ShadowNodeFamily*> unownedFamilies;
142
+
143
+ for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
144
+ if (isOwnedOnlyByUnistyles(linkedFamily)) {
145
+ unownedFamilies.emplace_back(family);
146
+ }
147
+ }
148
+
149
+ for (const auto* family : unownedFamilies) {
150
+ this->forgetFamilyUnsafe(family, releasedFamilies);
151
+ }
152
+ }
153
+
92
154
  void core::UnistylesRegistry::removeDuplicatedUnistyles(const ShadowNodeFamily *shadowNodeFamily, std::vector<core::Unistyle::Shared>& unistyles) {
93
- auto targetFamilyUnistyles = this->_shadowRegistry[shadowNodeFamily];
155
+ auto it = this->_shadowRegistry.find(shadowNodeFamily);
156
+
157
+ if (it == this->_shadowRegistry.end()) {
158
+ return;
159
+ }
160
+
161
+ auto& targetFamilyUnistyles = it->second.unistyles;
94
162
 
95
163
  unistyles.erase(
96
164
  std::remove_if(
@@ -111,26 +179,36 @@ void core::UnistylesRegistry::removeDuplicatedUnistyles(const ShadowNodeFamily *
111
179
  }
112
180
 
113
181
  void core::UnistylesRegistry::unlinkShadowNodeWithUnistyles(const ShadowNodeFamily* shadowNodeFamily) {
114
- this->trafficController.withLock([this, shadowNodeFamily](){
115
- this->_shadowRegistry.erase(shadowNodeFamily);
116
- this->_suspendedFamilies.erase(shadowNodeFamily);
117
- this->trafficController.removeShadowNode(shadowNodeFamily);
182
+ ReleasedFamilies releasedFamilies;
183
+
184
+ this->trafficController.withLock([this, shadowNodeFamily, &releasedFamilies](){
185
+ this->forgetFamilyUnsafe(shadowNodeFamily, releasedFamilies);
118
186
  });
119
187
  }
120
188
 
121
189
  void core::UnistylesRegistry::suspendShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
122
190
  this->trafficController.withLock([this, shadowNodeFamily](){
123
- if (this->_shadowRegistry.contains(shadowNodeFamily)) {
124
- this->_suspendedFamilies.insert(shadowNodeFamily);
191
+ auto it = this->_shadowRegistry.find(shadowNodeFamily);
192
+
193
+ if (it != this->_shadowRegistry.end()) {
194
+ it->second.isSuspended = true;
125
195
  }
126
196
  });
127
197
  }
128
198
 
129
199
  bool core::UnistylesRegistry::isSuspended(const ShadowNodeFamily* family) const noexcept {
130
- return _suspendedFamilies.count(family) > 0;
200
+ auto it = this->_shadowRegistry.find(family);
201
+
202
+ return it != this->_shadowRegistry.end() && it->second.isSuspended;
131
203
  }
132
204
 
133
- std::shared_ptr<core::StyleSheet> core::UnistylesRegistry::addStyleSheet(jsi::Runtime& rt, core::StyleSheetType type, jsi::Object&& rawValue) {
205
+ std::shared_ptr<core::StyleSheet> core::UnistylesRegistry::addStyleSheet(jsi::Runtime& rt, uint64_t generation, core::StyleSheetType type, jsi::Object&& rawValue) {
206
+ std::lock_guard<std::mutex> lock(this->_ownershipMutex);
207
+
208
+ if (!this->isCurrent(generation)) {
209
+ throw std::runtime_error(helpers::RUNTIME_REPLACED_ERROR);
210
+ }
211
+
134
212
  int tag = _nextStyleSheetTag.fetch_add(1);
135
213
 
136
214
  auto sheet = std::make_shared<core::StyleSheet>(tag, type, std::move(rawValue));
@@ -143,36 +221,56 @@ core::DependencyMap core::UnistylesRegistry::buildDependencyMap(std::vector<Unis
143
221
  core::DependencyMap dependencyMap;
144
222
 
145
223
  std::unordered_set<UnistyleDependency> uniqueDependencies(deps.begin(), deps.end());
224
+ ReleasedFamilies releasedFamilies;
225
+
226
+ // families stay pinned by the registry until the shadow tree update is committed
227
+ this->trafficController.withLock([this, &dependencyMap, &uniqueDependencies, &releasedFamilies](){
228
+ // we iterate whole registry anyway, so it's a free moment to drop families unmounted without unlink
229
+ this->sweepUnownedFamiliesUnsafe(releasedFamilies);
230
+
231
+ for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
232
+ bool hasAnyOfDependencies = false;
233
+
234
+ // Check if any dependency matches
235
+ for (const auto& unistyleData : linkedFamily.unistyles) {
236
+ for (const auto& dep : unistyleData->unistyle->dependencies) {
237
+ if (uniqueDependencies.count(dep)) {
238
+ hasAnyOfDependencies = true;
239
+ break;
240
+ }
241
+ }
146
242
 
147
- for (const auto& [family, unistyles] : this->_shadowRegistry) {
148
- bool hasAnyOfDependencies = false;
149
-
150
- // Check if any dependency matches
151
- for (const auto& unistyleData : unistyles) {
152
- for (const auto& dep : unistyleData->unistyle->dependencies) {
153
- if (uniqueDependencies.count(dep)) {
154
- hasAnyOfDependencies = true;
243
+ if (hasAnyOfDependencies) {
155
244
  break;
156
- }
245
+ };
246
+ }
247
+
248
+ if (!hasAnyOfDependencies) {
249
+ continue;
157
250
  }
158
251
 
159
- if (hasAnyOfDependencies) {
160
- break;
161
- };
252
+ dependencyMap[family].insert(
253
+ dependencyMap[family].end(),
254
+ linkedFamily.unistyles.begin(),
255
+ linkedFamily.unistyles.end()
256
+ );
162
257
  }
258
+ });
259
+
260
+ return dependencyMap;
261
+ }
262
+
263
+ void core::UnistylesRegistry::queueShadowLeafUpdatesUnsafe(shadow::ShadowLeafUpdates& updates) {
264
+ for (auto& [family, props] : updates) {
265
+ auto it = this->_shadowRegistry.find(family);
163
266
 
164
- if (!hasAnyOfDependencies) {
267
+ // family was unlinked in the meantime, there is nothing to update
268
+ if (it == this->_shadowRegistry.end()) {
165
269
  continue;
166
270
  }
167
271
 
168
- dependencyMap[family].insert(
169
- dependencyMap[family].end(),
170
- unistyles.begin(),
171
- unistyles.end()
172
- );
272
+ this->trafficController.setUpdate(it->second.family, std::move(props));
173
273
  }
174
-
175
- return dependencyMap;
176
274
  }
177
275
 
178
276
  // called from proxied function only, we don't know host
@@ -185,15 +283,15 @@ void core::UnistylesRegistry::shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Uni
185
283
  ? std::make_optional(maybePressableId.asString(rt).utf8(rt))
186
284
  : std::nullopt;
187
285
 
188
- for (const auto& [family, unistyles] : this->_shadowRegistry) {
189
- for (const auto& unistyleData : unistyles) {
286
+ for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
287
+ for (const auto& unistyleData : linkedFamily.unistyles) {
190
288
  if (unistyleData->unistyle == unistyle) {
191
289
  updates[family] = parser.parseStylesToShadowTreeStyles(rt, { unistyleData });
192
290
  }
193
291
  }
194
292
  }
195
293
 
196
- this->trafficController.setUpdates(updates);
294
+ this->queueShadowLeafUpdatesUnsafe(updates);
197
295
  });
198
296
  }
199
297
 
@@ -262,11 +360,85 @@ void core::UnistylesRegistry::setScopedTheme(std::optional<std::string> themeNam
262
360
  this->_scopedTheme = std::move(themeName);
263
361
  }
264
362
 
265
- void core::UnistylesRegistry::destroy() {
266
- this->_state.reset();
267
- this->_styleSheetRegistry.clear();
268
- this->_shadowRegistry.clear();
269
- this->_suspendedFamilies.clear();
363
+ uint64_t core::UnistylesRegistry::takeOwnership() {
364
+ std::lock_guard<std::mutex> lock(this->_ownershipMutex);
365
+ auto previousGeneration = this->_generation.load();
366
+
367
+ if (previousGeneration != 0) {
368
+ this->_replacedStates[previousGeneration] = this->takeOwnedState();
369
+ }
370
+
371
+ this->_generation.store(++this->_lastGeneration);
372
+
373
+ return this->_lastGeneration;
374
+ }
375
+
376
+ void core::UnistylesRegistry::releaseOwnership(uint64_t generation) {
377
+ // called when module is invalidated, before React Native destroys the runtime
378
+ // declared before the lock, so jsi values and pins are released after unlocking
379
+ OwnedState releasedState;
380
+
381
+ {
382
+ std::lock_guard<std::mutex> lock(this->_ownershipMutex);
383
+
384
+ if (this->isCurrent(generation)) {
385
+ releasedState = this->takeOwnedState();
386
+ this->_generation.store(0);
387
+ } else if (auto it = this->_replacedStates.find(generation); it != this->_replacedStates.end()) {
388
+ releasedState = std::move(it->second);
389
+ this->_replacedStates.erase(it);
390
+ }
391
+ }
392
+
393
+ for (auto& [_, styleSheet] : releasedState.styleSheets) {
394
+ styleSheet->unistyles.clear();
395
+ }
396
+ }
397
+
398
+ core::UnistylesRegistry::OwnedState core::UnistylesRegistry::takeOwnedState() {
399
+ OwnedState ownedState;
400
+
401
+ this->trafficController.withLock([this, &ownedState](){
402
+ ownedState.shadowRegistry = std::exchange(this->_shadowRegistry, {});
403
+ ownedState.updates = this->trafficController.takeUpdates();
404
+ this->_sweepThreshold = MIN_SWEEP_THRESHOLD;
405
+ });
406
+
407
+ ownedState.styleSheets = std::exchange(this->_styleSheetRegistry, {});
408
+ ownedState.state = std::move(this->_state);
270
409
  this->_scopedTheme = std::nullopt;
410
+ this->_committedTags.clear();
271
411
  _nextStyleSheetTag.store(0);
412
+
413
+ return ownedState;
414
+ }
415
+
416
+ std::vector<core::LinkedFamilySnapshot> core::UnistylesRegistry::getLinkedFamiliesSnapshot() {
417
+ std::vector<LinkedFamilySnapshot> snapshot;
418
+
419
+ this->trafficController.withLock([this, &snapshot](){
420
+ snapshot.reserve(this->_shadowRegistry.size());
421
+
422
+ for (const auto& [_, linkedFamily] : this->_shadowRegistry) {
423
+ const bool isOwnedOnlyByUnistyles = this->isOwnedOnlyByUnistyles(linkedFamily);
424
+
425
+ snapshot.push_back({linkedFamily.family, linkedFamily.unistyles, linkedFamily.isSuspended, isOwnedOnlyByUnistyles});
426
+ }
427
+ });
428
+
429
+ return snapshot;
430
+ }
431
+
432
+ size_t core::UnistylesRegistry::getPendingUpdatesCount() {
433
+ return this->trafficController.withLock([this](){
434
+ return this->trafficController.getUpdatesCount();
435
+ });
436
+ }
437
+
438
+ void core::UnistylesRegistry::setCommittedTags(std::vector<Tag>&& tags) {
439
+ this->_committedTags = std::move(tags);
440
+ }
441
+
442
+ std::vector<Tag> core::UnistylesRegistry::takeCommittedTags() {
443
+ return std::exchange(this->_committedTags, {});
272
444
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include "set"
4
4
  #include <atomic>
5
+ #include <mutex>
5
6
  #include <jsi/jsi.h>
6
7
  #include <folly/dynamic.h>
7
8
  #include <react/renderer/uimanager/UIManager.h>
@@ -23,6 +24,19 @@ using namespace facebook::react;
23
24
 
24
25
  using DependencyMap = std::unordered_map<const ShadowNodeFamily*, std::vector<std::shared_ptr<UnistyleData>>>;
25
26
 
27
+ struct LinkedFamily {
28
+ std::shared_ptr<const ShadowNodeFamily> family;
29
+ std::vector<std::shared_ptr<UnistyleData>> unistyles;
30
+ bool isSuspended = false;
31
+ };
32
+
33
+ struct LinkedFamilySnapshot {
34
+ std::shared_ptr<const ShadowNodeFamily> family;
35
+ std::vector<std::shared_ptr<UnistyleData>> unistyles;
36
+ bool isSuspended = false;
37
+ bool isOwnedOnlyByUnistyles = false;
38
+ };
39
+
26
40
  struct UnistylesRegistry: public StyleSheetRegistry {
27
41
  static UnistylesRegistry& get();
28
42
 
@@ -31,38 +45,81 @@ struct UnistylesRegistry: public StyleSheetRegistry {
31
45
 
32
46
  bool shouldUsePointsForBreakpoints = false;
33
47
 
34
- void registerTheme(jsi::Runtime& rt, std::string name, jsi::Value& theme);
48
+ void registerTheme(jsi::Runtime& rt, uint64_t generation, std::string name, jsi::Value& theme);
35
49
  void registerBreakpoints(std::vector<std::pair<std::string, double>>& sortedBreakpoints);
36
50
  void setPrefersAdaptiveThemes(bool prefersAdaptiveThemes);
37
51
  void setInitialThemeName(std::string themeName);
38
- void updateTheme(jsi::Runtime& rt, std::string& themeName, jsi::Function&& callback);
52
+ void updateTheme(jsi::Runtime& rt, uint64_t generation, std::string& themeName, jsi::Function&& callback);
39
53
 
40
54
  UnistylesState& getState();
41
55
  void createState();
42
56
  std::vector<std::shared_ptr<core::StyleSheet>> getStyleSheetsToRefresh(std::vector<UnistyleDependency>& unistylesDependencies);
43
- void linkShadowNodeWithUnistyle(jsi::Runtime& rt, const ShadowNodeFamily*, std::vector<std::shared_ptr<UnistyleData>>& unistylesData, std::optional<folly::dynamic> initialScopedUpdate = std::nullopt);
57
+ void linkShadowNodeWithUnistyle(jsi::Runtime& rt, uint64_t generation, const std::shared_ptr<const ShadowNodeFamily>& shadowNodeFamily, std::vector<std::shared_ptr<UnistyleData>>& unistylesData, std::optional<folly::dynamic> initialScopedUpdate = std::nullopt);
44
58
  void unlinkShadowNodeWithUnistyles(const ShadowNodeFamily*);
45
59
  void suspendShadowNode(const ShadowNodeFamily*);
46
60
  bool isSuspended(const ShadowNodeFamily*) const noexcept;
47
- std::shared_ptr<core::StyleSheet> addStyleSheet(jsi::Runtime& rt, core::StyleSheetType type, jsi::Object&& rawValue);
61
+ std::shared_ptr<core::StyleSheet> addStyleSheet(jsi::Runtime& rt, uint64_t generation, core::StyleSheetType type, jsi::Object&& rawValue);
48
62
  DependencyMap buildDependencyMap(std::vector<UnistyleDependency>& deps);
49
63
  void shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Unistyle::Shared unistyle, jsi::Value& maybePressableId);
64
+ // call it only within trafficController.withLock!
65
+ void queueShadowLeafUpdatesUnsafe(shadow::ShadowLeafUpdates& updates);
50
66
  shadow::ShadowTrafficController trafficController{};
51
67
  const std::optional<std::string> getScopedTheme();
52
68
  void removeDuplicatedUnistyles(const ShadowNodeFamily* shadowNodeFamily, std::vector<core::Unistyle::Shared>& unistyles);
53
69
  void setScopedTheme(std::optional<std::string> themeName);
54
70
  core::Unistyle::Shared getUnistyleById(std::string unistyleID);
55
- void destroy();
71
+ std::vector<LinkedFamilySnapshot> getLinkedFamiliesSnapshot();
72
+ size_t getPendingUpdatesCount();
73
+ void setCommittedTags(std::vector<Tag>&& tags);
74
+ std::vector<Tag> takeCommittedTags();
75
+
76
+ // newest installed runtime owns the registry, generation 0 means no owner
77
+ // previous runtime's state is kept aside until it releases ownership, as it may still run JS
78
+ uint64_t takeOwnership();
79
+ void releaseOwnership(uint64_t generation);
80
+
81
+ inline bool isCurrent(uint64_t generation) const noexcept {
82
+ return generation != 0 && generation == this->_generation.load();
83
+ }
56
84
 
57
85
  private:
86
+ using ReleasedFamilies = std::vector<std::shared_ptr<const ShadowNodeFamily>>;
87
+
88
+ struct OwnedState {
89
+ std::unordered_map<const ShadowNodeFamily*, LinkedFamily> shadowRegistry;
90
+ shadow::PinnedShadowLeafUpdates updates;
91
+ std::unordered_map<int, std::shared_ptr<core::StyleSheet>> styleSheets;
92
+ std::unique_ptr<UnistylesState> state;
93
+ };
94
+
58
95
  UnistylesRegistry() = default;
59
96
 
97
+ // call it only within _ownershipMutex!
98
+ // destructs nothing, so it can run on the next runtime's thread
99
+ OwnedState takeOwnedState();
100
+
101
+ // call it only within trafficController.withLock!
102
+ // pins are moved to releasedFamilies, so they can be released after unlocking
103
+ void forgetFamilyUnsafe(const ShadowNodeFamily* shadowNodeFamily, ReleasedFamilies& releasedFamilies);
104
+ void sweepUnownedFamiliesUnsafe(ReleasedFamilies& releasedFamilies);
105
+
106
+ // React Native doesn't own the family anymore, it was unmounted without unlink
107
+ static bool isOwnedOnlyByUnistyles(const LinkedFamily& linkedFamily) noexcept;
108
+
109
+ static constexpr size_t MIN_SWEEP_THRESHOLD = 64;
110
+
111
+ // lock order is _ownershipMutex -> trafficController, never call JS within
112
+ std::mutex _ownershipMutex;
113
+ std::atomic<uint64_t> _generation{0};
114
+ uint64_t _lastGeneration = 0;
115
+ std::unordered_map<uint64_t, OwnedState> _replacedStates{};
60
116
  static std::atomic<int> _nextStyleSheetTag;
61
117
  std::optional<std::string> _scopedTheme{};
62
118
  std::unique_ptr<UnistylesState> _state{};
63
119
  std::unordered_map<int, std::shared_ptr<core::StyleSheet>> _styleSheetRegistry{};
64
- std::unordered_map<const ShadowNodeFamily*, std::vector<std::shared_ptr<UnistyleData>>> _shadowRegistry{};
65
- std::unordered_set<const ShadowNodeFamily*> _suspendedFamilies{};
120
+ std::unordered_map<const ShadowNodeFamily*, LinkedFamily> _shadowRegistry{};
121
+ size_t _sweepThreshold = MIN_SWEEP_THRESHOLD;
122
+ std::vector<Tag> _committedTags{};
66
123
  };
67
124
 
68
125
  inline UnistylesRegistry& UnistylesRegistry::get() {