Skip to content

Commit ad85de5

Browse files
a1trl9alexcrichton
andauthored
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 <[email protected]>
1 parent a75570d commit ad85de5

File tree

5 files changed

+141
-54
lines changed

5 files changed

+141
-54
lines changed

crates/cli-support/src/js/mod.rs

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl<'a> Context<'a> {
113113
contents: &str,
114114
comments: Option<&str>,
115115
) -> Result<(), Error> {
116-
let definition_name = generate_identifier(export_name, &mut self.defined_identifiers);
116+
let definition_name = self.generate_identifier(export_name);
117117
if contents.starts_with("class") && definition_name != export_name {
118118
bail!("cannot shadow already defined class `{}`", export_name);
119119
}
@@ -1922,6 +1922,18 @@ impl<'a> Context<'a> {
19221922
require_class(&mut self.exported_classes, name).wrap_needed = true;
19231923
}
19241924

1925+
fn add_module_import(&mut self, module: String, name: &str, actual: &str) {
1926+
let rename = if name == actual {
1927+
None
1928+
} else {
1929+
Some(actual.to_string())
1930+
};
1931+
self.js_imports
1932+
.entry(module)
1933+
.or_insert(Vec::new())
1934+
.push((name.to_string(), rename));
1935+
}
1936+
19251937
fn import_name(&mut self, import: &JsImport) -> Result<String, Error> {
19261938
if let Some(name) = self.imported_names.get(&import.name) {
19271939
let mut name = name.clone();
@@ -1932,30 +1944,17 @@ impl<'a> Context<'a> {
19321944
return Ok(name.clone());
19331945
}
19341946

1935-
let js_imports = &mut self.js_imports;
1936-
let mut add_module_import = |module: String, name: &str, actual: &str| {
1937-
let rename = if name == actual {
1938-
None
1939-
} else {
1940-
Some(actual.to_string())
1941-
};
1942-
js_imports
1943-
.entry(module)
1944-
.or_insert(Vec::new())
1945-
.push((name.to_string(), rename));
1946-
};
1947-
19481947
let mut name = match &import.name {
19491948
JsImportName::Module { module, name } => {
1950-
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
1951-
add_module_import(module.clone(), name, &unique_name);
1949+
let unique_name = self.generate_identifier(name);
1950+
self.add_module_import(module.clone(), name, &unique_name);
19521951
unique_name
19531952
}
19541953

19551954
JsImportName::LocalModule { module, name } => {
1956-
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
1955+
let unique_name = self.generate_identifier(name);
19571956
let module = self.config.local_module_name(module);
1958-
add_module_import(module, name, &unique_name);
1957+
self.add_module_import(module, name, &unique_name);
19591958
unique_name
19601959
}
19611960

@@ -1967,8 +1966,8 @@ impl<'a> Context<'a> {
19671966
let module = self
19681967
.config
19691968
.inline_js_module_name(unique_crate_identifier, *snippet_idx_in_crate);
1970-
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
1971-
add_module_import(module, name, &unique_name);
1969+
let unique_name = self.generate_identifier(name);
1970+
self.add_module_import(module, name, &unique_name);
19721971
unique_name
19731972
}
19741973

@@ -1998,7 +1997,7 @@ impl<'a> Context<'a> {
19981997
}
19991998

20001999
JsImportName::Global { name } => {
2001-
let unique_name = generate_identifier(name, &mut self.defined_identifiers);
2000+
let unique_name = self.generate_identifier(name);
20022001
if unique_name != *name {
20032002
bail!("cannot import `{}` from two locations", name);
20042003
}
@@ -2055,6 +2054,7 @@ impl<'a> Context<'a> {
20552054
}
20562055

20572056
pub fn generate(&mut self) -> Result<(), Error> {
2057+
self.prestore_global_import_identifiers()?;
20582058
for (id, adapter) in crate::sorted_iter(&self.wit.adapters) {
20592059
let instrs = match &adapter.kind {
20602060
AdapterKind::Import { .. } => continue,
@@ -2084,6 +2084,29 @@ impl<'a> Context<'a> {
20842084
Ok(())
20852085
}
20862086

2087+
/// Registers import names for all `Global` imports first before we actually
2088+
/// process any adapters.
2089+
///
2090+
/// `Global` names must be imported as their exact name, so if the same name
2091+
/// from a global is also imported from a module we have to be sure to
2092+
/// import the global first to ensure we don't shadow the actual global
2093+
/// value. Otherwise we have no way of accessing the global value!
2094+
///
2095+
/// This function will iterate through the import map up-front and generate
2096+
/// a cache entry for each import name which is a `Global`.
2097+
fn prestore_global_import_identifiers(&mut self) -> Result<(), Error> {
2098+
for import in self.aux.import_map.values() {
2099+
let js = match import {
2100+
AuxImport::Value(AuxValue::Bare(js)) => js,
2101+
_ => continue,
2102+
};
2103+
if let JsImportName::Global { .. } = js.name {
2104+
self.import_name(js)?;
2105+
}
2106+
}
2107+
Ok(())
2108+
}
2109+
20872110
fn generate_adapter(
20882111
&mut self,
20892112
id: AdapterId,
@@ -3133,6 +3156,21 @@ impl<'a> Context<'a> {
31333156
fn adapter_name(&self, id: AdapterId) -> String {
31343157
format!("__wbg_adapter_{}", id.0)
31353158
}
3159+
3160+
fn generate_identifier(&mut self, name: &str) -> String {
3161+
let cnt = self
3162+
.defined_identifiers
3163+
.entry(name.to_string())
3164+
.or_insert(0);
3165+
*cnt += 1;
3166+
// We want to mangle `default` at once, so we can support default exports and don't generate
3167+
// invalid glue code like this: `import { default } from './module';`.
3168+
if *cnt == 1 && name != "default" {
3169+
name.to_string()
3170+
} else {
3171+
format!("{}{}", name, cnt)
3172+
}
3173+
}
31363174
}
31373175

31383176
fn check_duplicated_getter_and_setter_names(
@@ -3180,18 +3218,6 @@ fn check_duplicated_getter_and_setter_names(
31803218
Ok(())
31813219
}
31823220

3183-
fn generate_identifier(name: &str, used_names: &mut HashMap<String, usize>) -> String {
3184-
let cnt = used_names.entry(name.to_string()).or_insert(0);
3185-
*cnt += 1;
3186-
// We want to mangle `default` at once, so we can support default exports and don't generate
3187-
// invalid glue code like this: `import { default } from './module';`.
3188-
if *cnt == 1 && name != "default" {
3189-
name.to_string()
3190-
} else {
3191-
format!("{}{}", name, cnt)
3192-
}
3193-
}
3194-
31953221
fn format_doc_comments(comments: &str, js_doc_comments: Option<String>) -> String {
31963222
let body: String = comments.lines().map(|c| format!("*{}\n", c)).collect();
31973223
let doc = if let Some(docs) = js_doc_comments {
@@ -3294,27 +3320,6 @@ impl ExportedClass {
32943320
}
32953321
}
32963322

3297-
#[test]
3298-
fn test_generate_identifier() {
3299-
let mut used_names: HashMap<String, usize> = HashMap::new();
3300-
assert_eq!(
3301-
generate_identifier("someVar", &mut used_names),
3302-
"someVar".to_string()
3303-
);
3304-
assert_eq!(
3305-
generate_identifier("someVar", &mut used_names),
3306-
"someVar2".to_string()
3307-
);
3308-
assert_eq!(
3309-
generate_identifier("default", &mut used_names),
3310-
"default1".to_string()
3311-
);
3312-
assert_eq!(
3313-
generate_identifier("default", &mut used_names),
3314-
"default2".to_string()
3315-
);
3316-
}
3317-
33183323
struct MemView {
33193324
name: &'static str,
33203325
num: usize,

crates/cli/tests/wasm-bindgen/main.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,34 @@ fn works_on_empty_project() {
135135
cmd.assert().success();
136136
}
137137

138+
#[test]
139+
fn namespace_global_and_noglobal_works() {
140+
let (mut cmd, _out_dir) = Project::new("namespace_global_and_noglobal_works")
141+
.file(
142+
"src/lib.rs",
143+
r#"
144+
use wasm_bindgen::prelude::*;
145+
#[wasm_bindgen(module = "fs")]
146+
extern "C" {
147+
#[wasm_bindgen(js_namespace = window)]
148+
fn t1();
149+
}
150+
#[wasm_bindgen]
151+
extern "C" {
152+
#[wasm_bindgen(js_namespace = window)]
153+
fn t2();
154+
}
155+
#[wasm_bindgen]
156+
pub fn test() {
157+
t1();
158+
t2();
159+
}
160+
"#,
161+
)
162+
.wasm_bindgen("");
163+
cmd.assert().success();
164+
}
165+
138166
mod npm;
139167

140168
#[test]

tests/wasm/imports.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,13 @@ exports.receive_some = val => {
127127
};
128128

129129
exports.get_some_val = () => VAL;
130+
131+
exports.Math = {
132+
func_from_module_math: (a) => a * 2
133+
}
134+
135+
exports.same_name_from_import = (a) => a * 3;
136+
137+
exports.same_js_namespace_from_module = {
138+
func_from_module_1_same_js_namespace: (a) => a * 5
139+
}

tests/wasm/imports.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,32 @@ extern "C" {
6969
fn receive_some_ref(arg: Option<&PassOutOptionUndefined>);
7070
#[wasm_bindgen(js_name = "receive_some")]
7171
fn receive_some_owned(arg: Option<PassOutOptionUndefined>);
72+
73+
#[wasm_bindgen(js_namespace = Math)]
74+
fn func_from_module_math(a: i32) -> i32;
75+
76+
#[wasm_bindgen(js_name = "same_name_from_import")]
77+
fn same_name_from_import_1(s: i32) -> i32;
78+
79+
#[wasm_bindgen(js_namespace = same_js_namespace_from_module)]
80+
fn func_from_module_1_same_js_namespace(s: i32) -> i32;
81+
}
82+
83+
#[wasm_bindgen(module = "tests/wasm/imports_2.js")]
84+
extern "C" {
85+
#[wasm_bindgen(js_name = "same_name_from_import")]
86+
fn same_name_from_import_2(s: i32) -> i32;
87+
88+
#[wasm_bindgen(js_namespace = same_js_namespace_from_module)]
89+
fn func_from_module_2_same_js_namespace(s: i32) -> i32;
7290
}
7391

7492
#[wasm_bindgen]
7593
extern "C" {
7694
fn parseInt(a: &str) -> u32;
95+
96+
#[wasm_bindgen(js_namespace = Math, js_name = "sqrt")]
97+
fn func_from_global_math(s: f64) -> f64;
7798
}
7899

79100
#[wasm_bindgen_test]
@@ -274,3 +295,21 @@ fn pass_out_options_as_undefined() {
274295
receive_some_owned(Some(v.clone()));
275296
receive_some_owned(Some(v));
276297
}
298+
299+
#[wasm_bindgen_test]
300+
fn func_from_global_and_module_same_js_namespace() {
301+
assert_eq!(func_from_global_math(4.0), 2.0);
302+
assert_eq!(func_from_module_math(2), 4);
303+
}
304+
305+
#[wasm_bindgen_test]
306+
fn func_from_two_modules_same_js_name() {
307+
assert_eq!(same_name_from_import_1(1), 3);
308+
assert_eq!(same_name_from_import_2(1), 4);
309+
}
310+
311+
#[wasm_bindgen_test]
312+
fn func_from_two_modules_same_js_namespace() {
313+
assert_eq!(func_from_module_1_same_js_namespace(2), 10);
314+
assert_eq!(func_from_module_2_same_js_namespace(2), 12);
315+
}

tests/wasm/imports_2.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
exports.same_name_from_import = (a) => a * 4;
2+
3+
exports.same_js_namespace_from_module = {
4+
func_from_module_2_same_js_namespace: (a) => a * 6
5+
}

0 commit comments

Comments
 (0)