chore: update

This commit is contained in:
2026-02-18 21:52:32 +01:00
parent aed786cf2c
commit 1f2e4e96dd
399 changed files with 4173 additions and 10273 deletions
+1 -1
View File
@@ -1 +1 @@
../@expo/fingerprint/bin/cli.js
../expo/bin/fingerprint
+37 -126
View File
@@ -1245,7 +1245,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
"integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1778,9 +1777,9 @@
}
},
"node_modules/@expo/prebuild-config/node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -3859,12 +3858,12 @@
"integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw=="
},
"node_modules/expo": {
"version": "54.0.32",
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.32.tgz",
"integrity": "sha512-yL9eTxiQ/QKKggVDAWO5CLjUl6IS0lPYgEvC3QM4q4fxd6rs7ks3DnbXSGVU3KNFoY/7cRNYihvd0LKYP+MCXA==",
"version": "54.0.33",
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz",
"integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "54.0.22",
"@expo/cli": "54.0.23",
"@expo/config": "~12.0.13",
"@expo/config-plugins": "~54.0.4",
"@expo/devtools": "0.1.8",
@@ -4042,9 +4041,9 @@
}
},
"node_modules/expo/node_modules/@expo/cli": {
"version": "54.0.22",
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.22.tgz",
"integrity": "sha512-BTH2FCczhJLfj1cpfcKrzhKnvRLTOztgW4bVloKDqH+G3ZSohWLRFNAIz56XtdjPxBbi2/qWhGBAkl7kBon/Jw==",
"version": "54.0.23",
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.23.tgz",
"integrity": "sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==",
"dependencies": {
"@0no-co/graphql.web": "^1.0.8",
"@expo/code-signing-certificates": "^0.0.6",
@@ -4153,9 +4152,9 @@
}
},
"node_modules/expo/node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -5901,9 +5900,9 @@
}
},
"node_modules/npm-package-arg/node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -6693,120 +6692,32 @@
}
},
"node_modules/react-native-worklets": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.2.tgz",
"integrity": "sha512-DuLu1kMV/Uyl9pQHp3hehAlThoLw7Yk2FwRTpzASOmI+cd4845FWn3m2bk9MnjUw8FBRIyhwLqYm2AJaXDXsog==",
"peer": true,
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz",
"integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==",
"dependencies": {
"@babel/plugin-transform-arrow-functions": "7.27.1",
"@babel/plugin-transform-class-properties": "7.27.1",
"@babel/plugin-transform-classes": "7.28.4",
"@babel/plugin-transform-nullish-coalescing-operator": "7.27.1",
"@babel/plugin-transform-optional-chaining": "7.27.1",
"@babel/plugin-transform-shorthand-properties": "7.27.1",
"@babel/plugin-transform-template-literals": "7.27.1",
"@babel/plugin-transform-unicode-regex": "7.27.1",
"@babel/preset-typescript": "7.27.1",
"convert-source-map": "2.0.0",
"semver": "7.7.3"
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
"@babel/plugin-transform-class-properties": "^7.0.0-0",
"@babel/plugin-transform-classes": "^7.0.0-0",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0",
"@babel/plugin-transform-optional-chaining": "^7.0.0-0",
"@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
"@babel/plugin-transform-template-literals": "^7.0.0-0",
"@babel/plugin-transform-unicode-regex": "^7.0.0-0",
"@babel/preset-typescript": "^7.16.7",
"convert-source-map": "^2.0.0",
"semver": "7.7.2"
},
"peerDependencies": {
"@babel/core": "*",
"@babel/core": "^7.0.0-0",
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-worklets/node_modules/@babel/plugin-transform-class-properties": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz",
"integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==",
"peer": true,
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/react-native-worklets/node_modules/@babel/plugin-transform-classes": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz",
"integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==",
"peer": true,
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.27.3",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-globals": "^7.28.0",
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1",
"@babel/traverse": "^7.28.4"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/react-native-worklets/node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz",
"integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/react-native-worklets/node_modules/@babel/plugin-transform-optional-chaining": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
"integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/react-native-worklets/node_modules/@babel/preset-typescript": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz",
"integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-validator-option": "^7.27.1",
"@babel/plugin-syntax-jsx": "^7.27.1",
"@babel/plugin-transform-modules-commonjs": "^7.27.1",
"@babel/plugin-transform-typescript": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/react-native-worklets/node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"peer": true,
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -7573,9 +7484,9 @@
}
},
"node_modules/tar": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz",
"integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",
@@ -110,8 +110,9 @@ Options:
-l --loose
Interpret versions and ranges loosely
-n <0|1>
This is the base to be used for the prerelease identifier.
-n <0|1|false>
Base number for prerelease identifier (default: 0).
Use false to omit the number altogether.
-p --include-prerelease
Always include prerelease versions in range matching
@@ -105,7 +105,7 @@ const main = () => {
versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => {
return semver.valid(v)
return semver.valid(v, options)
})
if (!versions.length) {
return fail()
@@ -53,7 +53,7 @@ const diff = (version1, version2) => {
return prefix + 'patch'
}
// high and low are preleases
// high and low are prereleases
return 'prerelease'
}
@@ -78,8 +78,8 @@ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
// ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier.
// Non-numberic identifiers include numberic identifiers but can be longer.
// Therefore non-numberic identifiers must go first.
// Non-numeric identifiers include numeric identifiers but can be longer.
// Therefore non-numeric identifiers must go first.
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIER]})`)
@@ -1,6 +1,6 @@
{
"name": "semver",
"version": "7.7.3",
"version": "7.7.4",
"description": "The semantic version parser used by npm.",
"main": "index.js",
"scripts": {
@@ -14,8 +14,8 @@
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.25.1",
"@npmcli/eslint-config": "^6.0.0",
"@npmcli/template-oss": "4.29.0",
"benchmark": "^2.1.4",
"tap": "^16.0.0"
},
@@ -52,7 +52,7 @@
"author": "GitHub Inc.",
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.25.1",
"version": "4.29.0",
"engines": ">=10",
"distPaths": [
"classes/",
@@ -38,7 +38,7 @@ const compare = require('../functions/compare.js')
// - If LT
// - If LT.semver is greater than any < or <= comp in C, return false
// - If LT is <=, and LT.semver does not satisfy every C, return false
// - If GT.semver has a prerelease, and not in prerelease mode
// - If LT.semver has a prerelease, and not in prerelease mode
// - If no C has a prerelease and the LT.semver tuple, return false
// - Else return true
+2 -2
View File
@@ -32,7 +32,7 @@ buildscript {
def reactNativeVersion = project.extensions.getByType(ExpoModuleExtension).reactNativeVersion
group = 'host.exp.exponent'
version = '54.0.32'
version = '54.0.33'
expoModule {
// We can't prebuild the module because it depends on the generated files.
@@ -43,7 +43,7 @@ android {
namespace "expo.core"
defaultConfig {
versionCode 1
versionName "54.0.32"
versionName "54.0.33"
consumerProguardFiles("proguard-rules.pro")
}
testOptions {
+3 -3
View File
@@ -42,7 +42,7 @@
"expo-document-picker": "~14.0.8",
"expo-file-system": "~19.0.21",
"expo-font": "~14.0.11",
"expo-gl": "~16.0.9",
"expo-gl": "~16.0.10",
"expo-glass-effect": "~0.1.8",
"expo-google-app-auth": "~8.3.0",
"expo-haptics": "~15.0.8",
@@ -71,7 +71,7 @@
"expo-notifications": "~0.32.16",
"expo-print": "~15.0.8",
"expo-live-photo": "~1.0.8",
"expo-router": "~6.0.22",
"expo-router": "~6.0.23",
"expo-screen-capture": "~8.0.9",
"expo-screen-orientation": "~9.0.8",
"expo-secure-store": "~15.0.8",
@@ -92,7 +92,7 @@
"expo-video-thumbnails": "~10.0.8",
"expo-video": "~3.0.15",
"expo-web-browser": "~15.0.10",
"jest-expo": "~54.0.16",
"jest-expo": "~54.0.17",
"lottie-react-native": "~7.3.1",
"react": "19.1.0",
"react-dom": "19.1.0",
+1 -1
View File
@@ -123,7 +123,7 @@ const args = (0, _arg().default)({
});
if (args['--version']) {
// Version is added in the build script.
console.log("54.0.22");
console.log("54.0.23");
process.exit(0);
}
if (args['--non-interactive']) {
@@ -15,19 +15,11 @@ function _ws() {
};
return data;
}
const _net = require("../../../utils/net");
function createDevToolsPluginWebsocketEndpoint({ serverBaseUrl }) {
function createDevToolsPluginWebsocketEndpoint() {
const wss = new (_ws()).WebSocketServer({
noServer: true
});
wss.on('connection', (ws, request)=>{
// Explicitly limit devtools websocket to loopback requests
if (!(0, _net.isLocalSocket)(request.socket) || !(0, _net.isMatchingOrigin)(request, serverBaseUrl)) {
// NOTE: `socket.close` nicely closes the websocket, which will still allow incoming messages
// `socket.terminate` instead forcefully closes down the socket
ws.terminate();
return;
}
ws.on('message', (message, isBinary)=>{
// Broadcast the received message to all other connected clients
wss.clients.forEach((client)=>{
@@ -1 +1 @@
{"version":3,"sources":["../../../../../src/start/server/metro/DevToolsPluginWebsocketEndpoint.ts"],"sourcesContent":["import { WebSocket, WebSocketServer } from 'ws';\n\nimport { isLocalSocket, isMatchingOrigin } from '../../../utils/net';\n\ninterface DevToolsPluginWebsocketEndpointParams {\n serverBaseUrl: string;\n}\n\nexport function createDevToolsPluginWebsocketEndpoint({\n serverBaseUrl,\n}: DevToolsPluginWebsocketEndpointParams): Record<string, WebSocketServer> {\n const wss = new WebSocketServer({ noServer: true });\n\n wss.on('connection', (ws, request) => {\n // Explicitly limit devtools websocket to loopback requests\n if (!isLocalSocket(request.socket) || !isMatchingOrigin(request, serverBaseUrl)) {\n // NOTE: `socket.close` nicely closes the websocket, which will still allow incoming messages\n // `socket.terminate` instead forcefully closes down the socket\n ws.terminate();\n return;\n }\n\n ws.on('message', (message, isBinary) => {\n // Broadcast the received message to all other connected clients\n wss.clients.forEach((client) => {\n if (client !== ws && client.readyState === WebSocket.OPEN) {\n client.send(message, { binary: isBinary });\n }\n });\n });\n });\n\n return { '/expo-dev-plugins/broadcast': wss };\n}\n"],"names":["createDevToolsPluginWebsocketEndpoint","serverBaseUrl","wss","WebSocketServer","noServer","on","ws","request","isLocalSocket","socket","isMatchingOrigin","terminate","message","isBinary","clients","forEach","client","readyState","WebSocket","OPEN","send","binary"],"mappings":";;;;+BAQgBA;;;eAAAA;;;;yBAR2B;;;;;;qBAEK;AAMzC,SAASA,sCAAsC,EACpDC,aAAa,EACyB;IACtC,MAAMC,MAAM,IAAIC,CAAAA,KAAc,iBAAC,CAAC;QAAEC,UAAU;IAAK;IAEjDF,IAAIG,EAAE,CAAC,cAAc,CAACC,IAAIC;QACxB,2DAA2D;QAC3D,IAAI,CAACC,IAAAA,kBAAa,EAACD,QAAQE,MAAM,KAAK,CAACC,IAAAA,qBAAgB,EAACH,SAASN,gBAAgB;YAC/E,6FAA6F;YAC7F,+DAA+D;YAC/DK,GAAGK,SAAS;YACZ;QACF;QAEAL,GAAGD,EAAE,CAAC,WAAW,CAACO,SAASC;YACzB,gEAAgE;YAChEX,IAAIY,OAAO,CAACC,OAAO,CAAC,CAACC;gBACnB,IAAIA,WAAWV,MAAMU,OAAOC,UAAU,KAAKC,eAAS,CAACC,IAAI,EAAE;oBACzDH,OAAOI,IAAI,CAACR,SAAS;wBAAES,QAAQR;oBAAS;gBAC1C;YACF;QACF;IACF;IAEA,OAAO;QAAE,+BAA+BX;IAAI;AAC9C"}
{"version":3,"sources":["../../../../../src/start/server/metro/DevToolsPluginWebsocketEndpoint.ts"],"sourcesContent":["import { WebSocket, WebSocketServer } from 'ws';\n\nexport function createDevToolsPluginWebsocketEndpoint(): Record<string, WebSocketServer> {\n const wss = new WebSocketServer({ noServer: true });\n\n wss.on('connection', (ws, request) => {\n ws.on('message', (message, isBinary) => {\n // Broadcast the received message to all other connected clients\n wss.clients.forEach((client) => {\n if (client !== ws && client.readyState === WebSocket.OPEN) {\n client.send(message, { binary: isBinary });\n }\n });\n });\n });\n\n return { '/expo-dev-plugins/broadcast': wss };\n}\n"],"names":["createDevToolsPluginWebsocketEndpoint","wss","WebSocketServer","noServer","on","ws","request","message","isBinary","clients","forEach","client","readyState","WebSocket","OPEN","send","binary"],"mappings":";;;;+BAEgBA;;;eAAAA;;;;yBAF2B;;;;;;AAEpC,SAASA;IACd,MAAMC,MAAM,IAAIC,CAAAA,KAAc,iBAAC,CAAC;QAAEC,UAAU;IAAK;IAEjDF,IAAIG,EAAE,CAAC,cAAc,CAACC,IAAIC;QACxBD,GAAGD,EAAE,CAAC,WAAW,CAACG,SAASC;YACzB,gEAAgE;YAChEP,IAAIQ,OAAO,CAACC,OAAO,CAAC,CAACC;gBACnB,IAAIA,WAAWN,MAAMM,OAAOC,UAAU,KAAKC,eAAS,CAACC,IAAI,EAAE;oBACzDH,OAAOI,IAAI,CAACR,SAAS;wBAAES,QAAQR;oBAAS;gBAC1C;YACF;QACF;IACF;IAEA,OAAO;QAAE,+BAA+BP;IAAI;AAC9C"}
@@ -262,9 +262,7 @@ async function instantiateMetroAsync(metroBundler, options, { isExporting, exp =
}
return middleware.use(metroMiddleware);
};
const devtoolsWebsocketEndpoints = (0, _DevToolsPluginWebsocketEndpoint.createDevToolsPluginWebsocketEndpoint)({
serverBaseUrl
});
const devtoolsWebsocketEndpoints = (0, _DevToolsPluginWebsocketEndpoint.createDevToolsPluginWebsocketEndpoint)();
Object.assign(websocketEndpoints, devtoolsWebsocketEndpoints);
}
// Attach Expo Atlas if enabled
File diff suppressed because one or more lines are too long
@@ -33,7 +33,7 @@ class FetchClient {
this.headers = {
accept: 'application/json',
'content-type': 'application/json',
'user-agent': `expo-cli/${"54.0.22"}`,
'user-agent': `expo-cli/${"54.0.23"}`,
authorization: 'Basic ' + _nodebuffer().Buffer.from(`${target}:`).toString('base64')
};
}
@@ -83,7 +83,7 @@ function createContext() {
cpu: summarizeCpuInfo(),
app: {
name: 'expo/cli',
version: "54.0.22"
version: "54.0.23"
},
ci: _ciinfo().isCI ? {
name: _ciinfo().name,
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/cli",
"version": "54.0.22",
"version": "54.0.23",
"description": "The Expo CLI",
"main": "build/bin/cli",
"bin": {
@@ -169,5 +169,5 @@
"tree-kill": "^1.2.2",
"tsd": "^0.28.1"
},
"gitHead": "5205083d153ffa0c5e48db969aeaa1f9810453fc"
"gitHead": "cfd65241fb73317b63e6736e7890a784a69940fc"
}
+3 -2
View File
@@ -110,8 +110,9 @@ Options:
-l --loose
Interpret versions and ranges loosely
-n <0|1>
This is the base to be used for the prerelease identifier.
-n <0|1|false>
Base number for prerelease identifier (default: 0).
Use false to omit the number altogether.
-p --include-prerelease
Always include prerelease versions in range matching
+1 -1
View File
@@ -105,7 +105,7 @@ const main = () => {
versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => {
return semver.valid(v)
return semver.valid(v, options)
})
if (!versions.length) {
return fail()
+1 -1
View File
@@ -53,7 +53,7 @@ const diff = (version1, version2) => {
return prefix + 'patch'
}
// high and low are preleases
// high and low are prereleases
return 'prerelease'
}
+2 -2
View File
@@ -78,8 +78,8 @@ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
// ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier.
// Non-numberic identifiers include numberic identifiers but can be longer.
// Therefore non-numberic identifiers must go first.
// Non-numeric identifiers include numeric identifiers but can be longer.
// Therefore non-numeric identifiers must go first.
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIER]})`)
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "semver",
"version": "7.7.3",
"version": "7.7.4",
"description": "The semantic version parser used by npm.",
"main": "index.js",
"scripts": {
@@ -14,8 +14,8 @@
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.25.1",
"@npmcli/eslint-config": "^6.0.0",
"@npmcli/template-oss": "4.29.0",
"benchmark": "^2.1.4",
"tap": "^16.0.0"
},
@@ -52,7 +52,7 @@
"author": "GitHub Inc.",
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.25.1",
"version": "4.29.0",
"engines": ">=10",
"distPaths": [
"classes/",
+1 -1
View File
@@ -38,7 +38,7 @@ const compare = require('../functions/compare.js')
// - If LT
// - If LT.semver is greater than any < or <= comp in C, return false
// - If LT is <=, and LT.semver does not satisfy every C, return false
// - If GT.semver has a prerelease, and not in prerelease mode
// - If LT.semver has a prerelease, and not in prerelease mode
// - If no C has a prerelease and the LT.semver tuple, return false
// - Else return true
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "expo",
"version": "54.0.32",
"version": "54.0.33",
"description": "The Expo SDK",
"main": "src/Expo.ts",
"module": "src/Expo.ts",
@@ -75,7 +75,7 @@
"homepage": "https://github.com/expo/expo/tree/main/packages/expo",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "54.0.22",
"@expo/cli": "54.0.23",
"@expo/config": "~12.0.13",
"@expo/config-plugins": "~54.0.4",
"@expo/devtools": "0.1.8",
@@ -124,5 +124,5 @@
"optional": true
}
},
"gitHead": "5205083d153ffa0c5e48db969aeaa1f9810453fc"
"gitHead": "cfd65241fb73317b63e6736e7890a784a69940fc"
}
BIN
View File
Binary file not shown.
+3 -2
View File
@@ -110,8 +110,9 @@ Options:
-l --loose
Interpret versions and ranges loosely
-n <0|1>
This is the base to be used for the prerelease identifier.
-n <0|1|false>
Base number for prerelease identifier (default: 0).
Use false to omit the number altogether.
-p --include-prerelease
Always include prerelease versions in range matching
@@ -105,7 +105,7 @@ const main = () => {
versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => {
return semver.valid(v)
return semver.valid(v, options)
})
if (!versions.length) {
return fail()
@@ -53,7 +53,7 @@ const diff = (version1, version2) => {
return prefix + 'patch'
}
// high and low are preleases
// high and low are prereleases
return 'prerelease'
}
@@ -78,8 +78,8 @@ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
// ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier.
// Non-numberic identifiers include numberic identifiers but can be longer.
// Therefore non-numberic identifiers must go first.
// Non-numeric identifiers include numeric identifiers but can be longer.
// Therefore non-numeric identifiers must go first.
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIER]})`)
@@ -1,6 +1,6 @@
{
"name": "semver",
"version": "7.7.3",
"version": "7.7.4",
"description": "The semantic version parser used by npm.",
"main": "index.js",
"scripts": {
@@ -14,8 +14,8 @@
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.25.1",
"@npmcli/eslint-config": "^6.0.0",
"@npmcli/template-oss": "4.29.0",
"benchmark": "^2.1.4",
"tap": "^16.0.0"
},
@@ -52,7 +52,7 @@
"author": "GitHub Inc.",
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.25.1",
"version": "4.29.0",
"engines": ">=10",
"distPaths": [
"classes/",
@@ -38,7 +38,7 @@ const compare = require('../functions/compare.js')
// - If LT
// - If LT.semver is greater than any < or <= comp in C, return false
// - If LT is <=, and LT.semver does not satisfy every C, return false
// - If GT.semver has a prerelease, and not in prerelease mode
// - If LT.semver has a prerelease, and not in prerelease mode
// - If no C has a prerelease and the LT.semver tuple, return false
// - Else return true
@@ -1,7 +1,6 @@
#include <jsi/jsi.h>
#include <worklets/AnimationFrameQueue/AnimationFrameBatchinator.h>
#include <worklets/SharedItems/Serializable.h>
#include <worklets/WorkletRuntime/RuntimeBindings.h>
#include <atomic>
#include <functional>
@@ -12,22 +11,26 @@
namespace worklets {
void AnimationFrameBatchinator::addToBatch(jsi::Function &&callback) {
void AnimationFrameBatchinator::addToBatch(
const facebook::jsi::Value &callback) {
{
std::lock_guard<std::mutex> lock(callbacksMutex_);
callbacks_.push_back(std::make_shared<const jsi::Function>(std::move(callback)));
callbacks_.push_back(
std::make_shared<const facebook::jsi::Value>(*uiRuntime_, callback));
}
flush();
}
AnimationFrameBatchinator::JsiRequestAnimationFrame AnimationFrameBatchinator::getJsiRequestAnimationFrame() {
return [weakThis = weak_from_this()](facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback) {
AnimationFrameBatchinator::JsiRequestAnimationFrame
AnimationFrameBatchinator::getJsiRequestAnimationFrame() {
return [weakThis = weak_from_this()](
facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback) {
const auto strongThis = weakThis.lock();
if (!strongThis) {
return;
}
strongThis->addToBatch(callback.asObject(rt).asFunction(rt));
strongThis->addToBatch(callback);
};
}
@@ -45,21 +48,24 @@ void AnimationFrameBatchinator::flush() {
auto callbacks = strongThis->pullCallbacks();
strongThis->flushRequested_ = false;
auto &uiWorkletRuntime = strongThis->uiWorkletRuntime_;
auto &uiRuntime = *(strongThis->uiRuntime_);
for (const auto &callback : callbacks) {
uiWorkletRuntime->runSync(*callback, timestampMs);
runOnRuntimeGuarded(uiRuntime, *callback, timestampMs);
}
});
}
std::vector<std::shared_ptr<const jsi::Function>> AnimationFrameBatchinator::pullCallbacks() {
std::vector<std::shared_ptr<const facebook::jsi::Value>>
AnimationFrameBatchinator::pullCallbacks() {
std::lock_guard<std::mutex> lock(callbacksMutex_);
return std::move(callbacks_);
}
AnimationFrameBatchinator::AnimationFrameBatchinator(
const std::shared_ptr<WorkletRuntime> &uiWorkletRuntime,
RuntimeBindings::RequestAnimationFrame requestAnimationFrame)
: uiWorkletRuntime_(uiWorkletRuntime), requestAnimationFrame_(std::move(requestAnimationFrame)) {}
facebook::jsi::Runtime &uiRuntime,
std::function<void(std::function<void(const double)>)>
&&forwardedRequestAnimationFrame)
: uiRuntime_(&uiRuntime),
requestAnimationFrame_(std::move(forwardedRequestAnimationFrame)) {}
} // namespace worklets
@@ -2,9 +2,6 @@
#include <jsi/jsi.h>
#include <worklets/SharedItems/Serializable.h>
#include <worklets/WorkletRuntime/RuntimeBindings.h>
#include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <atomic>
#include <functional>
#include <memory>
@@ -13,25 +10,28 @@
namespace worklets {
class AnimationFrameBatchinator : public std::enable_shared_from_this<AnimationFrameBatchinator> {
class AnimationFrameBatchinator
: public std::enable_shared_from_this<AnimationFrameBatchinator> {
public:
using JsiRequestAnimationFrame = std::function<void(jsi::Runtime &, const jsi::Value &)>;
using JsiRequestAnimationFrame = std::function<
void(facebook::jsi::Runtime &, const facebook::jsi::Value &)>;
void addToBatch(jsi::Function &&callback);
void addToBatch(const facebook::jsi::Value &callback);
JsiRequestAnimationFrame getJsiRequestAnimationFrame();
AnimationFrameBatchinator(
const std::shared_ptr<WorkletRuntime> &uiWorkletRuntime,
RuntimeBindings::RequestAnimationFrame requestAnimationFrame);
facebook::jsi::Runtime &uiRuntime,
std::function<void(std::function<void(const double)>)>
&&forwardedRequestAnimationFrame);
private:
void flush();
std::vector<std::shared_ptr<const jsi::Function>> pullCallbacks();
std::vector<std::shared_ptr<const facebook::jsi::Value>> pullCallbacks();
std::vector<std::shared_ptr<const jsi::Function>> callbacks_{};
std::vector<std::shared_ptr<const facebook::jsi::Value>> callbacks_{};
std::mutex callbacksMutex_{};
std::atomic_bool flushRequested_{};
std::shared_ptr<WorkletRuntime> uiWorkletRuntime_;
facebook::jsi::Runtime *uiRuntime_;
std::function<void(std::function<void(const double)>)> requestAnimationFrame_;
};
@@ -14,10 +14,7 @@
#include <fbjni/fbjni.h>
#endif // __ANDROID__
#include <memory>
#include <string>
#include <utility>
#include <vector>
using namespace facebook;
@@ -50,7 +47,7 @@ inline void scheduleOnUI(
const auto scope = jsi::Scope(uiWorkletRuntime->getJSIRuntime());
#endif // JS_RUNTIME_HERMES
uiWorkletRuntime->runSync(serializableWorklet);
uiWorkletRuntime->runGuarded(serializableWorklet);
});
}
@@ -59,10 +56,7 @@ inline jsi::Value executeOnUIRuntimeSync(
jsi::Runtime &rt,
const jsi::Value &worklet) {
if (auto uiWorkletRuntime = weakUIWorkletRuntime.lock()) {
auto serializableWorklet = extractSerializableOrThrow<SerializableWorklet>(
rt, worklet, "[Worklets] Only worklets can be executed on UI runtime.");
auto serializedResult = uiWorkletRuntime->runSyncSerialized(serializableWorklet);
return serializedResult->toJSValue(rt);
return uiWorkletRuntime->executeSync(rt, worklet);
}
return jsi::Value::undefined();
}
@@ -76,8 +70,8 @@ inline jsi::Value createWorkletRuntime(
std::shared_ptr<SerializableWorklet> &initializer,
const std::shared_ptr<AsyncQueue> &queue,
bool enableEventLoop) {
const auto workletRuntime =
runtimeManager->createWorkletRuntime(std::move(jsiWorkletsModuleProxy), name, initializer, queue);
const auto workletRuntime = runtimeManager->createWorkletRuntime(
jsiWorkletsModuleProxy, name, initializer, queue);
return jsi::Object::createFromHostObject(originRuntime, workletRuntime);
}
@@ -89,9 +83,10 @@ inline jsi::Value propagateModuleUpdate(
const auto runtimes = runtimeManager->getAllRuntimes();
for (auto runtime : runtimes) {
runtime->runSync([code, sourceUrl](jsi::Runtime &rt) -> void {
runtime->executeSync([code, sourceUrl](jsi::Runtime &rt) {
const auto buffer = std::make_shared<jsi::StringBuffer>(code);
rt.evaluateJavaScript(buffer, sourceUrl);
return jsi::Value::undefined();
});
}
return jsi::Value::undefined();
@@ -105,11 +100,18 @@ inline jsi::Value reportFatalErrorOnJS(
const std::string &name,
const std::string &jsEngine) {
JSLogger::reportFatalErrorOnJS(
jsScheduler, JSErrorData{.message = message, .stack = stack, .name = name, .jsEngine = jsEngine});
jsScheduler,
JSErrorData{
.message = message,
.stack = stack,
.name = name,
.jsEngine = jsEngine});
return jsi::Value::undefined();
}
inline std::shared_ptr<AsyncQueue> extractAsyncQueue(jsi::Runtime &rt, const jsi::Value &value) {
inline std::shared_ptr<AsyncQueue> extractAsyncQueue(
jsi::Runtime &rt,
const jsi::Value &value) {
if (!value.isObject()) {
return nullptr;
}
@@ -125,33 +127,13 @@ inline std::shared_ptr<AsyncQueue> extractAsyncQueue(jsi::Runtime &rt, const jsi
return asyncQueue;
}
inline void registerCustomSerializable(
const std::shared_ptr<RuntimeManager> &runtimeManager,
const std::shared_ptr<MemoryManager> &memoryManager,
const std::shared_ptr<SerializableWorklet> &determine,
const std::shared_ptr<SerializableWorklet> &pack,
const std::shared_ptr<SerializableWorklet> &unpack,
const int typeId) {
const SerializationData data{.determine = determine, .pack = pack, .unpack = unpack, .typeId = typeId};
// Prevent registering new worklet runtimes while we are updating existing ones to prevent inconsistencies.
runtimeManager->pause();
memoryManager->registerCustomSerializable(data);
for (const auto &runtime : runtimeManager->getAllRuntimes()) {
memoryManager->loadCustomSerializable(runtime, data);
}
runtimeManager->resume();
}
JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(
const bool isDevBundle,
const std::shared_ptr<const JSBigStringBuffer> &script,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl,
const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<JSScheduler> &jsScheduler,
const std::shared_ptr<UIScheduler> &uiScheduler,
const std::shared_ptr<MemoryManager> &memoryManager,
const std::shared_ptr<RuntimeManager> &runtimeManager,
const std::weak_ptr<WorkletRuntime> &uiWorkletRuntime)
: jsi::HostObject(),
@@ -161,11 +143,11 @@ JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(
jsQueue_(jsQueue),
jsScheduler_(jsScheduler),
uiScheduler_(uiScheduler),
memoryManager_(memoryManager),
runtimeManager_(runtimeManager),
uiWorkletRuntime_(uiWorkletRuntime) {}
JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(const JSIWorkletsModuleProxy &other)
JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(
const JSIWorkletsModuleProxy &other)
: jsi::HostObject(),
isDevBundle_(other.isDevBundle_),
script_(other.script_),
@@ -173,203 +155,315 @@ JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(const JSIWorkletsModuleProxy &oth
jsQueue_(other.jsQueue_),
jsScheduler_(other.jsScheduler_),
uiScheduler_(other.uiScheduler_),
memoryManager_(other.memoryManager_),
runtimeManager_(other.runtimeManager_),
uiWorkletRuntime_(other.uiWorkletRuntime_) {}
JSIWorkletsModuleProxy::~JSIWorkletsModuleProxy() = default;
std::vector<jsi::PropNameID> JSIWorkletsModuleProxy::getPropertyNames(jsi::Runtime &rt) {
std::vector<jsi::PropNameID> JSIWorkletsModuleProxy::getPropertyNames(
jsi::Runtime &rt) {
std::vector<jsi::PropNameID> propertyNames;
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializable"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableBigInt"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableBoolean"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableImport"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableNull"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableNumber"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableString"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableUndefined"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableHostObject"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableInitializer"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableArray"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableFunction"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableTurboModuleLike"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableObject"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableMap"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableSet"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableWorklet"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createCustomSerializable"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "registerCustomSerializable"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializable"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableBigInt"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableBoolean"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableImport"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableNull"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableNumber"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableString"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableUndefined"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableHostObject"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableInitializer"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableArray"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableFunction"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableTurboModuleLike"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableObject"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableMap"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableSet"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSerializableWorklet"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "scheduleOnUI"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "executeOnUIRuntimeSync"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createWorkletRuntime"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "scheduleOnRuntime"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "reportFatalErrorOnJS"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "executeOnUIRuntimeSync"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createWorkletRuntime"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "scheduleOnRuntime"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "reportFatalErrorOnJS"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "getStaticFeatureFlag"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "setDynamicFeatureFlag"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "getStaticFeatureFlag"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "setDynamicFeatureFlag"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSynchronizable"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableGetDirty"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableGetBlocking"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableSetBlocking"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableLock"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableUnlock"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "createSynchronizable"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "synchronizableGetDirty"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "synchronizableGetBlocking"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "synchronizableSetBlocking"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "synchronizableLock"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "synchronizableUnlock"));
#ifdef WORKLETS_BUNDLE_MODE
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "propagateModuleUpdate"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "propagateModuleUpdate"));
#endif // WORKLETS_BUNDLE_MODE
return propertyNames;
}
jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &propName) {
jsi::Value JSIWorkletsModuleProxy::get(
jsi::Runtime &rt,
const jsi::PropNameID &propName) {
const auto name = propName.utf8(rt);
if (name == "createSerializable") {
return jsi::Function::createFromHostFunction(
rt, propName, 3, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
3,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableClone(rt, args[0], args[1], args[2]);
});
}
if (name == "createSerializableBigInt") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableBigInt(rt, args[0].asBigInt(rt));
});
}
if (name == "createSerializableBoolean") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableBoolean(rt, args[0].asBool());
});
}
if (name == "createSerializableImport") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableImport(rt, args[0].asNumber(), args[1].asString(rt));
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableImport(
rt, args[0].asNumber(), args[1].asString(rt));
});
}
if (name == "createSerializableNumber") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableNumber(rt, args[0].asNumber());
});
}
if (name == "createSerializableNull") {
return jsi::Function::createFromHostFunction(
rt, propName, 0, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableNull(rt);
});
rt,
propName,
0,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) { return makeSerializableNull(rt); });
}
if (name == "createSerializableString") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableString(rt, args[0].asString(rt));
});
}
if (name == "createSerializableUndefined") {
return jsi::Function::createFromHostFunction(
rt, propName, 0, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableUndefined(rt);
});
rt,
propName,
0,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) { return makeSerializableUndefined(rt); });
}
if (name == "createSerializableInitializer") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableInitializer(rt, args[0].asObject(rt));
});
}
if (name == "createSerializableArray") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableArray(rt, args[0].asObject(rt).asArray(rt), args[1]);
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableArray(
rt, args[0].asObject(rt).asArray(rt), args[1]);
});
}
if (name == "createSerializableMap") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableMap(rt, args[0].asObject(rt).asArray(rt), args[1].asObject(rt).asArray(rt));
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableMap(
rt,
args[0].asObject(rt).asArray(rt),
args[1].asObject(rt).asArray(rt));
});
}
if (name == "createSerializableSet") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableSet(rt, args[0].asObject(rt).asArray(rt));
});
}
if (name == "createSerializableHostObject") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableHostObject(rt, args[0].asObject(rt).asHostObject(rt));
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableHostObject(
rt, args[0].asObject(rt).asHostObject(rt));
});
}
if (name == "createSerializableFunction") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableFunction(rt, args[0].asObject(rt).asFunction(rt));
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableFunction(
rt, args[0].asObject(rt).asFunction(rt));
});
}
if (name == "createSerializableTurboModuleLike") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableTurboModuleLike(rt, args[0].asObject(rt), args[1].asObject(rt).asHostObject(rt));
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableTurboModuleLike(
rt, args[0].asObject(rt), args[1].asObject(rt).asHostObject(rt));
});
}
if (name == "createSerializableObject") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableObject(rt, args[0].getObject(rt), args[1].getBool(), args[2]);
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableObject(
rt, args[0].getObject(rt), args[1].getBool(), args[2]);
});
}
if (name == "createSerializableWorklet") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeSerializableWorklet(rt, args[0].getObject(rt), args[1].getBool());
});
}
if (name == "createCustomSerializable") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
return makeCustomSerializable(rt, args[0], args[1].asNumber());
});
}
if (name == "registerCustomSerializable") {
return jsi::Function::createFromHostFunction(
rt,
propName,
4,
[memoryManager = memoryManager_, runtimeManager = runtimeManager_](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
const auto determine = extractSerializableOrThrow<SerializableWorklet>(
rt, args[0], "[Worklets] Determine function must be a worklet.");
const auto pack = extractSerializableOrThrow<SerializableWorklet>(
rt, args[1], "[Worklets] Pack function must be a worklet.");
const auto unpack = extractSerializableOrThrow<SerializableWorklet>(
rt, args[2], "[Worklets] Unpack function must be a worklet.");
const auto typeId = args[3].asNumber();
registerCustomSerializable(runtimeManager, memoryManager, determine, pack, unpack, typeId);
return jsi::Value::undefined();
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return makeSerializableWorklet(
rt, args[0].getObject(rt), args[1].getBool());
});
}
@@ -379,7 +473,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName,
1,
[uiScheduler = uiScheduler_, uiWorkletRuntime = uiWorkletRuntime_](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
scheduleOnUI(uiScheduler, uiWorkletRuntime, rt, args[0]);
return jsi::Value::undefined();
});
@@ -391,7 +488,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName,
1,
[uiWorkletRuntime = uiWorkletRuntime_](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return executeOnUIRuntimeSync(uiWorkletRuntime, rt, args[0]);
});
}
@@ -402,10 +502,14 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName,
5,
[clone = std::make_shared<JSIWorkletsModuleProxy>(*this)](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
const auto name = args[0].asString(rt).utf8(rt);
auto serializableInitializer =
extractSerializableOrThrow<SerializableWorklet>(rt, args[1], "[Worklets] Initializer must be a worklet.");
extractSerializableOrThrow<SerializableWorklet>(
rt, args[1], "[Worklets] Initializer must be a worklet.");
const auto useDefaultQueue = args[2].asBool();
std::shared_ptr<AsyncQueue> asyncQueue;
@@ -431,7 +535,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "scheduleOnRuntime") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi ::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi ::Value &thisValue,
const jsi::Value *args,
size_t count) {
worklets::scheduleOnRuntime(rt, args[0], args[1]);
return jsi::Value::undefined();
});
@@ -443,7 +553,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName,
4,
[jsScheduler = jsScheduler_](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return reportFatalErrorOnJS(
jsScheduler,
/* message */ args[0].asString(rt).utf8(rt),
@@ -455,8 +568,15 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "createSynchronizable") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
auto initial = extractSerializableOrThrow(rt, args[0], "[Worklets] Value must be a Serializable.");
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
auto initial = extractSerializableOrThrow(
rt, args[0], "[Worklets] Value must be a Serializable.");
auto synchronizable = std::make_shared<Synchronizable>(initial);
return SerializableJSRef::newNativeStateObject(rt, synchronizable);
});
@@ -464,7 +584,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableGetDirty") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
return synchronizable->getDirty()->toJSValue(rt);
});
@@ -472,7 +598,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableGetBlocking") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
return synchronizable->getBlocking()->toJSValue(rt);
});
@@ -480,17 +612,30 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableSetBlocking") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
auto newValue = extractSerializableOrThrow(rt, args[1], "[Worklets] Value must be a Serializable.");
synchronizable->setBlocking(newValue);
auto newValue = extractSerializableOrThrow(
rt, args[1], "[Worklets] Value must be a Serializable.");
synchronizable->setBlocking(std::move(newValue));
return jsi::Value::undefined();
});
}
if (name == "synchronizableLock") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
synchronizable->lock();
return jsi::Value::undefined();
@@ -499,7 +644,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableUnlock") {
return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
1,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
synchronizable->unlock();
return jsi::Value::undefined();
@@ -513,7 +664,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName,
2,
[runtimeManager = runtimeManager_](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return propagateModuleUpdate(
runtimeManager,
/* code */ args[0].asString(rt).utf8(rt),
@@ -524,7 +678,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "getStaticFeatureFlag") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return worklets::StaticFeatureFlags::getFlag(
/* name */ args[0].asString(rt).utf8(rt));
});
@@ -532,7 +692,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "setDynamicFeatureFlag") {
return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
worklets::DynamicFeatureFlags::setFlag(
/* name */ args[0].asString(rt).utf8(rt),
/* value */ args[1].asBool());
@@ -5,7 +5,6 @@
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/primitives.h>
#include <worklets/SharedItems/MemoryManager.h>
#include <worklets/SharedItems/Serializable.h>
#include <worklets/Tools/Defs.h>
#include <worklets/WorkletRuntime/RuntimeManager.h>
@@ -31,12 +30,11 @@ class JSIWorkletsModuleProxy : public jsi::HostObject {
public:
explicit JSIWorkletsModuleProxy(
const bool isDevBundle,
const std::shared_ptr<const JSBigStringBuffer> &script,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl,
const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<JSScheduler> &jsScheduler,
const std::shared_ptr<UIScheduler> &uiScheduler,
const std::shared_ptr<MemoryManager> &memoryManager,
const std::shared_ptr<RuntimeManager> &runtimeManager,
const std::weak_ptr<WorkletRuntime> &uiWorkletRuntime);
@@ -64,7 +62,7 @@ class JSIWorkletsModuleProxy : public jsi::HostObject {
return isDevBundle_;
}
[[nodiscard]] std::shared_ptr<const JSBigStringBuffer> getScript() const {
[[nodiscard]] std::shared_ptr<const BigStringBuffer> getScript() const {
return script_;
}
@@ -72,22 +70,17 @@ class JSIWorkletsModuleProxy : public jsi::HostObject {
return sourceUrl_;
}
[[nodiscard]] std::shared_ptr<MemoryManager> getMemoryManager() const {
return memoryManager_;
}
[[nodiscard]] std::shared_ptr<RuntimeManager> getRuntimeManager() const {
return runtimeManager_;
}
private:
const bool isDevBundle_;
const std::shared_ptr<const JSBigStringBuffer> script_;
const std::shared_ptr<const BigStringBuffer> script_;
const std::string sourceUrl_;
const std::shared_ptr<MessageQueueThread> jsQueue_;
const std::shared_ptr<JSScheduler> jsScheduler_;
const std::shared_ptr<UIScheduler> uiScheduler_;
const std::shared_ptr<MemoryManager> memoryManager_;
const std::shared_ptr<RuntimeManager> runtimeManager_;
const std::weak_ptr<WorkletRuntime> uiWorkletRuntime_;
};
@@ -5,7 +5,6 @@
#include <worklets/RunLoop/AsyncQueueImpl.h>
#include <worklets/SharedItems/Serializable.h>
#include <worklets/Tools/Defs.h>
#include <worklets/WorkletRuntime/RuntimeBindings.h>
#include <worklets/WorkletRuntime/UIRuntimeDecorator.h>
#ifdef __ANDROID__
@@ -13,7 +12,6 @@
#endif // __ANDROID__
#include <memory>
#include <string>
#include <utility>
using namespace facebook;
@@ -28,36 +26,44 @@ WorkletsModuleProxy::WorkletsModuleProxy(
const std::shared_ptr<CallInvoker> &jsCallInvoker,
const std::shared_ptr<UIScheduler> &uiScheduler,
std::function<bool()> &&isJavaScriptThread,
RuntimeBindings runtimeBindings,
const std::shared_ptr<const JSBigStringBuffer> &script,
std::function<void(std::function<void(const double)>)>
&&forwardedRequestAnimationFrame,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl)
: isDevBundle_(isDevBundleFromRNRuntime(rnRuntime)),
jsQueue_(jsQueue),
jsScheduler_(std::make_shared<JSScheduler>(rnRuntime, jsCallInvoker, std::move(isJavaScriptThread))),
jsScheduler_(std::make_shared<JSScheduler>(
rnRuntime,
jsCallInvoker,
std::move(isJavaScriptThread))),
uiScheduler_(uiScheduler),
jsLogger_(std::make_shared<JSLogger>(jsScheduler_)),
runtimeBindings_(std::move(runtimeBindings)),
script_(script),
sourceUrl_(sourceUrl),
memoryManager_(std::make_shared<MemoryManager>()),
runtimeManager_(std::make_shared<RuntimeManager>()),
uiWorkletRuntime_(
runtimeManager_->createUninitializedUIRuntime(jsQueue_, std::make_shared<AsyncQueueUI>(uiScheduler_))) {
uiWorkletRuntime_(runtimeManager_->createUninitializedUIRuntime(
jsQueue_,
std::make_shared<AsyncQueueUI>(uiScheduler_))) {
/**
* We call additional `init` method here because
* JSIWorkletsModuleProxy needs a weak_ptr to the UI Runtime.
*/
uiWorkletRuntime_->init(createJSIWorkletsModuleProxy());
animationFrameBatchinator_ =
std::make_shared<AnimationFrameBatchinator>(uiWorkletRuntime_, runtimeBindings_.requestAnimationFrame);
animationFrameBatchinator_ = std::make_shared<AnimationFrameBatchinator>(
uiWorkletRuntime_->getJSIRuntime(),
std::move(forwardedRequestAnimationFrame));
UIRuntimeDecorator::decorate(
uiWorkletRuntime_->getJSIRuntime(), animationFrameBatchinator_->getJsiRequestAnimationFrame());
uiWorkletRuntime_->getJSIRuntime(),
animationFrameBatchinator_->getJsiRequestAnimationFrame());
}
std::shared_ptr<JSIWorkletsModuleProxy> WorkletsModuleProxy::createJSIWorkletsModuleProxy() const {
assert(uiWorkletRuntime_ && "UI Worklet Runtime must be initialized before creating JSI proxy.");
std::shared_ptr<JSIWorkletsModuleProxy>
WorkletsModuleProxy::createJSIWorkletsModuleProxy() const {
assert(
uiWorkletRuntime_ &&
"UI Worklet Runtime must be initialized before creating JSI proxy.");
return std::make_shared<JSIWorkletsModuleProxy>(
isDevBundle_,
script_,
@@ -65,7 +71,6 @@ std::shared_ptr<JSIWorkletsModuleProxy> WorkletsModuleProxy::createJSIWorkletsMo
jsQueue_,
jsScheduler_,
uiScheduler_,
memoryManager_,
runtimeManager_,
uiWorkletRuntime_);
}
@@ -5,8 +5,6 @@
#include <jsireact/JSIExecutor.h>
#include <worklets/AnimationFrameQueue/AnimationFrameBatchinator.h>
#include <worklets/NativeModules/JSIWorkletsModuleProxy.h>
#include <worklets/SharedItems/MemoryManager.h>
#include <worklets/Tools/Defs.h>
#include <worklets/Tools/JSLogger.h>
#include <worklets/Tools/JSScheduler.h>
#include <worklets/Tools/SingleInstanceChecker.h>
@@ -19,7 +17,8 @@
namespace worklets {
class WorkletsModuleProxy : public std::enable_shared_from_this<WorkletsModuleProxy> {
class WorkletsModuleProxy
: public std::enable_shared_from_this<WorkletsModuleProxy> {
public:
explicit WorkletsModuleProxy(
jsi::Runtime &rnRuntime,
@@ -27,8 +26,9 @@ class WorkletsModuleProxy : public std::enable_shared_from_this<WorkletsModulePr
const std::shared_ptr<CallInvoker> &jsCallInvoker,
const std::shared_ptr<UIScheduler> &uiScheduler,
std::function<bool()> &&isJavaScriptQueue,
RuntimeBindings runtimeBindings,
const std::shared_ptr<const JSBigStringBuffer> &script,
std::function<void(std::function<void(const double)>)>
&&forwardedRequestAnimationFrame,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl);
~WorkletsModuleProxy();
@@ -49,11 +49,13 @@ class WorkletsModuleProxy : public std::enable_shared_from_this<WorkletsModulePr
return jsLogger_;
}
[[nodiscard]] inline std::shared_ptr<WorkletRuntime> getUIWorkletRuntime() const {
[[nodiscard]] inline std::shared_ptr<WorkletRuntime> getUIWorkletRuntime()
const {
return uiWorkletRuntime_;
}
[[nodiscard]] std::shared_ptr<JSIWorkletsModuleProxy> createJSIWorkletsModuleProxy() const;
[[nodiscard]] std::shared_ptr<JSIWorkletsModuleProxy>
createJSIWorkletsModuleProxy() const;
[[nodiscard]] inline bool isDevBundle() const {
return isDevBundle_;
@@ -65,10 +67,8 @@ class WorkletsModuleProxy : public std::enable_shared_from_this<WorkletsModulePr
const std::shared_ptr<JSScheduler> jsScheduler_;
const std::shared_ptr<UIScheduler> uiScheduler_;
const std::shared_ptr<JSLogger> jsLogger_;
const RuntimeBindings runtimeBindings_;
const std::shared_ptr<const JSBigStringBuffer> script_;
const std::shared_ptr<const BigStringBuffer> script_;
const std::string sourceUrl_;
const std::shared_ptr<MemoryManager> memoryManager_;
const std::shared_ptr<RuntimeManager> runtimeManager_;
std::shared_ptr<WorkletRuntime> uiWorkletRuntime_;
std::shared_ptr<AnimationFrameBatchinator> animationFrameBatchinator_;
@@ -1,14 +1,13 @@
#include <worklets/Registries/EventHandlerRegistry.h>
#include <worklets/Tools/WorkletEventHandler.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace worklets {
void EventHandlerRegistry::registerEventHandler(const std::shared_ptr<WorkletEventHandler> &eventHandler) {
void EventHandlerRegistry::registerEventHandler(
const std::shared_ptr<WorkletEventHandler> &eventHandler) {
const std::lock_guard<std::mutex> lock(instanceMutex);
const auto &eventName = eventHandler->getEventName();
auto handlerId = eventHandler->getHandlerId();
@@ -62,27 +61,30 @@ void EventHandlerRegistry::processEvent(
const std::lock_guard<std::mutex> lock(instanceMutex);
auto handlersIt = eventMappingsWithoutTag.find(eventName);
if (handlersIt != eventMappingsWithoutTag.end()) {
for (const auto &handler : handlersIt->second) {
for (auto handler : handlersIt->second) {
handlersForEvent.push_back(handler.second);
}
}
const auto eventHash = std::make_pair(emitterReactTag, eventName);
auto handlersWithTagIt = eventMappingsWithTag.find(eventHash);
if (handlersWithTagIt != eventMappingsWithTag.end()) {
for (const auto &handler : handlersWithTagIt->second) {
for (auto handler : handlersWithTagIt->second) {
handlersForEvent.push_back(handler.second);
}
}
}
jsi::Runtime &rt = uiWorkletRuntime->getJSIRuntime();
eventPayload.asObject(rt).setProperty(rt, "eventName", jsi::String::createFromUtf8(rt, eventName));
for (const auto &handler : handlersForEvent) {
eventPayload.asObject(rt).setProperty(
rt, "eventName", jsi::String::createFromUtf8(rt, eventName));
for (auto handler : handlersForEvent) {
handler->process(uiWorkletRuntime, eventTimestamp, eventPayload);
}
}
bool EventHandlerRegistry::isAnyHandlerWaitingForEvent(const std::string &eventName, const int emitterReactTag) {
bool EventHandlerRegistry::isAnyHandlerWaitingForEvent(
const std::string &eventName,
const int emitterReactTag) {
const std::lock_guard<std::mutex> lock(instanceMutex);
const auto eventHash = std::make_pair(emitterReactTag, eventName);
auto it = eventMappingsWithTag.find(eventHash);
@@ -18,14 +18,20 @@ namespace worklets {
class WorkletEventHandler;
class EventHandlerRegistry {
std::map<std::pair<int, std::string>, std::unordered_map<uint64_t, std::shared_ptr<WorkletEventHandler>>>
std::map<
std::pair<int, std::string>,
std::unordered_map<uint64_t, std::shared_ptr<WorkletEventHandler>>>
eventMappingsWithTag;
std::map<std::string, std::unordered_map<uint64_t, std::shared_ptr<WorkletEventHandler>>> eventMappingsWithoutTag;
std::map<
std::string,
std::unordered_map<uint64_t, std::shared_ptr<WorkletEventHandler>>>
eventMappingsWithoutTag;
std::map<uint64_t, std::shared_ptr<WorkletEventHandler>> eventHandlers;
std::mutex instanceMutex;
public:
void registerEventHandler(const std::shared_ptr<WorkletEventHandler> &eventHandler);
void registerEventHandler(
const std::shared_ptr<WorkletEventHandler> &eventHandler);
void unregisterEventHandler(const uint64_t id);
void processEvent(
@@ -35,7 +41,9 @@ class EventHandlerRegistry {
const int emitterReactTag,
const jsi::Value &eventPayload);
bool isAnyHandlerWaitingForEvent(const std::string &eventName, const int emitterReactTag);
bool isAnyHandlerWaitingForEvent(
const std::string &eventName,
const int emitterReactTag);
};
} // namespace worklets
@@ -1,7 +1,5 @@
#include <worklets/Registries/WorkletRuntimeRegistry.h>
#include <set>
namespace worklets {
std::set<jsi::Runtime *> WorkletRuntimeRegistry::registry_{};
@@ -1,24 +0,0 @@
// This file was generated with
// `packages/react-native-worklets/scripts/export-unpackers.js`.
// Please do not modify it directly.
#include <worklets/Resources/Unpackers.h>
namespace worklets {
const char CustomSerializableUnpackerCode[] =
R"DELIMITER__((function () {
if (!globalThis.__customSerializationRegistry) {
globalThis.__customSerializationRegistry = [];
}
var registry = globalThis.__customSerializationRegistry;
function customSerializableUnpacker(value, typeId) {
var data = registry[typeId];
if (!data) {
throw new Error("[Worklets] No custom serializable registered for type ID ".concat(typeId, "."));
}
return data.unpack(value);
}
globalThis.__customSerializableUnpacker = customSerializableUnpacker;
})();)DELIMITER__";
} // namespace worklets
@@ -3,5 +3,4 @@
namespace worklets {
extern const char ValueUnpackerCode[];
extern const char SynchronizableUnpackerCode[];
extern const char CustomSerializableUnpackerCode[];
} // namespace worklets
@@ -40,7 +40,7 @@ const char ValueUnpackerCode[] =
} else if (category === 'RemoteFunction') {
var fun = function fun() {
var label = remoteFunctionName ? "function `".concat(remoteFunctionName, "`") : 'anonymous function';
throw new Error("[Worklets] Tried to synchronously call a non-worklet ".concat(label, " on the UI thread.\nSee https://docs.swmansion.com/react-native-worklets/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-worklet-function-on-the-ui-thread for more details."));
throw new Error("[Worklets] Tried to synchronously call a non-worklet ".concat(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;
@@ -1,14 +0,0 @@
#pragma once
#include <jsi/jsi.h>
namespace worklets {
class AsyncQueue : public facebook::jsi::NativeState {
public:
~AsyncQueue() override = default;
virtual void push(std::function<void()> &&job) = 0;
};
} // namespace worklets
@@ -1,20 +1,20 @@
#include <worklets/RunLoop/AsyncQueueImpl.h>
#include <memory>
#include <string>
#include <thread>
#include <utility>
namespace worklets {
AsyncQueueImpl::AsyncQueueImpl(const std::string &name) : state_(std::make_shared<AsyncQueueState>()) {
AsyncQueueImpl::AsyncQueueImpl(std::string name)
: state_(std::make_shared<AsyncQueueState>()) {
auto thread = std::thread([name, state = state_] {
#if __APPLE__
pthread_setname_np(name.c_str());
#endif
while (state->running) {
std::unique_lock<std::mutex> lock(state->mutex);
state->cv.wait(lock, [state] { return !state->queue.empty() || !state->running; });
state->cv.wait(
lock, [state] { return !state->queue.empty() || !state->running; });
if (!state->running) {
return;
}
@@ -50,7 +50,8 @@ void AsyncQueueImpl::push(std::function<void()> &&job) {
state_->cv.notify_one();
}
AsyncQueueUI::AsyncQueueUI(const std::shared_ptr<UIScheduler> &uiScheduler) : uiScheduler_(uiScheduler) {}
AsyncQueueUI::AsyncQueueUI(const std::shared_ptr<UIScheduler> &uiScheduler)
: uiScheduler_(uiScheduler) {}
void AsyncQueueUI::push(std::function<void()> &&job) {
uiScheduler_->scheduleOnUI(std::move(job));
@@ -1,7 +1,7 @@
#pragma once
#include <jsi/jsi.h>
#include <worklets/RunLoop/AsyncQueue.h>
#include <worklets/Public/AsyncQueue.h>
#include <worklets/Tools/UIScheduler.h>
#include <atomic>
@@ -21,7 +21,7 @@ struct AsyncQueueState {
class AsyncQueueImpl : public AsyncQueue {
public:
explicit AsyncQueueImpl(const std::string &name);
explicit AsyncQueueImpl(std::string name);
~AsyncQueueImpl() override;
@@ -1,7 +1,5 @@
#include <worklets/RunLoop/EventLoop.h>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
@@ -10,9 +8,12 @@ namespace worklets {
EventLoop::EventLoop(
const std::string &name,
const std::shared_ptr<jsi::Runtime> &runtime,
const std::shared_ptr<jsi::Runtime> runtime,
const std::shared_ptr<AsyncQueue> &queue)
: runtime_(runtime), queue_(queue), timeoutsQueueState_(std::make_shared<TimeoutsQueueState>()), name_(name) {}
: runtime_(runtime),
queue_(queue),
timeoutsQueueState_(std::make_shared<TimeoutsQueueState>()),
name_(name) {}
EventLoop::~EventLoop() {
{
@@ -25,50 +26,52 @@ EventLoop::~EventLoop() {
void EventLoop::run() {
const auto threadName = name_ + "(timeout)";
auto thread = std::thread([threadName, state = timeoutsQueueState_, weakThis = weak_from_this()] {
auto thread = std::thread(
[threadName, state = timeoutsQueueState_, weakThis = weak_from_this()] {
#if __APPLE__
pthread_setname_np(threadName.c_str());
pthread_setname_np(threadName.c_str());
#endif
while (state->running) {
std::unique_lock<std::mutex> lock(state->mutex);
while (state->running) {
std::unique_lock<std::mutex> lock(state->mutex);
if (state->queue.empty()) {
state->cv.wait(lock);
} else {
const auto &nextTimeout = state->queue[0];
const auto timeToWait = nextTimeout.targetTime - getCurrentTimeInMs();
state->cv.wait_for(lock, std::chrono::milliseconds(timeToWait));
}
if (state->queue.empty()) {
state->cv.wait(lock);
} else {
const auto &nextTimeout = state->queue[0];
const auto timeToWait =
nextTimeout.targetTime - getCurrentTimeInMs();
state->cv.wait_for(lock, std::chrono::milliseconds(timeToWait));
}
// Early return if the Event Loop got destroyed already.
if (!state->running) {
return;
}
// Early return if the Event Loop got destroyed already.
if (!state->running) {
return;
}
const auto currentTime = getCurrentTimeInMs();
std::vector<std::function<void(jsi::Runtime & rt)>> jobs;
auto &timeouts = state->queue;
timeouts.erase(
std::remove_if(
timeouts.begin(),
timeouts.end(),
[currentTime, &jobs](const Timeout &timeout) {
if (currentTime >= timeout.targetTime) {
jobs.emplace_back(timeout.callback);
return true;
}
return false;
}),
timeouts.end());
lock.unlock();
const auto currentTime = getCurrentTimeInMs();
std::vector<std::function<void(jsi::Runtime & rt)>> jobs;
auto &timeouts = state->queue;
timeouts.erase(
std::remove_if(
timeouts.begin(),
timeouts.end(),
[currentTime, &jobs](const Timeout &timeout) {
if (currentTime >= timeout.targetTime) {
jobs.emplace_back(timeout.callback);
return true;
}
return false;
}),
timeouts.end());
lock.unlock();
if (auto strongThis = weakThis.lock()) {
for (auto &job : jobs) {
strongThis->pushTask(std::move(job));
if (auto strongThis = weakThis.lock()) {
for (auto &job : jobs) {
strongThis->pushTask(std::move(job));
}
}
}
}
}
});
});
#ifdef ANDROID
pthread_setname_np(thread.native_handle(), threadName.c_str());
#endif
@@ -76,14 +79,17 @@ void EventLoop::run() {
}
void EventLoop::pushTask(std::function<void(jsi::Runtime &rt)> &&job) {
queue_->push([weakRuntime = std::weak_ptr<jsi::Runtime>{runtime_}, job = std::move(job)] {
queue_->push([weakRuntime = std::weak_ptr<jsi::Runtime>{runtime_},
job = std::move(job)] {
if (auto runtime = weakRuntime.lock()) {
job(*runtime);
}
});
}
void EventLoop::pushTimeout(std::function<void(jsi::Runtime &rt)> &&job, int64_t delay) {
void EventLoop::pushTimeout(
std::function<void(jsi::Runtime &rt)> &&job,
int64_t delay) {
{
std::unique_lock<std::mutex> lock(timeoutsQueueState_->mutex);
const auto targetTime = getCurrentTimeInMs() + delay;
@@ -97,7 +103,8 @@ void EventLoop::pushTimeout(std::function<void(jsi::Runtime &rt)> &&job, int64_t
int64_t EventLoop::getCurrentTimeInMs() {
const auto currentTime = std::chrono::system_clock::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::milliseconds>(currentTime).count();
return std::chrono::duration_cast<std::chrono::milliseconds>(currentTime)
.count();
}
} // namespace worklets
@@ -1,7 +1,7 @@
#pragma once
#include <jsi/jsi.h>
#include <worklets/RunLoop/AsyncQueue.h>
#include <worklets/Public/AsyncQueue.h>
#include <atomic>
#include <condition_variable>
@@ -33,7 +33,7 @@ class EventLoop : public std::enable_shared_from_this<EventLoop> {
public:
EventLoop(
const std::string &name,
const std::shared_ptr<jsi::Runtime> &runtime,
const std::shared_ptr<jsi::Runtime> runtime,
const std::shared_ptr<AsyncQueue> &queue);
~EventLoop();
void run();
@@ -1,60 +0,0 @@
#include <jsi/jsi.h>
#include <react/debug/react_native_assert.h>
#include <worklets/SharedItems/MemoryManager.h>
#include <worklets/SharedItems/Serializable.h>
#include <worklets/WorkletRuntime/WorkletRuntime.h>
namespace worklets {
void MemoryManager::loadAllCustomSerializables(const std::shared_ptr<WorkletRuntime> &runtime) {
std::lock_guard lock(customSerializationDataMutex_);
runtime->executeSync([&](jsi::Runtime &rt) -> jsi::Value {
const auto registry = getCustomSerializationRegistry(rt);
for (const auto &data : customSerializationData_) {
loadCustomSerializable(rt, registry, data);
}
return jsi::Value::undefined();
});
}
void MemoryManager::loadCustomSerializable(
const std::shared_ptr<WorkletRuntime> &runtime,
const SerializationData &data) {
std::lock_guard lock(customSerializationDataMutex_);
runtime->executeSync([this, data](jsi::Runtime &rt) -> jsi::Value {
const auto registry = getCustomSerializationRegistry(rt);
loadCustomSerializable(rt, registry, data);
return jsi::Value::undefined();
});
}
void MemoryManager::loadCustomSerializable(
jsi::Runtime &runtime,
const jsi::Array &registry,
const SerializationData &data) {
react_native_assert(
registry.length(runtime) == data.typeId &&
("Custom serializable type ID must match registry length. Expected typeId: " + std::to_string(data.typeId) +
", got registry length: " + std::to_string(registry.length(runtime)) +
". Custom serializables must be registered in the same order across all runtimes.")
.c_str());
const auto item = jsi::Object(runtime);
item.setProperty(runtime, "determine", data.determine->toJSValue(runtime));
item.setProperty(runtime, "pack", data.pack->toJSValue(runtime));
item.setProperty(runtime, "unpack", data.unpack->toJSValue(runtime));
item.setProperty(runtime, "typeId", data.typeId);
registry.getPropertyAsFunction(runtime, "push").callWithThis(runtime, registry, item);
}
void MemoryManager::registerCustomSerializable(const SerializationData &data) {
std::lock_guard lock(customSerializationDataMutex_);
customSerializationData_.emplace_back(data);
}
jsi::Array MemoryManager::getCustomSerializationRegistry(jsi::Runtime &rt) {
const auto data = rt.global().getProperty(rt, "__customSerializationRegistry");
return data.asObject(rt).asArray(rt);
}
} // namespace worklets
@@ -1,29 +0,0 @@
#pragma once
#include <jsi/jsi.h>
#include <react/debug/react_native_assert.h>
#include <worklets/SharedItems/Serializable.h>
#include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <memory>
#include <mutex>
#include <vector>
namespace worklets {
class MemoryManager {
public:
void loadAllCustomSerializables(const std::shared_ptr<WorkletRuntime> &workletRuntime);
void loadCustomSerializable(const std::shared_ptr<WorkletRuntime> &workletRuntime, const SerializationData &data);
void registerCustomSerializable(const SerializationData &data);
private:
static jsi::Array getCustomSerializationRegistry(jsi::Runtime &rt);
void loadCustomSerializable(jsi::Runtime &runtime, const jsi::Array &registry, const SerializationData &data);
std::mutex customSerializationDataMutex_;
std::vector<SerializationData> customSerializationData_;
};
} // namespace worklets
@@ -1,10 +1,6 @@
#include <jsi/jsi.h>
#include <worklets/SharedItems/Serializable.h>
#include <memory>
#include <string>
#include <utility>
using namespace facebook;
namespace worklets {
@@ -15,6 +11,34 @@ jsi::Function getValueUnpacker(jsi::Runtime &rt) {
return valueUnpacker.asObject(rt).asFunction(rt);
}
#ifndef NDEBUG
static const auto callGuardLambda = [](facebook::jsi::Runtime &rt,
const facebook::jsi::Value &thisVal,
const facebook::jsi::Value *args,
size_t count) {
return args[0].asObject(rt).asFunction(rt).call(rt, args + 1, count - 1);
};
jsi::Function getCallGuard(jsi::Runtime &rt) {
auto callGuard = rt.global().getProperty(rt, "__callGuardDEV");
if (callGuard.isObject()) {
// Use JS implementation if `__callGuardDEV` has already been installed.
// This is the desired behavior.
return callGuard.asObject(rt).asFunction(rt);
}
// Otherwise, fallback to C++ JSI implementation. This is necessary so that we
// can install `__callGuardDEV` itself and should happen only once. Note that
// the C++ implementation doesn't intercept errors and simply throws them as
// C++ exceptions which crashes the app. We assume that installing the guard
// doesn't throw any errors.
return jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "callGuard"), 1, callGuardLambda);
}
#endif // NDEBUG
jsi::Value makeSerializableClone(
jsi::Runtime &rt,
const jsi::Value &value,
@@ -34,26 +58,35 @@ jsi::Value makeSerializableClone(
return makeSerializableFunction(rt, object.asFunction(rt));
} else if (object.isArray(rt)) {
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
serializable = std::make_shared<RetainingSerializable<SerializableArray>>(rt, object.asArray(rt));
serializable =
std::make_shared<RetainingSerializable<SerializableArray>>(
rt, object.asArray(rt));
} else {
serializable = std::make_shared<SerializableArray>(rt, object.asArray(rt));
serializable =
std::make_shared<SerializableArray>(rt, object.asArray(rt));
}
} else if (object.isArrayBuffer(rt)) {
serializable = std::make_shared<SerializableArrayBuffer>(rt, object.getArrayBuffer(rt));
serializable = std::make_shared<SerializableArrayBuffer>(
rt, object.getArrayBuffer(rt));
} else if (object.isHostObject(rt)) {
if (object.isHostObject<SerializableJSRef>(rt)) {
return object;
}
serializable = std::make_shared<SerializableHostObject>(rt, object.getHostObject(rt));
serializable = std::make_shared<SerializableHostObject>(
rt, object.getHostObject(rt));
} else {
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
serializable = std::make_shared<RetainingSerializable<SerializableObject>>(rt, object, nativeStateSource);
serializable =
std::make_shared<RetainingSerializable<SerializableObject>>(
rt, object, nativeStateSource);
} else {
serializable = std::make_shared<SerializableObject>(rt, object, nativeStateSource);
serializable =
std::make_shared<SerializableObject>(rt, object, nativeStateSource);
}
}
} else if (value.isString()) {
serializable = std::make_shared<SerializableString>(value.asString(rt).utf8(rt));
serializable =
std::make_shared<SerializableString>(value.asString(rt).utf8(rt));
} else if (value.isUndefined()) {
serializable = std::make_shared<SerializableScalar>();
} else if (value.isNull()) {
@@ -63,21 +96,25 @@ jsi::Value makeSerializableClone(
} else if (value.isNumber()) {
serializable = std::make_shared<SerializableScalar>(value.getNumber());
} else if (value.isBigInt()) {
serializable = std::make_shared<SerializableBigInt>(rt, value.getBigInt(rt));
serializable =
std::make_shared<SerializableBigInt>(rt, value.getBigInt(rt));
} else if (value.isSymbol()) {
// TODO: this is only a placeholder implementation, here we replace symbols
// with strings in order to make certain objects to be captured. There isn't
// yet any use-case for using symbols on the UI runtime so it is fine to keep
// yet any usecase for using symbols on the UI runtime so it is fine to keep
// it like this for now.
serializable = std::make_shared<SerializableString>(value.getSymbol(rt).toString(rt));
serializable =
std::make_shared<SerializableString>(value.getSymbol(rt).toString(rt));
} else {
throw std::runtime_error("[Worklets] Attempted to convert an unsupported value type.");
throw std::runtime_error(
"[Worklets] Attempted to convert an unsupported value type.");
}
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableString(jsi::Runtime &rt, const jsi::String &string) {
const auto serializable = std::make_shared<SerializableString>(string.utf8(rt));
const auto serializable =
std::make_shared<SerializableString>(string.utf8(rt));
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
@@ -106,42 +143,58 @@ jsi::Value makeSerializableNull(jsi::Runtime &rt) {
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableWorklet(jsi::Runtime &rt, const jsi::Object &object, const bool &shouldRetainRemote) {
jsi::Value makeSerializableWorklet(
jsi::Runtime &rt,
const jsi::Object &object,
const bool &shouldRetainRemote) {
std::shared_ptr<Serializable> serializable;
if (shouldRetainRemote) {
serializable = std::make_shared<RetainingSerializable<SerializableWorklet>>(rt, object);
serializable = std::make_shared<RetainingSerializable<SerializableWorklet>>(
rt, object);
} else {
serializable = std::make_shared<SerializableWorklet>(rt, object);
}
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableInitializer(jsi::Runtime &rt, const jsi::Object &initializerObject) {
const auto serializable = std::make_shared<SerializableInitializer>(rt, initializerObject);
jsi::Value makeSerializableInitializer(
jsi::Runtime &rt,
const jsi::Object &initializerObject) {
const auto serializable =
std::make_shared<SerializableInitializer>(rt, initializerObject);
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableFunction(jsi::Runtime &rt, jsi::Function function) {
std::shared_ptr<Serializable> serializable;
if (function.isHostFunction(rt)) {
serializable = std::make_shared<SerializableHostFunction>(rt, std::move(function));
serializable =
std::make_shared<SerializableHostFunction>(rt, std::move(function));
} else {
serializable = std::make_shared<SerializableRemoteFunction>(rt, std::move(function));
serializable =
std::make_shared<SerializableRemoteFunction>(rt, std::move(function));
}
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableArray(jsi::Runtime &rt, const jsi::Array &array, const jsi::Value &shouldRetainRemote) {
jsi::Value makeSerializableArray(
jsi::Runtime &rt,
const jsi::Array &array,
const jsi::Value &shouldRetainRemote) {
std::shared_ptr<Serializable> serializable;
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
serializable = std::make_shared<RetainingSerializable<SerializableArray>>(rt, array);
serializable =
std::make_shared<RetainingSerializable<SerializableArray>>(rt, array);
} else {
serializable = std::make_shared<SerializableArray>(rt, array);
}
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableMap(jsi::Runtime &rt, const jsi::Array &keys, const jsi::Array &values) {
jsi::Value makeSerializableMap(
jsi::Runtime &rt,
const jsi::Array &keys,
const jsi::Array &values) {
auto serializable = std::make_shared<SerializableMap>(rt, keys, values);
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
@@ -151,7 +204,9 @@ jsi::Value makeSerializableSet(jsi::Runtime &rt, const jsi::Array &values) {
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableHostObject(jsi::Runtime &rt, const std::shared_ptr<jsi::HostObject> &value) {
jsi::Value makeSerializableHostObject(
jsi::Runtime &rt,
const std::shared_ptr<jsi::HostObject> &value) {
const auto serializable = std::make_shared<SerializableHostObject>(rt, value);
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
@@ -160,12 +215,17 @@ jsi::Value makeSerializableTurboModuleLike(
jsi::Runtime &rt,
const jsi::Object &object,
const std::shared_ptr<jsi::HostObject> &proto) {
const auto serializable = std::make_shared<SerializableTurboModuleLike>(rt, object, proto);
const auto serializable =
std::make_shared<SerializableTurboModuleLike>(rt, object, proto);
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
jsi::Value makeSerializableImport(jsi::Runtime &rt, const double source, const jsi::String &imported) {
auto serializable = std::make_shared<SerializableImport>(rt, source, imported);
jsi::Value makeSerializableImport(
jsi::Runtime &rt,
const double source,
const jsi::String &imported) {
auto serializable =
std::make_shared<SerializableImport>(rt, source, imported);
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
@@ -176,9 +236,11 @@ jsi::Value makeSerializableObject(
const jsi::Value &nativeStateSource) {
std::shared_ptr<Serializable> serializable;
if (shouldRetainRemote) {
serializable = std::make_shared<RetainingSerializable<SerializableObject>>(rt, object, nativeStateSource);
serializable = std::make_shared<RetainingSerializable<SerializableObject>>(
rt, object, nativeStateSource);
} else {
serializable = std::make_shared<SerializableObject>(rt, object, nativeStateSource);
serializable =
std::make_shared<SerializableObject>(rt, object, nativeStateSource);
}
return SerializableJSRef::newNativeStateObject(rt, serializable);
}
@@ -193,14 +255,15 @@ std::shared_ptr<Serializable> extractSerializableOrThrow(
auto nativeState = object.getNativeState(rt);
return std::dynamic_pointer_cast<SerializableJSRef>(nativeState)->value();
}
throw std::runtime_error("[Worklets] Attempted to extract from a Object that wasn't converted to a Serializable.");
throw std::runtime_error(
"[Worklets] Attempted to extract from a Object that wasn't converted to a Serializable.");
} else if (maybeSerializableValue.isUndefined()) {
return Serializable::undefined();
}
throw std::runtime_error(errorMessage);
}
Serializable::~Serializable() = default;
Serializable::~Serializable() {}
std::shared_ptr<Serializable> Serializable::undefined() {
static auto undefined = std::make_shared<SerializableScalar>();
@@ -230,13 +293,15 @@ jsi::Value RetainingSerializable<BaseClass>::toJSValue(jsi::Runtime &rt) {
return BaseClass::toJSValue(rt);
}
SerializableJSRef::~SerializableJSRef() = default;
SerializableJSRef::~SerializableJSRef() {}
SerializableArray::SerializableArray(jsi::Runtime &rt, const jsi::Array &array) : Serializable(ValueType::ArrayType) {
SerializableArray::SerializableArray(jsi::Runtime &rt, const jsi::Array &array)
: Serializable(ArrayType) {
auto size = array.size(rt);
data_.reserve(size);
for (size_t i = 0; i < size; i++) {
data_.push_back(extractSerializableOrThrow(rt, array.getValueAtIndex(rt, i)));
data_.push_back(
extractSerializableOrThrow(rt, array.getValueAtIndex(rt, i)));
}
}
@@ -251,14 +316,19 @@ jsi::Value SerializableArray::toJSValue(jsi::Runtime &rt) {
jsi::Value SerializableArrayBuffer::toJSValue(jsi::Runtime &rt) {
auto size = static_cast<int>(data_.size());
auto arrayBuffer =
rt.global().getPropertyAsFunction(rt, "ArrayBuffer").callAsConstructor(rt, size).getObject(rt).getArrayBuffer(rt);
auto arrayBuffer = rt.global()
.getPropertyAsFunction(rt, "ArrayBuffer")
.callAsConstructor(rt, size)
.getObject(rt)
.getArrayBuffer(rt);
memcpy(arrayBuffer.data(rt), data_.data(), size);
return arrayBuffer;
}
SerializableObject::SerializableObject(jsi::Runtime &rt, const jsi::Object &object)
: Serializable(ValueType::ObjectType) {
SerializableObject::SerializableObject(
jsi::Runtime &rt,
const jsi::Object &object)
: Serializable(ObjectType) {
auto propertyNames = object.getPropertyNames(rt);
auto size = propertyNames.size(rt);
data_.reserve(size);
@@ -272,17 +342,24 @@ SerializableObject::SerializableObject(jsi::Runtime &rt, const jsi::Object &obje
}
}
SerializableObject::SerializableObject(jsi::Runtime &rt, const jsi::Object &object, const jsi::Value &nativeStateSource)
SerializableObject::SerializableObject(
jsi::Runtime &rt,
const jsi::Object &object,
const jsi::Value &nativeStateSource)
: SerializableObject(rt, object) {
if (nativeStateSource.isObject() && nativeStateSource.asObject(rt).hasNativeState(rt)) {
if (nativeStateSource.isObject() &&
nativeStateSource.asObject(rt).hasNativeState(rt)) {
nativeState_ = nativeStateSource.asObject(rt).getNativeState(rt);
}
}
jsi::Value SerializableObject::toJSValue(jsi::Runtime &rt) {
auto obj = jsi::Object(rt);
for (const auto &i : data_) {
obj.setProperty(rt, jsi::String::createFromUtf8(rt, i.first), i.second->toJSValue(rt));
for (size_t i = 0, size = data_.size(); i < size; i++) {
obj.setProperty(
rt,
jsi::String::createFromUtf8(rt, data_[i].first),
data_[i].second->toJSValue(rt));
}
if (nativeState_ != nullptr) {
obj.setNativeState(rt, nativeState_);
@@ -290,10 +367,15 @@ jsi::Value SerializableObject::toJSValue(jsi::Runtime &rt) {
return obj;
}
SerializableMap::SerializableMap(jsi::Runtime &rt, const jsi::Array &keys, const jsi::Array &values)
: Serializable(ValueType::MapType) {
SerializableMap::SerializableMap(
jsi::Runtime &rt,
const jsi::Array &keys,
const jsi::Array &values)
: Serializable(MapType) {
auto size = keys.size(rt);
react_native_assert(size == values.size(rt) && "Keys and values arrays must have the same size.");
react_native_assert(
size == values.size(rt) &&
"Keys and values arrays must have the same size.");
data_.reserve(size);
for (size_t i = 0; i < size; i++) {
auto key = extractSerializableOrThrow(rt, keys.getValueAtIndex(rt, i));
@@ -312,16 +394,19 @@ jsi::Value SerializableMap::toJSValue(jsi::Runtime &rt) {
}
const auto &global = rt.global();
auto map = global.getPropertyAsFunction(rt, "Map").callAsConstructor(rt, std::move(keyValues));
auto map = global.getPropertyAsFunction(rt, "Map").callAsConstructor(
rt, std::move(keyValues));
return map;
}
SerializableSet::SerializableSet(jsi::Runtime &rt, const jsi::Array &values) : Serializable(ValueType::SetType) {
SerializableSet::SerializableSet(jsi::Runtime &rt, const jsi::Array &values)
: Serializable(SetType) {
auto size = values.size(rt);
data_.reserve(size);
for (size_t i = 0; i < size; i++) {
data_.push_back(extractSerializableOrThrow(rt, values.getValueAtIndex(rt, i)));
data_.push_back(
extractSerializableOrThrow(rt, values.getValueAtIndex(rt, i)));
}
}
@@ -332,7 +417,8 @@ jsi::Value SerializableSet::toJSValue(jsi::Runtime &rt) {
}
const auto &global = rt.global();
auto set = global.getPropertyAsFunction(rt, "Set").callAsConstructor(rt, std::move(values));
auto set = global.getPropertyAsFunction(rt, "Set").callAsConstructor(
rt, std::move(values));
return set;
}
@@ -342,15 +428,20 @@ jsi::Value SerializableHostObject::toJSValue(jsi::Runtime &rt) {
}
jsi::Value SerializableHostFunction::toJSValue(jsi::Runtime &rt) {
return jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forUtf8(rt, name_), paramCount_, hostFunction_);
return jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forUtf8(rt, name_), paramCount_, hostFunction_);
}
jsi::Value SerializableWorklet::toJSValue(jsi::Runtime &rt) {
react_native_assert(
std::any_of(data_.cbegin(), data_.cend(), [](const auto &item) { return item.first == "__workletHash"; }) &&
std::any_of(
data_.cbegin(),
data_.cend(),
[](const auto &item) { return item.first == "__workletHash"; }) &&
"SerializableWorklet doesn't have `__workletHash` property");
jsi::Value obj = SerializableObject::toJSValue(rt);
return getValueUnpacker(rt).call(rt, obj, jsi::String::createFromAscii(rt, "Worklet"));
return getValueUnpacker(rt).call(
rt, obj, jsi::String::createFromAscii(rt, "Worklet"));
}
jsi::Value SerializableImport::toJSValue(jsi::Runtime &rt) {
@@ -364,7 +455,11 @@ jsi::Value SerializableImport::toJSValue(jsi::Runtime &rt) {
}
const auto imported = jsi::String::createFromUtf8(rt, imported_);
return metroRequire.asObject(rt).asFunction(rt).call(rt, source_).asObject(rt).getProperty(rt, imported);
return metroRequire.asObject(rt)
.asFunction(rt)
.call(rt, source_)
.asObject(rt)
.getProperty(rt, imported);
}
jsi::Value SerializableRemoteFunction::toJSValue(jsi::Runtime &rt) {
@@ -386,12 +481,12 @@ jsi::Value SerializableRemoteFunction::toJSValue(jsi::Runtime &rt) {
jsi::Value SerializableInitializer::toJSValue(jsi::Runtime &rt) {
if (remoteValue_ == nullptr) {
auto initObj = initializer_->toJSValue(rt);
auto value = std::make_unique<jsi::Value>(
getValueUnpacker(rt).call(rt, initObj, jsi::String::createFromAscii(rt, "Handle")));
auto value = std::make_unique<jsi::Value>(getValueUnpacker(rt).call(
rt, initObj, jsi::String::createFromAscii(rt, "Handle")));
// We are locking the initialization here since the thread that is
// initializing can be preempted on runtime lock. E.g.
// UI thread can be preempted on initialization of a shared value and then
// initalizing can be pre-empted on runtime lock. E.g.
// UI thread can be pre-empted on initialization of a shared value and then
// JS thread can try to access the shared value, locking the whole runtime.
// If we put the lock on `getValueUnpacker` part (basically any part that
// requires runtime) we would get a deadlock since UI thread would never
@@ -406,7 +501,8 @@ jsi::Value SerializableInitializer::toJSValue(jsi::Runtime &rt) {
return jsi::Value(rt, *remoteValue_);
}
auto initObj = initializer_->toJSValue(rt);
return getValueUnpacker(rt).call(rt, initObj, jsi::String::createFromAscii(rt, "Handle"));
return getValueUnpacker(rt).call(
rt, initObj, jsi::String::createFromAscii(rt, "Handle"));
}
jsi::Value SerializableString::toJSValue(jsi::Runtime &rt) {
@@ -414,58 +510,36 @@ jsi::Value SerializableString::toJSValue(jsi::Runtime &rt) {
}
jsi::Value SerializableBigInt::toJSValue(jsi::Runtime &rt) {
if (fastValue_.has_value()) {
return jsi::BigInt::fromInt64(rt, fastValue_.value());
} else {
return rt.global().getPropertyAsFunction(rt, "BigInt").call(rt, jsi::String::createFromUtf8(rt, slowValue_));
}
return rt.global()
.getPropertyAsFunction(rt, "BigInt")
.call(rt, jsi::String::createFromUtf8(rt, string_));
}
jsi::Value SerializableScalar::toJSValue(jsi::Runtime &) {
switch (valueType_) {
case Serializable::ValueType::UndefinedType:
case Serializable::UndefinedType:
return jsi::Value();
case Serializable::ValueType::NullType:
case Serializable::NullType:
return jsi::Value(nullptr);
case Serializable::ValueType::BooleanType:
case Serializable::BooleanType:
return jsi::Value(data_.boolean);
case Serializable::ValueType::NumberType:
case Serializable::NumberType:
return jsi::Value(data_.number);
default:
throw std::runtime_error("[Worklets] Attempted to convert object that's not of a scalar type.");
throw std::runtime_error(
"[Worklets] Attempted to convert object that's not of a scalar type.");
}
}
jsi::Value SerializableTurboModuleLike::toJSValue(jsi::Runtime &rt) {
auto obj = properties_->toJSValue(rt).asObject(rt);
const auto prototype = proto_->toJSValue(rt);
rt.global().getPropertyAsObject(rt, "Object").getPropertyAsFunction(rt, "setPrototypeOf").call(rt, obj, prototype);
rt.global()
.getPropertyAsObject(rt, "Object")
.getPropertyAsFunction(rt, "setPrototypeOf")
.call(rt, obj, prototype);
return obj;
}
jsi::Function getCustomSerializableUnpacker(jsi::Runtime &rt) {
auto customSerializableUnpacker = rt.global().getProperty(rt, "__customSerializableUnpacker");
react_native_assert(customSerializableUnpacker.isObject() && "customSerializableUnpacker not found");
return customSerializableUnpacker.asObject(rt).asFunction(rt);
}
jsi::Value CustomSerializable::toJSValue(jsi::Runtime &rt) {
try {
auto unpack = getCustomSerializableUnpacker(rt);
auto data = data_->toJSValue(rt);
return unpack.call(rt, data, jsi::Value(typeId_));
} catch (jsi::JSError &e) {
throw std::runtime_error(
std::string("[Worklets] Failed to deserialize CustomSerializable. Reason: ") + e.getMessage());
}
}
jsi::Value makeCustomSerializable(jsi::Runtime &rt, const jsi::Value &data, const int typeId) {
auto rawData = extractSerializableOrThrow(rt, data, "[Worklets] Data must be a Serializable object.");
auto customSerializable = std::make_shared<CustomSerializable>(rawData, typeId);
return SerializableJSRef::newNativeStateObject(rt, customSerializable);
}
} // namespace worklets
@@ -15,7 +15,30 @@ namespace worklets {
jsi::Function getValueUnpacker(jsi::Runtime &rt);
inline void cleanupIfRuntimeExists(jsi::Runtime *rt, std::unique_ptr<jsi::Value> &value) {
#ifndef NDEBUG
jsi::Function getCallGuard(jsi::Runtime &rt);
#endif // NDEBUG
// If possible, please use `WorkletRuntime::runGuarded` instead.
template <typename... Args>
inline jsi::Value runOnRuntimeGuarded(
jsi::Runtime &rt,
const jsi::Value &function,
Args &&...args) {
// We only use callGuard in debug mode, otherwise we call the provided
// function directly. CallGuard provides a way of capturing exceptions in
// JavaScript and propagating them to the main React Native thread such that
// they can be presented using RN's LogBox.
#ifndef NDEBUG
return getCallGuard(rt).call(rt, function, args...);
#else
return function.asObject(rt).asFunction(rt).call(rt, args...);
#endif // NDEBUG
}
inline void cleanupIfRuntimeExists(
jsi::Runtime *rt,
std::unique_ptr<jsi::Value> &value) {
if (rt != nullptr && !WorkletRuntimeRegistry::isRuntimeAlive(rt)) {
// The below use of unique_ptr.release prevents the smart pointer from
// calling the destructor of the kept object. This effectively results in
@@ -35,7 +58,7 @@ inline void cleanupIfRuntimeExists(jsi::Runtime *rt, std::unique_ptr<jsi::Value>
// before the runtime is terminated. Note that the underlying memory that
// jsi::Value refers to is managed by the VM and gets freed along with the
// runtime.
value.release(); // NOLINT
value.release();
}
}
@@ -45,7 +68,7 @@ class Serializable {
virtual ~Serializable();
enum class ValueType : std::uint8_t {
enum ValueType {
UndefinedType,
NullType,
BooleanType,
@@ -66,7 +89,6 @@ class Serializable {
TurboModuleLikeType,
ImportType,
SynchronizableType,
CustomType,
};
explicit Serializable(ValueType valueType) : valueType_(valueType) {}
@@ -105,15 +127,18 @@ class SerializableJSRef : public jsi::NativeState {
const std::shared_ptr<Serializable> value_;
public:
explicit SerializableJSRef(const std::shared_ptr<Serializable> &value) : value_(value) {}
explicit SerializableJSRef(const std::shared_ptr<Serializable> &value)
: value_(value) {}
~SerializableJSRef() override;
virtual ~SerializableJSRef();
std::shared_ptr<Serializable> value() const {
return value_;
}
static jsi::Object newNativeStateObject(jsi::Runtime &rt, const std::shared_ptr<Serializable> &value) {
static jsi::Object newNativeStateObject(
jsi::Runtime &rt,
const std::shared_ptr<Serializable> &value) {
auto object = jsi::Object(rt);
object.setNativeState(rt, std::make_shared<SerializableJSRef>(value));
object.setProperty(rt, "__serializableRef", true);
@@ -150,35 +175,52 @@ jsi::Value makeSerializableObject(
bool shouldRetainRemote,
const jsi::Value &nativeStateSource);
jsi::Value makeSerializableImport(jsi::Runtime &rt, double source, const jsi::String &imported);
jsi::Value makeSerializableImport(
jsi::Runtime &rt,
const double source,
const jsi::String &imported);
jsi::Value makeSerializableHostObject(jsi::Runtime &rt, const std::shared_ptr<jsi::HostObject> &value);
jsi::Value makeSerializableHostObject(
jsi::Runtime &rt,
const std::shared_ptr<jsi::HostObject> &value);
jsi::Value makeSerializableArray(jsi::Runtime &rt, const jsi::Array &array, const jsi::Value &shouldRetainRemote);
jsi::Value makeSerializableArray(
jsi::Runtime &rt,
const jsi::Array &array,
const jsi::Value &shouldRetainRemote);
jsi::Value makeSerializableMap(jsi::Runtime &rt, const jsi::Array &keys, const jsi::Array &values);
jsi::Value makeSerializableMap(
jsi::Runtime &rt,
const jsi::Array &keys,
const jsi::Array &values);
jsi::Value makeSerializableSet(jsi::Runtime &rt, const jsi::Array &values);
jsi::Value makeSerializableInitializer(jsi::Runtime &rt, const jsi::Object &initializerObject);
jsi::Value makeSerializableInitializer(
jsi::Runtime &rt,
const jsi::Object &initializerObject);
jsi::Value makeSerializableFunction(jsi::Runtime &rt, jsi::Function function);
jsi::Value makeSerializableWorklet(jsi::Runtime &rt, const jsi::Object &object, const bool &shouldRetainRemote);
jsi::Value makeCustomSerializable(jsi::Runtime &rt, const jsi::Value &data, int typeId);
jsi::Value makeSerializableWorklet(
jsi::Runtime &rt,
const jsi::Object &object,
const bool &shouldRetainRemote);
std::shared_ptr<Serializable> extractSerializableOrThrow(
jsi::Runtime &rt,
const jsi::Value &maybeSerializableValue,
const std::string &errorMessage = "[Worklets] Expecting the object to be of type SerializableJSRef.");
const std::string &errorMessage =
"[Worklets] Expecting the object to be of type SerializableJSRef.");
template <typename T>
std::shared_ptr<T> extractSerializableOrThrow(
jsi::Runtime &rt,
const jsi::Value &serializableRef,
const std::string &errorMessage = "[Worklets] Provided serializable object is of an incompatible type.") {
auto res = std::dynamic_pointer_cast<T>(extractSerializableOrThrow(rt, serializableRef, errorMessage));
const std::string &errorMessage =
"[Worklets] Provided serializable object is of an incompatible type.") {
auto res = std::dynamic_pointer_cast<T>(
extractSerializableOrThrow(rt, serializableRef, errorMessage));
if (!res) {
throw std::runtime_error(errorMessage);
}
@@ -199,7 +241,10 @@ class SerializableObject : public Serializable {
public:
SerializableObject(jsi::Runtime &rt, const jsi::Object &object);
SerializableObject(jsi::Runtime &rt, const jsi::Object &object, const jsi::Value &nativeStateSource);
SerializableObject(
jsi::Runtime &rt,
const jsi::Object &object,
const jsi::Value &nativeStateSource);
jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -210,12 +255,17 @@ class SerializableObject : public Serializable {
class SerializableMap : public Serializable {
public:
SerializableMap(jsi::Runtime &rt, const jsi::Array &keys, const jsi::Array &values);
SerializableMap(
jsi::Runtime &rt,
const jsi::Array &keys,
const jsi::Array &values);
jsi::Value toJSValue(jsi::Runtime &rt) override;
protected:
std::vector<std::pair<std::shared_ptr<Serializable>, std::shared_ptr<Serializable>>> data_;
std::vector<
std::pair<std::shared_ptr<Serializable>, std::shared_ptr<Serializable>>>
data_;
};
class SerializableSet : public Serializable {
@@ -230,8 +280,10 @@ class SerializableSet : public Serializable {
class SerializableHostObject : public Serializable {
public:
SerializableHostObject(jsi::Runtime &, const std::shared_ptr<jsi::HostObject> &hostObject)
: Serializable(ValueType::HostObjectType), hostObject_(hostObject) {}
SerializableHostObject(
jsi::Runtime &,
const std::shared_ptr<jsi::HostObject> &hostObject)
: Serializable(HostObjectType), hostObject_(hostObject) {}
jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -242,7 +294,7 @@ class SerializableHostObject : public Serializable {
class SerializableHostFunction : public Serializable {
public:
SerializableHostFunction(jsi::Runtime &rt, jsi::Function function)
: Serializable(ValueType::HostFunctionType),
: Serializable(HostFunctionType),
hostFunction_(function.getHostFunction(rt)),
name_(function.getProperty(rt, "name").asString(rt).utf8(rt)),
paramCount_(function.getProperty(rt, "length").asNumber()) {}
@@ -258,8 +310,10 @@ class SerializableHostFunction : public Serializable {
class SerializableArrayBuffer : public Serializable {
public:
SerializableArrayBuffer(jsi::Runtime &rt, const jsi::ArrayBuffer &arrayBuffer)
: Serializable(ValueType::ArrayBufferType),
data_(arrayBuffer.data(rt), arrayBuffer.data(rt) + arrayBuffer.size(rt)) {}
: Serializable(ArrayBufferType),
data_(
arrayBuffer.data(rt),
arrayBuffer.data(rt) + arrayBuffer.size(rt)) {}
jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -269,8 +323,9 @@ class SerializableArrayBuffer : public Serializable {
class SerializableWorklet : public SerializableObject {
public:
SerializableWorklet(jsi::Runtime &rt, const jsi::Object &worklet) : SerializableObject(rt, worklet) {
valueType_ = ValueType::WorkletType;
SerializableWorklet(jsi::Runtime &rt, const jsi::Object &worklet)
: SerializableObject(rt, worklet) {
valueType_ = WorkletType;
}
jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -278,8 +333,13 @@ class SerializableWorklet : public SerializableObject {
class SerializableImport : public Serializable {
public:
SerializableImport(jsi::Runtime &rt, const double source, const jsi::String &imported)
: Serializable(ValueType::ImportType), source_(source), imported_(imported.utf8(rt)) {}
SerializableImport(
jsi::Runtime &rt,
const double source,
const jsi::String &imported)
: Serializable(ImportType),
source_(source),
imported_(imported.utf8(rt)) {}
jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -288,8 +348,9 @@ class SerializableImport : public Serializable {
const std::string imported_;
};
class SerializableRemoteFunction : public Serializable,
public std::enable_shared_from_this<SerializableRemoteFunction> {
class SerializableRemoteFunction
: public Serializable,
public std::enable_shared_from_this<SerializableRemoteFunction> {
private:
jsi::Runtime *runtime_;
#ifndef NDEBUG
@@ -299,7 +360,7 @@ class SerializableRemoteFunction : public Serializable,
public:
SerializableRemoteFunction(jsi::Runtime &rt, jsi::Function &&function)
: Serializable(ValueType::RemoteFunctionType),
: Serializable(RemoteFunctionType),
runtime_(&rt),
#ifndef NDEBUG
name_(function.getProperty(rt, "name").asString(rt).utf8(rt)),
@@ -307,7 +368,7 @@ class SerializableRemoteFunction : public Serializable,
function_(std::make_unique<jsi::Value>(rt, std::move(function))) {
}
~SerializableRemoteFunction() override {
~SerializableRemoteFunction() {
cleanupIfRuntimeExists(runtime_, function_);
}
@@ -326,11 +387,14 @@ class SerializableInitializer : public Serializable {
jsi::Runtime *remoteRuntime_;
public:
SerializableInitializer(jsi::Runtime &rt, const jsi::Object &initializerObject)
: Serializable(ValueType::HandleType),
initializer_(std::make_unique<SerializableObject>(rt, initializerObject)) {}
SerializableInitializer(
jsi::Runtime &rt,
const jsi::Object &initializerObject)
: Serializable(HandleType),
initializer_(
std::make_unique<SerializableObject>(rt, initializerObject)) {}
~SerializableInitializer() override {
~SerializableInitializer() {
cleanupIfRuntimeExists(remoteRuntime_, remoteValue_);
}
@@ -339,7 +403,8 @@ class SerializableInitializer : public Serializable {
class SerializableString : public Serializable {
public:
explicit SerializableString(const std::string &string) : Serializable(ValueType::StringType), data_(string) {}
explicit SerializableString(const std::string &string)
: Serializable(StringType), data_(string) {}
jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -349,36 +414,27 @@ class SerializableString : public Serializable {
class SerializableBigInt : public Serializable {
public:
explicit SerializableBigInt(jsi::Runtime &rt, const jsi::BigInt &bigInt) : Serializable(ValueType::BigIntType) {
if (bigInt.isInt64(rt)) {
fastValue_ = bigInt.getInt64(rt);
} else {
slowValue_ = bigInt.toString(rt).utf8(rt);
}
}
explicit SerializableBigInt(jsi::Runtime &rt, const jsi::BigInt &bigint)
: Serializable(BigIntType), string_(bigint.toString(rt).utf8(rt)) {}
jsi::Value toJSValue(jsi::Runtime &rt) override;
protected:
/**
* This member is used only when the BigInt fits into int64_t range.
*/
std::optional<int64_t> fastValue_{};
std::string slowValue_{};
const std::string string_;
};
class SerializableScalar : public Serializable {
public:
explicit SerializableScalar(double number) : Serializable(ValueType::NumberType) {
explicit SerializableScalar(double number) : Serializable(NumberType) {
data_.number = number;
}
explicit SerializableScalar(bool boolean) : Serializable(ValueType::BooleanType) {
explicit SerializableScalar(bool boolean) : Serializable(BooleanType) {
data_.boolean = boolean;
}
SerializableScalar() : Serializable(ValueType::UndefinedType) {}
explicit SerializableScalar(std::nullptr_t) : Serializable(ValueType::NullType) {}
SerializableScalar() : Serializable(UndefinedType) {}
explicit SerializableScalar(std::nullptr_t) : Serializable(NullType) {}
jsi::Value toJSValue(jsi::Runtime &) override;
jsi::Value toJSValue(jsi::Runtime &);
protected:
union Data {
@@ -396,7 +452,7 @@ class SerializableTurboModuleLike : public Serializable {
jsi::Runtime &rt,
const jsi::Object &object,
const std::shared_ptr<jsi::HostObject> &proto)
: Serializable(ValueType::TurboModuleLikeType),
: Serializable(TurboModuleLikeType),
proto_(std::make_unique<SerializableHostObject>(rt, proto)),
properties_(std::make_unique<SerializableObject>(rt, object)) {}
@@ -407,25 +463,4 @@ class SerializableTurboModuleLike : public Serializable {
const std::unique_ptr<SerializableObject> properties_;
};
jsi::Function getCustomSerializableUnpacker(jsi::Runtime &rt);
class CustomSerializable : public Serializable {
public:
CustomSerializable(std::shared_ptr<Serializable> data, const int typeId)
: Serializable(ValueType::CustomType), data_(std::move(data)), typeId_(typeId) {}
jsi::Value toJSValue(jsi::Runtime &rt) override;
private:
const std::shared_ptr<Serializable> data_;
const int typeId_;
};
struct SerializationData {
std::shared_ptr<SerializableWorklet> determine;
std::shared_ptr<SerializableWorklet> pack;
std::shared_ptr<SerializableWorklet> unpack;
int typeId;
};
} // namespace worklets
@@ -1,7 +1,6 @@
#include <react/debug/react_native_assert.h>
#include <worklets/SharedItems/Synchronizable.h>
#include <memory>
#include <utility>
namespace worklets {
@@ -33,7 +32,8 @@ void Synchronizable::setBlocking(const std::shared_ptr<Serializable> &value) {
jsi::Value Synchronizable::toJSValue(jsi::Runtime &rt) {
auto synchronizableUnpacker = getSynchronizableUnpacker(rt);
auto ref = SerializableJSRef::newNativeStateObject(rt, this->shared_from_this());
auto ref =
SerializableJSRef::newNativeStateObject(rt, this->shared_from_this());
return synchronizableUnpacker.call(rt, std::move(ref));
}
@@ -41,17 +41,25 @@ Synchronizable::Synchronizable(const std::shared_ptr<Serializable> &value)
: Serializable(ValueType::SynchronizableType), value_(value) {}
jsi::Function getSynchronizableUnpacker(jsi::Runtime &rt) {
auto synchronizableUnpacker = rt.global().getProperty(rt, "__synchronizableUnpacker");
react_native_assert(synchronizableUnpacker.isObject() && "synchronizableUnpacker not found");
auto synchronizableUnpacker =
rt.global().getProperty(rt, "__synchronizableUnpacker");
react_native_assert(
synchronizableUnpacker.isObject() && "synchronizableUnpacker not found");
return synchronizableUnpacker.asObject(rt).asFunction(rt);
}
std::shared_ptr<Synchronizable> extractSynchronizableOrThrow(jsi::Runtime &rt, const jsi::Value &value) {
auto serializable =
extractSerializableOrThrow(rt, value, "[Worklets] Expecting the object to be of type SerializableJSRef.");
std::shared_ptr<Synchronizable> extractSynchronizableOrThrow(
jsi::Runtime &rt,
const jsi::Value &value) {
auto serializable = extractSerializableOrThrow(
rt,
value,
"[Worklets] Expecting the object to be of type SerializableJSRef.");
auto synchronizable = std::dynamic_pointer_cast<Synchronizable>(serializable);
react_native_assert(synchronizable != nullptr && "[Worklets] Expected the object to be a Synchronizable.");
react_native_assert(
synchronizable != nullptr &&
"[Worklets] Expected the object to be a Synchronizable.");
return synchronizable;
}
@@ -44,7 +44,7 @@ class Synchronizable : public SynchronizableAccess,
explicit Synchronizable(const std::shared_ptr<Serializable> &value);
~Synchronizable() override = default;
virtual ~Synchronizable() = default;
private:
std::shared_ptr<Serializable> value_;
@@ -52,6 +52,8 @@ class Synchronizable : public SynchronizableAccess,
jsi::Function getSynchronizableUnpacker(jsi::Runtime &rt);
std::shared_ptr<Synchronizable> extractSynchronizableOrThrow(jsi::Runtime &rt, const jsi::Value &value);
std::shared_ptr<Synchronizable> extractSynchronizableOrThrow(
jsi::Runtime &rt,
const jsi::Value &value);
}; // namespace worklets
@@ -6,7 +6,8 @@ namespace worklets {
void SynchronizableAccess::getBlockingBefore() {
std::unique_lock<std::mutex> lock(accessLock_);
queue_.wait(lock, [this]() {
return !blockingWriter_ /* && dirtyWriters_ == 0 */ && (!imperativelyLocked_ || imperativeOwner_ == pthread_self());
return !blockingWriter_ /* && dirtyWriters_ == 0 */ &&
(!imperativelyLocked_ || imperativeOwner_ == pthread_self());
});
blockingReaders_++;
}
@@ -43,7 +44,8 @@ void SynchronizableAccess::getBlockingAfter() {
void SynchronizableAccess::setBlockingBefore() {
std::unique_lock<std::mutex> lock(accessLock_);
queue_.wait(lock, [this]() {
return !blockingWriter_ && blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ &&
return !blockingWriter_ &&
blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ &&
(!imperativelyLocked_ || imperativeOwner_ == pthread_self());
});
blockingWriter_ = true;
@@ -58,7 +60,8 @@ void SynchronizableAccess::setBlockingAfter() {
void SynchronizableAccess::lock() {
std::unique_lock<std::mutex> lock(accessLock_);
queue_.wait(lock, [this]() {
return !blockingWriter_ && blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ &&
return !blockingWriter_ &&
blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ &&
(!imperativelyLocked_ || imperativeOwner_ == pthread_self());
});
imperativelyLocked_ = true;
@@ -8,15 +8,3 @@ so we have to check if headers are available.
#if __APPLE__ && __has_include(<hermes/hermes.h>)
#define JS_RUNTIME_HERMES 1
#endif
#if REACT_NATIVE_MINOR_VERSION >= 84
#include <cxxreact/JSBigString.h>
namespace worklets {
using JSBigStringBuffer = facebook::react::JSBigString;
}
#else
#include <jsireact/JSIExecutor.h>
namespace worklets {
using JSBigStringBuffer = facebook::react::BigStringBuffer;
}
#endif // REACT_NATIVE_MINOR_VERSION >= 84
@@ -1,8 +1,5 @@
#include <worklets/Tools/FeatureFlags.h>
#include <string>
#include <unordered_map>
namespace worklets {
std::unordered_map<std::string, bool> DynamicFeatureFlags::flags_;
@@ -1,11 +1,8 @@
#include <worklets/Tools/JSISerializer.h>
#include <cxxabi.h>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace worklets {
@@ -63,23 +60,39 @@ const std::vector<std::string> SUPPORTED_REFLECTION_TYPES = {
"Proxy",
};
static inline std::string getObjectTypeName(jsi::Runtime &rt, const jsi::Object &object) {
return object.getPropertyAsObject(rt, "constructor").getProperty(rt, "name").toString(rt).utf8(rt);
static inline std::string getObjectTypeName(
jsi::Runtime &rt,
const jsi::Object &object) {
return object.getPropertyAsObject(rt, "constructor")
.getProperty(rt, "name")
.toString(rt)
.utf8(rt);
}
static inline bool isInstanceOf(jsi::Runtime &rt, const jsi::Object &object, const std::string &type) {
static inline bool isInstanceOf(
jsi::Runtime &rt,
const jsi::Object &object,
const std::string &type) {
return getObjectTypeName(rt, object) == type;
}
static inline bool
isInstanceOfAny(jsi::Runtime &rt, const jsi::Object &object, const std::vector<std::string> &supportedTypes) {
static inline bool isInstanceOfAny(
jsi::Runtime &rt,
const jsi::Object &object,
const std::vector<std::string> &supportedTypes) {
auto instanceType = getObjectTypeName(rt, object);
return std::find(supportedTypes.begin(), supportedTypes.end(), instanceType) != supportedTypes.end();
return std::find(
supportedTypes.begin(), supportedTypes.end(), instanceType) !=
supportedTypes.end();
}
JSISerializer::JSISerializer(jsi::Runtime &rt)
: rt_(rt), visitedNodes_(rt_.global().getPropertyAsFunction(rt_, "Set").callAsConstructor(rt_).asObject(rt_)) {}
: rt_(rt),
visitedNodes_(rt_.global()
.getPropertyAsFunction(rt_, "Set")
.callAsConstructor(rt_)
.asObject(rt_)) {}
std::string JSISerializer::stringifyWithName(const jsi::Object &object) {
std::stringstream ss;
@@ -117,7 +130,8 @@ std::string JSISerializer::stringifyFunction(const jsi::Function &func) {
std::string JSISerializer::stringifyHostObject(jsi::HostObject &hostObject) {
int status = -1;
char *hostObjClassName = abi::__cxa_demangle(typeid(hostObject).name(), NULL, NULL, &status);
char *hostObjClassName =
abi::__cxa_demangle(typeid(hostObject).name(), NULL, NULL, &status);
if (status != 0) {
return "[jsi::HostObject]";
}
@@ -135,7 +149,8 @@ std::string JSISerializer::stringifyHostObject(jsi::HostObject &hostObject) {
for (const auto &key : props) {
auto formattedKey = key.utf8(rt_);
auto value = hostObject.get(rt_, key);
ss << '"' << formattedKey << '"' << ": " << stringifyJSIValueRecursively(value);
ss << '"' << formattedKey << '"' << ": "
<< stringifyJSIValueRecursively(value);
if (formattedKey != lastKey) {
ss << ", ";
}
@@ -155,7 +170,8 @@ std::string JSISerializer::stringifyObject(const jsi::Object &object) {
for (size_t i = 0, propsCount = props.size(rt_); i < propsCount; i++) {
jsi::String propName = props.getValueAtIndex(rt_, i).toString(rt_);
ss << '"' << propName.utf8(rt_) << '"' << ": " << stringifyJSIValueRecursively(object.getProperty(rt_, propName));
ss << '"' << propName.utf8(rt_) << '"' << ": "
<< stringifyJSIValueRecursively(object.getProperty(rt_, propName));
if (i != propsCount - 1) {
ss << ", ";
}
@@ -175,7 +191,9 @@ std::string JSISerializer::stringifyError(const jsi::Object &object) {
std::string JSISerializer::stringifySet(const jsi::Object &object) {
std::stringstream ss;
jsi::Function arrayFrom = rt_.global().getPropertyAsObject(rt_, "Array").getPropertyAsFunction(rt_, "from");
jsi::Function arrayFrom = rt_.global()
.getPropertyAsObject(rt_, "Array")
.getPropertyAsFunction(rt_, "from");
jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_);
if (!result.isArray(rt_)) {
@@ -199,7 +217,9 @@ std::string JSISerializer::stringifySet(const jsi::Object &object) {
std::string JSISerializer::stringifyMap(const jsi::Object &object) {
std::stringstream ss;
jsi::Function arrayFrom = rt_.global().getPropertyAsObject(rt_, "Array").getPropertyAsFunction(rt_, "from");
jsi::Function arrayFrom = rt_.global()
.getPropertyAsObject(rt_, "Array")
.getPropertyAsFunction(rt_, "from");
jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_);
if (!result.isArray(rt_)) {
@@ -213,7 +233,8 @@ std::string JSISerializer::stringifyMap(const jsi::Object &object) {
auto pair = arr.getValueAtIndex(rt_, i).asObject(rt_).getArray(rt_);
auto key = pair.getValueAtIndex(rt_, 0);
auto value = pair.getValueAtIndex(rt_, 1);
ss << stringifyJSIValueRecursively(key) << ": " << stringifyJSIValueRecursively(value);
ss << stringifyJSIValueRecursively(key) << ": "
<< stringifyJSIValueRecursively(value);
if (i != length - 1) {
ss << ", ";
}
@@ -237,15 +258,21 @@ std::string JSISerializer::stringifyRecursiveType(const jsi::Object &object) {
}
std::string JSISerializer::stringifyWithToString(const jsi::Object &object) {
return object.getPropertyAsFunction(rt_, "toString").callWithThis(rt_, object).toString(rt_).utf8(rt_);
return object.getPropertyAsFunction(rt_, "toString")
.callWithThis(rt_, object)
.toString(rt_)
.utf8(rt_);
}
std::string JSISerializer::stringifyJSIValueRecursively(const jsi::Value &value, bool isTopLevel) {
std::string JSISerializer::stringifyJSIValueRecursively(
const jsi::Value &value,
bool isTopLevel) {
if (value.isBool() || value.isNumber()) {
return value.toString(rt_).utf8(rt_);
}
if (value.isString()) {
return isTopLevel ? value.getString(rt_).utf8(rt_) : '"' + value.getString(rt_).utf8(rt_) + '"';
return isTopLevel ? value.getString(rt_).utf8(rt_)
: '"' + value.getString(rt_).utf8(rt_) + '"';
}
if (value.isSymbol()) {
return value.getSymbol(rt_).toString(rt_);
@@ -283,12 +310,15 @@ std::string JSISerializer::stringifyJSIValueRecursively(const jsi::Value &value,
isInstanceOfAny(rt_, object, SUPPORTED_STRUCTURED_DATA_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_MANAGING_MEMORY_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_ABSTRACTION_OBJECT_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_REFLECTION_TYPES) || isInstanceOf(rt_, object, "Intl") ||
isInstanceOf(rt_, object, "WeakMap") || isInstanceOf(rt_, object, "WeakSet")) {
isInstanceOfAny(rt_, object, SUPPORTED_REFLECTION_TYPES) ||
isInstanceOf(rt_, object, "Intl") ||
isInstanceOf(rt_, object, "WeakMap") ||
isInstanceOf(rt_, object, "WeakSet")) {
// TODO: Consider extending this log info
return stringifyWithName(object);
}
if (isInstanceOf(rt_, object, "Date") || isInstanceOf(rt_, object, "RegExp")) {
if (isInstanceOf(rt_, object, "Date") ||
isInstanceOf(rt_, object, "RegExp")) {
return stringifyWithToString(object);
}
if (isInstanceOf(rt_, object, "Map")) {
@@ -11,7 +11,9 @@ namespace worklets {
class JSISerializer {
public:
explicit JSISerializer(jsi::Runtime &rt);
std::string stringifyJSIValueRecursively(const jsi::Value &value, bool isTopLevel = false);
std::string stringifyJSIValueRecursively(
const jsi::Value &value,
bool isTopLevel = false);
private:
std::string stringifyArray(const jsi::Array &arr);
@@ -26,11 +28,14 @@ class JSISerializer {
std::string stringifyRecursiveType(const jsi::Object &object);
bool hasBeenVisited(const jsi::Object &object) {
return visitedNodes_.getPropertyAsFunction(rt_, "has").callWithThis(rt_, visitedNodes_, object).getBool();
return visitedNodes_.getPropertyAsFunction(rt_, "has")
.callWithThis(rt_, visitedNodes_, object)
.getBool();
}
void markAsVisited(const jsi::Object &object) {
visitedNodes_.getPropertyAsFunction(rt_, "add").callWithThis(rt_, visitedNodes_, object);
visitedNodes_.getPropertyAsFunction(rt_, "add")
.callWithThis(rt_, visitedNodes_, object);
}
jsi::Runtime &rt_;
@@ -1,7 +1,6 @@
#include <worklets/Tools/JSLogger.h>
#include <memory>
#include <string>
#include <utility>
namespace worklets {
@@ -20,7 +19,8 @@ void JSLogger::reportFatalErrorOnJS(
const std::shared_ptr<JSScheduler> &jsScheduler,
JSErrorData &&jsErrorData,
bool force) {
auto job = [jsErrorData = std::move(jsErrorData), force](jsi::Runtime &rnRuntime) {
auto job = [jsErrorData = std::move(jsErrorData),
force](jsi::Runtime &rnRuntime) {
reportFatalErrorOnJS(rnRuntime, jsErrorData, force);
};
if (jsScheduler->canInvokeSyncOnJS()) {
@@ -30,20 +30,38 @@ void JSLogger::reportFatalErrorOnJS(
}
}
void JSLogger::reportFatalErrorOnJS(jsi::Runtime &rnRuntime, const JSErrorData &jsErrorData, bool force) {
void JSLogger::reportFatalErrorOnJS(
jsi::Runtime &rnRuntime,
const JSErrorData &jsErrorData,
bool force) {
const auto &global = rnRuntime.global();
const auto errorInstance =
rnRuntime.global().getPropertyAsFunction(rnRuntime, "Error").callAsConstructor(rnRuntime).asObject(rnRuntime);
const auto errorInstance = rnRuntime.global()
.getPropertyAsFunction(rnRuntime, "Error")
.callAsConstructor(rnRuntime)
.asObject(rnRuntime);
errorInstance.setProperty(rnRuntime, "message", jsi::String::createFromUtf8(rnRuntime, jsErrorData.message));
errorInstance.setProperty(
rnRuntime,
"message",
jsi::String::createFromUtf8(rnRuntime, jsErrorData.message));
errorInstance.setProperty(rnRuntime, "stack", jsi::String::createFromUtf8(rnRuntime, jsErrorData.stack));
errorInstance.setProperty(
rnRuntime,
"stack",
jsi::String::createFromUtf8(rnRuntime, jsErrorData.stack));
errorInstance.setProperty(rnRuntime, "name", jsi::String::createFromUtf8(rnRuntime, jsErrorData.name));
errorInstance.setProperty(
rnRuntime,
"name",
jsi::String::createFromUtf8(rnRuntime, jsErrorData.name));
errorInstance.setProperty(rnRuntime, "jsEngine", jsi::String::createFromUtf8(rnRuntime, jsErrorData.jsEngine));
errorInstance.setProperty(
rnRuntime,
"jsEngine",
jsi::String::createFromUtf8(rnRuntime, jsErrorData.jsEngine));
const auto &reportFatalErrorFunction = global.getPropertyAsFunction(rnRuntime, "__reportFatalRemoteError");
const auto &reportFatalErrorFunction =
global.getPropertyAsFunction(rnRuntime, "__reportFatalRemoteError");
reportFatalErrorFunction.call(rnRuntime, errorInstance, force);
}
@@ -16,14 +16,20 @@ struct JSErrorData {
class JSLogger {
public:
explicit JSLogger(const std::shared_ptr<JSScheduler> &jsScheduler) : jsScheduler_(jsScheduler) {}
explicit JSLogger(const std::shared_ptr<JSScheduler> &jsScheduler)
: jsScheduler_(jsScheduler) {}
void warnOnJS(const std::string &warning) const;
static void
reportFatalErrorOnJS(const std::shared_ptr<JSScheduler> &jsScheduler, JSErrorData &&jsErrorData, bool force = false);
static void reportFatalErrorOnJS(
const std::shared_ptr<JSScheduler> &jsScheduler,
JSErrorData &&jsErrorData,
bool force = false);
private:
static void reportFatalErrorOnJS(jsi::Runtime &rnRuntime, const JSErrorData &jsErrorData, bool force = false);
static void reportFatalErrorOnJS(
jsi::Runtime &rnRuntime,
const JSErrorData &jsErrorData,
bool force = false);
const std::shared_ptr<JSScheduler> jsScheduler_;
};
@@ -6,15 +6,18 @@
namespace worklets {
void JSScheduler::scheduleOnJS(Job job) {
jsCallInvoker_->invokeAsync([job = std::move(job), &rt = rnRuntime_] { job(rt); });
jsCallInvoker_->invokeAsync(
[job = std::move(job), &rt = rnRuntime_] { job(rt); });
}
bool JSScheduler::canInvokeSyncOnJS() {
return isJavaScriptQueue_();
}
void JSScheduler::invokeSyncOnJS(const Job &job) {
react_native_assert(canInvokeSyncOnJS() && "JSScheduler::invokeSyncOnJS should only be called from the JS thread");
void JSScheduler::invokeSyncOnJS(Job job) {
react_native_assert(
canInvokeSyncOnJS() &&
"JSScheduler::invokeSyncOnJS should only be called from the JS thread");
job(rnRuntime_);
}
@@ -18,11 +18,13 @@ class JSScheduler {
jsi::Runtime &rnRuntime,
const std::shared_ptr<CallInvoker> &jsCallInvoker,
std::function<bool()> &&isJavaScriptQueue)
: rnRuntime_(rnRuntime), jsCallInvoker_(jsCallInvoker), isJavaScriptQueue_(isJavaScriptQueue) {}
: rnRuntime_(rnRuntime),
jsCallInvoker_(jsCallInvoker),
isJavaScriptQueue_(isJavaScriptQueue) {}
void scheduleOnJS(std::function<void(jsi::Runtime &rt)> job);
void invokeSyncOnJS(const std::function<void(jsi::Runtime &rt)> &job);
void invokeSyncOnJS(std::function<void(jsi::Runtime &rt)> job);
bool canInvokeSyncOnJS();
@@ -28,7 +28,7 @@ class SingleInstanceChecker {
~SingleInstanceChecker();
private:
void assertWithMessage(bool condition, const std::string &message) {
void assertWithMessage(bool condition, std::string message) {
if (!condition) {
#ifdef ANDROID
__android_log_print(ANDROID_LOG_WARN, "Worklets", "%s", message.c_str());
@@ -50,7 +50,8 @@ class SingleInstanceChecker {
template <class T>
SingleInstanceChecker<T>::SingleInstanceChecker() {
int status = 0;
std::string className = __cxxabiv1::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
std::string className =
__cxxabiv1::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
// React Native can spawn up to two instances of a Native Module at the same
// time. This happens during a reload when a new instance of React Native is
@@ -1,9 +1,7 @@
#include <worklets/Tools/VersionUtils.h>
#include <iostream>
#include <memory>
#include <regex>
#include <string>
using namespace facebook;
@@ -11,7 +9,8 @@ namespace worklets {
bool matchVersion(const std::string &version1, const std::string &version2) {
std::regex pattern("^\\d+\\.\\d+\\.\\d+$");
if (std::regex_match(version1, pattern) && std::regex_match(version2, pattern)) {
if (std::regex_match(version1, pattern) &&
std::regex_match(version2, pattern)) {
auto majorPattern = std::regex("^\\d+");
std::smatch major1;
std::smatch major2;
@@ -64,7 +63,8 @@ void checkJSVersion(
libraryPrefix +
"Mismatch between C++ code version and "
"JavaScript code version (") +
cppVersion + " vs. " + jsVersion + " respectively).\n" + "See " + docsBaseUrl +
cppVersion + " vs. " + jsVersion + " respectively).\n" + "See " +
docsBaseUrl +
"/guides/"
"troubleshooting#mismatch-between-c-code-version-and-javascript-code-"
"version` for more details.");
@@ -1,15 +1,13 @@
#include <worklets/Tools/WorkletEventHandler.h>
#include <memory>
#include <string>
namespace worklets {
void WorkletEventHandler::process(
const std::shared_ptr<WorkletRuntime> &workletRuntime,
const double eventTimestamp,
const jsi::Value &eventValue) const {
workletRuntime->runSync(handlerFunction_, jsi::Value(eventTimestamp), eventValue);
workletRuntime->runGuarded(
handlerFunction_, jsi::Value(eventTimestamp), eventValue);
}
uint64_t WorkletEventHandler::getHandlerId() const {
@@ -4,14 +4,16 @@
#include <memory>
#include <sstream>
#include <string>
#include <vector>
using namespace facebook;
namespace worklets::jsi_utils {
jsi::Array convertStringToArray(jsi::Runtime &rt, const std::string &value, const unsigned int expectedSize) {
jsi::Array convertStringToArray(
jsi::Runtime &rt,
const std::string &value,
const unsigned int expectedSize) {
std::vector<float> transformMatrixList;
std::istringstream stringStream(value);
std::copy(
@@ -19,7 +21,8 @@ jsi::Array convertStringToArray(jsi::Runtime &rt, const std::string &value, cons
std::istream_iterator<float>(),
std::back_inserter(transformMatrixList));
react_native_assert(
transformMatrixList.size() == expectedSize && "Transform matrix list size is different than expected");
transformMatrixList.size() == expectedSize &&
"Transform matrix list size is different than expected");
jsi::Array matrix(rt, expectedSize);
for (unsigned int i = 0; i < expectedSize; i++) {
matrix.setValueAtIndex(rt, i, transformMatrixList[i]);
@@ -27,10 +30,13 @@ jsi::Array convertStringToArray(jsi::Runtime &rt, const std::string &value, cons
return matrix;
}
jsi::Object optimizedFromHostObject(jsi::Runtime &rt, std::shared_ptr<jsi::HostObject> &&hostObject) {
jsi::Object optimizedFromHostObject(
jsi::Runtime &rt,
std::shared_ptr<jsi::HostObject> &&hostObject) {
auto optimizedObject = jsi::Object(rt);
for (const auto &propertyName : hostObject->getPropertyNames(rt)) {
optimizedObject.setProperty(rt, propertyName, hostObject->get(rt, propertyName));
optimizedObject.setProperty(
rt, propertyName, hostObject->get(rt, propertyName));
}
return optimizedObject;
}
@@ -40,7 +40,9 @@ inline jsi::Object get<jsi::Object>(jsi::Runtime &rt, const jsi::Value *value) {
}
template <>
inline jsi::Value const &get<jsi::Value const &>(jsi::Runtime &, const jsi::Value *value) {
inline jsi::Value const &get<jsi::Value const &>(
jsi::Runtime &,
const jsi::Value *value) {
return *value;
}
@@ -55,7 +57,9 @@ inline jsi::Value const &get<jsi::Value const &>(jsi::Runtime &, const jsi::Valu
// BEGIN implementations for `convertArgs` specializations.
// specialization for empty `Targs` - returns an empty tuple
template <typename... Args>
inline std::enable_if_t<(sizeof...(Args) == 0), std::tuple<>> convertArgs(jsi::Runtime &, const jsi::Value *) {
inline std::enable_if_t<(sizeof...(Args) == 0), std::tuple<>> convertArgs(
jsi::Runtime &,
const jsi::Value *) {
return std::make_tuple();
}
@@ -63,7 +67,9 @@ inline std::enable_if_t<(sizeof...(Args) == 0), std::tuple<>> convertArgs(jsi::R
// then calls recursively on the rest of `args`
// and returns the concatenation of results
template <typename T, typename... Rest>
inline std::tuple<T, Rest...> convertArgs(jsi::Runtime &rt, const jsi::Value *args) {
inline std::tuple<T, Rest...> convertArgs(
jsi::Runtime &rt,
const jsi::Value *args) {
auto arg = std::tuple<T>(get<T>(rt, args));
auto rest = convertArgs<Rest...>(rt, std::next(args));
return std::tuple_cat(std::move(arg), std::move(rest));
@@ -73,9 +79,14 @@ inline std::tuple<T, Rest...> convertArgs(jsi::Runtime &rt, const jsi::Value *ar
// returns a tuple with the result of casting `args` to appropriate
// native C++ types needed to call `function`
template <typename Ret, typename... Args>
std::tuple<Args...>
getArgsForFunction(std::function<Ret(Args...)>, jsi::Runtime &rt, const jsi::Value *args, const size_t count) {
react_native_assert(sizeof...(Args) == count && "Argument list has different length than expected");
std::tuple<Args...> getArgsForFunction(
std::function<Ret(Args...)>,
jsi::Runtime &rt,
const jsi::Value *args,
const size_t count) {
react_native_assert(
sizeof...(Args) == count &&
"Argument list has different length than expected");
return convertArgs<Args...>(rt, args);
}
@@ -88,27 +99,36 @@ std::tuple<jsi::Runtime &, Args...> getArgsForFunction(
jsi::Runtime &rt,
const jsi::Value *args,
const size_t count) {
react_native_assert(sizeof...(Args) == count && "Argument list has different length than expected");
react_native_assert(
sizeof...(Args) == count &&
"Argument list has different length than expected");
return std::tuple_cat(std::tie(rt), convertArgs<Args...>(rt, args));
}
// calls `function` with `args`
template <typename Ret, typename... Args>
inline jsi::Value apply(std::function<Ret(Args...)> function, std::tuple<Args...> args) {
inline jsi::Value apply(
std::function<Ret(Args...)> function,
std::tuple<Args...> args) {
return std::apply(function, std::move(args));
}
// calls string-returning `function` with `args`,
// and returns the string
template <typename... Args>
inline jsi::Value apply(jsi::Runtime &rt, std::function<std::string(Args...)> function, std::tuple<Args...> args) {
inline jsi::Value apply(
jsi::Runtime &rt,
std::function<std::string(Args...)> function,
std::tuple<Args...> args) {
return jsi::String::createFromUtf8(rt, std::apply(function, std::move(args)));
}
// calls void-returning `function` with `args`,
// and returns `undefined`
template <typename... Args>
inline jsi::Value apply(std::function<void(Args...)> function, std::tuple<Args...> args) {
inline jsi::Value apply(
std::function<void(Args...)> function,
std::tuple<Args...> args) {
std::apply(function, std::move(args));
return jsi::Value::undefined();
}
@@ -117,7 +137,11 @@ inline jsi::Value apply(std::function<void(Args...)> function, std::tuple<Args..
// from a native function `function`
template <typename Fun>
jsi::HostFunctionType createHostFunction(Fun function) {
return [function](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, const size_t count) {
return [function](
jsi::Runtime &rt,
const jsi::Value &,
const jsi::Value *args,
const size_t count) {
auto argz = getArgsForFunction(function, rt, args, count);
return apply(function, std::move(argz));
};
@@ -126,8 +150,13 @@ jsi::HostFunctionType createHostFunction(Fun function) {
// returns a function with JSI calling convention
// from a native function `function` returning a string
template <typename... Args>
jsi::HostFunctionType createHostFunction(std::function<std::string(Args...)> function) {
return [function](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, const size_t count) {
jsi::HostFunctionType createHostFunction(
std::function<std::string(Args...)> function) {
return [function](
jsi::Runtime &rt,
const jsi::Value &,
const jsi::Value *args,
const size_t count) {
auto argz = getArgsForFunction(function, rt, args, count);
return apply(rt, function, std::move(argz));
};
@@ -150,11 +179,14 @@ struct takes_runtime<jsi::Runtime &, Rest...> {
// and installs it as a global function named `name`
// in the `rt` JS runtime
template <typename Ret, typename... Args>
void installJsiFunction(jsi::Runtime &rt, std::string_view name, std::function<Ret(Args...)> function) {
void installJsiFunction(
jsi::Runtime &rt,
std::string_view name,
std::function<Ret(Args...)> function) {
auto clb = createHostFunction(function);
auto argsCount = sizeof...(Args) - takes_runtime<Args...>::value;
jsi::Value jsiFunction =
jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, name.data()), argsCount, clb);
jsi::Value jsiFunction = jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, name.data()), argsCount, clb);
rt.global().setProperty(rt, name.data(), jsiFunction);
}
@@ -164,8 +196,13 @@ void installJsiFunction(jsi::Runtime &rt, std::string_view name, Fun function) {
installJsiFunction(rt, name, std::function(std::forward<Fun>(function)));
}
jsi::Array convertStringToArray(jsi::Runtime &rt, const std::string &value, const unsigned int expectedSize);
jsi::Array convertStringToArray(
jsi::Runtime &rt,
const std::string &value,
const unsigned int expectedSize);
jsi::Object optimizedFromHostObject(jsi::Runtime &rt, std::shared_ptr<jsi::HostObject> &&hostObject);
jsi::Object optimizedFromHostObject(
jsi::Runtime &rt,
std::shared_ptr<jsi::HostObject> &&hostObject);
} // namespace worklets::jsi_utils
@@ -31,7 +31,9 @@ namespace worklets {
struct WorkletsSystraceSection {
public:
template <typename... ConvertsToStringPiece>
explicit WorkletsSystraceSection(const char *name, ConvertsToStringPiece &&...args) {
explicit WorkletsSystraceSection(
const char *name,
ConvertsToStringPiece &&...args) {
ATrace_beginSection(name);
}
@@ -43,7 +45,8 @@ struct WorkletsSystraceSection {
// The apple part is copied from React Native
// from
// https://github.com/facebook/react-native/blob/5697d923a05119314b4cfcd556cb243986637764/packages/react-native/ReactCommon/cxxreact/SystraceSection.h
#elif defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES && defined(WORKLETS_PROFILING)
#elif defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES && \
defined(WORKLETS_PROFILING)
template <typename T, typename = void>
struct renderer {
@@ -55,7 +58,8 @@ struct renderer {
};
template <typename T>
static auto render(const T &t) -> decltype(renderer<T>::render(std::declval<const T &>())) {
static auto render(const T &t)
-> decltype(renderer<T>::render(std::declval<const T &>())) {
return renderer<T>::render(t);
}
@@ -63,7 +67,8 @@ inline os_log_t instrumentsLogHandle = nullptr;
static inline os_log_t getOrCreateInstrumentsLogHandle() {
if (!instrumentsLogHandle) {
instrumentsLogHandle = os_log_create("dev.worklets.instruments", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
instrumentsLogHandle = os_log_create(
"dev.worklets.instruments", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
}
return instrumentsLogHandle;
}
@@ -71,7 +76,9 @@ static inline os_log_t getOrCreateInstrumentsLogHandle() {
struct WorkletsSystraceSection {
public:
template <typename... ConvertsToStringPiece>
explicit WorkletsSystraceSection(const char *name, ConvertsToStringPiece &&...args) {
explicit WorkletsSystraceSection(
const char *name,
ConvertsToStringPiece &&...args) {
os_log_t instrumentsLogHandle = worklets::getOrCreateInstrumentsLogHandle();
// If the log isn't enabled, we don't want the performance overhead of the
@@ -90,11 +97,22 @@ struct WorkletsSystraceSection {
signpostID_ = os_signpost_id_make_with_pointer(instrumentsLogHandle, this);
os_signpost_interval_begin(instrumentsLogHandle, signpostID_, "Worklets", "%s begin: %s", name, argsString.c_str());
os_signpost_interval_begin(
instrumentsLogHandle,
signpostID_,
"Worklets",
"%s begin: %s",
name,
argsString.c_str());
}
~WorkletsSystraceSection() {
os_signpost_interval_end(worklets::instrumentsLogHandle, signpostID_, "Worklets", "%s end", name_.data());
os_signpost_interval_end(
worklets::instrumentsLogHandle,
signpostID_,
"Worklets",
"%s end",
name_.data());
}
private:
@@ -107,10 +125,12 @@ struct WorkletsSystraceSection {
struct WorkletsSystraceSection {
public:
template <typename... ConvertsToStringPiece>
explicit WorkletsSystraceSection(const char *name, ConvertsToStringPiece &&...args) {}
explicit WorkletsSystraceSection(
const char *name,
ConvertsToStringPiece &&...args) {}
};
#endif // defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES && \
// defined(WORKLETS_PROFILING)
#endif // defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES &&
// defined(WORKLETS_PROFILING)
} // namespace worklets
@@ -21,13 +21,19 @@ std::string getWorkletsCppVersion() {
void injectWorkletsCppVersion(jsi::Runtime &rnRuntime) {
auto version = getWorkletsCppVersion();
rnRuntime.global().setProperty(rnRuntime, "_WORKLETS_VERSION_CPP", jsi::String::createFromUtf8(rnRuntime, version));
rnRuntime.global().setProperty(
rnRuntime,
"_WORKLETS_VERSION_CPP",
jsi::String::createFromUtf8(rnRuntime, version));
}
#ifndef NDEBUG
void checkJSVersion(jsi::Runtime &rnRuntime, const std::shared_ptr<worklets::JSLogger> &jsLogger) {
void checkJSVersion(
jsi::Runtime &rnRuntime,
const std::shared_ptr<worklets::JSLogger> &jsLogger) {
auto cppVersion = getWorkletsCppVersion();
auto jsVersionValue = rnRuntime.global().getProperty(rnRuntime, "_WORKLETS_VERSION_JS");
auto jsVersionValue =
rnRuntime.global().getProperty(rnRuntime, "_WORKLETS_VERSION_JS");
worklets::checkJSVersion(
rnRuntime,
jsVersionValue,
@@ -37,7 +43,9 @@ void checkJSVersion(jsi::Runtime &rnRuntime, const std::shared_ptr<worklets::JSL
"https://docs.swmansion.com/react-native-worklets/docs");
}
#else
void checkJSVersion(jsi::Runtime &rnRuntime, const std::shared_ptr<worklets::JSLogger> &jsLogger) {
void checkJSVersion(
jsi::Runtime &rnRuntime,
const std::shared_ptr<worklets::JSLogger> &jsLogger) {
// In release builds we don't check the version, hence
// this function is a NOOP.
}
@@ -13,6 +13,8 @@ namespace worklets {
std::string getWorkletsCppVersion();
void injectWorkletsCppVersion(jsi::Runtime &);
void checkJSVersion(jsi::Runtime &, const std::shared_ptr<worklets::JSLogger> &);
void checkJSVersion(
jsi::Runtime &,
const std::shared_ptr<worklets::JSLogger> &);
}; // namespace worklets
@@ -1,10 +1,9 @@
#include <worklets/Tools/WorkletsJSIUtils.h>
#include <worklets/Tools/WorkletsVersion.h>
#include <worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h>
#include <worklets/WorkletRuntime/RuntimeKind.h>
#include <worklets/WorkletRuntime/WorkletRuntimeCollector.h>
#include <memory>
#include <utility>
namespace worklets {
@@ -13,7 +12,10 @@ void RNRuntimeWorkletDecorator::decorate(
jsi::Runtime &rnRuntime,
jsi::Object &&jsiWorkletsModuleProxy,
const std::shared_ptr<JSLogger> &jsLogger) {
rnRuntime.global().setProperty(rnRuntime, runtimeKindBindingName, static_cast<int>(RuntimeKind::ReactNative));
rnRuntime.global().setProperty(
rnRuntime,
runtimeKindBindingName,
static_cast<int>(RuntimeKind::ReactNative));
rnRuntime.global().setProperty(rnRuntime, "_WORKLET", false);
@@ -21,7 +23,8 @@ void RNRuntimeWorkletDecorator::decorate(
// react-native-screens 4.9.0 depends on it
rnRuntime.global().setProperty(rnRuntime, "_IS_FABRIC", true);
rnRuntime.global().setProperty(rnRuntime, "__workletsModuleProxy", std::move(jsiWorkletsModuleProxy));
rnRuntime.global().setProperty(
rnRuntime, "__workletsModuleProxy", std::move(jsiWorkletsModuleProxy));
WorkletRuntimeCollector::install(rnRuntime);
@@ -29,19 +32,7 @@ void RNRuntimeWorkletDecorator::decorate(
checkJSVersion(rnRuntime, jsLogger);
#endif // NDEBUG
#ifdef IS_REANIMATED_EXAMPLE_APP
installDebugBindings(rnRuntime);
#endif // IS_REANIMATED_EXAMPLE_APP
injectWorkletsCppVersion(rnRuntime);
}
#ifdef IS_REANIMATED_EXAMPLE_APP
void RNRuntimeWorkletDecorator::installDebugBindings(jsi::Runtime &rnRuntime) {
jsi_utils::installJsiFunction(rnRuntime, "__hasNativeState", [](jsi::Runtime &rt, const jsi::Value &value) {
return jsi::Value(value.isObject() && value.asObject(rt).hasNativeState(rt));
});
}
#endif // IS_REANIMATED_EXAMPLE_APP
} // namespace worklets
@@ -12,13 +12,10 @@ namespace worklets {
class RNRuntimeWorkletDecorator {
// TODO: Rename to `RNRuntimeWorkletsDecorator` or something more suitable.
public:
static void
decorate(jsi::Runtime &rnRuntime, jsi::Object &&jsiWorkletsModuleProxy, const std::shared_ptr<JSLogger> &jsLogger);
#ifdef IS_REANIMATED_EXAMPLE_APP
private:
static void installDebugBindings(jsi::Runtime &rnRuntime);
#endif // IS_REANIMATED_EXAMPLE_APP
static void decorate(
jsi::Runtime &rnRuntime,
jsi::Object &&jsiWorkletsModuleProxy,
const std::shared_ptr<JSLogger> &jsLogger);
};
} // namespace worklets
@@ -1,17 +0,0 @@
#pragma once
#include <jsi/jsi.h>
#include <functional>
using namespace facebook;
namespace worklets {
struct RuntimeBindings {
using RequestAnimationFrame = std::function<void(std::function<void(const double)>)>;
const RequestAnimationFrame requestAnimationFrame;
};
} // namespace worklets
@@ -1,11 +1,3 @@
#include <worklets/WorkletRuntime/RuntimeData.h>
#include <string>
namespace worklets {
namespace RuntimeData {
const std::string uiRuntimeName{"UI"};
} // namespace RuntimeData
} // namespace worklets
@@ -1,23 +1,10 @@
#pragma once
#include <jsi/jsi.h>
#include <string>
namespace worklets {
namespace RuntimeData {
/**
* Unused, but kept for possible future use.
*/
constexpr uint64_t rnRuntimeId{0};
constexpr uint64_t uiRuntimeId{1};
extern const std::string uiRuntimeName;
#if REACT_NATIVE_MINOR_VERSION >= 81
constexpr facebook::jsi::UUID weakRuntimeUUID{0x770c6f2e, 0x1e4d, 0x436a, 0xa2b1, 0x9f322c8d5f5e};
#endif // REACT_NATIVE_MINOR_VERSION >= 81
}; // namespace RuntimeData
} // namespace worklets
@@ -1,22 +0,0 @@
#pragma once
#if REACT_NATIVE_MINOR_VERSION >= 81
#include <memory>
namespace worklets {
class WorkletRuntime;
/**
* Holds a weak reference to a WorkletRuntime instance.
*
* Used to link jsi::Runtime instances back to their WorkletRuntime holders
* without inducing reference cycles.
*/
struct WeakRuntimeHolder {
std::weak_ptr<WorkletRuntime> weakRuntime;
};
} // namespace worklets
#endif // REACT_NATIVE_MINOR_VERSION >= 81
@@ -1,13 +1,11 @@
#pragma once
#include <utility>
namespace worklets {
/**
* Represents the different types of runtime environments available for
* worklets.
*/
enum class RuntimeKind : std::uint8_t {
enum class RuntimeKind {
ReactNative = 1,
UI = 2,
Worker = 3,
@@ -1,10 +1,7 @@
#include <worklets/NativeModules/JSIWorkletsModuleProxy.h>
#include <worklets/WorkletRuntime/RuntimeManager.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace worklets {
@@ -16,6 +13,26 @@ std::shared_ptr<WorkletRuntime> RuntimeManager::getRuntime(uint64_t runtimeId) {
return nullptr;
}
std::shared_ptr<WorkletRuntime> RuntimeManager::getRuntime(
const std::string &name) {
std::shared_lock lock(weakRuntimesMutex_);
if (nameToRuntimeId_.contains(name)) {
return getRuntime(nameToRuntimeId_.at(name));
}
return nullptr;
}
#ifdef WORKLETS_BUNDLE_MODE
std::shared_ptr<WorkletRuntime> RuntimeManager::getRuntime(
jsi::Runtime *runtime) {
std::shared_lock lock(weakRuntimesMutex_);
if (runtimeAddressToRuntimeId_.contains(runtime)) {
return getRuntime(runtimeAddressToRuntimeId_.at(runtime));
}
return nullptr;
}
#endif // WORKLETS_BUNDLE_MODE
std::vector<std::shared_ptr<WorkletRuntime>> RuntimeManager::getAllRuntimes() {
std::shared_lock lock(weakRuntimesMutex_);
@@ -32,27 +49,28 @@ std::vector<std::shared_ptr<WorkletRuntime>> RuntimeManager::getAllRuntimes() {
}
std::shared_ptr<WorkletRuntime> RuntimeManager::getUIRuntime() {
return getRuntime(RuntimeData::uiRuntimeId);
return getRuntime(uiRuntimeId);
}
std::shared_ptr<WorkletRuntime> RuntimeManager::createWorkletRuntime(
std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy,
const std::string &name,
const std::shared_ptr<SerializableWorklet> &initializer,
std::shared_ptr<SerializableWorklet> initializer,
const std::shared_ptr<AsyncQueue> &queue,
bool enableEventLoop) {
const auto runtimeId = getNextRuntimeId();
const auto jsQueue = jsiWorkletsModuleProxy->getJSQueue();
auto workletRuntime = std::make_shared<WorkletRuntime>(runtimeId, jsQueue, name, queue, enableEventLoop);
auto workletRuntime = std::make_shared<WorkletRuntime>(
runtimeId, jsQueue, name, queue, enableEventLoop);
workletRuntime->init(std::move(jsiWorkletsModuleProxy));
if (initializer) {
workletRuntime->runSync(initializer);
workletRuntime->runGuarded(initializer);
}
registerRuntime(runtimeId, workletRuntime);
registerRuntime(runtimeId, name, workletRuntime);
return workletRuntime;
}
@@ -61,13 +79,9 @@ std::shared_ptr<WorkletRuntime> RuntimeManager::createUninitializedUIRuntime(
const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<AsyncQueue> &uiAsyncQueue) {
const auto uiRuntime = std::make_shared<WorkletRuntime>(
RuntimeData::uiRuntimeId,
jsQueue,
RuntimeData::uiRuntimeName,
uiAsyncQueue,
/*enableEventLoop*/ false);
uiRuntimeId, jsQueue, uiRuntimeName, uiAsyncQueue);
registerRuntime(RuntimeData::uiRuntimeId, uiRuntime);
registerRuntime(uiRuntimeId, uiRuntimeName, uiRuntime);
return uiRuntime;
}
@@ -76,18 +90,16 @@ uint64_t RuntimeManager::getNextRuntimeId() {
return nextRuntimeId_.fetch_add(1, std::memory_order_relaxed);
}
void RuntimeManager::registerRuntime(const uint64_t runtimeId, const std::shared_ptr<WorkletRuntime> &workletRuntime) {
std::unique_lock registrationLock(registrationMutex_);
void RuntimeManager::registerRuntime(
const uint64_t runtimeId,
const std::string &name,
const std::shared_ptr<WorkletRuntime> &workletRuntime) {
std::unique_lock lock(weakRuntimesMutex_);
weakRuntimes_[runtimeId] = workletRuntime;
}
void RuntimeManager::pause() {
registrationMutex_.lock();
}
void RuntimeManager::resume() {
registrationMutex_.unlock();
nameToRuntimeId_[name] = runtimeId;
#ifdef WORKLETS_BUNDLE_MODE
runtimeAddressToRuntimeId_[&workletRuntime->getJSIRuntime()] = runtimeId;
#endif // WORKLETS_BUNDLE_MODE
}
} // namespace worklets
@@ -23,6 +23,10 @@ class JSIWorkletsModuleProxy;
class RuntimeManager {
public:
std::shared_ptr<WorkletRuntime> getRuntime(uint64_t runtimeId);
std::shared_ptr<WorkletRuntime> getRuntime(const std::string &name);
#ifdef WORKLETS_BUNDLE_MODE
std::shared_ptr<WorkletRuntime> getRuntime(jsi::Runtime *runtime);
#endif // WORKLETS_BUNDLE_MODE
std::vector<std::shared_ptr<WorkletRuntime>> getAllRuntimes();
@@ -31,7 +35,7 @@ class RuntimeManager {
std::shared_ptr<WorkletRuntime> createWorkletRuntime(
std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy,
const std::string &name,
const std::shared_ptr<SerializableWorklet> &initializer = nullptr,
std::shared_ptr<SerializableWorklet> initializer = nullptr,
const std::shared_ptr<AsyncQueue> &queue = nullptr,
bool enableEventLoop = true);
@@ -39,21 +43,21 @@ class RuntimeManager {
const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<AsyncQueue> &uiAsyncQueue);
/** Pauses registration of new Worklet Runtimes. */
void pause();
/** Resumes registration of new Worklet Runtimes. */
void resume();
private:
uint64_t getNextRuntimeId();
void registerRuntime(const uint64_t runtimeId, const std::shared_ptr<WorkletRuntime> &workletRuntime);
void registerRuntime(
const uint64_t runtimeId,
const std::string &name,
const std::shared_ptr<WorkletRuntime> &workletRuntime);
std::atomic_uint64_t nextRuntimeId_{RuntimeData::uiRuntimeId + 1};
std::atomic_uint64_t nextRuntimeId_{uiRuntimeId + 1};
std::map<uint64_t, std::weak_ptr<WorkletRuntime>> weakRuntimes_;
std::shared_mutex weakRuntimesMutex_;
std::mutex registrationMutex_;
std::map<std::string, uint64_t> nameToRuntimeId_;
#ifdef WORKLETS_BUNDLE_MODE
std::map<jsi::Runtime *, uint64_t> runtimeAddressToRuntimeId_;
#endif // WORKLETS_BUNDLE_MODE
};
} // namespace worklets
@@ -8,12 +8,16 @@ namespace worklets {
void UIRuntimeDecorator::decorate(
facebook::jsi::Runtime &uiRuntime,
std::function<void(facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback)> &&requestAnimationFrame) {
uiRuntime.global().setProperty(uiRuntime, runtimeKindBindingName, static_cast<int>(RuntimeKind::UI));
std::function<
void(facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback)>
&&requestAnimationFrame) {
uiRuntime.global().setProperty(
uiRuntime, runtimeKindBindingName, static_cast<int>(RuntimeKind::UI));
uiRuntime.global().setProperty(uiRuntime, "_UI", true);
jsi_utils::installJsiFunction(uiRuntime, "requestAnimationFrame", std::move(requestAnimationFrame));
jsi_utils::installJsiFunction(
uiRuntime, "requestAnimationFrame", std::move(requestAnimationFrame));
}
} // namespace worklets
@@ -8,7 +8,9 @@ class UIRuntimeDecorator {
public:
static void decorate(
facebook::jsi::Runtime &uiRuntime,
std::function<void(facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback)> &&requestAnimationFrame);
std::function<void(
facebook::jsi::Runtime &rt,
const facebook::jsi::Value &callback)> &&requestAnimationFrame);
};
} // namespace worklets
@@ -16,29 +16,25 @@ namespace worklets {
using namespace facebook;
using namespace react;
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#if HERMES_ENABLE_DEBUGGER
using namespace facebook::hermes::inspector_modern;
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#endif // HERMES_ENABLE_DEBUGGER
#if HERMES_ENABLE_DEBUGGER
class HermesExecutorRuntimeAdapter
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
: public RuntimeAdapter
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
{
class HermesExecutorRuntimeAdapter : public RuntimeAdapter {
public:
explicit HermesExecutorRuntimeAdapter(
facebook::hermes::HermesRuntime &hermesRuntime,
const std::shared_ptr<MessageQueueThread> &thread)
: hermesRuntime_(hermesRuntime), thread_(thread) {}
: hermesRuntime_(hermesRuntime), thread_(std::move(thread)) {}
virtual ~HermesExecutorRuntimeAdapter() {
// This is required by iOS, because there is an assertion in the destructor
// that the thread was indeed `quit` before
thread_->quitSynchronous();
}
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
facebook::hermes::HermesRuntime &getRuntime() override {
return hermesRuntime_;
}
@@ -48,7 +44,6 @@ class HermesExecutorRuntimeAdapter
// required us to hold a refernce to the runtime inside this adapter which
// caused issues while reloading the app.
void tickleJs() override {}
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
public:
facebook::hermes::HermesRuntime &hermesRuntime_;
@@ -61,11 +56,15 @@ WorkletHermesRuntime::WorkletHermesRuntime(
std::unique_ptr<facebook::hermes::HermesRuntime> runtime,
const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::string &name)
: jsi::WithRuntimeDecorator<WorkletsReentrancyCheck>(*runtime, reentrancyCheck_), runtime_(std::move(runtime)) {
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
auto adapter = std::make_unique<HermesExecutorRuntimeAdapter>(*runtime_, jsQueue);
: jsi::WithRuntimeDecorator<WorkletsReentrancyCheck>(
*runtime,
reentrancyCheck_),
runtime_(std::move(runtime)) {
#if HERMES_ENABLE_DEBUGGER
auto adapter =
std::make_unique<HermesExecutorRuntimeAdapter>(*runtime_, jsQueue);
debugToken_ = chrome::enableDebugging(std::move(adapter), name);
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#endif // HERMES_ENABLE_DEBUGGER
#ifndef NDEBUG
facebook::hermes::HermesRuntime *wrappedRuntime = runtime_.get();
@@ -74,27 +73,34 @@ WorkletHermesRuntime::WorkletHermesRuntime(
jsi::PropNameID::forAscii(*runtime_, "evalWithSourceMap"),
3,
[wrappedRuntime](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
auto code = std::make_shared<const jsi::StringBuffer>(args[0].asString(rt).utf8(rt));
jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) -> jsi::Value {
auto code = std::make_shared<const jsi::StringBuffer>(
args[0].asString(rt).utf8(rt));
std::string sourceURL;
if (count > 1 && args[1].isString()) {
sourceURL = args[1].asString(rt).utf8(rt);
}
std::shared_ptr<const jsi::Buffer> sourceMap;
if (count > 2 && args[2].isString()) {
sourceMap = std::make_shared<const jsi::StringBuffer>(args[2].asString(rt).utf8(rt));
sourceMap = std::make_shared<const jsi::StringBuffer>(
args[2].asString(rt).utf8(rt));
}
return wrappedRuntime->evaluateJavaScriptWithSourceMap(code, sourceMap, sourceURL);
return wrappedRuntime->evaluateJavaScriptWithSourceMap(
code, sourceMap, sourceURL);
});
runtime_->global().setProperty(*runtime_, "evalWithSourceMap", evalWithSourceMap);
runtime_->global().setProperty(
*runtime_, "evalWithSourceMap", evalWithSourceMap);
#endif // NDEBUG
}
WorkletHermesRuntime::~WorkletHermesRuntime() {
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#if HERMES_ENABLE_DEBUGGER
// We have to disable debugging before the runtime is destroyed.
chrome::disableDebugging(debugToken_);
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#endif // HERMES_ENABLE_DEBUGGER
}
} // namespace worklets
@@ -17,17 +17,17 @@
#include <string>
#include <thread>
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#if HERMES_ENABLE_DEBUGGER
#include <hermes/inspector-modern/chrome/Registration.h>
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#endif // HERMES_ENABLE_DEBUGGER
namespace worklets {
using namespace facebook;
using namespace react;
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#if HERMES_ENABLE_DEBUGGER
using namespace facebook::hermes::inspector_modern;
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#endif // HERMES_ENABLE_DEBUGGER
// ReentrancyCheck is copied from React Native
// from ReactCommon/hermes/executor/HermesExecutorFactory.cpp
@@ -57,17 +57,20 @@ struct WorkletsReentrancyCheck {
// of this sort would be surprising, because the decorator would
// need to call after() without before().
if (tid.compare_exchange_strong(expected, this_id, std::memory_order_relaxed)) {
if (tid.compare_exchange_strong(
expected, this_id, std::memory_order_relaxed)) {
// Returns true if tid and expected were the same. If they
// were, then the stored tid referred to no thread, and we
// atomically saved this thread's tid. Now increment depth.
react_native_assert(depth == 0 && "[Worklets] No thread id, but depth != 0");
react_native_assert(
depth == 0 && "[Worklets] No thread id, but depth != 0");
++depth;
} else if (expected == this_id) {
// If the stored tid referred to a thread, expected was set to
// that value. If that value is this thread's tid, that's ok,
// just increment depth again.
react_native_assert(depth != 0 && "[Worklets] Thread id was set, but depth == 0");
react_native_assert(
depth != 0 && "[Worklets] Thread id was set, but depth == 0");
++depth;
} else {
// The stored tid was some other thread. This indicates a bad
@@ -80,12 +83,15 @@ struct WorkletsReentrancyCheck {
void after() {
react_native_assert(
tid.load(std::memory_order_relaxed) == std::this_thread::get_id() && "[Worklets] No thread id in after()");
tid.load(std::memory_order_relaxed) == std::this_thread::get_id() &&
"[Worklets] No thread id in after()");
if (--depth == 0) {
// If we decremented depth to zero, store no-thread into tid.
std::thread::id expected = std::this_thread::get_id();
bool didWrite = tid.compare_exchange_strong(expected, std::thread::id(), std::memory_order_relaxed);
react_native_assert(didWrite && "[Worklets] Decremented to zero, but no tid write");
bool didWrite = tid.compare_exchange_strong(
expected, std::thread::id(), std::memory_order_relaxed);
react_native_assert(
didWrite && "[Worklets] Decremented to zero, but no tid write");
}
}
@@ -103,20 +109,21 @@ struct WorkletsReentrancyCheck {
// WithRuntimeDecorator -> DecoratedRuntime -> jsi::Runtime You can find out
// more about this in ReactCommon/jsi/jsi/Decorator.h or by following this link:
// https://github.com/facebook/react-native/blob/main/packages/react-native/ReactCommon/jsi/jsi/decorator.h
class WorkletHermesRuntime : public jsi::WithRuntimeDecorator<WorkletsReentrancyCheck> {
class WorkletHermesRuntime
: public jsi::WithRuntimeDecorator<WorkletsReentrancyCheck> {
public:
WorkletHermesRuntime(
std::unique_ptr<facebook::hermes::HermesRuntime> runtime,
const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::string &name);
~WorkletHermesRuntime() override;
~WorkletHermesRuntime();
private:
std::unique_ptr<facebook::hermes::HermesRuntime> runtime_;
WorkletsReentrancyCheck reentrancyCheck_;
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#if HERMES_ENABLE_DEBUGGER
chrome::DebugSessionToken debugToken_;
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
#endif // HERMES_ENABLE_DEBUGGER
};
} // namespace worklets
@@ -4,7 +4,6 @@
#include <worklets/Tools/JSISerializer.h>
#include <worklets/Tools/JSLogger.h>
#include <worklets/Tools/WorkletsJSIUtils.h>
#include <worklets/WorkletRuntime/RuntimeHolder.h>
#include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <worklets/WorkletRuntime/WorkletRuntimeCollector.h>
#include <worklets/WorkletRuntime/WorkletRuntimeDecorator.h>
@@ -14,9 +13,7 @@
#include <jsi/jsi.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#if JS_RUNTIME_HERMES
#include <worklets/WorkletRuntime/WorkletHermesRuntime.h>
@@ -30,7 +27,8 @@ class AroundLock {
const std::shared_ptr<std::recursive_mutex> mutex_;
public:
explicit AroundLock(const std::shared_ptr<std::recursive_mutex> &mutex) : mutex_(mutex) {}
explicit AroundLock(const std::shared_ptr<std::recursive_mutex> &mutex)
: mutex_(mutex) {}
void before() const {
mutex_->lock();
@@ -61,7 +59,8 @@ static std::shared_ptr<jsi::Runtime> makeRuntime(
std::shared_ptr<jsi::Runtime> jsiRuntime;
#if JS_RUNTIME_HERMES
auto hermesRuntime = facebook::hermes::makeHermesRuntime();
jsiRuntime = std::make_shared<WorkletHermesRuntime>(std::move(hermesRuntime), jsQueue, name);
jsiRuntime = std::make_shared<WorkletHermesRuntime>(
std::move(hermesRuntime), jsQueue, name);
#else
jsiRuntime = facebook::jsc::makeJSCRuntime();
#endif
@@ -82,37 +81,37 @@ WorkletRuntime::WorkletRuntime(
queue_(queue) {
jsi::Runtime &rt = *runtime_;
WorkletRuntimeCollector::install(rt);
if (enableEventLoop) {
if (enableEventLoop && name != uiRuntimeName) {
eventLoop_ = std::make_shared<EventLoop>(name_, runtime_, queue_);
eventLoop_->run();
}
}
void WorkletRuntime::init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy) {
void WorkletRuntime::init(
std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy) {
jsi::Runtime &rt = *runtime_;
#if REACT_NATIVE_MINOR_VERSION >= 81
rt.setRuntimeData(
RuntimeData::weakRuntimeUUID,
std::make_shared<WeakRuntimeHolder>(WeakRuntimeHolder{.weakRuntime = weak_from_this()}));
#endif // REACT_NATIVE_MINOR_VERSION >= 81
const auto jsScheduler = jsiWorkletsModuleProxy->getJSScheduler();
const auto isDevBundle = jsiWorkletsModuleProxy->isDevBundle();
const auto memoryManager_ = jsiWorkletsModuleProxy->getMemoryManager();
#ifdef WORKLETS_BUNDLE_MODE
auto script = jsiWorkletsModuleProxy->getScript();
const auto &sourceUrl = jsiWorkletsModuleProxy->getSourceUrl();
#endif // WORKLETS_BUNDLE_MODE
auto optimizedJsiWorkletsModuleProxy = jsi_utils::optimizedFromHostObject(rt, std::move(jsiWorkletsModuleProxy));
auto optimizedJsiWorkletsModuleProxy =
jsi_utils::optimizedFromHostObject(rt, std::move(jsiWorkletsModuleProxy));
WorkletRuntimeDecorator::decorate(
rt, name_, jsScheduler, isDevBundle, std::move(optimizedJsiWorkletsModuleProxy), eventLoop_);
rt,
name_,
jsScheduler,
isDevBundle,
std::move(optimizedJsiWorkletsModuleProxy),
eventLoop_);
#ifdef WORKLETS_BUNDLE_MODE
if (!script) {
throw std::runtime_error("[Worklets] Expected to receive the bundle, but got nullptr instead.");
throw std::runtime_error(
"[Worklets] Expected to receive the bundle, but got nullptr instead.");
}
try {
@@ -121,99 +120,87 @@ void WorkletRuntime::init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsMod
const auto &message = error.getMessage();
const auto &stack = error.getStack();
if (!message.starts_with("[Worklets] Worklets initialized successfully")) {
const auto newMessage = "[Worklets] Failed to initialize runtime. Reason: " + message;
const auto newMessage =
"[Worklets] Failed to initialize runtime. Reason: " + message;
JSLogger::reportFatalErrorOnJS(
jsScheduler, {.message = newMessage, .stack = stack, .name = "WorkletsError", .jsEngine = "Worklets"});
jsScheduler,
{.message = newMessage,
.stack = stack,
.name = "WorkletsError",
.jsEngine = "Worklets"});
}
}
#else
// Legacy behavior
auto valueUnpackerBuffer = std::make_shared<const jsi::StringBuffer>(ValueUnpackerCode);
auto valueUnpackerBuffer =
std::make_shared<const jsi::StringBuffer>(ValueUnpackerCode);
rt.evaluateJavaScript(valueUnpackerBuffer, "valueUnpacker");
auto synchronizableUnpackerBuffer = std::make_shared<const jsi::StringBuffer>(SynchronizableUnpackerCode);
auto synchronizableUnpackerBuffer =
std::make_shared<const jsi::StringBuffer>(SynchronizableUnpackerCode);
rt.evaluateJavaScript(synchronizableUnpackerBuffer, "synchronizableUnpacker");
auto customSerializableUnpackerBuffer = std::make_shared<const jsi::StringBuffer>(CustomSerializableUnpackerCode);
rt.evaluateJavaScript(customSerializableUnpackerBuffer, "customSerializableUnpacker");
#endif // WORKLETS_BUNDLE_MODE
try {
memoryManager_->loadAllCustomSerializables(shared_from_this());
} catch (jsi::JSError &e) {
throw std::runtime_error(std::string("[Worklets] Failed to load custom serializables. Reason: ") + e.getMessage());
}
}
/* #region schedule */
void WorkletRuntime::schedule(jsi::Function &&function) const {
void WorkletRuntime::runAsyncGuarded(
const std::shared_ptr<SerializableWorklet> &worklet) {
react_native_assert(
queue_ &&
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the "
"async queue is not set. Recreate the runtime with a valid async queue.");
queue_->push([function = std::make_shared<jsi::Function>(std::move(function)), weakThis = weak_from_this()]() {
"[Worklets] Tried to invoke `runAsyncGuarded` on a Worklet Runtime but "
"the async queue is not set. Recreate the runtime with a valid async queue.");
queue_->push([worklet, weakThis = weak_from_this()] {
auto strongThis = weakThis.lock();
if (!strongThis) {
return;
}
strongThis->runSync(*function);
strongThis->runGuarded(worklet);
});
}
void WorkletRuntime::schedule(std::shared_ptr<SerializableWorklet> worklet) const {
react_native_assert(
queue_ &&
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the "
"async queue is not set. Recreate the runtime with a valid async queue.");
queue_->push([worklet = std::move(worklet), weakThis = weak_from_this()] {
auto strongThis = weakThis.lock();
if (!strongThis) {
return;
}
strongThis->runSync(worklet);
});
jsi::Value WorkletRuntime::executeSync(
jsi::Runtime &rt,
const jsi::Value &worklet) const {
auto serializableWorklet = extractSerializableOrThrow<SerializableWorklet>(
rt,
worklet,
"[Worklets] Only worklets can be executed synchronously on UI runtime.");
auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
jsi::Runtime &uiRuntime = getJSIRuntime();
auto result = runGuarded(serializableWorklet);
auto serializableResult = extractSerializableOrThrow(uiRuntime, result);
lock.unlock();
return serializableResult->toJSValue(rt);
}
void WorkletRuntime::schedule(std::function<void()> job) const {
react_native_assert(
queue_ &&
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the "
"async queue is not set. Recreate the runtime with a valid async queue.");
queue_->push(std::move(job));
#ifdef WORKLETS_BUNDLE_MODE
jsi::Value WorkletRuntime::executeSync(
std::function<jsi::Value(jsi::Runtime &)> &&job) const {
auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
jsi::Runtime &uiRuntime = getJSIRuntime();
return job(uiRuntime);
}
void WorkletRuntime::schedule(std::function<void(jsi::Runtime &)> job) const {
react_native_assert(
queue_ &&
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the "
"async queue is not set. Recreate the runtime with a valid async queue.");
queue_->push([job = std::move(job), weakThis = weak_from_this()]() {
auto strongThis = weakThis.lock();
if (!strongThis) {
return;
}
auto lock = std::unique_lock<std::recursive_mutex>(*strongThis->runtimeMutex_);
jsi::Runtime &runtime = strongThis->getJSIRuntime();
job(runtime);
});
jsi::Value WorkletRuntime::executeSync(
const std::function<jsi::Value(jsi::Runtime &)> &job) const {
auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
jsi::Runtime &uiRuntime = getJSIRuntime();
return job(uiRuntime);
}
#endif // WORKLETS_BUNDLE_MODE
/* #endregion */
jsi::Value WorkletRuntime::get(jsi::Runtime &rt, const jsi::PropNameID &propName) {
jsi::Value WorkletRuntime::get(
jsi::Runtime &rt,
const jsi::PropNameID &propName) {
auto name = propName.utf8(rt);
if (name == "toString") {
return jsi::Function::createFromHostFunction(
rt,
propName,
0,
[weakThis = weak_from_this()](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *, size_t) -> jsi::Value {
[weakThis = weak_from_this()](
jsi::Runtime &rt, const jsi::Value &, const jsi::Value *, size_t)
-> jsi::Value {
auto strongThis = weakThis.lock();
if (!strongThis) {
return jsi::String::createFromUtf8(rt, "");
@@ -228,14 +215,17 @@ jsi::Value WorkletRuntime::get(jsi::Runtime &rt, const jsi::PropNameID &propName
return jsi::Value::undefined();
}
std::vector<jsi::PropNameID> WorkletRuntime::getPropertyNames(jsi::Runtime &rt) {
std::vector<jsi::PropNameID> WorkletRuntime::getPropertyNames(
jsi::Runtime &rt) {
std::vector<jsi::PropNameID> result;
result.push_back(jsi::PropNameID::forUtf8(rt, "toString"));
result.push_back(jsi::PropNameID::forUtf8(rt, "name"));
return result;
}
std::shared_ptr<WorkletRuntime> extractWorkletRuntime(jsi::Runtime &rt, const jsi::Value &value) {
std::shared_ptr<WorkletRuntime> extractWorkletRuntime(
jsi::Runtime &rt,
const jsi::Value &value) {
return value.getObject(rt).getHostObject<WorkletRuntime>(rt);
}
@@ -248,68 +238,7 @@ void scheduleOnRuntime(
rt,
serializableWorkletValue,
"[Worklets] Function passed to `_scheduleOnRuntime` is not a serializable worklet.");
workletRuntime->schedule(serializableWorklet);
workletRuntime->runAsyncGuarded(serializableWorklet);
}
#if REACT_NATIVE_MINOR_VERSION >= 81
std::weak_ptr<WorkletRuntime> WorkletRuntime::getWeakRuntimeFromJSIRuntime(jsi::Runtime &rt) {
auto runtimeData = rt.getRuntimeData(RuntimeData::weakRuntimeUUID);
if (!runtimeData) [[unlikely]] {
throw std::runtime_error(
"[Worklets] No weak runtime data found on the provided JSI runtime."
" Perhaps the JSI Runtime is not a WorkletRuntime?");
}
auto weakHolder = std::static_pointer_cast<WeakRuntimeHolder>(runtimeData);
return weakHolder->weakRuntime;
}
#endif // REACT_NATIVE_MINOR_VERSION >= 81
/* #region deprecated */
void WorkletRuntime::runAsyncGuarded(const std::shared_ptr<SerializableWorklet> &worklet) {
schedule(worklet);
}
jsi::Value WorkletRuntime::executeSync(jsi::Runtime &caller, const jsi::Value &worklet) const {
auto serializableWorklet = extractSerializableOrThrow<SerializableWorklet>(
caller, worklet, "[Worklets] Only worklets can be executed synchronously on UI runtime.");
auto result = runSyncSerialized(serializableWorklet);
return result->toJSValue(caller);
}
jsi::Value WorkletRuntime::executeSync(std::function<jsi::Value(jsi::Runtime &)> &&job) const {
return runSync(job);
}
jsi::Value WorkletRuntime::executeSync(const std::function<jsi::Value(jsi::Runtime &)> &job) const {
return runSync(job);
}
#ifndef NDEBUG
static const auto callGuardLambda = [](facebook::jsi::Runtime &rt,
const facebook::jsi::Value &thisVal,
const facebook::jsi::Value *args,
size_t count) {
return args[0].asObject(rt).asFunction(rt).call(rt, args + 1, count - 1);
};
jsi::Function WorkletRuntime::getCallGuard(jsi::Runtime &rt) {
auto callGuard = rt.global().getProperty(rt, "__callGuardDEV");
if (callGuard.isObject()) {
// Use JS implementation if `__callGuardDEV` has already been installed.
// This is the desired behavior.
return callGuard.asObject(rt).asFunction(rt);
}
// Otherwise, fallback to C++ JSI implementation. This is necessary so that we
// can install `__callGuardDEV` itself and should happen only once. Note that
// the C++ implementation doesn't intercept errors and simply throws them as
// C++ exceptions which crashes the app. We assume that installing the guard
// doesn't throw any errors.
return jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "callGuard"), 1, callGuardLambda);
}
#endif // NDEBUG
/* #endregion */
} // namespace worklets
@@ -5,7 +5,7 @@
#include <jsireact/JSIExecutor.h>
#include <react/debug/react_native_assert.h>
#include <worklets/RunLoop/AsyncQueue.h>
#include <worklets/Public/AsyncQueue.h>
#include <worklets/RunLoop/AsyncQueueImpl.h>
#include <worklets/RunLoop/EventLoop.h>
#include <worklets/SharedItems/Serializable.h>
@@ -14,7 +14,6 @@
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
@@ -23,111 +22,14 @@ using namespace react;
namespace worklets {
template <typename TCallable>
concept ImplicitlySerializableCallable = std::is_assignable_v<const jsi::Function &, TCallable> ||
std::is_assignable_v<const std::shared_ptr<SerializableWorklet> &, TCallable>;
template <typename TCallable>
concept RuntimeCallable = requires(TCallable &&callable, jsi::Runtime &rt) {
// NOLINTNEXTLINE(readability/braces) cpplint doesn't understand concepts
{ callable(rt) };
} || ImplicitlySerializableCallable<TCallable>;
/**
* Forward declaration to avoid circular dependencies.
*/
class JSIWorkletsModuleProxy;
class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_this<WorkletRuntime> {
class WorkletRuntime : public jsi::HostObject,
public std::enable_shared_from_this<WorkletRuntime> {
public:
void schedule(jsi::Function &&function) const;
void schedule(std::shared_ptr<SerializableWorklet> worklet) const;
void schedule(std::function<void()> job) const;
void schedule(std::function<void(jsi::Runtime &)> job) const;
/* #region runSync */
template <RuntimeCallable TCallable, typename... Args>
std::invoke_result_t<TCallable, Args...> runSync(TCallable &&callable, Args &&...args) const;
template <typename... Args>
jsi::Value runSync(const jsi::Function &function, Args &&...args) const {
auto &rt = *runtime_;
// We only use callGuard in debug mode, otherwise we call the provided
// function directly. CallGuard provides a way of capturing exceptions in
// JavaScript and propagating them to the main React Native thread such that
// they can be presented using RN's LogBox.
#ifndef NDEBUG
return getCallGuard(rt).call(rt, function, args...);
#else
return function.call(rt, args...);
#endif // NDEBUG
}
template <typename... Args>
jsi::Value runSync(const std::shared_ptr<SerializableWorklet> &worklet, Args &&...args) const {
jsi::Runtime &rt = *runtime_;
return runSync(worklet->toJSValue(rt).asObject(rt).asFunction(rt), std::forward<Args>(args)...);
}
template <RuntimeCallable TCallable>
std::invoke_result_t<TCallable, jsi::Runtime &> runSync(TCallable &&job) const {
jsi::Runtime &rt = getJSIRuntime();
auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
return job(rt);
}
/* #endregion */
/* #region runSyncSerialized */
template <ImplicitlySerializableCallable TCallable, typename... Args>
std::shared_ptr<Serializable> runSyncSerialized(TCallable &&callable, Args &&...args) const;
template <typename... Args>
std::shared_ptr<Serializable> runSyncSerialized(const jsi::Function &function, Args &&...args) const {
jsi::Runtime &rt = getJSIRuntime();
auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
auto result = runSync(function, std::forward<Args>(args)...);
auto serializableResult = extractSerializableOrThrow(
rt,
result,
"[Worklets] Function passed to `runSyncSerialized`"
"must return a value serialized with `createSerializable`.");
return serializableResult;
}
template <typename... Args>
std::shared_ptr<Serializable> runSyncSerialized(const std::shared_ptr<SerializableWorklet> &worklet, Args &&...args)
const {
jsi::Runtime &rt = getJSIRuntime();
auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
auto result = runSync(worklet, std::forward<Args>(args)...);
auto serializableResult = extractSerializableOrThrow(
rt,
result,
"[Worklets] Worklet passed to `runSyncSerialized`"
"must return a value serialized with `createSerializable`.");
return serializableResult;
}
/* #endregion */
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override;
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
[[nodiscard]] std::string toString() const noexcept {
return "[WorkletRuntime \"" + name_ + "\"]";
}
[[nodiscard]] jsi::Runtime &getJSIRuntime() const noexcept {
return *runtime_;
}
[[nodiscard]] uint64_t getRuntimeId() const noexcept {
return runtimeId_;
}
[[nodiscard]] std::string getRuntimeName() const noexcept {
return name_;
}
explicit WorkletRuntime(
uint64_t runtimeId,
const std::shared_ptr<MessageQueueThread> &jsQueue,
@@ -137,40 +39,45 @@ class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_th
void init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy);
/* #region deprecated */
jsi::Runtime &getJSIRuntime() const {
return *runtime_;
}
/** @deprecated Use `runSync` instead. */
template <typename... Args>
jsi::Value runGuarded(const std::shared_ptr<SerializableWorklet> &worklet, Args &&...args) const {
return runSync(worklet, std::forward<Args>(args)...);
inline jsi::Value runGuarded(
const std::shared_ptr<SerializableWorklet> &serializableWorklet,
Args &&...args) const {
jsi::Runtime &rt = *runtime_;
return runOnRuntimeGuarded(
rt, serializableWorklet->toJSValue(rt), std::forward<Args>(args)...);
}
/** @deprecated Use `schedule` instead. */
void runAsyncGuarded(const std::shared_ptr<SerializableWorklet> &worklet);
/** @deprecated Use `runSyncSerialized` and extract to `jsi::Value` with
* `extractSerializableOrThrow` instead. */
jsi::Value executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const;
/** @deprecated Use `runSync` instead. */
#ifdef WORKLETS_BUNDLE_MODE
jsi::Value executeSync(std::function<jsi::Value(jsi::Runtime &)> &&job) const;
/** @deprecated Use `runSync` instead. */
jsi::Value executeSync(const std::function<jsi::Value(jsi::Runtime &)> &job) const;
/* #endregion */
jsi::Value executeSync(
const std::function<jsi::Value(jsi::Runtime &)> &job) const;
#endif // WORKLETS_BUNDLE_MODE
#if REACT_NATIVE_MINOR_VERSION >= 81
/**
* Retrieves a weak reference to the WorkletRuntime associated with the
* provided jsi::Runtime.
*
* Throws when invoked with a non-worklet runtime.
*/
static std::weak_ptr<WorkletRuntime> getWeakRuntimeFromJSIRuntime(jsi::Runtime &rt);
#endif // REACT_NATIVE_MINOR_VERSION >= 81
std::string toString() const {
return "[WorkletRuntime \"" + name_ + "\"]";
}
#ifndef NDEBUG
static jsi::Function getCallGuard(jsi::Runtime &rt);
#endif // NDEBUG
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override;
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
[[nodiscard]] auto getRuntimeId() const -> uint64_t {
return runtimeId_;
}
[[nodiscard]] auto getRuntimeName() const -> std::string {
return name_;
}
private:
const uint64_t runtimeId_;
@@ -183,35 +90,13 @@ class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_th
// This function needs to be non-inline to avoid problems with dynamic_cast on
// Android
std::shared_ptr<WorkletRuntime> extractWorkletRuntime(jsi::Runtime &rt, const jsi::Value &value);
std::shared_ptr<WorkletRuntime> extractWorkletRuntime(
jsi::Runtime &rt,
const jsi::Value &value);
void scheduleOnRuntime(
jsi::Runtime &rt,
const jsi::Value &workletRuntimeValue,
const jsi::Value &serializableWorkletValue);
/**
* @deprecated Use `WorkletRuntime::runSync` instead.
*/
template <typename... Args>
inline jsi::Value runOnRuntimeGuarded(jsi::Runtime &rt, const jsi::Function &function, Args &&...args) {
// We only use callGuard in debug mode, otherwise we call the provided
// function directly. CallGuard provides a way of capturing exceptions in
// JavaScript and propagating them to the main React Native thread such that
// they can be presented using RN's LogBox.
#ifndef NDEBUG
return WorkletRuntime::getCallGuard(rt).call(rt, function, args...);
#else
return function.call(rt, args...);
#endif // NDEBUG
}
/**
* @deprecated Use `WorkletRuntime::runSync` instead.
*/
template <typename... Args>
inline jsi::Value runOnRuntimeGuarded(jsi::Runtime &rt, const jsi::Value &function, Args &&...args) {
return runOnRuntimeGuarded(rt, function.asObject(rt).asFunction(rt), std::forward<Args>(args)...);
}
} // namespace worklets
@@ -19,7 +19,7 @@ class WorkletRuntimeCollector : public jsi::HostObject {
WorkletRuntimeRegistry::registerRuntime(runtime_);
}
~WorkletRuntimeCollector() override {
~WorkletRuntimeCollector() {
WorkletRuntimeRegistry::unregisterRuntime(runtime_);
}
@@ -6,8 +6,6 @@
#include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <worklets/WorkletRuntime/WorkletRuntimeDecorator.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
@@ -16,7 +14,9 @@ namespace worklets {
static inline double performanceNow() {
// copied from JSExecutor.cpp
auto time = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(time.time_since_epoch()).count();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
time.time_since_epoch())
.count();
constexpr double NANOSECONDS_IN_MILLISECOND = 1000000.0;
return duration / NANOSECONDS_IN_MILLISECOND;
@@ -24,7 +24,7 @@ static inline double performanceNow() {
static inline std::vector<jsi::Value> parseArgs(
jsi::Runtime &rt,
const std::shared_ptr<SerializableArray> &serializableArgs) {
std::shared_ptr<SerializableArray> serializableArgs) {
if (serializableArgs == nullptr) {
return {};
}
@@ -48,7 +48,8 @@ void WorkletRuntimeDecorator::decorate(
// resolves "ReferenceError: Property 'global' doesn't exist at ..."
rt.global().setProperty(rt, "global", rt.global());
rt.global().setProperty(rt, runtimeKindBindingName, static_cast<int>(RuntimeKind::Worker));
rt.global().setProperty(
rt, runtimeKindBindingName, static_cast<int>(RuntimeKind::Worker));
rt.global().setProperty(rt, "_WORKLET", true);
@@ -60,12 +61,16 @@ void WorkletRuntimeDecorator::decorate(
rt.global().setProperty(rt, "__DEV__", isDevBundle);
rt.global().setProperty(rt, "__workletsModuleProxy", std::move(jsiWorkletsModuleProxy));
rt.global().setProperty(
rt, "__workletsModuleProxy", std::move(jsiWorkletsModuleProxy));
#ifndef NDEBUG
auto evalWithSourceUrl =
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
auto code = std::make_shared<const jsi::StringBuffer>(args[0].asString(rt).utf8(rt));
auto evalWithSourceUrl = [](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) -> jsi::Value {
auto code = std::make_shared<const jsi::StringBuffer>(
args[0].asString(rt).utf8(rt));
std::string url;
if (count > 1 && args[1].isString()) {
url = args[1].asString(rt).utf8(rt);
@@ -76,51 +81,85 @@ void WorkletRuntimeDecorator::decorate(
rt,
"evalWithSourceUrl",
jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "evalWithSourceUrl"), 1, evalWithSourceUrl));
rt,
jsi::PropNameID::forAscii(rt, "evalWithSourceUrl"),
1,
evalWithSourceUrl));
#endif // NDEBUG
jsi_utils::installJsiFunction(
rt, "_log", [](jsi::Runtime &rt, const jsi::Value &value) { PlatformLogger::log(stringifyJSIValue(rt, value)); });
jsi_utils::installJsiFunction(rt, "_toString", [](jsi::Runtime &rt, const jsi::Value &value) {
return jsi::String::createFromUtf8(rt, stringifyJSIValue(rt, value));
});
jsi_utils::installJsiFunction(
rt, "_createSerializable", [](jsi::Runtime &rt, const jsi::Value &value, const jsi::Value &nativeStateSource) {
auto shouldRetainRemote = jsi::Value::undefined();
return makeSerializableClone(rt, value, shouldRetainRemote, nativeStateSource);
rt, "_log", [](jsi::Runtime &rt, const jsi::Value &value) {
PlatformLogger::log(stringifyJSIValue(rt, value));
});
jsi_utils::installJsiFunction(rt, "_createSerializableHostObject", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableHostObject(rt, value.asObject(rt).asHostObject(rt));
});
jsi_utils::installJsiFunction(rt, "_createSerializableString", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableString(rt, value.asString(rt));
});
jsi_utils::installJsiFunction(rt, "_createSerializableNumber", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableNumber(rt, value.asNumber());
});
jsi_utils::installJsiFunction(rt, "_createSerializableBoolean", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableBoolean(rt, value.asBool());
});
jsi_utils::installJsiFunction(rt, "_createSerializableBigInt", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableBigInt(rt, value.asBigInt(rt));
});
jsi_utils::installJsiFunction(
rt, "_toString", [](jsi::Runtime &rt, const jsi::Value &value) {
return jsi::String::createFromUtf8(rt, stringifyJSIValue(rt, value));
});
jsi_utils::installJsiFunction(
rt, "_createSerializableUndefined", [](jsi::Runtime &rt) { return makeSerializableUndefined(rt); });
jsi_utils::installJsiFunction(rt, "_createSerializableArray", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableArray(rt, value.asObject(rt).asArray(rt), false);
});
rt,
"_createSerializable",
[](jsi::Runtime &rt,
const jsi::Value &value,
const jsi::Value &nativeStateSource) {
auto shouldRetainRemote = jsi::Value::undefined();
return makeSerializableClone(
rt, value, shouldRetainRemote, nativeStateSource);
});
jsi_utils::installJsiFunction(
rt, "_createSerializableNull", [](jsi::Runtime &rt) { return makeSerializableNull(rt); });
rt,
"_createSerializableHostObject",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableHostObject(
rt, value.asObject(rt).asHostObject(rt));
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableString",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableString(rt, value.asString(rt));
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableNumber",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableNumber(rt, value.asNumber());
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableBoolean",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableBoolean(rt, value.asBool());
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableBigInt",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableBigInt(rt, value.asBigInt(rt));
});
jsi_utils::installJsiFunction(
rt, "_createSerializableUndefined", [](jsi::Runtime &rt) {
return makeSerializableUndefined(rt);
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableArray",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableArray(rt, value.asObject(rt).asArray(rt), false);
});
jsi_utils::installJsiFunction(
rt, "_createSerializableNull", [](jsi::Runtime &rt) {
return makeSerializableNull(rt);
});
jsi_utils::installJsiFunction(
rt,
@@ -129,46 +168,70 @@ void WorkletRuntimeDecorator::decorate(
const jsi::Value &value,
const jsi::Value &shouldRetainRemote,
const jsi::Value &nativeStateSource) {
return makeSerializableObject(rt, value.getObject(rt), shouldRetainRemote.getBool(), nativeStateSource);
return makeSerializableObject(
rt,
value.getObject(rt),
shouldRetainRemote.getBool(),
nativeStateSource);
});
jsi_utils::installJsiFunction(rt, "_createSerializableWorklet", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableWorklet(rt, value.asObject(rt), false);
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableWorklet",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableWorklet(rt, value.asObject(rt), false);
});
jsi_utils::installJsiFunction(rt, "_createSerializableInitializer", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableInitializer(rt, value.asObject(rt));
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableInitializer",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableInitializer(rt, value.asObject(rt));
});
jsi_utils::installJsiFunction(rt, "_createSerializableFunction", [](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableFunction(rt, value.asObject(rt).asFunction(rt));
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableFunction",
[](jsi::Runtime &rt, const jsi::Value &value) {
return makeSerializableFunction(rt, value.asObject(rt).asFunction(rt));
});
jsi_utils::installJsiFunction(rt, "_createSerializableSynchronizable", [](jsi::Runtime &rt, const jsi::Value &value) {
return SerializableJSRef::newNativeStateObject(rt, extractSerializableOrThrow(rt, value));
});
jsi_utils::installJsiFunction(
rt,
"_createSerializableSynchronizable",
[](jsi::Runtime &rt, const jsi::Value &value) {
return SerializableJSRef::newNativeStateObject(
rt, extractSerializableOrThrow(rt, value));
});
jsi_utils::installJsiFunction(
rt,
"_scheduleRemoteFunctionOnJS",
[jsScheduler](jsi::Runtime &rt, const jsi::Value &funValue, const jsi::Value &argsValue) {
auto serializableRemoteFun = extractSerializableOrThrow<SerializableRemoteFunction>(
[jsScheduler](
jsi::Runtime &rt,
const jsi::Value &funValue,
const jsi::Value &argsValue) {
auto serializableRemoteFun = extractSerializableOrThrow<
SerializableRemoteFunction>(
rt,
funValue,
"[Worklets] Incompatible object passed to scheduleOnJS. It is only allowed to schedule worklets or functions defined on the React Native JS runtime this way.");
auto serializableArgs = argsValue.isUndefined()
? nullptr
: extractSerializableOrThrow<SerializableArray>(rt, argsValue, "[Worklets] Args must be an array.");
: extractSerializableOrThrow<SerializableArray>(
rt, argsValue, "[Worklets] Args must be an array.");
jsScheduler->scheduleOnJS([=](jsi::Runtime &rt) {
auto fun = serializableRemoteFun->toJSValue(rt).asObject(rt).asFunction(rt);
auto fun =
serializableRemoteFun->toJSValue(rt).asObject(rt).asFunction(rt);
if (serializableArgs == nullptr) {
// fast path for remote function w/o arguments
fun.call(rt);
} else {
auto args = parseArgs(rt, serializableArgs);
fun.call(rt, const_cast<const jsi::Value *>(args.data()), args.size());
fun.call(
rt, const_cast<const jsi::Value *>(args.data()), args.size());
}
});
});
@@ -176,23 +239,34 @@ void WorkletRuntimeDecorator::decorate(
jsi_utils::installJsiFunction(
rt,
"_scheduleHostFunctionOnJS",
[jsScheduler](jsi::Runtime &rt, const jsi::Value &hostFunValue, const jsi::Value &argsValue) {
auto hostFun = hostFunValue.asObject(rt).asFunction(rt).getHostFunction(rt);
[jsScheduler](
jsi::Runtime &rt,
const jsi::Value &hostFunValue,
const jsi::Value &argsValue) {
auto hostFun =
hostFunValue.asObject(rt).asFunction(rt).getHostFunction(rt);
auto serializableArgs = argsValue.isUndefined()
? nullptr
: extractSerializableOrThrow<SerializableArray>(rt, argsValue, "[Worklets] Args must be an array.");
: extractSerializableOrThrow<SerializableArray>(
rt, argsValue, "[Worklets] Args must be an array.");
jsScheduler->scheduleOnJS([=](jsi::Runtime &rt) {
auto args = parseArgs(rt, serializableArgs);
hostFun(rt, jsi::Value::undefined(), const_cast<const jsi::Value *>(args.data()), args.size());
hostFun(
rt,
jsi::Value::undefined(),
const_cast<const jsi::Value *>(args.data()),
args.size());
});
});
jsi_utils::installJsiFunction(
rt,
"_scheduleOnRuntime",
[](jsi::Runtime &rt, const jsi::Value &workletRuntimeValue, const jsi::Value &serializableWorkletValue) {
[](jsi::Runtime &rt,
const jsi::Value &workletRuntimeValue,
const jsi::Value &serializableWorkletValue) {
scheduleOnRuntime(rt, workletRuntimeValue, serializableWorkletValue);
});
@@ -204,20 +278,25 @@ void WorkletRuntimeDecorator::decorate(
rt,
jsi::PropNameID::forAscii(rt, "now"),
0,
[](jsi::Runtime &runtime, const jsi::Value &, const jsi::Value *args, size_t count) {
return jsi::Value(performanceNow());
}));
[](jsi::Runtime &runtime,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return jsi::Value(performanceNow()); }));
rt.global().setProperty(rt, "performance", performance);
jsi_utils::installJsiFunction(
rt,
"_scheduleTimeoutCallback",
[weakEventLoop = std::weak_ptr<EventLoop>(eventLoop)](
jsi::Runtime &rt, const jsi::Value &delayJs, const jsi::Value &handlerIdJs) -> jsi::Value {
jsi::Runtime &rt,
const jsi::Value &delayJs,
const jsi::Value &handlerIdJs) -> jsi::Value {
const auto delay = delayJs.asNumber();
const auto handlerId = handlerIdJs.asNumber();
const auto job = [handlerId](jsi::Runtime &rt) {
rt.global().getPropertyAsFunction(rt, "__runTimeoutCallback").call(rt, handlerId);
rt.global()
.getPropertyAsFunction(rt, "__runTimeoutCallback")
.call(rt, handlerId);
};
if (auto strongEventLoop = weakEventLoop.lock()) {
strongEventLoop->pushTimeout(job, delay);
+2 -3
View File
@@ -11,8 +11,7 @@ worklets_assert_new_architecture_enabled($new_arch_enabled)
ios_min_version = '13.4'
feature_flags = "-DWORKLETS_FEATURE_FLAGS=\"#{worklets_get_static_feature_flags()}\""
version_flags = "-DWORKLETS_VERSION=#{package['version']} -DREACT_NATIVE_MINOR_VERSION=#{$worklets_config[:react_native_minor_version]}"
hermes_v1_flag = ENV['RCT_HERMES_V1_ENABLED'] == '1' ? '-DHERMES_V1_ENABLED' : ''
version_flags = "-DWORKLETS_VERSION=#{package['version']}"
Pod::Spec.new do |s|
s.name = "RNWorklets"
@@ -76,7 +75,7 @@ Pod::Spec.new do |s|
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
"GCC_PREPROCESSOR_DEFINITIONS[config=*Debug*]" => "$(inherited) #{hermes_debug_hidden_flags} #{bundle_mode_flag}",
"GCC_PREPROCESSOR_DEFINITIONS[config=*Release*]" => "$(inherited) #{bundle_mode_flag}",
"OTHER_CFLAGS" => "$(inherited) #{feature_flags} #{version_flags} #{hermes_v1_flag}",
"OTHER_CFLAGS" => "$(inherited) #{feature_flags} #{version_flags}",
}
s.xcconfig = {
"HEADER_SEARCH_PATHS" => [
+29 -13
View File
@@ -15,9 +15,8 @@ string(
APPEND
CMAKE_CXX_FLAGS
" -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}\
-DWORKLETS_VERSION=${WORKLETS_VERSION}\
-DWORKLETS_FEATURE_FLAGS=\"${WORKLETS_FEATURE_FLAGS}\"\
-DHERMES_V1_ENABLED=${HERMES_V1_ENABLED}")
-DWORKLETS_VERSION=${WORKLETS_VERSION}\
-DWORKLETS_FEATURE_FLAGS=\"${WORKLETS_FEATURE_FLAGS}\"")
string(APPEND CMAKE_CXX_FLAGS " -fno-omit-frame-pointer -fstack-protector-all")
@@ -85,24 +84,41 @@ target_include_directories(
"${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx"
)
if(ReactAndroid_VERSION_MINOR LESS 76)
target_link_libraries(
worklets ReactAndroid::fabricjni ReactAndroid::react_debug
ReactAndroid::react_render_core
ReactAndroid::react_render_componentregistry ReactAndroid::rrc_view)
endif()
# build shared lib
set_target_properties(worklets PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(worklets log ReactAndroid::reactnative ReactAndroid::jsi
fbjni::fbjni)
target_link_libraries(worklets log ReactAndroid::jsi fbjni::fbjni)
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
target_link_libraries(worklets ReactAndroid::reactnative)
else()
target_link_libraries(
worklets ReactAndroid::react_nativemodule_core ReactAndroid::folly_runtime
ReactAndroid::glog ReactAndroid::reactnativejni)
endif()
if(${JS_RUNTIME} STREQUAL "hermes")
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 82)
target_link_libraries(worklets hermes-engine::hermesvm)
else()
target_link_libraries(worklets hermes-engine::libhermes)
endif()
target_link_libraries(worklets hermes-engine::libhermes)
if(${HERMES_ENABLE_DEBUGGER})
string(APPEND CMAKE_CXX_FLAGS " -DHERMES_ENABLE_DEBUGGER=1")
target_link_libraries(worklets ReactAndroid::hermestooling)
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
target_link_libraries(worklets ReactAndroid::hermestooling)
else()
target_link_libraries(worklets ReactAndroid::hermes_executor)
endif()
endif()
elseif(${JS_RUNTIME} STREQUAL "jsc")
target_link_libraries(worklets ReactAndroid::jsctooling)
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
target_link_libraries(worklets ReactAndroid::jsctooling)
else()
target_link_libraries(worklets ReactAndroid::jscexecutor)
endif()
endif()
+11 -6
View File
@@ -102,7 +102,6 @@ def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled()
def IS_REANIMATED_EXAMPLE_APP = safeAppExtGet("isReanimatedExampleApp", false)
def BUNDLE_MODE = safeAppExtGet("workletsBundleMode", false)
def WORKLETS_FEATURE_FLAGS = getStaticFeatureFlags()
def HERMES_V1_ENABLED = safeAppExtGet("hermesV1Enabled", false)
// Set version for prefab
version WORKLETS_VERSION
@@ -150,8 +149,6 @@ apply plugin: "com.android.library"
apply plugin: "maven-publish"
apply plugin: "de.undercouch.download"
apply from: "./fix-prefab.gradle"
android {
compileSdkVersion safeExtGet("compileSdkVersion", 34)
@@ -199,8 +196,7 @@ android {
"-DWORKLETS_BUNDLE_MODE=${BUNDLE_MODE}",
"-DWORKLETS_VERSION=${WORKLETS_VERSION}",
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
"-DWORKLETS_FEATURE_FLAGS=${WORKLETS_FEATURE_FLAGS}",
"-DHERMES_V1_ENABLED=${HERMES_V1_ENABLED}"
"-DWORKLETS_FEATURE_FLAGS=${WORKLETS_FEATURE_FLAGS}"
abiFilters (*reactNativeArchitectures())
targets("worklets")
}
@@ -248,10 +244,19 @@ android {
"**/libc++_shared.so",
"**/libfbjni.so",
"**/libjsi.so",
"**/libfolly_json.so",
"**/libfolly_runtime.so",
"**/libglog.so",
"**/libhermes.so",
"**/libhermesvm.so",
"**/libhermes-executor-debug.so",
"**/libhermes_executor.so",
"**/libhermestooling.so",
"**/libreactnativejni.so",
"**/libturbomodulejsijni.so",
"**/libreactnative.so",
"**/libreact_nativemodule_core.so",
"**/libreact_render*.so",
"**/librrc_root.so",
"**/libjscexecutor.so",
]
}
@@ -1,53 +0,0 @@
tasks.configureEach { task ->
// Make sure that we generate our prefab publication file only after having built the native library
// so that not a header publication file, but a full configuration publication will be generated, which
// will include the .so file
def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/
def matcher = task.name =~ prefabConfigurePattern
if (matcher.matches()) {
def variantName = matcher[0][1]
task.outputs.upToDateWhen { false }
task.dependsOn("externalNativeBuild${variantName}")
}
}
afterEvaluate {
def abis = reactNativeArchitectures()
rootProject.allprojects.each { proj ->
if (proj === rootProject) return
def dependsOnThisLib = proj.configurations.any { config ->
config.dependencies.any { dep ->
dep.group == project.group && dep.name == project.name
}
}
if (!dependsOnThisLib && proj != project) return
if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) {
return
}
def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants
// Touch the prefab_config.json files to ensure that in ExternalNativeJsonGenerator.kt we will re-trigger the prefab CLI to
// generate a libnameConfig.cmake file that will contain our native library (.so).
// See this condition: https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/ExternalNativeJsonGenerator.kt;l=207-219?q=createPrefabBuildSystemGlue
variants.all { variant ->
def variantName = variant.name
abis.each { abi ->
def searchDir = new File(proj.projectDir, ".cxx/${variantName}")
if (!searchDir.exists()) return
def matches = []
searchDir.eachDir { randomDir ->
def prefabFile = new File(randomDir, "${abi}/prefab_config.json")
if (prefabFile.exists()) matches << prefabFile
}
matches.each { prefabConfig ->
prefabConfig.setLastModified(System.currentTimeMillis())
}
}
}
}
}
@@ -3,12 +3,13 @@ package com.swmansion.worklets;
import androidx.annotation.OptIn;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.BundleConsumer;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.queue.MessageQueueThread;
import com.facebook.react.common.annotations.FrameworkAPI;
import com.facebook.react.fabric.BundleWrapper;
import com.facebook.react.fabric.BigStringBufferWrapper;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl;
import com.facebook.soloader.SoLoader;
@@ -19,7 +20,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
@SuppressWarnings("JavaJniMissingFunction")
@ReactModule(name = WorkletsModule.NAME)
public class WorkletsModule extends NativeWorkletsModuleSpec implements LifecycleEventListener {
public class WorkletsModule extends NativeWorkletsModuleSpec
implements LifecycleEventListener, BundleConsumer {
static {
SoLoader.loadLibrary("worklets");
}
@@ -37,9 +39,19 @@ public class WorkletsModule extends NativeWorkletsModuleSpec implements Lifecycl
private final AndroidUIScheduler mAndroidUIScheduler;
private final AnimationFrameQueue mAnimationFrameQueue;
private boolean mSlowAnimationsEnabled;
private BundleWrapper mBundleWrapper = null;
private BigStringBufferWrapper mScriptWrapper = null;
private String mSourceURL = null;
@Override
public void setScriptWrapper(BigStringBufferWrapper scriptWrapper) {
mScriptWrapper = scriptWrapper;
}
@Override
public void setSourceURL(String sourceURL) {
mSourceURL = sourceURL;
}
/**
* Invalidating concurrently could be fatal. It shouldn't happen in a normal flow, but it doesn't
* cost us much to add synchronization for extra safety.
@@ -52,7 +64,7 @@ public class WorkletsModule extends NativeWorkletsModuleSpec implements Lifecycl
MessageQueueThread messageQueueThread,
CallInvokerHolderImpl jsCallInvokerHolder,
AndroidUIScheduler androidUIScheduler,
BundleWrapper bundleWrapper,
BigStringBufferWrapper scriptWrapper,
String sourceURL);
public WorkletsModule(ReactApplicationContext reactContext) {
@@ -78,16 +90,13 @@ public class WorkletsModule extends NativeWorkletsModuleSpec implements Lifecycl
var jsContext = Objects.requireNonNull(context.getJavaScriptContextHolder()).get();
var jsCallInvokerHolder = JSCallInvokerResolver.getJSCallInvokerHolder(context);
mSourceURL = context.getSourceURL();
mBundleWrapper = context.getBundle();
mHybridData =
initHybrid(
jsContext,
mMessageQueueThread,
jsCallInvokerHolder,
mAndroidUIScheduler,
mBundleWrapper,
mScriptWrapper,
mSourceURL);
return true;
}
@@ -10,7 +10,8 @@ class UISchedulerWrapper : public UIScheduler {
jni::global_ref<AndroidUIScheduler::javaobject> androidUiScheduler_;
public:
explicit UISchedulerWrapper(jni::global_ref<AndroidUIScheduler::javaobject> androidUiScheduler)
explicit UISchedulerWrapper(
jni::global_ref<AndroidUIScheduler::javaobject> androidUiScheduler)
: androidUiScheduler_(androidUiScheduler) {}
void scheduleOnUI(std::function<void()> job) override {
@@ -22,10 +23,14 @@ class UISchedulerWrapper : public UIScheduler {
}
};
AndroidUIScheduler::AndroidUIScheduler(jni::alias_ref<AndroidUIScheduler::javaobject> jThis)
: javaPart_(jni::make_global(jThis)), uiScheduler_(std::make_shared<UISchedulerWrapper>(jni::make_global(jThis))) {}
AndroidUIScheduler::AndroidUIScheduler(
jni::alias_ref<AndroidUIScheduler::javaobject> jThis)
: javaPart_(jni::make_global(jThis)),
uiScheduler_(
std::make_shared<UISchedulerWrapper>(jni::make_global(jThis))) {}
jni::local_ref<AndroidUIScheduler::jhybriddata> AndroidUIScheduler::initHybrid(jni::alias_ref<jhybridobject> jThis) {
jni::local_ref<AndroidUIScheduler::jhybriddata> AndroidUIScheduler::initHybrid(
jni::alias_ref<jhybridobject> jThis) {
return makeCxxInstance(jThis);
}
@@ -37,7 +42,8 @@ void AndroidUIScheduler::triggerUI() {
}
void AndroidUIScheduler::scheduleTriggerOnUI() {
static const auto method = javaPart_->getClass()->getMethod<void()>("scheduleTriggerOnUI");
static const auto method =
javaPart_->getClass()->getMethod<void()>("scheduleTriggerOnUI");
method(javaPart_.get());
}
@@ -14,8 +14,10 @@ using namespace worklets;
class AndroidUIScheduler : public jni::HybridClass<AndroidUIScheduler> {
public:
static auto constexpr kJavaDescriptor = "Lcom/swmansion/worklets/AndroidUIScheduler;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/worklets/AndroidUIScheduler;";
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jhybridobject> jThis);
static void registerNatives();
std::shared_ptr<UIScheduler> getUIScheduler() {
@@ -34,7 +36,8 @@ class AndroidUIScheduler : public jni::HybridClass<AndroidUIScheduler> {
jni::global_ref<AndroidUIScheduler::javaobject> javaPart_;
std::shared_ptr<UIScheduler> uiScheduler_;
explicit AndroidUIScheduler(jni::alias_ref<AndroidUIScheduler::jhybridobject> jThis);
explicit AndroidUIScheduler(
jni::alias_ref<AndroidUIScheduler::jhybridobject> jThis);
};
} // namespace worklets
@@ -6,9 +6,11 @@
namespace worklets {
class AnimationFrameCallback : public facebook::jni::HybridClass<AnimationFrameCallback> {
class AnimationFrameCallback
: public facebook::jni::HybridClass<AnimationFrameCallback> {
public:
static auto constexpr kJavaDescriptor = "Lcom/swmansion/worklets/runloop/AnimationFrameCallback;";
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/worklets/runloop/AnimationFrameCallback;";
void onAnimationFrame(double timestampMs) {
callback_(timestampMs);
@@ -16,14 +18,16 @@ class AnimationFrameCallback : public facebook::jni::HybridClass<AnimationFrameC
static void registerNatives() {
javaClassStatic()->registerNatives({
makeNativeMethod("onAnimationFrame", AnimationFrameCallback::onAnimationFrame),
makeNativeMethod(
"onAnimationFrame", AnimationFrameCallback::onAnimationFrame),
});
}
private:
friend HybridBase;
explicit AnimationFrameCallback(std::function<void(const double)> callback) : callback_(std::move(callback)) {}
explicit AnimationFrameCallback(std::function<void(const double)> callback)
: callback_(std::move(callback)) {}
std::function<void(double)> callback_;
};
@@ -2,8 +2,6 @@
#include <worklets/Tools/PlatformLogger.h>
#include <string>
constexpr const auto tag = "Worklets";
namespace worklets {
@@ -5,7 +5,6 @@
#include <worklets/android/WorkletsModule.h>
#include <memory>
#include <string>
#include <utility>
namespace worklets {
@@ -19,7 +18,7 @@ WorkletsModule::WorkletsModule(
jni::alias_ref<JavaMessageQueueThread::javaobject> messageQueueThread,
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker,
const std::shared_ptr<UIScheduler> &uiScheduler,
const std::shared_ptr<const JSBigStringBuffer> &script,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceURL)
: javaPart_(jni::make_global(jThis)),
rnRuntime_(rnRuntime),
@@ -29,25 +28,32 @@ WorkletsModule::WorkletsModule(
jsCallInvoker,
uiScheduler,
getIsOnJSQueueThread(),
RuntimeBindings{.requestAnimationFrame = getRequestAnimationFrame()},
getForwardedRequestAnimationFrame(),
script,
sourceURL)) {
auto jsiWorkletsModuleProxy = workletsModuleProxy_->createJSIWorkletsModuleProxy();
auto jsiWorkletsModuleProxy =
workletsModuleProxy_->createJSIWorkletsModuleProxy();
auto optimizedJsiWorkletsModuleProxy = jsi_utils::optimizedFromHostObject(
*rnRuntime_, std::static_pointer_cast<jsi::HostObject>(std::move(jsiWorkletsModuleProxy)));
*rnRuntime_,
std::static_pointer_cast<jsi::HostObject>(
std::move(jsiWorkletsModuleProxy)));
RNRuntimeWorkletDecorator::decorate(
*rnRuntime_, std::move(optimizedJsiWorkletsModuleProxy), workletsModuleProxy_->getJSLogger());
*rnRuntime_,
std::move(optimizedJsiWorkletsModuleProxy),
workletsModuleProxy_->getJSLogger());
}
jni::local_ref<WorkletsModule::jhybriddata> WorkletsModule::initHybrid(
jni::alias_ref<jhybridobject> jThis,
jlong jsContext,
jni::alias_ref<JavaMessageQueueThread::javaobject> messageQueueThread,
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject> jsCallInvokerHolder,
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
jsCallInvokerHolder,
jni::alias_ref<worklets::AndroidUIScheduler::javaobject> androidUIScheduler
#ifdef WORKLETS_BUNDLE_MODE
,
jni::alias_ref<facebook::react::BundleWrapper::javaobject> bundleWrapper,
jni::alias_ref<facebook::react::BigStringBufferWrapper::javaobject>
scriptWrapper,
const std::string &sourceURL
#endif // WORKLETS_BUNDLE_MODE
) {
@@ -55,27 +61,42 @@ jni::local_ref<WorkletsModule::jhybriddata> WorkletsModule::initHybrid(
auto rnRuntime = reinterpret_cast<jsi::Runtime *>(jsContext);
auto uiScheduler = androidUIScheduler->cthis()->getUIScheduler();
std::shared_ptr<const JSBigStringBuffer> script = nullptr;
std::shared_ptr<const BigStringBuffer> script = nullptr;
#ifdef WORKLETS_BUNDLE_MODE
script = bundleWrapper->cthis()->getBundle();
script = scriptWrapper->cthis()->getScript();
#else
const auto sourceURL = std::string{};
#endif // WORKLETS_BUNDLE_MODE
return makeCxxInstance(jThis, rnRuntime, messageQueueThread, jsCallInvoker, uiScheduler, script, sourceURL);
return makeCxxInstance(
jThis,
rnRuntime,
messageQueueThread,
jsCallInvoker,
uiScheduler,
script,
sourceURL);
}
RuntimeBindings::RequestAnimationFrame WorkletsModule::getRequestAnimationFrame() {
return [javaPart = javaPart_](std::function<void(const double)> &&callback) -> void {
std::function<void(std::function<void(const double)>)>
WorkletsModule::getForwardedRequestAnimationFrame() {
return [javaPart =
javaPart_](std::function<void(const double)> &&callback) -> void {
static const auto jRequestAnimationFrame =
javaPart->getClass()->getMethod<void(AnimationFrameCallback::javaobject)>("requestAnimationFrame");
jRequestAnimationFrame(javaPart.get(), AnimationFrameCallback::newObjectCxxArgs(std::move(callback)).get());
javaPart->getClass()
->getMethod<void(AnimationFrameCallback::javaobject)>(
"requestAnimationFrame");
jRequestAnimationFrame(
javaPart.get(),
AnimationFrameCallback::newObjectCxxArgs(std::move(callback)).get());
};
}
std::function<bool()> WorkletsModule::getIsOnJSQueueThread() {
return [javaPart = javaPart_]() -> bool {
return javaPart->getClass()->getMethod<jboolean()>("isOnJSQueueThread").operator()(javaPart);
return javaPart->getClass()
->getMethod<jboolean()>("isOnJSQueueThread")
.operator()(javaPart);
};
}
@@ -3,14 +3,13 @@
#include <ReactCommon/CallInvokerHolder.h>
#include <fbjni/fbjni.h>
#include <jsi/jsi.h>
#include <jsireact/JSIExecutor.h>
#include <react/jni/JMessageQueueThread.h>
#include <worklets/Tools/Defs.h>
#ifdef WORKLETS_BUNDLE_MODE
#include <react/fabric/BundleWrapper.h>
#include <react/fabric/BigStringBufferWrapper.h>
#endif // WORKLETS_BUNDLE_MODE
#include <worklets/NativeModules/WorkletsModuleProxy.h>
#include <worklets/WorkletRuntime/RuntimeBindings.h>
#include <worklets/android/AndroidUIScheduler.h>
#include <memory>
@@ -23,17 +22,21 @@ using namespace facebook::jni;
class WorkletsModule : public jni::HybridClass<WorkletsModule> {
public:
static auto constexpr kJavaDescriptor = "Lcom/swmansion/worklets/WorkletsModule;";
static auto constexpr kJavaDescriptor =
"Lcom/swmansion/worklets/WorkletsModule;";
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jhybridobject> jThis,
jlong jsContext,
jni::alias_ref<JavaMessageQueueThread::javaobject> messageQueueThread,
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject> jsCallInvokerHolder,
jni::alias_ref<worklets::AndroidUIScheduler::javaobject> androidUIScheduler
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
jsCallInvokerHolder,
jni::alias_ref<worklets::AndroidUIScheduler::javaobject>
androidUIScheduler
#ifdef WORKLETS_BUNDLE_MODE
,
jni::alias_ref<facebook::react::BundleWrapper::javaobject> bundleWrapper,
jni::alias_ref<facebook::react::BigStringBufferWrapper::javaobject>
scriptWrapper,
const std::string &sourceURL
#endif // WORKLETS_BUNDLE_MODE
);
@@ -51,7 +54,7 @@ class WorkletsModule : public jni::HybridClass<WorkletsModule> {
jni::alias_ref<JavaMessageQueueThread::javaobject> messageQueueThread,
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker,
const std::shared_ptr<UIScheduler> &uiScheduler,
const std::shared_ptr<const JSBigStringBuffer> &bundle,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceURL);
void invalidateCpp();
@@ -61,7 +64,8 @@ class WorkletsModule : public jni::HybridClass<WorkletsModule> {
return javaPart_->getClass()->getMethod<Signature>(methodName.c_str());
}
RuntimeBindings::RequestAnimationFrame getRequestAnimationFrame();
std::function<void(std::function<void(const double)>)>
getForwardedRequestAnimationFrame();
std::function<bool()> getIsOnJSQueueThread();

Some files were not shown because too many files have changed in this diff Show More