Skip to content

Commit f8949ac

Browse files
authored
Merge pull request #457 from fitzgen/turn-throws-extended-attribute-into-catch
webidl: Turn the `[Throws]` extended attributes into `Result<T, JsValue>`
2 parents 5ce424e + 3cdb6ef commit f8949ac

File tree

6 files changed

+251
-29
lines changed

6 files changed

+251
-29
lines changed

crates/backend/src/util.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ pub fn raw_ident(name: &str) -> Ident {
3535
/// Create a path type from the given segments. For example an iterator yielding
3636
/// the idents `[foo, bar, baz]` will result in the path type `foo::bar::baz`.
3737
pub fn simple_path_ty<I>(segments: I) -> syn::Type
38+
where
39+
I: IntoIterator<Item = Ident>,
40+
{
41+
path_ty(false, segments)
42+
}
43+
44+
/// Create a global path type from the given segments. For example an iterator
45+
/// yielding the idents `[foo, bar, baz]` will result in the path type
46+
/// `::foo::bar::baz`.
47+
pub fn leading_colon_path_ty<I>(segments: I) -> syn::Type
48+
where
49+
I: IntoIterator<Item = Ident>,
50+
{
51+
path_ty(true, segments)
52+
}
53+
54+
fn path_ty<I>(leading_colon: bool, segments: I) -> syn::Type
3855
where
3956
I: IntoIterator<Item = Ident>,
4057
{
@@ -49,7 +66,11 @@ where
4966
syn::TypePath {
5067
qself: None,
5168
path: syn::Path {
52-
leading_colon: None,
69+
leading_colon: if leading_colon {
70+
Some(Default::default())
71+
} else {
72+
None
73+
},
5374
segments: syn::punctuated::Punctuated::from_iter(segments),
5475
},
5576
}.into()

crates/webidl/src/lib.rs

+65-12
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ fn compile_ast(mut ast: backend::ast::Program) -> String {
7777
let mut defined = BTreeSet::from_iter(
7878
vec![
7979
"str", "char", "bool", "JsValue", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64",
80-
"usize", "isize", "f32", "f64",
80+
"usize", "isize", "f32", "f64", "Result",
8181
].into_iter()
8282
.map(|id| proc_macro2::Ident::new(id, proc_macro2::Span::call_site())),
8383
);
@@ -210,19 +210,38 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
210210
) -> Result<()> {
211211
let mut add_constructor = |arguments: &[webidl::ast::Argument], class: &str| {
212212
let self_ty = ident_ty(rust_ident(&interface.name));
213+
213214
let kind = backend::ast::ImportFunctionKind::Method {
214215
class: class.to_string(),
215216
ty: self_ty.clone(),
216217
kind: backend::ast::MethodKind::Constructor,
217218
};
219+
220+
let structural = false;
221+
222+
// Constructors aren't annotated with `[Throws]` extended attributes
223+
// (how could they be, since they themselves are extended
224+
// attributes?) so we must conservatively assume that they can
225+
// always throw.
226+
//
227+
// From https://heycam.github.io/webidl/#Constructor (emphasis
228+
// mine):
229+
//
230+
// > The prose definition of a constructor must either return an IDL
231+
// > value of a type corresponding to the interface the
232+
// > `[Constructor]` extended attribute appears on, **or throw an
233+
// > exception**.
234+
let throws = true;
235+
218236
create_function(
219237
"new",
220238
arguments
221239
.iter()
222240
.map(|arg| (&*arg.name, &*arg.type_, arg.variadic)),
223241
Some(self_ty),
224242
kind,
225-
false,
243+
structural,
244+
throws,
226245
).map(|function| {
227246
program.imports.push(backend::ast::Import {
228247
module: None,
@@ -236,7 +255,8 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
236255
match self {
237256
webidl::ast::ExtendedAttribute::ArgumentList(
238257
webidl::ast::ArgumentListExtendedAttribute { arguments, name },
239-
) if name == "Constructor" =>
258+
)
259+
if name == "Constructor" =>
240260
{
241261
add_constructor(arguments, &interface.name);
242262
}
@@ -251,7 +271,8 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
251271
rhs_arguments,
252272
rhs_name,
253273
},
254-
) if lhs_name == "NamedConstructor" =>
274+
)
275+
if lhs_name == "NamedConstructor" =>
255276
{
256277
add_constructor(rhs_arguments, rhs_name);
257278
}
@@ -322,14 +343,27 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute {
322343
}
323344

324345
let is_structural = util::is_structural(&self.extended_attributes);
346+
let throws = util::throws(&self.extended_attributes);
325347

326-
create_getter(&self.name, &self.type_, self_name, false, is_structural)
327-
.map(wrap_import_function)
348+
create_getter(
349+
&self.name,
350+
&self.type_,
351+
self_name,
352+
false,
353+
is_structural,
354+
throws,
355+
).map(wrap_import_function)
328356
.map(|import| program.imports.push(import));
329357

330358
if !self.read_only {
331-
create_setter(&self.name, &self.type_, self_name, false, is_structural)
332-
.map(wrap_import_function)
359+
create_setter(
360+
&self.name,
361+
&self.type_,
362+
self_name,
363+
false,
364+
is_structural,
365+
throws,
366+
).map(wrap_import_function)
333367
.map(|import| program.imports.push(import));
334368
}
335369

@@ -344,14 +378,27 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute {
344378
}
345379

346380
let is_structural = util::is_structural(&self.extended_attributes);
381+
let throws = util::throws(&self.extended_attributes);
347382

348-
create_getter(&self.name, &self.type_, self_name, true, is_structural)
349-
.map(wrap_import_function)
383+
create_getter(
384+
&self.name,
385+
&self.type_,
386+
self_name,
387+
true,
388+
is_structural,
389+
throws,
390+
).map(wrap_import_function)
350391
.map(|import| program.imports.push(import));
351392

352393
if !self.read_only {
353-
create_setter(&self.name, &self.type_, self_name, true, is_structural)
354-
.map(wrap_import_function)
394+
create_setter(
395+
&self.name,
396+
&self.type_,
397+
self_name,
398+
true,
399+
is_structural,
400+
throws,
401+
).map(wrap_import_function)
355402
.map(|import| program.imports.push(import));
356403
}
357404

@@ -365,12 +412,15 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation {
365412
return Ok(());
366413
}
367414

415+
let throws = util::throws(&self.extended_attributes);
416+
368417
create_basic_method(
369418
&self.arguments,
370419
self.name.as_ref(),
371420
&self.return_type,
372421
self_name,
373422
false,
423+
throws,
374424
).map(wrap_import_function)
375425
.map(|import| program.imports.push(import));
376426

@@ -384,12 +434,15 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
384434
return Ok(());
385435
}
386436

437+
let throws = util::throws(&self.extended_attributes);
438+
387439
create_basic_method(
388440
&self.arguments,
389441
self.name.as_ref(),
390442
&self.return_type,
391443
self_name,
392444
true,
445+
throws,
393446
).map(wrap_import_function)
394447
.map(|import| program.imports.push(import));
395448

crates/webidl/src/util.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::iter;
1+
use std::iter::{self, FromIterator};
22

33
use backend;
4-
use backend::util::{ident_ty, raw_ident, rust_ident, simple_path_ty};
4+
use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident, simple_path_ty};
55
use heck::SnakeCase;
66
use proc_macro2::Ident;
77
use syn;
@@ -146,12 +146,40 @@ where
146146
Some(res)
147147
}
148148

149+
fn unit_ty() -> syn::Type {
150+
syn::Type::Tuple(syn::TypeTuple {
151+
paren_token: Default::default(),
152+
elems: syn::punctuated::Punctuated::new(),
153+
})
154+
}
155+
156+
fn result_ty(t: syn::Type) -> syn::Type {
157+
let js_value = leading_colon_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]);
158+
159+
let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
160+
colon2_token: None,
161+
lt_token: Default::default(),
162+
args: FromIterator::from_iter(vec![
163+
syn::GenericArgument::Type(t),
164+
syn::GenericArgument::Type(js_value),
165+
]),
166+
gt_token: Default::default(),
167+
});
168+
169+
let ident = raw_ident("Result");
170+
let seg = syn::PathSegment { ident, arguments };
171+
let path: syn::Path = seg.into();
172+
let ty = syn::TypePath { qself: None, path };
173+
ty.into()
174+
}
175+
149176
pub fn create_function<'a, I>(
150177
name: &str,
151178
arguments: I,
152-
ret: Option<syn::Type>,
179+
mut ret: Option<syn::Type>,
153180
kind: backend::ast::ImportFunctionKind,
154181
structural: bool,
182+
catch: bool,
155183
) -> Option<backend::ast::ImportFunction>
156184
where
157185
I: Iterator<Item = (&'a str, &'a webidl::ast::Type, bool)>,
@@ -163,6 +191,10 @@ where
163191

