Skip to content

WebIDL Enum Support #433

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub enum ImportKind {
Function(ImportFunction),
Static(ImportStatic),
Type(ImportType),
Enum(ImportEnum),
}

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

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub struct ImportEnum {
/// The Rust enum's visibility
pub vis: syn::Visibility,
/// The Rust enum's identifiers
pub name: Ident,
/// The Rust identifiers for the variants
pub variants: Vec<Ident>,
/// The JS string values of the variants
pub variant_values: Vec<String>,
}

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub struct Function {
pub name: Ident,
Expand Down Expand Up @@ -279,6 +292,7 @@ impl ImportKind {
ImportKind::Function(_) => true,
ImportKind::Static(_) => false,
ImportKind::Type(_) => false,
ImportKind::Enum(_) => false,
}
}

Expand All @@ -287,6 +301,7 @@ impl ImportKind {
ImportKind::Function(ref f) => shared::ImportKind::Function(f.shared()),
ImportKind::Static(ref f) => shared::ImportKind::Static(f.shared()),
ImportKind::Type(ref f) => shared::ImportKind::Type(f.shared()),
ImportKind::Enum(ref f) => shared::ImportKind::Enum(f.shared()),
}
}
}
Expand Down Expand Up @@ -364,6 +379,12 @@ impl ImportType {
}
}

impl ImportEnum {
fn shared(&self) -> shared::ImportEnum {
shared::ImportEnum {}
}
}

impl Struct {
fn shared(&self) -> shared::Struct {
shared::Struct {
Expand Down
89 changes: 88 additions & 1 deletion crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::env;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

use ast;
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::ToTokens;
use serde_json;
use shared;
Expand Down Expand Up @@ -500,6 +500,7 @@ impl ToTokens for ast::ImportKind {
ast::ImportKind::Function(ref f) => f.to_tokens(tokens),
ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
ast::ImportKind::Type(ref t) => t.to_tokens(tokens),
ast::ImportKind::Enum(ref e) => e.to_tokens(tokens),
}
}
}
Expand Down Expand Up @@ -586,6 +587,91 @@ impl ToTokens for ast::ImportType {
}
}

impl ToTokens for ast::ImportEnum {
fn to_tokens(&self, tokens: &mut TokenStream) {
let vis = &self.vis;
let name = &self.name;
let expect_string = format!("attempted to convert invalid JSValue into {}", name);
let variants = &self.variants;
let variant_strings = &self.variant_values;

let mut current_idx: usize = 0;
let variant_indexes: Vec<Literal> = variants
.iter()
.map(|_| {
let this_index = current_idx;
current_idx += 1;
Literal::usize_unsuffixed(this_index)
})
.collect();

// Borrow variant_indexes because we need to use it multiple times inside the quote! macro
let variant_indexes_ref = &variant_indexes;

// A vector of EnumName::VariantName tokens for this enum
let variant_paths: Vec<TokenStream> = self
.variants
.iter()
.map(|v| quote!(#name::#v).into_token_stream())
.collect();

// Borrow variant_paths because we need to use it multiple times inside the quote! macro
let variant_paths_ref = &variant_paths;

(quote! {
#[allow(bad_style)]
#[derive(Copy, Clone, Debug)]
#vis enum #name {
#(#variants = #variant_indexes_ref,)*
}

impl #name {
#vis fn from_js_value(obj: ::wasm_bindgen::JsValue) -> Option<#name> {
obj.as_string().and_then(|obj_str| match obj_str.as_str() {
#(#variant_strings => Some(#variant_paths_ref),)*
_ => None,
})
}
}

impl ::wasm_bindgen::describe::WasmDescribe for #name {
fn describe() {
::wasm_bindgen::JsValue::describe()
}
}

impl ::wasm_bindgen::convert::IntoWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::IntoWasmAbi>::Abi;

fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
::wasm_bindgen::JsValue::from(self).into_abi(extra)
}
}

impl ::wasm_bindgen::convert::FromWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::FromWasmAbi>::Abi;

unsafe fn from_abi(
js: Self::Abi,
extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self {
#name::from_js_value(::wasm_bindgen::JsValue::from_abi(js, extra)).expect(#expect_string)
}
}

impl From<#name> for ::wasm_bindgen::JsValue {
fn from(obj: #name) -> ::wasm_bindgen::JsValue {
match obj {
#(#variant_paths_ref => ::wasm_bindgen::JsValue::from_str(#variant_strings)),*
}
}
}
}).to_tokens(tokens);
}
}

impl ToTokens for ast::ImportFunction {
fn to_tokens(&self, tokens: &mut TokenStream) {
let mut class_ty = None;
Expand Down Expand Up @@ -755,6 +841,7 @@ impl<'a> ToTokens for DescribeImport<'a> {
ast::ImportKind::Function(ref f) => f,
ast::ImportKind::Static(_) => return,
ast::ImportKind::Type(_) => return,
ast::ImportKind::Enum(_) => return,
};
let describe_name = format!("__wbindgen_describe_{}", f.shim);
let describe_name = Ident::new(&describe_name, Span::call_site());
Expand Down
10 changes: 10 additions & 0 deletions crates/backend/src/defined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ impl ImportedTypes for ast::ImportKind {
ast::ImportKind::Static(s) => s.imported_types(f),
ast::ImportKind::Function(fun) => fun.imported_types(f),
ast::ImportKind::Type(ty) => ty.imported_types(f),
ast::ImportKind::Enum(enm) => enm.imported_types(f),
}
}
}
Expand Down Expand Up @@ -210,6 +211,15 @@ impl ImportedTypes for ast::ImportType {
}
}

