Skip to content

Commit de6235f

Browse files
committed
feat: harden
1 parent aad9069 commit de6235f

10 files changed

Lines changed: 27 additions & 137 deletions

File tree

packages/react-native-reanimated/Common/cpp/worklets/SharedItems/Shareables.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,7 @@ jsi::Value makeShareableClone(
5555
std::shared_ptr<Shareable> shareable;
5656
if (value.isObject()) {
5757
auto object = value.asObject(rt);
58-
59-
jsi::PropNameID prop = workletCodePropName(rt);
60-
if (object.hasProperty(rt, prop)) {
61-
jsi::Value code = object.getProperty(rt, prop);
62-
shareable = std::make_shared<ShareableString>(code.asString(rt).utf8(rt));
63-
} else if (!object.getProperty(rt, "__workletHash").isUndefined()) {
58+
if (!object.getProperty(rt, "__workletHash").isUndefined()) {
6459
shareable = std::make_shared<ShareableWorklet>(rt, object);
6560
} else if (!object.getProperty(rt, "__init").isUndefined()) {
6661
shareable = std::make_shared<ShareableHandle>(rt, object);
@@ -281,7 +276,8 @@ jsi::Value ShareableWorklet::toJSValue(jsi::Runtime &rt) {
281276
jsi::Value obj = ShareableObject::toJSValue(rt);
282277
auto initData = obj.asObject(rt).getProperty(rt, "__initData").asObject(rt);
283278
auto code = std::make_shared<const jsi::StringBuffer>(
284-
"(" + initData.getProperty(rt, "code").asString(rt).utf8(rt) + "\n)");
279+
"(" + initData.getProperty(rt, workletCodePropName(rt)).asString(rt).utf8(rt) + "\n)");
280+
285281
auto sourceURL = initData.getProperty(rt, "location").asString(rt).utf8(rt);
286282
// The code has to be evaluated in the context of the worklet runtime.
287283
// This is done by creating a new function that evaluates the code and

packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/ReanimatedHermesRuntime.cpp

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -80,34 +80,6 @@ ReanimatedHermesRuntime::ReanimatedHermesRuntime(
8080
jsQueue->quitSynchronous();
8181
#endif // HERMES_ENABLE_DEBUGGER
8282

83-
#ifndef NDEBUG
84-
facebook::hermes::HermesRuntime *wrappedRuntime = runtime_.get();
85-
jsi::Value evalWithSourceMap = jsi::Function::createFromHostFunction(
86-
*runtime_,
87-
jsi::PropNameID::forAscii(*runtime_, "evalWithSourceMap"),
88-
3,
89-
[wrappedRuntime](
90-
jsi::Runtime &rt,
91-
const jsi::Value &thisValue,
92-
const jsi::Value *args,
93-
size_t count) -> jsi::Value {
94-
auto code = std::make_shared<const jsi::StringBuffer>(
95-
args[0].asString(rt).utf8(rt));
96-
std::string sourceURL;
97-
if (count > 1 && args[1].isString()) {
98-
sourceURL = args[1].asString(rt).utf8(rt);
99-
}
100-
std::shared_ptr<const jsi::Buffer> sourceMap;
101-
if (count > 2 && args[2].isString()) {
102-
sourceMap = std::make_shared<const jsi::StringBuffer>(
103-
args[2].asString(rt).utf8(rt));
104-
}
105-
return wrappedRuntime->evaluateJavaScriptWithSourceMap(
106-
code, sourceMap, sourceURL);
107-
});
108-
runtime_->global().setProperty(
109-
*runtime_, "evalWithSourceMap", evalWithSourceMap);
110-
#endif // NDEBUG
11183
}
11284

11385
ReanimatedHermesRuntime::~ReanimatedHermesRuntime() {

packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@ static std::shared_ptr<jsi::Runtime> makeRuntime(
5151
}
5252
}
5353

54-
55-
std::string getJsValueUnpackerString() {
56-
return "(function valueUnpacker_Exodus_valueUnpackerJs1(objectToUnpack,category,remoteFunctionName){let workletsCache=global.__workletsCache;let handleCache=global.__handleCache;if(workletsCache===undefined){workletsCache=global.__workletsCache=new Map();handleCache=global.__handleCache=new WeakMap();}const workletHash=objectToUnpack.__workletHash;if(workletHash!==undefined){let workletFun=workletsCache.get(workletHash);if(workletFun===undefined){const initData=objectToUnpack.__initData;if(global.evalWithSourceMap){workletFun=global.evalWithSourceMap('('+initData.__reanimated_workletCodeWrapper+'\\n)',initData.location,initData.sourceMap);}else if(global.evalWithSourceUrl){workletFun=global.evalWithSourceUrl('('+initData.__reanimated_workletCodeWrapper+'\\n)',\"worklet_\"+workletHash);}else{workletFun=eval('('+initData.__reanimated_workletCodeWrapper+'\\n)');}workletsCache.set(workletHash,workletFun);}const functionInstance=workletFun.bind(objectToUnpack);objectToUnpack._recur=functionInstance;return functionInstance;}else if(objectToUnpack.__init!==undefined){let value=handleCache.get(objectToUnpack);if(value===undefined){value=objectToUnpack.__init();handleCache.set(objectToUnpack,value);}return value;}else if(category==='RemoteFunction'){const fun=function(){const label=remoteFunctionName?\"function \"+remoteFunctionName:'anonymous function';throw new Error(\"[Reanimated] Tried to synchronously call a non-worklet \"+label+\" on the UI thread.\\nSee https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-worklet-function-on-the-ui-thread for more details.\");};fun.__remoteFunction=objectToUnpack;return fun;}else{throw new Error(\"[Reanimated] Data type in category \"+category+\" not recognized by value unpacker: \"+_toString(objectToUnpack)+\".\");}}\n)";
57-
}
58-
5954
WorkletRuntime::WorkletRuntime(
6055
jsi::Runtime &rnRuntime,
6156
const std::shared_ptr<MessageQueueThread> &jsQueue,
@@ -78,7 +73,8 @@ WorkletRuntime::WorkletRuntime(
7873
WorkletRuntimeCollector::install(rt);
7974
WorkletRuntimeDecorator::decorate(rt, name, jsScheduler);
8075

81-
auto codeBuffer = std::make_shared<const jsi::StringBuffer>(getJsValueUnpackerString());
76+
auto codeBuffer = std::make_shared<const jsi::StringBuffer>(
77+
"(" + valueUnpackerCode + "\n)");
8278
auto valueUnpacker = rt.evaluateJavaScript(codeBuffer, "valueUnpacker")
8379
.asObject(rt)
8480
.asFunction(rt);

packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,29 +53,6 @@ void WorkletRuntimeDecorator::decorate(
5353
#endif // RCT_NEW_ARCH_ENABLED
5454
rt.global().setProperty(rt, "_IS_FABRIC", isFabric);
5555

56-
#ifndef NDEBUG
57-
auto evalWithSourceUrl = [](jsi::Runtime &rt,
58-
const jsi::Value &thisValue,
59-
const jsi::Value *args,
60-
size_t count) -> jsi::Value {
61-
auto code = std::make_shared<const jsi::StringBuffer>(
62-
args[0].asString(rt).utf8(rt));
63-
std::string url;
64-
if (count > 1 && args[1].isString()) {
65-
url = args[1].asString(rt).utf8(rt);
66-
}
67-
return rt.evaluateJavaScript(code, url);
68-
};
69-
rt.global().setProperty(
70-
rt,
71-
"evalWithSourceUrl",
72-
jsi::Function::createFromHostFunction(
73-
rt,
74-
jsi::PropNameID::forAscii(rt, "evalWithSourceUrl"),
75-
1,
76-
evalWithSourceUrl));
77-
#endif // NDEBUG
78-
7956
jsi_utils::installJsiFunction(
8057
rt, "_toString", [](jsi::Runtime &rt, const jsi::Value &value) {
8158
return jsi::String::createFromUtf8(rt, stringifyJSIValue(rt, value));

packages/react-native-reanimated/plugin/index.js

Lines changed: 1 addition & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-native-reanimated/plugin/src/workletFactory.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,7 @@ export function makeWorkletFactory(
153153
true
154154
);
155155

156-
const reanimatedSecretCodeProperty = objectProperty(
157-
identifier('__reanimated_workletCodeWrapper'),
158-
objectExpression([workletCodeProperty])
159-
);
160-
161-
const initDataObjectExpression = objectExpression([
162-
reanimatedSecretCodeProperty,
163-
]);
156+
const initDataObjectExpression = objectExpression([workletCodeProperty]);
164157

165158
// When testing with jest I noticed that environment variables are set later
166159
// than some functions are evaluated. E.g. this cannot be above this function

packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
} from '../commonTypes';
88
import { jsVersion } from '../platform-specific/jsVersion';
99
import type { WorkletRuntime } from '../runtimes';
10+
import { getValueUnpackerCode } from '../valueUnpacker';
1011
import { isFabric } from '../PlatformChecker';
1112
import type React from 'react';
1213
import { getShadowNodeWrapperFromRef } from '../fabricUtils';
@@ -85,7 +86,8 @@ export class NativeReanimated {
8586
}
8687
global._REANIMATED_VERSION_JS = jsVersion;
8788
if (global.__reanimatedModuleProxy === undefined) {
88-
ReanimatedModule?.installTurboModule('');
89+
const valueUnpackerCode = getValueUnpackerCode();
90+
ReanimatedModule?.installTurboModule(valueUnpackerCode);
8991
}
9092
if (global.__reanimatedModuleProxy === undefined) {
9193
throw new ReanimatedError(

packages/react-native-reanimated/src/privateGlobals.d.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ declare global {
3232
var _REANIMATED_VERSION_JS: string | undefined;
3333
var __reanimatedModuleProxy: NativeReanimatedModule | undefined;
3434
var __callGuardDEV: typeof callGuardDEV | undefined;
35-
var evalWithSourceMap:
36-
| ((js: string, sourceURL: string, sourceMap: string) => any)
37-
| undefined;
38-
var evalWithSourceUrl: ((js: string, sourceURL: string) => any) | undefined;
3935
var _log: (value: unknown) => void;
4036
var _toString: (value: unknown) => string;
4137
var _notifyAboutProgress: (

packages/react-native-reanimated/src/shareables.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,15 @@ Offending code was: \`${getWorkletCode(value)}\``);
201201
if (key === '__initData' && toAdapt.__initData !== undefined) {
202202
continue;
203203
}
204-
if (key === '__reanimated_workletCodeWrapper') {
205-
toAdapt[key] = NativeReanimatedModule.makeShareableClone(
206-
element,
207-
shouldPersistRemote,
208-
value
209-
);
210-
continue;
211-
}
204+
toAdapt[key] = makeShareableCloneRecursive(
205+
element,
206+
shouldPersistRemote,
207+
depth + 1
208+
);
209+
}
210+
211+
for (const key of Object.getOwnPropertySymbols(value)) {
212+
const element = value[key];
212213
toAdapt[key] = makeShareableCloneRecursive(
213214
element,
214215
shouldPersistRemote,
@@ -218,7 +219,7 @@ Offending code was: \`${getWorkletCode(value)}\``);
218219
freezeObjectIfDev(value);
219220
} else if (value instanceof RegExp) {
220221
// disabled, contact appsec if needed: https://github.com/ExodusMovement/exodus-mobile/pull/24699#issuecomment-2709694172
221-
/* const pattern = value.source;
222+
/* const pattern = value.source;
222223
const flags = value.flags;
223224
const handle = makeShareableCloneRecursive({
224225
__init: () => {
Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
/* eslint-disable reanimated/use-reanimated-error */
22
'use strict';
3-
import { shouldBeUseWeb } from './PlatformChecker';
4-
import { isWorkletFunction } from './commonTypes';
5-
import type { WorkletFunction } from './commonTypes';
63

7-
function valueUnpacker(
8-
objectToUnpack: any,
9-
category?: string,
10-
remoteFunctionName?: string,
11-
evaluateWorkletFunction?: () => Function
12-
): any {
4+
const valueUnpackerCode = `
5+
function valueUnpacker(objectToUnpack, category, remoteFunctionName, evaluateWorkletFunction) {
136
'worklet';
147
let workletsCache = global.__workletsCache;
158
let handleCache = global.__handleCache;
@@ -37,49 +30,16 @@ function valueUnpacker(
3730
return value;
3831
} else if (category === 'RemoteFunction') {
3932
const fun = () => {
40-
const label = remoteFunctionName
41-
? `function \`${remoteFunctionName}\``
42-
: 'anonymous function';
43-
throw new Error(`[Reanimated] Tried to synchronously call a non-worklet ${label} on the UI thread.
44-
See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-worklet-function-on-the-ui-thread for more details.`);
33+
const label = remoteFunctionName ? "function " + remoteFunctionName : "anonymous function";
34+
throw new Error("[Reanimated] Tried to synchronously call a non-worklet" + label + " on the UI thread. See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-worklet-function-on-the-ui-thread for more details.");
4535
};
4636
fun.__remoteFunction = objectToUnpack;
4737
return fun;
4838
} else {
49-
throw new Error(
50-
`[Reanimated] Data type in category "${category}" not recognized by value unpacker: "${_toString(
51-
objectToUnpack
52-
)}".`
53-
);
39+
throw new Error("[Reanimated] Data type in category " + category + " not recognized by value unpacker: " + _toString(objectToUnpack) + ".");
5440
}
55-
}
56-
57-
type ValueUnpacker = WorkletFunction<
58-
[objectToUnpack: any, category?: string],
59-
any
60-
>;
61-
62-
if (__DEV__ && !shouldBeUseWeb()) {
63-
const testWorklet = (() => {
64-
'worklet';
65-
}) as WorkletFunction<[], void>;
66-
if (!isWorkletFunction(testWorklet)) {
67-
throw new Error(
68-
`[Reanimated] Failed to create a worklet. See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#failed-to-create-a-worklet for more details.`
69-
);
70-
}
71-
if (!isWorkletFunction(valueUnpacker)) {
72-
throw new Error('[Reanimated] `valueUnpacker` is not a worklet');
73-
}
74-
const closure = (valueUnpacker as ValueUnpacker).__closure;
75-
if (closure === undefined) {
76-
throw new Error('[Reanimated] `valueUnpacker` closure is undefined');
77-
}
78-
if (Object.keys(closure).length !== 0) {
79-
throw new Error('[Reanimated] `valueUnpacker` must have empty closure');
80-
}
81-
}
41+
}`;
8242

8343
export function getValueUnpackerCode() {
84-
return (valueUnpacker as ValueUnpacker).__initData.code;
44+
return valueUnpackerCode;
8545
}

0 commit comments

Comments
 (0)