@react-native/babel-preset applies transform-destructuring unconditionally under Hermes — semantic divergence breaks three.js TSL (crashes every InstancedMesh on WebGPU)
Personne n'a encore pris cette issue.
- Langage dominant
- C++
- Étoiles
- 127k
- Forks
- 25.3k
- Merge moyen
- 1 j 23 h
- PR mergées (30 j)
- 4
Description
Description
@react-native/babel-preset pushes @babel/plugin-transform-destructuring unconditionally — including for Hermes targets (packages/react-native-babel-preset/src/configs/main.js, the always-on plugin list; verified in 0.81.4 at src/configs/main.js:122). Hermes supports array/object destructuring natively, so for Hermes builds the transform is (a) redundant bytecode and (b) a semantic hazard, because downleveled destructuring is not perfectly equivalent to native destructuring.
The concrete divergence: native array destructuring always goes through the iterator protocol, while Babel's _slicedToArray helper short-circuits via _arrayWithHoles — if (Array.isArray(r)) return r; — and then indexes positionally. For a Proxy over an array (Array.isArray pierces to the target) whose get trap serves a custom Symbol.iterator but does not answer positional reads, the two compile outputs produce different values: iterator yields the real elements, indexing yields undefined.
That exotic-sounding object is shipping in one of the most popular 3D libraries: three.js's TSL shader system passes Fn arguments through exactly such a Proxy, and three's own sources destructure it (src/nodes/accessors/Instance.js: Fn( ( [ instancedMesh ] ) => { const { instanceMatrix, instanceColor } = instancedMesh; ... } )). Metro rewrites three's code with this transform, so on device the body receives undefined and every React Native app using three.js WebGPU (react-native-webgpu, Dawn/Vulkan) with an InstancedMesh crashes on the first rendered frame:
TypeError: Cannot read property 'instanceMatrix' of undefined
at ... setupOutput / getOutputNode / build / flowNodeFromShaderStage / ... / render
The same pattern arms skinning(), batch() and morphReference() — i.e. skinned meshes, batched meshes and morph targets are equally affected. Nothing surfaces in Node/browser tests because those run untransformed code; the failure exists only in the Metro-bundled app.
Ask
Consider making transform-destructuring conditional on the engine target (skipped for Hermes, as several other transforms already are engine-aware), or documenting why it must stay unconditional. Cross-filed with the two neighboring projects, since each owns a slice of the collision:
- three.js (Proxy should honor positional reads): https://github.com/mrdoob/three.js/issues/34227
- Babel (
_arrayWithHolesfast path vs custom iterators on array-backed Proxies): link to follow in a comment
Steps to reproduce
- Bare RN 0.81.4 app (Hermes, new arch) +
react-native-webgpu0.8.2 +three0.185.1 - Render any scene containing a
THREE.InstancedMeshwithWebGPURenderer - First frame throws
TypeError: Cannot read property 'instanceMatrix' of undefined
A toolchain-only demonstration (no device needed): run @react-native/babel-preset over const fn = ([a]) => a; and observe the _slicedToArray output; feed it an array-backed Proxy with a custom Symbol.iterator in its get trap and no index handling — native semantics return the element, transformed semantics return undefined.
React Native Version
0.81.4
Affected Platforms
Runtime: Android (verified on device: Android 15, Hermes release bundle, Dawn/Vulkan). iOS untested but the transform is platform-independent.
Output of npx @react-native-community/cli info
Not attached — the report is toolchain-level (@react-native/babel-preset 0.81.4, @babel/plugin-transform-destructuring 7.29.7, @babel/runtime 7.29.7); happy to add on request.
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez dans packages/react-native-babel-preset/src/configs/main.js, en particulier dans la liste des plugins toujours activés autour de la ligne 122, et reproduisez la transformation avec l’exemple de Proxy basé sur un array de l’issue. Comparez les modèles de configuration Hermes et non-Hermes déjà présents ; le travail est terminé lorsque le preset évite la divergence sémantique pour Hermes ou documente une raison confirmée de conserver la transformation, et que le comportement de la toolchain est couvert par un test approprié.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- javascript, react-native, three.js
- Domaine
- build-system, mobile, tooling
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- Active
- Clarté
- Plutôt claire
- Accessibilité débutants
- 55/100