impl ImportedTypes for ast::ImportEnum {
fn imported_types<F>(&self, f: &mut F)
where
F: FnMut(&Ident, ImportedTypeKind),
{
f(&self.name, ImportedTypeKind::Definition);
}
}

impl ImportedTypes for ast::TypeAlias {
fn imported_types<F>(&self, f: &mut F)
where
Expand Down
12 changes: 7 additions & 5 deletions crates/cli-support/src/js/js2rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.prelude(
"if (this.ptr === 0) {
throw new Error('Attempt to use a moved value');
}"
}",
);
if consumed {
self.prelude("\
const ptr = this.ptr;\n\
this.ptr = 0;\n\
");
self.prelude(
"\
const ptr = this.ptr;\n\
this.ptr = 0;\n\
",
);
self.rust_arguments.insert(0, "ptr".to_string());
} else {
self.rust_arguments.insert(0, "this.ptr".to_string());
Expand Down
1 change: 1 addition & 0 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
})?;
}
shared::ImportKind::Type(_) => {}
shared::ImportKind::Enum(_) => {}
}
Ok(())
}
Expand Down
9 changes: 6 additions & 3 deletions crates/cli-support/src/js/rust2js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,16 +306,19 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
// Insert an assertion to the type of the returned value as
// otherwise this will cause memory unsafety on the Rust side of
// things.
self.ret_expr = format!("\
self.ret_expr = format!(
"\
const val = JS;
if (!(val instanceof {0})) {{
throw new Error('expected value of type {0}');
}}
const ret = val.ptr;
val.ptr = 0;
return ret;\
", class);
return Ok(())
",
class
);
return Ok(());
}

self.ret_expr = match *ty {
Expand Down
4 changes: 4 additions & 0 deletions crates/shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub enum ImportKind {
Function(ImportFunction),
Static(ImportStatic),
Type(ImportType),
Enum(ImportEnum),
}

#[derive(Deserialize, Serialize)]
Expand Down Expand Up @@ -78,6 +79,9 @@ pub struct ImportStatic {
#[derive(Deserialize, Serialize)]
pub struct ImportType {}

#[derive(Deserialize, Serialize)]
pub struct ImportEnum {}

#[derive(Deserialize, Serialize)]
pub struct Export {
pub class: Option<String>,
Expand Down
27 changes: 26 additions & 1 deletion crates/webidl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use std::path::Path;
use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports};
use backend::util::{ident_ty, rust_ident, wrap_import_function};
use failure::ResultExt;
use heck::CamelCase;
use quote::ToTokens;

use util::{
Expand Down Expand Up @@ -111,10 +112,10 @@ impl WebidlParse<()> for webidl::ast::Definition {
interface.webidl_parse(program, ())
}
webidl::ast::Definition::Typedef(ref typedef) => typedef.webidl_parse(program, ()),
webidl::ast::Definition::Enum(ref enumeration) => enumeration.webidl_parse(program, ()),
// TODO
webidl::ast::Definition::Callback(..)
| webidl::ast::Definition::Dictionary(..)
| webidl::ast::Definition::Enum(..)
| webidl::ast::Definition::Implements(..)
| webidl::ast::Definition::Includes(..)
| webidl::ast::Definition::Mixin(..)
Expand Down Expand Up @@ -449,3 +450,27 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
Ok(())
}
}

impl<'a> WebidlParse<()> for webidl::ast::Enum {
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
program.imports.push(backend::ast::Import {
module: None,
version: None,
js_namespace: None,
kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum {
vis: syn::Visibility::Public(syn::VisPublic {
pub_token: Default::default(),
}),
name: rust_ident(self.name.to_camel_case().as_str()),
variants: self
.variants
.iter()
.map(|v| rust_ident(v.to_camel_case().as_str()))
.collect(),
variant_values: self.variants.clone(),
}),
});

Ok(())
}
}
64 changes: 64 additions & 0 deletions crates/webidl/tests/all/enums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use super::project;

#[test]
fn top_level_enum() {
project()
.file(
"shape.webidl",
r#"
enum ShapeType { "circle", "square" };

[Constructor(ShapeType kind)]
interface Shape {
[Pure]
boolean isSquare();

[Pure]
boolean isCircle();
};
"#,
)
.file(
"shape.mjs",
r#"
export class Shape {
constructor(kind) {
this.kind = kind;
}

isSquare() {
return this.kind === 'square';
}

isCircle() {
return this.kind === 'circle';
}
}
"#,
)
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

pub mod shape;

use shape::{Shape, ShapeType};

#[wasm_bindgen]
pub fn test() {
let circle = Shape::new(ShapeType::Circle).unwrap();
let square = Shape::new(ShapeType::Square).unwrap();
assert!(circle.is_circle());
assert!(!circle.is_square());
assert!(square.is_square());
assert!(!square.is_circle());
}
"#,
)
.test();
}
1 change: 1 addition & 0 deletions crates/webidl/tests/all/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ extern crate wasm_bindgen_test_project_builder as project_builder;
use project_builder::project;

mod simple;
mod enums;
mod throws;