mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-31 17:31:06 +00:00
Merge pull request #149 from rustwasm/struct-fields
Generate accessors for public struct fields
This commit is contained in:
commit
542b3a28eb
crates
tests/all
@ -68,6 +68,15 @@ pub struct Function {
|
|||||||
|
|
||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
pub name: syn::Ident,
|
pub name: syn::Ident,
|
||||||
|
pub fields: Vec<StructField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StructField {
|
||||||
|
pub name: syn::Ident,
|
||||||
|
pub struct_name: syn::Ident,
|
||||||
|
pub ty: syn::Type,
|
||||||
|
pub getter: syn::Ident,
|
||||||
|
pub setter: syn::Ident,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Enum {
|
pub struct Enum {
|
||||||
@ -415,7 +424,7 @@ impl Program {
|
|||||||
pub fn shared(&self) -> shared::Program {
|
pub fn shared(&self) -> shared::Program {
|
||||||
shared::Program {
|
shared::Program {
|
||||||
exports: self.exports.iter().map(|a| a.shared()).collect(),
|
exports: self.exports.iter().map(|a| a.shared()).collect(),
|
||||||
structs: self.structs.iter().map(|a| a.name.as_ref().to_string()).collect(),
|
structs: self.structs.iter().map(|a| a.shared()).collect(),
|
||||||
enums: self.enums.iter().map(|a| a.shared()).collect(),
|
enums: self.enums.iter().map(|a| a.shared()).collect(),
|
||||||
imports: self.imports.iter().map(|a| a.shared()).collect(),
|
imports: self.imports.iter().map(|a| a.shared()).collect(),
|
||||||
version: shared::version(),
|
version: shared::version(),
|
||||||
@ -659,7 +668,47 @@ impl ImportType {
|
|||||||
|
|
||||||
impl Struct {
|
impl Struct {
|
||||||
fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct {
|
fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct {
|
||||||
Struct { name: s.ident }
|
let mut fields = Vec::new();
|
||||||
|
if let syn::Fields::Named(names) = s.fields {
|
||||||
|
for field in names.named.iter() {
|
||||||
|
match field.vis {
|
||||||
|
syn::Visibility::Public(..) => {}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
let name = match field.ident {
|
||||||
|
Some(n) => n,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let getter = shared::struct_field_get(s.ident.as_ref(), name.as_ref());
|
||||||
|
let setter = shared::struct_field_set(s.ident.as_ref(), name.as_ref());
|
||||||
|
fields.push(StructField {
|
||||||
|
name,
|
||||||
|
struct_name: s.ident,
|
||||||
|
ty: field.ty.clone(),
|
||||||
|
getter: getter.into(),
|
||||||
|
setter: setter.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Struct {
|
||||||
|
name: s.ident,
|
||||||
|
fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shared(&self) -> shared::Struct {
|
||||||
|
shared::Struct {
|
||||||
|
name: self.name.as_ref().to_string(),
|
||||||
|
fields: self.fields.iter().map(|s| s.shared()).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructField {
|
||||||
|
fn shared(&self) -> shared::StructField {
|
||||||
|
shared::StructField {
|
||||||
|
name: self.name.as_ref().to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +212,65 @@ impl ToTokens for ast::Struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).to_tokens(tokens);
|
}).to_tokens(tokens);
|
||||||
|
|
||||||
|
for field in self.fields.iter() {
|
||||||
|
field.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for ast::StructField {
|
||||||
|
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||||
|
let name = &self.name;
|
||||||
|
let struct_name = &self.struct_name;
|
||||||
|
let ty = &self.ty;
|
||||||
|
let getter = &self.getter;
|
||||||
|
let setter = &self.setter;
|
||||||
|
let desc = syn::Ident::from(format!("__wbindgen_describe_{}", getter));
|
||||||
|
(quote! {
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern fn #getter(js: u32)
|
||||||
|
-> <#ty as ::wasm_bindgen::convert::IntoWasmAbi>::Abi
|
||||||
|
{
|
||||||
|
use wasm_bindgen::__rt::{WasmRefCell, assert_not_null};
|
||||||
|
use wasm_bindgen::convert::{GlobalStack, IntoWasmAbi};
|
||||||
|
|
||||||
|
fn assert_copy<T: Copy>(){}
|
||||||
|
assert_copy::<#ty>();
|
||||||
|
|
||||||
|
let js = js as *mut WasmRefCell<#struct_name>;
|
||||||
|
assert_not_null(js);
|
||||||
|
let val = (*js).borrow().#name;
|
||||||
|
<#ty as IntoWasmAbi>::into_abi(
|
||||||
|
val,
|
||||||
|
&mut GlobalStack::new(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern fn #setter(
|
||||||
|
js: u32,
|
||||||
|
val: <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi,
|
||||||
|
) {
|
||||||
|
use wasm_bindgen::__rt::{WasmRefCell, assert_not_null};
|
||||||
|
use wasm_bindgen::convert::{GlobalStack, FromWasmAbi};
|
||||||
|
|
||||||
|
let js = js as *mut WasmRefCell<#struct_name>;
|
||||||
|
assert_not_null(js);
|
||||||
|
let val = <#ty as FromWasmAbi>::from_abi(
|
||||||
|
val,
|
||||||
|
&mut GlobalStack::new(),
|
||||||
|
);
|
||||||
|
(*js).borrow_mut().#name = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn #desc() {
|
||||||
|
use wasm_bindgen::describe::*;
|
||||||
|
<#ty as WasmDescribe>::describe();
|
||||||
|
|
||||||
|
}
|
||||||
|
}).to_tokens(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ pub struct Js2Rust<'a, 'b: 'a> {
|
|||||||
rust_arguments: Vec<String>,
|
rust_arguments: Vec<String>,
|
||||||
|
|
||||||
/// Arguments and their types to the JS shim.
|
/// Arguments and their types to the JS shim.
|
||||||
js_arguments: Vec<(String, String)>,
|
pub js_arguments: Vec<(String, String)>,
|
||||||
|
|
||||||
/// Conversions that happen before we invoke the wasm function, such as
|
/// Conversions that happen before we invoke the wasm function, such as
|
||||||
/// converting a string to a ptr/length pair.
|
/// converting a string to a ptr/length pair.
|
||||||
@ -100,7 +100,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn argument(&mut self, arg: &Descriptor) {
|
pub fn argument(&mut self, arg: &Descriptor) -> &mut Self {
|
||||||
let i = self.arg_idx;
|
let i = self.arg_idx;
|
||||||
self.arg_idx += 1;
|
self.arg_idx += 1;
|
||||||
let name = format!("arg{}", i);
|
let name = format!("arg{}", i);
|
||||||
@ -122,7 +122,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
self.cx.require_internal_export("__wbindgen_free");
|
self.cx.require_internal_export("__wbindgen_free");
|
||||||
}
|
}
|
||||||
self.rust_arguments.push(format!("ptr{}", i));
|
self.rust_arguments.push(format!("ptr{}", i));
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = arg.rust_struct() {
|
if let Some(s) = arg.rust_struct() {
|
||||||
@ -144,7 +144,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
", i = i, arg = name));
|
", i = i, arg = name));
|
||||||
self.rust_arguments.push(format!("ptr{}", i));
|
self.rust_arguments.push(format!("ptr{}", i));
|
||||||
}
|
}
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg.is_number() {
|
if arg.is_number() {
|
||||||
@ -156,7 +156,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.rust_arguments.push(name);
|
self.rust_arguments.push(name);
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg.is_ref_anyref() {
|
if arg.is_ref_anyref() {
|
||||||
@ -164,7 +164,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
self.cx.expose_borrowed_objects();
|
self.cx.expose_borrowed_objects();
|
||||||
self.finally("stack.pop();");
|
self.finally("stack.pop();");
|
||||||
self.rust_arguments.push(format!("addBorrowedObject({})", name));
|
self.rust_arguments.push(format!("addBorrowedObject({})", name));
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
match *arg {
|
match *arg {
|
||||||
@ -187,15 +187,16 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
panic!("unsupported argument to rust function {:?}", arg)
|
panic!("unsupported argument to rust function {:?}", arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret(&mut self, ret: &Option<Descriptor>) {
|
pub fn ret(&mut self, ret: &Option<Descriptor>) -> &mut Self {
|
||||||
let ty = match *ret {
|
let ty = match *ret {
|
||||||
Some(ref t) => t,
|
Some(ref t) => t,
|
||||||
None => {
|
None => {
|
||||||
self.ret_ty = "void".to_string();
|
self.ret_ty = "void".to_string();
|
||||||
self.ret_expr = format!("return RET;");
|
self.ret_expr = format!("return RET;");
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -203,7 +204,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
self.ret_ty = "any".to_string();
|
self.ret_ty = "any".to_string();
|
||||||
self.cx.expose_get_object();
|
self.cx.expose_get_object();
|
||||||
self.ret_expr = format!("return getObject(RET);");
|
self.ret_expr = format!("return getObject(RET);");
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
if ty.is_by_ref() {
|
if ty.is_by_ref() {
|
||||||
@ -222,19 +223,19 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
wasm.__wbindgen_free(ret, len * {});\n\
|
wasm.__wbindgen_free(ret, len * {});\n\
|
||||||
return realRet;\n\
|
return realRet;\n\
|
||||||
", f, ty.size());
|
", f, ty.size());
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = ty.rust_struct() {
|
if let Some(name) = ty.rust_struct() {
|
||||||
self.ret_ty = name.to_string();
|
self.ret_ty = name.to_string();
|
||||||
self.ret_expr = format!("return {name}.__construct(RET);", name = name);
|
self.ret_expr = format!("return {name}.__construct(RET);", name = name);
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
if ty.is_number() {
|
if ty.is_number() {
|
||||||
self.ret_ty = "number".to_string();
|
self.ret_ty = "number".to_string();
|
||||||
self.ret_expr = format!("return RET;");
|
self.ret_expr = format!("return RET;");
|
||||||
return
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
match *ty {
|
match *ty {
|
||||||
@ -249,6 +250,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
}
|
}
|
||||||
_ => panic!("unsupported return from Rust to JS {:?}", ty),
|
_ => panic!("unsupported return from Rust to JS {:?}", ty),
|
||||||
}
|
}
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the actual function.
|
/// Generate the actual function.
|
||||||
|
@ -35,6 +35,11 @@ pub struct ExportedClass {
|
|||||||
pub contents: String,
|
pub contents: String,
|
||||||
pub typescript: String,
|
pub typescript: String,
|
||||||
pub constructor: Option<String>,
|
pub constructor: Option<String>,
|
||||||
|
pub fields: Vec<ClassField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClassField {
|
||||||
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubContext<'a, 'b: 'a> {
|
pub struct SubContext<'a, 'b: 'a> {
|
||||||
@ -341,78 +346,111 @@ impl<'a> Context<'a> {
|
|||||||
fn write_classes(&mut self) {
|
fn write_classes(&mut self) {
|
||||||
let classes = mem::replace(&mut self.exported_classes, Default::default());
|
let classes = mem::replace(&mut self.exported_classes, Default::default());
|
||||||
for (class, exports) in classes {
|
for (class, exports) in classes {
|
||||||
let mut dst = format!("class {} {{\n", class);
|
self.write_class(&class, &exports);
|
||||||
let mut ts_dst = format!("export {}", dst);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.config.debug || exports.constructor.is_some() {
|
fn write_class(&mut self, name: &str, class: &ExportedClass) {
|
||||||
self.expose_constructor_token();
|
let mut dst = format!("class {} {{\n", name);
|
||||||
|
let mut ts_dst = format!("export {}", dst);
|
||||||
|
|
||||||
dst.push_str(&format!("
|
if self.config.debug || class.constructor.is_some() {
|
||||||
static __construct(ptr) {{
|
self.expose_constructor_token();
|
||||||
return new {}(new ConstructorToken(ptr));
|
|
||||||
}}
|
|
||||||
|
|
||||||
constructor(...args) {{
|
|
||||||
if (args.length === 1 && args[0] instanceof ConstructorToken) {{
|
|
||||||
this.ptr = args[0].ptr;
|
|
||||||
return;
|
|
||||||
}}
|
|
||||||
", class));
|
|
||||||
|
|
||||||
if let Some(constructor) = exports.constructor {
|
|
||||||
ts_dst.push_str(&format!("constructor(...args: [any]);\n"));
|
|
||||||
|
|
||||||
dst.push_str(&format!("
|
|
||||||
// This invocation of new will call this constructor with a ConstructorToken
|
|
||||||
let instance = {class}.{constructor}(...args);
|
|
||||||
this.ptr = instance.ptr;
|
|
||||||
", class = class, constructor = constructor));
|
|
||||||
} else {
|
|
||||||
dst.push_str("throw new Error('you cannot invoke `new` directly without having a \
|
|
||||||
method annotated a constructor');");
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.push_str("}");
|
|
||||||
} else {
|
|
||||||
dst.push_str(&format!("
|
|
||||||
static __construct(ptr) {{
|
|
||||||
return new {}(ptr);
|
|
||||||
}}
|
|
||||||
|
|
||||||
constructor(ptr) {{
|
|
||||||
this.ptr = ptr;
|
|
||||||
}}
|
|
||||||
", class));
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_name = shared::new_function(&class);
|
|
||||||
if self.wasm_import_needed(&new_name) {
|
|
||||||
self.expose_add_heap_object();
|
|
||||||
|
|
||||||
self.export(&new_name, &format!("
|
|
||||||
function(ptr) {{
|
|
||||||
return addHeapObject({}.__construct(ptr));
|
|
||||||
}}
|
|
||||||
", class));
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.push_str(&format!("
|
dst.push_str(&format!("
|
||||||
free() {{
|
static __construct(ptr) {{
|
||||||
const ptr = this.ptr;
|
return new {}(new ConstructorToken(ptr));
|
||||||
this.ptr = 0;
|
|
||||||
wasm.{}(ptr);
|
|
||||||
}}
|
}}
|
||||||
", shared::free_function(&class)));
|
|
||||||
ts_dst.push_str("free(): void;\n");
|
|
||||||
|
|
||||||
dst.push_str(&exports.contents);
|
constructor(...args) {{
|
||||||
ts_dst.push_str(&exports.typescript);
|
if (args.length === 1 && args[0] instanceof ConstructorToken) {{
|
||||||
dst.push_str("}\n");
|
this.ptr = args[0].ptr;
|
||||||
ts_dst.push_str("}\n");
|
return;
|
||||||
|
}}
|
||||||
|
", name));
|
||||||
|
|
||||||
self.export(&class, &dst);
|
if let Some(ref constructor) = class.constructor {
|
||||||
self.typescript.push_str(&ts_dst);
|
ts_dst.push_str(&format!("constructor(...args: [any]);\n"));
|
||||||
|
|
||||||
|
dst.push_str(&format!("
|
||||||
|
// This invocation of new will call this constructor with a ConstructorToken
|
||||||
|
let instance = {class}.{constructor}(...args);
|
||||||
|
this.ptr = instance.ptr;
|
||||||
|
", class = name, constructor = constructor));
|
||||||
|
} else {
|
||||||
|
dst.push_str("throw new Error('you cannot invoke `new` directly without having a \
|
||||||
|
method annotated a constructor');");
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.push_str("}");
|
||||||
|
} else {
|
||||||
|
dst.push_str(&format!("
|
||||||
|
static __construct(ptr) {{
|
||||||
|
return new {}(ptr);
|
||||||
|
}}
|
||||||
|
|
||||||
|
constructor(ptr) {{
|
||||||
|
this.ptr = ptr;
|
||||||
|
}}
|
||||||
|
", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let new_name = shared::new_function(&name);
|
||||||
|
if self.wasm_import_needed(&new_name) {
|
||||||
|
self.expose_add_heap_object();
|
||||||
|
|
||||||
|
self.export(&new_name, &format!("
|
||||||
|
function(ptr) {{
|
||||||
|
return addHeapObject({}.__construct(ptr));
|
||||||
|
}}
|
||||||
|
", name));
|
||||||
|
}
|
||||||
|
|
||||||
|
for field in class.fields.iter() {
|
||||||
|
let wasm_getter = shared::struct_field_get(name, &field.name);
|
||||||
|
let wasm_setter = shared::struct_field_set(name, &field.name);
|
||||||
|
let descriptor = self.describe(&wasm_getter);
|
||||||
|
|
||||||
|
let set = {
|
||||||
|
let mut cx = Js2Rust::new(&field.name, self);
|
||||||
|
cx.method(true)
|
||||||
|
.argument(&descriptor)
|
||||||
|
.ret(&None);
|
||||||
|
ts_dst.push_str(&format!("{}: {}\n",
|
||||||
|
field.name,
|
||||||
|
&cx.js_arguments[0].1));
|
||||||
|
cx.finish("", &format!("wasm.{}", wasm_setter)).0
|
||||||
|
};
|
||||||
|
let (get, _ts) = Js2Rust::new(&field.name, self)
|
||||||
|
.method(true)
|
||||||
|
.ret(&Some(descriptor))
|
||||||
|
.finish("", &format!("wasm.{}", wasm_getter));
|
||||||
|
|
||||||
|
dst.push_str("get ");
|
||||||
|
dst.push_str(&field.name);
|
||||||
|
dst.push_str(&get);
|
||||||
|
dst.push_str("\n");
|
||||||
|
dst.push_str("set ");
|
||||||
|
dst.push_str(&field.name);
|
||||||
|
dst.push_str(&set);
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.push_str(&format!("
|
||||||
|
free() {{
|
||||||
|
const ptr = this.ptr;
|
||||||
|
this.ptr = 0;
|
||||||
|
wasm.{}(ptr);
|
||||||
|
}}
|
||||||
|
", shared::free_function(&name)));
|
||||||
|
ts_dst.push_str("free(): void;\n");
|
||||||
|
|
||||||
|
dst.push_str(&class.contents);
|
||||||
|
ts_dst.push_str(&class.typescript);
|
||||||
|
dst.push_str("}\n");
|
||||||
|
ts_dst.push_str("}\n");
|
||||||
|
|
||||||
|
self.export(&name, &dst);
|
||||||
|
self.typescript.push_str(&ts_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn export_table(&mut self) {
|
fn export_table(&mut self) {
|
||||||
@ -1268,8 +1306,14 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
}
|
}
|
||||||
for s in self.program.structs.iter() {
|
for s in self.program.structs.iter() {
|
||||||
self.cx.exported_classes
|
self.cx.exported_classes
|
||||||
.entry(s.clone())
|
.entry(s.name.clone())
|
||||||
.or_insert_with(Default::default);
|
.or_insert_with(Default::default)
|
||||||
|
.fields
|
||||||
|
.extend(s.fields.iter().map(|s| {
|
||||||
|
ClassField {
|
||||||
|
name: s.name.clone(),
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
pub const SCHEMA_VERSION: &str = "2";
|
pub const SCHEMA_VERSION: &str = "3";
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct ProgramOnlySchema {
|
pub struct ProgramOnlySchema {
|
||||||
@ -14,7 +14,7 @@ pub struct Program {
|
|||||||
pub exports: Vec<Export>,
|
pub exports: Vec<Export>,
|
||||||
pub enums: Vec<Enum>,
|
pub enums: Vec<Enum>,
|
||||||
pub imports: Vec<Import>,
|
pub imports: Vec<Import>,
|
||||||
pub structs: Vec<String>,
|
pub structs: Vec<Struct>,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub schema_version: String,
|
pub schema_version: String,
|
||||||
}
|
}
|
||||||
@ -82,6 +82,17 @@ pub struct Function {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Struct {
|
||||||
|
pub name: String,
|
||||||
|
pub fields: Vec<StructField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct StructField {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_function(struct_name: &str) -> String {
|
pub fn new_function(struct_name: &str) -> String {
|
||||||
let mut name = format!("__wbg_");
|
let mut name = format!("__wbg_");
|
||||||
name.extend(struct_name
|
name.extend(struct_name
|
||||||
@ -114,6 +125,26 @@ pub fn struct_function_export_name(struct_: &str, f: &str) -> String {
|
|||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn struct_field_get(struct_: &str, f: &str) -> String {
|
||||||
|
let mut name = String::from("__wbg_get_");
|
||||||
|
name.extend(struct_
|
||||||
|
.chars()
|
||||||
|
.flat_map(|s| s.to_lowercase()));
|
||||||
|
name.push_str("_");
|
||||||
|
name.push_str(f);
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn struct_field_set(struct_: &str, f: &str) -> String {
|
||||||
|
let mut name = String::from("__wbg_set_");
|
||||||
|
name.extend(struct_
|
||||||
|
.chars()
|
||||||
|
.flat_map(|s| s.to_lowercase()));
|
||||||
|
name.push_str("_");
|
||||||
|
name.push_str(f);
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
pub fn version() -> String {
|
pub fn version() -> String {
|
||||||
let mut v = env!("CARGO_PKG_VERSION").to_string();
|
let mut v = env!("CARGO_PKG_VERSION").to_string();
|
||||||
if let Some(s) = option_env!("WBG_VERSION") {
|
if let Some(s) = option_env!("WBG_VERSION") {
|
||||||
|
@ -473,3 +473,56 @@ fn empty_structs() {
|
|||||||
"#)
|
"#)
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn public_fields() {
|
||||||
|
project()
|
||||||
|
.debug(false)
|
||||||
|
.file("src/lib.rs", r#"
|
||||||
|
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Foo {
|
||||||
|
pub a: u32,
|
||||||
|
pub b: f32,
|
||||||
|
pub c: f64,
|
||||||
|
pub d: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
impl Foo {
|
||||||
|
pub fn new() -> Foo {
|
||||||
|
Foo::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.file("test.ts", r#"
|
||||||
|
import { Foo } from "./out";
|
||||||
|
import * as assert from "assert";
|
||||||
|
|
||||||
|
export function test() {
|
||||||
|
const a = Foo.new();
|
||||||
|
assert.strictEqual(a.a, 0);
|
||||||
|
a.a = 3;
|
||||||
|
assert.strictEqual(a.a, 3);
|
||||||
|
|
||||||
|
assert.strictEqual(a.b, 0);
|
||||||
|
a.b = 7;
|
||||||
|
assert.strictEqual(a.b, 7);
|
||||||
|
|
||||||
|
assert.strictEqual(a.c, 0);
|
||||||
|
a.c = 8;
|
||||||
|
assert.strictEqual(a.c, 8);
|
||||||
|
|
||||||
|
assert.strictEqual(a.d, 0);
|
||||||
|
a.d = 3.3;
|
||||||
|
assert.strictEqual(a.d, 3);
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user