From 7a33e4e90a6c4f6f49d2f97b58e143f38f0e3a95 Mon Sep 17 00:00:00 2001 From: Chi Tsai Date: Wed, 13 May 2026 15:27:57 -0700 Subject: [PATCH] Add JSI methods for BigInt with data larger than 64-bits Summary: The existing JSI BigInt API can only construct or read back values that fit in 64 bits (createBigIntFromInt64 / createBigIntFromUint64 / truncate). This diff adds two new IRuntime methods so JSI consumers can round-trip arbitrarily large BigInt values. - `createBigIntFromBytes(const uint8_t* bytes, size_t length)`: constructs a BigInt from a raw little-endian, two's complement byte sequence. The most significant bit of the highest-indexed byte is the sign bit. - `bytes(const BigInt&)`: returns the BigInt's canonical little-endian, two's complement byte representation. The result round-trips through createBigIntFromBytes. Pieces in this diff: - jsi.h: new pure-virtual IRuntime methods, matching Runtime override declarations, and `BigInt::fromBytes` / `BigInt::bytes` convenience helpers with detailed docs describing the byte format. - jsi.cpp: default Runtime implementations throw JSINativeException so runtimes that have not opted in surface a clear error rather than silently misbehaving. - decorator.h: forwarding overrides on RuntimeDecorator and WithRuntimeDecorator. - React Native fork (xplat/js/react-native-github/.../jsi): mirrored changes to keep the two trees in sync. Differential Revision: D105071004 --- .../ReactCommon/jsi/jsi/decorator.h | 14 +++++++++++ .../react-native/ReactCommon/jsi/jsi/jsi.cpp | 11 +++++++++ .../react-native/ReactCommon/jsi/jsi/jsi.h | 24 +++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/packages/react-native/ReactCommon/jsi/jsi/decorator.h b/packages/react-native/ReactCommon/jsi/jsi/decorator.h index f101c1832e61..4bc4f18caa44 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/decorator.h +++ b/packages/react-native/ReactCommon/jsi/jsi/decorator.h @@ -210,6 +210,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { BigInt createBigIntFromUint64(uint64_t value) override { return plain_.createBigIntFromUint64(value); } + BigInt createBigIntFromBytes(const uint8_t* bytes, size_t length) override { + return plain_.createBigIntFromBytes(bytes, length); + } bool bigintIsInt64(const BigInt& b) override { return plain_.bigintIsInt64(b); } @@ -222,6 +225,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { String bigintToString(const BigInt& bigint, int radix) override { return plain_.bigintToString(bigint, radix); } + std::vector bytes(const BigInt& b) override { + return plain_.bytes(b); + } String createStringFromAscii(const char* str, size_t length) override { return plain_.createStringFromAscii(str, length); @@ -779,6 +785,10 @@ class WithRuntimeDecorator : public RuntimeDecorator { Around around{with_}; return RD::createBigIntFromUint64(i); } + BigInt createBigIntFromBytes(const uint8_t* bytes, size_t length) override { + Around around{with_}; + return RD::createBigIntFromBytes(bytes, length); + } bool bigintIsInt64(const BigInt& bi) override { Around around{with_}; return RD::bigintIsInt64(bi); @@ -795,6 +805,10 @@ class WithRuntimeDecorator : public RuntimeDecorator { Around around{with_}; return RD::bigintToString(bi, i); } + std::vector bytes(const BigInt& bi) override { + Around around{with_}; + return RD::bytes(bi); + } String createStringFromAscii(const char* str, size_t length) override { Around around{with_}; diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp b/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp index 792b5bf5a0bf..6536372194ab 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp @@ -606,6 +606,17 @@ size_t Runtime::length(const String& str) { return utf16(str).size(); } +BigInt Runtime::createBigIntFromBytes( + const uint8_t* /*bytes*/, + size_t /*length*/) { + throw JSINativeException( + "createBigIntFromBytes is not implemented by this runtime"); +} + +std::vector Runtime::bytes(const BigInt& /*bigint*/) { + throw JSINativeException("BigInt.bytes is not implemented by this runtime"); +} + bool Runtime::detached(const ArrayBuffer& buffer) { Value prop = buffer.getProperty(*this, "detached"); if (!prop.isBool()) { diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi.h b/packages/react-native/ReactCommon/jsi/jsi/jsi.h index bf98028fb3fb..aaa17ae90982 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi.h +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi.h @@ -498,10 +498,17 @@ class JSI_EXPORT IRuntime : public ICast { virtual BigInt createBigIntFromInt64(int64_t) = 0; virtual BigInt createBigIntFromUint64(uint64_t) = 0; + /// Create a BigInt from a raw byte sequence \p bytes of \p length, which + /// provides the BigInt data in little-endian, two's complement + /// representation. The data is copied. + virtual BigInt createBigIntFromBytes(const uint8_t* bytes, size_t length) = 0; virtual bool bigintIsInt64(const BigInt&) = 0; virtual bool bigintIsUint64(const BigInt&) = 0; virtual uint64_t truncate(const BigInt&) = 0; virtual String bigintToString(const BigInt&, int) = 0; + /// Returns the raw byte representation of a BigInt in little-endian, two's + /// complement representation. + virtual std::vector bytes(const BigInt&) = 0; virtual String createStringFromAscii(const char* str, size_t length) = 0; virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0; @@ -793,6 +800,9 @@ class JSI_EXPORT Runtime : public IRuntime { size_t length(const String& str) override; + BigInt createBigIntFromBytes(const uint8_t* bytes, size_t length) override; + std::vector bytes(const BigInt&) override; + protected: friend class Pointer; friend class PropNameID; @@ -1004,6 +1014,20 @@ class JSI_EXPORT BigInt : public Pointer { return runtime.createBigIntFromUint64(value); } + /// Create a BigInt from a raw byte sequence \p bytes of \p length, which + /// provides the BigInt data in little-endian, two's complement + /// representation. The data is copied. + static BigInt + fromBytes(IRuntime& runtime, const uint8_t* bytes, size_t length) { + return runtime.createBigIntFromBytes(bytes, length); + } + + /// Returns the raw byte representation of a BigInt in little-endian, two's + /// complement representation. + std::vector bytes(IRuntime& runtime) const { + return runtime.bytes(*this); + } + /// \return whether a === b. static bool strictEquals(IRuntime& runtime, const BigInt& a, const BigInt& b) {