mirror of
https://github.com/lexmount/moli.git
synced 2026-10-30 08:07:38 +00:00
feat(webidl): support EnforceRange signed long long
This commit is contained in:
@@ -20,6 +20,7 @@ pub(crate) enum ConverterKind {
|
||||
ClampedUnsignedShort,
|
||||
UnsignedLong,
|
||||
EnforceRangeUnsignedLong,
|
||||
EnforceRangeLongLong,
|
||||
UnsignedLongLong,
|
||||
EnforceRangeUnsignedLongLong,
|
||||
Float,
|
||||
@@ -54,6 +55,7 @@ impl ConverterKind {
|
||||
"clamped_unsigned_short" => Ok(Self::ClampedUnsignedShort),
|
||||
"unsigned_long" => Ok(Self::UnsignedLong),
|
||||
"enforce_range_unsigned_long" => Ok(Self::EnforceRangeUnsignedLong),
|
||||
"enforce_range_long_long" => Ok(Self::EnforceRangeLongLong),
|
||||
"unsigned_long_long" => Ok(Self::UnsignedLongLong),
|
||||
"enforce_range_unsigned_long_long" => Ok(Self::EnforceRangeUnsignedLongLong),
|
||||
"float" => Ok(Self::Float),
|
||||
@@ -87,6 +89,7 @@ impl ConverterKind {
|
||||
Self::EnforceRangeUnsignedLong => {
|
||||
quote!(::moli_webidl::EnforceRangeUnsignedLong)
|
||||
}
|
||||
Self::EnforceRangeLongLong => quote!(::moli_webidl::EnforceRangeLongLong),
|
||||
Self::UnsignedLongLong => quote!(::moli_webidl::UnsignedLongLong),
|
||||
Self::EnforceRangeUnsignedLongLong => {
|
||||
quote!(::moli_webidl::EnforceRangeUnsignedLongLong)
|
||||
@@ -129,6 +132,7 @@ impl ConverterKind {
|
||||
Self::EnforceRangeUnsignedLong => {
|
||||
quote!(::moli_webidl::EnforceRangeUnsignedLong(#expr))
|
||||
}
|
||||
Self::EnforceRangeLongLong => quote!(::moli_webidl::EnforceRangeLongLong(#expr)),
|
||||
Self::UnsignedLongLong => quote!(::moli_webidl::UnsignedLongLong(#expr)),
|
||||
Self::EnforceRangeUnsignedLongLong => {
|
||||
quote!(::moli_webidl::EnforceRangeUnsignedLongLong(#expr))
|
||||
@@ -160,6 +164,7 @@ impl ConverterKind {
|
||||
| Self::ClampedUnsignedShort
|
||||
| Self::UnsignedLong
|
||||
| Self::EnforceRangeUnsignedLong
|
||||
| Self::EnforceRangeLongLong
|
||||
| Self::UnsignedLongLong
|
||||
| Self::EnforceRangeUnsignedLongLong
|
||||
| Self::Float
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
Boolean, BufferSource, ByteString, ClampedUnsignedShort, Context, Dictionary, DomString,
|
||||
DomString16, Double, EnforceRangeLong, EnforceRangeUnsignedLong, EnforceRangeUnsignedLongLong,
|
||||
EnumValue, Long, Record, Sequence, StringOptions, UnrestrictedDouble, UnsignedLong,
|
||||
UnsignedLongLong, UnsignedShort, UsvString, WebIdlArguments, WebIdlConverter, WebIdlDictionary,
|
||||
WebIdlEnum, WebIdlError, WebIdlErrorKind, dictionary_value, is_nullish, property_result,
|
||||
symbol_property_result, throw_error, throw_type_error,
|
||||
DomString16, Double, EnforceRangeLong, EnforceRangeLongLong, EnforceRangeUnsignedLong,
|
||||
EnforceRangeUnsignedLongLong, EnumValue, Long, Record, Sequence, StringOptions,
|
||||
UnrestrictedDouble, UnsignedLong, UnsignedLongLong, UnsignedShort, UsvString, WebIdlArguments,
|
||||
WebIdlConverter, WebIdlDictionary, WebIdlEnum, WebIdlError, WebIdlErrorKind, dictionary_value,
|
||||
is_nullish, property_result, symbol_property_result, throw_error, throw_type_error,
|
||||
};
|
||||
|
||||
impl<'s> WebIdlConverter<'s> for DomString {
|
||||
@@ -331,6 +331,20 @@ impl<'s> WebIdlConverter<'s> for UnsignedLongLong {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> WebIdlConverter<'s> for EnforceRangeLongLong {
|
||||
type Options = ();
|
||||
|
||||
fn convert(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
context: Context,
|
||||
_options: &Self::Options,
|
||||
) -> Result<Self, WebIdlError> {
|
||||
let value = number_value(scope, value, context, "[EnforceRange] long long")?;
|
||||
enforce_range_long_long(value, context).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> WebIdlConverter<'s> for EnforceRangeUnsignedLongLong {
|
||||
type Options = ();
|
||||
|
||||
@@ -1402,6 +1416,19 @@ fn unsigned_long_long(value: f64) -> u64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_range_long_long(value: f64, context: Context) -> Result<i64, WebIdlError> {
|
||||
// ConvertToInt uses the exact JavaScript integer range for 64-bit types.
|
||||
let value = value.trunc();
|
||||
if !value.is_finite() || !(-9_007_199_254_740_991.0..=9_007_199_254_740_991.0).contains(&value)
|
||||
{
|
||||
return Err(WebIdlError::new(
|
||||
context,
|
||||
WebIdlErrorKind::CannotConvert("[EnforceRange] long long"),
|
||||
));
|
||||
}
|
||||
Ok(value as i64)
|
||||
}
|
||||
|
||||
fn enforce_range_unsigned_long_long(value: f64, context: Context) -> Result<u64, WebIdlError> {
|
||||
// ConvertToInt uses the exact JavaScript integer range for 64-bit types.
|
||||
let value = value.trunc();
|
||||
|
||||
@@ -98,5 +98,6 @@ pub use types::{
|
||||
UnsignedShort, UsvString,
|
||||
};
|
||||
|
||||
pub use types::EnforceRangeLongLong;
|
||||
/// Restricted WebIDL float represented as a finite binary32 value.
|
||||
pub use types::Float;
|
||||
|
||||
@@ -316,6 +316,14 @@ impl From<EnforceRangeUnsignedLongLong> for u64 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct EnforceRangeLongLong(pub i64);
|
||||
impl From<EnforceRangeLongLong> for i64 {
|
||||
fn from(value: EnforceRangeLongLong) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct StringOptions {
|
||||
pub treat_null_as_empty_string: bool,
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
use moli_webidl::{WebIdlArgs, WebIdlDictionary};
|
||||
#[derive(WebIdlArgs)]
|
||||
#[webidl(prefix = "Test.convert")]
|
||||
struct RequiredArgs {
|
||||
#[webidl(required, converter = "enforce_range_long_long")]
|
||||
value: i64,
|
||||
}
|
||||
#[derive(WebIdlDictionary)]
|
||||
#[webidl(prefix = "TestOptions")]
|
||||
struct TestOptions {
|
||||
#[webidl(default = -7, converter = "enforce_range_long_long")]
|
||||
value: i64,
|
||||
}
|
||||
#[derive(WebIdlArgs)]
|
||||
#[webidl(prefix = "Test.dictionary")]
|
||||
struct DictionaryArgs {
|
||||
#[webidl(dictionary)]
|
||||
options: TestOptions,
|
||||
}
|
||||
fn required<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s>,
|
||||
) {
|
||||
if let Some(parsed) = moli_webidl::parse_args::<RequiredArgs>(scope, &args) {
|
||||
rv.set(v8::Number::new(scope, parsed.value as f64).into());
|
||||
}
|
||||
}
|
||||
fn dictionary<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s>,
|
||||
) {
|
||||
if let Some(parsed) = moli_webidl::parse_args::<DictionaryArgs>(scope, &args) {
|
||||
rv.set(v8::Number::new(scope, parsed.options.value as f64).into());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn numeric_conversion_preserves_boundaries_defaults_and_callee_realm_errors() {
|
||||
moli_v8_test_util::ensure_v8();
|
||||
let mut isolate = v8::Isolate::new(v8::CreateParams::default());
|
||||
let scope = std::pin::pin!(v8::HandleScope::new(&mut isolate));
|
||||
let scope = &mut scope.init();
|
||||
let callee = v8::Context::new(scope, Default::default());
|
||||
let values = {
|
||||
let scope = &mut v8::ContextScope::new(scope, callee);
|
||||
let required = v8::Function::new(scope, required).unwrap();
|
||||
let dictionary = v8::Function::new(scope, dictionary).unwrap();
|
||||
let key = v8::String::new(scope, "TypeError").unwrap();
|
||||
let error = callee.global(scope).get(scope, key.into()).unwrap();
|
||||
[
|
||||
("required", required.into()),
|
||||
("dictionary", dictionary.into()),
|
||||
("CalleeTypeError", error),
|
||||
]
|
||||
};
|
||||
let caller = v8::Context::new(scope, Default::default());
|
||||
let scope = &mut v8::ContextScope::new(scope, caller);
|
||||
for (name, value) in values {
|
||||
let key = v8::String::new(scope, name).unwrap();
|
||||
assert_eq!(
|
||||
caller.global(scope).set(scope, key.into(), value),
|
||||
Some(true)
|
||||
);
|
||||
}
|
||||
let source=v8::String::new(scope,r#"(() => {
|
||||
const assert=(value,message)=>{if(!value)throw Error(message);};
|
||||
for(const convert of [required,value=>dictionary({value})]) {
|
||||
for(const [value,expected] of [[0,0],[-0,0],[3.9,3],[-3.9,-3],[9007199254740991,9007199254740991],[-9007199254740991,-9007199254740991],['-7.9',-7],[null,0],[true,1]])assert(Object.is(convert(value),expected),'boundary '+String(value));
|
||||
for(const value of [NaN,Infinity,-Infinity,9007199254740992,-9007199254740992,Symbol('number'),1n]) {let error;try{convert(value);}catch(caught){error=caught;}assert(error instanceof CalleeTypeError&&!(error instanceof TypeError),'callee TypeError');}
|
||||
let reads=0;assert(convert({[Symbol.toPrimitive](hint){reads++;assert(hint==='number','numeric hint');return 4;}})===4&&reads===1,'one conversion');
|
||||
const sentinel={};let error;try{convert({valueOf(){throw sentinel;}});}catch(caught){error=caught;}assert(error===sentinel,'exception identity');
|
||||
}
|
||||
assert(dictionary()===-7&&dictionary(null)===-7&&dictionary({})===-7,'dictionary defaults');
|
||||
const sentinel={};let error;try{dictionary({get value(){throw sentinel;}});}catch(caught){error=caught;}assert(error===sentinel,'getter exception');
|
||||
try{required();}catch(caught){error=caught;}assert(error instanceof CalleeTypeError,'required argument');
|
||||
return true;
|
||||
})()"#).unwrap();
|
||||
let script = v8::Script::compile(scope, source, None).unwrap();
|
||||
assert!(
|
||||
script
|
||||
.run(scope)
|
||||
.expect("numeric conversion regression")
|
||||
.is_true()
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user