Skip to content

Commit b7018c3

Browse files
YufJixusd320
andauthored
fix: load wasm (#1705)
* fix: 修复load .wasm文件对importObject的处理 * fix: 删除没必要的输出 * fix: 修改生成的成员表达式js代码 * fix: 变量重命名 * fix: 修复代码lint报错 * test: 补充wasm_runtime测试用例 * chore: 补充import js方式的示例 * chore: 修改import js示例wasm产物格式 * chore: wasmparser依赖包在配置文件的位置 * chore: 删除多余的.wasm load逻辑 --------- Co-authored-by: xusd320 <[email protected]>
1 parent 55b59a4 commit b7018c3

File tree

16 files changed

+571
-23
lines changed

16 files changed

+571
-23
lines changed

Cargo.lock

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/mako/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ tracing = "0.1.37"
114114
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
115115
tungstenite = "0.19.0"
116116
twox-hash = "1.6.3"
117+
wasmparser = "0.207.0"
117118

118119
[dev-dependencies]
119120
insta = { version = "1.30.0", features = ["yaml"] }

crates/mako/src/build/load.rs

-22
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ const CSS_EXTENSIONS: [&str; 1] = ["css"];
3636
const JSON_EXTENSIONS: [&str; 2] = ["json", "json5"];
3737
const YAML_EXTENSIONS: [&str; 2] = ["yaml", "yml"];
3838
const XML_EXTENSIONS: [&str; 1] = ["xml"];
39-
const WASM_EXTENSIONS: [&str; 1] = ["wasm"];
4039
const TOML_EXTENSIONS: [&str; 1] = ["toml"];
4140
const SVG_EXTENSIONS: [&str; 1] = ["svg"];
4241
const MD_EXTENSIONS: [&str; 2] = ["md", "mdx"];
@@ -180,27 +179,6 @@ export function moduleToDom(css) {
180179
}));
181180
}
182181

