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
@@ -111,6 +111,8 @@ jobs:
- name: Build preview APK - name: Build preview APK
working-directory: frontend-admin working-directory: frontend-admin
env:
EAS_BUILD_NO_EXPO_GO_WARNING: true
run: eas build --platform android --profile preview --non-interactive run: eas build --platform android --profile preview --non-interactive
- name: Download preview APK - name: Download preview APK
@@ -158,6 +160,8 @@ jobs:
- name: Build production APK - name: Build production APK
working-directory: frontend-admin working-directory: frontend-admin
env:
EAS_BUILD_NO_EXPO_GO_WARNING: true
run: eas build --platform android --profile production --non-interactive run: eas build --platform android --profile production --non-interactive
- name: Download production APK - name: Download production APK
+14 -24
View File
@@ -1,47 +1,37 @@
postgres_version: "16" pg_version: "16"
postgres_databases: db_user: admin_gestion_commande_db
- name: gestion_db db_name: gestion_commande_db
owner: postgres db_password: "hb-FS462(#I~6(4',Nt=3Rd4567890jsezZ"
postgres_password: "VotreMotDePasseSecure123!" db_host: "localhost"
db_port: 5432
# Redis Configuration # Redis Configuration
redis_bind: "127.0.0.1" redis_bind: "127.0.0.1"
redis_port: 6379 redis_port: 6379
redis_maxmemory: "512mb" redis_maxmemory: "1024mb"
redis_maxmemory_policy: "allkeys-lru" redis_maxmemory_policy: "allkeys-lru"
redis_password: "UltrasecretMdp123!" redis_password: "uQ64h5UhFdrm!H|_cCQer"
# ============================================ # ============================================
# Configuration Nginx et Applications # Configuration Nginx et Applications
# ============================================ # ============================================
nginx_app_name: "nantais-livraison"
nginx_domain: "{{ ansible_default_ipv4.address }}"
directory: "/home/ubuntu" directory: "/home/ubuntu"
tunnel_log: "/var/log/tunnel.log"
tunnel_log_error: "/var/log/tunnel-error.log"
# Chemins des applications (bonnes pratiques)
nginx_frontend_path: "/home/ubuntu/frontend"
backend_dir: "/home/ubuntu/backend" backend_dir: "/home/ubuntu/backend"
backend_binary: "/home/ubuntu/backend/main" backend_binary: "/home/ubuntu/backend/main"
docker_dir: "/home/ubuntu/docker" uploads_dir: "/home/ubuntu/backend/uploads"
frontend_port: 5173 frontend_port: 5173
backend_dir_docker: "/home/ubuntu/backend/docker" domain_name: "uber-stup.club"
# Utilisateurs et permissions # Utilisateurs et permissions
user_web: "www-data" # Utilisateur pour les applications web user_web: "www-data" # Utilisateur pour les applications web
user_deploy: "ubuntu" # Utilisateur pour le déploiement user_deploy: "ubuntu" # Utilisateur pour le déploiement
user_owner: "root" # Propriétaire des fichiers systemd user_owner: "root" # Propriétaire des fichiers systemd
docker_user: docker
docker_group: docker
# Backend
backend_port: 8080
# Backend
backend_local_port: 8080
backend_port: 443
# Timeouts # Timeouts
nginx_proxy_timeout: 60 nginx_proxy_timeout: 60
# Cache Duration
nginx_cache_static_duration: "1y"
nginx_cache_media_duration: "1y"
# Upload Size # Upload Size
nginx_max_body_size: "10M" nginx_max_body_size: "10M"
+3 -19
View File
@@ -1,21 +1,5 @@
[all] [all]
vm-postgres ansible_host=192.168.1.65 ansible_user=ubuntu ansible_ssh_pass=root uber-stup ansible_host=5.252.20.129 ansible_user=root ansible_ssh_pass=rL9lY6YkcDQmfRuZ3Z
vm-redis ansible_host=192.168.1.62 ansible_user=ubuntu ansible_ssh_pass=root
vm-nginx ansible_host=192.168.1.67 ansible_user=ubuntu ansible_ssh_pass=root
vm-backend ansible_host=192.168.1.72 ansible_user=ubuntu ansible_ssh_pass=root
[postgres] [uber-stup]
vm-postgres ansible_host=192.168.1.65 ansible_user=ubuntu ansible_ssh_pass=root uber-stup ansible_host=5.252.20.129 ansible_user=root ansible_ssh_pass=rL9lY6YkcDQmfRuZ3Z
[redis]
vm-redis ansible_host=192.168.1.62 ansible_user=ubuntu ansible_ssh_pass=root
[nginx]
vm-nginx ansible_host=192.168.1.67 ansible_user=ubuntu ansible_ssh_pass=root
[backend]
vm-backend ansible_host=192.168.1.72 ansible_user=ubuntu ansible_ssh_pass=root
[services]
vm-redis ansible_host=192.168.1.62 ansible_user=ubuntu ansible_ssh_pass=root
vm-postgres ansible_host=192.168.1.65 ansible_user=ubuntu ansible_ssh_pass=root
@@ -1,6 +1,6 @@
--- ---
- name: Installation et configuration du frontend et backend - name: Installation et configuration du frontend et backend
hosts: nginx hosts: all
become: true become: true
gather_facts: true gather_facts: true
@@ -19,6 +19,9 @@
- tar - tar
- rsync - rsync
- acl - acl
- nginx
- certbot
- python3-certbot-nginx
state: present state: present
update_cache: yes update_cache: yes
@@ -42,23 +45,8 @@
state: present state: present
mode: "0644" mode: "0644"
- name: Setup Node.js 20 repository - name: Check Go versions
ansible.builtin.shell: | ansible.builtin.shell: |
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
args:
executable: /bin/bash
- name: Install Node.js 20
ansible.builtin.apt:
name: nodejs
state: present
update_cache: yes
dpkg_options: "force-overwrite"
- name: Check Node.js, npm and Go versions
ansible.builtin.shell: |
echo "Node: $(node -v)"
echo "npm: $(npm -v)"
echo "Go: $(go version)" echo "Go: $(go version)"
register: versions_check register: versions_check
changed_when: false changed_when: false
@@ -80,68 +68,25 @@
group: root group: root
mode: "0755" mode: "0755"
- name: Ensure /home/ubuntu exists with correct permissions - name: Créer l'utilisateur de déploiement s'il n'existe pas
ansible.builtin.user:
name: "{{ user_deploy }}"
shell: /bin/bash
create_home: yes
state: present
- name: Ensure /home/{{ user_deploy }} exists with correct permissions
ansible.builtin.file: ansible.builtin.file:
path: /home/ubuntu path: "/home/{{ user_deploy }}"
state: directory state: directory
owner: "{{ user_deploy }}" owner: "{{ user_deploy }}"
group: "{{ user_deploy }}" group: "{{ user_deploy }}"
mode: "0755" mode: "0755"
- name: Remove old frontend directory if exists
ansible.builtin.file:
path: "{{ nginx_frontend_path }}"
state: absent
- name: Create frontend directory with deploy user ownership
ansible.builtin.file:
path: "{{ nginx_frontend_path }}"
state: directory
owner: "{{ user_deploy }}"
group: "{{ user_deploy }}"
mode: "0755"
- name: Synchronize frontend content (excluding node_modules and .git)
ansible.builtin.synchronize:
src: ../frontend/
dest: "{{ nginx_frontend_path }}/"
rsync_opts:
- "--exclude=node_modules"
- "--exclude=.git"
- "--exclude=dist"
- "--exclude=build"
delete: no
recursive: yes
tags:
- syncro
- name: Install npm dependencies as deploy user
become_user: "{{ user_deploy }}"
ansible.builtin.shell: |
export PATH=/usr/local/go/bin:$PATH
npm install
npm run build
args:
chdir: "{{ nginx_frontend_path }}"
executable: /bin/bash
- name: Set ownership to www-data for runtime
ansible.builtin.file:
path: "{{ nginx_frontend_path }}"
owner: "{{ user_web }}"
group: "{{ user_web }}"
mode: "0755"
recurse: yes
# ============================================ # ============================================
# BACKEND # BACKEND
# ============================================ # ============================================
- name: Remove old backend directory if exists
ansible.builtin.file:
path: "{{ backend_dir }}"
state: absent
- name: Create backend directory with deploy user ownership - name: Create backend directory with deploy user ownership
ansible.builtin.file: ansible.builtin.file:
path: "{{ backend_dir }}" path: "{{ backend_dir }}"
@@ -149,15 +94,22 @@
owner: "{{ user_deploy }}" owner: "{{ user_deploy }}"
group: "{{ user_deploy }}" group: "{{ user_deploy }}"
mode: "0755" mode: "0755"
tags: backend
- name: Copy backend files - name: Synchroniser le backend Go
ansible.builtin.copy: ansible.builtin.synchronize:
src: "../backendprod/gestion/" src: ../backend/gestion/
dest: "{{ backend_dir }}/" dest: "{{ backend_dir }}/"
owner: "{{ user_deploy }}" rsync_opts:
group: "{{ user_deploy }}" - "--exclude=.git"
mode: preserve - "--exclude=.gitignore"
directory_mode: "0755" - "--exclude=docker-compose.yml"
- "--exclude=.env"
- "--no-owner"
- "--no-group"
delete: yes
recursive: yes
tags: backend
- name: Compile backend as deploy user - name: Compile backend as deploy user
become_user: "{{ user_deploy }}" become_user: "{{ user_deploy }}"
@@ -168,8 +120,8 @@
args: args:
chdir: "{{ backend_dir }}" chdir: "{{ backend_dir }}"
executable: /bin/bash executable: /bin/bash
tags: tags: backend
- compile
- name: Set ownership to www-data for runtime - name: Set ownership to www-data for runtime
ansible.builtin.file: ansible.builtin.file:
path: "{{ backend_dir }}" path: "{{ backend_dir }}"
@@ -177,6 +129,7 @@
group: "{{ user_web }}" group: "{{ user_web }}"
mode: "0755" mode: "0755"
recurse: yes recurse: yes
tags: backend
- name: Set executable permission on backend binary - name: Set executable permission on backend binary
ansible.builtin.file: ansible.builtin.file:
@@ -184,6 +137,7 @@
owner: "{{ user_web }}" owner: "{{ user_web }}"
group: "{{ user_web }}" group: "{{ user_web }}"
mode: "0755" mode: "0755"
tags: backend
# ============================================ # ============================================
# SYSTEMD SERVICES # SYSTEMD SERVICES
@@ -196,39 +150,156 @@
owner: root owner: root
group: root group: root
mode: "0644" mode: "0644"
tags: backend
- name: Reload systemd daemon - name: Reload systemd daemon
ansible.builtin.systemd: ansible.builtin.systemd:
daemon_reload: yes daemon_reload: yes
tags: backend
- name: Enable and restart frontend service
ansible.builtin.systemd:
name: frontend
enabled: yes
state: restarted
- name: Enable and restart backend service - name: Enable and restart backend service
ansible.builtin.systemd: ansible.builtin.systemd:
name: backend name: backend
enabled: yes enabled: yes
state: restarted state: restarted
tags: backend
- name: Wait for backend to be ready - name: Wait for backend to be ready
ansible.builtin.wait_for: ansible.builtin.wait_for:
port: "{{ backend_port }}" port: "{{ backend_local_port }}"
delay: 2 delay: 2
timeout: 30 timeout: 30
tags: backend
- name: Display service status - name: Display service status
ansible.builtin.shell: | ansible.builtin.shell: |
echo "=== Frontend Service ==="
systemctl status frontend --no-pager || true
echo ""
echo "=== Backend Service ===" echo "=== Backend Service ==="
systemctl status backend --no-pager || true systemctl status backend --no-pager || true
register: service_status register: service_status
changed_when: false changed_when: false
tags: backend
- name: Show service status - name: Show service status
ansible.builtin.debug: ansible.builtin.debug:
msg: "{{ service_status.stdout_lines }}" msg: "{{ service_status.stdout_lines }}"
- name: Configuration UFW
ansible.builtin.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "{{ backend_port }}"
- 80
- 22
- name: Activation du firewall
ansible.builtin.ufw:
state: enabled
- name: Vérifier si un certificat existe déjà
ansible.builtin.stat:
path: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem"
register: cert_file
tags: [certbot]
- name: Déployer la configuration Nginx HTTP
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/api
vars:
ssl_enabled: false
tags: [certbot]
- name: Activation du site Nginx
ansible.builtin.file:
src: /etc/nginx/sites-available/api
dest: /etc/nginx/sites-enabled/api
state: link
force: yes
tags: [certbot]
- name: Suppression du site par défaut
ansible.builtin.file:
path: /etc/nginx/sites-enabled/default
state: absent
tags: [certbot]
- name: Test de la configuration Nginx
ansible.builtin.command: nginx -t
changed_when: false
tags: [certbot]
- name: Redémarrage de Nginx
ansible.builtin.systemd:
name: nginx
state: restarted
enabled: yes
tags: [certbot]
# ============================================================
# Certificat SSL Let's Encrypt
# ============================================================
- name: Générer le certificat SSL avec Certbot
ansible.builtin.command: >
certbot certonly --nginx
-d {{ domain_name }}
--non-interactive
--agree-tos
--email admin@{{ domain_name }}
when: not cert_file.stat.exists
tags: [certbot]
# ============================================================
# Nginx - reconfiguration HTTPS après certificat
# ============================================================
- name: Vérifier la présence du certificat
ansible.builtin.stat:
path: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem"
register: cert_file_after
tags: [nginx]
- name: Déployer la configuration Nginx HTTPS
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/api
vars:
ssl_enabled: true
when: cert_file_after.stat.exists
notify: Restart nginx
tags: [nginx]
- name: Test de la configuration Nginx finale
ansible.builtin.command: nginx -t
changed_when: false
tags: [nginx]
- name: Redémarrage de Nginx avec SSL
ansible.builtin.systemd:
name: nginx
state: restarted
when: cert_file_after.stat.exists
tags: [nginx]
- name: Vérifier le renouvellement automatique
ansible.builtin.command: certbot renew --dry-run
register: certbot_renew
changed_when: false
failed_when: false
tags: [certbot]
- name: Afficher le statut du renouvellement
ansible.builtin.debug:
msg: "{{ certbot_renew.stdout_lines }}"
tags: [certbot]
handlers:
- name: Reload systemd
ansible.builtin.systemd:
daemon_reload: yes
- name: Restart nginx
ansible.builtin.systemd:
name: nginx
state: restarted
-108
View File
@@ -1,108 +0,0 @@
---
- name: Installation et configuration de Nginx
hosts: nginx
become: true
gather_facts: true
tasks:
- name: Installer Nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: yes
- name: Créer le répertoire frontend
ansible.builtin.file:
path: "{{ nginx_frontend_path }}"
state: directory
owner: www-data
group: www-data
mode: "0755"
- name: Créer le répertoire de logs
ansible.builtin.file:
path: /var/log/nginx
state: directory
owner: www-data
group: adm
mode: "0755"
- name: Supprimer la config par défaut
ansible.builtin.file:
path: /etc/nginx/sites-enabled/default
state: absent
- name: Créer la configuration Nginx
ansible.builtin.template:
src: ./templates/nginx.conf.j2
dest: /etc/nginx/sites-available/{{ nginx_app_name }}
notify: Recharger Nginx
- name: Activer la configuration
ansible.builtin.file:
src: /etc/nginx/sites-available/{{ nginx_app_name }}
dest: /etc/nginx/sites-enabled/{{ nginx_app_name }}
state: link
notify: Recharger Nginx
- name: Configurer Nginx global settings
lineinfile:
path: /etc/nginx/nginx.conf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
insertafter: "http {"
loop:
- {
regexp: '^\s*server_tokens',
line: " server_tokens off;",
}
- {
regexp: '^\s*client_max_body_size',
line: " client_max_body_size {{ nginx_max_body_size }};",
}
notify: Recharger Nginx
- name: Tester la configuration Nginx
command: nginx -t
register: nginx_test
changed_when: false
- name: Afficher le résultat du test
debug:
var: nginx_test.stderr_lines
- name: Démarrer Nginx
ansible.builtin.systemd:
name: nginx
state: started
enabled: yes
- name: Configurer UFW - Autoriser HTTP
ansible.builtin.ufw:
rule: allow
port: "80"
proto: tcp
when: nginx_enable_firewall | default(true)
- name: Configurer UFW - Bloquer accès direct au backend
ansible.builtin.ufw:
rule: deny
port: "{{ backend_port }}"
proto: tcp
from_ip: any
when: nginx_enable_firewall | default(true)
- name: Configurer le tunnel
ansible.builtin.template:
src: tunnel.conf.j2
dest: /etc/tunnel.conf
owner: root
group: root
mode: 0644
notify: Recharger Nginx
handlers:
- name: Recharger Nginx
systemd:
name: nginx
state: reloaded
+137 -58
View File
@@ -3,101 +3,180 @@
# PostgreSQL Installation (vm-postgres uniquement) # PostgreSQL Installation (vm-postgres uniquement)
# ============================================ # ============================================
- name: Installation et configuration de PostgreSQL - name: Installation et configuration de PostgreSQL
hosts: postgres hosts: uber-stup
become: true become: true
gather_facts: true gather_facts: true
tasks: tasks:
- name: Installer les dépendances système # ============================================================
apt: # Dépendances requises
# ============================================================
- name: Installer les dépendances pour le dépôt APT
ansible.builtin.apt:
name: name:
- wget
- gnupg2 - gnupg2
- lsb-release - curl
- ca-certificates - ca-certificates
- apt-transport-https - lsb-release
- acl # Nécessaire pour become_user - python3-psycopg2
- acl
state: present state: present
update_cache: yes update_cache: yes
# ============================================================
# Ajout du dépôt officiel PostgreSQL
# ============================================================
- name: Ajouter la clé GPG du dépôt PostgreSQL - name: Ajouter la clé GPG du dépôt PostgreSQL
apt_key: ansible.builtin.apt_key:
url: https://www.postgresql.org/media/keys/ACCC4CF8.asc url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
state: present state: present
- name: Ajouter le dépôt PostgreSQL - name: Ajouter le dépôt PostgreSQL 16
apt_repository: ansible.builtin.apt_repository:
repo: "deb http://apt.postgresql.org/pub/repos/apt {{ ansible_distribution_release }}-pgdg main" repo: "deb http://apt.postgresql.org/pub/repos/apt {{ ansible_distribution_release }}-pgdg main"
state: present state: present
filename: pgdg filename: pgdg
- name: Installer PostgreSQL {{ postgres_version }} # ============================================================
apt: # Installation PostgreSQL 16
# ============================================================
- name: Installer PostgreSQL 16
ansible.builtin.apt:
name: name:
- postgresql-{{ postgres_version }} - "postgresql-{{ pg_version }}"
- postgresql-contrib-{{ postgres_version }} - "postgresql-client-{{ pg_version }}"
- python3-psycopg2 - "postgresql-contrib-{{ pg_version }}"
state: present state: present
update_cache: yes update_cache: yes
- name: S'assurer que PostgreSQL est démarré - name: Démarrer et activer PostgreSQL
systemd: ansible.builtin.systemd:
name: postgresql name: postgresql
state: started state: started
enabled: yes enabled: yes
- name: Définir le mot de passe du user postgres # ============================================================
postgresql_user: # Configuration PostgreSQL
name: postgres # ============================================================
password: "{{ postgres_password }}" - name: Configurer l'écoute sur localhost
state: present ansible.builtin.lineinfile:
become_user: postgres path: "/etc/postgresql/{{ pg_version }}/main/postgresql.conf"
- name: Créer les bases de données PostgreSQL
postgresql_db:
name: "{{ item.name }}"
owner: postgres
state: present
loop: "{{ postgres_databases }}"
become_user: postgres
- name: Configurer pg_hba.conf pour autoriser le backend
blockinfile:
path: "/etc/postgresql/{{ postgres_version }}/main/pg_hba.conf"
block: |
# Connexions locales
local all postgres peer
host all postgres 127.0.0.1/32 scram-sha-256
# Connexions backend LAN
host all postgres 192.168.1.0/24 scram-sha-256
marker: "# {mark} ANSIBLE MANAGED BLOCK"
notify: Redémarrer PostgreSQL
tags:
- reload
- name: Configurer PostgreSQL pour écouter sur toutes les interfaces
lineinfile:
path: "/etc/postgresql/{{ postgres_version }}/main/postgresql.conf"
regexp: "^#?listen_addresses" regexp: "^#?listen_addresses"
line: "listen_addresses = '*'" line: "listen_addresses = 'localhost'"
notify: Restart postgresql
- name: Configurer le port
ansible.builtin.lineinfile:
path: "/etc/postgresql/{{ pg_version }}/main/postgresql.conf"
regexp: "^#?port"
line: "port = {{ db_port }}"
notify: Restart postgresql
# ============================================================
# Locale requise
# ============================================================
- name: Générer la locale fr_FR.UTF-8
ansible.builtin.command: locale-gen fr_FR.UTF-8
register: locale_gen
changed_when: "'Generation complete' in locale_gen.stdout"
- name: Mettre à jour les locales
ansible.builtin.command: update-locale
when: locale_gen.changed
# ============================================================
# Création de la base de données et configuration utilisateur
# ============================================================
- name: Définir le mot de passe de l'utilisateur postgres
become_user: postgres
ansible.builtin.postgresql_user:
name: "{{ db_user }}"
password: "{{ db_password }}"
state: present state: present
notify: Redémarrer PostgreSQL
tags: - name: Vérifier si la base de données existe
- reload become_user: postgres
ansible.builtin.command: psql -tAc "SELECT 1 FROM pg_database WHERE datname='{{ db_name }}'"
register: db_exists
changed_when: false
- name: Créer la base de données avec ICU
become_user: postgres
ansible.builtin.command: >
psql -c "CREATE DATABASE {{ db_name }}
OWNER {{ db_user }}
ENCODING 'UTF8'
LOCALE_PROVIDER icu
ICU_LOCALE 'fr-FR'
TEMPLATE template0"
when: db_exists.stdout != "1"
- name: Accorder tous les privilèges sur la base
become_user: postgres
ansible.builtin.postgresql_privs:
db: "{{ db_name }}"
role: "{{ db_user }}"
type: database
privs: ALL
state: present
# ============================================================
# Authentification pg_hba.conf
# ============================================================
- name: Configurer l'authentification md5 pour les connexions locales
ansible.builtin.postgresql_pg_hba:
dest: "/etc/postgresql/{{ pg_version }}/main/pg_hba.conf"
contype: host
databases: "{{ db_name }}"
users: "{{ db_user }}"
source: "127.0.0.1/32"
method: md5
notify: Restart postgresql
- name: Configurer l'authentification md5 pour IPv6 local
ansible.builtin.postgresql_pg_hba:
dest: "/etc/postgresql/{{ pg_version }}/main/pg_hba.conf"
contype: host
databases: "{{ db_name }}"
users: "{{ db_user }}"
source: "::1/128"
method: md5
notify: Restart postgresql
# ============================================================
# Vérification
# ============================================================
- name: Vérifier la version de PostgreSQL
become_user: postgres
ansible.builtin.command: psql --version
register: pg_version_check
changed_when: false
- name: Afficher la version
ansible.builtin.debug:
msg: "{{ pg_version_check.stdout }}"
- name: Vérifier que la base existe
become_user: postgres
ansible.builtin.command: psql -lqt
register: pg_databases
changed_when: false
- name: Afficher les bases de données
ansible.builtin.debug:
msg: "{{ pg_databases.stdout_lines }}"
handlers: handlers:
- name: Redémarrer PostgreSQL - name: Restart postgresql
systemd: ansible.builtin.systemd:
name: postgresql name: postgresql
state: restarted state: restarted
tags:
- reload
# ============================================ # ============================================
# Redis Installation (vm-redis uniquement) # Redis Installation (vm-redis uniquement)
# ============================================ # ============================================
- name: Installation et configuration de Redis - name: Installation et configuration de Redis
hosts: redis hosts: uber-stup
become: true become: true
gather_facts: true gather_facts: true
+16
View File
@@ -5,6 +5,22 @@ After=network.target
[Service] [Service]
User={{ user_owner }} User={{ user_owner }}
WorkingDirectory={{ backend_dir }} WorkingDirectory={{ backend_dir }}
Environment="PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Environment="USER_JWT_SECRET=TheAmaziNgSecretJwtMotherFuckerInThEbitCh3232131231313443jqfdksdjfjsldfjlds"
Environment="DB_HOST=localhost"
Environment="DB_PORT=5432"
Environment="DB_USER=admin_gestion_commande_db"
Environment="DB_PASSWORD=hb-FS462(#I~6(4',Nt=3Rd4567890jsezZ"
Environment="DB_NAME=gestion_commande_db"
Environment="DB_SSLMODE=disable"
Environment="REDIS_HOST=127.0.0.1"
Environment="REDIS_PORT=6379"
Environment="REDIS_PASSWORD=uQ64h5UhFdrm!H|_cCQer"
Environment="SESSION_SECRET=jfWR21Ywbuy{Yq<1A26TV)jCe"
Environment="GIN_MODE=release"
Environment="TOMTOM_API_KEY=MERY8I7LMeYVSLKO5WuV73W9rKJpBLoB"
Environment="API_PORT={{ backend_local_port }}"
Environment="ADMIN_JWT_SECRET=TheAmaziNgSecretJwtMotherFuckerInThEbitCh3232131231313443jqfdksdjfjsldfjlzZ"
ExecStart={{ backend_binary }} ExecStart={{ backend_binary }}
Restart=always Restart=always
+135 -44
View File
@@ -1,79 +1,170 @@
upstream backend { # =========================================================
server 127.0.0.1:{{ backend_port }} max_fails=3 fail_timeout=30s; # Request ID for correlation
keepalive 32; # =========================================================
map $http_x_request_id $req_id {
default $http_x_request_id;
"" $request_id;
} }
# =========================================================
# Rate limiting
# =========================================================
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=uploads:10m rate=10r/m;
# =========================================================
# Upstream backend
# =========================================================
upstream backend {
server 127.0.0.1:{{ backend_local_port }} max_fails=3 fail_timeout=30s;
keepalive 32;
keepalive_requests 100;
keepalive_timeout 60s;
}
{% if ssl_enabled %}
# =========================================================
# HTTP → HTTPS redirect
# =========================================================
server { server {
listen 80; listen 80;
listen [::]:80; listen [::]:80;
server_name {{ nginx_domain }}; server_name {{ domain_name }};
root {{ nginx_frontend_path }}/dist; return 301 https://$host$request_uri;
index index.html; }
{% endif %}
access_log /var/log/nginx/{{ nginx_app_name }}.access.log; # =========================================================
error_log /var/log/nginx/{{ nginx_app_name }}.error.log; # API Server
# =========================================================
server {
{% if ssl_enabled %}
listen {{ backend_port }} ssl;
listen [::]:{{ backend_port }} ssl;
ssl_certificate /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ domain_name }}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
{% else %}
listen {{ backend_port }};
listen [::]:{{ backend_port }};
{% endif %}
server_name {{ domain_name }};
client_max_body_size {{ nginx_max_body_size }}; client_max_body_size {{ nginx_max_body_size }};
location /api/ { server_tokens off;
limit_except GET POST PUT DELETE PATCH OPTIONS {
deny all;
}
proxy_pass http://backend/; # =========================================================
# Security headers
# =========================================================
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
# =========================================================
# API proxy
# =========================================================
location / {
limit_req zone=api burst=20 nodelay;
limit_except GET POST PUT PATCH DELETE OPTIONS { deny all; }
proxy_pass http://backend;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $req_id;
proxy_set_header Connection "";
proxy_connect_timeout {{ nginx_proxy_timeout }}s; proxy_connect_timeout 60s;
proxy_send_timeout {{ nginx_proxy_timeout }}s; proxy_send_timeout 60s;
proxy_read_timeout {{ nginx_proxy_timeout }}s; proxy_read_timeout 60s;
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_no_cache 1;
proxy_cache_bypass 1;
} }
location / { # =========================================================
try_files $uri $uri/ /index.html; # Uploads
add_header Cache-Control "no-cache, no-store, must-revalidate"; # =========================================================
} location /uploads/ {
alias {{ uploads_dir }}/;
limit_req zone=uploads burst=20 nodelay;
limit_except GET HEAD { deny all; }
location ~* \.(css|js)$ { # Bloquer les fichiers exécutables
expires {{ nginx_cache_static_duration }}; location ~* \.(php|php5|phtml|sh|py|pl|cgi|exe|asp|aspx|jsp)$ {
deny all;
}
expires 30d;
add_header Cache-Control "public, immutable"; add_header Cache-Control "public, immutable";
access_log off; add_header X-Content-Type-Options "nosniff" always;
} }
location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ { location ^~ /uploads/images/ {
expires {{ nginx_cache_media_duration }}; alias {{ uploads_dir }}/images/;
add_header Cache-Control "public, immutable"; limit_req zone=uploads burst=20 nodelay;
access_log off; limit_except GET HEAD OPTIONS { deny all; }
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
add_header X-Content-Type-Options "nosniff" always;
expires 30d;
add_header Cache-Control "public, immutable" always;
types {
image/jpeg jpg jpeg;
image/png png;
image/gif gif;
image/webp webp;
image/svg+xml svg;
}
default_type image/jpeg;
} }
location = /health { location ^~ /uploads/videos/ {
access_log off; alias {{ uploads_dir }}/videos/;
return 200 "healthy\n"; limit_req zone=uploads burst=20 nodelay;
add_header Content-Type text/plain; limit_except GET HEAD OPTIONS { deny all; }
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
add_header X-Content-Type-Options "nosniff" always;
expires 30d;
add_header Cache-Control "public, immutable" always;
types {
video/mp4 mp4;
video/webm webm;
video/ogg ogv;
}
default_type video/mp4;
} }
add_header X-Frame-Options "SAMEORIGIN" always; # =========================================================
add_header X-Content-Type-Options "nosniff" always; # Deny hidden files and sensitive files
add_header X-XSS-Protection "1; mode=block" always; # =========================================================
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location ~ /\. { location ~ /\. {
deny all; deny all;
access_log off; access_log off;
log_not_found off; log_not_found off;
} }
location ~* (\.env|\.git|package\.json|package-lock\.json|yarn\.lock)$ { location ~* (\.env|\.git|package\.json|package-lock\.json|yarn\.lock|Dockerfile|docker-compose\.yml)$ {
deny all; deny all;
access_log off;
log_not_found off;
} }
} }
+3 -3
View File
@@ -89,14 +89,14 @@ func main() {
Domain: "", Domain: "",
MaxAge: 3600, MaxAge: 3600,
HttpOnly: true, HttpOnly: true,
Secure: false, Secure: true,
SameSite: http.SameSiteLaxMode, SameSite: http.SameSiteStrictMode,
}) })
r.Use(sessions.Sessions("mysession", store)) r.Use(sessions.Sessions("mysession", store))
// Configuration CORS // Configuration CORS
r.Use(cors.New(cors.Config{ r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://172.20.167.237", "http://localhost:5173", "http://localhost:8080", "http://192.168.1.72"}, AllowOrigins: []string{"https://uber-stup.club"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
ExposeHeaders: []string{"Content-Length"}, ExposeHeaders: []string{"Content-Length"},
+5
View File
@@ -0,0 +1,5 @@
node_modules
.expo
dist
build
*.log
+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", "version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
"integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
"peer": true,
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.27.1" "@babel/helper-plugin-utils": "^7.27.1"
}, },
@@ -1778,9 +1777,9 @@
} }
}, },
"node_modules/@expo/prebuild-config/node_modules/semver": { "node_modules/@expo/prebuild-config/node_modules/semver": {
"version": "7.7.3", "version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -3859,12 +3858,12 @@
"integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==" "integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw=="
}, },
"node_modules/expo": { "node_modules/expo": {
"version": "54.0.32", "version": "54.0.33",
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.32.tgz", "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz",
"integrity": "sha512-yL9eTxiQ/QKKggVDAWO5CLjUl6IS0lPYgEvC3QM4q4fxd6rs7ks3DnbXSGVU3KNFoY/7cRNYihvd0LKYP+MCXA==", "integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.0", "@babel/runtime": "^7.20.0",
"@expo/cli": "54.0.22", "@expo/cli": "54.0.23",
"@expo/config": "~12.0.13", "@expo/config": "~12.0.13",
"@expo/config-plugins": "~54.0.4", "@expo/config-plugins": "~54.0.4",
"@expo/devtools": "0.1.8", "@expo/devtools": "0.1.8",
@@ -4042,9 +4041,9 @@
} }
}, },
"node_modules/expo/node_modules/@expo/cli": { "node_modules/expo/node_modules/@expo/cli": {
"version": "54.0.22", "version": "54.0.23",
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.22.tgz", "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.23.tgz",
"integrity": "sha512-BTH2FCczhJLfj1cpfcKrzhKnvRLTOztgW4bVloKDqH+G3ZSohWLRFNAIz56XtdjPxBbi2/qWhGBAkl7kBon/Jw==", "integrity": "sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==",
"dependencies": { "dependencies": {
"@0no-co/graphql.web": "^1.0.8", "@0no-co/graphql.web": "^1.0.8",
"@expo/code-signing-certificates": "^0.0.6", "@expo/code-signing-certificates": "^0.0.6",
@@ -4153,9 +4152,9 @@
} }
}, },
"node_modules/expo/node_modules/semver": { "node_modules/expo/node_modules/semver": {
"version": "7.7.3", "version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -5901,9 +5900,9 @@
} }
}, },
"node_modules/npm-package-arg/node_modules/semver": { "node_modules/npm-package-arg/node_modules/semver": {
"version": "7.7.3", "version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -6693,120 +6692,32 @@
} }
}, },
"node_modules/react-native-worklets": { "node_modules/react-native-worklets": {
"version": "0.7.2", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.2.tgz", "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz",
"integrity": "sha512-DuLu1kMV/Uyl9pQHp3hehAlThoLw7Yk2FwRTpzASOmI+cd4845FWn3m2bk9MnjUw8FBRIyhwLqYm2AJaXDXsog==", "integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==",
"peer": true,
"dependencies": { "dependencies": {
"@babel/plugin-transform-arrow-functions": "7.27.1", "@babel/plugin-transform-arrow-functions": "^7.0.0-0",
"@babel/plugin-transform-class-properties": "7.27.1", "@babel/plugin-transform-class-properties": "^7.0.0-0",
"@babel/plugin-transform-classes": "7.28.4", "@babel/plugin-transform-classes": "^7.0.0-0",
"@babel/plugin-transform-nullish-coalescing-operator": "7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0",
"@babel/plugin-transform-optional-chaining": "7.27.1", "@babel/plugin-transform-optional-chaining": "^7.0.0-0",
"@babel/plugin-transform-shorthand-properties": "7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
"@babel/plugin-transform-template-literals": "7.27.1", "@babel/plugin-transform-template-literals": "^7.0.0-0",
"@babel/plugin-transform-unicode-regex": "7.27.1", "@babel/plugin-transform-unicode-regex": "^7.0.0-0",
"@babel/preset-typescript": "7.27.1", "@babel/preset-typescript": "^7.16.7",
"convert-source-map": "2.0.0", "convert-source-map": "^2.0.0",
"semver": "7.7.3" "semver": "7.7.2"
}, },
"peerDependencies": { "peerDependencies": {
"@babel/core": "*", "@babel/core": "^7.0.0-0",
"react": "*", "react": "*",
"react-native": "*" "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": { "node_modules/react-native-worklets/node_modules/semver": {
"version": "7.7.3", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"peer": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -7573,9 +7484,9 @@
} }
}, },
"node_modules/tar": { "node_modules/tar": {
"version": "7.5.7", "version": "7.5.9",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz",
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", "integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==",
"dependencies": { "dependencies": {
"@isaacs/fs-minipass": "^4.0.0", "@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0", "chownr": "^3.0.0",
@@ -110,8 +110,9 @@ Options:
-l --loose -l --loose
Interpret versions and ranges loosely Interpret versions and ranges loosely
-n <0|1> -n <0|1|false>
This is the base to be used for the prerelease identifier. Base number for prerelease identifier (default: 0).
Use false to omit the number altogether.
-p --include-prerelease -p --include-prerelease
Always include prerelease versions in range matching Always include prerelease versions in range matching
@@ -105,7 +105,7 @@ const main = () => {
versions = versions.map((v) => { versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => { }).filter((v) => {
return semver.valid(v) return semver.valid(v, options)
}) })
if (!versions.length) { if (!versions.length) {
return fail() return fail()
@@ -53,7 +53,7 @@ const diff = (version1, version2) => {
return prefix + 'patch' return prefix + 'patch'
} }
// high and low are preleases // high and low are prereleases
return 'prerelease' return 'prerelease'
} }
@@ -78,8 +78,8 @@ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
// ## Pre-release Version Identifier // ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier. // A numeric identifier, or a non-numeric identifier.
// Non-numberic identifiers include numberic identifiers but can be longer. // Non-numeric identifiers include numeric identifiers but can be longer.
// Therefore non-numberic identifiers must go first. // Therefore non-numeric identifiers must go first.
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER] createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIER]})`) }|${src[t.NUMERICIDENTIFIER]})`)
@@ -1,6 +1,6 @@
{ {
"name": "semver", "name": "semver",
"version": "7.7.3", "version": "7.7.4",
"description": "The semantic version parser used by npm.", "description": "The semantic version parser used by npm.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -14,8 +14,8 @@
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
}, },
"devDependencies": { "devDependencies": {
"@npmcli/eslint-config": "^5.0.0", "@npmcli/eslint-config": "^6.0.0",
"@npmcli/template-oss": "4.25.1", "@npmcli/template-oss": "4.29.0",
"benchmark": "^2.1.4", "benchmark": "^2.1.4",
"tap": "^16.0.0" "tap": "^16.0.0"
}, },
@@ -52,7 +52,7 @@
"author": "GitHub Inc.", "author": "GitHub Inc.",
"templateOSS": { "templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "//@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", "engines": ">=10",
"distPaths": [ "distPaths": [
"classes/", "classes/",
@@ -38,7 +38,7 @@ const compare = require('../functions/compare.js')
// - If LT // - If LT
// - If LT.semver is greater than any < or <= comp in C, return false // - 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 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 // - If no C has a prerelease and the LT.semver tuple, return false
// - Else return true // - Else return true
+2 -2
View File
@@ -32,7 +32,7 @@ buildscript {
def reactNativeVersion = project.extensions.getByType(ExpoModuleExtension).reactNativeVersion def reactNativeVersion = project.extensions.getByType(ExpoModuleExtension).reactNativeVersion
group = 'host.exp.exponent' group = 'host.exp.exponent'
version = '54.0.32' version = '54.0.33'
expoModule { expoModule {
// We can't prebuild the module because it depends on the generated files. // We can't prebuild the module because it depends on the generated files.
@@ -43,7 +43,7 @@ android {
namespace "expo.core" namespace "expo.core"
defaultConfig { defaultConfig {
versionCode 1 versionCode 1
versionName "54.0.32" versionName "54.0.33"
consumerProguardFiles("proguard-rules.pro") consumerProguardFiles("proguard-rules.pro")
} }
testOptions { testOptions {
+3 -3
View File
@@ -42,7 +42,7 @@
"expo-document-picker": "~14.0.8", "expo-document-picker": "~14.0.8",
"expo-file-system": "~19.0.21", "expo-file-system": "~19.0.21",
"expo-font": "~14.0.11", "expo-font": "~14.0.11",
"expo-gl": "~16.0.9", "expo-gl": "~16.0.10",
"expo-glass-effect": "~0.1.8", "expo-glass-effect": "~0.1.8",
"expo-google-app-auth": "~8.3.0", "expo-google-app-auth": "~8.3.0",
"expo-haptics": "~15.0.8", "expo-haptics": "~15.0.8",
@@ -71,7 +71,7 @@
"expo-notifications": "~0.32.16", "expo-notifications": "~0.32.16",
"expo-print": "~15.0.8", "expo-print": "~15.0.8",
"expo-live-photo": "~1.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-capture": "~8.0.9",
"expo-screen-orientation": "~9.0.8", "expo-screen-orientation": "~9.0.8",
"expo-secure-store": "~15.0.8", "expo-secure-store": "~15.0.8",
@@ -92,7 +92,7 @@
"expo-video-thumbnails": "~10.0.8", "expo-video-thumbnails": "~10.0.8",
"expo-video": "~3.0.15", "expo-video": "~3.0.15",
"expo-web-browser": "~15.0.10", "expo-web-browser": "~15.0.10",
"jest-expo": "~54.0.16", "jest-expo": "~54.0.17",
"lottie-react-native": "~7.3.1", "lottie-react-native": "~7.3.1",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0", "react-dom": "19.1.0",
+1 -1
View File
@@ -123,7 +123,7 @@ const args = (0, _arg().default)({
}); });
if (args['--version']) { if (args['--version']) {
// Version is added in the build script. // Version is added in the build script.
console.log("54.0.22"); console.log("54.0.23");
process.exit(0); process.exit(0);
} }
if (args['--non-interactive']) { if (args['--non-interactive']) {
@@ -15,19 +15,11 @@ function _ws() {
}; };
return data; return data;
} }
const _net = require("../../../utils/net"); function createDevToolsPluginWebsocketEndpoint() {
function createDevToolsPluginWebsocketEndpoint({ serverBaseUrl }) {
const wss = new (_ws()).WebSocketServer({ const wss = new (_ws()).WebSocketServer({
noServer: true noServer: true
}); });
wss.on('connection', (ws, request)=>{ 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)=>{ ws.on('message', (message, isBinary)=>{
// Broadcast the received message to all other connected clients // Broadcast the received message to all other connected clients
wss.clients.forEach((client)=>{ 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); return middleware.use(metroMiddleware);
}; };
const devtoolsWebsocketEndpoints = (0, _DevToolsPluginWebsocketEndpoint.createDevToolsPluginWebsocketEndpoint)({ const devtoolsWebsocketEndpoints = (0, _DevToolsPluginWebsocketEndpoint.createDevToolsPluginWebsocketEndpoint)();
serverBaseUrl
});
Object.assign(websocketEndpoints, devtoolsWebsocketEndpoints); Object.assign(websocketEndpoints, devtoolsWebsocketEndpoints);
} }
// Attach Expo Atlas if enabled // Attach Expo Atlas if enabled
File diff suppressed because one or more lines are too long
@@ -33,7 +33,7 @@ class FetchClient {
this.headers = { this.headers = {
accept: 'application/json', accept: 'application/json',
'content-type': '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') authorization: 'Basic ' + _nodebuffer().Buffer.from(`${target}:`).toString('base64')
}; };
} }
@@ -83,7 +83,7 @@ function createContext() {
cpu: summarizeCpuInfo(), cpu: summarizeCpuInfo(),
app: { app: {
name: 'expo/cli', name: 'expo/cli',
version: "54.0.22" version: "54.0.23"
}, },
ci: _ciinfo().isCI ? { ci: _ciinfo().isCI ? {
name: _ciinfo().name, name: _ciinfo().name,
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expo/cli", "name": "@expo/cli",
"version": "54.0.22", "version": "54.0.23",
"description": "The Expo CLI", "description": "The Expo CLI",
"main": "build/bin/cli", "main": "build/bin/cli",
"bin": { "bin": {
@@ -169,5 +169,5 @@
"tree-kill": "^1.2.2", "tree-kill": "^1.2.2",
"tsd": "^0.28.1" "tsd": "^0.28.1"
}, },
"gitHead": "5205083d153ffa0c5e48db969aeaa1f9810453fc" "gitHead": "cfd65241fb73317b63e6736e7890a784a69940fc"
} }
+3 -2
View File
@@ -110,8 +110,9 @@ Options:
-l --loose -l --loose
Interpret versions and ranges loosely Interpret versions and ranges loosely
-n <0|1> -n <0|1|false>
This is the base to be used for the prerelease identifier. Base number for prerelease identifier (default: 0).
Use false to omit the number altogether.
-p --include-prerelease -p --include-prerelease
Always include prerelease versions in range matching Always include prerelease versions in range matching
+1 -1
View File
@@ -105,7 +105,7 @@ const main = () => {
versions = versions.map((v) => { versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => { }).filter((v) => {
return semver.valid(v) return semver.valid(v, options)
}) })
if (!versions.length) { if (!versions.length) {
return fail() return fail()
+1 -1
View File
@@ -53,7 +53,7 @@ const diff = (version1, version2) => {
return prefix + 'patch' return prefix + 'patch'
} }
// high and low are preleases // high and low are prereleases
return 'prerelease' return 'prerelease'
} }
+2 -2
View File
@@ -78,8 +78,8 @@ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
// ## Pre-release Version Identifier // ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier. // A numeric identifier, or a non-numeric identifier.
// Non-numberic identifiers include numberic identifiers but can be longer. // Non-numeric identifiers include numeric identifiers but can be longer.
// Therefore non-numberic identifiers must go first. // Therefore non-numeric identifiers must go first.
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER] createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIER]})`) }|${src[t.NUMERICIDENTIFIER]})`)
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "semver", "name": "semver",
"version": "7.7.3", "version": "7.7.4",
"description": "The semantic version parser used by npm.", "description": "The semantic version parser used by npm.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -14,8 +14,8 @@
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
}, },
"devDependencies": { "devDependencies": {
"@npmcli/eslint-config": "^5.0.0", "@npmcli/eslint-config": "^6.0.0",
"@npmcli/template-oss": "4.25.1", "@npmcli/template-oss": "4.29.0",
"benchmark": "^2.1.4", "benchmark": "^2.1.4",
"tap": "^16.0.0" "tap": "^16.0.0"
}, },
@@ -52,7 +52,7 @@
"author": "GitHub Inc.", "author": "GitHub Inc.",
"templateOSS": { "templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "//@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", "engines": ">=10",
"distPaths": [ "distPaths": [
"classes/", "classes/",
+1 -1
View File
@@ -38,7 +38,7 @@ const compare = require('../functions/compare.js')
// - If LT // - If LT
// - If LT.semver is greater than any < or <= comp in C, return false // - 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 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 // - If no C has a prerelease and the LT.semver tuple, return false
// - Else return true // - Else return true
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "expo", "name": "expo",
"version": "54.0.32", "version": "54.0.33",
"description": "The Expo SDK", "description": "The Expo SDK",
"main": "src/Expo.ts", "main": "src/Expo.ts",
"module": "src/Expo.ts", "module": "src/Expo.ts",
@@ -75,7 +75,7 @@
"homepage": "https://github.com/expo/expo/tree/main/packages/expo", "homepage": "https://github.com/expo/expo/tree/main/packages/expo",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.0", "@babel/runtime": "^7.20.0",
"@expo/cli": "54.0.22", "@expo/cli": "54.0.23",
"@expo/config": "~12.0.13", "@expo/config": "~12.0.13",
"@expo/config-plugins": "~54.0.4", "@expo/config-plugins": "~54.0.4",
"@expo/devtools": "0.1.8", "@expo/devtools": "0.1.8",
@@ -124,5 +124,5 @@
"optional": true "optional": true
} }
}, },
"gitHead": "5205083d153ffa0c5e48db969aeaa1f9810453fc" "gitHead": "cfd65241fb73317b63e6736e7890a784a69940fc"
} }
BIN
View File
Binary file not shown.
+3 -2
View File
@@ -110,8 +110,9 @@ Options:
-l --loose -l --loose
Interpret versions and ranges loosely Interpret versions and ranges loosely
-n <0|1> -n <0|1|false>
This is the base to be used for the prerelease identifier. Base number for prerelease identifier (default: 0).
Use false to omit the number altogether.
-p --include-prerelease -p --include-prerelease
Always include prerelease versions in range matching Always include prerelease versions in range matching
@@ -105,7 +105,7 @@ const main = () => {
versions = versions.map((v) => { versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => { }).filter((v) => {
return semver.valid(v) return semver.valid(v, options)
}) })
if (!versions.length) { if (!versions.length) {
return fail() return fail()
@@ -53,7 +53,7 @@ const diff = (version1, version2) => {
return prefix + 'patch' return prefix + 'patch'
} }
// high and low are preleases // high and low are prereleases
return 'prerelease' return 'prerelease'
} }
@@ -78,8 +78,8 @@ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
// ## Pre-release Version Identifier // ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier. // A numeric identifier, or a non-numeric identifier.
// Non-numberic identifiers include numberic identifiers but can be longer. // Non-numeric identifiers include numeric identifiers but can be longer.
// Therefore non-numberic identifiers must go first. // Therefore non-numeric identifiers must go first.
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER] createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIER]})`) }|${src[t.NUMERICIDENTIFIER]})`)
@@ -1,6 +1,6 @@
{ {
"name": "semver", "name": "semver",
"version": "7.7.3", "version": "7.7.4",
"description": "The semantic version parser used by npm.", "description": "The semantic version parser used by npm.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -14,8 +14,8 @@
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
}, },
"devDependencies": { "devDependencies": {
"@npmcli/eslint-config": "^5.0.0", "@npmcli/eslint-config": "^6.0.0",
"@npmcli/template-oss": "4.25.1", "@npmcli/template-oss": "4.29.0",
"benchmark": "^2.1.4", "benchmark": "^2.1.4",
"tap": "^16.0.0" "tap": "^16.0.0"
}, },
@@ -52,7 +52,7 @@
"author": "GitHub Inc.", "author": "GitHub Inc.",
"templateOSS": { "templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "//@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", "engines": ">=10",
"distPaths": [ "distPaths": [
"classes/", "classes/",
@@ -38,7 +38,7 @@ const compare = require('../functions/compare.js')
// - If LT // - If LT
// - If LT.semver is greater than any < or <= comp in C, return false // - 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 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 // - If no C has a prerelease and the LT.semver tuple, return false
// - Else return true // - Else return true
@@ -1,7 +1,6 @@
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <worklets/AnimationFrameQueue/AnimationFrameBatchinator.h> #include <worklets/AnimationFrameQueue/AnimationFrameBatchinator.h>
#include <worklets/SharedItems/Serializable.h> #include <worklets/SharedItems/Serializable.h>
#include <worklets/WorkletRuntime/RuntimeBindings.h>
#include <atomic> #include <atomic>
#include <functional> #include <functional>
@@ -12,22 +11,26 @@
namespace worklets { namespace worklets {
void AnimationFrameBatchinator::addToBatch(jsi::Function &&callback) { void AnimationFrameBatchinator::addToBatch(
const facebook::jsi::Value &callback) {
{ {
std::lock_guard<std::mutex> lock(callbacksMutex_); 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(); flush();
} }
AnimationFrameBatchinator::JsiRequestAnimationFrame AnimationFrameBatchinator::getJsiRequestAnimationFrame() { AnimationFrameBatchinator::JsiRequestAnimationFrame
return [weakThis = weak_from_this()](facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback) { AnimationFrameBatchinator::getJsiRequestAnimationFrame() {
return [weakThis = weak_from_this()](
facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback) {
const auto strongThis = weakThis.lock(); const auto strongThis = weakThis.lock();
if (!strongThis) { if (!strongThis) {
return; return;
} }
strongThis->addToBatch(callback.asObject(rt).asFunction(rt)); strongThis->addToBatch(callback);
}; };
} }
@@ -45,21 +48,24 @@ void AnimationFrameBatchinator::flush() {
auto callbacks = strongThis->pullCallbacks(); auto callbacks = strongThis->pullCallbacks();
strongThis->flushRequested_ = false; strongThis->flushRequested_ = false;
auto &uiWorkletRuntime = strongThis->uiWorkletRuntime_; auto &uiRuntime = *(strongThis->uiRuntime_);
for (const auto &callback : callbacks) { 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_); std::lock_guard<std::mutex> lock(callbacksMutex_);
return std::move(callbacks_); return std::move(callbacks_);
} }
AnimationFrameBatchinator::AnimationFrameBatchinator( AnimationFrameBatchinator::AnimationFrameBatchinator(
const std::shared_ptr<WorkletRuntime> &uiWorkletRuntime, facebook::jsi::Runtime &uiRuntime,
RuntimeBindings::RequestAnimationFrame requestAnimationFrame) std::function<void(std::function<void(const double)>)>
: uiWorkletRuntime_(uiWorkletRuntime), requestAnimationFrame_(std::move(requestAnimationFrame)) {} &&forwardedRequestAnimationFrame)
: uiRuntime_(&uiRuntime),
requestAnimationFrame_(std::move(forwardedRequestAnimationFrame)) {}
} // namespace worklets } // namespace worklets
@@ -2,9 +2,6 @@
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <worklets/SharedItems/Serializable.h> #include <worklets/SharedItems/Serializable.h>
#include <worklets/WorkletRuntime/RuntimeBindings.h>
#include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <memory> #include <memory>
@@ -13,25 +10,28 @@
namespace worklets { namespace worklets {
class AnimationFrameBatchinator : public std::enable_shared_from_this<AnimationFrameBatchinator> { class AnimationFrameBatchinator
: public std::enable_shared_from_this<AnimationFrameBatchinator> {
public: 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(); JsiRequestAnimationFrame getJsiRequestAnimationFrame();
AnimationFrameBatchinator( AnimationFrameBatchinator(
const std::shared_ptr<WorkletRuntime> &uiWorkletRuntime, facebook::jsi::Runtime &uiRuntime,
RuntimeBindings::RequestAnimationFrame requestAnimationFrame); std::function<void(std::function<void(const double)>)>
&&forwardedRequestAnimationFrame);
private: private:
void flush(); 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::mutex callbacksMutex_{};
std::atomic_bool flushRequested_{}; std::atomic_bool flushRequested_{};
std::shared_ptr<WorkletRuntime> uiWorkletRuntime_; facebook::jsi::Runtime *uiRuntime_;
std::function<void(std::function<void(const double)>)> requestAnimationFrame_; std::function<void(std::function<void(const double)>)> requestAnimationFrame_;
}; };
@@ -14,10 +14,7 @@
#include <fbjni/fbjni.h> #include <fbjni/fbjni.h>
#endif // __ANDROID__ #endif // __ANDROID__
#include <memory>
#include <string>
#include <utility> #include <utility>
#include <vector>
using namespace facebook; using namespace facebook;
@@ -50,7 +47,7 @@ inline void scheduleOnUI(
const auto scope = jsi::Scope(uiWorkletRuntime->getJSIRuntime()); const auto scope = jsi::Scope(uiWorkletRuntime->getJSIRuntime());
#endif // JS_RUNTIME_HERMES #endif // JS_RUNTIME_HERMES
uiWorkletRuntime->runSync(serializableWorklet); uiWorkletRuntime->runGuarded(serializableWorklet);
}); });
} }
@@ -59,10 +56,7 @@ inline jsi::Value executeOnUIRuntimeSync(
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Value &worklet) { const jsi::Value &worklet) {
if (auto uiWorkletRuntime = weakUIWorkletRuntime.lock()) { if (auto uiWorkletRuntime = weakUIWorkletRuntime.lock()) {
auto serializableWorklet = extractSerializableOrThrow<SerializableWorklet>( return uiWorkletRuntime->executeSync(rt, worklet);
rt, worklet, "[Worklets] Only worklets can be executed on UI runtime.");
auto serializedResult = uiWorkletRuntime->runSyncSerialized(serializableWorklet);
return serializedResult->toJSValue(rt);
} }
return jsi::Value::undefined(); return jsi::Value::undefined();
} }
@@ -76,8 +70,8 @@ inline jsi::Value createWorkletRuntime(
std::shared_ptr<SerializableWorklet> &initializer, std::shared_ptr<SerializableWorklet> &initializer,
const std::shared_ptr<AsyncQueue> &queue, const std::shared_ptr<AsyncQueue> &queue,
bool enableEventLoop) { bool enableEventLoop) {
const auto workletRuntime = const auto workletRuntime = runtimeManager->createWorkletRuntime(
runtimeManager->createWorkletRuntime(std::move(jsiWorkletsModuleProxy), name, initializer, queue); jsiWorkletsModuleProxy, name, initializer, queue);
return jsi::Object::createFromHostObject(originRuntime, workletRuntime); return jsi::Object::createFromHostObject(originRuntime, workletRuntime);
} }
@@ -89,9 +83,10 @@ inline jsi::Value propagateModuleUpdate(
const auto runtimes = runtimeManager->getAllRuntimes(); const auto runtimes = runtimeManager->getAllRuntimes();
for (auto runtime : runtimes) { 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); const auto buffer = std::make_shared<jsi::StringBuffer>(code);
rt.evaluateJavaScript(buffer, sourceUrl); rt.evaluateJavaScript(buffer, sourceUrl);
return jsi::Value::undefined();
}); });
} }
return jsi::Value::undefined(); return jsi::Value::undefined();
@@ -105,11 +100,18 @@ inline jsi::Value reportFatalErrorOnJS(
const std::string &name, const std::string &name,
const std::string &jsEngine) { const std::string &jsEngine) {
JSLogger::reportFatalErrorOnJS( 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(); 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()) { if (!value.isObject()) {
return nullptr; return nullptr;
} }
@@ -125,33 +127,13 @@ inline std::shared_ptr<AsyncQueue> extractAsyncQueue(jsi::Runtime &rt, const jsi
return asyncQueue; 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( JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(
const bool isDevBundle, const bool isDevBundle,
const std::shared_ptr<const JSBigStringBuffer> &script, const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl, const std::string &sourceUrl,
const std::shared_ptr<MessageQueueThread> &jsQueue, const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<JSScheduler> &jsScheduler, const std::shared_ptr<JSScheduler> &jsScheduler,
const std::shared_ptr<UIScheduler> &uiScheduler, const std::shared_ptr<UIScheduler> &uiScheduler,
const std::shared_ptr<MemoryManager> &memoryManager,
const std::shared_ptr<RuntimeManager> &runtimeManager, const std::shared_ptr<RuntimeManager> &runtimeManager,
const std::weak_ptr<WorkletRuntime> &uiWorkletRuntime) const std::weak_ptr<WorkletRuntime> &uiWorkletRuntime)
: jsi::HostObject(), : jsi::HostObject(),
@@ -161,11 +143,11 @@ JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(
jsQueue_(jsQueue), jsQueue_(jsQueue),
jsScheduler_(jsScheduler), jsScheduler_(jsScheduler),
uiScheduler_(uiScheduler), uiScheduler_(uiScheduler),
memoryManager_(memoryManager),
runtimeManager_(runtimeManager), runtimeManager_(runtimeManager),
uiWorkletRuntime_(uiWorkletRuntime) {} uiWorkletRuntime_(uiWorkletRuntime) {}
JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(const JSIWorkletsModuleProxy &other) JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(
const JSIWorkletsModuleProxy &other)
: jsi::HostObject(), : jsi::HostObject(),
isDevBundle_(other.isDevBundle_), isDevBundle_(other.isDevBundle_),
script_(other.script_), script_(other.script_),
@@ -173,203 +155,315 @@ JSIWorkletsModuleProxy::JSIWorkletsModuleProxy(const JSIWorkletsModuleProxy &oth
jsQueue_(other.jsQueue_), jsQueue_(other.jsQueue_),
jsScheduler_(other.jsScheduler_), jsScheduler_(other.jsScheduler_),
uiScheduler_(other.uiScheduler_), uiScheduler_(other.uiScheduler_),
memoryManager_(other.memoryManager_),
runtimeManager_(other.runtimeManager_), runtimeManager_(other.runtimeManager_),
uiWorkletRuntime_(other.uiWorkletRuntime_) {} uiWorkletRuntime_(other.uiWorkletRuntime_) {}
JSIWorkletsModuleProxy::~JSIWorkletsModuleProxy() = default; 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; std::vector<jsi::PropNameID> propertyNames;
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializable")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableBigInt")); jsi::PropNameID::forAscii(rt, "createSerializable"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableBoolean")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableImport")); jsi::PropNameID::forAscii(rt, "createSerializableBigInt"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableNull")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableNumber")); jsi::PropNameID::forAscii(rt, "createSerializableBoolean"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableString")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableUndefined")); jsi::PropNameID::forAscii(rt, "createSerializableImport"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableHostObject")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableInitializer")); jsi::PropNameID::forAscii(rt, "createSerializableNull"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableArray")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableFunction")); jsi::PropNameID::forAscii(rt, "createSerializableNumber"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableTurboModuleLike")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableObject")); jsi::PropNameID::forAscii(rt, "createSerializableString"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableMap")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableSet")); jsi::PropNameID::forAscii(rt, "createSerializableUndefined"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSerializableWorklet")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createCustomSerializable")); jsi::PropNameID::forAscii(rt, "createSerializableHostObject"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "registerCustomSerializable")); 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, "scheduleOnUI"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "executeOnUIRuntimeSync")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createWorkletRuntime")); jsi::PropNameID::forAscii(rt, "executeOnUIRuntimeSync"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "scheduleOnRuntime")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "reportFatalErrorOnJS")); 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(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "setDynamicFeatureFlag")); jsi::PropNameID::forAscii(rt, "getStaticFeatureFlag"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "setDynamicFeatureFlag"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "createSynchronizable")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableGetDirty")); jsi::PropNameID::forAscii(rt, "createSynchronizable"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableGetBlocking")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableSetBlocking")); jsi::PropNameID::forAscii(rt, "synchronizableGetDirty"));
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableLock")); propertyNames.emplace_back(
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "synchronizableUnlock")); 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 #ifdef WORKLETS_BUNDLE_MODE
propertyNames.emplace_back(jsi::PropNameID::forAscii(rt, "propagateModuleUpdate")); propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "propagateModuleUpdate"));
#endif // WORKLETS_BUNDLE_MODE #endif // WORKLETS_BUNDLE_MODE
return propertyNames; 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); const auto name = propName.utf8(rt);
if (name == "createSerializable") { if (name == "createSerializable") {
return jsi::Function::createFromHostFunction( 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]); return makeSerializableClone(rt, args[0], args[1], args[2]);
}); });
} }
if (name == "createSerializableBigInt") { if (name == "createSerializableBigInt") {
return jsi::Function::createFromHostFunction( 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)); return makeSerializableBigInt(rt, args[0].asBigInt(rt));
}); });
} }
if (name == "createSerializableBoolean") { if (name == "createSerializableBoolean") {
return jsi::Function::createFromHostFunction( 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()); return makeSerializableBoolean(rt, args[0].asBool());
}); });
} }
if (name == "createSerializableImport") { if (name == "createSerializableImport") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableImport(rt, args[0].asNumber(), args[1].asString(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") { if (name == "createSerializableNumber") {
return jsi::Function::createFromHostFunction( 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()); return makeSerializableNumber(rt, args[0].asNumber());
}); });
} }
if (name == "createSerializableNull") { if (name == "createSerializableNull") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 0, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableNull(rt); propName,
}); 0,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) { return makeSerializableNull(rt); });
} }
if (name == "createSerializableString") { if (name == "createSerializableString") {
return jsi::Function::createFromHostFunction( 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)); return makeSerializableString(rt, args[0].asString(rt));
}); });
} }
if (name == "createSerializableUndefined") { if (name == "createSerializableUndefined") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 0, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableUndefined(rt); propName,
}); 0,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) { return makeSerializableUndefined(rt); });
} }
if (name == "createSerializableInitializer") { if (name == "createSerializableInitializer") {
return jsi::Function::createFromHostFunction( 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)); return makeSerializableInitializer(rt, args[0].asObject(rt));
}); });
} }
if (name == "createSerializableArray") { if (name == "createSerializableArray") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableArray(rt, args[0].asObject(rt).asArray(rt), args[1]); 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") { if (name == "createSerializableMap") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableMap(rt, args[0].asObject(rt).asArray(rt), args[1].asObject(rt).asArray(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") { if (name == "createSerializableSet") {
return jsi::Function::createFromHostFunction( 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)); return makeSerializableSet(rt, args[0].asObject(rt).asArray(rt));
}); });
} }
if (name == "createSerializableHostObject") { if (name == "createSerializableHostObject") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableHostObject(rt, args[0].asObject(rt).asHostObject(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") { if (name == "createSerializableFunction") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableFunction(rt, args[0].asObject(rt).asFunction(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") { if (name == "createSerializableTurboModuleLike") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 2, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableTurboModuleLike(rt, args[0].asObject(rt), args[1].asObject(rt).asHostObject(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") { if (name == "createSerializableObject") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
return makeSerializableObject(rt, args[0].getObject(rt), args[1].getBool(), args[2]); 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") { 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( return jsi::Function::createFromHostFunction(
rt, rt,
propName, propName,
4, 2,
[memoryManager = memoryManager_, runtimeManager = runtimeManager_]( [](jsi::Runtime &rt,
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { const jsi::Value &thisValue,
const auto determine = extractSerializableOrThrow<SerializableWorklet>( const jsi::Value *args,
rt, args[0], "[Worklets] Determine function must be a worklet."); size_t count) {
const auto pack = extractSerializableOrThrow<SerializableWorklet>( return makeSerializableWorklet(
rt, args[1], "[Worklets] Pack function must be a worklet."); rt, args[0].getObject(rt), args[1].getBool());
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();
}); });
} }
@@ -379,7 +473,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName, propName,
1, 1,
[uiScheduler = uiScheduler_, uiWorkletRuntime = uiWorkletRuntime_]( [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]); scheduleOnUI(uiScheduler, uiWorkletRuntime, rt, args[0]);
return jsi::Value::undefined(); return jsi::Value::undefined();
}); });
@@ -391,7 +488,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName, propName,
1, 1,
[uiWorkletRuntime = uiWorkletRuntime_]( [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]); return executeOnUIRuntimeSync(uiWorkletRuntime, rt, args[0]);
}); });
} }
@@ -402,10 +502,14 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName, propName,
5, 5,
[clone = std::make_shared<JSIWorkletsModuleProxy>(*this)]( [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); const auto name = args[0].asString(rt).utf8(rt);
auto serializableInitializer = 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(); const auto useDefaultQueue = args[2].asBool();
std::shared_ptr<AsyncQueue> asyncQueue; std::shared_ptr<AsyncQueue> asyncQueue;
@@ -431,7 +535,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "scheduleOnRuntime") { if (name == "scheduleOnRuntime") {
return jsi::Function::createFromHostFunction( 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]); worklets::scheduleOnRuntime(rt, args[0], args[1]);
return jsi::Value::undefined(); return jsi::Value::undefined();
}); });
@@ -443,7 +553,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName, propName,
4, 4,
[jsScheduler = jsScheduler_]( [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( return reportFatalErrorOnJS(
jsScheduler, jsScheduler,
/* message */ args[0].asString(rt).utf8(rt), /* message */ args[0].asString(rt).utf8(rt),
@@ -455,8 +568,15 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "createSynchronizable") { if (name == "createSynchronizable") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, propName, 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) { rt,
auto initial = extractSerializableOrThrow(rt, args[0], "[Worklets] Value must be a Serializable."); 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); auto synchronizable = std::make_shared<Synchronizable>(initial);
return SerializableJSRef::newNativeStateObject(rt, synchronizable); return SerializableJSRef::newNativeStateObject(rt, synchronizable);
}); });
@@ -464,7 +584,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableGetDirty") { if (name == "synchronizableGetDirty") {
return jsi::Function::createFromHostFunction( 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]); auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
return synchronizable->getDirty()->toJSValue(rt); return synchronizable->getDirty()->toJSValue(rt);
}); });
@@ -472,7 +598,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableGetBlocking") { if (name == "synchronizableGetBlocking") {
return jsi::Function::createFromHostFunction( 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]); auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
return synchronizable->getBlocking()->toJSValue(rt); return synchronizable->getBlocking()->toJSValue(rt);
}); });
@@ -480,17 +612,30 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableSetBlocking") { if (name == "synchronizableSetBlocking") {
return jsi::Function::createFromHostFunction( 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 synchronizable = extractSynchronizableOrThrow(rt, args[0]);
auto newValue = extractSerializableOrThrow(rt, args[1], "[Worklets] Value must be a Serializable."); auto newValue = extractSerializableOrThrow(
synchronizable->setBlocking(newValue); rt, args[1], "[Worklets] Value must be a Serializable.");
synchronizable->setBlocking(std::move(newValue));
return jsi::Value::undefined(); return jsi::Value::undefined();
}); });
} }
if (name == "synchronizableLock") { if (name == "synchronizableLock") {
return jsi::Function::createFromHostFunction( 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]); auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
synchronizable->lock(); synchronizable->lock();
return jsi::Value::undefined(); return jsi::Value::undefined();
@@ -499,7 +644,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "synchronizableUnlock") { if (name == "synchronizableUnlock") {
return jsi::Function::createFromHostFunction( 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]); auto synchronizable = extractSynchronizableOrThrow(rt, args[0]);
synchronizable->unlock(); synchronizable->unlock();
return jsi::Value::undefined(); return jsi::Value::undefined();
@@ -513,7 +664,10 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
propName, propName,
2, 2,
[runtimeManager = runtimeManager_]( [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( return propagateModuleUpdate(
runtimeManager, runtimeManager,
/* code */ args[0].asString(rt).utf8(rt), /* code */ args[0].asString(rt).utf8(rt),
@@ -524,7 +678,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "getStaticFeatureFlag") { if (name == "getStaticFeatureFlag") {
return jsi::Function::createFromHostFunction( 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( return worklets::StaticFeatureFlags::getFlag(
/* name */ args[0].asString(rt).utf8(rt)); /* name */ args[0].asString(rt).utf8(rt));
}); });
@@ -532,7 +692,13 @@ jsi::Value JSIWorkletsModuleProxy::get(jsi::Runtime &rt, const jsi::PropNameID &
if (name == "setDynamicFeatureFlag") { if (name == "setDynamicFeatureFlag") {
return jsi::Function::createFromHostFunction( 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( worklets::DynamicFeatureFlags::setFlag(
/* name */ args[0].asString(rt).utf8(rt), /* name */ args[0].asString(rt).utf8(rt),
/* value */ args[1].asBool()); /* value */ args[1].asBool());
@@ -5,7 +5,6 @@
#include <react/renderer/uimanager/UIManagerBinding.h> #include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/primitives.h> #include <react/renderer/uimanager/primitives.h>
#include <worklets/SharedItems/MemoryManager.h>
#include <worklets/SharedItems/Serializable.h> #include <worklets/SharedItems/Serializable.h>
#include <worklets/Tools/Defs.h> #include <worklets/Tools/Defs.h>
#include <worklets/WorkletRuntime/RuntimeManager.h> #include <worklets/WorkletRuntime/RuntimeManager.h>
@@ -31,12 +30,11 @@ class JSIWorkletsModuleProxy : public jsi::HostObject {
public: public:
explicit JSIWorkletsModuleProxy( explicit JSIWorkletsModuleProxy(
const bool isDevBundle, const bool isDevBundle,
const std::shared_ptr<const JSBigStringBuffer> &script, const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl, const std::string &sourceUrl,
const std::shared_ptr<MessageQueueThread> &jsQueue, const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<JSScheduler> &jsScheduler, const std::shared_ptr<JSScheduler> &jsScheduler,
const std::shared_ptr<UIScheduler> &uiScheduler, const std::shared_ptr<UIScheduler> &uiScheduler,
const std::shared_ptr<MemoryManager> &memoryManager,
const std::shared_ptr<RuntimeManager> &runtimeManager, const std::shared_ptr<RuntimeManager> &runtimeManager,
const std::weak_ptr<WorkletRuntime> &uiWorkletRuntime); const std::weak_ptr<WorkletRuntime> &uiWorkletRuntime);
@@ -64,7 +62,7 @@ class JSIWorkletsModuleProxy : public jsi::HostObject {
return isDevBundle_; return isDevBundle_;
} }
[[nodiscard]] std::shared_ptr<const JSBigStringBuffer> getScript() const { [[nodiscard]] std::shared_ptr<const BigStringBuffer> getScript() const {
return script_; return script_;
} }
@@ -72,22 +70,17 @@ class JSIWorkletsModuleProxy : public jsi::HostObject {
return sourceUrl_; return sourceUrl_;
} }
[[nodiscard]] std::shared_ptr<MemoryManager> getMemoryManager() const {
return memoryManager_;
}
[[nodiscard]] std::shared_ptr<RuntimeManager> getRuntimeManager() const { [[nodiscard]] std::shared_ptr<RuntimeManager> getRuntimeManager() const {
return runtimeManager_; return runtimeManager_;
} }
private: private:
const bool isDevBundle_; const bool isDevBundle_;
const std::shared_ptr<const JSBigStringBuffer> script_; const std::shared_ptr<const BigStringBuffer> script_;
const std::string sourceUrl_; const std::string sourceUrl_;
const std::shared_ptr<MessageQueueThread> jsQueue_; const std::shared_ptr<MessageQueueThread> jsQueue_;
const std::shared_ptr<JSScheduler> jsScheduler_; const std::shared_ptr<JSScheduler> jsScheduler_;
const std::shared_ptr<UIScheduler> uiScheduler_; const std::shared_ptr<UIScheduler> uiScheduler_;
const std::shared_ptr<MemoryManager> memoryManager_;
const std::shared_ptr<RuntimeManager> runtimeManager_; const std::shared_ptr<RuntimeManager> runtimeManager_;
const std::weak_ptr<WorkletRuntime> uiWorkletRuntime_; const std::weak_ptr<WorkletRuntime> uiWorkletRuntime_;
}; };
@@ -5,7 +5,6 @@
#include <worklets/RunLoop/AsyncQueueImpl.h> #include <worklets/RunLoop/AsyncQueueImpl.h>
#include <worklets/SharedItems/Serializable.h> #include <worklets/SharedItems/Serializable.h>
#include <worklets/Tools/Defs.h> #include <worklets/Tools/Defs.h>
#include <worklets/WorkletRuntime/RuntimeBindings.h>
#include <worklets/WorkletRuntime/UIRuntimeDecorator.h> #include <worklets/WorkletRuntime/UIRuntimeDecorator.h>
#ifdef __ANDROID__ #ifdef __ANDROID__
@@ -13,7 +12,6 @@
#endif // __ANDROID__ #endif // __ANDROID__
#include <memory> #include <memory>
#include <string>
#include <utility> #include <utility>
using namespace facebook; using namespace facebook;
@@ -28,36 +26,44 @@ WorkletsModuleProxy::WorkletsModuleProxy(
const std::shared_ptr<CallInvoker> &jsCallInvoker, const std::shared_ptr<CallInvoker> &jsCallInvoker,
const std::shared_ptr<UIScheduler> &uiScheduler, const std::shared_ptr<UIScheduler> &uiScheduler,
std::function<bool()> &&isJavaScriptThread, std::function<bool()> &&isJavaScriptThread,
RuntimeBindings runtimeBindings, std::function<void(std::function<void(const double)>)>
const std::shared_ptr<const JSBigStringBuffer> &script, &&forwardedRequestAnimationFrame,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl) const std::string &sourceUrl)
: isDevBundle_(isDevBundleFromRNRuntime(rnRuntime)), : isDevBundle_(isDevBundleFromRNRuntime(rnRuntime)),
jsQueue_(jsQueue), jsQueue_(jsQueue),
jsScheduler_(std::make_shared<JSScheduler>(rnRuntime, jsCallInvoker, std::move(isJavaScriptThread))), jsScheduler_(std::make_shared<JSScheduler>(
rnRuntime,
jsCallInvoker,
std::move(isJavaScriptThread))),
uiScheduler_(uiScheduler), uiScheduler_(uiScheduler),
jsLogger_(std::make_shared<JSLogger>(jsScheduler_)), jsLogger_(std::make_shared<JSLogger>(jsScheduler_)),
runtimeBindings_(std::move(runtimeBindings)),
script_(script), script_(script),
sourceUrl_(sourceUrl), sourceUrl_(sourceUrl),
memoryManager_(std::make_shared<MemoryManager>()),
runtimeManager_(std::make_shared<RuntimeManager>()), runtimeManager_(std::make_shared<RuntimeManager>()),
uiWorkletRuntime_( uiWorkletRuntime_(runtimeManager_->createUninitializedUIRuntime(
runtimeManager_->createUninitializedUIRuntime(jsQueue_, std::make_shared<AsyncQueueUI>(uiScheduler_))) { jsQueue_,
std::make_shared<AsyncQueueUI>(uiScheduler_))) {
/** /**
* We call additional `init` method here because * We call additional `init` method here because
* JSIWorkletsModuleProxy needs a weak_ptr to the UI Runtime. * JSIWorkletsModuleProxy needs a weak_ptr to the UI Runtime.
*/ */
uiWorkletRuntime_->init(createJSIWorkletsModuleProxy()); uiWorkletRuntime_->init(createJSIWorkletsModuleProxy());
animationFrameBatchinator_ = animationFrameBatchinator_ = std::make_shared<AnimationFrameBatchinator>(
std::make_shared<AnimationFrameBatchinator>(uiWorkletRuntime_, runtimeBindings_.requestAnimationFrame); uiWorkletRuntime_->getJSIRuntime(),
std::move(forwardedRequestAnimationFrame));
UIRuntimeDecorator::decorate( UIRuntimeDecorator::decorate(
uiWorkletRuntime_->getJSIRuntime(), animationFrameBatchinator_->getJsiRequestAnimationFrame()); uiWorkletRuntime_->getJSIRuntime(),
animationFrameBatchinator_->getJsiRequestAnimationFrame());
} }
std::shared_ptr<JSIWorkletsModuleProxy> WorkletsModuleProxy::createJSIWorkletsModuleProxy() const { std::shared_ptr<JSIWorkletsModuleProxy>
assert(uiWorkletRuntime_ && "UI Worklet Runtime must be initialized before creating JSI proxy."); WorkletsModuleProxy::createJSIWorkletsModuleProxy() const {
assert(
uiWorkletRuntime_ &&
"UI Worklet Runtime must be initialized before creating JSI proxy.");
return std::make_shared<JSIWorkletsModuleProxy>( return std::make_shared<JSIWorkletsModuleProxy>(
isDevBundle_, isDevBundle_,
script_, script_,
@@ -65,7 +71,6 @@ std::shared_ptr<JSIWorkletsModuleProxy> WorkletsModuleProxy::createJSIWorkletsMo
jsQueue_, jsQueue_,
jsScheduler_, jsScheduler_,
uiScheduler_, uiScheduler_,
memoryManager_,
runtimeManager_, runtimeManager_,
uiWorkletRuntime_); uiWorkletRuntime_);
} }
@@ -5,8 +5,6 @@
#include <jsireact/JSIExecutor.h> #include <jsireact/JSIExecutor.h>
#include <worklets/AnimationFrameQueue/AnimationFrameBatchinator.h> #include <worklets/AnimationFrameQueue/AnimationFrameBatchinator.h>
#include <worklets/NativeModules/JSIWorkletsModuleProxy.h> #include <worklets/NativeModules/JSIWorkletsModuleProxy.h>
#include <worklets/SharedItems/MemoryManager.h>
#include <worklets/Tools/Defs.h>
#include <worklets/Tools/JSLogger.h> #include <worklets/Tools/JSLogger.h>
#include <worklets/Tools/JSScheduler.h> #include <worklets/Tools/JSScheduler.h>
#include <worklets/Tools/SingleInstanceChecker.h> #include <worklets/Tools/SingleInstanceChecker.h>
@@ -19,7 +17,8 @@
namespace worklets { namespace worklets {
class WorkletsModuleProxy : public std::enable_shared_from_this<WorkletsModuleProxy> { class WorkletsModuleProxy
: public std::enable_shared_from_this<WorkletsModuleProxy> {
public: public:
explicit WorkletsModuleProxy( explicit WorkletsModuleProxy(
jsi::Runtime &rnRuntime, 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<CallInvoker> &jsCallInvoker,
const std::shared_ptr<UIScheduler> &uiScheduler, const std::shared_ptr<UIScheduler> &uiScheduler,
std::function<bool()> &&isJavaScriptQueue, std::function<bool()> &&isJavaScriptQueue,
RuntimeBindings runtimeBindings, std::function<void(std::function<void(const double)>)>
const std::shared_ptr<const JSBigStringBuffer> &script, &&forwardedRequestAnimationFrame,
const std::shared_ptr<const BigStringBuffer> &script,
const std::string &sourceUrl); const std::string &sourceUrl);
~WorkletsModuleProxy(); ~WorkletsModuleProxy();
@@ -49,11 +49,13 @@ class WorkletsModuleProxy : public std::enable_shared_from_this<WorkletsModulePr
return jsLogger_; return jsLogger_;
} }
[[nodiscard]] inline std::shared_ptr<WorkletRuntime> getUIWorkletRuntime() const { [[nodiscard]] inline std::shared_ptr<WorkletRuntime> getUIWorkletRuntime()
const {
return uiWorkletRuntime_; return uiWorkletRuntime_;
} }
[[nodiscard]] std::shared_ptr<JSIWorkletsModuleProxy> createJSIWorkletsModuleProxy() const; [[nodiscard]] std::shared_ptr<JSIWorkletsModuleProxy>
createJSIWorkletsModuleProxy() const;
[[nodiscard]] inline bool isDevBundle() const { [[nodiscard]] inline bool isDevBundle() const {
return isDevBundle_; 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<JSScheduler> jsScheduler_;
const std::shared_ptr<UIScheduler> uiScheduler_; const std::shared_ptr<UIScheduler> uiScheduler_;
const std::shared_ptr<JSLogger> jsLogger_; const std::shared_ptr<JSLogger> jsLogger_;
const RuntimeBindings runtimeBindings_; const std::shared_ptr<const BigStringBuffer> script_;
const std::shared_ptr<const JSBigStringBuffer> script_;
const std::string sourceUrl_; const std::string sourceUrl_;
const std::shared_ptr<MemoryManager> memoryManager_;
const std::shared_ptr<RuntimeManager> runtimeManager_; const std::shared_ptr<RuntimeManager> runtimeManager_;
std::shared_ptr<WorkletRuntime> uiWorkletRuntime_; std::shared_ptr<WorkletRuntime> uiWorkletRuntime_;
std::shared_ptr<AnimationFrameBatchinator> animationFrameBatchinator_; std::shared_ptr<AnimationFrameBatchinator> animationFrameBatchinator_;
@@ -1,14 +1,13 @@
#include <worklets/Registries/EventHandlerRegistry.h> #include <worklets/Registries/EventHandlerRegistry.h>
#include <worklets/Tools/WorkletEventHandler.h> #include <worklets/Tools/WorkletEventHandler.h>
#include <memory>
#include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace worklets { 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 std::lock_guard<std::mutex> lock(instanceMutex);
const auto &eventName = eventHandler->getEventName(); const auto &eventName = eventHandler->getEventName();
auto handlerId = eventHandler->getHandlerId(); auto handlerId = eventHandler->getHandlerId();
@@ -62,27 +61,30 @@ void EventHandlerRegistry::processEvent(
const std::lock_guard<std::mutex> lock(instanceMutex); const std::lock_guard<std::mutex> lock(instanceMutex);
auto handlersIt = eventMappingsWithoutTag.find(eventName); auto handlersIt = eventMappingsWithoutTag.find(eventName);
if (handlersIt != eventMappingsWithoutTag.end()) { if (handlersIt != eventMappingsWithoutTag.end()) {
for (const auto &handler : handlersIt->second) { for (auto handler : handlersIt->second) {
handlersForEvent.push_back(handler.second); handlersForEvent.push_back(handler.second);
} }
} }
const auto eventHash = std::make_pair(emitterReactTag, eventName); const auto eventHash = std::make_pair(emitterReactTag, eventName);
auto handlersWithTagIt = eventMappingsWithTag.find(eventHash); auto handlersWithTagIt = eventMappingsWithTag.find(eventHash);
if (handlersWithTagIt != eventMappingsWithTag.end()) { if (handlersWithTagIt != eventMappingsWithTag.end()) {
for (const auto &handler : handlersWithTagIt->second) { for (auto handler : handlersWithTagIt->second) {
handlersForEvent.push_back(handler.second); handlersForEvent.push_back(handler.second);
} }
} }
} }
jsi::Runtime &rt = uiWorkletRuntime->getJSIRuntime(); jsi::Runtime &rt = uiWorkletRuntime->getJSIRuntime();
eventPayload.asObject(rt).setProperty(rt, "eventName", jsi::String::createFromUtf8(rt, eventName)); eventPayload.asObject(rt).setProperty(
for (const auto &handler : handlersForEvent) { rt, "eventName", jsi::String::createFromUtf8(rt, eventName));
for (auto handler : handlersForEvent) {
handler->process(uiWorkletRuntime, eventTimestamp, eventPayload); 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 std::lock_guard<std::mutex> lock(instanceMutex);
const auto eventHash = std::make_pair(emitterReactTag, eventName); const auto eventHash = std::make_pair(emitterReactTag, eventName);
auto it = eventMappingsWithTag.find(eventHash); auto it = eventMappingsWithTag.find(eventHash);
@@ -18,14 +18,20 @@ namespace worklets {
class WorkletEventHandler; class WorkletEventHandler;
class EventHandlerRegistry { 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; 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::map<uint64_t, std::shared_ptr<WorkletEventHandler>> eventHandlers;
std::mutex instanceMutex; std::mutex instanceMutex;
public: public:
void registerEventHandler(const std::shared_ptr<WorkletEventHandler> &eventHandler); void registerEventHandler(
const std::shared_ptr<WorkletEventHandler> &eventHandler);
void unregisterEventHandler(const uint64_t id); void unregisterEventHandler(const uint64_t id);
void processEvent( void processEvent(
@@ -35,7 +41,9 @@ class EventHandlerRegistry {
const int emitterReactTag, const int emitterReactTag,
const jsi::Value &eventPayload); const jsi::Value &eventPayload);
bool isAnyHandlerWaitingForEvent(const std::string &eventName, const int emitterReactTag); bool isAnyHandlerWaitingForEvent(
const std::string &eventName,
const int emitterReactTag);
}; };
} // namespace worklets } // namespace worklets
@@ -1,7 +1,5 @@
#include <worklets/Registries/WorkletRuntimeRegistry.h> #include <worklets/Registries/WorkletRuntimeRegistry.h>
#include <set>
namespace worklets { namespace worklets {
std::set<jsi::Runtime *> WorkletRuntimeRegistry::registry_{}; 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 { namespace worklets {
extern const char ValueUnpackerCode[]; extern const char ValueUnpackerCode[];
extern const char SynchronizableUnpackerCode[]; extern const char SynchronizableUnpackerCode[];
extern const char CustomSerializableUnpackerCode[];
} // namespace worklets } // namespace worklets
@@ -40,7 +40,7 @@ const char ValueUnpackerCode[] =
} else if (category === 'RemoteFunction') { } else if (category === 'RemoteFunction') {
var fun = function fun() { var fun = function fun() {
var label = remoteFunctionName ? "function `".concat(remoteFunctionName, "`") : 'anonymous function'; 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; fun.__remoteFunction = objectToUnpack;
return fun; 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 <worklets/RunLoop/AsyncQueueImpl.h>
#include <memory>
#include <string>
#include <thread> #include <thread>
#include <utility> #include <utility>
namespace worklets { 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_] { auto thread = std::thread([name, state = state_] {
#if __APPLE__ #if __APPLE__
pthread_setname_np(name.c_str()); pthread_setname_np(name.c_str());
#endif #endif
while (state->running) { while (state->running) {
std::unique_lock<std::mutex> lock(state->mutex); 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) { if (!state->running) {
return; return;
} }
@@ -50,7 +50,8 @@ void AsyncQueueImpl::push(std::function<void()> &&job) {
state_->cv.notify_one(); 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) { void AsyncQueueUI::push(std::function<void()> &&job) {
uiScheduler_->scheduleOnUI(std::move(job)); uiScheduler_->scheduleOnUI(std::move(job));
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <worklets/RunLoop/AsyncQueue.h> #include <worklets/Public/AsyncQueue.h>
#include <worklets/Tools/UIScheduler.h> #include <worklets/Tools/UIScheduler.h>
#include <atomic> #include <atomic>
@@ -21,7 +21,7 @@ struct AsyncQueueState {
class AsyncQueueImpl : public AsyncQueue { class AsyncQueueImpl : public AsyncQueue {
public: public:
explicit AsyncQueueImpl(const std::string &name); explicit AsyncQueueImpl(std::string name);
~AsyncQueueImpl() override; ~AsyncQueueImpl() override;
@@ -1,7 +1,5 @@
#include <worklets/RunLoop/EventLoop.h> #include <worklets/RunLoop/EventLoop.h>
#include <memory>
#include <string>
#include <thread> #include <thread>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -10,9 +8,12 @@ namespace worklets {
EventLoop::EventLoop( EventLoop::EventLoop(
const std::string &name, const std::string &name,
const std::shared_ptr<jsi::Runtime> &runtime, const std::shared_ptr<jsi::Runtime> runtime,
const std::shared_ptr<AsyncQueue> &queue) 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() { EventLoop::~EventLoop() {
{ {
@@ -25,50 +26,52 @@ EventLoop::~EventLoop() {
void EventLoop::run() { void EventLoop::run() {
const auto threadName = name_ + "(timeout)"; 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__ #if __APPLE__
pthread_setname_np(threadName.c_str()); pthread_setname_np(threadName.c_str());
#endif #endif
while (state->running) { while (state->running) {
std::unique_lock<std::mutex> lock(state->mutex); std::unique_lock<std::mutex> lock(state->mutex);
if (state->queue.empty()) { if (state->queue.empty()) {
state->cv.wait(lock); state->cv.wait(lock);
} else { } else {
const auto &nextTimeout = state->queue[0]; const auto &nextTimeout = state->queue[0];
const auto timeToWait = nextTimeout.targetTime - getCurrentTimeInMs(); const auto timeToWait =
state->cv.wait_for(lock, std::chrono::milliseconds(timeToWait)); nextTimeout.targetTime - getCurrentTimeInMs();
} state->cv.wait_for(lock, std::chrono::milliseconds(timeToWait));
}
// Early return if the Event Loop got destroyed already. // Early return if the Event Loop got destroyed already.
if (!state->running) { if (!state->running) {
return; return;
} }
const auto currentTime = getCurrentTimeInMs(); const auto currentTime = getCurrentTimeInMs();
std::vector<std::function<void(jsi::Runtime & rt)>> jobs; std::vector<std::function<void(jsi::Runtime & rt)>> jobs;
auto &timeouts = state->queue; auto &timeouts = state->queue;
timeouts.erase( timeouts.erase(
std::remove_if( std::remove_if(
timeouts.begin(), timeouts.begin(),
timeouts.end(), timeouts.end(),
[currentTime, &jobs](const Timeout &timeout) { [currentTime, &jobs](const Timeout &timeout) {
if (currentTime >= timeout.targetTime) { if (currentTime >= timeout.targetTime) {
jobs.emplace_back(timeout.callback); jobs.emplace_back(timeout.callback);
return true; return true;
} }
return false; return false;
}), }),
timeouts.end()); timeouts.end());
lock.unlock(); lock.unlock();
if (auto strongThis = weakThis.lock()) { if (auto strongThis = weakThis.lock()) {
for (auto &job : jobs) { for (auto &job : jobs) {
strongThis->pushTask(std::move(job)); strongThis->pushTask(std::move(job));
}
}
} }
} });
}
});
#ifdef ANDROID #ifdef ANDROID
pthread_setname_np(thread.native_handle(), threadName.c_str()); pthread_setname_np(thread.native_handle(), threadName.c_str());
#endif #endif
@@ -76,14 +79,17 @@ void EventLoop::run() {
} }
void EventLoop::pushTask(std::function<void(jsi::Runtime &rt)> &&job) { 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()) { if (auto runtime = weakRuntime.lock()) {
job(*runtime); 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); std::unique_lock<std::mutex> lock(timeoutsQueueState_->mutex);
const auto targetTime = getCurrentTimeInMs() + delay; 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() { int64_t EventLoop::getCurrentTimeInMs() {
const auto currentTime = std::chrono::system_clock::now().time_since_epoch(); 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 } // namespace worklets
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <worklets/RunLoop/AsyncQueue.h> #include <worklets/Public/AsyncQueue.h>
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
@@ -33,7 +33,7 @@ class EventLoop : public std::enable_shared_from_this<EventLoop> {
public: public:
EventLoop( EventLoop(
const std::string &name, const std::string &name,
const std::shared_ptr<jsi::Runtime> &runtime, const std::shared_ptr<jsi::Runtime> runtime,
const std::shared_ptr<AsyncQueue> &queue); const std::shared_ptr<AsyncQueue> &queue);
~EventLoop(); ~EventLoop();
void run(); 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 <jsi/jsi.h>
#include <worklets/SharedItems/Serializable.h> #include <worklets/SharedItems/Serializable.h>
#include <memory>
#include <string>
#include <utility>
using namespace facebook; using namespace facebook;
namespace worklets { namespace worklets {
@@ -15,6 +11,34 @@ jsi::Function getValueUnpacker(jsi::Runtime &rt) {
return valueUnpacker.asObject(rt).asFunction(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::Value makeSerializableClone(
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Value &value, const jsi::Value &value,
@@ -34,26 +58,35 @@ jsi::Value makeSerializableClone(
return makeSerializableFunction(rt, object.asFunction(rt)); return makeSerializableFunction(rt, object.asFunction(rt));
} else if (object.isArray(rt)) { } else if (object.isArray(rt)) {
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) { 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 { } else {
serializable = std::make_shared<SerializableArray>(rt, object.asArray(rt)); serializable =
std::make_shared<SerializableArray>(rt, object.asArray(rt));
} }
} else if (object.isArrayBuffer(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)) { } else if (object.isHostObject(rt)) {
if (object.isHostObject<SerializableJSRef>(rt)) { if (object.isHostObject<SerializableJSRef>(rt)) {
return object; return object;
} }
serializable = std::make_shared<SerializableHostObject>(rt, object.getHostObject(rt)); serializable = std::make_shared<SerializableHostObject>(
rt, object.getHostObject(rt));
} else { } else {
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) { if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
serializable = std::make_shared<RetainingSerializable<SerializableObject>>(rt, object, nativeStateSource); serializable =
std::make_shared<RetainingSerializable<SerializableObject>>(
rt, object, nativeStateSource);
} else { } else {
serializable = std::make_shared<SerializableObject>(rt, object, nativeStateSource); serializable =
std::make_shared<SerializableObject>(rt, object, nativeStateSource);
} }
} }
} else if (value.isString()) { } 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()) { } else if (value.isUndefined()) {
serializable = std::make_shared<SerializableScalar>(); serializable = std::make_shared<SerializableScalar>();
} else if (value.isNull()) { } else if (value.isNull()) {
@@ -63,21 +96,25 @@ jsi::Value makeSerializableClone(
} else if (value.isNumber()) { } else if (value.isNumber()) {
serializable = std::make_shared<SerializableScalar>(value.getNumber()); serializable = std::make_shared<SerializableScalar>(value.getNumber());
} else if (value.isBigInt()) { } 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()) { } else if (value.isSymbol()) {
// TODO: this is only a placeholder implementation, here we replace symbols // 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 // 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. // 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 { } 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); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
jsi::Value makeSerializableString(jsi::Runtime &rt, const jsi::String &string) { 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); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
@@ -106,42 +143,58 @@ jsi::Value makeSerializableNull(jsi::Runtime &rt) {
return SerializableJSRef::newNativeStateObject(rt, serializable); 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; std::shared_ptr<Serializable> serializable;
if (shouldRetainRemote) { if (shouldRetainRemote) {
serializable = std::make_shared<RetainingSerializable<SerializableWorklet>>(rt, object); serializable = std::make_shared<RetainingSerializable<SerializableWorklet>>(
rt, object);
} else { } else {
serializable = std::make_shared<SerializableWorklet>(rt, object); serializable = std::make_shared<SerializableWorklet>(rt, object);
} }
return SerializableJSRef::newNativeStateObject(rt, serializable); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
jsi::Value makeSerializableInitializer(jsi::Runtime &rt, const jsi::Object &initializerObject) { jsi::Value makeSerializableInitializer(
const auto serializable = std::make_shared<SerializableInitializer>(rt, initializerObject); jsi::Runtime &rt,
const jsi::Object &initializerObject) {
const auto serializable =
std::make_shared<SerializableInitializer>(rt, initializerObject);
return SerializableJSRef::newNativeStateObject(rt, serializable); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
jsi::Value makeSerializableFunction(jsi::Runtime &rt, jsi::Function function) { jsi::Value makeSerializableFunction(jsi::Runtime &rt, jsi::Function function) {
std::shared_ptr<Serializable> serializable; std::shared_ptr<Serializable> serializable;
if (function.isHostFunction(rt)) { if (function.isHostFunction(rt)) {
serializable = std::make_shared<SerializableHostFunction>(rt, std::move(function)); serializable =
std::make_shared<SerializableHostFunction>(rt, std::move(function));
} else { } else {
serializable = std::make_shared<SerializableRemoteFunction>(rt, std::move(function)); serializable =
std::make_shared<SerializableRemoteFunction>(rt, std::move(function));
} }
return SerializableJSRef::newNativeStateObject(rt, serializable); 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; std::shared_ptr<Serializable> serializable;
if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) { if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
serializable = std::make_shared<RetainingSerializable<SerializableArray>>(rt, array); serializable =
std::make_shared<RetainingSerializable<SerializableArray>>(rt, array);
} else { } else {
serializable = std::make_shared<SerializableArray>(rt, array); serializable = std::make_shared<SerializableArray>(rt, array);
} }
return SerializableJSRef::newNativeStateObject(rt, serializable); 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); auto serializable = std::make_shared<SerializableMap>(rt, keys, values);
return SerializableJSRef::newNativeStateObject(rt, serializable); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
@@ -151,7 +204,9 @@ jsi::Value makeSerializableSet(jsi::Runtime &rt, const jsi::Array &values) {
return SerializableJSRef::newNativeStateObject(rt, serializable); 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); const auto serializable = std::make_shared<SerializableHostObject>(rt, value);
return SerializableJSRef::newNativeStateObject(rt, serializable); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
@@ -160,12 +215,17 @@ jsi::Value makeSerializableTurboModuleLike(
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Object &object, const jsi::Object &object,
const std::shared_ptr<jsi::HostObject> &proto) { 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); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
jsi::Value makeSerializableImport(jsi::Runtime &rt, const double source, const jsi::String &imported) { jsi::Value makeSerializableImport(
auto serializable = std::make_shared<SerializableImport>(rt, source, imported); jsi::Runtime &rt,
const double source,
const jsi::String &imported) {
auto serializable =
std::make_shared<SerializableImport>(rt, source, imported);
return SerializableJSRef::newNativeStateObject(rt, serializable); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
@@ -176,9 +236,11 @@ jsi::Value makeSerializableObject(
const jsi::Value &nativeStateSource) { const jsi::Value &nativeStateSource) {
std::shared_ptr<Serializable> serializable; std::shared_ptr<Serializable> serializable;
if (shouldRetainRemote) { if (shouldRetainRemote) {
serializable = std::make_shared<RetainingSerializable<SerializableObject>>(rt, object, nativeStateSource); serializable = std::make_shared<RetainingSerializable<SerializableObject>>(
rt, object, nativeStateSource);
} else { } else {
serializable = std::make_shared<SerializableObject>(rt, object, nativeStateSource); serializable =
std::make_shared<SerializableObject>(rt, object, nativeStateSource);
} }
return SerializableJSRef::newNativeStateObject(rt, serializable); return SerializableJSRef::newNativeStateObject(rt, serializable);
} }
@@ -193,14 +255,15 @@ std::shared_ptr<Serializable> extractSerializableOrThrow(
auto nativeState = object.getNativeState(rt); auto nativeState = object.getNativeState(rt);
return std::dynamic_pointer_cast<SerializableJSRef>(nativeState)->value(); 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()) { } else if (maybeSerializableValue.isUndefined()) {
return Serializable::undefined(); return Serializable::undefined();
} }
throw std::runtime_error(errorMessage); throw std::runtime_error(errorMessage);
} }
Serializable::~Serializable() = default; Serializable::~Serializable() {}
std::shared_ptr<Serializable> Serializable::undefined() { std::shared_ptr<Serializable> Serializable::undefined() {
static auto undefined = std::make_shared<SerializableScalar>(); static auto undefined = std::make_shared<SerializableScalar>();
@@ -230,13 +293,15 @@ jsi::Value RetainingSerializable<BaseClass>::toJSValue(jsi::Runtime &rt) {
return BaseClass::toJSValue(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); auto size = array.size(rt);
data_.reserve(size); data_.reserve(size);
for (size_t i = 0; i < size; i++) { 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) { jsi::Value SerializableArrayBuffer::toJSValue(jsi::Runtime &rt) {
auto size = static_cast<int>(data_.size()); auto size = static_cast<int>(data_.size());
auto arrayBuffer = auto arrayBuffer = rt.global()
rt.global().getPropertyAsFunction(rt, "ArrayBuffer").callAsConstructor(rt, size).getObject(rt).getArrayBuffer(rt); .getPropertyAsFunction(rt, "ArrayBuffer")
.callAsConstructor(rt, size)
.getObject(rt)
.getArrayBuffer(rt);
memcpy(arrayBuffer.data(rt), data_.data(), size); memcpy(arrayBuffer.data(rt), data_.data(), size);
return arrayBuffer; return arrayBuffer;
} }
SerializableObject::SerializableObject(jsi::Runtime &rt, const jsi::Object &object) SerializableObject::SerializableObject(
: Serializable(ValueType::ObjectType) { jsi::Runtime &rt,
const jsi::Object &object)
: Serializable(ObjectType) {
auto propertyNames = object.getPropertyNames(rt); auto propertyNames = object.getPropertyNames(rt);
auto size = propertyNames.size(rt); auto size = propertyNames.size(rt);
data_.reserve(size); 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) { : 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); nativeState_ = nativeStateSource.asObject(rt).getNativeState(rt);
} }
} }
jsi::Value SerializableObject::toJSValue(jsi::Runtime &rt) { jsi::Value SerializableObject::toJSValue(jsi::Runtime &rt) {
auto obj = jsi::Object(rt); auto obj = jsi::Object(rt);
for (const auto &i : data_) { for (size_t i = 0, size = data_.size(); i < size; i++) {
obj.setProperty(rt, jsi::String::createFromUtf8(rt, i.first), i.second->toJSValue(rt)); obj.setProperty(
rt,
jsi::String::createFromUtf8(rt, data_[i].first),
data_[i].second->toJSValue(rt));
} }
if (nativeState_ != nullptr) { if (nativeState_ != nullptr) {
obj.setNativeState(rt, nativeState_); obj.setNativeState(rt, nativeState_);
@@ -290,10 +367,15 @@ jsi::Value SerializableObject::toJSValue(jsi::Runtime &rt) {
return obj; return obj;
} }
SerializableMap::SerializableMap(jsi::Runtime &rt, const jsi::Array &keys, const jsi::Array &values) SerializableMap::SerializableMap(
: Serializable(ValueType::MapType) { jsi::Runtime &rt,
const jsi::Array &keys,
const jsi::Array &values)
: Serializable(MapType) {
auto size = keys.size(rt); 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); data_.reserve(size);
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
auto key = extractSerializableOrThrow(rt, keys.getValueAtIndex(rt, 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(); 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; 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); auto size = values.size(rt);
data_.reserve(size); data_.reserve(size);
for (size_t i = 0; i < size; i++) { 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(); 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; return set;
} }
@@ -342,15 +428,20 @@ jsi::Value SerializableHostObject::toJSValue(jsi::Runtime &rt) {
} }
jsi::Value SerializableHostFunction::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) { jsi::Value SerializableWorklet::toJSValue(jsi::Runtime &rt) {
react_native_assert( 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"); "SerializableWorklet doesn't have `__workletHash` property");
jsi::Value obj = SerializableObject::toJSValue(rt); 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) { 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_); 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) { 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) { jsi::Value SerializableInitializer::toJSValue(jsi::Runtime &rt) {
if (remoteValue_ == nullptr) { if (remoteValue_ == nullptr) {
auto initObj = initializer_->toJSValue(rt); auto initObj = initializer_->toJSValue(rt);
auto value = std::make_unique<jsi::Value>( auto value = std::make_unique<jsi::Value>(getValueUnpacker(rt).call(
getValueUnpacker(rt).call(rt, initObj, jsi::String::createFromAscii(rt, "Handle"))); rt, initObj, jsi::String::createFromAscii(rt, "Handle")));
// We are locking the initialization here since the thread that is // We are locking the initialization here since the thread that is
// initializing can be preempted on runtime lock. E.g. // initalizing can be pre-empted on runtime lock. E.g.
// UI thread can be preempted on initialization of a shared value and then // 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. // 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 // If we put the lock on `getValueUnpacker` part (basically any part that
// requires runtime) we would get a deadlock since UI thread would never // 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_); return jsi::Value(rt, *remoteValue_);
} }
auto initObj = initializer_->toJSValue(rt); 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) { 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) { jsi::Value SerializableBigInt::toJSValue(jsi::Runtime &rt) {
if (fastValue_.has_value()) { return rt.global()
return jsi::BigInt::fromInt64(rt, fastValue_.value()); .getPropertyAsFunction(rt, "BigInt")
} else { .call(rt, jsi::String::createFromUtf8(rt, string_));
return rt.global().getPropertyAsFunction(rt, "BigInt").call(rt, jsi::String::createFromUtf8(rt, slowValue_));
}
} }
jsi::Value SerializableScalar::toJSValue(jsi::Runtime &) { jsi::Value SerializableScalar::toJSValue(jsi::Runtime &) {
switch (valueType_) { switch (valueType_) {
case Serializable::ValueType::UndefinedType: case Serializable::UndefinedType:
return jsi::Value(); return jsi::Value();
case Serializable::ValueType::NullType: case Serializable::NullType:
return jsi::Value(nullptr); return jsi::Value(nullptr);
case Serializable::ValueType::BooleanType: case Serializable::BooleanType:
return jsi::Value(data_.boolean); return jsi::Value(data_.boolean);
case Serializable::ValueType::NumberType: case Serializable::NumberType:
return jsi::Value(data_.number); return jsi::Value(data_.number);
default: 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) { jsi::Value SerializableTurboModuleLike::toJSValue(jsi::Runtime &rt) {
auto obj = properties_->toJSValue(rt).asObject(rt); auto obj = properties_->toJSValue(rt).asObject(rt);
const auto prototype = proto_->toJSValue(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; 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 } // namespace worklets
@@ -15,7 +15,30 @@ namespace worklets {
jsi::Function getValueUnpacker(jsi::Runtime &rt); 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)) { if (rt != nullptr && !WorkletRuntimeRegistry::isRuntimeAlive(rt)) {
// The below use of unique_ptr.release prevents the smart pointer from // The below use of unique_ptr.release prevents the smart pointer from
// calling the destructor of the kept object. This effectively results in // 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 // 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 // jsi::Value refers to is managed by the VM and gets freed along with the
// runtime. // runtime.
value.release(); // NOLINT value.release();
} }
} }
@@ -45,7 +68,7 @@ class Serializable {
virtual ~Serializable(); virtual ~Serializable();
enum class ValueType : std::uint8_t { enum ValueType {
UndefinedType, UndefinedType,
NullType, NullType,
BooleanType, BooleanType,
@@ -66,7 +89,6 @@ class Serializable {
TurboModuleLikeType, TurboModuleLikeType,
ImportType, ImportType,
SynchronizableType, SynchronizableType,
CustomType,
}; };
explicit Serializable(ValueType valueType) : valueType_(valueType) {} explicit Serializable(ValueType valueType) : valueType_(valueType) {}
@@ -105,15 +127,18 @@ class SerializableJSRef : public jsi::NativeState {
const std::shared_ptr<Serializable> value_; const std::shared_ptr<Serializable> value_;
public: 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 { std::shared_ptr<Serializable> value() const {
return value_; 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); auto object = jsi::Object(rt);
object.setNativeState(rt, std::make_shared<SerializableJSRef>(value)); object.setNativeState(rt, std::make_shared<SerializableJSRef>(value));
object.setProperty(rt, "__serializableRef", true); object.setProperty(rt, "__serializableRef", true);
@@ -150,35 +175,52 @@ jsi::Value makeSerializableObject(
bool shouldRetainRemote, bool shouldRetainRemote,
const jsi::Value &nativeStateSource); 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 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 makeSerializableFunction(jsi::Runtime &rt, jsi::Function function);
jsi::Value makeSerializableWorklet(jsi::Runtime &rt, const jsi::Object &object, const bool &shouldRetainRemote); jsi::Value makeSerializableWorklet(
jsi::Runtime &rt,
jsi::Value makeCustomSerializable(jsi::Runtime &rt, const jsi::Value &data, int typeId); const jsi::Object &object,
const bool &shouldRetainRemote);
std::shared_ptr<Serializable> extractSerializableOrThrow( std::shared_ptr<Serializable> extractSerializableOrThrow(
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Value &maybeSerializableValue, 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> template <typename T>
std::shared_ptr<T> extractSerializableOrThrow( std::shared_ptr<T> extractSerializableOrThrow(
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Value &serializableRef, const jsi::Value &serializableRef,
const std::string &errorMessage = "[Worklets] Provided serializable object is of an incompatible type.") { const std::string &errorMessage =
auto res = std::dynamic_pointer_cast<T>(extractSerializableOrThrow(rt, serializableRef, errorMessage)); "[Worklets] Provided serializable object is of an incompatible type.") {
auto res = std::dynamic_pointer_cast<T>(
extractSerializableOrThrow(rt, serializableRef, errorMessage));
if (!res) { if (!res) {
throw std::runtime_error(errorMessage); throw std::runtime_error(errorMessage);
} }
@@ -199,7 +241,10 @@ class SerializableObject : public Serializable {
public: public:
SerializableObject(jsi::Runtime &rt, const jsi::Object &object); 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; jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -210,12 +255,17 @@ class SerializableObject : public Serializable {
class SerializableMap : public Serializable { class SerializableMap : public Serializable {
public: 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; jsi::Value toJSValue(jsi::Runtime &rt) override;
protected: 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 { class SerializableSet : public Serializable {
@@ -230,8 +280,10 @@ class SerializableSet : public Serializable {
class SerializableHostObject : public Serializable { class SerializableHostObject : public Serializable {
public: public:
SerializableHostObject(jsi::Runtime &, const std::shared_ptr<jsi::HostObject> &hostObject) SerializableHostObject(
: Serializable(ValueType::HostObjectType), hostObject_(hostObject) {} jsi::Runtime &,
const std::shared_ptr<jsi::HostObject> &hostObject)
: Serializable(HostObjectType), hostObject_(hostObject) {}
jsi::Value toJSValue(jsi::Runtime &rt) override; jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -242,7 +294,7 @@ class SerializableHostObject : public Serializable {
class SerializableHostFunction : public Serializable { class SerializableHostFunction : public Serializable {
public: public:
SerializableHostFunction(jsi::Runtime &rt, jsi::Function function) SerializableHostFunction(jsi::Runtime &rt, jsi::Function function)
: Serializable(ValueType::HostFunctionType), : Serializable(HostFunctionType),
hostFunction_(function.getHostFunction(rt)), hostFunction_(function.getHostFunction(rt)),
name_(function.getProperty(rt, "name").asString(rt).utf8(rt)), name_(function.getProperty(rt, "name").asString(rt).utf8(rt)),
paramCount_(function.getProperty(rt, "length").asNumber()) {} paramCount_(function.getProperty(rt, "length").asNumber()) {}
@@ -258,8 +310,10 @@ class SerializableHostFunction : public Serializable {
class SerializableArrayBuffer : public Serializable { class SerializableArrayBuffer : public Serializable {
public: public:
SerializableArrayBuffer(jsi::Runtime &rt, const jsi::ArrayBuffer &arrayBuffer) SerializableArrayBuffer(jsi::Runtime &rt, const jsi::ArrayBuffer &arrayBuffer)
: Serializable(ValueType::ArrayBufferType), : Serializable(ArrayBufferType),
data_(arrayBuffer.data(rt), arrayBuffer.data(rt) + arrayBuffer.size(rt)) {} data_(
arrayBuffer.data(rt),
arrayBuffer.data(rt) + arrayBuffer.size(rt)) {}
jsi::Value toJSValue(jsi::Runtime &rt) override; jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -269,8 +323,9 @@ class SerializableArrayBuffer : public Serializable {
class SerializableWorklet : public SerializableObject { class SerializableWorklet : public SerializableObject {
public: public:
SerializableWorklet(jsi::Runtime &rt, const jsi::Object &worklet) : SerializableObject(rt, worklet) { SerializableWorklet(jsi::Runtime &rt, const jsi::Object &worklet)
valueType_ = ValueType::WorkletType; : SerializableObject(rt, worklet) {
valueType_ = WorkletType;
} }
jsi::Value toJSValue(jsi::Runtime &rt) override; jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -278,8 +333,13 @@ class SerializableWorklet : public SerializableObject {
class SerializableImport : public Serializable { class SerializableImport : public Serializable {
public: public:
SerializableImport(jsi::Runtime &rt, const double source, const jsi::String &imported) SerializableImport(
: Serializable(ValueType::ImportType), source_(source), imported_(imported.utf8(rt)) {} 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; jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -288,8 +348,9 @@ class SerializableImport : public Serializable {
const std::string imported_; const std::string imported_;
}; };
class SerializableRemoteFunction : public Serializable, class SerializableRemoteFunction
public std::enable_shared_from_this<SerializableRemoteFunction> { : public Serializable,
public std::enable_shared_from_this<SerializableRemoteFunction> {
private: private:
jsi::Runtime *runtime_; jsi::Runtime *runtime_;
#ifndef NDEBUG #ifndef NDEBUG
@@ -299,7 +360,7 @@ class SerializableRemoteFunction : public Serializable,
public: public:
SerializableRemoteFunction(jsi::Runtime &rt, jsi::Function &&function) SerializableRemoteFunction(jsi::Runtime &rt, jsi::Function &&function)
: Serializable(ValueType::RemoteFunctionType), : Serializable(RemoteFunctionType),
runtime_(&rt), runtime_(&rt),
#ifndef NDEBUG #ifndef NDEBUG
name_(function.getProperty(rt, "name").asString(rt).utf8(rt)), 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))) { function_(std::make_unique<jsi::Value>(rt, std::move(function))) {
} }
~SerializableRemoteFunction() override { ~SerializableRemoteFunction() {
cleanupIfRuntimeExists(runtime_, function_); cleanupIfRuntimeExists(runtime_, function_);
} }
@@ -326,11 +387,14 @@ class SerializableInitializer : public Serializable {
jsi::Runtime *remoteRuntime_; jsi::Runtime *remoteRuntime_;
public: public:
SerializableInitializer(jsi::Runtime &rt, const jsi::Object &initializerObject) SerializableInitializer(
: Serializable(ValueType::HandleType), jsi::Runtime &rt,
initializer_(std::make_unique<SerializableObject>(rt, initializerObject)) {} const jsi::Object &initializerObject)
: Serializable(HandleType),
initializer_(
std::make_unique<SerializableObject>(rt, initializerObject)) {}
~SerializableInitializer() override { ~SerializableInitializer() {
cleanupIfRuntimeExists(remoteRuntime_, remoteValue_); cleanupIfRuntimeExists(remoteRuntime_, remoteValue_);
} }
@@ -339,7 +403,8 @@ class SerializableInitializer : public Serializable {
class SerializableString : public Serializable { class SerializableString : public Serializable {
public: 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; jsi::Value toJSValue(jsi::Runtime &rt) override;
@@ -349,36 +414,27 @@ class SerializableString : public Serializable {
class SerializableBigInt : public Serializable { class SerializableBigInt : public Serializable {
public: public:
explicit SerializableBigInt(jsi::Runtime &rt, const jsi::BigInt &bigInt) : Serializable(ValueType::BigIntType) { explicit SerializableBigInt(jsi::Runtime &rt, const jsi::BigInt &bigint)
if (bigInt.isInt64(rt)) { : Serializable(BigIntType), string_(bigint.toString(rt).utf8(rt)) {}
fastValue_ = bigInt.getInt64(rt);
} else {
slowValue_ = bigInt.toString(rt).utf8(rt);
}
}
jsi::Value toJSValue(jsi::Runtime &rt) override; jsi::Value toJSValue(jsi::Runtime &rt) override;
protected: protected:
/** const std::string string_;
* This member is used only when the BigInt fits into int64_t range.
*/
std::optional<int64_t> fastValue_{};
std::string slowValue_{};
}; };
class SerializableScalar : public Serializable { class SerializableScalar : public Serializable {
public: public:
explicit SerializableScalar(double number) : Serializable(ValueType::NumberType) { explicit SerializableScalar(double number) : Serializable(NumberType) {
data_.number = number; data_.number = number;
} }
explicit SerializableScalar(bool boolean) : Serializable(ValueType::BooleanType) { explicit SerializableScalar(bool boolean) : Serializable(BooleanType) {
data_.boolean = boolean; data_.boolean = boolean;
} }
SerializableScalar() : Serializable(ValueType::UndefinedType) {} SerializableScalar() : Serializable(UndefinedType) {}
explicit SerializableScalar(std::nullptr_t) : Serializable(ValueType::NullType) {} explicit SerializableScalar(std::nullptr_t) : Serializable(NullType) {}
jsi::Value toJSValue(jsi::Runtime &) override; jsi::Value toJSValue(jsi::Runtime &);
protected: protected:
union Data { union Data {
@@ -396,7 +452,7 @@ class SerializableTurboModuleLike : public Serializable {
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Object &object, const jsi::Object &object,
const std::shared_ptr<jsi::HostObject> &proto) const std::shared_ptr<jsi::HostObject> &proto)
: Serializable(ValueType::TurboModuleLikeType), : Serializable(TurboModuleLikeType),
proto_(std::make_unique<SerializableHostObject>(rt, proto)), proto_(std::make_unique<SerializableHostObject>(rt, proto)),
properties_(std::make_unique<SerializableObject>(rt, object)) {} properties_(std::make_unique<SerializableObject>(rt, object)) {}
@@ -407,25 +463,4 @@ class SerializableTurboModuleLike : public Serializable {
const std::unique_ptr<SerializableObject> properties_; 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 } // namespace worklets
@@ -1,7 +1,6 @@
#include <react/debug/react_native_assert.h> #include <react/debug/react_native_assert.h>
#include <worklets/SharedItems/Synchronizable.h> #include <worklets/SharedItems/Synchronizable.h>
#include <memory>
#include <utility> #include <utility>
namespace worklets { namespace worklets {
@@ -33,7 +32,8 @@ void Synchronizable::setBlocking(const std::shared_ptr<Serializable> &value) {
jsi::Value Synchronizable::toJSValue(jsi::Runtime &rt) { jsi::Value Synchronizable::toJSValue(jsi::Runtime &rt) {
auto synchronizableUnpacker = getSynchronizableUnpacker(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)); return synchronizableUnpacker.call(rt, std::move(ref));
} }
@@ -41,17 +41,25 @@ Synchronizable::Synchronizable(const std::shared_ptr<Serializable> &value)
: Serializable(ValueType::SynchronizableType), value_(value) {} : Serializable(ValueType::SynchronizableType), value_(value) {}
jsi::Function getSynchronizableUnpacker(jsi::Runtime &rt) { jsi::Function getSynchronizableUnpacker(jsi::Runtime &rt) {
auto synchronizableUnpacker = rt.global().getProperty(rt, "__synchronizableUnpacker"); auto synchronizableUnpacker =
react_native_assert(synchronizableUnpacker.isObject() && "synchronizableUnpacker not found"); rt.global().getProperty(rt, "__synchronizableUnpacker");
react_native_assert(
synchronizableUnpacker.isObject() && "synchronizableUnpacker not found");
return synchronizableUnpacker.asObject(rt).asFunction(rt); return synchronizableUnpacker.asObject(rt).asFunction(rt);
} }
std::shared_ptr<Synchronizable> extractSynchronizableOrThrow(jsi::Runtime &rt, const jsi::Value &value) { std::shared_ptr<Synchronizable> extractSynchronizableOrThrow(
auto serializable = jsi::Runtime &rt,
extractSerializableOrThrow(rt, value, "[Worklets] Expecting the object to be of type SerializableJSRef."); 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); 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; return synchronizable;
} }
@@ -44,7 +44,7 @@ class Synchronizable : public SynchronizableAccess,
explicit Synchronizable(const std::shared_ptr<Serializable> &value); explicit Synchronizable(const std::shared_ptr<Serializable> &value);
~Synchronizable() override = default; virtual ~Synchronizable() = default;
private: private:
std::shared_ptr<Serializable> value_; std::shared_ptr<Serializable> value_;
@@ -52,6 +52,8 @@ class Synchronizable : public SynchronizableAccess,
jsi::Function getSynchronizableUnpacker(jsi::Runtime &rt); 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 }; // namespace worklets
@@ -6,7 +6,8 @@ namespace worklets {
void SynchronizableAccess::getBlockingBefore() { void SynchronizableAccess::getBlockingBefore() {
std::unique_lock<std::mutex> lock(accessLock_); std::unique_lock<std::mutex> lock(accessLock_);
queue_.wait(lock, [this]() { queue_.wait(lock, [this]() {
return !blockingWriter_ /* && dirtyWriters_ == 0 */ && (!imperativelyLocked_ || imperativeOwner_ == pthread_self()); return !blockingWriter_ /* && dirtyWriters_ == 0 */ &&
(!imperativelyLocked_ || imperativeOwner_ == pthread_self());
}); });
blockingReaders_++; blockingReaders_++;
} }
@@ -43,7 +44,8 @@ void SynchronizableAccess::getBlockingAfter() {
void SynchronizableAccess::setBlockingBefore() { void SynchronizableAccess::setBlockingBefore() {
std::unique_lock<std::mutex> lock(accessLock_); std::unique_lock<std::mutex> lock(accessLock_);
queue_.wait(lock, [this]() { queue_.wait(lock, [this]() {
return !blockingWriter_ && blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ && return !blockingWriter_ &&
blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ &&
(!imperativelyLocked_ || imperativeOwner_ == pthread_self()); (!imperativelyLocked_ || imperativeOwner_ == pthread_self());
}); });
blockingWriter_ = true; blockingWriter_ = true;
@@ -58,7 +60,8 @@ void SynchronizableAccess::setBlockingAfter() {
void SynchronizableAccess::lock() { void SynchronizableAccess::lock() {
std::unique_lock<std::mutex> lock(accessLock_); std::unique_lock<std::mutex> lock(accessLock_);
queue_.wait(lock, [this]() { queue_.wait(lock, [this]() {
return !blockingWriter_ && blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ && return !blockingWriter_ &&
blockingReaders_ == 0 /* && dirtyWriters_ == 0 */ &&
(!imperativelyLocked_ || imperativeOwner_ == pthread_self()); (!imperativelyLocked_ || imperativeOwner_ == pthread_self());
}); });
imperativelyLocked_ = true; imperativelyLocked_ = true;
@@ -8,15 +8,3 @@ so we have to check if headers are available.
#if __APPLE__ && __has_include(<hermes/hermes.h>) #if __APPLE__ && __has_include(<hermes/hermes.h>)
#define JS_RUNTIME_HERMES 1 #define JS_RUNTIME_HERMES 1
#endif #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 <worklets/Tools/FeatureFlags.h>
#include <string>
#include <unordered_map>
namespace worklets { namespace worklets {
std::unordered_map<std::string, bool> DynamicFeatureFlags::flags_; std::unordered_map<std::string, bool> DynamicFeatureFlags::flags_;
@@ -1,11 +1,8 @@
#include <worklets/Tools/JSISerializer.h> #include <worklets/Tools/JSISerializer.h>
#include <cxxabi.h> #include <cxxabi.h>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string>
#include <vector>
namespace worklets { namespace worklets {
@@ -63,23 +60,39 @@ const std::vector<std::string> SUPPORTED_REFLECTION_TYPES = {
"Proxy", "Proxy",
}; };
static inline std::string getObjectTypeName(jsi::Runtime &rt, const jsi::Object &object) { static inline std::string getObjectTypeName(
return object.getPropertyAsObject(rt, "constructor").getProperty(rt, "name").toString(rt).utf8(rt); 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; return getObjectTypeName(rt, object) == type;
} }
static inline bool static inline bool isInstanceOfAny(
isInstanceOfAny(jsi::Runtime &rt, const jsi::Object &object, const std::vector<std::string> &supportedTypes) { jsi::Runtime &rt,
const jsi::Object &object,
const std::vector<std::string> &supportedTypes) {
auto instanceType = getObjectTypeName(rt, object); 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) 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::string JSISerializer::stringifyWithName(const jsi::Object &object) {
std::stringstream ss; std::stringstream ss;
@@ -117,7 +130,8 @@ std::string JSISerializer::stringifyFunction(const jsi::Function &func) {
std::string JSISerializer::stringifyHostObject(jsi::HostObject &hostObject) { std::string JSISerializer::stringifyHostObject(jsi::HostObject &hostObject) {
int status = -1; 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) { if (status != 0) {
return "[jsi::HostObject]"; return "[jsi::HostObject]";
} }
@@ -135,7 +149,8 @@ std::string JSISerializer::stringifyHostObject(jsi::HostObject &hostObject) {
for (const auto &key : props) { for (const auto &key : props) {
auto formattedKey = key.utf8(rt_); auto formattedKey = key.utf8(rt_);
auto value = hostObject.get(rt_, key); auto value = hostObject.get(rt_, key);
ss << '"' << formattedKey << '"' << ": " << stringifyJSIValueRecursively(value); ss << '"' << formattedKey << '"' << ": "
<< stringifyJSIValueRecursively(value);
if (formattedKey != lastKey) { if (formattedKey != lastKey) {
ss << ", "; 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++) { for (size_t i = 0, propsCount = props.size(rt_); i < propsCount; i++) {
jsi::String propName = props.getValueAtIndex(rt_, i).toString(rt_); 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) { if (i != propsCount - 1) {
ss << ", "; ss << ", ";
} }
@@ -175,7 +191,9 @@ std::string JSISerializer::stringifyError(const jsi::Object &object) {
std::string JSISerializer::stringifySet(const jsi::Object &object) { std::string JSISerializer::stringifySet(const jsi::Object &object) {
std::stringstream ss; 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_); jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_);
if (!result.isArray(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::string JSISerializer::stringifyMap(const jsi::Object &object) {
std::stringstream ss; 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_); jsi::Object result = arrayFrom.call(rt_, object).asObject(rt_);
if (!result.isArray(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 pair = arr.getValueAtIndex(rt_, i).asObject(rt_).getArray(rt_);
auto key = pair.getValueAtIndex(rt_, 0); auto key = pair.getValueAtIndex(rt_, 0);
auto value = pair.getValueAtIndex(rt_, 1); auto value = pair.getValueAtIndex(rt_, 1);
ss << stringifyJSIValueRecursively(key) << ": " << stringifyJSIValueRecursively(value); ss << stringifyJSIValueRecursively(key) << ": "
<< stringifyJSIValueRecursively(value);
if (i != length - 1) { if (i != length - 1) {
ss << ", "; ss << ", ";
} }
@@ -237,15 +258,21 @@ std::string JSISerializer::stringifyRecursiveType(const jsi::Object &object) {
} }
std::string JSISerializer::stringifyWithToString(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()) { if (value.isBool() || value.isNumber()) {
return value.toString(rt_).utf8(rt_); return value.toString(rt_).utf8(rt_);
} }
if (value.isString()) { 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()) { if (value.isSymbol()) {
return value.getSymbol(rt_).toString(rt_); 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_STRUCTURED_DATA_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_MANAGING_MEMORY_TYPES) || isInstanceOfAny(rt_, object, SUPPORTED_MANAGING_MEMORY_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_ABSTRACTION_OBJECT_TYPES) || isInstanceOfAny(rt_, object, SUPPORTED_ABSTRACTION_OBJECT_TYPES) ||
isInstanceOfAny(rt_, object, SUPPORTED_REFLECTION_TYPES) || isInstanceOf(rt_, object, "Intl") || isInstanceOfAny(rt_, object, SUPPORTED_REFLECTION_TYPES) ||
isInstanceOf(rt_, object, "WeakMap") || isInstanceOf(rt_, object, "WeakSet")) { isInstanceOf(rt_, object, "Intl") ||
isInstanceOf(rt_, object, "WeakMap") ||
isInstanceOf(rt_, object, "WeakSet")) {
// TODO: Consider extending this log info // TODO: Consider extending this log info
return stringifyWithName(object); return stringifyWithName(object);
} }
if (isInstanceOf(rt_, object, "Date") || isInstanceOf(rt_, object, "RegExp")) { if (isInstanceOf(rt_, object, "Date") ||
isInstanceOf(rt_, object, "RegExp")) {
return stringifyWithToString(object); return stringifyWithToString(object);
} }
if (isInstanceOf(rt_, object, "Map")) { if (isInstanceOf(rt_, object, "Map")) {
@@ -11,7 +11,9 @@ namespace worklets {
class JSISerializer { class JSISerializer {
public: public:
explicit JSISerializer(jsi::Runtime &rt); 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: private:
std::string stringifyArray(const jsi::Array &arr); std::string stringifyArray(const jsi::Array &arr);
@@ -26,11 +28,14 @@ class JSISerializer {
std::string stringifyRecursiveType(const jsi::Object &object); std::string stringifyRecursiveType(const jsi::Object &object);
bool hasBeenVisited(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) { 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_; jsi::Runtime &rt_;
@@ -1,7 +1,6 @@
#include <worklets/Tools/JSLogger.h> #include <worklets/Tools/JSLogger.h>
#include <memory> #include <memory>
#include <string>
#include <utility> #include <utility>
namespace worklets { namespace worklets {
@@ -20,7 +19,8 @@ void JSLogger::reportFatalErrorOnJS(
const std::shared_ptr<JSScheduler> &jsScheduler, const std::shared_ptr<JSScheduler> &jsScheduler,
JSErrorData &&jsErrorData, JSErrorData &&jsErrorData,
bool force) { 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); reportFatalErrorOnJS(rnRuntime, jsErrorData, force);
}; };
if (jsScheduler->canInvokeSyncOnJS()) { 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 &global = rnRuntime.global();
const auto errorInstance = const auto errorInstance = rnRuntime.global()
rnRuntime.global().getPropertyAsFunction(rnRuntime, "Error").callAsConstructor(rnRuntime).asObject(rnRuntime); .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); reportFatalErrorFunction.call(rnRuntime, errorInstance, force);
} }
@@ -16,14 +16,20 @@ struct JSErrorData {
class JSLogger { class JSLogger {
public: 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; void warnOnJS(const std::string &warning) const;
static void static void reportFatalErrorOnJS(
reportFatalErrorOnJS(const std::shared_ptr<JSScheduler> &jsScheduler, JSErrorData &&jsErrorData, bool force = false); const std::shared_ptr<JSScheduler> &jsScheduler,
JSErrorData &&jsErrorData,
bool force = false);
private: 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_; const std::shared_ptr<JSScheduler> jsScheduler_;
}; };
@@ -6,15 +6,18 @@
namespace worklets { namespace worklets {
void JSScheduler::scheduleOnJS(Job job) { 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() { bool JSScheduler::canInvokeSyncOnJS() {
return isJavaScriptQueue_(); return isJavaScriptQueue_();
} }
void JSScheduler::invokeSyncOnJS(const Job &job) { void JSScheduler::invokeSyncOnJS(Job job) {
react_native_assert(canInvokeSyncOnJS() && "JSScheduler::invokeSyncOnJS should only be called from the JS thread"); react_native_assert(
canInvokeSyncOnJS() &&
"JSScheduler::invokeSyncOnJS should only be called from the JS thread");
job(rnRuntime_); job(rnRuntime_);
} }
@@ -18,11 +18,13 @@ class JSScheduler {
jsi::Runtime &rnRuntime, jsi::Runtime &rnRuntime,
const std::shared_ptr<CallInvoker> &jsCallInvoker, const std::shared_ptr<CallInvoker> &jsCallInvoker,
std::function<bool()> &&isJavaScriptQueue) 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 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(); bool canInvokeSyncOnJS();
@@ -28,7 +28,7 @@ class SingleInstanceChecker {
~SingleInstanceChecker(); ~SingleInstanceChecker();
private: private:
void assertWithMessage(bool condition, const std::string &message) { void assertWithMessage(bool condition, std::string message) {
if (!condition) { if (!condition) {
#ifdef ANDROID #ifdef ANDROID
__android_log_print(ANDROID_LOG_WARN, "Worklets", "%s", message.c_str()); __android_log_print(ANDROID_LOG_WARN, "Worklets", "%s", message.c_str());
@@ -50,7 +50,8 @@ class SingleInstanceChecker {
template <class T> template <class T>
SingleInstanceChecker<T>::SingleInstanceChecker() { SingleInstanceChecker<T>::SingleInstanceChecker() {
int status = 0; 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 // 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 // time. This happens during a reload when a new instance of React Native is
@@ -1,9 +1,7 @@
#include <worklets/Tools/VersionUtils.h> #include <worklets/Tools/VersionUtils.h>
#include <iostream> #include <iostream>
#include <memory>
#include <regex> #include <regex>
#include <string>
using namespace facebook; using namespace facebook;
@@ -11,7 +9,8 @@ namespace worklets {
bool matchVersion(const std::string &version1, const std::string &version2) { bool matchVersion(const std::string &version1, const std::string &version2) {
std::regex pattern("^\\d+\\.\\d+\\.\\d+$"); 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+"); auto majorPattern = std::regex("^\\d+");
std::smatch major1; std::smatch major1;
std::smatch major2; std::smatch major2;
@@ -64,7 +63,8 @@ void checkJSVersion(
libraryPrefix + libraryPrefix +
"Mismatch between C++ code version and " "Mismatch between C++ code version and "
"JavaScript code version (") + "JavaScript code version (") +
cppVersion + " vs. " + jsVersion + " respectively).\n" + "See " + docsBaseUrl + cppVersion + " vs. " + jsVersion + " respectively).\n" + "See " +
docsBaseUrl +
"/guides/" "/guides/"
"troubleshooting#mismatch-between-c-code-version-and-javascript-code-" "troubleshooting#mismatch-between-c-code-version-and-javascript-code-"
"version` for more details."); "version` for more details.");
@@ -1,15 +1,13 @@
#include <worklets/Tools/WorkletEventHandler.h> #include <worklets/Tools/WorkletEventHandler.h>
#include <memory>
#include <string>
namespace worklets { namespace worklets {
void WorkletEventHandler::process( void WorkletEventHandler::process(
const std::shared_ptr<WorkletRuntime> &workletRuntime, const std::shared_ptr<WorkletRuntime> &workletRuntime,
const double eventTimestamp, const double eventTimestamp,
const jsi::Value &eventValue) const { const jsi::Value &eventValue) const {
workletRuntime->runSync(handlerFunction_, jsi::Value(eventTimestamp), eventValue); workletRuntime->runGuarded(
handlerFunction_, jsi::Value(eventTimestamp), eventValue);
} }
uint64_t WorkletEventHandler::getHandlerId() const { uint64_t WorkletEventHandler::getHandlerId() const {
@@ -4,14 +4,16 @@
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <string>
#include <vector> #include <vector>
using namespace facebook; using namespace facebook;
namespace worklets::jsi_utils { 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::vector<float> transformMatrixList;
std::istringstream stringStream(value); std::istringstream stringStream(value);
std::copy( std::copy(
@@ -19,7 +21,8 @@ jsi::Array convertStringToArray(jsi::Runtime &rt, const std::string &value, cons
std::istream_iterator<float>(), std::istream_iterator<float>(),
std::back_inserter(transformMatrixList)); std::back_inserter(transformMatrixList));
react_native_assert( 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); jsi::Array matrix(rt, expectedSize);
for (unsigned int i = 0; i < expectedSize; i++) { for (unsigned int i = 0; i < expectedSize; i++) {
matrix.setValueAtIndex(rt, i, transformMatrixList[i]); matrix.setValueAtIndex(rt, i, transformMatrixList[i]);
@@ -27,10 +30,13 @@ jsi::Array convertStringToArray(jsi::Runtime &rt, const std::string &value, cons
return matrix; 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); auto optimizedObject = jsi::Object(rt);
for (const auto &propertyName : hostObject->getPropertyNames(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; return optimizedObject;
} }
@@ -40,7 +40,9 @@ inline jsi::Object get<jsi::Object>(jsi::Runtime &rt, const jsi::Value *value) {
} }
template <> 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; return *value;
} }
@@ -55,7 +57,9 @@ inline jsi::Value const &get<jsi::Value const &>(jsi::Runtime &, const jsi::Valu
// BEGIN implementations for `convertArgs` specializations. // BEGIN implementations for `convertArgs` specializations.
// specialization for empty `Targs` - returns an empty tuple // specialization for empty `Targs` - returns an empty tuple
template <typename... Args> 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(); 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` // then calls recursively on the rest of `args`
// and returns the concatenation of results // and returns the concatenation of results
template <typename T, typename... Rest> 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 arg = std::tuple<T>(get<T>(rt, args));
auto rest = convertArgs<Rest...>(rt, std::next(args)); auto rest = convertArgs<Rest...>(rt, std::next(args));
return std::tuple_cat(std::move(arg), std::move(rest)); 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 // returns a tuple with the result of casting `args` to appropriate
// native C++ types needed to call `function` // native C++ types needed to call `function`
template <typename Ret, typename... Args> template <typename Ret, typename... Args>
std::tuple<Args...> std::tuple<Args...> getArgsForFunction(
getArgsForFunction(std::function<Ret(Args...)>, jsi::Runtime &rt, const jsi::Value *args, const size_t count) { std::function<Ret(Args...)>,
react_native_assert(sizeof...(Args) == count && "Argument list has different length than expected"); 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); return convertArgs<Args...>(rt, args);
} }
@@ -88,27 +99,36 @@ std::tuple<jsi::Runtime &, Args...> getArgsForFunction(
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Value *args, const jsi::Value *args,
const size_t count) { 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)); return std::tuple_cat(std::tie(rt), convertArgs<Args...>(rt, args));
} }
// calls `function` with `args` // calls `function` with `args`
template <typename Ret, typename... 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)); return std::apply(function, std::move(args));
} }
// calls string-returning `function` with `args`, // calls string-returning `function` with `args`,
// and returns the string // and returns the string
template <typename... Args> 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))); return jsi::String::createFromUtf8(rt, std::apply(function, std::move(args)));
} }
// calls void-returning `function` with `args`, // calls void-returning `function` with `args`,
// and returns `undefined` // and returns `undefined`
template <typename... Args> 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)); std::apply(function, std::move(args));
return jsi::Value::undefined(); 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` // from a native function `function`
template <typename Fun> template <typename Fun>
jsi::HostFunctionType createHostFunction(Fun function) { 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); auto argz = getArgsForFunction(function, rt, args, count);
return apply(function, std::move(argz)); return apply(function, std::move(argz));
}; };
@@ -126,8 +150,13 @@ jsi::HostFunctionType createHostFunction(Fun function) {
// returns a function with JSI calling convention // returns a function with JSI calling convention
// from a native function `function` returning a string // from a native function `function` returning a string
template <typename... Args> template <typename... Args>
jsi::HostFunctionType createHostFunction(std::function<std::string(Args...)> function) { jsi::HostFunctionType createHostFunction(
return [function](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, const size_t count) { 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); auto argz = getArgsForFunction(function, rt, args, count);
return apply(rt, function, std::move(argz)); 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` // and installs it as a global function named `name`
// in the `rt` JS runtime // in the `rt` JS runtime
template <typename Ret, typename... Args> 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 clb = createHostFunction(function);
auto argsCount = sizeof...(Args) - takes_runtime<Args...>::value; auto argsCount = sizeof...(Args) - takes_runtime<Args...>::value;
jsi::Value jsiFunction = jsi::Value jsiFunction = jsi::Function::createFromHostFunction(
jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, name.data()), argsCount, clb); rt, jsi::PropNameID::forAscii(rt, name.data()), argsCount, clb);
rt.global().setProperty(rt, name.data(), jsiFunction); 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))); 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 } // namespace worklets::jsi_utils
@@ -31,7 +31,9 @@ namespace worklets {
struct WorkletsSystraceSection { struct WorkletsSystraceSection {
public: public:
template <typename... ConvertsToStringPiece> template <typename... ConvertsToStringPiece>
explicit WorkletsSystraceSection(const char *name, ConvertsToStringPiece &&...args) { explicit WorkletsSystraceSection(
const char *name,
ConvertsToStringPiece &&...args) {
ATrace_beginSection(name); ATrace_beginSection(name);
} }
@@ -43,7 +45,8 @@ struct WorkletsSystraceSection {
// The apple part is copied from React Native // The apple part is copied from React Native
// from // from
// https://github.com/facebook/react-native/blob/5697d923a05119314b4cfcd556cb243986637764/packages/react-native/ReactCommon/cxxreact/SystraceSection.h // 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> template <typename T, typename = void>
struct renderer { struct renderer {
@@ -55,7 +58,8 @@ struct renderer {
}; };
template <typename T> 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); return renderer<T>::render(t);
} }
@@ -63,7 +67,8 @@ inline os_log_t instrumentsLogHandle = nullptr;
static inline os_log_t getOrCreateInstrumentsLogHandle() { static inline os_log_t getOrCreateInstrumentsLogHandle() {
if (!instrumentsLogHandle) { 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; return instrumentsLogHandle;
} }
@@ -71,7 +76,9 @@ static inline os_log_t getOrCreateInstrumentsLogHandle() {
struct WorkletsSystraceSection { struct WorkletsSystraceSection {
public: public:
template <typename... ConvertsToStringPiece> template <typename... ConvertsToStringPiece>
explicit WorkletsSystraceSection(const char *name, ConvertsToStringPiece &&...args) { explicit WorkletsSystraceSection(
const char *name,
ConvertsToStringPiece &&...args) {
os_log_t instrumentsLogHandle = worklets::getOrCreateInstrumentsLogHandle(); os_log_t instrumentsLogHandle = worklets::getOrCreateInstrumentsLogHandle();
// If the log isn't enabled, we don't want the performance overhead of the // 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); 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() { ~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: private:
@@ -107,10 +125,12 @@ struct WorkletsSystraceSection {
struct WorkletsSystraceSection { struct WorkletsSystraceSection {
public: public:
template <typename... ConvertsToStringPiece> 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 && \ #endif // defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES &&
// defined(WORKLETS_PROFILING) // defined(WORKLETS_PROFILING)
} // namespace worklets } // namespace worklets
@@ -21,13 +21,19 @@ std::string getWorkletsCppVersion() {
void injectWorkletsCppVersion(jsi::Runtime &rnRuntime) { void injectWorkletsCppVersion(jsi::Runtime &rnRuntime) {
auto version = getWorkletsCppVersion(); 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 #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 cppVersion = getWorkletsCppVersion();
auto jsVersionValue = rnRuntime.global().getProperty(rnRuntime, "_WORKLETS_VERSION_JS"); auto jsVersionValue =
rnRuntime.global().getProperty(rnRuntime, "_WORKLETS_VERSION_JS");
worklets::checkJSVersion( worklets::checkJSVersion(
rnRuntime, rnRuntime,
jsVersionValue, jsVersionValue,
@@ -37,7 +43,9 @@ void checkJSVersion(jsi::Runtime &rnRuntime, const std::shared_ptr<worklets::JSL
"https://docs.swmansion.com/react-native-worklets/docs"); "https://docs.swmansion.com/react-native-worklets/docs");
} }
#else #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 // In release builds we don't check the version, hence
// this function is a NOOP. // this function is a NOOP.
} }
@@ -13,6 +13,8 @@ namespace worklets {
std::string getWorkletsCppVersion(); std::string getWorkletsCppVersion();
void injectWorkletsCppVersion(jsi::Runtime &); 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 }; // namespace worklets
@@ -1,10 +1,9 @@
#include <worklets/Tools/WorkletsJSIUtils.h>
#include <worklets/Tools/WorkletsVersion.h> #include <worklets/Tools/WorkletsVersion.h>
#include <worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h> #include <worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h>
#include <worklets/WorkletRuntime/RuntimeKind.h> #include <worklets/WorkletRuntime/RuntimeKind.h>
#include <worklets/WorkletRuntime/WorkletRuntimeCollector.h> #include <worklets/WorkletRuntime/WorkletRuntimeCollector.h>
#include <memory> #include <memory>
#include <utility> #include <utility>
namespace worklets { namespace worklets {
@@ -13,7 +12,10 @@ void RNRuntimeWorkletDecorator::decorate(
jsi::Runtime &rnRuntime, jsi::Runtime &rnRuntime,
jsi::Object &&jsiWorkletsModuleProxy, jsi::Object &&jsiWorkletsModuleProxy,
const std::shared_ptr<JSLogger> &jsLogger) { 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); rnRuntime.global().setProperty(rnRuntime, "_WORKLET", false);
@@ -21,7 +23,8 @@ void RNRuntimeWorkletDecorator::decorate(
// react-native-screens 4.9.0 depends on it // react-native-screens 4.9.0 depends on it
rnRuntime.global().setProperty(rnRuntime, "_IS_FABRIC", true); 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); WorkletRuntimeCollector::install(rnRuntime);
@@ -29,19 +32,7 @@ void RNRuntimeWorkletDecorator::decorate(
checkJSVersion(rnRuntime, jsLogger); checkJSVersion(rnRuntime, jsLogger);
#endif // NDEBUG #endif // NDEBUG
#ifdef IS_REANIMATED_EXAMPLE_APP
installDebugBindings(rnRuntime);
#endif // IS_REANIMATED_EXAMPLE_APP
injectWorkletsCppVersion(rnRuntime); 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 } // namespace worklets
@@ -12,13 +12,10 @@ namespace worklets {
class RNRuntimeWorkletDecorator { class RNRuntimeWorkletDecorator {
// TODO: Rename to `RNRuntimeWorkletsDecorator` or something more suitable. // TODO: Rename to `RNRuntimeWorkletsDecorator` or something more suitable.
public: public:
static void static void decorate(
decorate(jsi::Runtime &rnRuntime, jsi::Object &&jsiWorkletsModuleProxy, const std::shared_ptr<JSLogger> &jsLogger); jsi::Runtime &rnRuntime,
jsi::Object &&jsiWorkletsModuleProxy,
#ifdef IS_REANIMATED_EXAMPLE_APP const std::shared_ptr<JSLogger> &jsLogger);
private:
static void installDebugBindings(jsi::Runtime &rnRuntime);
#endif // IS_REANIMATED_EXAMPLE_APP
}; };
} // namespace worklets } // 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 <worklets/WorkletRuntime/RuntimeData.h>
#include <string>
namespace worklets {
namespace RuntimeData {
const std::string uiRuntimeName{"UI"}; const std::string uiRuntimeName{"UI"};
} // namespace RuntimeData
} // namespace worklets
@@ -1,23 +1,10 @@
#pragma once #pragma once
#include <jsi/jsi.h>
#include <string> #include <string>
namespace worklets {
namespace RuntimeData {
/** /**
* Unused, but kept for possible future use. * Unused, but kept for possible future use.
*/ */
constexpr uint64_t rnRuntimeId{0}; constexpr uint64_t rnRuntimeId{0};
constexpr uint64_t uiRuntimeId{1}; constexpr uint64_t uiRuntimeId{1};
extern const std::string uiRuntimeName; 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 #pragma once
#include <utility>
namespace worklets { namespace worklets {
/** /**
* Represents the different types of runtime environments available for * Represents the different types of runtime environments available for
* worklets. * worklets.
*/ */
enum class RuntimeKind : std::uint8_t { enum class RuntimeKind {
ReactNative = 1, ReactNative = 1,
UI = 2, UI = 2,
Worker = 3, Worker = 3,
@@ -1,10 +1,7 @@
#include <worklets/NativeModules/JSIWorkletsModuleProxy.h> #include <worklets/NativeModules/JSIWorkletsModuleProxy.h>
#include <worklets/WorkletRuntime/RuntimeManager.h> #include <worklets/WorkletRuntime/RuntimeManager.h>
#include <memory>
#include <string>
#include <utility> #include <utility>
#include <vector>
namespace worklets { namespace worklets {
@@ -16,6 +13,26 @@ std::shared_ptr<WorkletRuntime> RuntimeManager::getRuntime(uint64_t runtimeId) {
return nullptr; 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::vector<std::shared_ptr<WorkletRuntime>> RuntimeManager::getAllRuntimes() {
std::shared_lock lock(weakRuntimesMutex_); std::shared_lock lock(weakRuntimesMutex_);
@@ -32,27 +49,28 @@ std::vector<std::shared_ptr<WorkletRuntime>> RuntimeManager::getAllRuntimes() {
} }
std::shared_ptr<WorkletRuntime> RuntimeManager::getUIRuntime() { std::shared_ptr<WorkletRuntime> RuntimeManager::getUIRuntime() {
return getRuntime(RuntimeData::uiRuntimeId); return getRuntime(uiRuntimeId);
} }
std::shared_ptr<WorkletRuntime> RuntimeManager::createWorkletRuntime( std::shared_ptr<WorkletRuntime> RuntimeManager::createWorkletRuntime(
std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy, std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy,
const std::string &name, const std::string &name,
const std::shared_ptr<SerializableWorklet> &initializer, std::shared_ptr<SerializableWorklet> initializer,
const std::shared_ptr<AsyncQueue> &queue, const std::shared_ptr<AsyncQueue> &queue,
bool enableEventLoop) { bool enableEventLoop) {
const auto runtimeId = getNextRuntimeId(); const auto runtimeId = getNextRuntimeId();
const auto jsQueue = jsiWorkletsModuleProxy->getJSQueue(); 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)); workletRuntime->init(std::move(jsiWorkletsModuleProxy));
if (initializer) { if (initializer) {
workletRuntime->runSync(initializer); workletRuntime->runGuarded(initializer);
} }
registerRuntime(runtimeId, workletRuntime); registerRuntime(runtimeId, name, workletRuntime);
return workletRuntime; return workletRuntime;
} }
@@ -61,13 +79,9 @@ std::shared_ptr<WorkletRuntime> RuntimeManager::createUninitializedUIRuntime(
const std::shared_ptr<MessageQueueThread> &jsQueue, const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<AsyncQueue> &uiAsyncQueue) { const std::shared_ptr<AsyncQueue> &uiAsyncQueue) {
const auto uiRuntime = std::make_shared<WorkletRuntime>( const auto uiRuntime = std::make_shared<WorkletRuntime>(
RuntimeData::uiRuntimeId, uiRuntimeId, jsQueue, uiRuntimeName, uiAsyncQueue);
jsQueue,
RuntimeData::uiRuntimeName,
uiAsyncQueue,
/*enableEventLoop*/ false);
registerRuntime(RuntimeData::uiRuntimeId, uiRuntime); registerRuntime(uiRuntimeId, uiRuntimeName, uiRuntime);
return uiRuntime; return uiRuntime;
} }
@@ -76,18 +90,16 @@ uint64_t RuntimeManager::getNextRuntimeId() {
return nextRuntimeId_.fetch_add(1, std::memory_order_relaxed); return nextRuntimeId_.fetch_add(1, std::memory_order_relaxed);
} }
void RuntimeManager::registerRuntime(const uint64_t runtimeId, const std::shared_ptr<WorkletRuntime> &workletRuntime) { void RuntimeManager::registerRuntime(
std::unique_lock registrationLock(registrationMutex_); const uint64_t runtimeId,
const std::string &name,
const std::shared_ptr<WorkletRuntime> &workletRuntime) {
std::unique_lock lock(weakRuntimesMutex_); std::unique_lock lock(weakRuntimesMutex_);
weakRuntimes_[runtimeId] = workletRuntime; weakRuntimes_[runtimeId] = workletRuntime;
} nameToRuntimeId_[name] = runtimeId;
#ifdef WORKLETS_BUNDLE_MODE
void RuntimeManager::pause() { runtimeAddressToRuntimeId_[&workletRuntime->getJSIRuntime()] = runtimeId;
registrationMutex_.lock(); #endif // WORKLETS_BUNDLE_MODE
}
void RuntimeManager::resume() {
registrationMutex_.unlock();
} }
} // namespace worklets } // namespace worklets
@@ -23,6 +23,10 @@ class JSIWorkletsModuleProxy;
class RuntimeManager { class RuntimeManager {
public: public:
std::shared_ptr<WorkletRuntime> getRuntime(uint64_t runtimeId); 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(); std::vector<std::shared_ptr<WorkletRuntime>> getAllRuntimes();
@@ -31,7 +35,7 @@ class RuntimeManager {
std::shared_ptr<WorkletRuntime> createWorkletRuntime( std::shared_ptr<WorkletRuntime> createWorkletRuntime(
std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy, std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy,
const std::string &name, const std::string &name,
const std::shared_ptr<SerializableWorklet> &initializer = nullptr, std::shared_ptr<SerializableWorklet> initializer = nullptr,
const std::shared_ptr<AsyncQueue> &queue = nullptr, const std::shared_ptr<AsyncQueue> &queue = nullptr,
bool enableEventLoop = true); bool enableEventLoop = true);
@@ -39,21 +43,21 @@ class RuntimeManager {
const std::shared_ptr<MessageQueueThread> &jsQueue, const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::shared_ptr<AsyncQueue> &uiAsyncQueue); const std::shared_ptr<AsyncQueue> &uiAsyncQueue);
/** Pauses registration of new Worklet Runtimes. */
void pause();
/** Resumes registration of new Worklet Runtimes. */
void resume();
private: private:
uint64_t getNextRuntimeId(); 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::map<uint64_t, std::weak_ptr<WorkletRuntime>> weakRuntimes_;
std::shared_mutex weakRuntimesMutex_; 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 } // namespace worklets
@@ -8,12 +8,16 @@ namespace worklets {
void UIRuntimeDecorator::decorate( void UIRuntimeDecorator::decorate(
facebook::jsi::Runtime &uiRuntime, facebook::jsi::Runtime &uiRuntime,
std::function<void(facebook::jsi::Runtime &rt, const facebook::jsi::Value &callback)> &&requestAnimationFrame) { std::function<
uiRuntime.global().setProperty(uiRuntime, runtimeKindBindingName, static_cast<int>(RuntimeKind::UI)); 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); uiRuntime.global().setProperty(uiRuntime, "_UI", true);
jsi_utils::installJsiFunction(uiRuntime, "requestAnimationFrame", std::move(requestAnimationFrame)); jsi_utils::installJsiFunction(
uiRuntime, "requestAnimationFrame", std::move(requestAnimationFrame));
} }
} // namespace worklets } // namespace worklets
@@ -8,7 +8,9 @@ class UIRuntimeDecorator {
public: public:
static void decorate( static void decorate(
facebook::jsi::Runtime &uiRuntime, 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 } // namespace worklets
@@ -16,29 +16,25 @@ namespace worklets {
using namespace facebook; using namespace facebook;
using namespace react; using namespace react;
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #if HERMES_ENABLE_DEBUGGER
using namespace facebook::hermes::inspector_modern; using namespace facebook::hermes::inspector_modern;
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #endif // HERMES_ENABLE_DEBUGGER
#if HERMES_ENABLE_DEBUGGER #if HERMES_ENABLE_DEBUGGER
class HermesExecutorRuntimeAdapter class HermesExecutorRuntimeAdapter : public RuntimeAdapter {
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
: public RuntimeAdapter
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
{
public: public:
explicit HermesExecutorRuntimeAdapter( explicit HermesExecutorRuntimeAdapter(
facebook::hermes::HermesRuntime &hermesRuntime, facebook::hermes::HermesRuntime &hermesRuntime,
const std::shared_ptr<MessageQueueThread> &thread) const std::shared_ptr<MessageQueueThread> &thread)
: hermesRuntime_(hermesRuntime), thread_(thread) {} : hermesRuntime_(hermesRuntime), thread_(std::move(thread)) {}
virtual ~HermesExecutorRuntimeAdapter() { virtual ~HermesExecutorRuntimeAdapter() {
// This is required by iOS, because there is an assertion in the destructor // This is required by iOS, because there is an assertion in the destructor
// that the thread was indeed `quit` before // that the thread was indeed `quit` before
thread_->quitSynchronous(); thread_->quitSynchronous();
} }
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
facebook::hermes::HermesRuntime &getRuntime() override { facebook::hermes::HermesRuntime &getRuntime() override {
return hermesRuntime_; return hermesRuntime_;
} }
@@ -48,7 +44,6 @@ class HermesExecutorRuntimeAdapter
// required us to hold a refernce to the runtime inside this adapter which // required us to hold a refernce to the runtime inside this adapter which
// caused issues while reloading the app. // caused issues while reloading the app.
void tickleJs() override {} void tickleJs() override {}
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED)
public: public:
facebook::hermes::HermesRuntime &hermesRuntime_; facebook::hermes::HermesRuntime &hermesRuntime_;
@@ -61,11 +56,15 @@ WorkletHermesRuntime::WorkletHermesRuntime(
std::unique_ptr<facebook::hermes::HermesRuntime> runtime, std::unique_ptr<facebook::hermes::HermesRuntime> runtime,
const std::shared_ptr<MessageQueueThread> &jsQueue, const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::string &name) const std::string &name)
: jsi::WithRuntimeDecorator<WorkletsReentrancyCheck>(*runtime, reentrancyCheck_), runtime_(std::move(runtime)) { : jsi::WithRuntimeDecorator<WorkletsReentrancyCheck>(
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) *runtime,
auto adapter = std::make_unique<HermesExecutorRuntimeAdapter>(*runtime_, jsQueue); reentrancyCheck_),
runtime_(std::move(runtime)) {
#if HERMES_ENABLE_DEBUGGER
auto adapter =
std::make_unique<HermesExecutorRuntimeAdapter>(*runtime_, jsQueue);
debugToken_ = chrome::enableDebugging(std::move(adapter), name); debugToken_ = chrome::enableDebugging(std::move(adapter), name);
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #endif // HERMES_ENABLE_DEBUGGER
#ifndef NDEBUG #ifndef NDEBUG
facebook::hermes::HermesRuntime *wrappedRuntime = runtime_.get(); facebook::hermes::HermesRuntime *wrappedRuntime = runtime_.get();
@@ -74,27 +73,34 @@ WorkletHermesRuntime::WorkletHermesRuntime(
jsi::PropNameID::forAscii(*runtime_, "evalWithSourceMap"), jsi::PropNameID::forAscii(*runtime_, "evalWithSourceMap"),
3, 3,
[wrappedRuntime]( [wrappedRuntime](
jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value { jsi::Runtime &rt,
auto code = std::make_shared<const jsi::StringBuffer>(args[0].asString(rt).utf8(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; std::string sourceURL;
if (count > 1 && args[1].isString()) { if (count > 1 && args[1].isString()) {
sourceURL = args[1].asString(rt).utf8(rt); sourceURL = args[1].asString(rt).utf8(rt);
} }
std::shared_ptr<const jsi::Buffer> sourceMap; std::shared_ptr<const jsi::Buffer> sourceMap;
if (count > 2 && args[2].isString()) { 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 #endif // NDEBUG
} }
WorkletHermesRuntime::~WorkletHermesRuntime() { WorkletHermesRuntime::~WorkletHermesRuntime() {
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #if HERMES_ENABLE_DEBUGGER
// We have to disable debugging before the runtime is destroyed. // We have to disable debugging before the runtime is destroyed.
chrome::disableDebugging(debugToken_); chrome::disableDebugging(debugToken_);
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #endif // HERMES_ENABLE_DEBUGGER
} }
} // namespace worklets } // namespace worklets
@@ -17,17 +17,17 @@
#include <string> #include <string>
#include <thread> #include <thread>
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #if HERMES_ENABLE_DEBUGGER
#include <hermes/inspector-modern/chrome/Registration.h> #include <hermes/inspector-modern/chrome/Registration.h>
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #endif // HERMES_ENABLE_DEBUGGER
namespace worklets { namespace worklets {
using namespace facebook; using namespace facebook;
using namespace react; using namespace react;
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #if HERMES_ENABLE_DEBUGGER
using namespace facebook::hermes::inspector_modern; using namespace facebook::hermes::inspector_modern;
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #endif // HERMES_ENABLE_DEBUGGER
// ReentrancyCheck is copied from React Native // ReentrancyCheck is copied from React Native
// from ReactCommon/hermes/executor/HermesExecutorFactory.cpp // from ReactCommon/hermes/executor/HermesExecutorFactory.cpp
@@ -57,17 +57,20 @@ struct WorkletsReentrancyCheck {
// of this sort would be surprising, because the decorator would // of this sort would be surprising, because the decorator would
// need to call after() without before(). // 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 // Returns true if tid and expected were the same. If they
// were, then the stored tid referred to no thread, and we // were, then the stored tid referred to no thread, and we
// atomically saved this thread's tid. Now increment depth. // 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; ++depth;
} else if (expected == this_id) { } else if (expected == this_id) {
// If the stored tid referred to a thread, expected was set to // 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, // that value. If that value is this thread's tid, that's ok,
// just increment depth again. // 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; ++depth;
} else { } else {
// The stored tid was some other thread. This indicates a bad // The stored tid was some other thread. This indicates a bad
@@ -80,12 +83,15 @@ struct WorkletsReentrancyCheck {
void after() { void after() {
react_native_assert( 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 (--depth == 0) {
// If we decremented depth to zero, store no-thread into tid. // If we decremented depth to zero, store no-thread into tid.
std::thread::id expected = std::this_thread::get_id(); std::thread::id expected = std::this_thread::get_id();
bool didWrite = tid.compare_exchange_strong(expected, std::thread::id(), std::memory_order_relaxed); bool didWrite = tid.compare_exchange_strong(
react_native_assert(didWrite && "[Worklets] Decremented to zero, but no tid write"); 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 // WithRuntimeDecorator -> DecoratedRuntime -> jsi::Runtime You can find out
// more about this in ReactCommon/jsi/jsi/Decorator.h or by following this link: // 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 // 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: public:
WorkletHermesRuntime( WorkletHermesRuntime(
std::unique_ptr<facebook::hermes::HermesRuntime> runtime, std::unique_ptr<facebook::hermes::HermesRuntime> runtime,
const std::shared_ptr<MessageQueueThread> &jsQueue, const std::shared_ptr<MessageQueueThread> &jsQueue,
const std::string &name); const std::string &name);
~WorkletHermesRuntime() override; ~WorkletHermesRuntime();
private: private:
std::unique_ptr<facebook::hermes::HermesRuntime> runtime_; std::unique_ptr<facebook::hermes::HermesRuntime> runtime_;
WorkletsReentrancyCheck reentrancyCheck_; WorkletsReentrancyCheck reentrancyCheck_;
#if HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #if HERMES_ENABLE_DEBUGGER
chrome::DebugSessionToken debugToken_; chrome::DebugSessionToken debugToken_;
#endif // HERMES_ENABLE_DEBUGGER && !defined(HERMES_V1_ENABLED) #endif // HERMES_ENABLE_DEBUGGER
}; };
} // namespace worklets } // namespace worklets
@@ -4,7 +4,6 @@
#include <worklets/Tools/JSISerializer.h> #include <worklets/Tools/JSISerializer.h>
#include <worklets/Tools/JSLogger.h> #include <worklets/Tools/JSLogger.h>
#include <worklets/Tools/WorkletsJSIUtils.h> #include <worklets/Tools/WorkletsJSIUtils.h>
#include <worklets/WorkletRuntime/RuntimeHolder.h>
#include <worklets/WorkletRuntime/WorkletRuntime.h> #include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <worklets/WorkletRuntime/WorkletRuntimeCollector.h> #include <worklets/WorkletRuntime/WorkletRuntimeCollector.h>
#include <worklets/WorkletRuntime/WorkletRuntimeDecorator.h> #include <worklets/WorkletRuntime/WorkletRuntimeDecorator.h>
@@ -14,9 +13,7 @@
#include <jsi/jsi.h> #include <jsi/jsi.h>
#include <memory> #include <memory>
#include <string>
#include <utility> #include <utility>
#include <vector>
#if JS_RUNTIME_HERMES #if JS_RUNTIME_HERMES
#include <worklets/WorkletRuntime/WorkletHermesRuntime.h> #include <worklets/WorkletRuntime/WorkletHermesRuntime.h>
@@ -30,7 +27,8 @@ class AroundLock {
const std::shared_ptr<std::recursive_mutex> mutex_; const std::shared_ptr<std::recursive_mutex> mutex_;
public: 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 { void before() const {
mutex_->lock(); mutex_->lock();
@@ -61,7 +59,8 @@ static std::shared_ptr<jsi::Runtime> makeRuntime(
std::shared_ptr<jsi::Runtime> jsiRuntime; std::shared_ptr<jsi::Runtime> jsiRuntime;
#if JS_RUNTIME_HERMES #if JS_RUNTIME_HERMES
auto hermesRuntime = facebook::hermes::makeHermesRuntime(); 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 #else
jsiRuntime = facebook::jsc::makeJSCRuntime(); jsiRuntime = facebook::jsc::makeJSCRuntime();
#endif #endif
@@ -82,37 +81,37 @@ WorkletRuntime::WorkletRuntime(
queue_(queue) { queue_(queue) {
jsi::Runtime &rt = *runtime_; jsi::Runtime &rt = *runtime_;
WorkletRuntimeCollector::install(rt); WorkletRuntimeCollector::install(rt);
if (enableEventLoop) { if (enableEventLoop && name != uiRuntimeName) {
eventLoop_ = std::make_shared<EventLoop>(name_, runtime_, queue_); eventLoop_ = std::make_shared<EventLoop>(name_, runtime_, queue_);
eventLoop_->run(); eventLoop_->run();
} }
} }
void WorkletRuntime::init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy) { void WorkletRuntime::init(
std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy) {
jsi::Runtime &rt = *runtime_; 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 jsScheduler = jsiWorkletsModuleProxy->getJSScheduler();
const auto isDevBundle = jsiWorkletsModuleProxy->isDevBundle(); const auto isDevBundle = jsiWorkletsModuleProxy->isDevBundle();
const auto memoryManager_ = jsiWorkletsModuleProxy->getMemoryManager();
#ifdef WORKLETS_BUNDLE_MODE #ifdef WORKLETS_BUNDLE_MODE
auto script = jsiWorkletsModuleProxy->getScript(); auto script = jsiWorkletsModuleProxy->getScript();
const auto &sourceUrl = jsiWorkletsModuleProxy->getSourceUrl(); const auto &sourceUrl = jsiWorkletsModuleProxy->getSourceUrl();
#endif // WORKLETS_BUNDLE_MODE #endif // WORKLETS_BUNDLE_MODE
auto optimizedJsiWorkletsModuleProxy = jsi_utils::optimizedFromHostObject(rt, std::move(jsiWorkletsModuleProxy)); auto optimizedJsiWorkletsModuleProxy =
jsi_utils::optimizedFromHostObject(rt, std::move(jsiWorkletsModuleProxy));
WorkletRuntimeDecorator::decorate( WorkletRuntimeDecorator::decorate(
rt, name_, jsScheduler, isDevBundle, std::move(optimizedJsiWorkletsModuleProxy), eventLoop_); rt,
name_,
jsScheduler,
isDevBundle,
std::move(optimizedJsiWorkletsModuleProxy),
eventLoop_);
#ifdef WORKLETS_BUNDLE_MODE #ifdef WORKLETS_BUNDLE_MODE
if (!script) { 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 { try {
@@ -121,99 +120,87 @@ void WorkletRuntime::init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsMod
const auto &message = error.getMessage(); const auto &message = error.getMessage();
const auto &stack = error.getStack(); const auto &stack = error.getStack();
if (!message.starts_with("[Worklets] Worklets initialized successfully")) { 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( JSLogger::reportFatalErrorOnJS(
jsScheduler, {.message = newMessage, .stack = stack, .name = "WorkletsError", .jsEngine = "Worklets"}); jsScheduler,
{.message = newMessage,
.stack = stack,
.name = "WorkletsError",
.jsEngine = "Worklets"});
} }
} }
#else #else
// Legacy behavior // Legacy behavior
auto valueUnpackerBuffer = std::make_shared<const jsi::StringBuffer>(ValueUnpackerCode); auto valueUnpackerBuffer =
std::make_shared<const jsi::StringBuffer>(ValueUnpackerCode);
rt.evaluateJavaScript(valueUnpackerBuffer, "valueUnpacker"); 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"); rt.evaluateJavaScript(synchronizableUnpackerBuffer, "synchronizableUnpacker");
auto customSerializableUnpackerBuffer = std::make_shared<const jsi::StringBuffer>(CustomSerializableUnpackerCode);
rt.evaluateJavaScript(customSerializableUnpackerBuffer, "customSerializableUnpacker");
#endif // WORKLETS_BUNDLE_MODE #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::runAsyncGuarded(
const std::shared_ptr<SerializableWorklet> &worklet) {
void WorkletRuntime::schedule(jsi::Function &&function) const {
react_native_assert( react_native_assert(
queue_ && "[Worklets] Tried to invoke `runAsyncGuarded` on a Worklet Runtime but "
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " "the async queue is not set. Recreate the runtime with a valid async queue.");
"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()]() { queue_->push([worklet, weakThis = weak_from_this()] {
auto strongThis = weakThis.lock(); auto strongThis = weakThis.lock();
if (!strongThis) { if (!strongThis) {
return; return;
} }
strongThis->runSync(*function); strongThis->runGuarded(worklet);
}); });
} }
void WorkletRuntime::schedule(std::shared_ptr<SerializableWorklet> worklet) const { jsi::Value WorkletRuntime::executeSync(
react_native_assert( jsi::Runtime &rt,
queue_ && const jsi::Value &worklet) const {
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " auto serializableWorklet = extractSerializableOrThrow<SerializableWorklet>(
"async queue is not set. Recreate the runtime with a valid async queue."); rt,
worklet,
queue_->push([worklet = std::move(worklet), weakThis = weak_from_this()] { "[Worklets] Only worklets can be executed synchronously on UI runtime.");
auto strongThis = weakThis.lock(); auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
if (!strongThis) { jsi::Runtime &uiRuntime = getJSIRuntime();
return; auto result = runGuarded(serializableWorklet);
} auto serializableResult = extractSerializableOrThrow(uiRuntime, result);
lock.unlock();
strongThis->runSync(worklet); return serializableResult->toJSValue(rt);
});
} }
void WorkletRuntime::schedule(std::function<void()> job) const { #ifdef WORKLETS_BUNDLE_MODE
react_native_assert( jsi::Value WorkletRuntime::executeSync(
queue_ && std::function<jsi::Value(jsi::Runtime &)> &&job) const {
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
"async queue is not set. Recreate the runtime with a valid async queue."); jsi::Runtime &uiRuntime = getJSIRuntime();
return job(uiRuntime);
queue_->push(std::move(job));
} }
void WorkletRuntime::schedule(std::function<void(jsi::Runtime &)> job) const { jsi::Value WorkletRuntime::executeSync(
react_native_assert( const std::function<jsi::Value(jsi::Runtime &)> &job) const {
queue_ && auto lock = std::unique_lock<std::recursive_mutex>(*runtimeMutex_);
"[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " jsi::Runtime &uiRuntime = getJSIRuntime();
"async queue is not set. Recreate the runtime with a valid async queue."); return job(uiRuntime);
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);
});
} }
#endif // WORKLETS_BUNDLE_MODE
/* #endregion */ jsi::Value WorkletRuntime::get(
jsi::Runtime &rt,
jsi::Value WorkletRuntime::get(jsi::Runtime &rt, const jsi::PropNameID &propName) { const jsi::PropNameID &propName) {
auto name = propName.utf8(rt); auto name = propName.utf8(rt);
if (name == "toString") { if (name == "toString") {
return jsi::Function::createFromHostFunction( return jsi::Function::createFromHostFunction(
rt, rt,
propName, propName,
0, 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(); auto strongThis = weakThis.lock();
if (!strongThis) { if (!strongThis) {
return jsi::String::createFromUtf8(rt, ""); return jsi::String::createFromUtf8(rt, "");
@@ -228,14 +215,17 @@ jsi::Value WorkletRuntime::get(jsi::Runtime &rt, const jsi::PropNameID &propName
return jsi::Value::undefined(); 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; std::vector<jsi::PropNameID> result;
result.push_back(jsi::PropNameID::forUtf8(rt, "toString")); result.push_back(jsi::PropNameID::forUtf8(rt, "toString"));
result.push_back(jsi::PropNameID::forUtf8(rt, "name")); result.push_back(jsi::PropNameID::forUtf8(rt, "name"));
return result; 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); return value.getObject(rt).getHostObject<WorkletRuntime>(rt);
} }
@@ -248,68 +238,7 @@ void scheduleOnRuntime(
rt, rt,
serializableWorkletValue, serializableWorkletValue,
"[Worklets] Function passed to `_scheduleOnRuntime` is not a serializable worklet."); "[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 } // namespace worklets
@@ -5,7 +5,7 @@
#include <jsireact/JSIExecutor.h> #include <jsireact/JSIExecutor.h>
#include <react/debug/react_native_assert.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/AsyncQueueImpl.h>
#include <worklets/RunLoop/EventLoop.h> #include <worklets/RunLoop/EventLoop.h>
#include <worklets/SharedItems/Serializable.h> #include <worklets/SharedItems/Serializable.h>
@@ -14,7 +14,6 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -23,111 +22,14 @@ using namespace react;
namespace worklets { 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. * Forward declaration to avoid circular dependencies.
*/ */
class JSIWorkletsModuleProxy; 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: 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( explicit WorkletRuntime(
uint64_t runtimeId, uint64_t runtimeId,
const std::shared_ptr<MessageQueueThread> &jsQueue, 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); void init(std::shared_ptr<JSIWorkletsModuleProxy> jsiWorkletsModuleProxy);
/* #region deprecated */ jsi::Runtime &getJSIRuntime() const {
return *runtime_;
}
/** @deprecated Use `runSync` instead. */
template <typename... Args> template <typename... Args>
jsi::Value runGuarded(const std::shared_ptr<SerializableWorklet> &worklet, Args &&...args) const { inline jsi::Value runGuarded(
return runSync(worklet, std::forward<Args>(args)...); 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); 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; 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; 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 std::string toString() const {
/** return "[WorkletRuntime \"" + name_ + "\"]";
* 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
#ifndef NDEBUG jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override;
static jsi::Function getCallGuard(jsi::Runtime &rt);
#endif // NDEBUG 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: private:
const uint64_t runtimeId_; 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 // This function needs to be non-inline to avoid problems with dynamic_cast on
// Android // 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( void scheduleOnRuntime(
jsi::Runtime &rt, jsi::Runtime &rt,
const jsi::Value &workletRuntimeValue, const jsi::Value &workletRuntimeValue,
const jsi::Value &serializableWorkletValue); 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 } // namespace worklets
@@ -19,7 +19,7 @@ class WorkletRuntimeCollector : public jsi::HostObject {
WorkletRuntimeRegistry::registerRuntime(runtime_); WorkletRuntimeRegistry::registerRuntime(runtime_);
} }
~WorkletRuntimeCollector() override { ~WorkletRuntimeCollector() {
WorkletRuntimeRegistry::unregisterRuntime(runtime_); WorkletRuntimeRegistry::unregisterRuntime(runtime_);
} }
@@ -6,8 +6,6 @@
#include <worklets/WorkletRuntime/WorkletRuntime.h> #include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <worklets/WorkletRuntime/WorkletRuntimeDecorator.h> #include <worklets/WorkletRuntime/WorkletRuntimeDecorator.h>
#include <memory>
#include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -16,7 +14,9 @@ namespace worklets {
static inline double performanceNow() { static inline double performanceNow() {
// copied from JSExecutor.cpp // copied from JSExecutor.cpp
auto time = std::chrono::steady_clock::now(); 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; constexpr double NANOSECONDS_IN_MILLISECOND = 1000000.0;
return duration / NANOSECONDS_IN_MILLISECOND; return duration / NANOSECONDS_IN_MILLISECOND;
@@ -24,7 +24,7 @@ static inline double performanceNow() {
static inline std::vector<jsi::Value> parseArgs( static inline std::vector<jsi::Value> parseArgs(
jsi::Runtime &rt, jsi::Runtime &rt,
const std::shared_ptr<SerializableArray> &serializableArgs) { std::shared_ptr<SerializableArray> serializableArgs) {
if (serializableArgs == nullptr) { if (serializableArgs == nullptr) {
return {}; return {};
} }
@@ -48,7 +48,8 @@ void WorkletRuntimeDecorator::decorate(
// resolves "ReferenceError: Property 'global' doesn't exist at ..." // resolves "ReferenceError: Property 'global' doesn't exist at ..."
rt.global().setProperty(rt, "global", rt.global()); 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); rt.global().setProperty(rt, "_WORKLET", true);
@@ -60,12 +61,16 @@ void WorkletRuntimeDecorator::decorate(
rt.global().setProperty(rt, "__DEV__", isDevBundle); rt.global().setProperty(rt, "__DEV__", isDevBundle);
rt.global().setProperty(rt, "__workletsModuleProxy", std::move(jsiWorkletsModuleProxy)); rt.global().setProperty(
rt, "__workletsModuleProxy", std::move(jsiWorkletsModuleProxy));
#ifndef NDEBUG #ifndef NDEBUG
auto evalWithSourceUrl = auto evalWithSourceUrl = [](jsi::Runtime &rt,
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value { const jsi::Value &thisValue,
auto code = std::make_shared<const jsi::StringBuffer>(args[0].asString(rt).utf8(rt)); 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; std::string url;
if (count > 1 && args[1].isString()) { if (count > 1 && args[1].isString()) {
url = args[1].asString(rt).utf8(rt); url = args[1].asString(rt).utf8(rt);
@@ -76,51 +81,85 @@ void WorkletRuntimeDecorator::decorate(
rt, rt,
"evalWithSourceUrl", "evalWithSourceUrl",
jsi::Function::createFromHostFunction( jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, "evalWithSourceUrl"), 1, evalWithSourceUrl)); rt,
jsi::PropNameID::forAscii(rt, "evalWithSourceUrl"),
1,
evalWithSourceUrl));
#endif // NDEBUG #endif // NDEBUG
jsi_utils::installJsiFunction( jsi_utils::installJsiFunction(
rt, "_log", [](jsi::Runtime &rt, const jsi::Value &value) { PlatformLogger::log(stringifyJSIValue(rt, value)); }); 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);
}); });
jsi_utils::installJsiFunction(rt, "_createSerializableHostObject", [](jsi::Runtime &rt, const jsi::Value &value) { jsi_utils::installJsiFunction(
return makeSerializableHostObject(rt, value.asObject(rt).asHostObject(rt)); rt, "_toString", [](jsi::Runtime &rt, const jsi::Value &value) {
}); return jsi::String::createFromUtf8(rt, stringifyJSIValue(rt, value));
});
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( jsi_utils::installJsiFunction(
rt, "_createSerializableUndefined", [](jsi::Runtime &rt) { return makeSerializableUndefined(rt); }); rt,
"_createSerializable",
jsi_utils::installJsiFunction(rt, "_createSerializableArray", [](jsi::Runtime &rt, const jsi::Value &value) { [](jsi::Runtime &rt,
return makeSerializableArray(rt, value.asObject(rt).asArray(rt), false); const jsi::Value &value,
}); const jsi::Value &nativeStateSource) {
auto shouldRetainRemote = jsi::Value::undefined();
return makeSerializableClone(
rt, value, shouldRetainRemote, nativeStateSource);
});
jsi_utils::installJsiFunction( 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( jsi_utils::installJsiFunction(
rt, rt,
@@ -129,46 +168,70 @@ void WorkletRuntimeDecorator::decorate(
const jsi::Value &value, const jsi::Value &value,
const jsi::Value &shouldRetainRemote, const jsi::Value &shouldRetainRemote,
const jsi::Value &nativeStateSource) { 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) { jsi_utils::installJsiFunction(
return makeSerializableWorklet(rt, value.asObject(rt), false); 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) { jsi_utils::installJsiFunction(
return makeSerializableInitializer(rt, value.asObject(rt)); 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) { jsi_utils::installJsiFunction(
return makeSerializableFunction(rt, value.asObject(rt).asFunction(rt)); 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) { jsi_utils::installJsiFunction(
return SerializableJSRef::newNativeStateObject(rt, extractSerializableOrThrow(rt, value)); rt,
}); "_createSerializableSynchronizable",
[](jsi::Runtime &rt, const jsi::Value &value) {
return SerializableJSRef::newNativeStateObject(
rt, extractSerializableOrThrow(rt, value));
});
jsi_utils::installJsiFunction( jsi_utils::installJsiFunction(
rt, rt,
"_scheduleRemoteFunctionOnJS", "_scheduleRemoteFunctionOnJS",
[jsScheduler](jsi::Runtime &rt, const jsi::Value &funValue, const jsi::Value &argsValue) { [jsScheduler](
auto serializableRemoteFun = extractSerializableOrThrow<SerializableRemoteFunction>( jsi::Runtime &rt,
const jsi::Value &funValue,
const jsi::Value &argsValue) {
auto serializableRemoteFun = extractSerializableOrThrow<
SerializableRemoteFunction>(
rt, rt,
funValue, 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."); "[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() auto serializableArgs = argsValue.isUndefined()
? nullptr ? 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) { 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) { if (serializableArgs == nullptr) {
// fast path for remote function w/o arguments // fast path for remote function w/o arguments
fun.call(rt); fun.call(rt);
} else { } else {
auto args = parseArgs(rt, serializableArgs); 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( jsi_utils::installJsiFunction(
rt, rt,
"_scheduleHostFunctionOnJS", "_scheduleHostFunctionOnJS",
[jsScheduler](jsi::Runtime &rt, const jsi::Value &hostFunValue, const jsi::Value &argsValue) { [jsScheduler](
auto hostFun = hostFunValue.asObject(rt).asFunction(rt).getHostFunction(rt); jsi::Runtime &rt,
const jsi::Value &hostFunValue,
const jsi::Value &argsValue) {
auto hostFun =
hostFunValue.asObject(rt).asFunction(rt).getHostFunction(rt);
auto serializableArgs = argsValue.isUndefined() auto serializableArgs = argsValue.isUndefined()
? nullptr ? 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) { jsScheduler->scheduleOnJS([=](jsi::Runtime &rt) {
auto args = parseArgs(rt, serializableArgs); 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( jsi_utils::installJsiFunction(
rt, rt,
"_scheduleOnRuntime", "_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); scheduleOnRuntime(rt, workletRuntimeValue, serializableWorkletValue);
}); });
@@ -204,20 +278,25 @@ void WorkletRuntimeDecorator::decorate(
rt, rt,
jsi::PropNameID::forAscii(rt, "now"), jsi::PropNameID::forAscii(rt, "now"),
0, 0,
[](jsi::Runtime &runtime, const jsi::Value &, const jsi::Value *args, size_t count) { [](jsi::Runtime &runtime,
return jsi::Value(performanceNow()); const jsi::Value &,
})); const jsi::Value *args,
size_t count) { return jsi::Value(performanceNow()); }));
rt.global().setProperty(rt, "performance", performance); rt.global().setProperty(rt, "performance", performance);
jsi_utils::installJsiFunction( jsi_utils::installJsiFunction(
rt, rt,
"_scheduleTimeoutCallback", "_scheduleTimeoutCallback",
[weakEventLoop = std::weak_ptr<EventLoop>(eventLoop)]( [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 delay = delayJs.asNumber();
const auto handlerId = handlerIdJs.asNumber(); const auto handlerId = handlerIdJs.asNumber();
const auto job = [handlerId](jsi::Runtime &rt) { 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()) { if (auto strongEventLoop = weakEventLoop.lock()) {
strongEventLoop->pushTimeout(job, delay); strongEventLoop->pushTimeout(job, delay);

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