diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 2c3c9661..cfdc2367 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -42,6 +42,7 @@ pub enum ImportKind { Static(ImportStatic), Type(ImportType), Enum(ImportEnum), + Const(Const), } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -174,6 +175,24 @@ pub struct TypeAlias { pub src: syn::Type, } +#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +pub struct Const { + pub vis: syn::Visibility, + pub name: Ident, + pub interface_name: Ident, + pub ty: syn::Type, + pub value: ConstValue, +} + +#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +/// same as webidl::ast::ConstValue +pub enum ConstValue { + BooleanLiteral(bool), + FloatLiteral(f64), + IntegerLiteral(i64), + Null, +} + impl Program { pub(crate) fn shared(&self) -> shared::Program { shared::Program { @@ -293,6 +312,7 @@ impl ImportKind { ImportKind::Static(_) => false, ImportKind::Type(_) => false, ImportKind::Enum(_) => false, + ImportKind::Const(_) => false, } } @@ -302,6 +322,7 @@ impl ImportKind { 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()), + ImportKind::Const(ref f) => shared::ImportKind::Const(f.shared()), } } } @@ -404,3 +425,9 @@ impl StructField { } } } + +impl Const { + fn shared(&self) -> shared::Const { + shared::Const {} + } +} diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 1e3e69af..a83d1923 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -501,6 +501,7 @@ impl ToTokens for ast::ImportKind { 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), + ast::ImportKind::Const(ref c) => c.to_tokens(tokens), } } } @@ -842,6 +843,7 @@ impl<'a> ToTokens for DescribeImport<'a> { ast::ImportKind::Static(_) => return, ast::ImportKind::Type(_) => return, ast::ImportKind::Enum(_) => return, + ast::ImportKind::Const(_) => return, }; let describe_name = format!("__wbindgen_describe_{}", f.shim); let describe_name = Ident::new(&describe_name, Span::call_site()); @@ -958,3 +960,41 @@ impl ToTokens for ast::TypeAlias { }).to_tokens(into); } } + +impl ToTokens for ast::Const { + fn to_tokens(&self, tokens: &mut TokenStream) { + use ast::ConstValue::*; + + let vis = &self.vis; + let name = &self.name; + let interface_name = &self.interface_name; + let ty = &self.ty; + + let value: TokenStream = match self.value { + BooleanLiteral(false) => quote!(false), + BooleanLiteral(true) => quote!(true), + // the actual type is unknown because of typedefs + // so we cannot use std::fxx::INFINITY + // but we can use type inference + FloatLiteral(f) if f.is_infinite() && f.is_sign_positive() => quote!(1.0 / 0.0), + FloatLiteral(f) if f.is_infinite() && f.is_sign_negative() => quote!(-1.0 / 0.0), + FloatLiteral(f) if f.is_nan() => quote!(0.0 / 0.0), + // again no suffix + // panics on +-inf, nan + FloatLiteral(f) => { + let f = Literal::f64_unsuffixed(f); + quote!(#f) + }, + IntegerLiteral(i) => { + let i = Literal::i64_unsuffixed(i); + quote!(#i) + }, + Null => unimplemented!(), + }; + (quote! { + impl #interface_name { + #vis const #name: #ty = #value; + } + }).to_tokens(tokens); + } +} diff --git a/crates/backend/src/defined.rs b/crates/backend/src/defined.rs index d775cb02..84eabe50 100644 --- a/crates/backend/src/defined.rs +++ b/crates/backend/src/defined.rs @@ -106,6 +106,7 @@ impl ImportedTypes for ast::ImportKind { ast::ImportKind::Function(fun) => fun.imported_types(f), ast::ImportKind::Type(ty) => ty.imported_types(f), ast::ImportKind::Enum(enm) => enm.imported_types(f), + ast::ImportKind::Const(c) => c.imported_types(f), } } } @@ -229,6 +230,15 @@ impl ImportedTypes for ast::TypeAlias { } } +impl ImportedTypes for ast::Const { + fn imported_types(&self, f: &mut F) + where + F: FnMut(&Ident, ImportedTypeKind), + { + self.ty.imported_types(f); + } +} + /// Remove any methods, statics, &c, that reference types that are *not* /// defined. pub trait RemoveUndefinedImports { diff --git a/crates/cli-support/Cargo.toml b/crates/cli-support/Cargo.toml index 7cf552f2..09ef9558 100644 --- a/crates/cli-support/Cargo.toml +++ b/crates/cli-support/Cargo.toml @@ -19,5 +19,5 @@ serde_derive = "1.0" serde_json = "1.0" tempfile = "3.0" wasm-bindgen-shared = { path = "../shared", version = '=0.2.11' } -wasm-gc-api = "0.1" +wasm-gc-api = "0.1.8" wasmi = "0.3" diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index ef47aa55..9a9a7271 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1580,6 +1580,7 @@ impl<'a> Context<'a> { let wasm_bytes = parity_wasm::serialize(module)?; let bytes = wasm_gc::Config::new() .demangle(self.config.demangle) + .keep_debug(self.config.keep_debug || self.config.debug) .gc(&wasm_bytes)?; *self.module = deserialize_buffer(&bytes)?; Ok(()) @@ -1757,6 +1758,7 @@ impl<'a, 'b> SubContext<'a, 'b> { } shared::ImportKind::Type(_) => {} shared::ImportKind::Enum(_) => {} + shared::ImportKind::Const(_) => {} } Ok(()) } diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index 410700f3..a9a88769 100644 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -31,6 +31,7 @@ pub struct Bindgen { debug: bool, typescript: bool, demangle: bool, + keep_debug: bool, } impl Bindgen { @@ -45,6 +46,7 @@ impl Bindgen { debug: false, typescript: false, demangle: true, + keep_debug: false, } } @@ -93,6 +95,11 @@ impl Bindgen { self } + pub fn keep_debug(&mut self, keep_debug: bool) -> &mut Bindgen { + self.keep_debug = keep_debug; + self + } + pub fn generate>(&mut self, path: P) -> Result<(), Error> { self._generate(path.as_ref()) } diff --git a/crates/cli/src/bin/wasm-bindgen.rs b/crates/cli/src/bin/wasm-bindgen.rs index 606a3fee..996bf5aa 100644 --- a/crates/cli/src/bin/wasm-bindgen.rs +++ b/crates/cli/src/bin/wasm-bindgen.rs @@ -32,6 +32,7 @@ Options: --no-typescript Don't emit a *.d.ts file --debug Include otherwise-extraneous debug checks in output --no-demangle Don't demangle Rust symbol names + --keep-debug Keep debug sections in wasm files -V --version Print the version number of wasm-bindgen "; @@ -47,6 +48,7 @@ struct Args { flag_version: bool, flag_no_demangle: bool, flag_no_modules_global: Option, + flag_keep_debug: bool, arg_input: Option, } @@ -85,6 +87,7 @@ fn rmain(args: &Args) -> Result<(), Error> { .no_modules(args.flag_no_modules) .debug(args.flag_debug) .demangle(!args.flag_no_demangle) + .keep_debug(args.flag_keep_debug) .typescript(typescript); if let Some(ref name) = args.flag_no_modules_global { b.no_modules_global(name); diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 8df1e534..8985733d 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -34,6 +34,7 @@ pub enum ImportKind { Static(ImportStatic), Type(ImportType), Enum(ImportEnum), + Const(Const) } #[derive(Deserialize, Serialize)] @@ -124,6 +125,9 @@ pub struct StructField { pub comments: Vec, } +#[derive(Deserialize, Serialize)] +pub struct Const {} + pub fn new_function(struct_name: &str) -> String { let mut name = format!("__wbg_"); name.extend(struct_name.chars().flat_map(|s| s.to_lowercase())); diff --git a/crates/web-sys/tests/all/headers.rs b/crates/web-sys/tests/all/headers.rs new file mode 100644 index 00000000..8431f4f4 --- /dev/null +++ b/crates/web-sys/tests/all/headers.rs @@ -0,0 +1,33 @@ +use super::websys_project; + +#[test] +fn headers() { + websys_project() + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section)] + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + extern crate web_sys; + + #[wasm_bindgen] + pub fn test_headers(_headers: &web_sys::Headers) { + // empty for now... + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + let headers = new Headers({'Content-Type': 'text/plain'}); + wasm.test_headers(headers); + } + "#, + ) + .test(); +} diff --git a/crates/web-sys/tests/all/main.rs b/crates/web-sys/tests/all/main.rs index e0181c24..181a596e 100644 --- a/crates/web-sys/tests/all/main.rs +++ b/crates/web-sys/tests/all/main.rs @@ -2,6 +2,7 @@ extern crate wasm_bindgen_test_project_builder as project_builder; use project_builder::{project, Project}; mod event; +mod headers; mod response; fn websys_project() -> Project { diff --git a/crates/web-sys/webidls/available/Headers.webidl b/crates/web-sys/webidls/enabled/Headers.webidl similarity index 100% rename from crates/web-sys/webidls/available/Headers.webidl rename to crates/web-sys/webidls/enabled/Headers.webidl diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 02fe8224..37c18758 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -30,11 +30,11 @@ 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 heck::{CamelCase, ShoutySnakeCase}; use quote::ToTokens; use first_pass::{FirstPass, FirstPassRecord}; -use util::{public, TypePosition}; +use util::{public, webidl_const_ty_to_syn_ty, webidl_const_v_to_backend_const_v, TypePosition}; /// Either `Ok(t)` or `Err(failure::Error)`. pub type Result = ::std::result::Result; @@ -250,7 +250,7 @@ impl WebidlParse<()> for webidl::ast::NonPartialInterface { js_namespace: None, kind: backend::ast::ImportKind::Type(backend::ast::ImportType { vis: public(), - name: rust_ident(&self.name), + name: rust_ident(self.name.to_camel_case().as_str()), attrs: Vec::new(), }), }); @@ -343,7 +343,8 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte match self { webidl::ast::ExtendedAttribute::ArgumentList( webidl::ast::ArgumentListExtendedAttribute { arguments, name }, - ) if name == "Constructor" => + ) + if name == "Constructor" => { add_constructor(arguments, &interface.name) } @@ -358,7 +359,8 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte rhs_arguments, rhs_name, }, - ) if lhs_name == "NamedConstructor" => + ) + if lhs_name == "NamedConstructor" => { add_constructor(rhs_arguments, rhs_name) } @@ -389,9 +391,11 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { webidl::ast::InterfaceMember::Operation(op) => { op.webidl_parse(program, first_pass, self_name) } + webidl::ast::InterfaceMember::Const(cnst) => { + cnst.webidl_parse(program, first_pass, self_name) + } // TODO - webidl::ast::InterfaceMember::Const(_) - | webidl::ast::InterfaceMember::Iterable(_) + webidl::ast::InterfaceMember::Iterable(_) | webidl::ast::InterfaceMember::Maplike(_) | webidl::ast::InterfaceMember::Setlike(_) => { warn!("Unsupported WebIDL interface member: {:?}", self); @@ -637,3 +641,29 @@ impl<'a> WebidlParse<()> for webidl::ast::Enum { Ok(()) } } + +impl<'a> WebidlParse<&'a str> for webidl::ast::Const { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + _: &FirstPassRecord<'_>, + interface_name: &'a str, + ) -> Result<()> { + let syn_ty = webidl_const_ty_to_syn_ty(&self.type_); + program.imports.push(backend::ast::Import { + module: None, + version: None, + js_namespace: None, + kind: backend::ast::ImportKind::Const(backend::ast::Const { + vis: syn::Visibility::Public(syn::VisPublic { + pub_token: Default::default(), + }), + name: rust_ident(self.name.to_shouty_snake_case().as_str()), + interface_name: rust_ident(interface_name.to_camel_case().as_str()), + ty: syn_ty, + value: webidl_const_v_to_backend_const_v(&self.value), + }), + }); + Ok(()) + } +} diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 2eec7b07..93c7d050 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -19,6 +19,35 @@ fn shared_ref(ty: syn::Type) -> syn::Type { }.into() } +pub fn webidl_const_ty_to_syn_ty(ty: &webidl::ast::ConstType) -> syn::Type { + use webidl::ast::ConstType::*; + + // similar to webidl_ty_to_syn_ty + match ty { + Boolean => ident_ty(raw_ident("bool")), + Byte => ident_ty(raw_ident("i8")), + Octet => ident_ty(raw_ident("u8")), + RestrictedDouble | UnrestrictedDouble => ident_ty(raw_ident("f64")), + RestrictedFloat | UnrestrictedFloat => ident_ty(raw_ident("f32")), + SignedLong => ident_ty(raw_ident("i32")), + SignedLongLong => ident_ty(raw_ident("i64")), + SignedShort => ident_ty(raw_ident("i16")), + UnsignedLong => ident_ty(raw_ident("u32")), + UnsignedLongLong => ident_ty(raw_ident("u64")), + UnsignedShort => ident_ty(raw_ident("u16")), + Identifier(ref id) => ident_ty(rust_ident(id)), + } +} + +pub fn webidl_const_v_to_backend_const_v(v: &webidl::ast::ConstValue) -> backend::ast::ConstValue { + match *v { + webidl::ast::ConstValue::BooleanLiteral(b) => backend::ast::ConstValue::BooleanLiteral(b), + webidl::ast::ConstValue::FloatLiteral(f) => backend::ast::ConstValue::FloatLiteral(f), + webidl::ast::ConstValue::IntegerLiteral(i) => backend::ast::ConstValue::IntegerLiteral(i), + webidl::ast::ConstValue::Null => backend::ast::ConstValue::Null, + } +} + fn simple_fn_arg(ident: Ident, ty: syn::Type) -> syn::ArgCaptured { syn::ArgCaptured { pat: syn::Pat::Ident(syn::PatIdent { diff --git a/crates/webidl/tests/all/consts.rs b/crates/webidl/tests/all/consts.rs new file mode 100644 index 00000000..716fbe8a --- /dev/null +++ b/crates/webidl/tests/all/consts.rs @@ -0,0 +1,190 @@ +use super::project; + +#[test] +fn bool() { + project() + .file( + "foo.webidl", + r#" + interface Foo { + const boolean not_true = false; + const boolean not_false = true; + }; + "#, + ) + // a corresponding const in the js implementation is not required + // value is taken directly from idl + .file( + "foo.js", + r#" + export class Foo { + } + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + + pub mod foo; + use foo::Foo; + + #[wasm_bindgen] + pub fn test() { + let falsish: bool = Foo::NOT_TRUE; + assert!(!falsish); + let trueish: bool = Foo::NOT_FALSE; + assert!(trueish); + } + "#, + ) + .test(); +} + +#[test] +fn ints() { + project() + .file( + "foo.webidl", + r#" + interface Byte { + const byte imin = -128; + const byte imax = 127; + const octet umin = 0; + const octet umax = 255; + }; + interface Short { + const short imin = -32768; + const short imax = 32767; + const unsigned short umin = 0; + const unsigned short umax = 65535; + }; + interface Long { + const long imin = -2147483648; + const long imax = 2147483647; + const unsigned long umin = 0; + const unsigned long umax = 4294967295; + }; + interface LongLong { + const long long imin = -9223372036854775808; + const long long imax = 9223372036854775807; + const unsigned long long umin = 0; + // bug in webidl + // https://github.com/sgodwincs/webidl-rs/issues/15 + //const unsigned long long umax = 18446744073709551615; + }; + "#, + ) + // a corresponding const in the js implementation is not required + // value is taken directly from idl + .file( + "foo.js", + r#" + export class Byte { + } + export class Short { + } + export class Long { + } + export class LongLong { + } + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + + pub mod foo; + + #[wasm_bindgen] + pub fn test() { + assert_eq!(foo::Byte::IMIN, i8::min_value()); + assert_eq!(foo::Byte::IMAX, i8::max_value()); + assert_eq!(foo::Byte::UMIN, u8::min_value()); + assert_eq!(foo::Byte::UMAX, u8::max_value()); + + assert_eq!(foo::Short::IMIN, i16::min_value()); + assert_eq!(foo::Short::IMAX, i16::max_value()); + assert_eq!(foo::Short::UMIN, u16::min_value()); + assert_eq!(foo::Short::UMAX, u16::max_value()); + + assert_eq!(foo::Long::IMIN, i32::min_value()); + assert_eq!(foo::Long::IMAX, i32::max_value()); + assert_eq!(foo::Long::UMIN, u32::min_value()); + assert_eq!(foo::Long::UMAX, u32::max_value()); + + assert_eq!(foo::LongLong::IMIN, i64::min_value()); + assert_eq!(foo::LongLong::IMAX, i64::max_value()); + assert_eq!(foo::LongLong::UMIN, u64::min_value()); + //assert_eq!(foo::LongLong::UMAX, u64::max_value()); + } + "#, + ) + .test(); +} + +#[test] +fn floats() { + project() + .file( + "foo.webidl", + r#" + interface floats { + const float f = 0.0; + const unrestricted float neg_inf = -Infinity; + const unrestricted float inf = Infinity; + const unrestricted float nan = NaN; + }; + interface doubles { + const double d = 0.0; + const unrestricted double neg_inf = -Infinity; + const unrestricted double inf = Infinity; + const unrestricted double nan = NaN; + }; + "#, + ) + // a corresponding const in the js implementation is not required + // value is taken directly from idl + .file( + "foo.js", + r#" + export class floats { + } + export class doubles { + } + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + + pub mod foo; + + #[wasm_bindgen] + pub fn test() { + assert_eq!(foo::Floats::F, 0.0_f32); + assert!(foo::Floats::NEG_INF.is_infinite()); + assert!(foo::Floats::NEG_INF.is_sign_negative()); + assert!(foo::Floats::INF.is_infinite()); + assert!(foo::Floats::INF.is_sign_positive()); + assert!(foo::Floats::NAN.is_nan()); + + assert_eq!(foo::Doubles::D, 0.0_f64); + assert!(foo::Doubles::NEG_INF.is_infinite()); + assert!(foo::Doubles::NEG_INF.is_sign_negative()); + assert!(foo::Doubles::INF.is_infinite()); + assert!(foo::Doubles::INF.is_sign_positive()); + assert!(foo::Doubles::NAN.is_nan()); + } + "#, + ) + .test(); +} diff --git a/crates/webidl/tests/all/main.rs b/crates/webidl/tests/all/main.rs index 13a6f789..3815c632 100644 --- a/crates/webidl/tests/all/main.rs +++ b/crates/webidl/tests/all/main.rs @@ -1,6 +1,7 @@ extern crate wasm_bindgen_test_project_builder as project_builder; use project_builder::project; -mod simple; +mod consts; mod enums; +mod simple; mod throws; diff --git a/guide/src/contributing.md b/guide/src/contributing.md index dbf4f6f5..62d2f222 100644 --- a/guide/src/contributing.md +++ b/guide/src/contributing.md @@ -14,7 +14,7 @@ development. [install Rust]: https://www.rust-lang.org/en-US/install.html -2. The tests for this project use Node. Make sure you have node >= 8 installed, +2. The tests for this project use Node. Make sure you have node >= 10 installed, as that is when WebAssembly support was introduced. [Install Node]. [Install Node]: https://nodejs.org/en/ diff --git a/package.json b/package.json index 274915ec..51645974 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,13 @@ }, "devDependencies": { "@types/node": "^10.5.2", - "babel-eslint": "^8.2.5", + "babel-eslint": "^8.2.6", "eslint": "^5.1.0", "geckodriver": "^1.11.0", "selenium-webdriver": "^4.0.0-alpha.1", "ts-loader": "^4.4.2", "typescript": "^2.7.2", - "webpack": "^4.15.1", + "webpack": "^4.16.0", "webpack-cli": "^3.0.8", "webpack-dev-server": "^3.1.4" } diff --git a/src/convert.rs b/src/convert.rs index c73a553f..65e652cd 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -77,7 +77,6 @@ pub trait RefMutFromWasmAbi: WasmDescribe { pub trait Stack { fn push(&mut self, bits: u32); - fn pop(&mut self) -> u32; } /// An unsafe trait which represents types that are ABI-safe to pass via wasm @@ -105,11 +104,15 @@ macro_rules! simple { ($($t:tt)*) => ($( impl IntoWasmAbi for $t { type Abi = $t; + + #[inline] fn into_abi(self, _extra: &mut Stack) -> $t { self } } impl FromWasmAbi for $t { type Abi = $t; + + #[inline] unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js } } )*) @@ -121,6 +124,8 @@ macro_rules! sixtyfour { ($($t:tt)*) => ($( impl IntoWasmAbi for $t { type Abi = WasmSlice; + + #[inline] fn into_abi(self, _extra: &mut Stack) -> WasmSlice { WasmSlice { ptr: self as u32, @@ -131,6 +136,8 @@ macro_rules! sixtyfour { impl FromWasmAbi for $t { type Abi = WasmSlice; + + #[inline] unsafe fn from_abi(js: WasmSlice, _extra: &mut Stack) -> $t { (js.ptr as $t) | ((js.len as $t) << 32) } @@ -144,11 +151,15 @@ macro_rules! as_u32 { ($($t:tt)*) => ($( impl IntoWasmAbi for $t { type Abi = u32; + + #[inline] fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } } impl FromWasmAbi for $t { type Abi = u32; + + #[inline] unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t } } )*) @@ -159,6 +170,7 @@ as_u32!(i8 u8 i16 u16 isize usize); impl IntoWasmAbi for bool { type Abi = u32; + #[inline] fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } @@ -167,6 +179,7 @@ impl IntoWasmAbi for bool { impl FromWasmAbi for bool { type Abi = u32; + #[inline] unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool { js != 0 } @@ -174,6 +187,8 @@ impl FromWasmAbi for bool { impl IntoWasmAbi for char { type Abi = u32; + + #[inline] fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } @@ -181,6 +196,8 @@ impl IntoWasmAbi for char { impl FromWasmAbi for char { type Abi = u32; + + #[inline] unsafe fn from_abi(js: u32, _extra: &mut Stack) -> char { char::from_u32_unchecked(js) } @@ -224,6 +241,7 @@ macro_rules! vectors { impl IntoWasmAbi for Box<[$t]> { type Abi = WasmSlice; + #[inline] fn into_abi(self, extra: &mut Stack) -> WasmSlice { let ptr = self.as_ptr(); let len = self.len(); @@ -239,6 +257,7 @@ macro_rules! vectors { impl FromWasmAbi for Box<[$t]> { type Abi = WasmSlice; + #[inline] unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self { let ptr = <*mut $t>::from_abi(js.ptr, extra); let len = js.len as usize; @@ -249,6 +268,7 @@ macro_rules! vectors { impl<'a> IntoWasmAbi for &'a [$t] { type Abi = WasmSlice; + #[inline] fn into_abi(self, extra: &mut Stack) -> WasmSlice { WasmSlice { ptr: self.as_ptr().into_abi(extra), @@ -260,6 +280,7 @@ macro_rules! vectors { impl<'a> IntoWasmAbi for &'a mut [$t] { type Abi = WasmSlice; + #[inline] fn into_abi(self, extra: &mut Stack) -> WasmSlice { (&*self).into_abi(extra) } @@ -269,6 +290,7 @@ macro_rules! vectors { type Abi = WasmSlice; type Anchor = &'static [$t]; + #[inline] unsafe fn ref_from_abi(js: WasmSlice, extra: &mut Stack) -> &'static [$t] { slice::from_raw_parts( <*const $t>::from_abi(js.ptr, extra), @@ -281,6 +303,7 @@ macro_rules! vectors { type Abi = WasmSlice; type Anchor = &'static mut [$t]; + #[inline] unsafe fn ref_mut_from_abi(js: WasmSlice, extra: &mut Stack) -> &'static mut [$t] { @@ -300,6 +323,7 @@ vectors! { if_std! { impl IntoWasmAbi for Vec where Box<[T]>: IntoWasmAbi { type Abi = as IntoWasmAbi>::Abi; + fn into_abi(self, extra: &mut Stack) -> Self::Abi { self.into_boxed_slice().into_abi(extra) } @@ -316,6 +340,7 @@ if_std! { impl IntoWasmAbi for String { type Abi = as IntoWasmAbi>::Abi; + #[inline] fn into_abi(self, extra: &mut Stack) -> Self::Abi { self.into_bytes().into_abi(extra) } @@ -324,6 +349,7 @@ if_std! { impl FromWasmAbi for String { type Abi = as FromWasmAbi>::Abi; + #[inline] unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self { String::from_utf8_unchecked(>::from_abi(js, extra)) } @@ -333,6 +359,7 @@ if_std! { impl<'a> IntoWasmAbi for &'a str { type Abi = <&'a [u8] as IntoWasmAbi>::Abi; + #[inline] fn into_abi(self, extra: &mut Stack) -> Self::Abi { self.as_bytes().into_abi(extra) } @@ -342,6 +369,7 @@ impl RefFromWasmAbi for str { type Abi = <[u8] as RefFromWasmAbi>::Abi; type Anchor = &'static str; + #[inline] unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor { str::from_utf8_unchecked(<[u8]>::ref_from_abi(js, extra)) } @@ -350,6 +378,7 @@ impl RefFromWasmAbi for str { impl IntoWasmAbi for JsValue { type Abi = u32; + #[inline] fn into_abi(self, _extra: &mut Stack) -> u32 { let ret = self.idx; mem::forget(self); @@ -360,6 +389,7 @@ impl IntoWasmAbi for JsValue { impl FromWasmAbi for JsValue { type Abi = u32; + #[inline] unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue { JsValue { idx: js } } @@ -367,6 +397,8 @@ impl FromWasmAbi for JsValue { impl<'a> IntoWasmAbi for &'a JsValue { type Abi = u32; + + #[inline] fn into_abi(self, _extra: &mut Stack) -> u32 { self.idx } @@ -376,6 +408,7 @@ impl RefFromWasmAbi for JsValue { type Abi = u32; type Anchor = ManuallyDrop; + #[inline] unsafe fn ref_from_abi(js: u32, _extra: &mut Stack) -> Self::Anchor { ManuallyDrop::new(JsValue { idx: js }) } @@ -385,6 +418,7 @@ if_std! { impl IntoWasmAbi for Box<[JsValue]> { type Abi = WasmSlice; + #[inline] fn into_abi(self, extra: &mut Stack) -> WasmSlice { let ptr = self.as_ptr(); let len = self.len(); @@ -399,6 +433,7 @@ if_std! { impl FromWasmAbi for Box<[JsValue]> { type Abi = WasmSlice; + #[inline] unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self { let ptr = <*mut JsValue>::from_abi(js.ptr, extra); let len = js.len as usize; @@ -415,12 +450,14 @@ const GLOBAL_STACK_CAP: usize = 16; static mut GLOBAL_STACK: [u32; GLOBAL_STACK_CAP] = [0; GLOBAL_STACK_CAP]; impl GlobalStack { + #[inline] pub unsafe fn new() -> GlobalStack { GlobalStack { next: 0 } } } impl Stack for GlobalStack { + #[inline] fn push(&mut self, val: u32) { unsafe { assert!(self.next < GLOBAL_STACK_CAP); @@ -428,15 +465,6 @@ impl Stack for GlobalStack { self.next += 1; } } - - fn pop(&mut self) -> u32 { - unsafe { - assert!(self.next < GLOBAL_STACK_CAP); - let ret = GLOBAL_STACK[self.next]; - self.next += 1; - ret - } - } } #[doc(hidden)] diff --git a/src/js.rs b/src/js.rs index 208ffe87..33524bd2 100644 --- a/src/js.rs +++ b/src/js.rs @@ -240,6 +240,20 @@ extern "C" { #[wasm_bindgen(method)] pub fn push(this: &Array, value: JsValue) -> u32; + /// The reduce() method applies a function against an accumulator and each element in + /// the array (from left to right) to reduce it to a single value. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce + #[wasm_bindgen(method)] + pub fn reduce(this: &Array, predicate: &mut FnMut(JsValue, JsValue, u32, Array) -> JsValue, initial_value: JsValue) -> JsValue; + + /// The reduceRight() method applies a function against an accumulator and each value + /// of the array (from right-to-left) to reduce it to a single value. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight + #[wasm_bindgen(method, js_name = reduceRight)] + pub fn reduce_right(this: &Array, predicate: &mut FnMut(JsValue, JsValue, u32, Array) -> JsValue, initial_value: JsValue) -> JsValue; + /// The reverse() method reverses an array in place. The first array /// element becomes the last, and the last array element becomes the first. /// @@ -378,6 +392,152 @@ extern "C" { pub fn value_of(this: &Boolean) -> bool; } +// DataView +#[wasm_bindgen] +extern "C" { + pub type DataView; + + /// The `DataView` view provides a low-level interface for reading and + /// writing multiple number types in an `ArrayBuffer` irrespective of the + /// platform's endianness. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView + #[wasm_bindgen(constructor)] + pub fn new(buffer: &ArrayBuffer, byteOffset: usize, byteLength: usize) -> DataView; + + /// The ArrayBuffer referenced by this view. Fixed at construction time and thus read only. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/buffer + #[wasm_bindgen(method, getter, structural)] + pub fn buffer(this: &DataView) -> ArrayBuffer; + + /// The length (in bytes) of this view from the start of its ArrayBuffer. + /// Fixed at construction time and thus read only. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteLength + #[wasm_bindgen(method, getter, structural, js_name = byteLength)] + pub fn byte_length(this: &DataView) -> usize; + + /// The offset (in bytes) of this view from the start of its ArrayBuffer. + /// Fixed at construction time and thus read only. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteOffset + #[wasm_bindgen(method, getter, structural, js_name = byteOffset)] + pub fn byte_offset(this: &DataView) -> usize; + + /// The getInt8() method gets a signed 8-bit integer (byte) at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8 + #[wasm_bindgen(method, js_name = getInt8)] + pub fn get_int8(this: &DataView, byte_offset: usize) -> i8; + + /// The getUint8() method gets a unsigned 8-bit integer (byte) at the specified + /// byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8 + #[wasm_bindgen(method, js_name = getUint8)] + pub fn get_uint8(this: &DataView, byte_offset: usize) -> u8; + + /// The getInt16() method gets a signed 16-bit integer (byte) at the specified + /// byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16 + #[wasm_bindgen(method, js_name = getInt16)] + pub fn get_int16(this: &DataView, byte_offset: usize) -> i16; + + /// The getUint16() an unsigned 16-bit integer (unsigned byte) at the specified + /// byte offset from the start of the view. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16 + #[wasm_bindgen(method, js_name = getUint16)] + pub fn get_uint16(this: &DataView, byte_offset: usize) -> u16; + + /// The getInt32() method gets a signed 16-bit integer (byte) at the specified + /// byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32 + #[wasm_bindgen(method, js_name = getInt32)] + pub fn get_int32(this: &DataView, byte_offset: usize) -> i32; + + /// The getUint32() an unsigned 16-bit integer (unsigned byte) at the specified + /// byte offset from the start of the view. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32 + #[wasm_bindgen(method, js_name = getUint32)] + pub fn get_uint32(this: &DataView, byte_offset: usize) -> u32; + + /// The getFloat32() method gets a signed 32-bit float (float) at the specified + /// byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32 + #[wasm_bindgen(method, js_name = getFloat32)] + pub fn get_float32(this: &DataView, byte_offset: usize) -> f32; + + /// The getFloat64() method gets a signed 32-bit float (float) at the specified + /// byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64 + #[wasm_bindgen(method, js_name = getFloat64)] + pub fn get_float64(this: &DataView, byte_offset: usize) -> f64; + + /// The setInt8() method stores a signed 8-bit integer (byte) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8 + #[wasm_bindgen(method, js_name = setInt8)] + pub fn set_int8(this: &DataView, byte_offset: usize, value: i8); + + /// The setUint8() method stores an unsigned 8-bit integer (byte) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8 + #[wasm_bindgen(method, js_name = setUint8)] + pub fn set_uint8(this: &DataView, byte_offset: usize, value: u8); + + /// The setInt16() method stores a signed 16-bit integer (byte) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16 + #[wasm_bindgen(method, js_name = setInt16)] + pub fn set_int16(this: &DataView, byte_offset: usize, value: i16); + + /// The setUint16() method stores an unsigned 16-bit integer (byte) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16 + #[wasm_bindgen(method, js_name = setUint16)] + pub fn set_uint16(this: &DataView, byte_offset: usize, value: u16); + + /// The setInt32() method stores a signed 32-bit integer (byte) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32 + #[wasm_bindgen(method, js_name = setInt32)] + pub fn set_int32(this: &DataView, byte_offset: usize, value: i32); + + /// The setUint32() method stores an unsigned 32-bit integer (byte) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32 + #[wasm_bindgen(method, js_name = setUint32)] + pub fn set_uint32(this: &DataView, byte_offset: usize, value: u32); + + /// The setFloat32() method stores a signed 32-bit float (float) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32 + #[wasm_bindgen(method, js_name = setFloat32)] + pub fn set_float32(this: &DataView, byte_offset: usize, value: f32); + + /// The setFloat64() method stores a signed 64-bit float (float) value at the + /// specified byte offset from the start of the DataView. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64 + #[wasm_bindgen(method, js_name = setFloat64)] + pub fn set_float64(this: &DataView, byte_offset: usize, value: f64); +} + // Error #[wasm_bindgen] extern "C" { diff --git a/src/lib.rs b/src/lib.rs index a43a8d80..ee2d19c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! this crate and this crate also provides JS bindings through the `JsValue` //! interface. -#![feature(use_extern_macros, wasm_import_module, try_reserve, unsize)] +#![feature(use_extern_macros, wasm_import_module, unsize)] #![cfg_attr(feature = "js_globals", feature(proc_macro, wasm_custom_section))] #![no_std] @@ -661,24 +661,29 @@ pub mod __rt { } if_std! { - use std::prelude::v1::*; + use std::alloc::{System, GlobalAlloc, Layout}; + use std::mem; #[no_mangle] pub extern fn __wbindgen_malloc(size: usize) -> *mut u8 { - use core::mem; - - let mut ret = Vec::new(); - if ret.try_reserve_exact(size).is_err() { - super::throw("invalid malloc request"); + let align = mem::align_of::(); + if let Ok(layout) = Layout::from_size_align(size, align) { + unsafe { + let ptr = System.alloc(layout); + if !ptr.is_null() { + return ptr + } + } } - let ptr = ret.as_mut_ptr(); - mem::forget(ret); - return ptr + + super::throw("invalid malloc request"); } #[no_mangle] pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) { - drop(Vec::::from_raw_parts(ptr, 0, size)); + let align = mem::align_of::(); + let layout = Layout::from_size_align_unchecked(size, align); + System.dealloc(ptr, layout); } } diff --git a/tests/all/js_globals/Array.rs b/tests/all/js_globals/Array.rs index a8ab5477..c9a0d55b 100644 --- a/tests/all/js_globals/Array.rs +++ b/tests/all/js_globals/Array.rs @@ -830,3 +830,69 @@ fn map() { ) .test() } + +#[test] +fn reduce() { + project() + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js; + use JsValue; + + #[wasm_bindgen] + pub fn array_reduce(array: &js::Array) -> JsValue { + array.reduce(&mut |ac, cr, _, _| JsValue::from_str(&format!("{}{}", &ac.as_string().unwrap(), &cr.as_string().unwrap().as_str())), JsValue::from_str("")) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + assert.equal(wasm.array_reduce(['0', '1', '2', '3', '4']), '01234'); + } + "#, + ) + .test() +} + +#[test] +fn reduce_right() { + project() + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js; + use JsValue; + + #[wasm_bindgen] + pub fn array_reduce_right(array: &js::Array) -> JsValue { + array.reduce_right(&mut |ac, cr, _, _| JsValue::from_str(&format!("{}{}", &ac.as_string().unwrap(), &cr.as_string().unwrap().as_str())), JsValue::from_str("")) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + assert.equal(wasm.array_reduce_right(['0', '1', '2', '3', '4']), '43210'); + } + "#, + ) + .test() +} \ No newline at end of file diff --git a/tests/all/js_globals/DataView.rs b/tests/all/js_globals/DataView.rs new file mode 100644 index 00000000..ced10a22 --- /dev/null +++ b/tests/all/js_globals/DataView.rs @@ -0,0 +1,55 @@ +#![allow(non_snake_case)] + +use super::project; + +#[test] +fn test() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js::{ArrayBuffer, DataView}; + + #[wasm_bindgen] + pub fn test_data_view(buffer: &ArrayBuffer, offset: usize, len: usize) { + let v = DataView::new(buffer, offset, len); + assert_eq!(v.byte_offset(), offset); + assert_eq!(v.byte_length(), len); + assert_eq!(v.get_int8(0), 2); + assert_eq!(v.get_uint8(0), 2); + + v.set_int8(0, 42); + assert_eq!(v.get_int8(0), 42); + v.set_uint8(0, 255); + assert_eq!(v.get_uint8(0), 255); + v.set_int16(0, 32767); + assert_eq!(v.get_int16(0), 32767); + v.set_uint16(0, 65535); + assert_eq!(v.get_uint16(0), 65535); + v.set_int32(0, 123456789); + assert_eq!(v.get_int32(0), 123456789); + v.set_uint32(0, 3_123_456_789); + assert_eq!(v.get_uint32(0), 3_123_456_789); + v.set_float32(0, 100.123); + assert_eq!(v.get_float32(0), 100.123); + v.set_float64(0, 123456789.123456); + assert_eq!(v.get_float64(0), 123456789.123456); + + v.set_int8(0, 42); + } + "#) + .file("test.js", r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const bytes = new Int8Array(10); + bytes[2] = 2; + wasm.test_data_view(bytes.buffer, 2, 8); + assert.equal(bytes[2], 42); + } + "#) + .test() +} diff --git a/tests/all/js_globals/mod.rs b/tests/all/js_globals/mod.rs index 2af15330..211df89e 100644 --- a/tests/all/js_globals/mod.rs +++ b/tests/all/js_globals/mod.rs @@ -6,6 +6,7 @@ mod Array; mod ArrayBuffer; mod ArrayIterator; mod Boolean; +mod DataView; mod Date; mod Error; mod Function; diff --git a/yarn.lock b/yarn.lock index 8702f195..2de4b8b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -408,15 +408,15 @@ babel-code-frame@^6.26.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-eslint@^8.2.5: - version "8.2.5" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.5.tgz#dc2331c259d36782aa189da510c43dedd5adc7a3" +babel-eslint@^8.2.6: + version "8.2.6" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.6.tgz#6270d0c73205628067c0f7ae1693a9e797acefd9" dependencies: "@babel/code-frame" "7.0.0-beta.44" "@babel/traverse" "7.0.0-beta.44" "@babel/types" "7.0.0-beta.44" babylon "7.0.0-beta.44" - eslint-scope "~3.7.1" + eslint-scope "3.7.1" eslint-visitor-keys "^1.0.0" babylon@7.0.0-beta.44: @@ -1239,7 +1239,7 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -eslint-scope@^3.7.1, eslint-scope@~3.7.1: +eslint-scope@3.7.1, eslint-scope@^3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" dependencies: @@ -4150,9 +4150,9 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.15.1: - version "4.15.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.15.1.tgz#dc716779a3b88827c369f18c71a6137fa7e582fd" +webpack@^4.16.0: + version "4.16.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.0.tgz#660dae90890e55b8ed17c6f9d17bebb01dab5b4c" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-module-context" "1.5.13"