diff --git a/CHANGELOG.md b/CHANGELOG.md index 95d8901c392..e4c194d677c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ * Added bindings for the draft [WebRTC Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform) spec. [#4125](https://github.com/rustwasm/wasm-bindgen/pull/4125) +### Changed + +* Implicitly enable reference type and multivalue transformations if the module already makes use of the corresponding target features. + [#4133](https://github.com/rustwasm/wasm-bindgen/pull/4133) + ### Fixed * Fixed linked modules emitting snippet files when not using `--split-linked-modules`. diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index a35b057e6a6..8c49ddbc1a6 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -323,6 +323,17 @@ impl Bindgen { .context("failed getting Wasm module")?, }; + // Enable reference type transformations if the module is already using it. + if let Ok(true) = wasm_bindgen_wasm_conventions::target_feature(&module, "reference-types") + { + self.externref = true; + } + + // Enable multivalue transformations if the module is already using it. + if let Ok(true) = wasm_bindgen_wasm_conventions::target_feature(&module, "multivalue") { + self.multi_value = true; + } + // Check that no exported symbol is called "default" if we target web. if matches!(self.mode, OutputMode::Web) && module.exports.iter().any(|export| export.name == "default") diff --git a/crates/wasm-conventions/src/lib.rs b/crates/wasm-conventions/src/lib.rs index c0f25739456..beb0fe9da09 100755 --- a/crates/wasm-conventions/src/lib.rs +++ b/crates/wasm-conventions/src/lib.rs @@ -177,6 +177,50 @@ pub fn get_or_insert_start_builder(module: &mut Module) -> &mut FunctionBuilder .builder_mut() } +pub fn target_feature(module: &Module, feature: &str) -> Result { + // Taken from . + anyhow::ensure!(feature.len() <= 100_000, "feature name too long"); + + // Try to find an existing section. + let section = module + .customs + .iter() + .find(|(_, custom)| custom.name() == "target_features"); + + if let Some((_, section)) = section { + let section: &RawCustomSection = section + .as_any() + .downcast_ref() + .context("failed to read section")?; + let mut reader = BinaryReader::new(§ion.data, 0, WasmFeatures::default()); + // The first integer contains the target feature count. + let count = reader.read_var_u32()?; + + // Try to find if the target feature is already present. + for _ in 0..count { + // First byte is the prefix. + let prefix = reader.read_u8()?; + // Read the feature. + let length = reader.read_var_u32()?; + let this_feature = reader.read_bytes(length as usize)?; + + // If we found the target feature, we are done here. + if this_feature == feature.as_bytes() { + // Make sure we set any existing prefix to "enabled". + if prefix == b'-' { + return Ok(false); + } + + return Ok(true); + } + } + + Ok(false) + } else { + Ok(false) + } +} + pub fn insert_target_feature(module: &mut Module, new_feature: &str) -> Result<()> { // Taken from . anyhow::ensure!(new_feature.len() <= 100_000, "feature name too long");