Rect Native bridging 源码分析--Class.h

代码目录:

复制代码
packages/react-native/ReactCommon/react/bridging/Class.h

源代码:

cpp 复制代码
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#pragma once

#include <react/bridging/Base.h>

namespace facebook::react::bridging {

template <typename JSReturnT, typename ClassT, typename ReturnT, typename... ArgsT, typename... JSArgsT>
JSReturnT callFromJs(
    jsi::Runtime &rt,
    ReturnT (ClassT::*method)(jsi::Runtime &, ArgsT...),
    const std::shared_ptr<CallInvoker> &jsInvoker,
    ClassT *instance,
    JSArgsT &&...args)
{
  static_assert(sizeof...(ArgsT) == sizeof...(JSArgsT), "Incorrect arguments length");
  static_assert((supportsFromJs<ArgsT, JSArgsT> && ...), "Incompatible arguments");
  if constexpr (std::is_void_v<JSReturnT>) {
    static_assert(std::is_void_v<ReturnT>, "Method must return void when JSReturnT is void");
  }

  if constexpr (std::is_void_v<JSReturnT>) {
    (instance->*method)(rt, fromJs<ArgsT>(rt, std::forward<JSArgsT>(args), jsInvoker)...);

  } else if constexpr (std::is_void_v<ReturnT>) {
    static_assert(std::is_same_v<JSReturnT, jsi::Value>, "Void functions may only return undefined");

    (instance->*method)(rt, fromJs<ArgsT>(rt, std::forward<JSArgsT>(args), jsInvoker)...);
    return jsi::Value();

  } else if constexpr (is_jsi_v<JSReturnT> || supportsToJs<ReturnT, JSReturnT>) {
    static_assert(supportsToJs<ReturnT, JSReturnT>, "Incompatible return type");

    return toJs(rt, (instance->*method)(rt, fromJs<ArgsT>(rt, std::forward<JSArgsT>(args), jsInvoker)...), jsInvoker);
  } else if constexpr (is_optional_jsi_v<JSReturnT>) {
    static_assert(
        is_optional_v<ReturnT> ? supportsToJs<typename ReturnT::value_type, typename JSReturnT::value_type>
                               : supportsToJs<ReturnT, typename JSReturnT::value_type>,
        "Incompatible return type");

    auto result =
        toJs(rt, (instance->*method)(rt, fromJs<ArgsT>(rt, std::forward<JSArgsT>(args), jsInvoker)...), jsInvoker);

    if constexpr (std::is_same_v<decltype(result), jsi::Value>) {
      if (result.isNull() || result.isUndefined()) {
        return std::nullopt;
      }
    }

    return convert(rt, std::move(result));
  } else {
    static_assert(std::is_convertible_v<ReturnT, JSReturnT>, "Incompatible return type");
    return (instance->*method)(rt, fromJs<ArgsT>(rt, std::forward<JSArgsT>(args), jsInvoker)...);
  }
}

template <typename ReturnT, typename... ArgsT>
constexpr size_t getParameterCount(ReturnT (* /*unused*/)(ArgsT...))
{
  return sizeof...(ArgsT);
}

template <typename Class, typename ReturnT, typename... ArgsT>
constexpr size_t getParameterCount(ReturnT (Class::* /*unused*/)(ArgsT...))
{
  return sizeof...(ArgsT);
}

} // namespace facebook::react::bridging

概述

该头文件隶属于 facebook::react::bridging 命名空间,提供了两大核心功能:一是 callFromJs 模板函数,用于从 JavaScript(JSI)层安全调用 C++ 类成员方法,自动完成参数类型转换(JS→C++)与返回值类型转换(C++→JS);二是 getParameterCount 模板函数,用于在编译期获取普通函数 / 类成员函数的参数个数。该工具是 React Native 跨端框架中 JS 层与 C++ 层方法交互的核心支撑,保证了跨语言方法调用的类型安全与便捷性。

核心命名空间

cpp 复制代码
facebook::react::bridging

所有工具函数均位于该命名空间下,与 React 桥接体系的其他组件(如类型转换、集合桥接)保持统一规范。

1. callFromJs 模板函数:JS 调用 C++ 类成员方法的核心桥接

1.1 模板定义

cpp 复制代码
template <typename JSReturnT, typename ClassT, typename ReturnT, typename... ArgsT, typename... JSArgsT>
JSReturnT callFromJs(
    jsi::Runtime &rt,
    ReturnT (ClassT::*method)(jsi::Runtime &, ArgsT...),
    const std::shared_ptr<CallInvoker> &jsInvoker,
    ClassT *instance,
    JSArgsT &&...args);

