Files
react-native/scripts/cxx-api
Bartlomiej Bloniarz d9d2502b61 Fix AnimatedPropsRegistry surface-stop resurrection race (#57376)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57376

AnimatedPropsRegistry::update() runs on the UI thread every animation frame and
created a surface's entry via operator[]. clearOnSurfaceStop() (run on the JS
thread when a surface stops) erases that entry, but an in-flight animation frame
landing after the stop re-created it via operator[] -- and since the surface is
gone, nothing ever cleans it up again. The resurrected entry leaks its
PropsSnapshot and ShadowNodeFamily for the lifetime of the registry.

A surface's entry is legitimately created by getMap(), which
AnimationBackendCommitHook calls on every React commit. stopSurface drains
in-flight commits before unregistering the ShadowTree
(ShadowTreeRegistry::remove takes the registry's unique lock, which excludes the
shared-locked commit visits), so getMap() can never run for a stopped surface.
That leaves update()'s operator[] as the only thing that can resurrect one.

Fix: update() now only refines surfaces that already exist (find instead of
operator[]) and never creates an entry; getMap() remains the sole creator. A
stopped surface can no longer be resurrected, and there is no extra bookkeeping
that could grow over time.

Changelog: [General][Fixed] - Fix a surface-stop race in the C++ Animated shared backend that could permanently leak per-surface animated state

Reviewed By: javache

Differential Revision: D109156094

fbshipit-source-id: 4684ca51d372023e3b082427a225de7d84d14889
2026-07-01 07:13:13 -07:00
..

scripts/cxx-api

Python build pipeline for React Native's C++ (and Objective-C) API snapshots.

Overview

scripts/cxx-api generates human-readable snapshots of React Native's public C++ API surface. It uses Doxygen to parse C/C++/Objective-C headers and a custom Python parser to produce a simplified, sorted representation of every public symbol.

The pipeline produces one .api snapshot file per configured API view × variant combination:

Snapshot Description
ReactCommonDebugCxx.api Platform-independent C++ API (debug)
ReactCommonReleaseCxx.api Platform-independent C++ API (release)
ReactAndroidDebugCxx.api Android-specific C++ API (debug)
ReactAndroidReleaseCxx.api Android-specific C++ API (release)
ReactAppleDebugCxx.api Apple-specific C++/Obj-C API (debug)
ReactAppleReleaseCxx.api Apple-specific C++/Obj-C API (release)

For each view, debug and release variants are generated with different preprocessor definitions (e.g. REACT_NATIVE_DEBUG vs NDEBUG), since #ifdef guards in the source headers can produce a different public API surface per variant.

Snapshot files are committed to the repo under scripts/cxx-api/api-snapshots/.

Usage

Generate snapshots

Maintainers should run this command whenever making intentional C++ API changes:

python -m scripts.cxx-api.parser

Validate snapshots against committed baseline

This mode generates snapshots to a temporary directory and compares them against the committed .api files. It is designed for CI:

python -m scripts.cxx-api.parser --validate

If any snapshot differs, a unified diff is printed and the process exits with a non-zero status. To fix a failing validation, regenerate the snapshots with python -m scripts.cxx-api.parser and commit the updated .api files.

How it works

The pipeline has two main stages:

1. Doxygen XML generation

Doxygen is configured via a generated config file (built from .doxygen.config.template) with the input directories, exclude patterns, and preprocessor definitions specified in config.yml. It outputs XML describing every symbol found in the headers.

2. Snapshot parsing

The Python parser (parser/) reads the Doxygen XML output and builds a scope tree of the public API surface. The tree is then serialized to a deterministically sorted, human-readable .api text format.

When to use it

The snapshot should be regenerated whenever making intentional changes to the public C++ API surface. This includes additions, removals, and changes to files located in:

  • xplat/js/react-native-github/
  • xplat/js/react-native-github/ReactCommon/
  • xplat/js/react-native-github/ReactAndroid/
  • xplat/js/react-native-github/ReactApple/
  • xplat/js/react-native-github/Libraries/

Configuration

All API views and their variants are defined in config.yml. Each view specifies:

Field Description
inputs Directories to scan for headers
exclude_patterns Glob patterns for files to skip
definitions Preprocessor macros to define
variants Named build variants (e.g. debug/release) with extra definitions
codegen Optional codegen platform (android, ios) to generate TurboModule/Component headers before scanning
private_directories Directories whose headers are scanned (they may be transitively included) but should not contribute public symbols. If any public API entity is defined in a private directory, a warning is printed to help catch accidental API exposure.

Snapshot format

The .api files use a minimal pseudo-C++ syntax designed for easy diffing:

namespace facebook::react {
  class ComponentDescriptor {
    public ComponentDescriptor(ComponentDescriptorParameters params);
    public ComponentHandle getComponentHandle();
  }

  enum class AccessibilityRole {
    None = 0,
    Button = 1,
  }
}
  • Scopes and members are sorted alphabetically.
  • Access specifiers (public, protected) are preserved.
  • Template parameters are included.
  • Doc comments and source file names are stripped.