164192
let js_ret = ret.clone();
165193

194+
if catch {
195+
ret = Some(ret.map_or_else(|| result_ty(unit_ty()), |ret| result_ty(ret)))
196+
}
197+
166198
let shim = {
167199
let ns = match kind {
168200
backend::ast::ImportFunctionKind::Normal => "",
@@ -184,7 +216,7 @@ where
184216
},
185217
rust_name,
186218
js_ret,
187-
catch: false,
219+
catch,
188220
structural,
189221
kind,
190222
shim,
@@ -197,6 +229,7 @@ pub fn create_basic_method(
197229
return_type: &webidl::ast::ReturnType,
198230
self_name: &str,
199231
is_static: bool,
232+
catch: bool,
200233
) -> Option<backend::ast::ImportFunction> {
201234
let name = match name {
202235
None => {
@@ -235,6 +268,7 @@ pub fn create_basic_method(
235268
ret,
236269
kind,
237270
false,
271+
catch,
238272
)
239273
}
240274

@@ -244,6 +278,7 @@ pub fn create_getter(
244278
self_name: &str,
245279
is_static: bool,
246280
is_structural: bool,
281+
catch: bool,
247282
) -> Option<backend::ast::ImportFunction> {
248283
let ret = match webidl_ty_to_syn_ty(ty, TypePosition::Return) {
249284
None => {
@@ -262,7 +297,7 @@ pub fn create_getter(
262297
}),
263298
};
264299

265-
create_function(name, iter::empty(), ret, kind, is_structural)
300+
create_function(name, iter::empty(), ret, kind, is_structural, catch)
266301
}
267302

268303
pub fn create_setter(
@@ -271,6 +306,7 @@ pub fn create_setter(
271306
self_name: &str,
272307
is_static: bool,
273308
is_structural: bool,
309+
catch: bool,
274310
) -> Option<backend::ast::ImportFunction> {
275311
let kind = backend::ast::ImportFunctionKind::Method {
276312
class: self_name.to_string(),
@@ -287,6 +323,7 @@ pub fn create_setter(
287323
None,
288324
kind,
289325
is_structural,
326+
catch,
290327
)
291328
}
292329

@@ -315,3 +352,13 @@ pub fn is_structural(attrs: &[Box<ExtendedAttribute>]) -> bool {
315352
}
316353
})
317354
}
355+
356+
pub fn throws(attrs: &[Box<ExtendedAttribute>]) -> bool {
357+
attrs.iter().any(|attr| {
358+
if let ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(ref name)) = **attr {
359+
name == "Throws"
360+
} else {
361+
false
362+
}
363+
})
364+
}

crates/webidl/tests/all/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ extern crate wasm_bindgen_test_project_builder as project_builder;
22
use project_builder::project;
33

44
mod simple;
5+
mod throws;

0 commit comments

Comments
 (0)