internal import jsi /// A Swift representation of a JavaScript BigInt. BigInt is a built-in JavaScript object that provides /// a way to represent whole numbers larger than 2^53 - 1, which is the largest number JavaScript can /// reliably represent with the Number primitive. /// /// BigInts can be created from 64-bit integers (signed or unsigned) and can be converted back to /// 64-bit integers with optional validation to ensure no data loss. /// /// ## Example Usage /// ```swift /// let runtime = JavaScriptRuntime() /// /// // Create from Int64 /// let bigInt = JavaScriptBigInt(runtime, fromInt64: 9007199254740991) /// /// // Create from UInt64 /// let largeBigInt = JavaScriptBigInt(runtime, fromUint64: UInt64.max) /// /// // Convert to string with different radixes /// let decimal = bigInt.toString(radix: 10) // "9007199254740991" /// let hex = bigInt.toString(radix: 16) // "1fffffffffffff" /// let binary = bigInt.toString(radix: 2) // "11111111111111111111111111111111111111111111111111111" /// /// // Safe conversion back to integers /// if bigInt.isInt64() { /// let value = try bigInt.asInt64() // Safe conversion /// } /// /// // Equality testing /// let another = JavaScriptBigInt(runtime, fromInt64: 9007199254740991) /// if bigInt == another { /// print("BigInts are equal") /// } /// ``` /// /// - Note: BigInt values cannot be mixed with Number values in arithmetic operations in JavaScript. /// You must explicitly convert between them. public struct JavaScriptBigInt: JavaScriptType, Sendable, ~Copyable { internal weak let runtime: JavaScriptRuntime? internal var pointee: facebook.jsi.BigInt /// Creates a BigInt from an existing JSI BigInt. internal init(_ runtime: JavaScriptRuntime, _ bigInt: consuming facebook.jsi.BigInt) { self.runtime = runtime self.pointee = bigInt } /// Creates a BigInt from a signed 64-bit integer. /// /// - Parameters: /// - runtime: The JavaScript runtime /// - value: The Int64 value to convert to BigInt public init(_ runtime: JavaScriptRuntime, fromInt64 value: Int64) { self.runtime = runtime self.pointee = facebook.jsi.BigInt.fromInt64(runtime.pointee, value) } /// Creates a BigInt from an unsigned 64-bit integer. /// /// - Parameters: /// - runtime: The JavaScript runtime /// - value: The UInt64 value to convert to BigInt public init(_ runtime: JavaScriptRuntime, fromUint64 value: UInt64) { self.runtime = runtime self.pointee = facebook.jsi.BigInt.fromUint64(runtime.pointee, value) } /// Creates a BigInt from a string representation. /// /// This initializer parses a string to create a BigInt value. The string can be in decimal format /// or use prefixes for other bases (e.g., "0x" for hexadecimal, "0o" for octal, "0b" for binary). /// Unlike the numeric initializers which are limited to 64-bit ranges, this initializer can create /// BigInt values of arbitrary size. /// /// - Parameters: /// - runtime: The JavaScript runtime /// - string: A string representation of the number to parse. Can include: /// - Decimal: "12345" or "-12345" /// - Hexadecimal: "0xFF" or "0xff" /// - Octal: "0o77" /// - Binary: "0b1111" /// /// - Throws: An error if the string cannot be parsed as a valid BigInt. This can occur if: /// - The string is empty /// - The string contains invalid characters /// - The string format is not recognized by JavaScript's `BigInt()` constructor /// /// - Note: This method calls JavaScript's `BigInt()` constructor internally, so it follows /// the same parsing rules as JavaScript. Whitespace is trimmed automatically. /// /// ## Examples /// ```swift /// // Create from decimal string /// let bigInt1 = try JavaScriptBigInt(runtime, string: "123456789012345678901234567890") /// /// // Create from hexadecimal string /// let bigInt2 = try JavaScriptBigInt(runtime, string: "0xFFFFFFFFFFFFFFFF") /// /// // Create from binary string /// let bigInt3 = try JavaScriptBigInt(runtime, string: "0b11111111") /// /// // Create from octal string /// let bigInt4 = try JavaScriptBigInt(runtime, string: "0o777") /// /// // Negative values /// let bigInt5 = try JavaScriptBigInt(runtime, string: "-999999999999999999") /// /// // Handle errors /// do { /// let bigInt = try JavaScriptBigInt(runtime, string: "invalid") /// } catch { /// print("Failed to parse BigInt: \(error)") /// } /// ``` public init(_ runtime: JavaScriptRuntime, string: String) throws { self.runtime = runtime self.pointee = try runtime .global() .getPropertyAsFunction("BigInt") .call(arguments: string) .pointee .getBigInt(runtime.pointee) } // MARK: - Conversions to Integer Types /// Returns whether this BigInt can be losslessly converted to a signed 64-bit integer. /// /// If this returns `true`, calling `asInt64()` will succeed without throwing. /// /// - Returns: `true` if the BigInt fits in an Int64 range, `false` otherwise public func isInt64() -> Bool { guard let runtime else { FatalError.runtimeLost() } return pointee.isInt64(runtime.pointee) } /// Returns whether this BigInt can be losslessly converted to an unsigned 64-bit integer. /// /// If this returns `true`, calling `asUint64()` will succeed without throwing. /// /// - Returns: `true` if the BigInt fits in a UInt64 range, `false` otherwise public func isUint64() -> Bool { guard let runtime else { FatalError.runtimeLost() } return pointee.isUint64(runtime.pointee) } /// Returns this BigInt truncated to a signed 64-bit integer. /// /// This method always succeeds but may lose data if the BigInt is outside the Int64 range. /// Use `isInt64()` first to check if conversion is lossless, or use `asInt64()` for safe conversion. /// /// - Returns: The truncated Int64 value public func getInt64() -> Int64 { guard let runtime else { FatalError.runtimeLost() } return pointee.getInt64(runtime.pointee) } /// Returns this BigInt truncated to an unsigned 64-bit integer. /// /// This method always succeeds but may lose data if the BigInt is outside the UInt64 range. /// Use `isUint64()` first to check if conversion is lossless, or use `asUint64()` for safe conversion. /// /// - Returns: The truncated UInt64 value public func getUint64() -> UInt64 { guard let runtime else { FatalError.runtimeLost() } return pointee.getUint64(runtime.pointee) } /// Returns this BigInt as a signed 64-bit integer, throwing an error if the conversion is lossy. /// /// Use this method when you need to ensure no data is lost during conversion. /// /// - Returns: The Int64 value /// - Throws: `BigIntConversionError` if the BigInt is outside the Int64 range public func asInt64() throws -> Int64 { guard isInt64() else { throw BigIntConversionError.outOfInt64Range } return getInt64() } /// Returns this BigInt as an unsigned 64-bit integer, throwing an error if the conversion is lossy. /// /// Use this method when you need to ensure no data is lost during conversion. /// /// - Returns: The UInt64 value /// - Throws: `BigIntConversionError` if the BigInt is outside the UInt64 range public func asUint64() throws -> UInt64 { guard isUint64() else { throw BigIntConversionError.outOfUint64Range } return getUint64() } // MARK: - String Conversion /// Converts this BigInt to a string representation in the specified radix (base). /// /// The radix can be any value from 2 to 36. Common bases include: /// - 2: Binary /// - 8: Octal /// - 10: Decimal (default) /// - 16: Hexadecimal /// /// - Parameter radix: The base to use for string conversion (2-36). Defaults to 10. /// - Returns: A string representation of the BigInt /// - Throws: `BigIntConversionError.invalidRadix` if radix is not in the valid range /// /// ## Examples /// ```swift /// let bigInt = JavaScriptBigInt(runtime, fromInt64: 255) /// bigInt.toString(radix: 2) // "11111111" /// bigInt.toString(radix: 8) // "377" /// bigInt.toString(radix: 10) // "255" /// bigInt.toString(radix: 16) // "ff" /// ``` public func toString(radix: Int = 10) throws -> String { guard let runtime else { FatalError.runtimeLost() } guard radix >= 2 && radix <= 36 else { throw BigIntConversionError.invalidRadix(radix) } let jsiString = pointee.toString(runtime.pointee, Int32(radix)) return String(jsiString.utf16(runtime.pointee)) } // MARK: - JavaScriptType public func asValue() -> JavaScriptValue { guard let runtime else { FatalError.runtimeLost() } return JavaScriptValue(runtime, facebook.jsi.Value(runtime.pointee, pointee)) } /// Returns the BigInt as a `facebook.jsi.Value` instance. internal func asJSIValue() -> facebook.jsi.Value { guard let runtime else { FatalError.runtimeLost() } return facebook.jsi.Value(runtime.pointee, pointee) } // MARK: - Equality /// Tests whether two BigInts are strictly equal (represent the same numeric value). /// /// This performs a strict equality check as defined by JavaScript's `===` operator for BigInts. /// /// - Parameters: /// - lhs: The first BigInt to compare /// - rhs: The second BigInt to compare /// - Returns: `true` if the BigInts represent the same value, `false` otherwise public static func == (lhs: borrowing JavaScriptBigInt, rhs: borrowing JavaScriptBigInt) -> Bool { guard let runtime = lhs.runtime else { FatalError.runtimeLost() } return facebook.jsi.BigInt.strictEquals(runtime.pointee, lhs.pointee, rhs.pointee) } } extension JavaScriptBigInt: JavaScriptRepresentable { public static func fromJavaScriptValue(_ value: JavaScriptValue) -> JavaScriptBigInt { return value.getBigInt() } public func toJavaScriptValue(in runtime: JavaScriptRuntime) -> JavaScriptValue { return asValue() } } extension JavaScriptBigInt: JSIRepresentable { static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> JavaScriptBigInt { fatalError("Not implemented: Use JavaScriptValue.getBigInt() instead") } func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value { return asJSIValue() } } // MARK: - Errors /// Errors that can occur when working with BigInt conversions. public enum BigIntConversionError: Error, CustomStringConvertible { /// The BigInt value is outside the valid Int64 range (-2^63 to 2^63-1) case outOfInt64Range /// The BigInt value is outside the valid UInt64 range (0 to 2^64-1) case outOfUint64Range /// The radix parameter is not in the valid range (2-36) case invalidRadix(Int) public var description: String { switch self { case .outOfInt64Range: return "BigInt value is outside the Int64 range (-9223372036854775808 to 9223372036854775807)" case .outOfUint64Range: return "BigInt value is outside the UInt64 range (0 to 18446744073709551615)" case .invalidRadix(let radix): return "Invalid radix \(radix). Radix must be between 2 and 36." } } }