diff --git a/src/bun.js/bindings/v8/V8Value.cpp b/src/bun.js/bindings/v8/V8Value.cpp index 68499be9b5..05ecde4c85 100644 --- a/src/bun.js/bindings/v8/V8Value.cpp +++ b/src/bun.js/bindings/v8/V8Value.cpp @@ -23,7 +23,7 @@ bool Value::IsNumber() const bool Value::IsUint32() const { - return localToJSValue().isUInt32(); + return localToJSValue().isUInt32AsAnyInt(); } bool Value::IsUndefined() const @@ -64,11 +64,10 @@ bool Value::IsFunction() const Maybe Value::Uint32Value(Local context) const { auto js_value = localToJSValue(); - uint32_t value; - if (js_value.getUInt32(value)) { - return Just(value); - } - return Nothing(); + auto scope = DECLARE_THROW_SCOPE(context->vm()); + uint32_t num = js_value.toUInt32(context->globalObject()); + RETURN_IF_EXCEPTION(scope, Nothing()); + RELEASE_AND_RETURN(scope, Just(num)); } bool Value::FullIsTrue() const diff --git a/test/v8/v8-module/main.cpp b/test/v8/v8-module/main.cpp index 965b426a84..fb54c9abe7 100644 --- a/test/v8/v8-module/main.cpp +++ b/test/v8/v8-module/main.cpp @@ -13,6 +13,17 @@ using namespace v8; #define LOG_EXPR(e) std::cout << #e << " = " << (e) << std::endl +#define LOG_MAYBE(m) \ + do { \ + auto maybe__ = (m); \ + std::cout << #m << " = "; \ + if (maybe__.IsJust()) { \ + std::cout << "Just(" << maybe__.FromJust() << ")" << std::endl; \ + } else { \ + std::cout << "Nothing" << std::endl; \ + } \ + } while (0) + #define LOG_VALUE_KIND(v) \ do { \ LOG_EXPR(v->IsUndefined()); \ @@ -24,6 +35,7 @@ using namespace v8; LOG_EXPR(v->IsString()); \ LOG_EXPR(v->IsObject()); \ LOG_EXPR(v->IsNumber()); \ + LOG_EXPR(v->IsUint32()); \ } while (0) namespace v8tests { @@ -115,6 +127,7 @@ static void perform_number_test(const FunctionCallbackInfo &info, Local v8_number = Number::New(isolate, number); LOG_EXPR(v8_number->Value()); + LOG_MAYBE(v8_number->Uint32Value(isolate->GetCurrentContext())); LOG_VALUE_KIND(v8_number); return ok(info); @@ -125,7 +138,9 @@ void test_v8_number_int(const FunctionCallbackInfo &info) { } void test_v8_number_large_int(const FunctionCallbackInfo &info) { - // 2^33 + // 2^31 (should fit as uint32 but not as int32) + perform_number_test(info, 2147483648.0); + // 2^33 (should not fit as any 32-bit integer) perform_number_test(info, 8589934592.0); } @@ -133,6 +148,28 @@ void test_v8_number_fraction(const FunctionCallbackInfo &info) { perform_number_test(info, 2.5); } +void test_v8_value_uint32value(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Local vals[] = { + String::NewFromUtf8(isolate, "53").ToLocalChecked(), + Boolean::New(isolate, true), + Number::New(isolate, -1.5), + Number::New(isolate, 8589934593.9), + }; + + for (int i = 0; i < 4; i++) { + Maybe maybe_u32 = vals[i]->Uint32Value(context); + LOG_MAYBE(maybe_u32); + } +} + +void call_uint32value_on_arg_from_js(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + LOG_MAYBE(info[0]->Uint32Value(context)); +} + static void perform_string_test(const FunctionCallbackInfo &info, Local v8_string) { Isolate *isolate = info.GetIsolate(); @@ -271,12 +308,7 @@ void test_v8_object(const FunctionCallbackInfo &info) { Local obj = Object::New(isolate); auto key = String::NewFromUtf8(isolate, "key").ToLocalChecked(); auto val = Number::New(isolate, 5.0); - Maybe set_status = obj->Set(context, key, val); - LOG_EXPR(set_status.IsJust()); - LOG_EXPR(set_status.FromJust()); - - // Local retval = obj->Get(context, key).ToLocalChecked(); - // LOG_EXPR(describe(isolate, retval)); + LOG_MAYBE(obj->Set(context, key, val)); return ok(info); } @@ -288,11 +320,7 @@ void set_field_from_js(const FunctionCallbackInfo &info) { Local obj = info[0].As(); Local key = info[1]; Local value = Number::New(isolate, 321.0); - Maybe ret = obj->Set(context, key, value); - LOG_EXPR(ret.IsJust()); - if (ret.IsJust()) { - LOG_EXPR(ret.ToChecked()); - } + LOG_MAYBE(obj->Set(context, key, value)); return ok(info); } @@ -702,6 +730,10 @@ void initialize(Local exports, Local module, NODE_SET_METHOD(exports, "test_v8_number_large_int", test_v8_number_large_int); NODE_SET_METHOD(exports, "test_v8_number_fraction", test_v8_number_fraction); + NODE_SET_METHOD(exports, "test_v8_value_uint32value", + test_v8_value_uint32value); + NODE_SET_METHOD(exports, "call_uint32value_on_arg_from_js", + call_uint32value_on_arg_from_js); NODE_SET_METHOD(exports, "test_v8_string_ascii", test_v8_string_ascii); NODE_SET_METHOD(exports, "test_v8_string_utf8", test_v8_string_utf8); NODE_SET_METHOD(exports, "test_v8_string_invalid_utf8", diff --git a/test/v8/v8-module/module.js b/test/v8/v8-module/module.js index 6f0b07bf72..e1217ccf0b 100644 --- a/test/v8/v8-module/module.js +++ b/test/v8/v8-module/module.js @@ -26,6 +26,34 @@ module.exports = debugMode => { return { ...nativeModule, + test_v8_value_uint32value_throw() { + // TODO(@190n) once Symbol and BigInt are supported in the V8 API, do this test in C++ + try { + nativeModule.call_uint32value_on_arg_from_js(Symbol("20")); + console.log("Uint32Value() on Symbol did not throw"); + } catch (e) { + console.log("threw", e.name); + } + + try { + nativeModule.call_uint32value_on_arg_from_js(190n); + console.log("Uint32Value() on BigInt did not throw"); + } catch (e) { + console.log("threw", e.name); + } + + try { + nativeModule.call_uint32value_on_arg_from_js({ + [Symbol.toPrimitive]() { + throw new RangeError("oops"); + }, + }); + console.log("Uint32Value() on object with [Symbol.toPrimitive] that throws did not throw"); + } catch (e) { + console.log("threw", e.toString()); + } + }, + test_v8_global() { console.log("global initial value =", nativeModule.global_get()); diff --git a/test/v8/v8.test.ts b/test/v8/v8.test.ts index 9abb4209ba..dc0411406f 100644 --- a/test/v8/v8.test.ts +++ b/test/v8/v8.test.ts @@ -135,6 +135,17 @@ describe("Number", () => { }); }); +describe("Value", () => { + describe("Uint32Value", () => { + it("coerces correctly", () => { + checkSameOutput("test_v8_value_uint32value", []); + }); + it("throws in the right cases", () => { + checkSameOutput("test_v8_value_uint32value_throw", []); + }); + }); +}); + describe("String", () => { it("can create and read back strings with only ASCII characters", () => { checkSameOutput("test_v8_string_ascii", []);