From 8ca9986eec1f9d806b982b1def24d512b49a7f38 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Sun, 11 Oct 2026 21:53:25 +0800 Subject: [PATCH] feat(webidl): derive wrapping signed long long conversion Extracted from wpt-misc-fix. Source-commit: 4fb35a340e3dd773d2f805c5c4e6bed2e8e71b69 --- moli-webidl-derive/src/converter.rs | 8 ++++ moli-webidl/src/convert.rs | 13 +++++++ moli-webidl/src/lib.rs | 4 +- moli-webidl/src/types.rs | 9 +++++ moli-webidl/tests/long_long.rs | 60 +++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 moli-webidl/tests/long_long.rs diff --git a/moli-webidl-derive/src/converter.rs b/moli-webidl-derive/src/converter.rs index f2ec9999d8..2f926bbb27 100644 --- a/moli-webidl-derive/src/converter.rs +++ b/moli-webidl-derive/src/converter.rs @@ -14,6 +14,7 @@ pub(crate) enum ConverterKind { CallbackFunction, CallbackInterface, Long, + LongLong, EnforceRangeLong, Short, UnsignedShort, @@ -49,6 +50,7 @@ impl ConverterKind { "callback_function" => Ok(Self::CallbackFunction), "callback_interface" => Ok(Self::CallbackInterface), "long" => Ok(Self::Long), + "long_long" => Ok(Self::LongLong), "enforce_range_long" => Ok(Self::EnforceRangeLong), "short" => Ok(Self::Short), "unsigned_short" => Ok(Self::UnsignedShort), @@ -81,6 +83,7 @@ impl ConverterKind { Self::CallbackFunction => quote!(::moli_webidl::WebIdlCallbackFunction), Self::CallbackInterface => quote!(::moli_webidl::WebIdlCallbackInterface), Self::Long => quote!(::moli_webidl::Long), + Self::LongLong => quote!(::moli_webidl::LongLong), Self::EnforceRangeLong => quote!(::moli_webidl::EnforceRangeLong), Self::Short => quote!(::moli_webidl::Short), Self::UnsignedShort => quote!(::moli_webidl::UnsignedShort), @@ -122,6 +125,7 @@ impl ConverterKind { Self::BufferSource => quote!(::moli_webidl::BufferSource::from(#expr)), Self::CallbackFunction | Self::CallbackInterface => quote!(#expr), Self::Long => quote!(::moli_webidl::Long(#expr)), + Self::LongLong => quote!(::moli_webidl::LongLong(#expr)), Self::EnforceRangeLong => quote!(::moli_webidl::EnforceRangeLong(#expr)), Self::Short => quote!(::moli_webidl::Short(#expr)), Self::UnsignedShort => quote!(::moli_webidl::UnsignedShort(#expr)), @@ -158,6 +162,7 @@ impl ConverterKind { Self::DomString | Self::UsvString | Self::Long + | Self::LongLong | Self::EnforceRangeLong | Self::Short | Self::UnsignedShort @@ -372,6 +377,9 @@ fn infer_converter_kind(ty: &Type) -> Option { if is_type_ident(ty, "i16") { return Some(ConverterKind::Short); } + if is_type_ident(ty, "i64") { + return Some(ConverterKind::LongLong); + } if is_type_ident(ty, "u64") { return Some(ConverterKind::UnsignedLongLong); } diff --git a/moli-webidl/src/convert.rs b/moli-webidl/src/convert.rs index 75f806846a..0d94186df0 100644 --- a/moli-webidl/src/convert.rs +++ b/moli-webidl/src/convert.rs @@ -331,6 +331,19 @@ impl<'s> WebIdlConverter<'s> for UnsignedLongLong { } } +impl<'s> WebIdlConverter<'s> for crate::LongLong { + type Options = (); + fn convert( + scope: &mut v8::PinScope<'s, '_>, + value: v8::Local<'s, v8::Value>, + context: Context, + _: &(), + ) -> Result { + number_value(scope, value, context, "long long") + .map(|value| Self(unsigned_long_long(value) as i64)) + } +} + impl<'s> WebIdlConverter<'s> for EnforceRangeLongLong { type Options = (); diff --git a/moli-webidl/src/lib.rs b/moli-webidl/src/lib.rs index 32648cb465..2e03bee005 100644 --- a/moli-webidl/src/lib.rs +++ b/moli-webidl/src/lib.rs @@ -95,8 +95,8 @@ pub use traits::{ParseOutcome, WebIdlArguments, WebIdlConverter, WebIdlDictionar pub use types::{ Boolean, BufferSource, ByteString, ClampedUnsignedShort, Dictionary, DomString, DomString16, Double, EnforceRangeLong, EnforceRangeUnsignedLong, EnforceRangeUnsignedLongLong, EnumValue, - EventListenerOptions, InterfaceObject, InterfaceOptions, Long, Record, Sequence, Short, - StringOptions, UnrestrictedDouble, UnrestrictedFloat, UnsignedLong, UnsignedLongLong, + EventListenerOptions, InterfaceObject, InterfaceOptions, Long, LongLong, Record, Sequence, + Short, StringOptions, UnrestrictedDouble, UnrestrictedFloat, UnsignedLong, UnsignedLongLong, UnsignedShort, UsvString, }; diff --git a/moli-webidl/src/types.rs b/moli-webidl/src/types.rs index c5be05eaf7..fd4a9af9e0 100644 --- a/moli-webidl/src/types.rs +++ b/moli-webidl/src/types.rs @@ -298,6 +298,15 @@ impl From for u16 { } } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct LongLong(pub i64); + +impl From for i64 { + fn from(value: LongLong) -> Self { + value.0 + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct UnsignedLongLong(pub u64); diff --git a/moli-webidl/tests/long_long.rs b/moli-webidl/tests/long_long.rs new file mode 100644 index 0000000000..ccca9777ff --- /dev/null +++ b/moli-webidl/tests/long_long.rs @@ -0,0 +1,60 @@ +use moli_webidl::{Context, WebIdlDictionary}; + +#[derive(WebIdlDictionary)] +#[webidl(prefix = "LongLongInit")] +struct Init { + #[webidl(default = 0)] + value: i64, + #[webidl(converter = "long_long", default = 0)] + explicit: i64, +} + +#[test] +fn long_long_derive_wraps_and_preserves_conversion_errors() { + moli_v8_test_util::ensure_v8(); + let mut isolate = v8::Isolate::new(v8::CreateParams::default()); + v8::scope!(let scope, &mut isolate); + let context = v8::Context::new(scope, Default::default()); + let scope = &mut v8::ContextScope::new(scope, context); + for (expression, expected) in [ + ("-17.9", -17), + ("NaN", 0), + ("Infinity", 0), + ("undefined", 0), + ("2**63", i64::MIN), + ("2**64", 0), + ("-(2**63)-2048", i64::MAX - 2047), + ] { + let source = v8::String::new( + scope, + &format!("({{value:{expression},explicit:{expression}}})"), + ) + .unwrap(); + let value = v8::Script::compile(scope, source, None) + .unwrap() + .run(scope) + .unwrap(); + let init = + moli_webidl::parse_dictionary::(scope, value, Context::argument("Test", 1)) + .unwrap() + .unwrap(); + assert_eq!(init.value, expected, "{expression}"); + assert_eq!(init.explicit, expected, "{expression}"); + } + for source in [ + "({value:Symbol(),explicit:0})", + "({value:0,explicit:1n})", + "({get explicit(){throw 42},value:0})", + ] { + v8::tc_scope!(let scope, scope); + let source = v8::String::new(scope, source).unwrap(); + let value = v8::Script::compile(scope, source, None) + .unwrap() + .run(scope) + .unwrap(); + assert!( + moli_webidl::parse_dictionary::(scope, value, Context::argument("Test", 1)) + .is_err() + ); + } +}