Skip to content

Commit 28adf58

Browse files
committed
webidl: initial enum support
Add enum support to the WebIDL interface generator.
1 parent f2f2d72 commit 28adf58

File tree

10 files changed

+240
-12
lines changed

10 files changed

+240
-12
lines changed

crates/backend/src/ast.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub enum ImportKind {
4141
Function(ImportFunction),
4242
Static(ImportStatic),
4343
Type(ImportType),
44+
Enum(ImportEnum),
4445
}
4546

4647
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@@ -99,6 +100,18 @@ pub struct ImportType {
99100
pub attrs: Vec<syn::Attribute>,
100101
}
101102

103+
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
104+
pub struct ImportEnum {
105+
/// The Rust enum's visibility
106+
pub vis: syn::Visibility,
107+
/// The Rust enum's identifiers
108+
pub name: Ident,
109+
/// The Rust identifiers for the variants
110+
pub variants: Vec<Ident>,
111+
/// The JS string values of the variants
112+
pub variant_values: Vec<String>,
113+
}
114+
102115
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
103116
pub struct Function {
104117
pub name: Ident,
@@ -279,6 +292,7 @@ impl ImportKind {
279292
ImportKind::Function(_) => true,
280293
ImportKind::Static(_) => false,
281294
ImportKind::Type(_) => false,
295+
ImportKind::Enum(_) => false,
282296
}
283297
}
284298

@@ -287,6 +301,7 @@ impl ImportKind {
287301
ImportKind::Function(ref f) => shared::ImportKind::Function(f.shared()),
288302
ImportKind::Static(ref f) => shared::ImportKind::Static(f.shared()),
289303
ImportKind::Type(ref f) => shared::ImportKind::Type(f.shared()),
304+
ImportKind::Enum(ref f) => shared::ImportKind::Enum(f.shared()),
290305
}
291306
}
292307
}
@@ -364,6 +379,12 @@ impl ImportType {
364379
}
365380
}
366381

382+
impl ImportEnum {
383+
fn shared(&self) -> shared::ImportEnum {
384+
shared::ImportEnum {}
385+
}
386+
}
387+
367388
impl Struct {
368389
fn shared(&self) -> shared::Struct {
369390
shared::Struct {

crates/backend/src/codegen.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::env;
44
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
55

66
use ast;
7-
use proc_macro2::{Ident, Span, TokenStream};
7+
use proc_macro2::{Ident, Literal, Span, TokenStream};
88
use quote::ToTokens;
99
use serde_json;
1010
use shared;
@@ -500,6 +500,7 @@ impl ToTokens for ast::ImportKind {
500500
ast::ImportKind::Function(ref f) => f.to_tokens(tokens),
501501
ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
502502
ast::ImportKind::Type(ref t) => t.to_tokens(tokens),
503+
ast::ImportKind::Enum(ref e) => e.to_tokens(tokens),
503504
}
504505
}
505506
}
@@ -586,6 +587,98 @@ impl ToTokens for ast::ImportType {
586587
}
587588
}
588589

