Skip to content

Commit 9c7b15e

Browse files
authored
Merge pull request #433 from Slowki/feat/basic-enum-support
WebIDL Enum Support
2 parents 3578a83 + 5520da1 commit 9c7b15e

File tree

10 files changed

+228
-10
lines changed

10 files changed

+228
-10
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: 88 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,91 @@ 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 expect_string = format!("attempted to convert invalid JSValue into {}", name);
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 #name {
629+
#vis fn from_js_value(obj: ::wasm_bindgen::JsValue) -> Option<#name> {
630+
obj.as_string().and_then(|obj_str| match obj_str.as_str() {
631+
#(#variant_strings => Some(#variant_paths_ref),)*
632+
_ => None,
633+
})
634+
}
635+
}
636+
637+
impl ::wasm_bindgen::describe::WasmDescribe for #name {
638+
fn describe() {
639+
::wasm_bindgen::JsValue::describe()
640+
}
641+
}
642+
643+
impl ::wasm_bindgen::convert::IntoWasmAbi for #name {
644+
type Abi = <::wasm_bindgen::JsValue as
645+
::wasm_bindgen::convert::IntoWasmAbi>::Abi;
646+
647+
fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
648+
::wasm_bindgen::JsValue::from(self).into_abi(extra)
649+
}
650+
}
651+
652+
impl ::wasm_bindgen::convert::FromWasmAbi for #name {
653+
type Abi = <::wasm_bindgen::JsValue as
654+
::wasm_bindgen::convert::FromWasmAbi>::Abi;
655+
656+
unsafe fn from_abi(
657+
js: Self::Abi,
658+
extra: &mut ::wasm_bindgen::convert::Stack,
659+
) -> Self {
660+
#name::from_js_value(::wasm_bindgen::JsValue::from_abi(js, extra)).expect(#expect_string)
661+
}
662+
}
663+
664+
impl From<#name> for ::wasm_bindgen::JsValue {
665+
fn from(obj: #name) -> ::wasm_bindgen::JsValue {
666+
match obj {
667+
#(#variant_paths_ref => ::wasm_bindgen::JsValue::from_str(#variant_strings)),*
668+
}
669+
}
670+
}
671+
}).to_tokens(tokens);
672+
}
673+
}
674+
589675
impl ToTokens for ast::ImportFunction {
590676
fn to_tokens(&self, tokens: &mut TokenStream) {
591677
let mut class_ty = None;
@@ -755,6 +841,7 @@ impl<'a> ToTokens for DescribeImport<'a> {
755841
ast::ImportKind::Function(ref f) => f,
756842
ast::ImportKind::Static(_) => return,
757843
ast::ImportKind::Type(_) => return,
844+
ast::ImportKind::Enum(_) => return,
758845
};
759846
let describe_name = format!("__wbindgen_describe_{}", f.shim);
760847
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: 26 additions & 1 deletion
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(..)
@@ -449,3 +450,27 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
449450
Ok(())
450451
}
451452
}
453+
454+
impl<'a> WebidlParse<()> for webidl::ast::Enum {
455+
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
456+
program.imports.push(backend::ast::Import {
457+
module: None,
458+
version: None,
459+
js_namespace: None,
460+
kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum {
461+
vis: syn::Visibility::Public(syn::VisPublic {
462+
pub_token: Default::default(),
463+
}),
464+
name: rust_ident(self.name.to_camel_case().as_str()),
465+
variants: self
466+
.variants
467+
.iter()
468+
.map(|v| rust_ident(v.to_camel_case().as_str()))
469+
.collect(),
470+
variant_values: self.variants.clone(),
471+
}),
472+
});
473+
474+
Ok(())
475+
}
476+
}

crates/webidl/tests/all/enums.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use super::project;
2+
3+
#[test]
4+
fn top_level_enum() {
5+
project()
6+
.file(
7+
"shape.webidl",
8+
r#"
9+
enum ShapeType { "circle", "square" };
10+
11+
[Constructor(ShapeType kind)]
12+
interface Shape {
13+
[Pure]
14+
boolean isSquare();
15+
16+
[Pure]
17+
boolean isCircle();
18+
};
19+
"#,
20+
)
21+
.file(
22+
"shape.mjs",
23+
r#"
24+
export class Shape {
25+
constructor(kind) {
26+
this.kind = kind;
27+
}
28+
29+
isSquare() {
30+
return this.kind === 'square';
31+
}
32+
33+
isCircle() {
34+
return this.kind === 'circle';
35+
}
36+
}
37+
"#,
38+
)
39+
.file(
40+
"src/lib.rs",
41+
r#"
42+
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
43+
44+
extern crate wasm_bindgen;
45+
46+
use wasm_bindgen::prelude::*;
47+
48+
pub mod shape;
49+
50+
use shape::{Shape, ShapeType};
51+
52+
#[wasm_bindgen]
53+
pub fn test() {
54+
let circle = Shape::new(ShapeType::Circle).unwrap();
55+
let square = Shape::new(ShapeType::Square).unwrap();
56+
assert!(circle.is_circle());
57+
assert!(!circle.is_square());
58+
assert!(square.is_square());
59+
assert!(!square.is_circle());
60+
}
61+
"#,
62+
)
63+
.test();
64+
}

crates/webidl/tests/all/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ extern crate wasm_bindgen_test_project_builder as project_builder;
22
use project_builder::project;
33

44
mod simple;
5+
mod enums;
56
mod throws;

0 commit comments

Comments
 (0)