mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-13 21:11:22 +00:00
try to fix global / modulaized import ns conflict (#2057)
* use global import map for rename * fix same ns import * cargo fmt * add basic test * move generate_identifier, add comments, add tests * remove leading &mut * remove unnecessary bail * use import_name for global and some refine * Add back in error handling, clean up instruction iteration * Remove unnecessary patch statements Co-authored-by: Alex Crichton <alex@alexcrichton.com>
This commit is contained in:
@ -113,7 +113,7 @@ impl<'a> Context<'a> {
|
|||||||
contents: &str,
|
contents: &str,
|
||||||
comments: Option<&str>,
|
comments: Option<&str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let definition_name = generate_identifier(export_name, &mut self.defined_identifiers);
|
let definition_name = self.generate_identifier(export_name);
|
||||||
if contents.starts_with("class") && definition_name != export_name {
|
if contents.starts_with("class") && definition_name != export_name {
|
||||||
bail!("cannot shadow already defined class `{}`", export_name);
|
bail!("cannot shadow already defined class `{}`", export_name);
|
||||||
}
|
}
|
||||||
@ -1922,6 +1922,18 @@ impl<'a> Context<'a> {
|
|||||||
require_class(&mut self.exported_classes, name).wrap_needed = true;
|
require_class(&mut self.exported_classes, name).wrap_needed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_module_import(&mut self, module: String, name: &str, actual: &str) {
|
||||||
|
let rename = if name == actual {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(actual.to_string())
|
||||||
|
};
|
||||||
|
self.js_imports
|
||||||
|
.entry(module)
|
||||||
|
.or_insert(Vec::new())
|
||||||
|
.push((name.to_string(), rename));
|
||||||
|
}
|
||||||
|
|
||||||
fn import_name(&mut self, import: &JsImport) -> Result<String, Error> {
|
fn import_name(&mut self, import: &JsImport) -> Result<String, Error> {
|
||||||
if let Some(name) = self.imported_names.get(&import.name) {
|
if let Some(name) = self.imported_names.get(&import.name) {
|
||||||
let mut name = name.clone();
|
let mut name = name.clone();
|
||||||
@ -1932,30 +1944,17 @@ impl<'a> Context<'a> {
|
|||||||
return Ok(name.clone());
|
return Ok(name.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let js_imports = &mut self.js_imports;
|
|
||||||
let mut add_module_import = |module: String, name: &str, actual: &str| {
|
|
||||||
let rename = if name == actual {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(actual.to_string())
|
|
||||||
};
|
|
||||||
js_imports
|
|
||||||
.entry(module)
|
|
||||||
.or_insert(Vec::new())
|
|
||||||
.push((name.to_string(), rename));
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut name = match &import.name {
|
let mut name = match &import.name {
|
||||||
JsImportName::Module { module, name } => {
|
JsImportName::Module { module, name } => {
|
||||||
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
|
let unique_name = self.generate_identifier(name);
|
||||||
add_module_import(module.clone(), name, &unique_name);
|
self.add_module_import(module.clone(), name, &unique_name);
|
||||||
unique_name
|
unique_name
|
||||||
}
|
}
|
||||||
|
|
||||||
JsImportName::LocalModule { module, name } => {
|
JsImportName::LocalModule { module, name } => {
|
||||||
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
|
let unique_name = self.generate_identifier(name);
|
||||||
let module = self.config.local_module_name(module);
|
let module = self.config.local_module_name(module);
|
||||||
add_module_import(module, name, &unique_name);
|
self.add_module_import(module, name, &unique_name);
|
||||||
unique_name
|
unique_name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1967,8 +1966,8 @@ impl<'a> Context<'a> {
|
|||||||
let module = self
|
let module = self
|
||||||
.config
|
.config
|
||||||
.inline_js_module_name(unique_crate_identifier, *snippet_idx_in_crate);
|
.inline_js_module_name(unique_crate_identifier, *snippet_idx_in_crate);
|
||||||
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
|
let unique_name = self.generate_identifier(name);
|
||||||
add_module_import(module, name, &unique_name);
|
self.add_module_import(module, name, &unique_name);
|
||||||
unique_name
|
unique_name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1998,7 +1997,7 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JsImportName::Global { name } => {
|
JsImportName::Global { name } => {
|
||||||
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
|
let unique_name = self.generate_identifier(name);
|
||||||
if unique_name != *name {
|
if unique_name != *name {
|
||||||
bail!("cannot import `{}` from two locations", name);
|
bail!("cannot import `{}` from two locations", name);
|
||||||
}
|
}
|
||||||
@ -2055,6 +2054,7 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate(&mut self) -> Result<(), Error> {
|
pub fn generate(&mut self) -> Result<(), Error> {
|
||||||
|
self.prestore_global_import_identifiers()?;
|
||||||
for (id, adapter) in crate::sorted_iter(&self.wit.adapters) {
|
for (id, adapter) in crate::sorted_iter(&self.wit.adapters) {
|
||||||
let instrs = match &adapter.kind {
|
let instrs = match &adapter.kind {
|
||||||
AdapterKind::Import { .. } => continue,
|
AdapterKind::Import { .. } => continue,
|
||||||
@ -2084,6 +2084,29 @@ impl<'a> Context<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Registers import names for all `Global` imports first before we actually
|
||||||
|
/// process any adapters.
|
||||||
|
///
|
||||||
|
/// `Global` names must be imported as their exact name, so if the same name
|
||||||
|
/// from a global is also imported from a module we have to be sure to
|
||||||
|
/// import the global first to ensure we don't shadow the actual global
|
||||||
|
/// value. Otherwise we have no way of accessing the global value!
|
||||||
|
///
|
||||||
|
/// This function will iterate through the import map up-front and generate
|
||||||
|
/// a cache entry for each import name which is a `Global`.
|
||||||
|
fn prestore_global_import_identifiers(&mut self) -> Result<(), Error> {
|
||||||
|
for import in self.aux.import_map.values() {
|
||||||
|
let js = match import {
|
||||||
|
AuxImport::Value(AuxValue::Bare(js)) => js,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
if let JsImportName::Global { .. } = js.name {
|
||||||
|
self.import_name(js)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_adapter(
|
fn generate_adapter(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: AdapterId,
|
id: AdapterId,
|
||||||
@ -3133,6 +3156,21 @@ impl<'a> Context<'a> {
|
|||||||
fn adapter_name(&self, id: AdapterId) -> String {
|
fn adapter_name(&self, id: AdapterId) -> String {
|
||||||
format!("__wbg_adapter_{}", id.0)
|
format!("__wbg_adapter_{}", id.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_identifier(&mut self, name: &str) -> String {
|
||||||
|
let cnt = self
|
||||||
|
.defined_identifiers
|
||||||
|
.entry(name.to_string())
|
||||||
|
.or_insert(0);
|
||||||
|
*cnt += 1;
|
||||||
|
// We want to mangle `default` at once, so we can support default exports and don't generate
|
||||||
|
// invalid glue code like this: `import { default } from './module';`.
|
||||||
|
if *cnt == 1 && name != "default" {
|
||||||
|
name.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}{}", name, cnt)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_duplicated_getter_and_setter_names(
|
fn check_duplicated_getter_and_setter_names(
|
||||||
@ -3180,18 +3218,6 @@ fn check_duplicated_getter_and_setter_names(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_identifier(name: &str, used_names: &mut HashMap<String, usize>) -> String {
|
|
||||||
let cnt = used_names.entry(name.to_string()).or_insert(0);
|
|
||||||
*cnt += 1;
|
|
||||||
// We want to mangle `default` at once, so we can support default exports and don't generate
|
|
||||||
// invalid glue code like this: `import { default } from './module';`.
|
|
||||||
if *cnt == 1 && name != "default" {
|
|
||||||
name.to_string()
|
|
||||||
} else {
|
|
||||||
format!("{}{}", name, cnt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_doc_comments(comments: &str, js_doc_comments: Option<String>) -> String {
|
fn format_doc_comments(comments: &str, js_doc_comments: Option<String>) -> String {
|
||||||
let body: String = comments.lines().map(|c| format!("*{}\n", c)).collect();
|
let body: String = comments.lines().map(|c| format!("*{}\n", c)).collect();
|
||||||
let doc = if let Some(docs) = js_doc_comments {
|
let doc = if let Some(docs) = js_doc_comments {
|
||||||
@ -3294,27 +3320,6 @@ impl ExportedClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_generate_identifier() {
|
|
||||||
let mut used_names: HashMap<String, usize> = HashMap::new();
|
|
||||||
assert_eq!(
|
|
||||||
generate_identifier("someVar", &mut used_names),
|
|
||||||
"someVar".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
generate_identifier("someVar", &mut used_names),
|
|
||||||
"someVar2".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
generate_identifier("default", &mut used_names),
|
|
||||||
"default1".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
generate_identifier("default", &mut used_names),
|
|
||||||
"default2".to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MemView {
|
struct MemView {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
num: usize,
|
num: usize,
|
||||||
|
@ -135,6 +135,34 @@ fn works_on_empty_project() {
|
|||||||
cmd.assert().success();
|
cmd.assert().success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn namespace_global_and_noglobal_works() {
|
||||||
|
let (mut cmd, _out_dir) = Project::new("namespace_global_and_noglobal_works")
|
||||||
|
.file(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
#[wasm_bindgen(module = "fs")]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = window)]
|
||||||
|
fn t1();
|
||||||
|
}
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = window)]
|
||||||
|
fn t2();
|
||||||
|
}
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn test() {
|
||||||
|
t1();
|
||||||
|
t2();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.wasm_bindgen("");
|
||||||
|
cmd.assert().success();
|
||||||
|
}
|
||||||
|
|
||||||
mod npm;
|
mod npm;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -127,3 +127,13 @@ exports.receive_some = val => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.get_some_val = () => VAL;
|
exports.get_some_val = () => VAL;
|
||||||
|
|
||||||
|
exports.Math = {
|
||||||
|
func_from_module_math: (a) => a * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.same_name_from_import = (a) => a * 3;
|
||||||
|
|
||||||
|
exports.same_js_namespace_from_module = {
|
||||||
|
func_from_module_1_same_js_namespace: (a) => a * 5
|
||||||
|
}
|
@ -69,11 +69,32 @@ extern "C" {
|
|||||||
fn receive_some_ref(arg: Option<&PassOutOptionUndefined>);
|
fn receive_some_ref(arg: Option<&PassOutOptionUndefined>);
|
||||||
#[wasm_bindgen(js_name = "receive_some")]
|
#[wasm_bindgen(js_name = "receive_some")]
|
||||||
fn receive_some_owned(arg: Option<PassOutOptionUndefined>);
|
fn receive_some_owned(arg: Option<PassOutOptionUndefined>);
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = Math)]
|
||||||
|
fn func_from_module_math(a: i32) -> i32;
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = "same_name_from_import")]
|
||||||
|
fn same_name_from_import_1(s: i32) -> i32;
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = same_js_namespace_from_module)]
|
||||||
|
fn func_from_module_1_same_js_namespace(s: i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(module = "tests/wasm/imports_2.js")]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_name = "same_name_from_import")]
|
||||||
|
fn same_name_from_import_2(s: i32) -> i32;
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = same_js_namespace_from_module)]
|
||||||
|
fn func_from_module_2_same_js_namespace(s: i32) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn parseInt(a: &str) -> u32;
|
fn parseInt(a: &str) -> u32;
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = Math, js_name = "sqrt")]
|
||||||
|
fn func_from_global_math(s: f64) -> f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
@ -274,3 +295,21 @@ fn pass_out_options_as_undefined() {
|
|||||||
receive_some_owned(Some(v.clone()));
|
receive_some_owned(Some(v.clone()));
|
||||||
receive_some_owned(Some(v));
|
receive_some_owned(Some(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn func_from_global_and_module_same_js_namespace() {
|
||||||
|
assert_eq!(func_from_global_math(4.0), 2.0);
|
||||||
|
assert_eq!(func_from_module_math(2), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn func_from_two_modules_same_js_name() {
|
||||||
|
assert_eq!(same_name_from_import_1(1), 3);
|
||||||
|
assert_eq!(same_name_from_import_2(1), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn func_from_two_modules_same_js_namespace() {
|
||||||
|
assert_eq!(func_from_module_1_same_js_namespace(2), 10);
|
||||||
|
assert_eq!(func_from_module_2_same_js_namespace(2), 12);
|
||||||
|
}
|
||||||
|
5
tests/wasm/imports_2.js
Normal file
5
tests/wasm/imports_2.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
exports.same_name_from_import = (a) => a * 4;
|
||||||
|
|
||||||
|
exports.same_js_namespace_from_module = {
|
||||||
|
func_from_module_2_same_js_namespace: (a) => a * 6
|
||||||
|
}
|
Reference in New Issue
Block a user