1.2 功能描述

作为 JS 层调用 C++ 类成员方法的统一入口,自动完成以下核心流程:

  1. 编译期类型校验:校验参数个数匹配性、参数类型兼容性、返回值类型兼容性。
  2. 参数转换 :将 JS 层传入的参数(JSArgsT 类型)通过 fromJs 方法批量转换为 C++ 成员方法所需的参数类型(ArgsT 类型)。
  3. 方法调用:通过类实例指针调用目标成员方法。
  4. 返回值转换 :根据 JSReturnT(JS 层期望返回类型)与 ReturnT(C++ 方法返回类型)的类型特性,自动选择最优转换逻辑,将 C++ 方法返回值转换为 JS 层可识别的类型。

1.3 参数说明

参数名 类型 必选 说明
rt jsi::Runtime & JSI 运行时环境引用,提供 JS 层执行上下文,用于类型转换与方法执行。
method ReturnT (ClassT::*)(jsi::Runtime &, ArgsT...) 指向 C++ 类 ClassT 成员方法的指针,该方法第一个参数固定为 jsi::Runtime &,后续为业务参数 ArgsT...
jsInvoker const std::shared_ptr<CallInvoker> & JS 调用器智能指针,用于支撑转换过程中可能的同步 / 异步 JS 方法调用,传递给底层 fromJs/toJs 方法。
instance ClassT * C++ 类 ClassT 的实例指针,用于调用目标成员方法((instance->*method)(...))。
args JSArgsT &&... 可变参数模板,JS 层传入的原始参数(右值引用,支持完美转发,减少拷贝开销)。

1.4 编译期静态断言(类型安全校验)

该函数内置 3 个核心静态断言,在编译期拦截非法调用,避免运行时错误:

  1. static_assert(sizeof...(ArgsT) == sizeof...(JSArgsT), "Incorrect arguments length")
    • 功能:校验 C++ 成员方法的业务参数个数与 JS 传入的参数个数完全一致,不一致则编译失败。
  2. static_assert((supportsFromJs<ArgsT, JSArgsT> && ...), "Incompatible arguments")
    • 功能:校验每一组 JSArgsT(JS 参数类型)都能通过 fromJs 转换为对应的 ArgsT(C++ 参数类型),存在不兼容类型则编译失败。
  3. 分支内静态断言(如返回值类型校验)
    • 功能:针对不同返回值场景,校验 JSReturnTReturnT 的兼容性,如 "当 JSReturnTvoid 时,ReturnT 必须也为 void"。

1.5 核心分支逻辑(按返回值类型划分)

函数通过 if constexpr(C++17 编译期条件判断)实现不同返回值场景的分支处理,无运行时条件判断开销:

分支场景 适用条件 核心逻辑
1. 无返回值(JS/C++ 均为 void) std::is_void_v<JSReturnT> 1. 断言 ReturnT 也为 void(否则编译失败)2. 转换所有 JS 参数为 C++ 参数,调用类成员方法(无返回值,直接执行)3. 无返回值返回
2. C++ 无返回值,JS 期望 jsi::Value std::is_void_v<ReturnT> && !std::is_void_v<JSReturnT> 1. 断言 JSReturnTjsi::Value(否则编译失败,因为无返回值方法仅能返回 JS undefined)2. 执行类成员方法3. 返回 jsi::Value()(对应 JS 层 undefined
3. 普通返回值转换(JSI 类型 / 支持 toJs 转换) `is_jsi_v<JSReturnT> supportsToJs<ReturnT, JSReturnT>` 1. 断言 ReturnT 可转换为 JSReturnT(否则编译失败)2. 执行类成员方法,获取 C++ 返回值3. 调用 toJs 方法将返回值转换为 JSReturnT 类型并返回
4. 可选类型返回值转换(optional JSI 类型) is_optional_jsi_v<JSReturnT> 1. 断言 ReturnT(或可选类型的内部值类型)可转换为 JSReturnT 的内部值类型2. 执行类成员方法,通过 toJs 转换返回值3. 若转换结果为 jsi::Value 且是 null/undefined,返回 std::nullopt4. 否则通过 convert 函数转换为 JSReturnT 类型并返回
5. 直接类型转换(无桥接工具依赖) 以上场景均不满足 1. 断言 ReturnT 可隐式转换为 JSReturnT(否则编译失败)2. 执行类成员方法,直接返回转换后的结果(无额外桥接转换逻辑)

1.6 返回值

  • 类型:JSReturnT(模板参数指定,JS 层期望的返回类型,通常为 jsi::Value 或可选 JSI 类型)
  • 说明:根据不同分支场景,返回经过类型转换后的结果,与 JS 层类型完全兼容,可直接在 JS 层使用。

2. getParameterCount 模板函数:编译期获取函数参数个数

2.1 函数重载定义

重载 1:普通函数指针
cpp 复制代码
template <typename ReturnT, typename... ArgsT>
constexpr size_t getParameterCount(ReturnT (* /*unused*/)(ArgsT...));
重载 2:类成员函数指针
cpp 复制代码
template <typename Class, typename ReturnT, typename... ArgsT>
constexpr size_t getParameterCount(ReturnT (Class::* /*unused*/)(ArgsT...));

2.2 功能描述

在编译期(constexpr 函数)获取普通函数或 C++ 类成员函数的参数个数,无运行时开销,适用于模板元编程场景下的参数个数判断。

2.3 参数说明

参数名 类型 必选 说明
(匿名参数) 函数指针 / 类成员函数指针 目标函数的指针(仅用于推导模板参数 ArgsT...,无需实际传入值,标注为 /*unused*/

2.4 返回值

  • 类型:constexpr size_t(编译期常量,无符号整数)
  • 说明:返回函数的参数个数,即 sizeof...(ArgsT),其中 ArgsT... 是目标函数的参数类型列表。

2.5 适用场景

  1. 模板元编程中,根据函数参数个数进行条件分支选择。
  2. 跨语言方法绑定中,校验 JS 传入参数个数与 C++ 函数参数个数的一致性(与 callFromJs 中的静态断言功能互补)。
  3. 自动生成方法绑定代码时,获取函数参数信息。

3. 核心特性总结

  1. 类型安全:通过静态断言与编译期类型推导,在编译期拦截参数个数不匹配、参数 / 返回值类型不兼容等问题,避免运行时异常。
  2. 自动转换 :无缝集成 fromJs/toJs 类型转换工具,无需手动处理 JS 与 C++ 之间的类型映射,降低使用成本。
  3. 编译期优化 :使用 if constexprconstexpr 等 C++17 特性,所有条件判断与参数个数计算均在编译期完成,无运行时额外开销。
  4. 灵活适配:支持无返回值、普通返回值、可选类型返回值等多种场景,兼容 JSI 类型与普通 C++ 类型的转换需求。
  5. 完美转发 :对 JS 传入参数使用右值引用(JSArgsT &&...)与 std::forward,实现参数完美转发,减少不必要的内存拷贝。

4. 注意事项

  1. 依赖 C++17 及以上标准:需支持 if constexprconstexpr 函数、可变参数模板、完美转发等特性。
  2. 依赖 React 桥接组件:依赖 fromJs/toJs 转换方法、supportsFromJs/supportsToJs 兼容性判断常量、CallInvoker 类、JSI 库(jsi::Runtimejsi::Value)等,使用时需引入对应头文件与依赖。
  3. 类成员方法约束:目标类成员方法的第一个参数必须为 jsi::Runtime &,否则无法匹配 callFromJs 的方法指针参数类型。
  4. 可选类型支持:仅支持满足 is_optional_jsi_v 特性的可选类型,通常为 std::optional 包裹的 JSI 类型。
相关推荐
ヤ鬧鬧o.11 小时前
导航菜单实现平滑切换页面
javascript·css·html
ohyeah11 小时前
React 自定义 Hook 实战:从鼠标追踪到待办事项管理
前端·react.js
前端 贾公子12 小时前
剖析源码Vue项目结构 (一)
前端·javascript·vue.js
狂龙骄子12 小时前
jQuery表单验证插件全攻略
前端·javascript·jquery·jquery表单验证
十铭忘12 小时前
Vue3实现Pixso中的钢笔工具
开发语言·javascript·vue
局i12 小时前
【无标题】
前端·javascript·vue.js
前端小L13 小时前
双指针专题(四):像毛毛虫一样伸缩——「长度最小的子数组」
javascript·算法·双指针与滑动窗口
沛沛rh4513 小时前
React入门:从一个简单的Hello World开始
前端·react.js·前端框架
谢尔登13 小时前
Vue3 应用实例创建及页面渲染底层原理
javascript·vue.js·ecmascript