183-
// wasm
184-
if WASM_EXTENSIONS.contains(&file.extname.as_str()) {
185-
let final_file_name = format!(
186-
"{}.{}.{}",
187-
file.get_file_stem(),
188-
file.get_content_hash()?,
189-
file.extname
190-
);
191-
context.emit_assets(
192-
file.pathname.to_string_lossy().to_string(),
193-
final_file_name.clone(),
194-
);
195-
return Ok(Content::Js(JsContent {
196-
content: format!(
197-
"module.exports = require._interopreRequireWasm(exports, \"{}\")",
198-
final_file_name
199-
),
200-
..Default::default()
201-
}));
202-
}
203-
204182
// xml
205183
if XML_EXTENSIONS.contains(&file.extname.as_str()) {
206184
let content = FileSystem::read_file(&file.pathname)?;

crates/mako/src/plugins/wasm_runtime.rs

+143-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
use std::collections::HashMap;
2+
use std::fs::File;
3+
use std::io::Read;
14
use std::sync::Arc;
25

36
use anyhow;
7+
use wasmparser::{Import, Parser, Payload};
48

9+
use crate::ast::file::{Content, JsContent};
510
use crate::compiler::Context;
6-
use crate::plugin::Plugin;
11+
use crate::plugin::{Plugin, PluginLoadParam};
712

813
pub struct WasmRuntimePlugin {}
914

15+
const WASM_EXTENSIONS: [&str; 1] = ["wasm"];
16+
1017
impl Plugin for WasmRuntimePlugin {
1118
fn name(&self) -> &str {
1219
"wasm_runtime"
@@ -27,4 +34,139 @@ impl Plugin for WasmRuntimePlugin {
2734
Ok(vec![])
2835
}
2936
}
37+
38+
fn load(
39+
&self,
40+
param: &PluginLoadParam,
41+
_context: &Arc<Context>,
42+
) -> anyhow::Result<Option<Content>> {
43+
let file = param.file;
44+
45+
if WASM_EXTENSIONS.contains(&file.extname.as_str()) {
46+
let final_file_name = format!(
47+
"{}.{}.{}",
48+
file.get_file_stem(),
49+
file.get_content_hash()?,
50+
file.extname
51+
);
52+
_context.emit_assets(
53+
file.pathname.to_string_lossy().to_string(),
54+
final_file_name.clone(),
55+
);
56+
57+
let mut buffer = Vec::new();
58+
File::open(&file.path)?.read_to_end(&mut buffer)?;
59+
// Parse wasm file to get imports
60+
let mut wasm_import_object_map: HashMap<&str, Vec<String>> = HashMap::new();
61+
Parser::new(0).parse_all(&buffer).for_each(|payload| {
62+
if let Ok(Payload::ImportSection(imports)) = payload {
63+
imports.into_iter_with_offsets().for_each(|import| {
64+
if let Ok((
65+
_,
66+
Import {
67+
module,
68+
name,
69+
ty: _,
70+
},
71+
)) = import
72+
{
73+
if let Some(import_object) = wasm_import_object_map.get_mut(module) {
74+
import_object.push(name.to_string());
75+
} else {
76+
wasm_import_object_map.insert(module, vec![name.to_string()]);
77+
}
78+
}
79+
});
80+
}
81+
});
82+
83+
let mut module_import_code = String::new();
84+
let mut wasm_import_object_code = String::new();
85+
86+
for (index, (key, value)) in wasm_import_object_map.iter().enumerate() {
87+
module_import_code.push_str(&format!(
88+
"import * as module{module_idx} from \"{module}\";\n",
89+
module_idx = index,
90+
module = key
91+
));
92+
93+
wasm_import_object_code.push_str(&format!(
94+
"\"{module}\": {{ {names} }}",
95+
module = key,
96+
names = value
97+
.iter()
98+
.map(|name| format!("\"{}\": module{}[\"{}\"]", name, index, name))
99+
.collect::<Vec<String>>()
100+
.join(", ")
101+
));
102+
}
103+
104+
let mut content = String::new();
105+
content.push_str(&module_import_code);
106+
107+
if wasm_import_object_code.is_empty() {
108+
content.push_str(&format!(
109+
"module.exports = require._interopreRequireWasm(exports, \"{}\")",
110+
final_file_name
111+
));
112+
} else {
113+
content.push_str(&format!(
114+
"module.exports = require._interopreRequireWasm(exports, \"{}\", {{{}}})",
115+
final_file_name, wasm_import_object_code
116+
));
117+
}
118+
119+
return Ok(Some(Content::Js(JsContent {
120+
content,
121+
..Default::default()
122+
})));
123+
}
124+
125+
Ok(None)
126+
}
127+
}
128+
129+
#[cfg(test)]
130+
mod tests {
131+
use std::sync::Arc;
132+
133+
use super::*;
134+
use crate::ast::file::File;
135+
use crate::compiler::Context;
136+
137+
#[test]
138+
fn test_wasm_runtime_load_with_import_object() {
139+
let plugin = WasmRuntimePlugin {};
140+
let context = Arc::new(Context {
141+
..Default::default()
142+
});
143+
let wasm_relative_path =
144+
std::path::Path::new("../../examples/import-resources/minus-wasm-pack/index_bg.wasm");
145+
let wasm_path = std::fs::canonicalize(wasm_relative_path).unwrap();
146+
let file = File::new(wasm_path.to_string_lossy().to_string(), context.clone());
147+
let param = PluginLoadParam { file: &file };
148+
let result: Option<Content> = plugin.load(&param, &context).unwrap();
149+
150+
assert!(result.is_some());
151+
if let Some(Content::Js(js_content)) = result {
152+
assert!(js_content.content.contains("import * as module0 from"));
153+
}
154+
}
155+
156+
#[test]
157+
fn test_wasm_runtime_load_without_import_object() {
158+
let plugin = WasmRuntimePlugin {};
159+
let context = Arc::new(Context {
160+
..Default::default()
161+
});
162+
let wasm_relative_path = std::path::Path::new("../../examples/import-resources/add.wasm");
163+
let wasm_path = std::fs::canonicalize(wasm_relative_path).unwrap();
164+
let file = File::new(wasm_path.to_string_lossy().to_string(), context.clone());
165+
let param = PluginLoadParam { file: &file };
166+
let result = plugin.load(&param, &context).unwrap();
167+
assert!(result.is_some());
168+
if let Some(Content::Js(js_content)) = result {
169+
assert!(!js_content.content.contains("import * as module0 from"))
170+
}
171+
}
30172
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
export function run(): void;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import * as wasm from './index_bg.wasm';
2+
export * from './index_bg.js';
3+
import { __wbg_set_wasm } from './index_bg.js';
4+
__wbg_set_wasm(wasm);
5+
wasm.__wbindgen_start();

0 commit comments

Comments
 (0)