mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-11-28 05:52:21 +03:00
commit
97c021a53c
@ -978,6 +978,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
for f in self.program.imports.iter() {
|
||||
self.generate_import(f);
|
||||
}
|
||||
for e in self.program.enums.iter() {
|
||||
self.generate_enum(e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_export(&mut self, export: &shared::Export) {
|
||||
@ -1051,7 +1054,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
passed_args.push_str(arg);
|
||||
};
|
||||
match *arg {
|
||||
shared::TYPE_NUMBER => {
|
||||
shared::TYPE_ENUM | shared::TYPE_NUMBER => {
|
||||
dst_ts.push_str(": number");
|
||||
if self.cx.config.debug {
|
||||
self.cx.expose_assert_num();
|
||||
@ -1156,6 +1159,10 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
dst_ts.push_str(": void");
|
||||
format!("return ret;")
|
||||
}
|
||||
Some(shared::TYPE_ENUM) => {
|
||||
dst_ts.push_str(": number");
|
||||
format!("return ret;")
|
||||
}
|
||||
Some(shared::TYPE_NUMBER) => {
|
||||
dst_ts.push_str(": number");
|
||||
format!("return ret;")
|
||||
@ -1423,6 +1430,25 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
self.cx.globals.push_str(&dst);
|
||||
self.cx.globals.push_str("\n");
|
||||
}
|
||||
|
||||
pub fn generate_enum(&mut self, enum_: &shared::Enum) {
|
||||
let mut variants = String::new();
|
||||
|
||||
for variant in enum_.variants.iter() {
|
||||
variants.push_str(&format!("{}:{},", variant.name, variant.value));
|
||||
}
|
||||
self.cx.globals.push_str(&format!("export const {} = {{", enum_.name));
|
||||
self.cx.globals.push_str(&variants);
|
||||
self.cx.globals.push_str("}\n");
|
||||
self.cx.typescript.push_str(&format!("export enum {} {{", enum_.name));
|
||||
|
||||
variants.clear();
|
||||
for variant in enum_.variants.iter() {
|
||||
variants.push_str(&format!("{},", variant.name));
|
||||
}
|
||||
self.cx.typescript.push_str(&variants);
|
||||
self.cx.typescript.push_str("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct VectorType {
|
||||
|
@ -9,6 +9,7 @@ use syn;
|
||||
pub struct Program {
|
||||
pub exports: Vec<Export>,
|
||||
pub imports: Vec<Import>,
|
||||
pub enums: Vec<Enum>,
|
||||
pub imported_types: Vec<(syn::Visibility, syn::Ident)>,
|
||||
pub structs: Vec<Struct>,
|
||||
}
|
||||
@ -47,6 +48,11 @@ pub struct Struct {
|
||||
pub name: syn::Ident,
|
||||
}
|
||||
|
||||
pub struct Enum {
|
||||
pub name: syn::Ident,
|
||||
pub variants: Vec<(syn::Ident, u32)>
|
||||
}
|
||||
|
||||
pub enum Type {
|
||||
// special
|
||||
Vector(VectorType, bool),
|
||||
@ -110,8 +116,13 @@ impl Program {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||
self.push_foreign_mod(f, opts);
|
||||
}
|
||||
syn::Item::Enum(mut e) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut e.attrs));
|
||||
e.to_tokens(tokens);
|
||||
self.push_enum(e, opts);
|
||||
}
|
||||
_ => panic!("#[wasm_bindgen] can only be applied to a function, \
|
||||
struct, impl, or extern block"),
|
||||
struct, enum, impl, or extern block"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +206,36 @@ impl Program {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_enum(&mut self, item: syn::ItemEnum, _opts: BindgenAttrs) {
|
||||
match item.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => panic!("only public enums are allowed"),
|
||||
}
|
||||
|
||||
let variants = item.variants.iter().enumerate().map(|(i, v)| {
|
||||
match v.fields {
|
||||
syn::Fields::Unit => (),
|
||||
_ => panic!("Only C-Style enums allowed")
|
||||
}
|
||||
let value = match v.discriminant {
|
||||
Some((_, syn::Expr::Lit(syn::ExprLit {attrs: _, lit: syn::Lit::Int(ref int_lit)}))) => {
|
||||
if int_lit.value() > <u32>::max_value() as u64 {
|
||||
panic!("Enums can only support numbers that can be represented as u32");
|
||||
}
|
||||
int_lit.value() as u32
|
||||
},
|
||||
None => i as u32,
|
||||
_ => panic!("Enums may only have number literal values")
|
||||
};
|
||||
|
||||
(v.ident, value)
|
||||
}).collect();
|
||||
self.enums.push(Enum {
|
||||
name: item.ident,
|
||||
variants
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_foreign_fn(&mut self,
|
||||
mut f: syn::ForeignItemFn,
|
||||
module_opts: &BindgenAttrs) {
|
||||
@ -297,6 +338,7 @@ impl Program {
|
||||
a.fields(&[
|
||||
("exports", &|a| a.list(&self.exports, Export::wbg_literal)),
|
||||
("imports", &|a| a.list(&self.imports, Import::wbg_literal)),
|
||||
("enums", &|a| a.list(&self.enums, Enum::wbg_literal)),
|
||||
("custom_type_names", &|a| {
|
||||
let names = self.exports.iter()
|
||||
.filter_map(|e| e.class)
|
||||
@ -634,6 +676,19 @@ impl Import {
|
||||
}
|
||||
}
|
||||
|
||||
impl Enum {
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("variants", &|a| a.list(&self.variants, |v, a| {
|
||||
let &(name, value) = v;
|
||||
a.fields(&[("name", &|a| a.str(name.as_ref())),
|
||||
("value", &|a| a.append(&format!("{}", value)))])
|
||||
})),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct {
|
||||
Struct { name: s.ident }
|
||||
|
@ -50,6 +50,9 @@ fn generate_wrappers(program: ast::Program, tokens: &mut Tokens) {
|
||||
for i in program.imports.iter() {
|
||||
bindgen_import(i, tokens);
|
||||
}
|
||||
for e in program.enums.iter() {
|
||||
bindgen_enum(e, tokens);
|
||||
}
|
||||
for &(ref vis, ref t) in program.imported_types.iter() {
|
||||
bindgen_imported_type(vis, t, tokens);
|
||||
}
|
||||
@ -510,3 +513,40 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||
invocation.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
fn bindgen_enum(e: &ast::Enum, into: &mut Tokens) {
|
||||
let enum_name = &e.name;
|
||||
let c = shared::TYPE_ENUM as u32;
|
||||
let incoming_u32 = quote! { n };
|
||||
let enum_name_as_string = enum_name.to_string();
|
||||
let cast_clauses = e.variants.iter().map(|variant| {
|
||||
let &(variant_name, _) = variant;
|
||||
quote! {
|
||||
if #incoming_u32 == #enum_name::#variant_name as u32 {
|
||||
#enum_name::#variant_name
|
||||
}
|
||||
}
|
||||
});
|
||||
(my_quote! {
|
||||
impl #enum_name {
|
||||
fn from_u32(#incoming_u32: u32) -> #enum_name {
|
||||
#(#cast_clauses else)* {
|
||||
wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #enum_name_as_string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::wasm_bindgen::convert::WasmBoundary for #enum_name {
|
||||
type Js = u32;
|
||||
const DESCRIPTOR: u32 = #c;
|
||||
|
||||
fn into_js(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
|
||||
unsafe fn from_js(js: u32) -> Self {
|
||||
#enum_name::from_u32(js)
|
||||
}
|
||||
}
|
||||
}).to_tokens(into);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use std::hash::{Hash, Hasher};
|
||||
#[derive(Deserialize)]
|
||||
pub struct Program {
|
||||
pub exports: Vec<Export>,
|
||||
pub enums: Vec<Enum>,
|
||||
pub imports: Vec<Import>,
|
||||
pub custom_type_names: Vec<CustomTypeName>,
|
||||
}
|
||||
@ -32,6 +33,18 @@ pub struct Export {
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Enum {
|
||||
pub name: String,
|
||||
pub variants: Vec<EnumVariant>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EnumVariant {
|
||||
pub name: String,
|
||||
pub value: u32
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
@ -77,6 +90,7 @@ pub fn mangled_import_name(struct_: Option<&str>, f: &str) -> String {
|
||||
|
||||
pub type Type = char;
|
||||
|
||||
pub const TYPE_ENUM: char = '\u{5d}';
|
||||
pub const TYPE_NUMBER: char = '\u{5e}';
|
||||
pub const TYPE_BORROWED_STR: char = '\u{5f}';
|
||||
pub const TYPE_STRING: char = '\u{60}';
|
||||
|
87
tests/enums.rs
Normal file
87
tests/enums.rs
Normal file
@ -0,0 +1,87 @@
|
||||
extern crate test_support;
|
||||
|
||||
#[test]
|
||||
fn c_style_enum() {
|
||||
test_support::project()
|
||||
.file("src/lib.rs", r#"
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub enum Color {
|
||||
Green,
|
||||
Yellow,
|
||||
Red,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[wasm_bindgen]
|
||||
pub extern fn cycle(color: Color) -> Color {
|
||||
match color {
|
||||
Color::Green => Color::Yellow,
|
||||
Color::Yellow => Color::Red,
|
||||
Color::Red => Color::Green,
|
||||
}
|
||||
}
|
||||
"#)
|
||||
.file("test.ts", r#"
|
||||
import * as assert from "assert";
|
||||
import * as wasm from "./out";
|
||||
|
||||
export function test() {
|
||||
assert.strictEqual(wasm.Color.Green, 0);
|
||||
assert.strictEqual(wasm.Color.Yellow, 1);
|
||||
assert.strictEqual(wasm.Color.Red, 2);
|
||||
assert.strictEqual(Object.keys(wasm.Color).length, 3);
|
||||
|
||||
assert.strictEqual(wasm.cycle(wasm.Color.Green), wasm.Color.Yellow);
|
||||
}
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_style_enum_with_custom_values() {
|
||||
test_support::project()
|
||||
.file("src/lib.rs", r#"
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub enum Color {
|
||||
Green = 21,
|
||||
Yellow = 34,
|
||||
Red,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[wasm_bindgen]
|
||||
pub extern fn cycle(color: Color) -> Color {
|
||||
match color {
|
||||
Color::Green => Color::Yellow,
|
||||
Color::Yellow => Color::Red,
|
||||
Color::Red => Color::Green,
|
||||
}
|
||||
}
|
||||
"#)
|
||||
.file("test.ts", r#"
|
||||
import * as assert from "assert";
|
||||
import * as wasm from "./out";
|
||||
|
||||
export function test() {
|
||||
assert.strictEqual(wasm.Color.Green, 21);
|
||||
assert.strictEqual(wasm.Color.Yellow, 34);
|
||||
assert.strictEqual(wasm.Color.Red, 2);
|
||||
assert.strictEqual(Object.keys(wasm.Color).length, 3);
|
||||
|
||||
assert.strictEqual(wasm.cycle(wasm.Color.Green), wasm.Color.Yellow);
|
||||
}
|
||||
"#)
|
||||
.test();
|
||||
}
|
Loading…
Reference in New Issue
Block a user