From b1e129dd9fc3b2e3161f0bfc6cbb985b74c6d052 Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Wed, 20 Mar 2024 12:49:38 +0800 Subject: [PATCH 01/20] feat: support expr when use `typescript_custom_section` attribute --- crates/backend/src/ast.rs | 12 +++++- crates/backend/src/codegen.rs | 65 +++++++++++++++++++++++++----- crates/backend/src/encode.rs | 63 ++++++++++++++++++++++++----- crates/cli-support/src/decode.rs | 20 ++++++++- crates/cli-support/src/wit/mod.rs | 6 +-- crates/macro-support/src/parser.rs | 16 ++++---- crates/shared/src/lib.rs | 2 +- src/lib.rs | 48 ++++++++++++++++++++++ 8 files changed, 196 insertions(+), 36 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 9cd8ebec140..7ddccb23c1b 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -24,7 +24,7 @@ pub struct Program { /// rust structs pub structs: Vec, /// custom typescript sections to be included in the definition file - pub typescript_custom_sections: Vec, + pub typescript_custom_sections: Vec, /// Inline JS snippets pub inline_js: Vec, /// Path to wasm_bindgen @@ -460,6 +460,16 @@ pub enum TypeLocation { ExportRet, } +/// An enum representing either a literal value (`Lit`) or an expression (`syn::Expr`). +#[cfg_attr(feature = "extra-traits", derive(Debug))] +#[derive(Clone)] +pub enum LitOrExpr { + /// Represents an expression that needs to be evaluated before it can be encoded + Expr(syn::Expr), + /// Represents a literal string that can be directly encoded. + Lit(String), +} + impl Export { /// Mangles a rust -> javascript export, so that the created Ident will be unique over function /// name and class name, if the function belongs to a javascript class. diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index bbcbc9cea14..eda18466fee 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1,5 +1,6 @@ use crate::ast; use crate::encode; +use crate::encode::EncodeChunk; use crate::Diagnostic; use once_cell::sync::Lazy; use proc_macro2::{Ident, Literal, Span, TokenStream}; @@ -95,16 +96,47 @@ impl TryToTokens for ast::Program { shared::version() ); let encoded = encode::encode(self)?; - let len = prefix_json.len() as u32; - let bytes = [ - &len.to_le_bytes()[..], - prefix_json.as_bytes(), - &encoded.custom_section, - ] - .concat(); - let generated_static_length = bytes.len(); - let generated_static_value = syn::LitByteStr::new(&bytes, Span::call_site()); + let encoded_chunks: Vec<_> = encoded + .custom_section + .iter() + .map(|chunk| match chunk { + EncodeChunk::EncodedBuf(buf) => { + let buf = syn::LitByteStr::new(buf.as_slice(), Span::call_site()); + quote!(#buf) + } + EncodeChunk::StrExpr(expr) => { + // encode expr as str + quote!({ + use wasm_bindgen::__rt::utils::consts::{encode_u32_to_fixed_len_bytes}; + const _STR_EXPR: &str = #expr; + const _STR_EXPR_BYTES: &[u8] = _STR_EXPR.as_bytes(); + const _STR_EXPR_BYTES_LEN: usize = _STR_EXPR_BYTES.len() + 5; + const _ENCODED_BYTES: [u8; _STR_EXPR_BYTES_LEN] = flat_slices([ + &encode_u32_to_fixed_len_bytes(_STR_EXPR_BYTES.len() as u32), + _STR_EXPR_BYTES, + ]); + &_ENCODED_BYTES + }) + } + }) + .collect(); + + let chunk_len = encoded_chunks.len(); + + // concatenate all encoded chunks and write the length in front of the chunk; + let encode_bytes = quote!({ + const _CHUNK_SLICES: [&[u8]; #chunk_len] = [ + #(#encoded_chunks,)* + ]; + const _CHUNK_LEN: usize = flat_len(_CHUNK_SLICES); + const _CHUNKS: [u8; _CHUNK_LEN] = flat_slices(_CHUNK_SLICES); + + const _LEN_BYTES: [u8; 4] = (_CHUNK_LEN as u32).to_le_bytes(); + const _ENCODED_BYTES_LEN: usize = _CHUNK_LEN + 4; + const _ENCODED_BYTES: [u8; _ENCODED_BYTES_LEN] = flat_slices([&_LEN_BYTES, &_CHUNKS]); + &_ENCODED_BYTES + }); // We already consumed the contents of included files when generating // the custom section, but we want to make sure that updates to the @@ -119,15 +151,26 @@ impl TryToTokens for ast::Program { quote! { include_str!(#file) } }); + let len = prefix_json.len() as u32; + let prefix_json_bytes = [&len.to_le_bytes()[..], prefix_json.as_bytes()].concat(); + let prefix_json_bytes = syn::LitByteStr::new(&prefix_json_bytes, Span::call_site()); + (quote! { #[cfg(target_arch = "wasm32")] #[automatically_derived] const _: () = { + use wasm_bindgen::__rt::utils::consts::{flat_len,flat_slices}; + static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*]; + const _ENCODED_BYTES: &[u8] = #encode_bytes; + const _PREFIX_JSON_BYTES: &[u8] = #prefix_json_bytes; + const _ENCODED_BYTES_LEN: usize = _ENCODED_BYTES.len(); + const _PREFIX_JSON_BYTES_LEN:usize = _PREFIX_JSON_BYTES.len(); + const _LEN: usize = _PREFIX_JSON_BYTES_LEN + _ENCODED_BYTES_LEN; + #[link_section = "__wasm_bindgen_unstable"] - pub static _GENERATED: [u8; #generated_static_length] = - *#generated_static_value; + static _GENERATED: [u8; _LEN] = flat_slices([_PREFIX_JSON_BYTES,_ENCODED_BYTES]); }; }) .to_tokens(tokens); diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index e7c7624e0ea..2d0df3f9810 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -9,8 +9,15 @@ use std::path::PathBuf; use crate::ast; use crate::Diagnostic; +#[derive(Clone)] +pub enum EncodeChunk { + EncodedBuf(Vec), + StrExpr(syn::Expr), + // TODO: support more expr type; +} + pub struct EncodeResult { - pub custom_section: Vec, + pub custom_section: Vec, pub included_files: Vec, } @@ -144,7 +151,7 @@ fn shared_program<'a>( typescript_custom_sections: prog .typescript_custom_sections .iter() - .map(|x| -> &'a str { x }) + .map(|x| shared_typescript_custom_section(x, intern)) .collect(), linked_modules: prog .linked_modules @@ -253,6 +260,16 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result( + i: &'a ast::LitOrExpr, + _intern: &'a Interner, +) -> CustomSection<'a> { + match i { + ast::LitOrExpr::Lit(lit) => CustomSection::Lit(lit), + ast::LitOrExpr::Expr(expr) => CustomSection::Expr(expr), + } +} + fn shared_linked_module<'a>( name: &str, i: &'a ast::ImportModule, @@ -358,24 +375,48 @@ trait Encode { } struct Encoder { - dst: Vec, + dst: Vec, +} + +enum CustomSection<'a> { + Expr(&'a syn::Expr), + Lit(&'a str), +} + +impl<'a> Encode for CustomSection<'a> { + fn encode(&self, dst: &mut Encoder) { + match self { + CustomSection::Expr(expr) => { + dst.dst.push(EncodeChunk::StrExpr((*expr).clone())); + } + CustomSection::Lit(s) => s.encode(dst), + } + } } impl Encoder { fn new() -> Encoder { - Encoder { - dst: vec![0, 0, 0, 0], - } + Encoder { dst: vec![] } } - fn finish(mut self) -> Vec { - let len = (self.dst.len() - 4) as u32; - self.dst[..4].copy_from_slice(&len.to_le_bytes()[..]); + fn finish(self) -> Vec { self.dst } fn byte(&mut self, byte: u8) { - self.dst.push(byte); + if let Some(EncodeChunk::EncodedBuf(buf)) = self.dst.last_mut() { + buf.push(byte); + } else { + self.dst.push(EncodeChunk::EncodedBuf(vec![byte])); + } + } + + fn extends_from_slice(&mut self, slice: &[u8]) { + if let Some(EncodeChunk::EncodedBuf(buf)) = self.dst.last_mut() { + buf.extend_from_slice(slice); + } else { + self.dst.push(EncodeChunk::EncodedBuf(slice.to_owned())); + } } } @@ -407,7 +448,7 @@ impl Encode for usize { impl<'a> Encode for &'a [u8] { fn encode(&self, dst: &mut Encoder) { self.len().encode(dst); - dst.dst.extend_from_slice(self); + dst.extends_from_slice(self); } } diff --git a/crates/cli-support/src/decode.rs b/crates/cli-support/src/decode.rs index a212147d3fd..109fe51e44f 100644 --- a/crates/cli-support/src/decode.rs +++ b/crates/cli-support/src/decode.rs @@ -1,4 +1,4 @@ -use std::str; +use std::{ops::Deref, str}; pub trait Decode<'src>: Sized { fn decode(data: &mut &'src [u8]) -> Self; @@ -10,12 +10,30 @@ pub trait Decode<'src>: Sized { } } +pub struct CustomSection<'src> { + str: &'src str, +} + fn get(b: &mut &[u8]) -> u8 { let r = b[0]; *b = &b[1..]; r } +impl<'src> Deref for CustomSection<'src> { + type Target = str; + fn deref(&self) -> &Self::Target { + self.str + } +} + +impl<'src> Decode<'src> for CustomSection<'src> { + fn decode(data: &mut &'src [u8]) -> Self { + let str = <&'src str>::decode(data); + Self { str } + } +} + impl<'src> Decode<'src> for bool { fn decode(data: &mut &'src [u8]) -> Self { get(data) != 0 diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index ca0211478b8..45333858f33 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -455,7 +455,7 @@ impl<'a> Context<'a> { self.struct_(struct_)?; } for section in typescript_custom_sections { - self.aux.extra_typescript.push_str(section); + self.aux.extra_typescript.push_str(§ion); self.aux.extra_typescript.push_str("\n\n"); } self.aux @@ -1536,14 +1536,14 @@ version of wasm-bindgen that uses a different bindgen format than this binary: this binary schema version: {my_version} Currently the bindgen format is unstable enough that these two schema versions -must exactly match. You can accomplish this by either updating this binary or +must exactly match. You can accomplish this by either updating this binary or the wasm-bindgen dependency in the Rust project. You should be able to update the wasm-bindgen dependency with: cargo update -p wasm-bindgen --precise {my_version} -don't forget to recompile your wasm file! Alternatively, you can update the +don't forget to recompile your wasm file! Alternatively, you can update the binary with: cargo install -f wasm-bindgen-cli --version {their_version} diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index eee4f66e95d..36ad61e3be6 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1399,17 +1399,17 @@ impl MacroParse for syn::ItemConst { bail_span!(self, "#[wasm_bindgen] will not work on constants unless you are defining a #[wasm_bindgen(typescript_custom_section)]."); } - match get_expr(&self.expr) { + let typescript_custom_section = match get_expr(&self.expr) { syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(litstr), .. - }) => { - program.typescript_custom_sections.push(litstr.value()); - } - expr => { - bail_span!(expr, "Expected a string literal to be used with #[wasm_bindgen(typescript_custom_section)]."); - } - } + }) => ast::LitOrExpr::Lit(litstr.value()), + expr => ast::LitOrExpr::Expr(expr.clone()), + }; + + program + .typescript_custom_sections + .push(typescript_custom_section); opts.check_used(); diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index f8ad45c7cd3..b0f68809a7a 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -17,7 +17,7 @@ macro_rules! shared_api { enums: Vec>, imports: Vec>, structs: Vec>, - typescript_custom_sections: Vec<&'a str>, + typescript_custom_sections: Vec>, local_modules: Vec>, inline_js: Vec<&'a str>, unique_crate_identifier: &'a str, diff --git a/src/lib.rs b/src/lib.rs index fdef0e8d39a..41e276f8701 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1804,6 +1804,54 @@ pub mod __rt { } } } + + pub mod utils { + pub mod consts { + pub const fn flat_len(slices: [&[T]; SIZE]) -> usize { + let mut len = 0; + let mut i = 0; + while i < slices.len() { + len += slices[i].len(); + i += 1; + } + len + } + + pub const fn flat_slices( + slices: [&[T]; SIZE], + ) -> [T; RESULT_LEN] { + let mut result = [unsafe { ::core::mem::zeroed() }; RESULT_LEN]; + + let mut slice_index = 0; + let mut result_offset = 0; + + while slice_index < slices.len() { + let mut i = 0; + let slice = slices[slice_index]; + while i < slice.len() { + result[result_offset] = slice[i]; + i += 1; + result_offset += 1; + } + slice_index += 1; + } + + result + } + + // encode u32 into var length integer, but fix bytes length to 5 + pub const fn encode_u32_to_fixed_len_bytes(value: u32) -> [u8; 5] { + let mut result: [u8; 5] = [0; 5]; + let mut i = 0; + while i < 4 { + result[i] = ((value >> (7 * i)) | 0x80) as u8; + i += 1; + } + result[4] = (value >> (7 * 4)) as u8; + result + } + } + } } /// A wrapper type around slices and vectors for binding the `Uint8ClampedArray` From 34959a4aefd0b9c45e21b1ea5d5bbb2944aad291 Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Wed, 20 Mar 2024 13:19:00 +0800 Subject: [PATCH 02/20] test: update typescript-tests --- crates/typescript-tests/src/custom_section.rs | 7 +++++++ crates/typescript-tests/src/custom_section.ts | 8 ++++++-- crates/typescript-tests/src/custom_section_types | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 crates/typescript-tests/src/custom_section_types diff --git a/crates/typescript-tests/src/custom_section.rs b/crates/typescript-tests/src/custom_section.rs index d4b602677f7..3fbf7f74da3 100644 --- a/crates/typescript-tests/src/custom_section.rs +++ b/crates/typescript-tests/src/custom_section.rs @@ -5,6 +5,13 @@ const TS_INTERFACE_EXPORT: &'static str = r" interface Height { height: number; } "; +#[wasm_bindgen(typescript_custom_section)] +const TS_INTERFACE_EXPORT1: &'static str = include_str!("./custom_section_types"); + +const TS_INTERFACE_EXPORT2: &str = "interface Person2 { height: number; }"; +#[wasm_bindgen(typescript_custom_section)] +const _: &str = TS_INTERFACE_EXPORT2; + #[wasm_bindgen] pub struct Person { pub height: u32, diff --git a/crates/typescript-tests/src/custom_section.ts b/crates/typescript-tests/src/custom_section.ts index 6420ea6dd59..cb3c826a80d 100644 --- a/crates/typescript-tests/src/custom_section.ts +++ b/crates/typescript-tests/src/custom_section.ts @@ -1,3 +1,7 @@ -import * as wbg from '../pkg/typescript_tests'; +import * as wbg from "../pkg/typescript_tests" -const height: wbg.Height = new wbg.Person(); \ No newline at end of file +const height: wbg.Height = new wbg.Person() + +const height1: wbg.Person1 = new wbg.Person() + +const height2: wbg.Person2 = new wbg.Person() diff --git a/crates/typescript-tests/src/custom_section_types b/crates/typescript-tests/src/custom_section_types new file mode 100644 index 00000000000..b3552d07ce1 --- /dev/null +++ b/crates/typescript-tests/src/custom_section_types @@ -0,0 +1,3 @@ +interface Person1 { + height: number +} From a01159a6a3c090237546e479d477b5359040868c Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Wed, 20 Mar 2024 13:22:37 +0800 Subject: [PATCH 03/20] chore: update "APPROVED_SCHEMA_FILE_HASH" of shared lib --- crates/shared/src/schema_hash_approval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index 471ccc9beac..5936653f556 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &str = "11955579329744078753"; +const APPROVED_SCHEMA_FILE_HASH: &str = "4163127631492445203"; #[test] fn schema_version() { From 145796733683c1a93bc1e803bb2bcc51064e6715 Mon Sep 17 00:00:00 2001 From: swnb Date: Mon, 25 Mar 2024 22:09:01 +0800 Subject: [PATCH 04/20] chore: cargo fmt --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index cf6cc44ce57..0fcddc4bcc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1867,7 +1867,7 @@ pub mod __rt { } } } - + if_std! { use core::mem; use std::boxed::Box; From 8545f56ad1b8e0ddff553c0196e1260ecf96f15b Mon Sep 17 00:00:00 2001 From: ZGY <1786718956@qq.com> Date: Tue, 9 Apr 2024 15:44:41 +0800 Subject: [PATCH 05/20] Apply suggestions from code review include fix typo and adding whitespace to ensure consistent code style. Co-authored-by: Liam Murphy --- crates/backend/src/codegen.rs | 6 +++--- crates/backend/src/encode.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index b0813399a99..f7c7cd1a60c 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -159,18 +159,18 @@ impl TryToTokens for ast::Program { #[cfg(target_arch = "wasm32")] #[automatically_derived] const _: () = { - use wasm_bindgen::__rt::utils::consts::{flat_len,flat_slices}; + use wasm_bindgen::__rt::utils::consts::{flat_len, flat_slices}; static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*]; const _ENCODED_BYTES: &[u8] = #encode_bytes; const _PREFIX_JSON_BYTES: &[u8] = #prefix_json_bytes; const _ENCODED_BYTES_LEN: usize = _ENCODED_BYTES.len(); - const _PREFIX_JSON_BYTES_LEN:usize = _PREFIX_JSON_BYTES.len(); + const _PREFIX_JSON_BYTES_LEN: usize = _PREFIX_JSON_BYTES.len(); const _LEN: usize = _PREFIX_JSON_BYTES_LEN + _ENCODED_BYTES_LEN; #[link_section = "__wasm_bindgen_unstable"] - static _GENERATED: [u8; _LEN] = flat_slices([_PREFIX_JSON_BYTES,_ENCODED_BYTES]); + static _GENERATED: [u8; _LEN] = flat_slices([_PREFIX_JSON_BYTES, _ENCODED_BYTES]); }; }) .to_tokens(tokens); diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index 2d0df3f9810..de32cc8e8f2 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -411,7 +411,7 @@ impl Encoder { } } - fn extends_from_slice(&mut self, slice: &[u8]) { + fn extend_from_slice(&mut self, slice: &[u8]) { if let Some(EncodeChunk::EncodedBuf(buf)) = self.dst.last_mut() { buf.extend_from_slice(slice); } else { From 8c5e20863b9a92dce8a02a2fcbf0096fdbb64c06 Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 15:52:24 +0800 Subject: [PATCH 06/20] chore(backend): fix typo --- crates/backend/src/encode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index de32cc8e8f2..7cd379b0ab1 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -448,7 +448,7 @@ impl Encode for usize { impl<'a> Encode for &'a [u8] { fn encode(&self, dst: &mut Encoder) { self.len().encode(dst); - dst.extends_from_slice(self); + dst.extend_from_slice(self); } } From fe2d0580ebd95b9e2c582d1df1aebfb1c4122fae Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 15:53:22 +0800 Subject: [PATCH 07/20] chore(typescript-tests): rename custom_section_type to custom_section_type.d.ts --- crates/typescript-tests/src/custom_section.rs | 2 +- .../src/{custom_section_types => custom_section_types.d.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename crates/typescript-tests/src/{custom_section_types => custom_section_types.d.ts} (100%) diff --git a/crates/typescript-tests/src/custom_section.rs b/crates/typescript-tests/src/custom_section.rs index 3fbf7f74da3..68c748c89c7 100644 --- a/crates/typescript-tests/src/custom_section.rs +++ b/crates/typescript-tests/src/custom_section.rs @@ -6,7 +6,7 @@ const TS_INTERFACE_EXPORT: &'static str = r" "; #[wasm_bindgen(typescript_custom_section)] -const TS_INTERFACE_EXPORT1: &'static str = include_str!("./custom_section_types"); +const TS_INTERFACE_EXPORT1: &'static str = include_str!("./custom_section_types.d.ts"); const TS_INTERFACE_EXPORT2: &str = "interface Person2 { height: number; }"; #[wasm_bindgen(typescript_custom_section)] diff --git a/crates/typescript-tests/src/custom_section_types b/crates/typescript-tests/src/custom_section_types.d.ts similarity index 100% rename from crates/typescript-tests/src/custom_section_types rename to crates/typescript-tests/src/custom_section_types.d.ts From c48c2fc539e12f803c72d3b785ffd1c4119490ae Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 16:04:36 +0800 Subject: [PATCH 08/20] fix(backend/codegen): change method flat_slices to flat_byte_slices in order to avoid unsafe code --- crates/backend/src/codegen.rs | 10 +++++----- src/lib.rs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index f7c7cd1a60c..1ad77c742bb 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -112,7 +112,7 @@ impl TryToTokens for ast::Program { const _STR_EXPR: &str = #expr; const _STR_EXPR_BYTES: &[u8] = _STR_EXPR.as_bytes(); const _STR_EXPR_BYTES_LEN: usize = _STR_EXPR_BYTES.len() + 5; - const _ENCODED_BYTES: [u8; _STR_EXPR_BYTES_LEN] = flat_slices([ + const _ENCODED_BYTES: [u8; _STR_EXPR_BYTES_LEN] = flat_byte_slices([ &encode_u32_to_fixed_len_bytes(_STR_EXPR_BYTES.len() as u32), _STR_EXPR_BYTES, ]); @@ -130,11 +130,11 @@ impl TryToTokens for ast::Program { #(#encoded_chunks,)* ]; const _CHUNK_LEN: usize = flat_len(_CHUNK_SLICES); - const _CHUNKS: [u8; _CHUNK_LEN] = flat_slices(_CHUNK_SLICES); + const _CHUNKS: [u8; _CHUNK_LEN] = flat_byte_slices(_CHUNK_SLICES); const _LEN_BYTES: [u8; 4] = (_CHUNK_LEN as u32).to_le_bytes(); const _ENCODED_BYTES_LEN: usize = _CHUNK_LEN + 4; - const _ENCODED_BYTES: [u8; _ENCODED_BYTES_LEN] = flat_slices([&_LEN_BYTES, &_CHUNKS]); + const _ENCODED_BYTES: [u8; _ENCODED_BYTES_LEN] = flat_byte_slices([&_LEN_BYTES, &_CHUNKS]); &_ENCODED_BYTES }); @@ -159,7 +159,7 @@ impl TryToTokens for ast::Program { #[cfg(target_arch = "wasm32")] #[automatically_derived] const _: () = { - use wasm_bindgen::__rt::utils::consts::{flat_len, flat_slices}; + use wasm_bindgen::__rt::utils::consts::{flat_len, flat_byte_slices}; static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*]; @@ -170,7 +170,7 @@ impl TryToTokens for ast::Program { const _LEN: usize = _PREFIX_JSON_BYTES_LEN + _ENCODED_BYTES_LEN; #[link_section = "__wasm_bindgen_unstable"] - static _GENERATED: [u8; _LEN] = flat_slices([_PREFIX_JSON_BYTES, _ENCODED_BYTES]); + static _GENERATED: [u8; _LEN] = flat_byte_slices([_PREFIX_JSON_BYTES, _ENCODED_BYTES]); }; }) .to_tokens(tokens); diff --git a/src/lib.rs b/src/lib.rs index 0fcddc4bcc1..30c32840cfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1832,10 +1832,10 @@ pub mod __rt { len } - pub const fn flat_slices( - slices: [&[T]; SIZE], - ) -> [T; RESULT_LEN] { - let mut result = [unsafe { ::core::mem::zeroed() }; RESULT_LEN]; + pub const fn flat_byte_slices( + slices: [&[u8]; SIZE], + ) -> [u8; RESULT_LEN] { + let mut result = [0; RESULT_LEN]; let mut slice_index = 0; let mut result_offset = 0; From 894b93edbbd19cf28ee6ea2954b3c35d0debb27e Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 16:16:19 +0800 Subject: [PATCH 09/20] fix(backend/codegen): use dynamic wasm_bindgen path as import entry --- crates/backend/src/codegen.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 1ad77c742bb..9841db3fdda 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -95,6 +95,9 @@ impl TryToTokens for ast::Program { shared::SCHEMA_VERSION, shared::version() ); + + let wasm_bindgen = &self.wasm_bindgen; + let encoded = encode::encode(self)?; let encoded_chunks: Vec<_> = encoded @@ -108,7 +111,7 @@ impl TryToTokens for ast::Program { EncodeChunk::StrExpr(expr) => { // encode expr as str quote!({ - use wasm_bindgen::__rt::utils::consts::{encode_u32_to_fixed_len_bytes}; + use #wasm_bindgen::__rt::utils::consts::{encode_u32_to_fixed_len_bytes}; const _STR_EXPR: &str = #expr; const _STR_EXPR_BYTES: &[u8] = _STR_EXPR.as_bytes(); const _STR_EXPR_BYTES_LEN: usize = _STR_EXPR_BYTES.len() + 5; @@ -159,7 +162,7 @@ impl TryToTokens for ast::Program { #[cfg(target_arch = "wasm32")] #[automatically_derived] const _: () = { - use wasm_bindgen::__rt::utils::consts::{flat_len, flat_byte_slices}; + use #wasm_bindgen::__rt::utils::consts::{flat_len, flat_byte_slices}; static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*]; From 4f9663146a6b7ece88d402d2555a54586ec43bf4 Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 17:20:58 +0800 Subject: [PATCH 10/20] chore(typescript-tests): ignore *.d.ts file when test --- crates/typescript-tests/jest.config.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/typescript-tests/jest.config.cjs b/crates/typescript-tests/jest.config.cjs index 2db7cd7178d..31ee1910442 100644 --- a/crates/typescript-tests/jest.config.cjs +++ b/crates/typescript-tests/jest.config.cjs @@ -4,7 +4,7 @@ module.exports = { testEnvironment: 'node', extensionsToTreatAsEsm: [".ts"], verbose: true, - testMatch: ['**/src/*.ts'], + testMatch: ['**/src/*.ts', '!**/src/*.d.ts'], // TODO: migrate all test files and remove this testPathIgnorePatterns: [ ".*/src/custom_section.ts$", From e45c86ff3480bd3597732c97e2af58bb11eae27a Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 17:41:16 +0800 Subject: [PATCH 11/20] chore(shared/program): rename CustomSection to LitOrExpr --- crates/backend/src/encode.rs | 14 +++++++------- crates/cli-support/src/decode.rs | 6 +++--- crates/shared/src/lib.rs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index 7cd379b0ab1..55ebb7fc031 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -263,10 +263,10 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result( i: &'a ast::LitOrExpr, _intern: &'a Interner, -) -> CustomSection<'a> { +) -> LitOrExpr<'a> { match i { - ast::LitOrExpr::Lit(lit) => CustomSection::Lit(lit), - ast::LitOrExpr::Expr(expr) => CustomSection::Expr(expr), + ast::LitOrExpr::Lit(lit) => LitOrExpr::Lit(lit), + ast::LitOrExpr::Expr(expr) => LitOrExpr::Expr(expr), } } @@ -378,18 +378,18 @@ struct Encoder { dst: Vec, } -enum CustomSection<'a> { +enum LitOrExpr<'a> { Expr(&'a syn::Expr), Lit(&'a str), } -impl<'a> Encode for CustomSection<'a> { +impl<'a> Encode for LitOrExpr<'a> { fn encode(&self, dst: &mut Encoder) { match self { - CustomSection::Expr(expr) => { + LitOrExpr::Expr(expr) => { dst.dst.push(EncodeChunk::StrExpr((*expr).clone())); } - CustomSection::Lit(s) => s.encode(dst), + LitOrExpr::Lit(s) => s.encode(dst), } } } diff --git a/crates/cli-support/src/decode.rs b/crates/cli-support/src/decode.rs index 109fe51e44f..4264939f5c4 100644 --- a/crates/cli-support/src/decode.rs +++ b/crates/cli-support/src/decode.rs @@ -10,7 +10,7 @@ pub trait Decode<'src>: Sized { } } -pub struct CustomSection<'src> { +pub struct LitOrExpr<'src> { str: &'src str, } @@ -20,14 +20,14 @@ fn get(b: &mut &[u8]) -> u8 { r } -impl<'src> Deref for CustomSection<'src> { +impl<'src> Deref for LitOrExpr<'src> { type Target = str; fn deref(&self) -> &Self::Target { self.str } } -impl<'src> Decode<'src> for CustomSection<'src> { +impl<'src> Decode<'src> for LitOrExpr<'src> { fn decode(data: &mut &'src [u8]) -> Self { let str = <&'src str>::decode(data); Self { str } diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index b0f68809a7a..71a302c591d 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -17,7 +17,7 @@ macro_rules! shared_api { enums: Vec>, imports: Vec>, structs: Vec>, - typescript_custom_sections: Vec>, + typescript_custom_sections: Vec>, local_modules: Vec>, inline_js: Vec<&'a str>, unique_crate_identifier: &'a str, From c916908d4cb4c562804e10f512bf272605a2fdd5 Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 18:06:06 +0800 Subject: [PATCH 12/20] doc(shared/lib): add doc for program[typescript_custom_sections], explain why there are different types of LitOrExpr when encoding and decoding --- crates/shared/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 71a302c591d..eeadecbfec5 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -17,6 +17,10 @@ macro_rules! shared_api { enums: Vec>, imports: Vec>, structs: Vec>, + // NOTE: Originally typescript_custom_sections are just some strings + // But the expression type can only be parsed into a string during compilation + // So when encoding, LitOrExpr contains two types, one is that expressions are parsed into strings during compilation, and the other is can be parsed directly. + // When decoding, LitOrExpr can be decoded as a string. typescript_custom_sections: Vec>, local_modules: Vec>, inline_js: Vec<&'a str>, From 6eca679455c71efcf0e1ef5baa1cb5e7067b815f Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 18:13:44 +0800 Subject: [PATCH 13/20] chore(shared): update "APPROVED_SCHEMA_FILE_HASH" of shared lib --- crates/shared/src/schema_hash_approval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index 5936653f556..143f8483c7b 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &str = "4163127631492445203"; +const APPROVED_SCHEMA_FILE_HASH: &str = "10197913343580353876"; #[test] fn schema_version() { From 722e00e5fd9ad929d3a5ee7f271b54c6961506cc Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Tue, 9 Apr 2024 19:35:22 +0800 Subject: [PATCH 14/20] doc: add docs for method encode_u32_to_fixed_len_bytes --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 30c32840cfb..6f60d174e43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1854,7 +1854,10 @@ pub mod __rt { result } - // encode u32 into var length integer, but fix bytes length to 5 + // NOTE: This method is used to encode u32 into a variable-length-integer during the compile-time . + // Generally speaking, the length of the encoded variable-length-integer depends on the size of the integer + // but the maximum capacity can be used here to simplify the amount of code during the compile-time . + // TODO: compute the length of (var length integer) in const time pub const fn encode_u32_to_fixed_len_bytes(value: u32) -> [u8; 5] { let mut result: [u8; 5] = [0; 5]; let mut i = 0; From 37f8d1407f22ab693186a2c2dbfbf7d8d91cfd15 Mon Sep 17 00:00:00 2001 From: swnb Date: Tue, 9 Apr 2024 22:14:55 +0800 Subject: [PATCH 15/20] refactor(backend/encode): rename method shared_typescript_custom_section to shared_lit_or_expr --- crates/backend/src/encode.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index 55ebb7fc031..ac2c0f2803c 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -151,7 +151,7 @@ fn shared_program<'a>( typescript_custom_sections: prog .typescript_custom_sections .iter() - .map(|x| shared_typescript_custom_section(x, intern)) + .map(|x| shared_lit_or_expr(x, intern)) .collect(), linked_modules: prog .linked_modules @@ -260,7 +260,7 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result( +fn shared_lit_or_expr<'a>( i: &'a ast::LitOrExpr, _intern: &'a Interner, ) -> LitOrExpr<'a> { From 47f87f9437efe14a36d9316e300f16b57d3c3665 Mon Sep 17 00:00:00 2001 From: swnb Date: Tue, 9 Apr 2024 22:19:59 +0800 Subject: [PATCH 16/20] refactor(__rt): extract methods from nested mod directly into `__rt` --- crates/backend/src/codegen.rs | 4 +- src/lib.rs | 86 +++++++++++++++++------------------ 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 9841db3fdda..bae386fc476 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -111,7 +111,7 @@ impl TryToTokens for ast::Program { EncodeChunk::StrExpr(expr) => { // encode expr as str quote!({ - use #wasm_bindgen::__rt::utils::consts::{encode_u32_to_fixed_len_bytes}; + use #wasm_bindgen::__rt::{encode_u32_to_fixed_len_bytes}; const _STR_EXPR: &str = #expr; const _STR_EXPR_BYTES: &[u8] = _STR_EXPR.as_bytes(); const _STR_EXPR_BYTES_LEN: usize = _STR_EXPR_BYTES.len() + 5; @@ -162,7 +162,7 @@ impl TryToTokens for ast::Program { #[cfg(target_arch = "wasm32")] #[automatically_derived] const _: () = { - use #wasm_bindgen::__rt::utils::consts::{flat_len, flat_byte_slices}; + use #wasm_bindgen::__rt::{flat_len, flat_byte_slices}; static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*]; diff --git a/src/lib.rs b/src/lib.rs index 6f60d174e43..84b825bd94c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1820,55 +1820,51 @@ pub mod __rt { } } - pub mod utils { - pub mod consts { - pub const fn flat_len(slices: [&[T]; SIZE]) -> usize { - let mut len = 0; - let mut i = 0; - while i < slices.len() { - len += slices[i].len(); - i += 1; - } - len + pub const fn flat_len(slices: [&[T]; SIZE]) -> usize { + let mut len = 0; + let mut i = 0; + while i < slices.len() { + len += slices[i].len(); + i += 1; + } + len + } + + pub const fn flat_byte_slices( + slices: [&[u8]; SIZE], + ) -> [u8; RESULT_LEN] { + let mut result = [0; RESULT_LEN]; + + let mut slice_index = 0; + let mut result_offset = 0; + + while slice_index < slices.len() { + let mut i = 0; + let slice = slices[slice_index]; + while i < slice.len() { + result[result_offset] = slice[i]; + i += 1; + result_offset += 1; } + slice_index += 1; + } - pub const fn flat_byte_slices( - slices: [&[u8]; SIZE], - ) -> [u8; RESULT_LEN] { - let mut result = [0; RESULT_LEN]; - - let mut slice_index = 0; - let mut result_offset = 0; - - while slice_index < slices.len() { - let mut i = 0; - let slice = slices[slice_index]; - while i < slice.len() { - result[result_offset] = slice[i]; - i += 1; - result_offset += 1; - } - slice_index += 1; - } - - result - } + result + } - // NOTE: This method is used to encode u32 into a variable-length-integer during the compile-time . - // Generally speaking, the length of the encoded variable-length-integer depends on the size of the integer - // but the maximum capacity can be used here to simplify the amount of code during the compile-time . - // TODO: compute the length of (var length integer) in const time - pub const fn encode_u32_to_fixed_len_bytes(value: u32) -> [u8; 5] { - let mut result: [u8; 5] = [0; 5]; - let mut i = 0; - while i < 4 { - result[i] = ((value >> (7 * i)) | 0x80) as u8; - i += 1; - } - result[4] = (value >> (7 * 4)) as u8; - result - } + // NOTE: This method is used to encode u32 into a variable-length-integer during the compile-time . + // Generally speaking, the length of the encoded variable-length-integer depends on the size of the integer + // but the maximum capacity can be used here to simplify the amount of code during the compile-time . + // TODO: compute the length of (var length integer) in const time + pub const fn encode_u32_to_fixed_len_bytes(value: u32) -> [u8; 5] { + let mut result: [u8; 5] = [0; 5]; + let mut i = 0; + while i < 4 { + result[i] = ((value >> (7 * i)) | 0x80) as u8; + i += 1; } + result[4] = (value >> (7 * 4)) as u8; + result } if_std! { From c8cec206bc78294f9993f9bc411f57b307fc7dee Mon Sep 17 00:00:00 2001 From: swnb Date: Tue, 9 Apr 2024 22:31:07 +0800 Subject: [PATCH 17/20] chore: cargo fmt --- crates/backend/src/encode.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index ac2c0f2803c..438bd944b7a 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -260,10 +260,7 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result( - i: &'a ast::LitOrExpr, - _intern: &'a Interner, -) -> LitOrExpr<'a> { +fn shared_lit_or_expr<'a>(i: &'a ast::LitOrExpr, _intern: &'a Interner) -> LitOrExpr<'a> { match i { ast::LitOrExpr::Lit(lit) => LitOrExpr::Lit(lit), ast::LitOrExpr::Expr(expr) => LitOrExpr::Expr(expr), From d0669bc45fb805d1c91627bdac4fa2edb1d17df2 Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Wed, 10 Apr 2024 11:00:33 +0800 Subject: [PATCH 18/20] chore(__rt): remove unnecessary TODO --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 84b825bd94c..c4f41d0e357 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1855,7 +1855,6 @@ pub mod __rt { // NOTE: This method is used to encode u32 into a variable-length-integer during the compile-time . // Generally speaking, the length of the encoded variable-length-integer depends on the size of the integer // but the maximum capacity can be used here to simplify the amount of code during the compile-time . - // TODO: compute the length of (var length integer) in const time pub const fn encode_u32_to_fixed_len_bytes(value: u32) -> [u8; 5] { let mut result: [u8; 5] = [0; 5]; let mut i = 0; From 88bcc9f202e3c645d1800e7f4679c54e35a0b4e5 Mon Sep 17 00:00:00 2001 From: async-rustacean Date: Wed, 10 Apr 2024 11:14:05 +0800 Subject: [PATCH 19/20] chore(changelog): update change log Support Expressions when using the `typescript_custom_section` attribute[#3901] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0add0955534..ae31cb2163d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Added +* Support Expressions when using the `typescript_custom_section` attribute + [#3901](https://github.com/rustwasm/wasm-bindgen/pull/3901) + * Implement `From>` for `JsValue`. [#3877](https://github.com/rustwasm/wasm-bindgen/pull/3877) From d7796a7f1db5e5f2c9ba1481d70a360a6c8dea3d Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Wed, 10 Apr 2024 16:42:10 +1000 Subject: [PATCH 20/20] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae31cb2163d..f65d6cd1631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Added -* Support Expressions when using the `typescript_custom_section` attribute +* Added support for arbitrary expressions when using `#[wasm_bindgen(typescript_custom_section)]`. [#3901](https://github.com/rustwasm/wasm-bindgen/pull/3901) * Implement `From>` for `JsValue`.