mirror of
https://github.com/lexmount/moli.git
synced 2026-10-18 00:01:32 +00:00
feat(webidl): support signed short argument conversion
This commit is contained in:
@@ -15,6 +15,7 @@ pub(crate) enum ConverterKind {
|
||||
CallbackInterface,
|
||||
Long,
|
||||
EnforceRangeLong,
|
||||
Short,
|
||||
UnsignedShort,
|
||||
ClampedUnsignedShort,
|
||||
UnsignedLong,
|
||||
@@ -45,6 +46,7 @@ impl ConverterKind {
|
||||
"callback_interface" => Ok(Self::CallbackInterface),
|
||||
"long" => Ok(Self::Long),
|
||||
"enforce_range_long" => Ok(Self::EnforceRangeLong),
|
||||
"short" => Ok(Self::Short),
|
||||
"unsigned_short" => Ok(Self::UnsignedShort),
|
||||
"clamped_unsigned_short" => Ok(Self::ClampedUnsignedShort),
|
||||
"unsigned_long" => Ok(Self::UnsignedLong),
|
||||
@@ -74,6 +76,7 @@ impl ConverterKind {
|
||||
Self::CallbackInterface => quote!(::moli_webidl::WebIdlCallbackInterface),
|
||||
Self::Long => quote!(::moli_webidl::Long),
|
||||
Self::EnforceRangeLong => quote!(::moli_webidl::EnforceRangeLong),
|
||||
Self::Short => quote!(::moli_webidl::Short),
|
||||
Self::UnsignedShort => quote!(::moli_webidl::UnsignedShort),
|
||||
Self::ClampedUnsignedShort => quote!(::moli_webidl::ClampedUnsignedShort),
|
||||
Self::UnsignedLong => quote!(::moli_webidl::UnsignedLong),
|
||||
@@ -110,6 +113,7 @@ impl ConverterKind {
|
||||
Self::CallbackFunction | Self::CallbackInterface => quote!(#expr),
|
||||
Self::Long => quote!(::moli_webidl::Long(#expr)),
|
||||
Self::EnforceRangeLong => quote!(::moli_webidl::EnforceRangeLong(#expr)),
|
||||
Self::Short => quote!(::moli_webidl::Short(#expr)),
|
||||
Self::UnsignedShort => quote!(::moli_webidl::UnsignedShort(#expr)),
|
||||
Self::ClampedUnsignedShort => {
|
||||
quote!(::moli_webidl::ClampedUnsignedShort(#expr))
|
||||
@@ -141,6 +145,7 @@ impl ConverterKind {
|
||||
| Self::UsvString
|
||||
| Self::Long
|
||||
| Self::EnforceRangeLong
|
||||
| Self::Short
|
||||
| Self::UnsignedShort
|
||||
| Self::ClampedUnsignedShort
|
||||
| Self::UnsignedLong
|
||||
@@ -242,6 +247,9 @@ fn infer_converter_kind(ty: &Type) -> Option<ConverterKind> {
|
||||
if is_type_ident(ty, "u16") {
|
||||
return Some(ConverterKind::UnsignedShort);
|
||||
}
|
||||
if is_type_ident(ty, "i16") {
|
||||
return Some(ConverterKind::Short);
|
||||
}
|
||||
if is_type_ident(ty, "u64") {
|
||||
return Some(ConverterKind::UnsignedLongLong);
|
||||
}
|
||||
|
||||
@@ -261,6 +261,21 @@ impl<'s> WebIdlConverter<'s> for EnforceRangeUnsignedLong {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> WebIdlConverter<'s> for crate::Short {
|
||||
type Options = ();
|
||||
|
||||
fn convert(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
context: Context,
|
||||
_options: &Self::Options,
|
||||
) -> Result<Self, WebIdlError> {
|
||||
// Both 16-bit integer types truncate and reduce modulo 2^16; signed
|
||||
// short then interprets the high bit as a negative value.
|
||||
number_value(scope, value, context, "short").map(|value| Self(unsigned_short(value) as i16))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> WebIdlConverter<'s> for UnsignedShort {
|
||||
type Options = ();
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ pub use traits::{ParseOutcome, WebIdlArguments, WebIdlConverter, WebIdlDictionar
|
||||
pub use types::{
|
||||
Boolean, BufferSource, ByteString, ClampedUnsignedShort, DomString, DomString16, Double,
|
||||
EnforceRangeLong, EnforceRangeUnsignedLong, EnforceRangeUnsignedLongLong, EnumValue,
|
||||
EventListenerOptions, Long, Record, Sequence, StringOptions, UnrestrictedDouble, UnsignedLong,
|
||||
UnsignedLongLong, UnsignedShort, UsvString,
|
||||
EventListenerOptions, Long, Record, Sequence, Short, StringOptions, UnrestrictedDouble,
|
||||
UnsignedLong, UnsignedLongLong, UnsignedShort, UsvString,
|
||||
};
|
||||
|
||||
/// Restricted WebIDL float represented as a finite binary32 value.
|
||||
|
||||
@@ -227,6 +227,15 @@ impl From<EnforceRangeUnsignedLong> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Short(pub i16);
|
||||
|
||||
impl From<Short> for i16 {
|
||||
fn from(value: Short) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct UnsignedShort(pub u16);
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
use moli_webidl::WebIdlArgs;
|
||||
|
||||
#[derive(WebIdlArgs)]
|
||||
#[webidl(prefix = "Test.short")]
|
||||
struct InferredShortArgs {
|
||||
#[webidl(required)]
|
||||
value: i16,
|
||||
}
|
||||
|
||||
#[derive(WebIdlArgs)]
|
||||
#[webidl(prefix = "Test.explicitShort")]
|
||||
struct ExplicitShortArgs {
|
||||
#[webidl(required, converter = "short")]
|
||||
value: i16,
|
||||
}
|
||||
|
||||
fn inferred_short<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s>,
|
||||
) {
|
||||
if let Some(parsed) = moli_webidl::parse_args::<InferredShortArgs>(scope, &args) {
|
||||
rv.set(v8::Number::new(scope, f64::from(parsed.value)).into());
|
||||
}
|
||||
}
|
||||
|
||||
fn explicit_short<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s>,
|
||||
) {
|
||||
if let Some(parsed) = moli_webidl::parse_args::<ExplicitShortArgs>(scope, &args) {
|
||||
rv.set(v8::Number::new(scope, f64::from(parsed.value)).into());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_short_wraps_and_preserves_conversion_errors_in_the_callee_realm() {
|
||||
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 inferred = v8::Function::new(scope, inferred_short).unwrap();
|
||||
let explicit = v8::Function::new(scope, explicit_short).unwrap();
|
||||
let key = v8::String::new(scope, "TypeError").unwrap();
|
||||
let error = callee.global(scope).get(scope, key.into()).unwrap();
|
||||
[
|
||||
("inferred", inferred.into()),
|
||||
("explicit", explicit.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 = (ok, message) => { if (!ok) throw Error(message); };
|
||||
const values = [[0,0],[-0,0],[3.9,3],[-3.9,-3],[32767,32767],
|
||||
[32768,-32768],[65535,-1],[65536,0],[65537,1],[-65537,-1],
|
||||
[4294967295,-1],[4294967297,1],[NaN,0],[Infinity,0],[-Infinity,0],
|
||||
[undefined,0],[null,0],[true,1],['65535',-1]];
|
||||
for (const convert of [inferred, explicit]) {
|
||||
for (const [value, expected] of values) assert(Object.is(convert(value),expected),'modulo conversion '+value);
|
||||
for (const value of [Symbol('number'), 1n]) {
|
||||
let error;try { convert(value); } catch(caught) { error=caught; }
|
||||
assert(error instanceof CalleeTypeError && !(error instanceof TypeError),'callee realm TypeError');
|
||||
}
|
||||
let reads=0;const value={[Symbol.toPrimitive](hint){reads++;assert(hint==='number','numeric hint');return 65535;}};
|
||||
assert(convert(value)===-1&&reads===1,'one observable conversion');
|
||||
const sentinel={};let error;try{convert({valueOf(){throw sentinel;}});}catch(caught){error=caught;}assert(error===sentinel,'exception identity');
|
||||
try { convert(); } 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("signed short conversion matrix")
|
||||
.is_true()
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user