diff --git a/packages/react-native/ReactCommon/react/bridging/BigInt.h b/packages/react-native/ReactCommon/react/bridging/BigInt.h new file mode 100644 index 000000000000..8c6156d1a39a --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridging/BigInt.h @@ -0,0 +1,81 @@ +/* + * 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 + +#include +#include + +namespace facebook::react { + +class BigInt { + public: + BigInt(jsi::Runtime &rt, const jsi::BigInt &bigint) + { + if (bigint.isInt64(rt)) { + value_ = bigint.asInt64(rt); + } else if (bigint.isUint64(rt)) { + value_ = bigint.asUint64(rt); + } else { + throw jsi::JSError(rt, "BigInt value cannot be losslessly represented as int64_t or uint64_t"); + } + } + + /* implicit */ BigInt(int64_t value) : value_(value) {} + /* implicit */ BigInt(uint64_t value) : value_(value) {} + + bool isInt64() const + { + return std::holds_alternative(value_); + } + + bool isUint64() const + { + return std::holds_alternative(value_); + } + + int64_t asInt64() const + { + return std::get(value_); + } + + uint64_t asUint64() const + { + return std::get(value_); + } + + jsi::BigInt toJSBigInt(jsi::Runtime &rt) const + { + if (isInt64()) { + return jsi::BigInt::fromInt64(rt, asInt64()); + } else { + return jsi::BigInt::fromUint64(rt, asUint64()); + } + } + + bool operator==(const BigInt &other) const = default; + + private: + std::variant value_; +}; + +template <> +struct Bridging { + static BigInt fromJs(jsi::Runtime &rt, const jsi::Value &value) + { + return {rt, value.getBigInt(rt)}; + } + + static jsi::BigInt toJs(jsi::Runtime &rt, const BigInt &value) + { + return value.toJSBigInt(rt); + } +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/bridging/Bridging.h b/packages/react-native/ReactCommon/react/bridging/Bridging.h index 7507c8ae3048..df1109a6a57f 100644 --- a/packages/react-native/ReactCommon/react/bridging/Bridging.h +++ b/packages/react-native/ReactCommon/react/bridging/Bridging.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp b/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp index 484226012c4d..6f79d82c7a9a 100644 --- a/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp +++ b/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp @@ -10,6 +10,10 @@ #include "BridgingTest.h" +#include +#include +#include + namespace facebook::react { using namespace std::literals; @@ -971,4 +975,87 @@ TEST_F(BridgingTest, highResTimeStampTest) { 1.000001, bridging::toJs(rt, HighResDuration::fromNanoseconds(1e6 + 1))); } +TEST_F(BridgingTest, bigintTest) { + // Test BigInt construction from int64_t + BigInt fromSigned(static_cast(42)); + EXPECT_TRUE(fromSigned.isInt64()); + EXPECT_FALSE(fromSigned.isUint64()); + EXPECT_EQ(42, fromSigned.asInt64()); + + // Test BigInt construction from uint64_t + BigInt fromUnsigned(static_cast(42)); + EXPECT_FALSE(fromUnsigned.isInt64()); + EXPECT_TRUE(fromUnsigned.isUint64()); + EXPECT_EQ(42ULL, fromUnsigned.asUint64()); + + // Test BigInt construction from jsi::BigInt with signed value + auto jsiBigint = jsi::BigInt::fromInt64(rt, -123456789012345LL); + BigInt fromJsi(rt, jsiBigint); + EXPECT_TRUE(fromJsi.isInt64()); + EXPECT_EQ(-123456789012345LL, fromJsi.asInt64()); + + // Test BigInt construction from jsi::BigInt with large unsigned value + // (doesn't fit in int64_t, so should be stored as uint64_t) + constexpr uint64_t uint64Max = std::numeric_limits::max(); + auto jsiUnsigned = jsi::BigInt::fromUint64(rt, uint64Max); + BigInt fromJsiUnsigned(rt, jsiUnsigned); + EXPECT_TRUE(fromJsiUnsigned.isUint64()); + EXPECT_EQ(uint64Max, fromJsiUnsigned.asUint64()); + + // Test BigInt construction from jsi::BigInt with small positive value + // (fits in both int64_t and uint64_t — should prefer int64_t) + auto jsiSmall = jsi::BigInt::fromInt64(rt, 5); + BigInt fromJsiSmall(rt, jsiSmall); + EXPECT_TRUE(fromJsiSmall.isInt64()); + EXPECT_EQ(5, fromJsiSmall.asInt64()); + + // Test toJSBigInt roundtrip for signed value + BigInt signedVal(static_cast(-42)); + auto jsResult = signedVal.toJSBigInt(rt); + EXPECT_EQ(-42, jsResult.asInt64(rt)); + + // Test toJSBigInt roundtrip for unsigned value + BigInt unsignedVal(uint64Max); + auto jsUnsignedResult = unsignedVal.toJSBigInt(rt); + EXPECT_EQ(uint64Max, jsUnsignedResult.asUint64(rt)); + + // Test Bridging::fromJs + constexpr int64_t int64Max = std::numeric_limits::max(); + auto jsBigint = jsi::BigInt::fromInt64(rt, int64Max); + auto bridged = + bridging::fromJs(rt, jsi::Value(rt, jsBigint), invoker); + EXPECT_TRUE(bridged.isInt64()); + EXPECT_EQ(int64Max, bridged.asInt64()); + + // Test Bridging::toJs + BigInt toConvert(static_cast(123456789012345LL)); + auto jsConverted = bridging::toJs(rt, toConvert); + EXPECT_EQ(123456789012345LL, jsConverted.asInt64(rt)); + + // Test roundtrip at extreme values via bridging + constexpr int64_t int64Min = std::numeric_limits::min(); + + auto roundtripMin = bridging::fromJs( + rt, jsi::Value(rt, bridging::toJs(rt, BigInt(int64Min))), invoker); + EXPECT_TRUE(roundtripMin.isInt64()); + EXPECT_EQ(int64Min, roundtripMin.asInt64()); + + auto roundtripMax = bridging::fromJs( + rt, jsi::Value(rt, bridging::toJs(rt, BigInt(int64Max))), invoker); + EXPECT_TRUE(roundtripMax.isInt64()); + EXPECT_EQ(int64Max, roundtripMax.asInt64()); + + auto roundtripUmax = bridging::fromJs( + rt, jsi::Value(rt, bridging::toJs(rt, BigInt(uint64Max))), invoker); + EXPECT_TRUE(roundtripUmax.isUint64()); + EXPECT_EQ(uint64Max, roundtripUmax.asUint64()); + + // Test equality + EXPECT_EQ(BigInt(static_cast(42)), BigInt(static_cast(42))); + EXPECT_EQ(BigInt(uint64Max), BigInt(uint64Max)); + // Same numeric value but different variant type are not equal + EXPECT_NE( + BigInt(static_cast(42)), BigInt(static_cast(42))); +} + } // namespace facebook::react diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 9a10db992942..0af8b94fb22f 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -1834,6 +1834,18 @@ class facebook::react::BaseViewProps : public facebook::react::YogaStylableProps public void setProp(const facebook::react::PropsParserContext& context, facebook::react::RawPropsPropNameHash hash, const char* propName, const facebook::react::RawValue& value); } +class facebook::react::BigInt { + public BigInt(facebook::jsi::Runtime& rt, const facebook::jsi::BigInt& bigint); + public BigInt(int64_t value); + public BigInt(uint64_t value); + public bool isInt64() const; + public bool isUint64() const; + public bool operator==(const facebook::react::BigInt& other) const = default; + public facebook::jsi::BigInt toJSBigInt(facebook::jsi::Runtime& rt) const; + public int64_t asInt64() const; + public uint64_t asUint64() const; +} + class facebook::react::BigStringBuffer : public facebook::jsi::Buffer { public BigStringBuffer(std::unique_ptr script); public virtual const uint8_t* data() const override; @@ -9600,8 +9612,9 @@ struct facebook::react::Bridging { public static facebook::jsi::WeakObject fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Object& value); } -struct facebook::react::Bridging { - public static facebook::jsi::Value toJs(facebook::jsi::Runtime& rt, facebook::react::AsyncArrayBuffer buffer); +struct facebook::react::Bridging { + public static facebook::jsi::BigInt toJs(facebook::jsi::Runtime& rt, const facebook::react::BigInt& value); + public static facebook::react::BigInt fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Value& value); } struct facebook::react::Bridging : public facebook::react::NativeAnimatedTurboModuleEndResultBridging { diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 93f507c31385..bfe6dccdbd15 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -1832,6 +1832,18 @@ class facebook::react::BaseViewProps : public facebook::react::YogaStylableProps public void setProp(const facebook::react::PropsParserContext& context, facebook::react::RawPropsPropNameHash hash, const char* propName, const facebook::react::RawValue& value); } +class facebook::react::BigInt { + public BigInt(facebook::jsi::Runtime& rt, const facebook::jsi::BigInt& bigint); + public BigInt(int64_t value); + public BigInt(uint64_t value); + public bool isInt64() const; + public bool isUint64() const; + public bool operator==(const facebook::react::BigInt& other) const = default; + public facebook::jsi::BigInt toJSBigInt(facebook::jsi::Runtime& rt) const; + public int64_t asInt64() const; + public uint64_t asUint64() const; +} + class facebook::react::BigStringBuffer : public facebook::jsi::Buffer { public BigStringBuffer(std::unique_ptr script); public virtual const uint8_t* data() const override; @@ -9453,8 +9465,9 @@ struct facebook::react::Bridging { public static facebook::jsi::WeakObject fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Object& value); } -struct facebook::react::Bridging { - public static facebook::jsi::Value toJs(facebook::jsi::Runtime& rt, facebook::react::AsyncArrayBuffer buffer); +struct facebook::react::Bridging { + public static facebook::jsi::BigInt toJs(facebook::jsi::Runtime& rt, const facebook::react::BigInt& value); + public static facebook::react::BigInt fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Value& value); } struct facebook::react::Bridging : public facebook::react::NativeAnimatedTurboModuleEndResultBridging { diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index a2b9bb40e125..13f3cc3e6680 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -4808,6 +4808,18 @@ class facebook::react::BaseViewProps : public facebook::react::YogaStylableProps public void setProp(const facebook::react::PropsParserContext& context, facebook::react::RawPropsPropNameHash hash, const char* propName, const facebook::react::RawValue& value); } +class facebook::react::BigInt { + public BigInt(facebook::jsi::Runtime& rt, const facebook::jsi::BigInt& bigint); + public BigInt(int64_t value); + public BigInt(uint64_t value); + public bool isInt64() const; + public bool isUint64() const; + public bool operator==(const facebook::react::BigInt& other) const = default; + public facebook::jsi::BigInt toJSBigInt(facebook::jsi::Runtime& rt) const; + public int64_t asInt64() const; + public uint64_t asUint64() const; +} + class facebook::react::BigStringBuffer : public facebook::jsi::Buffer { public BigStringBuffer(std::unique_ptr script); public virtual const uint8_t* data() const override; @@ -11904,8 +11916,9 @@ struct facebook::react::Bridging { public static facebook::jsi::WeakObject fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Object& value); } -struct facebook::react::Bridging { - public static facebook::jsi::Value toJs(facebook::jsi::Runtime& rt, facebook::react::AsyncArrayBuffer buffer); +struct facebook::react::Bridging { + public static facebook::jsi::BigInt toJs(facebook::jsi::Runtime& rt, const facebook::react::BigInt& value); + public static facebook::react::BigInt fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Value& value); } struct facebook::react::Bridging : public facebook::react::NativeAnimatedTurboModuleEndResultBridging { diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 84098faa3185..65aaabb6167d 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -4806,6 +4806,18 @@ class facebook::react::BaseViewProps : public facebook::react::YogaStylableProps public void setProp(const facebook::react::PropsParserContext& context, facebook::react::RawPropsPropNameHash hash, const char* propName, const facebook::react::RawValue& value); } +class facebook::react::BigInt { + public BigInt(facebook::jsi::Runtime& rt, const facebook::jsi::BigInt& bigint); + public BigInt(int64_t value); + public BigInt(uint64_t value); + public bool isInt64() const; + public bool isUint64() const; + public bool operator==(const facebook::react::BigInt& other) const = default; + public facebook::jsi::BigInt toJSBigInt(facebook::jsi::Runtime& rt) const; + public int64_t asInt64() const; + public uint64_t asUint64() const; +} + class facebook::react::BigStringBuffer : public facebook::jsi::Buffer { public BigStringBuffer(std::unique_ptr script); public virtual const uint8_t* data() const override; @@ -11767,8 +11779,9 @@ struct facebook::react::Bridging { public static facebook::jsi::WeakObject fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Object& value); } -struct facebook::react::Bridging { - public static facebook::jsi::Value toJs(facebook::jsi::Runtime& rt, facebook::react::AsyncArrayBuffer buffer); +struct facebook::react::Bridging { + public static facebook::jsi::BigInt toJs(facebook::jsi::Runtime& rt, const facebook::react::BigInt& value); + public static facebook::react::BigInt fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Value& value); } struct facebook::react::Bridging : public facebook::react::NativeAnimatedTurboModuleEndResultBridging { diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 3a7473351c4b..1ee1ab76a3c9 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -1158,6 +1158,18 @@ class facebook::react::BaseViewProps : public facebook::react::YogaStylableProps public void setProp(const facebook::react::PropsParserContext& context, facebook::react::RawPropsPropNameHash hash, const char* propName, const facebook::react::RawValue& value); } +class facebook::react::BigInt { + public BigInt(facebook::jsi::Runtime& rt, const facebook::jsi::BigInt& bigint); + public BigInt(int64_t value); + public BigInt(uint64_t value); + public bool isInt64() const; + public bool isUint64() const; + public bool operator==(const facebook::react::BigInt& other) const = default; + public facebook::jsi::BigInt toJSBigInt(facebook::jsi::Runtime& rt) const; + public int64_t asInt64() const; + public uint64_t asUint64() const; +} + class facebook::react::BigStringBuffer : public facebook::jsi::Buffer { public BigStringBuffer(std::unique_ptr script); public virtual const uint8_t* data() const override; @@ -6669,8 +6681,9 @@ struct facebook::react::Bridging { public static facebook::jsi::WeakObject fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Object& value); } -struct facebook::react::Bridging { - public static facebook::jsi::Value toJs(facebook::jsi::Runtime& rt, facebook::react::AsyncArrayBuffer buffer); +struct facebook::react::Bridging { + public static facebook::jsi::BigInt toJs(facebook::jsi::Runtime& rt, const facebook::react::BigInt& value); + public static facebook::react::BigInt fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Value& value); } struct facebook::react::Bridging : public NativeAnimatedTurboModuleEndResultBridging { diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index f64fbd2790a4..283301341a59 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -1156,6 +1156,18 @@ class facebook::react::BaseViewProps : public facebook::react::YogaStylableProps public void setProp(const facebook::react::PropsParserContext& context, facebook::react::RawPropsPropNameHash hash, const char* propName, const facebook::react::RawValue& value); } +class facebook::react::BigInt { + public BigInt(facebook::jsi::Runtime& rt, const facebook::jsi::BigInt& bigint); + public BigInt(int64_t value); + public BigInt(uint64_t value); + public bool isInt64() const; + public bool isUint64() const; + public bool operator==(const facebook::react::BigInt& other) const = default; + public facebook::jsi::BigInt toJSBigInt(facebook::jsi::Runtime& rt) const; + public int64_t asInt64() const; + public uint64_t asUint64() const; +} + class facebook::react::BigStringBuffer : public facebook::jsi::Buffer { public BigStringBuffer(std::unique_ptr script); public virtual const uint8_t* data() const override; @@ -6660,8 +6672,9 @@ struct facebook::react::Bridging { public static facebook::jsi::WeakObject fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Object& value); } -struct facebook::react::Bridging { - public static facebook::jsi::Value toJs(facebook::jsi::Runtime& rt, facebook::react::AsyncArrayBuffer buffer); +struct facebook::react::Bridging { + public static facebook::jsi::BigInt toJs(facebook::jsi::Runtime& rt, const facebook::react::BigInt& value); + public static facebook::react::BigInt fromJs(facebook::jsi::Runtime& rt, const facebook::jsi::Value& value); } struct facebook::react::Bridging : public NativeAnimatedTurboModuleEndResultBridging {