mirror of
https://github.com/lexmount/moli.git
synced 2026-10-30 16:07:24 +00:00
feat(webidl): support lossless DOMString16 in derive bindings
This commit is contained in:
@@ -8,6 +8,7 @@ use crate::attrs::FieldAttrs;
|
||||
pub(crate) enum ConverterKind {
|
||||
#[default]
|
||||
DomString,
|
||||
DomString16,
|
||||
UsvString,
|
||||
ByteString,
|
||||
BufferSource,
|
||||
@@ -45,6 +46,7 @@ impl ConverterKind {
|
||||
pub(crate) fn from_lit(value: &LitStr) -> Result<Self, Error> {
|
||||
match value.value().as_str() {
|
||||
"dom_string" => Ok(Self::DomString),
|
||||
"dom_string16" => Ok(Self::DomString16),
|
||||
"usv_string" => Ok(Self::UsvString),
|
||||
"byte_string" => Ok(Self::ByteString),
|
||||
"buffer_source" => Ok(Self::BufferSource),
|
||||
@@ -80,6 +82,7 @@ impl ConverterKind {
|
||||
pub(crate) fn wrapper_type(self, ty: &Type) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
Self::DomString => quote!(::moli_webidl::DomString),
|
||||
Self::DomString16 => quote!(::moli_webidl::DomString16),
|
||||
Self::UsvString => quote!(::moli_webidl::UsvString),
|
||||
Self::ByteString => quote!(::moli_webidl::ByteString),
|
||||
Self::BufferSource => quote!(::moli_webidl::BufferSource),
|
||||
@@ -120,6 +123,9 @@ impl ConverterKind {
|
||||
Self::DomString => {
|
||||
quote!(::moli_webidl::DomString(::std::convert::Into::into(#expr)))
|
||||
}
|
||||
Self::DomString16 => {
|
||||
quote!(::moli_webidl::DomString16(::std::convert::Into::into(#expr)))
|
||||
}
|
||||
Self::UsvString => {
|
||||
quote!(::moli_webidl::UsvString(::std::convert::Into::into(#expr)))
|
||||
}
|
||||
@@ -165,6 +171,7 @@ impl ConverterKind {
|
||||
Self::BufferSource => quote!(::std::convert::Into::into(#value)),
|
||||
Self::CallbackFunction | Self::CallbackInterface => value,
|
||||
Self::DomString
|
||||
| Self::DomString16
|
||||
| Self::UsvString
|
||||
| Self::Octet
|
||||
| Self::Long
|
||||
@@ -213,7 +220,7 @@ impl ConverterKind {
|
||||
}
|
||||
if attrs.treat_null_as_empty_string {
|
||||
match self {
|
||||
Self::DomString | Self::UsvString | Self::ByteString => {
|
||||
Self::DomString | Self::DomString16 | Self::UsvString | Self::ByteString => {
|
||||
return Ok(quote!(::moli_webidl::StringOptions {
|
||||
treat_null_as_empty_string: true,
|
||||
}));
|
||||
|
||||
@@ -1,5 +1,49 @@
|
||||
use moli_webidl::{Context, DomString, DomString16, StringOptions, UsvString};
|
||||
|
||||
#[derive(moli_webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "derivedLossless")]
|
||||
struct DerivedLosslessArgs {
|
||||
#[webidl(required, converter = "dom_string16")]
|
||||
value: Vec<u16>,
|
||||
#[webidl(nullable, converter = "dom_string16")]
|
||||
optional: Option<Vec<u16>>,
|
||||
#[webidl(converter = "dom_string16", default = vec![0xdfff])]
|
||||
defaulted: Vec<u16>,
|
||||
#[webidl(converter = "dom_string16", treat_null_as_empty_string, default = Vec::new())]
|
||||
null_as_empty: Vec<u16>,
|
||||
#[webidl(variadic, converter = "dom_string16")]
|
||||
tail: Vec<Vec<u16>>,
|
||||
}
|
||||
|
||||
fn derived_lossless<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s>,
|
||||
) {
|
||||
let Some(parsed) = moli_webidl::parse_args::<DerivedLosslessArgs>(scope, &args) else {
|
||||
return;
|
||||
};
|
||||
let mut values = Vec::new();
|
||||
for units in [
|
||||
Some(parsed.value),
|
||||
parsed.optional,
|
||||
Some(parsed.defaulted),
|
||||
Some(parsed.null_as_empty),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(parsed.tail.into_iter().map(Some))
|
||||
{
|
||||
let value = match units {
|
||||
Some(units) => v8::String::new_from_two_byte(scope, &units, v8::NewStringType::Normal)
|
||||
.unwrap()
|
||||
.into(),
|
||||
None => v8::null(scope).into(),
|
||||
};
|
||||
values.push(value);
|
||||
}
|
||||
rv.set(v8::Array::new_with_elements(scope, &values).into());
|
||||
}
|
||||
|
||||
fn lossless<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -47,13 +91,19 @@ fn dom_string16_preserves_code_units_and_webidl_conversion_semantics() {
|
||||
let scope = std::pin::pin!(v8::HandleScope::new(&mut isolate));
|
||||
let scope = &mut scope.init();
|
||||
let callee = v8::Context::new(scope, Default::default());
|
||||
let (lossless_function, utf8_function, type_error) = {
|
||||
let (lossless_function, utf8_function, derived_function, type_error) = {
|
||||
let scope = &mut v8::ContextScope::new(scope, callee);
|
||||
let lossless_function = v8::Function::new(scope, lossless).unwrap();
|
||||
let utf8_function = v8::Function::new(scope, utf8_boundary).unwrap();
|
||||
let derived_function = v8::Function::new(scope, derived_lossless).unwrap();
|
||||
let key = v8::String::new(scope, "TypeError").unwrap();
|
||||
let type_error = callee.global(scope).get(scope, key.into()).unwrap();
|
||||
(lossless_function, utf8_function, type_error)
|
||||
(
|
||||
lossless_function,
|
||||
utf8_function,
|
||||
derived_function,
|
||||
type_error,
|
||||
)
|
||||
};
|
||||
let caller = v8::Context::new(scope, Default::default());
|
||||
let scope = &mut v8::ContextScope::new(scope, caller);
|
||||
@@ -61,6 +111,7 @@ fn dom_string16_preserves_code_units_and_webidl_conversion_semantics() {
|
||||
for (name, value) in [
|
||||
("lossless", lossless_function.into()),
|
||||
("utf8Boundary", utf8_function.into()),
|
||||
("derivedLossless", derived_function.into()),
|
||||
("CalleeTypeError", type_error),
|
||||
] {
|
||||
let key = v8::String::new(scope, name).unwrap();
|
||||
@@ -97,6 +148,20 @@ fn dom_string16_preserves_code_units_and_webidl_conversion_semantics() {
|
||||
assert(utf8Boundary('\ud800x\udfff') === '\ufffdx\ufffd', 'existing Rust String boundary stays lossy');
|
||||
assert(utf8Boundary('\ud800x\udfff', true) === '\ufffdx\ufffd', 'USVString applies scalar replacement');
|
||||
assert(utf8Boundary('\ud83d\ude00') === '\ud83d\ude00', 'UTF-8 boundary preserves valid pairs');
|
||||
const same = (actual, expected) => actual.length === expected.length && actual.every((value, i) => value === expected[i]);
|
||||
for (const value of ['\ud800', '\udfff', '\ud800x\udfff', '\ud83d\ude00', '\0']) {
|
||||
assert(same(derivedLossless(value, value, value, value, value, value), Array(6).fill(value)), 'derived required/optional/variadic UTF-16');
|
||||
}
|
||||
assert(same(derivedLossless(null, null, undefined, null), ['null', null, '\udfff', '']), 'derived nullable/default/null-as-empty');
|
||||
assert(same(derivedLossless(undefined, undefined, undefined, undefined), ['undefined', null, '\udfff', '']), 'optional undefined uses defaults');
|
||||
assert(thrown(() => derivedLossless()) instanceof CalleeTypeError, 'required arity');
|
||||
const derivedConversions = [];
|
||||
const convert = label => ({toString() { derivedConversions.push(label); return '\ud800' + label; }});
|
||||
const derived = derivedLossless(convert('one'), null, undefined, null, convert('two'), convert('three'));
|
||||
assert(same(derivedConversions, ['one', 'two', 'three']) && same(derived, ['\ud800one', null, '\udfff', '', '\ud800two', '\ud800three']), 'derived variadic conversion ordering');
|
||||
derivedConversions.length = 0;
|
||||
assert(thrown(() => derivedLossless('x', null, undefined, null, convert('one'), {toString() {throw sentinel; }}, convert('last'))) === sentinel && same(derivedConversions, ['one']), 'derived original exception stops remaining conversions');
|
||||
assert(thrown(() => derivedLossless('x', null, undefined, null, Symbol())) instanceof CalleeTypeError, 'derived conversion TypeError uses callee realm');
|
||||
return true;
|
||||
})()
|
||||
"#).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user