react-native-worklets 0.8.0-rc.0 → 0.8.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.
- package/Common/cpp/worklets/Compat/StableApi.cpp +121 -0
- package/Common/cpp/worklets/Compat/StableApi.h +109 -0
- package/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp +14 -14
- package/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.h +10 -7
- package/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp +5 -14
- package/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h +3 -5
- package/Common/cpp/worklets/SharedItems/Serializable.h +1 -43
- package/Common/cpp/worklets/Tools/Defs.h +2 -7
- package/Common/cpp/worklets/Tools/ScriptBuffer.h +1 -1
- package/Common/cpp/worklets/WorkletRuntime/BundleModeConfig.h +17 -0
- package/Common/cpp/worklets/WorkletRuntime/HermesProfiling.cpp +73 -0
- package/Common/cpp/worklets/WorkletRuntime/HermesProfiling.h +12 -0
- package/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.cpp +20 -0
- package/Common/cpp/worklets/WorkletRuntime/RuntimeBindings.h +2 -2
- package/Common/cpp/worklets/WorkletRuntime/RuntimeData.h +0 -4
- package/Common/cpp/worklets/WorkletRuntime/RuntimeHolder.h +0 -3
- package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp +27 -17
- package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h +12 -3
- package/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp +74 -2
- package/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.h +0 -4
- package/RNWorklets.podspec +1 -2
- package/android/CMakeLists.txt +1 -4
- package/android/build.gradle +0 -3
- package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.cpp +1 -1
- package/android/src/main/cpp/worklets/android/JWorkletRuntimeWrapper.cpp +2 -2
- package/android/src/main/cpp/worklets/android/JWorkletRuntimeWrapper.h +2 -2
- package/android/src/main/cpp/worklets/android/WorkletsModule.cpp +28 -18
- package/android/src/main/cpp/worklets/android/WorkletsModule.h +6 -5
- package/android/src/main/cpp/worklets/android/WorkletsOnLoad.cpp +2 -2
- package/android/src/networking/com/swmansion/worklets/WorkletsModule.java +4 -2
- package/android/src/no-networking/com/swmansion/worklets/WorkletsModule.java +4 -2
- package/apple/worklets/apple/Networking/WorkletsNetworking.h +5 -2
- package/apple/worklets/apple/Networking/WorkletsNetworking.mm +3 -2
- package/apple/worklets/apple/WorkletsModule.mm +40 -26
- package/compatibility.json +2 -2
- package/lib/module/WorkletsModule/NativeWorklets.native.js +3 -2
- package/lib/module/WorkletsModule/NativeWorklets.native.js.map +1 -1
- package/lib/module/debug/jsVersion.js +1 -1
- package/lib/module/debug/jsVersion.js.map +1 -1
- package/lib/module/featureFlags/staticFlags.json +0 -1
- package/lib/module/featureFlags/types.js +0 -1
- package/lib/module/featureFlags/types.js.map +1 -1
- package/lib/module/memory/serializable.native.js +14 -5
- package/lib/module/memory/serializable.native.js.map +1 -1
- package/lib/module/memory/shareable.js +15 -0
- package/lib/module/memory/shareable.js.map +1 -1
- package/lib/module/memory/shareable.native.js +15 -0
- package/lib/module/memory/shareable.native.js.map +1 -1
- package/lib/module/runtimeKind.js +2 -0
- package/lib/module/runtimeKind.js.map +1 -1
- package/lib/typescript/WorkletsModule/NativeWorklets.native.d.ts.map +1 -1
- package/lib/typescript/debug/jsVersion.d.ts +1 -1
- package/lib/typescript/debug/jsVersion.d.ts.map +1 -1
- package/lib/typescript/featureFlags/types.d.ts +0 -1
- package/lib/typescript/featureFlags/types.d.ts.map +1 -1
- package/lib/typescript/memory/serializable.native.d.ts.map +1 -1
- package/lib/typescript/memory/shareable.d.ts +16 -2
- package/lib/typescript/memory/shareable.d.ts.map +1 -1
- package/lib/typescript/memory/shareable.native.d.ts +16 -2
- package/lib/typescript/memory/shareable.native.d.ts.map +1 -1
- package/lib/typescript/memory/types.d.ts +42 -0
- package/lib/typescript/memory/types.d.ts.map +1 -1
- package/lib/typescript/specs/NativeWorkletsModule.d.ts +1 -1
- package/lib/typescript/specs/NativeWorkletsModule.d.ts.map +1 -1
- package/package.json +16 -15
- package/scripts/worklets_utils.rb +0 -2
- package/src/WorkletsModule/NativeWorklets.native.ts +3 -2
- package/src/debug/jsVersion.ts +1 -1
- package/src/featureFlags/staticFlags.json +0 -1
- package/src/featureFlags/types.ts +0 -1
- package/src/memory/serializable.native.ts +16 -10
- package/src/memory/shareable.native.ts +16 -2
- package/src/memory/shareable.ts +16 -2
- package/src/memory/types.ts +42 -0
- package/src/privateGlobals.d.ts +4 -0
- package/src/runtimeKind.ts +1 -0
- package/src/specs/NativeWorkletsModule.ts +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#include <worklets/Tools/WorkletsJSIUtils.h>
|
|
2
2
|
#include <worklets/Tools/WorkletsVersion.h>
|
|
3
|
+
#include <worklets/WorkletRuntime/HermesProfiling.h>
|
|
3
4
|
#include <worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h>
|
|
4
5
|
#include <worklets/WorkletRuntime/RuntimeKind.h>
|
|
5
6
|
#include <worklets/WorkletRuntime/WorkletRuntimeCollector.h>
|
|
6
7
|
|
|
7
8
|
#include <memory>
|
|
9
|
+
#include <string>
|
|
8
10
|
#include <utility>
|
|
9
11
|
|
|
10
12
|
namespace worklets {
|
|
@@ -34,6 +36,24 @@ void RNRuntimeWorkletDecorator::decorate(
|
|
|
34
36
|
#endif // IS_REANIMATED_EXAMPLE_APP
|
|
35
37
|
|
|
36
38
|
injectWorkletsCppVersion(rnRuntime);
|
|
39
|
+
|
|
40
|
+
rnRuntime.global().setProperty(
|
|
41
|
+
rnRuntime,
|
|
42
|
+
"_startProfiling",
|
|
43
|
+
jsi::Function::createFromHostFunction(
|
|
44
|
+
rnRuntime,
|
|
45
|
+
jsi::PropNameID::forAscii(rnRuntime, "_startProfiling"),
|
|
46
|
+
1,
|
|
47
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) {
|
|
48
|
+
const double meanHzFreq = (count > 0 && !args[0].isUndefined()) ? args[0].asNumber() : 100.0;
|
|
49
|
+
startProfiling(rt, meanHzFreq);
|
|
50
|
+
return jsi::Value::undefined();
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
jsi_utils::installJsiFunction(rnRuntime, "_stopProfiling", [](jsi::Runtime &rt) {
|
|
54
|
+
std::string path = stopProfiling(rt);
|
|
55
|
+
return jsi::String::createFromUtf8(rt, path);
|
|
56
|
+
});
|
|
37
57
|
}
|
|
38
58
|
|
|
39
59
|
#ifdef IS_REANIMATED_EXAMPLE_APP
|
|
@@ -13,7 +13,7 @@ struct RuntimeBindings {
|
|
|
13
13
|
|
|
14
14
|
const RequestAnimationFrame requestAnimationFrame;
|
|
15
15
|
|
|
16
|
-
#
|
|
16
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
17
17
|
using AbortRequest = std::function<void(jsi::Runtime &rt, double requestId)>;
|
|
18
18
|
using ClearCookies = std::function<void(jsi::Runtime &rt, jsi::Function &&responseSender)>;
|
|
19
19
|
#ifdef ANDROID
|
|
@@ -35,7 +35,7 @@ struct RuntimeBindings {
|
|
|
35
35
|
const AbortRequest abortRequest;
|
|
36
36
|
const ClearCookies clearCookies;
|
|
37
37
|
const SendRequest sendRequest;
|
|
38
|
-
#endif //
|
|
38
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
} // namespace worklets
|
|
@@ -16,11 +16,7 @@ constexpr RuntimeId rnRuntimeId{1};
|
|
|
16
16
|
constexpr RuntimeId uiRuntimeId{2};
|
|
17
17
|
extern const std::string uiRuntimeName;
|
|
18
18
|
|
|
19
|
-
#if REACT_NATIVE_MINOR_VERSION >= 81
|
|
20
|
-
|
|
21
19
|
constexpr facebook::jsi::UUID weakRuntimeUUID{0x770c6f2e, 0x1e4d, 0x436a, 0xa2b1, 0x9f322c8d5f5e};
|
|
22
20
|
|
|
23
|
-
#endif // REACT_NATIVE_MINOR_VERSION >= 81
|
|
24
|
-
|
|
25
21
|
}; // namespace RuntimeData
|
|
26
22
|
} // namespace worklets
|
|
@@ -91,27 +91,43 @@ WorkletRuntime::WorkletRuntime(
|
|
|
91
91
|
void WorkletRuntime::init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy) {
|
|
92
92
|
jsi::Runtime &rt = *runtime_;
|
|
93
93
|
|
|
94
|
-
#if REACT_NATIVE_MINOR_VERSION >= 81
|
|
95
94
|
rt.setRuntimeData(
|
|
96
95
|
RuntimeData::weakRuntimeUUID,
|
|
97
96
|
std::make_shared<WeakRuntimeHolder>(WeakRuntimeHolder{.weakRuntime = weak_from_this()}));
|
|
98
|
-
#endif // REACT_NATIVE_MINOR_VERSION >= 81
|
|
99
97
|
|
|
100
98
|
const auto jsScheduler = jsiWorkletsModuleProxy->getJSScheduler();
|
|
101
99
|
const auto isDevBundle = jsiWorkletsModuleProxy->isDevBundle();
|
|
102
100
|
const auto memoryManager_ = jsiWorkletsModuleProxy->getMemoryManager();
|
|
103
|
-
|
|
104
|
-
auto script = jsiWorkletsModuleProxy->getScript();
|
|
101
|
+
const auto script = jsiWorkletsModuleProxy->getScript();
|
|
105
102
|
const auto &sourceUrl = jsiWorkletsModuleProxy->getSourceUrl();
|
|
106
|
-
auto runtimeBindings = jsiWorkletsModuleProxy->getRuntimeBindings();
|
|
107
|
-
|
|
103
|
+
const auto runtimeBindings = jsiWorkletsModuleProxy->getRuntimeBindings();
|
|
104
|
+
const auto bundleModeEnabled = jsiWorkletsModuleProxy->isBundleModeEnabled();
|
|
108
105
|
|
|
109
106
|
auto optimizedJsiWorkletsModuleProxy = jsi_utils::optimizedFromHostObject(rt, std::move(jsiWorkletsModuleProxy));
|
|
110
107
|
|
|
111
108
|
WorkletRuntimeDecorator::decorate(
|
|
112
109
|
rt, name_, jsScheduler, isDevBundle, std::move(optimizedJsiWorkletsModuleProxy), eventLoop_);
|
|
113
110
|
|
|
114
|
-
|
|
111
|
+
if (bundleModeEnabled) {
|
|
112
|
+
bundleModeInit(jsScheduler, script, sourceUrl, runtimeBindings);
|
|
113
|
+
} else {
|
|
114
|
+
legacyModeInit();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
memoryManager_->loadAllCustomSerializables(shared_from_this());
|
|
119
|
+
} catch (jsi::JSError &e) {
|
|
120
|
+
throw std::runtime_error(std::string("[Worklets] Failed to load custom serializables. Reason: ") + e.getMessage());
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void WorkletRuntime::bundleModeInit(
|
|
125
|
+
const std::shared_ptr<JSScheduler> &jsScheduler,
|
|
126
|
+
const std::shared_ptr<const ScriptBuffer> &script,
|
|
127
|
+
const std::string &sourceUrl,
|
|
128
|
+
const std::shared_ptr<RuntimeBindings> &runtimeBindings) {
|
|
129
|
+
jsi::Runtime &rt = *runtime_;
|
|
130
|
+
|
|
115
131
|
if (!script) {
|
|
116
132
|
throw std::runtime_error("[Worklets] Expected to receive the bundle, but got nullptr instead.");
|
|
117
133
|
}
|
|
@@ -130,9 +146,11 @@ void WorkletRuntime::init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsMod
|
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
WorkletRuntimeDecorator::postEvaluateScript(rt, runtimeBindings);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
void WorkletRuntime::legacyModeInit() {
|
|
152
|
+
jsi::Runtime &rt = *runtime_;
|
|
133
153
|
|
|
134
|
-
#else
|
|
135
|
-
// Legacy behavior
|
|
136
154
|
auto valueUnpackerBuffer = std::make_shared<const jsi::StringBuffer>(ValueUnpackerCode);
|
|
137
155
|
rt.evaluateJavaScript(valueUnpackerBuffer, "valueUnpacker");
|
|
138
156
|
|
|
@@ -146,12 +164,6 @@ void WorkletRuntime::init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsMod
|
|
|
146
164
|
|
|
147
165
|
auto customSerializableUnpackerBuffer = std::make_shared<const jsi::StringBuffer>(CustomSerializableUnpackerCode);
|
|
148
166
|
rt.evaluateJavaScript(customSerializableUnpackerBuffer, "customSerializableUnpacker");
|
|
149
|
-
#endif // WORKLETS_BUNDLE_MODE_ENABLED
|
|
150
|
-
try {
|
|
151
|
-
memoryManager_->loadAllCustomSerializables(shared_from_this());
|
|
152
|
-
} catch (jsi::JSError &e) {
|
|
153
|
-
throw std::runtime_error(std::string("[Worklets] Failed to load custom serializables. Reason: ") + e.getMessage());
|
|
154
|
-
}
|
|
155
167
|
}
|
|
156
168
|
|
|
157
169
|
/* #region schedule */
|
|
@@ -265,7 +277,6 @@ void scheduleOnRuntime(
|
|
|
265
277
|
workletRuntime->schedule(serializableWorklet);
|
|
266
278
|
}
|
|
267
279
|
|
|
268
|
-
#if REACT_NATIVE_MINOR_VERSION >= 81
|
|
269
280
|
std::weak_ptr<WorkletRuntime> WorkletRuntime::getWeakRuntimeFromJSIRuntime(jsi::Runtime &rt) {
|
|
270
281
|
auto runtimeData = rt.getRuntimeData(RuntimeData::weakRuntimeUUID);
|
|
271
282
|
if (!runtimeData) [[unlikely]] {
|
|
@@ -276,7 +287,6 @@ std::weak_ptr<WorkletRuntime> WorkletRuntime::getWeakRuntimeFromJSIRuntime(jsi::
|
|
|
276
287
|
auto weakHolder = std::static_pointer_cast<WeakRuntimeHolder>(runtimeData);
|
|
277
288
|
return weakHolder->weakRuntime;
|
|
278
289
|
}
|
|
279
|
-
#endif // REACT_NATIVE_MINOR_VERSION >= 81
|
|
280
290
|
|
|
281
291
|
/* #region deprecated */
|
|
282
292
|
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
#include <jsi/jsi.h>
|
|
5
5
|
#include <jsireact/JSIExecutor.h>
|
|
6
6
|
#include <react/debug/react_native_assert.h>
|
|
7
|
-
|
|
8
7
|
#include <worklets/RunLoop/AsyncQueue.h>
|
|
9
8
|
#include <worklets/RunLoop/AsyncQueueImpl.h>
|
|
10
9
|
#include <worklets/RunLoop/EventLoop.h>
|
|
11
10
|
#include <worklets/SharedItems/Serializable.h>
|
|
12
11
|
#include <worklets/Tools/JSScheduler.h>
|
|
12
|
+
#include <worklets/Tools/ScriptBuffer.h>
|
|
13
|
+
#include <worklets/WorkletRuntime/RuntimeBindings.h>
|
|
13
14
|
#include <worklets/WorkletRuntime/RuntimeData.h>
|
|
14
15
|
|
|
15
16
|
#include <memory>
|
|
@@ -158,21 +159,29 @@ class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_th
|
|
|
158
159
|
|
|
159
160
|
/* #endregion */
|
|
160
161
|
|
|
161
|
-
#if REACT_NATIVE_MINOR_VERSION >= 81
|
|
162
162
|
/**
|
|
163
163
|
* Retrieves a weak reference to the WorkletRuntime associated with the
|
|
164
164
|
* provided jsi::Runtime.
|
|
165
165
|
*
|
|
166
166
|
* Throws when invoked with a non-worklet runtime.
|
|
167
|
+
*
|
|
168
|
+
* Available only on React Native 0.81 and higher.
|
|
167
169
|
*/
|
|
168
170
|
static std::weak_ptr<WorkletRuntime> getWeakRuntimeFromJSIRuntime(jsi::Runtime &rt);
|
|
169
|
-
#endif // REACT_NATIVE_MINOR_VERSION >= 81
|
|
170
171
|
|
|
171
172
|
#ifndef NDEBUG
|
|
172
173
|
static jsi::Function getCallGuard(jsi::Runtime &rt);
|
|
173
174
|
#endif // NDEBUG
|
|
174
175
|
|
|
175
176
|
private:
|
|
177
|
+
void bundleModeInit(
|
|
178
|
+
const std::shared_ptr<JSScheduler> &jsScheduler,
|
|
179
|
+
const std::shared_ptr<const ScriptBuffer> &script,
|
|
180
|
+
const std::string &sourceUrl,
|
|
181
|
+
const std::shared_ptr<RuntimeBindings> &runtimeBindings);
|
|
182
|
+
|
|
183
|
+
void legacyModeInit();
|
|
184
|
+
|
|
176
185
|
const RuntimeData::RuntimeId runtimeId_;
|
|
177
186
|
const std::shared_ptr<std::recursive_mutex> runtimeMutex_;
|
|
178
187
|
const std::shared_ptr<jsi::Runtime> runtime_;
|
|
@@ -1,17 +1,45 @@
|
|
|
1
1
|
#include <worklets/SharedItems/Serializable.h>
|
|
2
2
|
#include <worklets/SharedItems/SerializableFactory.h>
|
|
3
|
+
#include <worklets/Tools/Defs.h>
|
|
3
4
|
#include <worklets/Tools/JSISerializer.h>
|
|
4
5
|
#include <worklets/Tools/PlatformLogger.h>
|
|
5
6
|
#include <worklets/Tools/WorkletsJSIUtils.h>
|
|
7
|
+
#include <worklets/WorkletRuntime/HermesProfiling.h>
|
|
6
8
|
#include <worklets/WorkletRuntime/RuntimeKind.h>
|
|
7
9
|
#include <worklets/WorkletRuntime/WorkletRuntime.h>
|
|
8
10
|
#include <worklets/WorkletRuntime/WorkletRuntimeDecorator.h>
|
|
9
11
|
|
|
12
|
+
#ifdef ANDROID
|
|
13
|
+
#include <android/trace.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#if defined(__APPLE__)
|
|
17
|
+
#include <os/trace_base.h>
|
|
18
|
+
#if OS_LOG_TARGET_HAS_10_15_FEATURES
|
|
19
|
+
#include <os/log.h>
|
|
20
|
+
#include <os/signpost.h>
|
|
21
|
+
#endif
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
#include <chrono>
|
|
10
25
|
#include <memory>
|
|
11
26
|
#include <string>
|
|
12
27
|
#include <utility>
|
|
13
28
|
#include <vector>
|
|
14
29
|
|
|
30
|
+
#if defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES
|
|
31
|
+
static os_log_t workletsInstrumentsLogHandle = nullptr;
|
|
32
|
+
static thread_local os_signpost_id_t tls_signpostId = OS_SIGNPOST_ID_INVALID;
|
|
33
|
+
static thread_local std::string tls_signpostName;
|
|
34
|
+
|
|
35
|
+
static os_log_t getWorkletsInstrumentsLogHandle() {
|
|
36
|
+
if (!workletsInstrumentsLogHandle) {
|
|
37
|
+
workletsInstrumentsLogHandle = os_log_create("dev.worklets.instruments", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
|
|
38
|
+
}
|
|
39
|
+
return workletsInstrumentsLogHandle;
|
|
40
|
+
}
|
|
41
|
+
#endif
|
|
42
|
+
|
|
15
43
|
namespace worklets {
|
|
16
44
|
|
|
17
45
|
static inline double performanceNow() {
|
|
@@ -87,6 +115,33 @@ void WorkletRuntimeDecorator::decorate(
|
|
|
87
115
|
return jsi::String::createFromUtf8(rt, stringifyJSIValue(rt, value));
|
|
88
116
|
});
|
|
89
117
|
|
|
118
|
+
jsi_utils::installJsiFunction(rt, "_beginSection", [](jsi::Runtime &rt, const jsi::Value &nameValue) {
|
|
119
|
+
#ifdef ANDROID
|
|
120
|
+
ATrace_beginSection(nameValue.asString(rt).utf8(rt).c_str());
|
|
121
|
+
#elif defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES
|
|
122
|
+
os_log_t logHandle = getWorkletsInstrumentsLogHandle();
|
|
123
|
+
if (os_signpost_enabled(logHandle)) {
|
|
124
|
+
tls_signpostName = nameValue.asString(rt).utf8(rt);
|
|
125
|
+
tls_signpostId = os_signpost_id_make_with_pointer(logHandle, &tls_signpostId);
|
|
126
|
+
os_signpost_interval_begin(logHandle, tls_signpostId, "Worklets", "%s", tls_signpostName.c_str());
|
|
127
|
+
}
|
|
128
|
+
#endif
|
|
129
|
+
return jsi::Value::undefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
jsi_utils::installJsiFunction(rt, "_endSection", [](jsi::Runtime &rt) {
|
|
133
|
+
#ifdef ANDROID
|
|
134
|
+
ATrace_endSection();
|
|
135
|
+
#elif defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES
|
|
136
|
+
os_log_t logHandle = getWorkletsInstrumentsLogHandle();
|
|
137
|
+
if (os_signpost_enabled(logHandle) && tls_signpostId != OS_SIGNPOST_ID_INVALID) {
|
|
138
|
+
os_signpost_interval_end(logHandle, tls_signpostId, "Worklets", "%s end", tls_signpostName.c_str());
|
|
139
|
+
tls_signpostId = OS_SIGNPOST_ID_INVALID;
|
|
140
|
+
}
|
|
141
|
+
#endif
|
|
142
|
+
return jsi::Value::undefined();
|
|
143
|
+
});
|
|
144
|
+
|
|
90
145
|
jsi_utils::installJsiFunction(
|
|
91
146
|
rt, "_createSerializable", [](jsi::Runtime &rt, const jsi::Value &value, const jsi::Value &nativeStateSource) {
|
|
92
147
|
auto shouldRetainRemote = jsi::Value::undefined();
|
|
@@ -210,6 +265,25 @@ void WorkletRuntimeDecorator::decorate(
|
|
|
210
265
|
}));
|
|
211
266
|
rt.global().setProperty(rt, "performance", performance);
|
|
212
267
|
|
|
268
|
+
#if JS_RUNTIME_HERMES
|
|
269
|
+
rt.global().setProperty(
|
|
270
|
+
rt,
|
|
271
|
+
"_startProfiling",
|
|
272
|
+
jsi::Function::createFromHostFunction(
|
|
273
|
+
rt,
|
|
274
|
+
jsi::PropNameID::forAscii(rt, "_startProfiling"),
|
|
275
|
+
1,
|
|
276
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) {
|
|
277
|
+
const double meanHzFreq = (count > 0 && !args[0].isUndefined()) ? args[0].asNumber() : 100.0;
|
|
278
|
+
startProfiling(rt, meanHzFreq);
|
|
279
|
+
return jsi::Value::undefined();
|
|
280
|
+
}));
|
|
281
|
+
jsi_utils::installJsiFunction(rt, "_stopProfiling", [](jsi::Runtime &rt) {
|
|
282
|
+
std::string path = stopProfiling(rt);
|
|
283
|
+
return jsi::String::createFromUtf8(rt, path);
|
|
284
|
+
});
|
|
285
|
+
#endif // JS_RUNTIME_HERMES
|
|
286
|
+
|
|
213
287
|
jsi_utils::installJsiFunction(
|
|
214
288
|
rt,
|
|
215
289
|
"_scheduleTimeoutCallback",
|
|
@@ -227,7 +301,6 @@ void WorkletRuntimeDecorator::decorate(
|
|
|
227
301
|
});
|
|
228
302
|
}
|
|
229
303
|
|
|
230
|
-
#ifdef WORKLETS_BUNDLE_MODE_ENABLED
|
|
231
304
|
void WorkletRuntimeDecorator::postEvaluateScript(
|
|
232
305
|
jsi::Runtime &rt,
|
|
233
306
|
const std::shared_ptr<RuntimeBindings> &runtimeBindings) {
|
|
@@ -307,6 +380,5 @@ void WorkletRuntimeDecorator::installNetworking(
|
|
|
307
380
|
Networking.asObject(rt).setProperty(rt, "clearCookies", std::move(jsiClearCookies));
|
|
308
381
|
}
|
|
309
382
|
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
310
|
-
#endif // WORKLETS_BUNDLE_MODE_ENABLED
|
|
311
383
|
|
|
312
384
|
} // namespace worklets
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <worklets/RunLoop/EventLoop.h>
|
|
4
4
|
#include <worklets/Tools/JSScheduler.h>
|
|
5
|
-
#ifdef WORKLETS_BUNDLE_MODE_ENABLED
|
|
6
5
|
#include <worklets/WorkletRuntime/RuntimeBindings.h>
|
|
7
|
-
#endif // WORKLETS_BUNDLE_MODE_ENABLED
|
|
8
6
|
|
|
9
7
|
#include <jsi/jsi.h>
|
|
10
8
|
|
|
@@ -25,14 +23,12 @@ class WorkletRuntimeDecorator {
|
|
|
25
23
|
jsi::Object &&jsiWorkletsModuleProxy,
|
|
26
24
|
const std::shared_ptr<EventLoop> &eventLoop);
|
|
27
25
|
|
|
28
|
-
#ifdef WORKLETS_BUNDLE_MODE_ENABLED
|
|
29
26
|
static void postEvaluateScript(jsi::Runtime &rt, const std::shared_ptr<RuntimeBindings> &runtimeBindings);
|
|
30
27
|
|
|
31
28
|
private:
|
|
32
29
|
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
33
30
|
static void installNetworking(jsi::Runtime &rt, const std::shared_ptr<RuntimeBindings> &runtimeBindings);
|
|
34
31
|
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
35
|
-
#endif // WORKLETS_BUNDLE_MODE_ENABLED
|
|
36
32
|
};
|
|
37
33
|
|
|
38
34
|
} // namespace worklets
|
package/RNWorklets.podspec
CHANGED
|
@@ -13,7 +13,6 @@ ios_min_version = '13.4'
|
|
|
13
13
|
feature_flags = $worklets_config[:feature_flags_flag]
|
|
14
14
|
version_flags = "-DWORKLETS_VERSION=#{package['version']} -DREACT_NATIVE_MINOR_VERSION=#{$worklets_config[:react_native_minor_version]}"
|
|
15
15
|
worklets_profiling_flag = ENV['IS_WORKLETS_PROFILING'] ? '-DWORKLETS_PROFILING' : ''
|
|
16
|
-
bundle_mode_flag = $worklets_config[:bundle_mode_flag]
|
|
17
16
|
fetch_preview_flag = $worklets_config[:fetch_preview_flag]
|
|
18
17
|
hermes_v1_flag = ENV['RCT_HERMES_V1_ENABLED'] == '1' ? '-DHERMES_V1_ENABLED' : ''
|
|
19
18
|
|
|
@@ -76,7 +75,7 @@ Pod::Spec.new do |s|
|
|
|
76
75
|
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
|
|
77
76
|
"GCC_PREPROCESSOR_DEFINITIONS[config=*Debug*]" => "$(inherited) #{hermes_debug_hidden_flags}",
|
|
78
77
|
"GCC_PREPROCESSOR_DEFINITIONS[config=*Release*]" => "$(inherited)",
|
|
79
|
-
"OTHER_CFLAGS" => "$(inherited) #{feature_flags} #{version_flags} #{worklets_profiling_flag} #{
|
|
78
|
+
"OTHER_CFLAGS" => "$(inherited) #{feature_flags} #{version_flags} #{worklets_profiling_flag} #{fetch_preview_flag} #{hermes_v1_flag}",
|
|
80
79
|
}
|
|
81
80
|
s.xcconfig = {
|
|
82
81
|
"HEADER_SEARCH_PATHS" => [
|
package/android/CMakeLists.txt
CHANGED
|
@@ -32,9 +32,6 @@ endif()
|
|
|
32
32
|
if(${IS_REANIMATED_EXAMPLE_APP})
|
|
33
33
|
string(APPEND CMAKE_CXX_FLAGS " -DIS_REANIMATED_EXAMPLE_APP -Wpedantic")
|
|
34
34
|
endif()
|
|
35
|
-
if(${WORKLETS_BUNDLE_MODE_ENABLED})
|
|
36
|
-
string(APPEND CMAKE_CXX_FLAGS " -DWORKLETS_BUNDLE_MODE_ENABLED")
|
|
37
|
-
endif()
|
|
38
35
|
if(${WORKLETS_FETCH_PREVIEW_ENABLED})
|
|
39
36
|
string(APPEND CMAKE_CXX_FLAGS " -DWORKLETS_FETCH_PREVIEW_ENABLED")
|
|
40
37
|
endif()
|
|
@@ -99,7 +96,7 @@ target_include_directories(
|
|
|
99
96
|
# build shared lib
|
|
100
97
|
set_target_properties(worklets PROPERTIES LINKER_LANGUAGE CXX)
|
|
101
98
|
|
|
102
|
-
target_link_libraries(worklets log ReactAndroid::reactnative ReactAndroid::jsi
|
|
99
|
+
target_link_libraries(worklets android log ReactAndroid::reactnative ReactAndroid::jsi
|
|
103
100
|
fbjni::fbjni)
|
|
104
101
|
|
|
105
102
|
if(${JS_RUNTIME} STREQUAL "hermes")
|
package/android/build.gradle
CHANGED
|
@@ -122,7 +122,6 @@ def REACT_NATIVE_VERSION = getReactNativeVersion()
|
|
|
122
122
|
def WORKLETS_VERSION = getWorkletsVersion()
|
|
123
123
|
def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled()
|
|
124
124
|
def IS_REANIMATED_EXAMPLE_APP = safeAppExtGet("isReanimatedExampleApp", false)
|
|
125
|
-
def BUNDLE_MODE_ENABLED = isFlagEnabled(featureFlags, "BUNDLE_MODE_ENABLED");
|
|
126
125
|
def FETCH_PREVIEW_ENABLED = isFlagEnabled(featureFlags, "FETCH_PREVIEW_ENABLED");
|
|
127
126
|
def WORKLETS_FEATURE_FLAGS = getStaticFeatureFlagsString(featureFlags)
|
|
128
127
|
def HERMES_V1_ENABLED = getHermesV1Enabled()
|
|
@@ -213,7 +212,6 @@ android {
|
|
|
213
212
|
buildConfigField("boolean", "IS_INTERNAL_BUILD", "false")
|
|
214
213
|
buildConfigField("int", "EXOPACKAGE_FLAGS", "0")
|
|
215
214
|
buildConfigField("int", "REACT_NATIVE_MINOR_VERSION", REACT_NATIVE_MINOR_VERSION.toString())
|
|
216
|
-
buildConfigField("boolean", "BUNDLE_MODE_ENABLED", BUNDLE_MODE_ENABLED.toString())
|
|
217
215
|
|
|
218
216
|
externalNativeBuild {
|
|
219
217
|
cmake {
|
|
@@ -223,7 +221,6 @@ android {
|
|
|
223
221
|
"-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}",
|
|
224
222
|
"-DJS_RUNTIME=${JS_RUNTIME}",
|
|
225
223
|
"-DIS_REANIMATED_EXAMPLE_APP=${IS_REANIMATED_EXAMPLE_APP}",
|
|
226
|
-
"-DWORKLETS_BUNDLE_MODE_ENABLED=${BUNDLE_MODE_ENABLED}",
|
|
227
224
|
"-DWORKLETS_FETCH_PREVIEW_ENABLED=${FETCH_PREVIEW_ENABLED}",
|
|
228
225
|
"-DWORKLETS_PROFILING=${WORKLETS_PROFILING}",
|
|
229
226
|
"-DWORKLETS_VERSION=${WORKLETS_VERSION}",
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
#include <cxxreact/JSBigString.h>
|
|
7
6
|
#include <cxxreact/RecoverableError.h>
|
|
7
|
+
#include <worklets/Tools/Defs.h>
|
|
8
8
|
#include <worklets/Tools/ScriptBuffer.h>
|
|
9
9
|
#include <worklets/android/JScriptBufferWrapper.h>
|
|
10
10
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
2
2
|
|
|
3
3
|
#include <jsi/JSIDynamic.h>
|
|
4
4
|
#include <jsi/jsi.h>
|
|
@@ -49,4 +49,4 @@ void JWorkletRuntimeWrapper::registerNatives() {
|
|
|
49
49
|
}
|
|
50
50
|
} // namespace worklets
|
|
51
51
|
|
|
52
|
-
#endif //
|
|
52
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
4
4
|
|
|
5
5
|
#include <fbjni/fbjni.h>
|
|
6
6
|
#include <react/jni/ReadableNativeArray.h>
|
|
@@ -40,4 +40,4 @@ class JWorkletRuntimeWrapper : public jni::HybridClass<JWorkletRuntimeWrapper> {
|
|
|
40
40
|
|
|
41
41
|
} // namespace worklets
|
|
42
42
|
|
|
43
|
-
#endif //
|
|
43
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#include <worklets/NativeModules/JSIWorkletsModuleProxy.h>
|
|
2
2
|
#include <worklets/Tools/ScriptBuffer.h>
|
|
3
3
|
#include <worklets/Tools/WorkletsJSIUtils.h>
|
|
4
|
+
#include <worklets/WorkletRuntime/BundleModeConfig.h>
|
|
4
5
|
#include <worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h>
|
|
5
6
|
#include <worklets/WorkletRuntime/RuntimeBindings.h>
|
|
6
7
|
#include <worklets/android/AnimationFrameCallback.h>
|
|
7
8
|
#include <worklets/android/WorkletsModule.h>
|
|
8
9
|
|
|
9
|
-
#
|
|
10
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
10
11
|
#include <folly/json/dynamic.h>
|
|
11
12
|
#include <jni.h>
|
|
12
13
|
#include <jsi/JSIDynamic.h>
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
#include <react/jni/ReadableNativeMap.h>
|
|
16
17
|
#include <worklets/WorkletRuntime/WorkletRuntime.h>
|
|
17
18
|
#include <worklets/android/JWorkletRuntimeWrapper.h>
|
|
18
|
-
#endif //
|
|
19
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
19
20
|
|
|
20
21
|
#include <memory>
|
|
21
22
|
#include <string>
|
|
@@ -29,13 +30,12 @@ using namespace react;
|
|
|
29
30
|
|
|
30
31
|
WorkletsModule::WorkletsModule(
|
|
31
32
|
jni::alias_ref<jhybridobject> jThis, // NOLINT //(performance-unnecessary-value-param)
|
|
33
|
+
const BundleModeConfig &bundleModeConfig,
|
|
32
34
|
jsi::Runtime *rnRuntime,
|
|
33
35
|
jni::alias_ref<JavaMessageQueueThread::javaobject>
|
|
34
36
|
messageQueueThread, // NOLINT //(performance-unnecessary-value-param)
|
|
35
37
|
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker,
|
|
36
|
-
const std::shared_ptr<UIScheduler> &uiScheduler
|
|
37
|
-
const std::shared_ptr<const ScriptBuffer> &script,
|
|
38
|
-
const std::string &sourceURL)
|
|
38
|
+
const std::shared_ptr<UIScheduler> &uiScheduler)
|
|
39
39
|
: javaPart_(jni::make_global(jThis)),
|
|
40
40
|
rnRuntime_(rnRuntime),
|
|
41
41
|
workletsModuleProxy_(std::make_shared<WorkletsModuleProxy>(
|
|
@@ -46,15 +46,14 @@ WorkletsModule::WorkletsModule(
|
|
|
46
46
|
getIsOnJSQueueThread(),
|
|
47
47
|
std::make_shared<RuntimeBindings>(RuntimeBindings{
|
|
48
48
|
.requestAnimationFrame = getRequestAnimationFrame()
|
|
49
|
-
#
|
|
49
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
50
50
|
,
|
|
51
51
|
.abortRequest = getAbortRequest(),
|
|
52
52
|
.clearCookies = getClearCookies(),
|
|
53
53
|
.sendRequest = getSendRequest()
|
|
54
|
-
#endif //
|
|
54
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
55
55
|
}),
|
|
56
|
-
|
|
57
|
-
sourceURL)) {
|
|
56
|
+
bundleModeConfig)) {
|
|
58
57
|
auto jsiWorkletsModuleProxy = workletsModuleProxy_->createJSIWorkletsModuleProxy();
|
|
59
58
|
auto optimizedJsiWorkletsModuleProxy = jsi_utils::optimizedFromHostObject(
|
|
60
59
|
*rnRuntime_, std::static_pointer_cast<jsi::HostObject>(std::move(jsiWorkletsModuleProxy)));
|
|
@@ -64,6 +63,7 @@ WorkletsModule::WorkletsModule(
|
|
|
64
63
|
|
|
65
64
|
jni::local_ref<WorkletsModule::jhybriddata> WorkletsModule::initHybrid(
|
|
66
65
|
jni::alias_ref<jhybridobject> jThis, // NOLINT //(performance-unnecessary-value-param)
|
|
66
|
+
jboolean bundleModeEnabled,
|
|
67
67
|
jlong jsContext,
|
|
68
68
|
jni::alias_ref<JavaMessageQueueThread::javaobject>
|
|
69
69
|
messageQueueThread, // NOLINT //(performance-unnecessary-value-param)
|
|
@@ -78,13 +78,23 @@ jni::local_ref<WorkletsModule::jhybriddata> WorkletsModule::initHybrid(
|
|
|
78
78
|
|
|
79
79
|
std::shared_ptr<const ScriptBuffer> script = nullptr;
|
|
80
80
|
std::string sourceURL;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return makeCxxInstance(
|
|
81
|
+
if (bundleModeEnabled) {
|
|
82
|
+
auto cxxWrapper = jScriptBufferWrapper->cthis();
|
|
83
|
+
script = cxxWrapper->getScript();
|
|
84
|
+
sourceURL = cxxWrapper->getSourceUrl();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return makeCxxInstance(
|
|
88
|
+
jThis,
|
|
89
|
+
BundleModeConfig{
|
|
90
|
+
.enabled = static_cast<bool>(bundleModeEnabled),
|
|
91
|
+
.script = script,
|
|
92
|
+
.sourceURL = sourceURL,
|
|
93
|
+
},
|
|
94
|
+
rnRuntime,
|
|
95
|
+
messageQueueThread,
|
|
96
|
+
jsCallInvoker,
|
|
97
|
+
uiScheduler);
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
RuntimeBindings::RequestAnimationFrame WorkletsModule::getRequestAnimationFrame() {
|
|
@@ -95,7 +105,7 @@ RuntimeBindings::RequestAnimationFrame WorkletsModule::getRequestAnimationFrame(
|
|
|
95
105
|
};
|
|
96
106
|
}
|
|
97
107
|
|
|
98
|
-
#
|
|
108
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
99
109
|
RuntimeBindings::AbortRequest WorkletsModule::getAbortRequest() {
|
|
100
110
|
return [javaPart = javaPart_](jsi::Runtime &rt, double requestId) -> void {
|
|
101
111
|
static const auto jAbortRequest = javaPart->getClass()->getMethod<void(int, double)>("abortRequest");
|
|
@@ -167,7 +177,7 @@ RuntimeBindings::SendRequest WorkletsModule::getSendRequest() {
|
|
|
167
177
|
withCredentials);
|
|
168
178
|
};
|
|
169
179
|
}
|
|
170
|
-
#endif //
|
|
180
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
171
181
|
|
|
172
182
|
std::function<bool()> WorkletsModule::getIsOnJSQueueThread() {
|
|
173
183
|
return [javaPart = javaPart_]() -> bool {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <worklets/NativeModules/WorkletsModuleProxy.h>
|
|
8
8
|
#include <worklets/Tools/Defs.h>
|
|
9
9
|
#include <worklets/Tools/ScriptBuffer.h>
|
|
10
|
+
#include <worklets/WorkletRuntime/BundleModeConfig.h>
|
|
10
11
|
#include <worklets/WorkletRuntime/RuntimeBindings.h>
|
|
11
12
|
#include <worklets/android/AndroidUIScheduler.h>
|
|
12
13
|
#include <worklets/android/JScriptBufferWrapper.h>
|
|
@@ -25,6 +26,7 @@ class WorkletsModule : public jni::HybridClass<WorkletsModule> {
|
|
|
25
26
|
|
|
26
27
|
static jni::local_ref<jhybriddata> initHybrid(
|
|
27
28
|
jni::alias_ref<jhybridobject> jThis,
|
|
29
|
+
jboolean bundleModeEnabled,
|
|
28
30
|
jlong jsContext,
|
|
29
31
|
jni::alias_ref<JavaMessageQueueThread::javaobject> messageQueueThread,
|
|
30
32
|
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject> jsCallInvokerHolder,
|
|
@@ -40,12 +42,11 @@ class WorkletsModule : public jni::HybridClass<WorkletsModule> {
|
|
|
40
42
|
private:
|
|
41
43
|
explicit WorkletsModule(
|
|
42
44
|
jni::alias_ref<jhybridobject> jThis,
|
|
45
|
+
const BundleModeConfig &bundleModeConfig,
|
|
43
46
|
jsi::Runtime *rnRuntime,
|
|
44
47
|
jni::alias_ref<JavaMessageQueueThread::javaobject> messageQueueThread,
|
|
45
48
|
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker,
|
|
46
|
-
const std::shared_ptr<UIScheduler> &uiScheduler
|
|
47
|
-
const std::shared_ptr<const ScriptBuffer> &script,
|
|
48
|
-
const std::string &sourceURL);
|
|
49
|
+
const std::shared_ptr<UIScheduler> &uiScheduler);
|
|
49
50
|
|
|
50
51
|
void invalidateCpp();
|
|
51
52
|
|
|
@@ -55,11 +56,11 @@ class WorkletsModule : public jni::HybridClass<WorkletsModule> {
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
RuntimeBindings::RequestAnimationFrame getRequestAnimationFrame();
|
|
58
|
-
#
|
|
59
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
59
60
|
RuntimeBindings::AbortRequest getAbortRequest();
|
|
60
61
|
RuntimeBindings::ClearCookies getClearCookies();
|
|
61
62
|
RuntimeBindings::SendRequest getSendRequest();
|
|
62
|
-
#endif //
|
|
63
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
63
64
|
|
|
64
65
|
std::function<bool()> getIsOnJSQueueThread();
|
|
65
66
|
|
|
@@ -12,8 +12,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
|
|
12
12
|
worklets::AndroidUIScheduler::registerNatives();
|
|
13
13
|
worklets::AnimationFrameCallback::registerNatives();
|
|
14
14
|
worklets::JScriptBufferWrapper::registerNatives();
|
|
15
|
-
#
|
|
15
|
+
#ifdef WORKLETS_FETCH_PREVIEW_ENABLED
|
|
16
16
|
worklets::JWorkletRuntimeWrapper::registerNatives();
|
|
17
|
-
#endif //
|
|
17
|
+
#endif // WORKLETS_FETCH_PREVIEW_ENABLED
|
|
18
18
|
});
|
|
19
19
|
}
|