590+
impl ToTokens for ast::ImportEnum {
591+
fn to_tokens(&self, tokens: &mut TokenStream) {
592+
let vis = &self.vis;
593+
let name = &self.name;
594+
let name_string = &self.name.to_string();
595+
let variants = &self.variants;
596+
let variant_strings = &self.variant_values;
597+
598+
let mut current_idx: usize = 0;
599+
let variant_indexes: Vec<Literal> = variants
600+
.iter()
601+
.map(|_| {
602+
let this_index = current_idx;
603+
current_idx += 1;
604+
Literal::usize_unsuffixed(this_index)
605+
})
606+
.collect();
607+
608+
// Borrow variant_indexes because we need to use it multiple times inside the quote! macro
609+
let variant_indexes_ref = &variant_indexes;
610+
611+
// A vector of EnumName::VariantName tokens for this enum
612+
let variant_paths: Vec<TokenStream> = self
613+
.variants
614+
.iter()
615+
.map(|v| quote!(#name::#v).into_token_stream())
616+
.collect();
617+
618+
// Borrow variant_paths because we need to use it multiple times inside the quote! macro
619+
let variant_paths_ref = &variant_paths;
620+
621+
(quote! {
622+
#[allow(bad_style)]
623+
#[derive(Copy, Clone, Debug)]
624+
#vis enum #name {
625+
#(#variants = #variant_indexes_ref,)*
626+
}
627+
628+
impl ::wasm_bindgen::describe::WasmDescribe for #name {
629+
fn describe() {
630+
::wasm_bindgen::JsValue::describe()
631+
}
632+
}
633+
634+
impl ::wasm_bindgen::convert::IntoWasmAbi for #name {
635+
type Abi = <::wasm_bindgen::JsValue as
636+
::wasm_bindgen::convert::IntoWasmAbi>::Abi;
637+
638+
fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
639+
match self {
640+
#(#variant_paths_ref => ::wasm_bindgen::JsValue::from_str(#variant_strings).into_abi(extra),)*
641+
}
642+
}
643+
}
644+
645+
impl ::wasm_bindgen::convert::FromWasmAbi for #name {
646+
type Abi = <::wasm_bindgen::JsValue as
647+
::wasm_bindgen::convert::FromWasmAbi>::Abi;
648+
649+
unsafe fn from_abi(
650+
js: Self::Abi,
651+
extra: &mut ::wasm_bindgen::convert::Stack,
652+
) -> Self {
653+
#name::from(::wasm_bindgen::JsValue::from_abi(js, extra))
654+
}
655+
}
656+
657+
impl From<::wasm_bindgen::JsValue> for #name {
658+
fn from(obj: ::wasm_bindgen::JsValue) -> #name {
659+
let obj_str = match obj.as_string() {
660+
Some(string_value) => string_value,
661+
None => panic!("Can't convert a non-string into {}", #name_string),
662+
};
663+
664+
match obj_str.as_str() {
665+
#(#variant_strings => #variant_paths_ref,)*
666+
unknown_value => panic!("Can't convert \"{}\" into {}", unknown_value, #name_string),
667+
}
668+
}
669+
}
670+
671+
impl From<#name> for ::wasm_bindgen::JsValue {
672+
fn from(obj: #name) -> ::wasm_bindgen::JsValue {
673+
match obj {
674+
#(#variant_paths_ref => ::wasm_bindgen::JsValue::from_str(#variant_strings)),*
675+
}
676+
}
677+
}
678+
}).to_tokens(tokens);
679+
}
680+
}
681+
589682
impl ToTokens for ast::ImportFunction {
590683
fn to_tokens(&self, tokens: &mut TokenStream) {
591684
let mut class_ty = None;
@@ -755,6 +848,7 @@ impl<'a> ToTokens for DescribeImport<'a> {
755848
ast::ImportKind::Function(ref f) => f,
756849
ast::ImportKind::Static(_) => return,
757850
ast::ImportKind::Type(_) => return,
851+
ast::ImportKind::Enum(_) => return,
758852
};
759853
let describe_name = format!("__wbindgen_describe_{}", f.shim);
760854
let describe_name = Ident::new(&describe_name, Span::call_site());

crates/backend/src/defined.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ impl ImportedTypes for ast::ImportKind {
105105
ast::ImportKind::Static(s) => s.imported_types(f),
106106
ast::ImportKind::Function(fun) => fun.imported_types(f),
107107
ast::ImportKind::Type(ty) => ty.imported_types(f),
108+
ast::ImportKind::Enum(enm) => enm.imported_types(f),
108109
}
109110
}
110111
}
@@ -210,6 +211,15 @@ impl ImportedTypes for ast::ImportType {
210211
}
211212
}
212213

214+
impl ImportedTypes for ast::ImportEnum {
215+
fn imported_types<F>(&self, f: &mut F)
216+
where
217+
F: FnMut(&Ident, ImportedTypeKind),
218+
{
219+
f(&self.name, ImportedTypeKind::Definition);
220+
}
221+
}
222+
213223
impl ImportedTypes for ast::TypeAlias {
214224
fn imported_types<F>(&self, f: &mut F)
215225
where

crates/cli-support/src/js/js2rust.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
7171
self.prelude(
7272
"if (this.ptr === 0) {
7373
throw new Error('Attempt to use a moved value');
74-
}"
74+
}",
7575
);
7676
if consumed {
77-
self.prelude("\
78-
const ptr = this.ptr;\n\
79-
this.ptr = 0;\n\
80-
");
77+
self.prelude(
78+
"\
79+
const ptr = this.ptr;\n\
80+
this.ptr = 0;\n\
81+
",
82+
);
8183
self.rust_arguments.insert(0, "ptr".to_string());
8284
} else {
8385
self.rust_arguments.insert(0, "this.ptr".to_string());

crates/cli-support/src/js/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
17561756
})?;
17571757
}
17581758
shared::ImportKind::Type(_) => {}
1759+
shared::ImportKind::Enum(_) => {}
17591760
}
17601761
Ok(())
17611762
}

