diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index 0c8bdec56da47a..2929ae9e23fdc1 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -2501,6 +2501,82 @@ extern "C" napi_status napi_typeof(napi_env env, napi_value val, return napi_generic_failure; } +static_assert(std::is_same_v, "All NAPI bigint functions assume that bigint words are 64 bits"); +#if USE(BIGINT32) +#error All NAPI bigint functions assume that BIGINT32 is disabled +#endif + +extern "C" napi_status napi_get_value_bigint_int64(napi_env env, napi_value value, int64_t* result, bool* lossless) +{ + NAPI_PREMABLE + JSValue jsValue = toJS(value); + if (jsValue.isEmpty() || !result) { + return napi_invalid_arg; + } + if (!jsValue.isHeapBigInt()) { + return napi_bigint_expected; + } + + JSBigInt* bigint = jsValue.asHeapBigInt(); + + if (bigint->isZero()) { + *result = 0; + if (lossless != nullptr) *lossless = true; + } else { + uint64_t digit = bigint->digit(0); + uint64_t signed_digit = digit; + if (bigint->sign()) { + // negate, while keeping it unsigned because i don't trust signed overflow + signed_digit = ~signed_digit + 1; + } + memcpy(result, &signed_digit, sizeof(signed_digit)); + + if (lossless != nullptr) { + if (bigint->length() > 1) { + *lossless = false; + } else if (bigint->sign()) { + // negative + // lossless if numeric value is >= -2^63, + // for which digit will be <= 2^63 + *lossless = (digit <= (1ull << 63)); + } else { + // positive + // lossless if numeric value is <= 2^63 - 1 + *lossless = (digit <= static_cast(INT64_MAX)); + } + } + } + + return napi_ok; +} + +extern "C" napi_status napi_get_value_bigint_uint64(napi_env env, napi_value value, uint64_t* result, bool* lossless) +{ + NAPI_PREMABLE + JSValue jsValue = toJS(value); + if (jsValue.isEmpty() || !result) { + return napi_invalid_arg; + } + if (!jsValue.isHeapBigInt()) { + return napi_bigint_expected; + } + + JSBigInt* bigint = jsValue.asHeapBigInt(); + + if (bigint->isZero()) { + *result = 0; + if (lossless != nullptr) *lossless = true; + } else { + *result = bigint->digit(0); + if (lossless != nullptr) { + // lossless if and only if only one digit and positive + *lossless = (bigint->length() == 1 && bigint->sign() == false); + } + } + + return napi_ok; +} + extern "C" napi_status napi_get_value_bigint_words(napi_env env, napi_value value, int* sign_bit, diff --git a/src/napi/napi.zig b/src/napi/napi.zig index c8d735d0b23f37..ab089d9b6d1f0c 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -1032,26 +1032,8 @@ pub export fn napi_create_bigint_uint64(env: napi_env, value: u64, result_: ?*na return .ok; } pub extern fn napi_create_bigint_words(env: napi_env, sign_bit: c_int, word_count: usize, words: [*c]const u64, result: *napi_value) napi_status; -// TODO: lossless -pub export fn napi_get_value_bigint_int64(_: napi_env, value_: napi_value, result_: ?*i64, _: *bool) napi_status { - log("napi_get_value_bigint_int64", .{}); - const result = result_ orelse { - return invalidArg(); - }; - const value = value_.get(); - result.* = value.toInt64(); - return .ok; -} -// TODO: lossless -pub export fn napi_get_value_bigint_uint64(_: napi_env, value_: napi_value, result_: ?*u64, _: *bool) napi_status { - log("napi_get_value_bigint_uint64", .{}); - const result = result_ orelse { - return invalidArg(); - }; - const value = value_.get(); - result.* = value.toUInt64NoTruncate(); - return .ok; -} +pub extern fn napi_get_value_bigint_int64(env: napi_env, value: napi_value, result: ?*i64, lossless: ?*bool) napi_status; +pub extern fn napi_get_value_bigint_uint64(env: napi_env, value: napi_value, result: ?*u64, lossless: ?*bool) napi_status; pub extern fn napi_get_value_bigint_words(env: napi_env, value: napi_value, sign_bit: [*c]c_int, word_count: [*c]usize, words: [*c]u64) napi_status; pub extern fn napi_get_all_property_names(env: napi_env, object: napi_value, key_mode: napi_key_collection_mode, key_filter: napi_key_filter, key_conversion: napi_key_conversion, result: *napi_value) napi_status;