diff -Nru rust-stdweb-internal-macros-0.2.2/build.rs rust-stdweb-internal-macros-0.2.5/build.rs --- rust-stdweb-internal-macros-0.2.2/build.rs 2018-09-15 00:11:25.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/build.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -extern crate rustc_version; -use rustc_version::{version, Version}; - -fn main() { - let mut current = version().unwrap(); - current.pre = Vec::new(); - - if current >= Version::parse( "1.30.0" ).unwrap() { - println!( "cargo:rustc-cfg=rust_1_30_or_newer" ); - } -} diff -Nru rust-stdweb-internal-macros-0.2.2/Cargo.toml rust-stdweb-internal-macros-0.2.5/Cargo.toml --- rust-stdweb-internal-macros-0.2.2/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 @@ -12,9 +12,8 @@ [package] name = "stdweb-internal-macros" -version = "0.2.2" +version = "0.2.5" authors = ["Jan Bujak "] -build = "build.rs" description = "Internal procedural macros for the `stdweb` crate" homepage = "https://github.com/koute/stdweb" documentation = "https://docs.rs/stdweb/*/stdweb/" @@ -44,9 +43,10 @@ [dependencies.serde_json] version = "1" +[dependencies.sha1] +version = "0.6" + [dependencies.syn] version = "0.15" features = ["full", "parsing", "printing", "clone-impls"] default-features = false -[build-dependencies.rustc_version] -version = "0.2" diff -Nru rust-stdweb-internal-macros-0.2.2/Cargo.toml.orig rust-stdweb-internal-macros-0.2.5/Cargo.toml.orig --- rust-stdweb-internal-macros-0.2.2/Cargo.toml.orig 2018-10-18 20:09:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/Cargo.toml.orig 2019-01-08 19:22:17.000000000 +0000 @@ -1,6 +1,6 @@ [package] name = "stdweb-internal-macros" -version = "0.2.2" +version = "0.2.5" authors = ["Jan Bujak "] repository = "https://github.com/koute/stdweb" homepage = "https://github.com/koute/stdweb" @@ -11,8 +11,6 @@ categories = ["api-bindings", "gui", "web-programming"] description = "Internal procedural macros for the `stdweb` crate" -build = "build.rs" - [lib] proc-macro = true @@ -23,11 +21,10 @@ serde_derive = "1" serde_json = "1" proc-macro2 = "0.4" +sha1 = "0.6" [dependencies.syn] version = "0.15" default-features = false features = ["full", "parsing", "printing", "clone-impls"] -[build-dependencies] -rustc_version = "0.2" diff -Nru rust-stdweb-internal-macros-0.2.2/.cargo_vcs_info.json rust-stdweb-internal-macros-0.2.5/.cargo_vcs_info.json --- rust-stdweb-internal-macros-0.2.2/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +1,5 @@ { "git": { - "sha1": "21edc8061e453deffd6cac6f0ec300dfd06b58a4" + "sha1": "5c4f1c5165d484545361e785539a28adffaf5dfb" } } diff -Nru rust-stdweb-internal-macros-0.2.2/debian/cargo-checksum.json rust-stdweb-internal-macros-0.2.5/debian/cargo-checksum.json --- rust-stdweb-internal-macros-0.2.2/debian/cargo-checksum.json 2018-11-16 11:54:08.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/debian/cargo-checksum.json 2019-01-22 08:58:56.000000000 +0000 @@ -1 +1 @@ -{"package":"bcbc9155af9606d44c740197d7d6672b49c4ee93a176c7cecde8b49322677604","files":{}} +{"package":"432465093692af7379dcd196ce4be398c906958d91b412fff9102a66238d6f26","files":{}} diff -Nru rust-stdweb-internal-macros-0.2.2/debian/changelog rust-stdweb-internal-macros-0.2.5/debian/changelog --- rust-stdweb-internal-macros-0.2.2/debian/changelog 2018-11-16 11:54:08.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/debian/changelog 2019-01-22 08:58:56.000000000 +0000 @@ -1,3 +1,9 @@ +rust-stdweb-internal-macros (0.2.5-1) unstable; urgency=medium + + * Package stdweb-internal-macros 0.2.5 from crates.io using debcargo 2.2.9 + + -- Wolfgang Silbermayr Tue, 22 Jan 2019 09:58:56 +0100 + rust-stdweb-internal-macros (0.2.2-1) unstable; urgency=medium * Team upload. diff -Nru rust-stdweb-internal-macros-0.2.2/debian/control rust-stdweb-internal-macros-0.2.5/debian/control --- rust-stdweb-internal-macros-0.2.2/debian/control 2018-11-16 11:54:08.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/debian/control 2019-01-22 08:58:56.000000000 +0000 @@ -9,17 +9,18 @@ librust-base-x-0.2+default-dev , librust-proc-macro2-0.4+default-dev , librust-quote-0.6+default-dev , - librust-rustc-version-0.2+default-dev , librust-serde-1+default-dev , librust-serde-derive-1+default-dev , librust-serde-json-1+default-dev , + librust-sha1-0.6+default-dev , librust-syn-0.15+clone-impls-dev , librust-syn-0.15+full-dev , librust-syn-0.15+parsing-dev , librust-syn-0.15+printing-dev Maintainer: Debian Rust Maintainers Uploaders: - kpcyrd + kpcyrd , + Wolfgang Silbermayr Standards-Version: 4.2.0 Vcs-Git: https://salsa.debian.org/rust-team/debcargo-conf.git [src/stdweb-internal-macros] Vcs-Browser: https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/stdweb-internal-macros @@ -33,10 +34,10 @@ librust-base-x-0.2+default-dev, librust-proc-macro2-0.4+default-dev, librust-quote-0.6+default-dev, - librust-rustc-version-0.2+default-dev, librust-serde-1+default-dev, librust-serde-derive-1+default-dev, librust-serde-json-1+default-dev, + librust-sha1-0.6+default-dev, librust-syn-0.15+clone-impls-dev, librust-syn-0.15+full-dev, librust-syn-0.15+parsing-dev, @@ -47,8 +48,8 @@ librust-stdweb-internal-macros-0+default-dev (= ${binary:Version}), librust-stdweb-internal-macros-0.2-dev (= ${binary:Version}), librust-stdweb-internal-macros-0.2+default-dev (= ${binary:Version}), - librust-stdweb-internal-macros-0.2.2-dev (= ${binary:Version}), - librust-stdweb-internal-macros-0.2.2+default-dev (= ${binary:Version}) + librust-stdweb-internal-macros-0.2.5-dev (= ${binary:Version}), + librust-stdweb-internal-macros-0.2.5+default-dev (= ${binary:Version}) Description: Internal procedural macros for the `stdweb` crate - Rust source code This package contains the source for the Rust stdweb-internal-macros crate, packaged by debcargo for use with cargo and dh-cargo. diff -Nru rust-stdweb-internal-macros-0.2.2/debian/copyright rust-stdweb-internal-macros-0.2.5/debian/copyright --- rust-stdweb-internal-macros-0.2.2/debian/copyright 2018-11-16 11:54:08.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/debian/copyright 2019-01-22 08:58:56.000000000 +0000 @@ -4,13 +4,14 @@ Source: https://github.com/koute/stdweb Files: * -Copyright: 2018 Jan Bujak +Copyright: 2018-2019 Jan Bujak License: MIT or Apache-2.0 Files: debian/* Copyright: - 2018 Debian Rust Maintainers + 2018-2019 Debian Rust Maintainers 2018 kpcyrd + 2019 Wolfgang Silbermayr License: MIT or Apache-2.0 License: Apache-2.0 diff -Nru rust-stdweb-internal-macros-0.2.2/debian/copyright.debcargo.hint rust-stdweb-internal-macros-0.2.5/debian/copyright.debcargo.hint --- rust-stdweb-internal-macros-0.2.2/debian/copyright.debcargo.hint 2018-11-16 11:54:08.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/debian/copyright.debcargo.hint 2019-01-22 08:58:56.000000000 +0000 @@ -14,8 +14,9 @@ Files: debian/* Copyright: - 2018 Debian Rust Maintainers - 2018 kpcyrd + 2018-2019 Debian Rust Maintainers + 2018-2019 kpcyrd + 2018-2019 Wolfgang Silbermayr License: MIT or Apache-2.0 License: Apache-2.0 diff -Nru rust-stdweb-internal-macros-0.2.2/debian/debcargo.toml rust-stdweb-internal-macros-0.2.5/debian/debcargo.toml --- rust-stdweb-internal-macros-0.2.2/debian/debcargo.toml 2018-11-16 11:54:08.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/debian/debcargo.toml 2019-01-22 08:58:56.000000000 +0000 @@ -1,2 +1,5 @@ overlay = "." -uploaders = ["kpcyrd "] +uploaders = [ + "kpcyrd ", + "Wolfgang Silbermayr ", +] diff -Nru rust-stdweb-internal-macros-0.2.2/src/attr_hack.rs rust-stdweb-internal-macros-0.2.5/src/attr_hack.rs --- rust-stdweb-internal-macros-0.2.2/src/attr_hack.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/attr_hack.rs 2018-12-08 22:25:17.000000000 +0000 @@ -0,0 +1,41 @@ +use syn::parse::{ParseStream, Parse, Result}; + +// Since currently producing an expression from a procedural +// macro is not stable we use something like this to work around +// this problem. +pub struct AttrHack< T: Parse > { + pub fn_name: syn::Ident, + pub inner: T +} + +impl< T > Parse for AttrHack< T > where T: Parse { + fn parse( input: ParseStream ) -> Result< Self > { + input.parse::< Token![fn] >()?; + let fn_name = input.parse::< syn::Ident >()?; + + #[allow(unused_variables)] + let fn_args_input; + parenthesized!( fn_args_input in input ); + + let fn_body_input; + braced!( fn_body_input in input ); + + let ident = fn_body_input.parse::< syn::Ident >()?; + if ident == "call" { + fn_body_input.parse::< Token![!] >()?; + + let inner; + parenthesized!( inner in fn_body_input ); + let inner = inner.parse::< T >()?; + fn_body_input.parse::< Token![;] >()?; + + Ok( AttrHack { + fn_name, + inner + }) + } else { + let ident_str = ident.to_string(); + Err( syn::Error::new_spanned( ident, format!( "unexpected ident '{}'", ident_str ) ) ) + } + } +} diff -Nru rust-stdweb-internal-macros-0.2.2/src/js_shim.rs rust-stdweb-internal-macros-0.2.5/src/js_shim.rs --- rust-stdweb-internal-macros-0.2.2/src/js_shim.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/js_shim.rs 2018-12-15 12:23:57.000000000 +0000 @@ -0,0 +1,91 @@ +use std::env; +use std::path::PathBuf; +use std::fs; + +use syn; +use proc_macro2::{TokenStream, Span}; +use sha1::Sha1; + +use utils::{Target, dummy_idents}; + +#[derive(Clone, Serialize, Deserialize, Debug)] +struct Snippet { + name: String, + code: String, + arg_count: usize +} + +fn hash( string: &str ) -> String { + let mut hasher = Sha1::new(); + hasher.update( string.as_bytes() ); + format!( "{}", hasher.digest() ) +} + +fn database_path() -> PathBuf { + let target_path = env::var_os( "CARGO_WEB_TARGET_DIR" ) + .map( PathBuf::from ) + .expect( "you need to use `cargo-web` to compile your project for the `wasm32-unknown-unknown` target" ); + assert!( target_path.exists() ); + + target_path.join( ".cargo-web" ).join( "snippets" ) +} + +fn output_snippet( snippet: &Snippet ) { + let hash = hash( &snippet.name ); + let directory = database_path().join( &hash[ 0..2 ] ); + + fs::create_dir_all( &directory ).expect( "failed to create a directory for the JS snippet database" ); + let path = directory.join( format!( "{}.json", hash ) ); + + let blob: Vec< u8 > = serde_json::to_string( &snippet ).expect( "failed to convert the JS snipped to JSON" ).into_bytes(); + if path.exists() { + if let Ok( size ) = path.metadata().map( |metadata| metadata.len() ) { + if size == blob.len() as u64 { + return; + } + } + } + + fs::write( path, blob ).expect( "failed to write a JS snippet" ); +} + +pub fn js_shim_extern_code( target: Target, code: &str, arg_count: usize ) -> (syn::Ident, TokenStream) { + let snippet = Snippet { + name: format!( "__cargo_web_snippet_{}", hash( code ) ), + code: code.to_owned(), + arg_count + }; + + let shim_name = syn::Ident::new( &snippet.name, Span::call_site() ); + let shim_args: Vec< _ > = dummy_idents( arg_count ).map( |name| quote! { #name: *const u8 } ).collect(); + let shim_args_passthrough: Vec< _ > = dummy_idents( arg_count ).map( |name| quote! { #name } ).collect(); + let output = match target { + Target::Emscripten => { + let code_bytes = syn::LitByteStr::new( format!( "{}\0", code ).as_str().as_bytes(), Span::call_site() ); + + quote! { + const SNIPPET: &'static [u8] = #code_bytes; + + fn #shim_name( #(#shim_args),* ) -> i32 { + extern "C" { + pub fn emscripten_asm_const_int( code: *const u8, ... ) -> i32; + } + + unsafe { + emscripten_asm_const_int( SNIPPET as *const _ as *const u8, #(#shim_args_passthrough),* ) + } + } + } + }, + Target::NativeWebAssembly => { + output_snippet( &snippet ); + quote! { + extern "C" { + pub fn #shim_name( #(#shim_args),* ) -> i32; + } + } + } + }; + + (shim_name, output) +} diff -Nru rust-stdweb-internal-macros-0.2.2/src/js_stringify.rs rust-stdweb-internal-macros-0.2.5/src/js_stringify.rs --- rust-stdweb-internal-macros-0.2.2/src/js_stringify.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/js_stringify.rs 2018-12-29 22:19:46.000000000 +0000 @@ -0,0 +1,164 @@ +use std::fmt::Write; +use proc_macro2::{TokenTree, Delimiter}; +use syn; +use syn::buffer::Cursor; +use syn::parse::{Result, ParseStream}; +use quote::ToTokens; + +enum Chunk { + Text( String ), + Block( syn::Block ) +} + +pub struct StringifiedCode { + chunks: Vec< Chunk > +} + +fn can_trim_whitespace_next_to( ch: char ) -> bool { + ch.is_whitespace() || "=+-*/%<>&^|~?:{}()[];.,".contains( ch ) +} + +impl StringifiedCode { + fn push( &mut self, string: &str ) { + match self.chunks.last_mut() { + None | Some( Chunk::Block( .. ) ) => {}, + Some( Chunk::Text( ref mut buffer ) ) => { + let l = buffer.chars().last().unwrap_or( ' ' ); + let r = string.chars().next().unwrap_or( ' ' ); + if !can_trim_whitespace_next_to( l ) && !can_trim_whitespace_next_to( r ) { + buffer.push( ' ' ); + } + buffer.push_str( string ); + return; + } + } + + self.chunks.push( Chunk::Text( string.into() ) ); + } + + fn push_block( &mut self, block: syn::Block ) { + self.chunks.push( Chunk::Block( block ) ); + } + + pub fn arg_count( &self ) -> usize { + self.chunks.iter().filter( |chunk| + match chunk { + Chunk::Block( .. ) => true, + _ => false + } + ).count() + } + + pub fn code( &self, initial_placeholder_index: usize ) -> String { + let capacity = self.chunks.iter().map( |chunk| + match chunk { + Chunk::Text( text ) => text.len(), + Chunk::Block( .. ) => 4 + } + ).fold( 0, |sum, len| sum + len ); + + let mut counter = initial_placeholder_index; + let mut output = String::with_capacity( capacity ); + for chunk in &self.chunks { + match chunk { + Chunk::Text( text ) => output.push_str( text ), + Chunk::Block( _ ) => { + write!( output, "(${})", counter ).unwrap(); + counter += 1; + } + } + } + + output + } +} + +fn stringify< 'a >( mut cursor: Cursor< 'a >, output: &mut StringifiedCode ) -> Result< Cursor< 'a > > { + while let Some( (tt, next) ) = cursor.token_tree() { + cursor = match tt { + TokenTree::Punct( ref punct ) if punct.as_char() == '@' && next.group( Delimiter::Brace ).is_some() => { + let (tt, next_next) = next.token_tree().unwrap(); + output.push_block( syn::parse2( tt.into_token_stream() )? ); + next_next + }, + TokenTree::Group( ref group ) => { + let (start, end) = match group.delimiter() { + Delimiter::Brace => ("{", "}"), + Delimiter::Bracket => ("[", "]"), + Delimiter::Parenthesis => ("(", ")"), + Delimiter::None => ("", "") + }; + + output.push( start ); + let inner = cursor.group( group.delimiter() ).unwrap().0; + stringify( inner, output )?; + output.push( end ); + next + }, + _ => { + let token = tt.to_string(); + output.push( &token ); + next + } + }; + } + + Ok( cursor ) +} + +impl syn::parse::Parse for StringifiedCode { + fn parse( input: ParseStream ) -> Result< Self > { + input.step( |cursor| { + let mut output = StringifiedCode { + chunks: Vec::new() + }; + let cursor = stringify( *cursor, &mut output )?; + Ok( (output, cursor) ) + }) + } +} + +#[cfg(test)] +mod tests { + use super::StringifiedCode; + use proc_macro2::TokenStream; + + fn assert_stringify( input: TokenStream, initial_placeholder: usize, expected: &str ) { + let snippet: StringifiedCode = syn::parse2( input ).unwrap(); + assert_eq!( snippet.code( initial_placeholder ), expected ); + } + + #[test] + fn test_stringify() { + assert_stringify( quote! { return thing; }, 0, "return thing;" ); + assert_stringify( quote! { console.log }, 0, "console.log" ); + assert_stringify( quote! { 1.0 }, 0, "1.0" ); + assert_stringify( quote! { [ 1.0 ] }, 0, "[1.0]" ); + assert_stringify( quote! { { 1.0 } }, 0, "{1.0}" ); + assert_stringify( quote! { ( 1.0 ) }, 0, "(1.0)" ); + assert_stringify( quote! { a b }, 0, "a b" ); + assert_stringify( quote! { === }, 0, "===" ); + assert_stringify( quote! { ++i }, 0, "++i" ); + assert_stringify( quote! { i++ }, 0, "i++" ); + assert_stringify( quote! { --i }, 0, "--i" ); + assert_stringify( quote! { i-- }, 0, "i--" ); + assert_stringify( quote! { return _.sum([1, 2]); }, 0, "return _.sum([1,2]);" ); + assert_stringify( quote! { return $; }, 0, "return $;" ); + assert_stringify( quote! { ( @{1} ); }, 0, "(($0));" ); + assert_stringify( + quote! { console.log( "Hello!", @{1234i32} ); }, + 0, + "console.log(\"Hello!\",($0));" + ); + assert_stringify( + quote! { @{a}.fn( @{b} ); }, + 0, + "($0).fn(($1));" + ); + assert_stringify( + quote! { @{a}.fn( @{b} ); }, + 1, + "($1).fn(($2));" + ); + } +} diff -Nru rust-stdweb-internal-macros-0.2.2/src/lib.rs rust-stdweb-internal-macros-0.2.5/src/lib.rs --- rust-stdweb-internal-macros-0.2.2/src/lib.rs 2018-09-15 22:56:31.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/lib.rs 2018-12-13 16:52:46.000000000 +0000 @@ -2,6 +2,7 @@ extern crate proc_macro; extern crate proc_macro2; +#[macro_use] extern crate syn; #[macro_use] extern crate quote; @@ -10,20 +11,77 @@ #[macro_use] extern crate serde_derive; extern crate serde_json; +extern crate sha1; + +#[cfg(test)] +mod testutils; + +mod utils; -#[cfg(rust_1_30_or_newer)] mod macro_js_export; -#[cfg(rust_1_30_or_newer)] mod macro_async_test; +mod macro_js_raw; +mod macro_js; + +mod attr_hack; +mod js_stringify; +mod js_shim; + +use utils::Target; + +fn emit( result: syn::parse::Result< proc_macro2::TokenStream > ) -> proc_macro::TokenStream { + match result { + Ok( stream ) => stream.into(), + Err( error ) => proc_macro::TokenStream::from( error.to_compile_error() ) + } +} -#[cfg(rust_1_30_or_newer)] #[proc_macro_attribute] pub fn js_export( attrs: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { macro_js_export::js_export( attrs, input ) } -#[cfg(rust_1_30_or_newer)] #[proc_macro_attribute] pub fn async_test( attrs: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { macro_async_test::async_test( attrs, input ) } + +#[proc_macro] +pub fn wasm32_unknown_unknown_js_raw( input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js_raw::js_raw( Target::NativeWebAssembly, input.into() ) ) +} + +#[proc_macro_attribute] +pub fn wasm32_unknown_unknown_js_raw_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js_raw::js_raw_attr( Target::NativeWebAssembly, input.into() ) ) +} + +#[proc_macro_attribute] +pub fn wasm32_unknown_unknown_js_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js::js_attr( Target::NativeWebAssembly, input.into(), false ) ) +} + +#[proc_macro_attribute] +pub fn wasm32_unknown_unknown_js_no_return_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js::js_attr( Target::NativeWebAssembly, input.into(), true ) ) +} + +#[proc_macro] +pub fn emscripten_js_raw( input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js_raw::js_raw( Target::Emscripten, input.into() ) ) +} + +#[proc_macro_attribute] +pub fn emscripten_js_raw_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js_raw::js_raw_attr( Target::Emscripten, input.into() ) ) +} + +#[proc_macro_attribute] +pub fn emscripten_js_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js::js_attr( Target::Emscripten, input.into(), false ) ) +} + +#[proc_macro_attribute] +pub fn emscripten_js_no_return_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream { + emit( macro_js::js_attr( Target::Emscripten, input.into(), true ) ) +} diff -Nru rust-stdweb-internal-macros-0.2.2/src/macro_async_test.rs rust-stdweb-internal-macros-0.2.5/src/macro_async_test.rs --- rust-stdweb-internal-macros-0.2.2/src/macro_async_test.rs 2018-09-16 00:06:56.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/macro_async_test.rs 2018-12-08 22:25:17.000000000 +0000 @@ -2,6 +2,9 @@ use proc_macro::TokenStream; use proc_macro2::{self, Span}; +#[cfg(test)] +use testutils::assert_code_eq; + fn check_return_type( return_type: &syn::ReturnType ) -> bool { match return_type { &syn::ReturnType::Default => true, @@ -269,52 +272,6 @@ async_test_impl( item ).into() } -#[cfg(test)] -fn rust_pretty_print( code: &proc_macro2::TokenStream ) -> String { - use std::process::{Command, Stdio}; - use std::mem; - use std::io::{Read, Write}; - - let mut cmd = Command::new( "rustfmt" ); - cmd.arg( "--version" ); - let has_rustfmt = cmd.output().map( |output| output.status.success() ).unwrap_or( false ); - if !has_rustfmt { - return format!( "{}", code ); - } - - let mut cmd = Command::new( "rustfmt" ); - cmd.stdin( Stdio::piped() ); - cmd.stdout( Stdio::piped() ); - cmd.stderr( Stdio::null() ); - let mut child = cmd.spawn().expect( "cannot spawn rustfmt" ); - let mut stdin = child.stdin.take().unwrap(); - write!( stdin, "{}", code ).unwrap(); - mem::drop( stdin ); - - let mut pretty_code = String::new(); - let mut stdout = child.stdout.take().unwrap(); - stdout.read_to_string( &mut pretty_code ).unwrap(); - child.wait().expect( "rustfmt failed" ); - - pretty_code -} - -#[cfg(test)] -fn assert_code_eq( actual: proc_macro2::TokenStream, expected: proc_macro2::TokenStream ) { - if format!( "{}", actual ) != format!( "{}", expected ) { - let expected_pretty = rust_pretty_print( &expected ); - let actual_pretty = rust_pretty_print( &actual ); - if expected_pretty != actual_pretty { - println!( "Expected:\n{}", expected_pretty ); - println!( "Actual:\n{}", actual_pretty ); - } else { - println!( "Expected:\n{}", expected ); - println!( "Actual:\n{}", actual ); - } - panic!( "Expected different generated code!" ); - } -} - #[test] fn test_async_test_simple() { let input = quote! { diff -Nru rust-stdweb-internal-macros-0.2.2/src/macro_js_raw.rs rust-stdweb-internal-macros-0.2.5/src/macro_js_raw.rs --- rust-stdweb-internal-macros-0.2.2/src/macro_js_raw.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/macro_js_raw.rs 2018-12-13 16:52:46.000000000 +0000 @@ -0,0 +1,194 @@ +use proc_macro2::TokenStream; +use syn; +use syn::parse::{ParseStream, Parse, Result}; + +use attr_hack::AttrHack; +use js_shim::js_shim_extern_code; +use utils::{Target, dummy_idents}; + +#[cfg(test)] +use testutils::assert_code_eq; + +fn parse_js_raw( input: TokenStream ) -> Result< JsRawInvocation > { + syn::parse2( input ) +} + +struct JsRawCode( String ); +struct JsRawInvocation { + code: String, + args: Vec< syn::Expr > +} + +impl Parse for JsRawCode { + fn parse( input: ParseStream ) -> Result< Self > { + if let Ok( ident ) = syn::Ident::parse( input ) { + if ident == "stringify" { + input.parse::< Token![!] >()?; + + let inner; + parenthesized!( inner in input ); + let code = inner.parse::< TokenStream >()?.to_string(); + return Ok( JsRawCode( code ) ); + } else if ident == "concat" { + input.parse::< Token![!] >()?; + + let inner; + parenthesized!( inner in input ); + let code: syn::punctuated::Punctuated< JsRawCode, Token![,] > = inner.parse_terminated( JsRawCode::parse )?; + let code: Vec< String > = code.into_iter().map( |chunk| chunk.0 ).collect(); + let code = code.join( "" ); + return Ok( JsRawCode( code ) ); + } else { + let ident_str = ident.to_string(); + return Err( syn::Error::new_spanned( ident, format!( "unexpected ident '{}'", ident_str ) ) ); + } + } + + let literal: syn::LitStr = Parse::parse( input )?; + let code = literal.value(); + Ok( JsRawCode( code ) ) + } +} + +impl Parse for JsRawInvocation { + fn parse( input: ParseStream ) -> Result< Self > { + let code = input.parse::< JsRawCode >()?.0; + let mut args = Vec::new(); + while !input.is_empty() { + syn::token::Comma::parse( input )?; + if input.is_empty() { + break; + } + + let arg: syn::Expr = input.parse()?; + args.push( arg ); + } + + Ok( JsRawInvocation { code, args } ) + } +} + +fn js_raw_code( target: Target, js_raw: JsRawInvocation ) -> TokenStream { + let (shim_name, shim) = js_shim_extern_code( target, &js_raw.code, js_raw.args.len() ); + let args = js_raw.args; + + quote! {{ + #shim + unsafe { + #shim_name( #((#args) as *const u8),* ) + } + }} +} + +pub fn js_raw( target: Target, input: TokenStream ) -> Result< TokenStream > { + let args = parse_js_raw( input )?; + Ok( js_raw_code( target, args ) ) +} + +// TODO: Delete this once expression procedural macros are stable. +pub fn js_raw_attr( target: Target, input: TokenStream ) -> Result< TokenStream > { + let wrapper: AttrHack< JsRawInvocation > = syn::parse2( input )?; + let wrapper_name = wrapper.fn_name; + let js_raw = wrapper.inner; + + let (shim_name, shim) = js_shim_extern_code( target, &js_raw.code, js_raw.args.len() ); + + let prototype_args = dummy_idents( js_raw.args.len() ).map( |name| quote! { #name: *const u8 } ); + let call_args = dummy_idents( js_raw.args.len() ).map( |name| quote! { #name } ); + let output = quote! { + fn #wrapper_name( #(#prototype_args),* ) -> i32 { + #shim + unsafe { + #shim_name( #(#call_args),* ) + } + } + }; + + Ok( output ) +} + +#[test] +fn test_parse_js_raw_only_code() { + let input = quote! { + "function();" + }; + + let js_raw = parse_js_raw( input ).unwrap(); + assert_eq!( js_raw.code, "function();" ); + assert!( js_raw.args.is_empty() ); +} + +#[test] +fn test_parse_js_raw_one_simple_arg() { + let input = quote! { + "function( $0 );", arg + }; + + let js_raw = parse_js_raw( input ).unwrap(); + assert_eq!( js_raw.code, "function( $0 );" ); + assert_eq!( js_raw.args.len(), 1 ); + assert_code_eq( &js_raw.args[ 0 ], quote! { arg } ); +} + +#[test] +fn test_parse_js_raw_complex() { + let input = quote! { + "dummy", { struct Foobar {} &[Foobar, Foobar] }, 1234, + }; + + let js_raw = parse_js_raw( input ).unwrap(); + assert_eq!( js_raw.code, "dummy" ); + assert_eq!( js_raw.args.len(), 2 ); + assert_code_eq( &js_raw.args[ 0 ], quote! { { struct Foobar {} &[Foobar, Foobar] } } ); + assert_code_eq( &js_raw.args[ 1 ], quote! { 1234 } ); +} + +#[test] +fn test_parse_js_raw_stringify() { + let input = quote! { + stringify!( hello_world ) + }; + + let js_raw = parse_js_raw( input ).unwrap(); + assert_eq!( js_raw.code, "hello_world" ); + assert!( js_raw.args.is_empty() ); +} + +#[test] +fn test_parse_js_raw_concat() { + let input = quote! { + concat!( "abc", "def", ) + }; + + let js_raw = parse_js_raw( input ).unwrap(); + assert_eq!( js_raw.code, "abcdef" ); + assert!( js_raw.args.is_empty() ); +} + +#[test] +fn test_parse_js_raw_with_concat_and_stringify() { + let input = quote! { + concat!( + "return foo( new ", + stringify!( Bar ), + "( baz )", + " );" + ), + arg.as_ref().as_raw() + }; + + let js_raw = parse_js_raw( input ).unwrap(); + assert_eq!( js_raw.code, "return foo( new Bar( baz ) );" ); + assert_eq!( js_raw.args.len(), 1 ); + assert_code_eq( &js_raw.args[ 0 ], quote! { arg.as_ref().as_raw() } ); +} + +#[test] +fn test_js_raw_code_generation_succeeds() { + let input = quote! { + "function( $0 )", + arg.as_ref().as_raw() + }; + + js_raw( Target::Emscripten, input ).unwrap(); +} diff -Nru rust-stdweb-internal-macros-0.2.2/src/macro_js.rs rust-stdweb-internal-macros-0.2.5/src/macro_js.rs --- rust-stdweb-internal-macros-0.2.2/src/macro_js.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/macro_js.rs 2018-12-13 16:52:46.000000000 +0000 @@ -0,0 +1,48 @@ +use std::fmt::Write; +use syn; +use syn::parse::Result; +use proc_macro2::TokenStream; + +use attr_hack::AttrHack; +use js_shim::js_shim_extern_code; +use js_stringify::StringifiedCode; +use utils::{Target, dummy_idents}; + +// TODO: Delete this once expression procedural macros are stable. +pub fn js_attr( target: Target, input: TokenStream, no_return: bool ) -> Result< TokenStream > { + let wrapper: AttrHack< StringifiedCode > = syn::parse2( input )?; + let wrapper_name = wrapper.fn_name; + let snippet = wrapper.inner; + let mut arg_count = snippet.arg_count(); + if !no_return { + arg_count += 1; + } + + let initial_arg_index = if no_return { 0 } else { 1 }; + let mut code = snippet.code( initial_arg_index ); + if !no_return { + code = format!( "Module.STDWEB_PRIVATE.from_js($0, (function(){{{}}})());", code ); + } + + let mut prelude = String::new(); + for nth in initial_arg_index..arg_count { + write!( prelude, "${} = Module.STDWEB_PRIVATE.to_js(${});", nth, nth ).unwrap(); + } + + code = format!( "{}{}", prelude, code ); + + let (shim_name, shim) = js_shim_extern_code( target, &code, arg_count ); + + let prototype_args = dummy_idents( arg_count ).map( |name| quote! { #name: *const u8 } ); + let call_args = dummy_idents( arg_count ).map( |name| quote! { #name } ); + let output = quote! { + fn #wrapper_name( #(#prototype_args),* ) -> i32 { + #shim + unsafe { + #shim_name( #(#call_args),* ) + } + } + }; + + Ok( output ) +} diff -Nru rust-stdweb-internal-macros-0.2.2/src/testutils.rs rust-stdweb-internal-macros-0.2.5/src/testutils.rs --- rust-stdweb-internal-macros-0.2.2/src/testutils.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/testutils.rs 2018-12-08 22:25:17.000000000 +0000 @@ -0,0 +1,50 @@ +use proc_macro2; +use quote::ToTokens; + +#[cfg(test)] +pub fn rust_pretty_print( code: &proc_macro2::TokenStream ) -> String { + use std::process::{Command, Stdio}; + use std::mem; + use std::io::{Read, Write}; + + let mut cmd = Command::new( "rustfmt" ); + cmd.arg( "--version" ); + let has_rustfmt = cmd.output().map( |output| output.status.success() ).unwrap_or( false ); + if !has_rustfmt { + return format!( "{}", code ); + } + + let mut cmd = Command::new( "rustfmt" ); + cmd.stdin( Stdio::piped() ); + cmd.stdout( Stdio::piped() ); + cmd.stderr( Stdio::null() ); + let mut child = cmd.spawn().expect( "cannot spawn rustfmt" ); + let mut stdin = child.stdin.take().unwrap(); + write!( stdin, "{}", code ).unwrap(); + mem::drop( stdin ); + + let mut pretty_code = String::new(); + let mut stdout = child.stdout.take().unwrap(); + stdout.read_to_string( &mut pretty_code ).unwrap(); + child.wait().expect( "rustfmt failed" ); + + pretty_code +} + +#[cfg(test)] +pub fn assert_code_eq< T: ToTokens, U: ToTokens >( actual: T, expected: U ) { + let actual = actual.into_token_stream(); + let expected = expected.into_token_stream(); + if format!( "{}", actual ) != format!( "{}", expected ) { + let expected_pretty = rust_pretty_print( &expected ); + let actual_pretty = rust_pretty_print( &actual ); + if expected_pretty != actual_pretty { + println!( "Expected:\n{}", expected_pretty ); + println!( "Actual:\n{}", actual_pretty ); + } else { + println!( "Expected:\n{}", expected ); + println!( "Actual:\n{}", actual ); + } + panic!( "Expected different generated code!" ); + } +} diff -Nru rust-stdweb-internal-macros-0.2.2/src/utils.rs rust-stdweb-internal-macros-0.2.5/src/utils.rs --- rust-stdweb-internal-macros-0.2.2/src/utils.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-stdweb-internal-macros-0.2.5/src/utils.rs 2018-12-13 16:52:46.000000000 +0000 @@ -0,0 +1,14 @@ +use syn; +use proc_macro2::Span; + +#[derive(Copy, Clone)] +pub enum Target { + Emscripten, + NativeWebAssembly +} + +pub fn dummy_idents( count: usize ) -> impl Iterator< Item = syn::Ident > { + (0..count).into_iter().map( |nth| { + syn::Ident::new( &format!( "a{}", nth ), Span::call_site() ) + }) +}