crates/cli-support/src/js/rust2js.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,16 +306,19 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
306306
// Insert an assertion to the type of the returned value as
307307
// otherwise this will cause memory unsafety on the Rust side of
308308
// things.
309-
self.ret_expr = format!("\
309+
self.ret_expr = format!(
310+
"\
310311
const val = JS;
311312
if (!(val instanceof {0})) {{
312313
throw new Error('expected value of type {0}');
313314
}}
314315
const ret = val.ptr;
315316
val.ptr = 0;
316317
return ret;\
317-
", class);
318-
return Ok(())
318+
",
319+
class
320+
);
321+
return Ok(());
319322
}
320323

321324
self.ret_expr = match *ty {

crates/shared/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub enum ImportKind {
3333
Function(ImportFunction),
3434
Static(ImportStatic),
3535
Type(ImportType),
36+
Enum(ImportEnum),
3637
}
3738

3839
#[derive(Deserialize, Serialize)]
@@ -78,6 +79,9 @@ pub struct ImportStatic {
7879
#[derive(Deserialize, Serialize)]
7980
pub struct ImportType {}
8081

82+
#[derive(Deserialize, Serialize)]
83+
pub struct ImportEnum {}
84+
8185
#[derive(Deserialize, Serialize)]
8286
pub struct Export {
8387
pub class: Option<String>,

crates/webidl/src/lib.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use std::path::Path;
2929
use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports};
3030
use backend::util::{ident_ty, rust_ident, wrap_import_function};
3131
use failure::ResultExt;
32+
use heck::CamelCase;
3233
use quote::ToTokens;
3334

3435
use util::{
@@ -111,10 +112,10 @@ impl WebidlParse<()> for webidl::ast::Definition {
111112
interface.webidl_parse(program, ())
112113
}
113114
webidl::ast::Definition::Typedef(ref typedef) => typedef.webidl_parse(program, ()),
115+
webidl::ast::Definition::Enum(ref enumeration) => enumeration.webidl_parse(program, ()),
114116
// TODO
115117
webidl::ast::Definition::Callback(..)
116118
| webidl::ast::Definition::Dictionary(..)
117-
| webidl::ast::Definition::Enum(..)
118119
| webidl::ast::Definition::Implements(..)
119120
| webidl::ast::Definition::Includes(..)
120121
| webidl::ast::Definition::Mixin(..)
@@ -236,7 +237,8 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
236237
match self {
237238
webidl::ast::ExtendedAttribute::ArgumentList(
238239
webidl::ast::ArgumentListExtendedAttribute { arguments, name },
239-
) if name == "Constructor" =>
240+
)
241+
if name == "Constructor" =>
240242
{
241243
add_constructor(arguments, &interface.name);
242244
}
@@ -251,7 +253,8 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
251253
rhs_arguments,
252254
rhs_name,
253255
},
254-
) if lhs_name == "NamedConstructor" =>
256+
)
257+
if lhs_name == "NamedConstructor" =>
255258
{
256259
add_constructor(rhs_arguments, rhs_name);
257260
}
@@ -396,3 +399,27 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
396399
Ok(())
397400
}
398401
}
402+
403+
impl<'a> WebidlParse<()> for webidl::ast::Enum {
404+
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
405+
program.imports.push(backend::ast::Import {
406+
module: None,
407+
version: None,
408+
js_namespace: None,
409+
kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum {
410+
vis: syn::Visibility::Public(syn::VisPublic {
411+
pub_token: Default::default(),
412+
}),
413+
name: rust_ident(self.name.to_camel_case().as_str()),
414+
variants: self
415+
.variants
416+
.iter()
417+
.map(|v| rust_ident(v.to_camel_case().as_str()))
418+
.collect(),
419+
variant_values: self.variants.clone(),
420+
}),
421+
});
422+
423+
Ok(())
424+
}
425+
}

0 commit comments

Comments
 (0)