forked from denoland/deno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loader: support .wasm imports (denoland#3328)
* loader: support .wasm imports * http_server: true * Support named exports * Clippy
- Loading branch information
1 parent
fdf0ede
commit 4189cc1
Showing
20 changed files
with
388 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
cli/compilers/wasm_wrap.js | ||
cli/tests/error_syntax.js | ||
std/deno.d.ts | ||
std/prettier/vendor | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. | ||
use crate::compilers::CompiledModule; | ||
use crate::compilers::CompiledModuleFuture; | ||
use crate::file_fetcher::SourceFile; | ||
use crate::global_state::ThreadSafeGlobalState; | ||
use crate::startup_data; | ||
use crate::state::*; | ||
use crate::worker::Worker; | ||
use deno::Buf; | ||
use futures::Future; | ||
use futures::IntoFuture; | ||
use serde_derive::Deserialize; | ||
use serde_json; | ||
use std::collections::HashMap; | ||
use std::sync::atomic::Ordering; | ||
use std::sync::{Arc, Mutex}; | ||
use url::Url; | ||
|
||
// TODO(kevinkassimo): This is a hack to encode/decode data as base64 string. | ||
// (Since Deno namespace might not be available, Deno.read can fail). | ||
// Binary data is already available through source_file.source_code. | ||
// If this is proven too wasteful in practice, refactor this. | ||
|
||
// Ref: https://webassembly.github.io/esm-integration/js-api/index.html#esm-integration | ||
// https://github.com/nodejs/node/blob/35ec01097b2a397ad0a22aac536fe07514876e21/lib/internal/modules/esm/translators.js#L190-L210 | ||
|
||
// Dynamically construct JS wrapper with custom static imports and named exports. | ||
// Boots up an internal worker to resolve imports/exports through query from V8. | ||
|
||
static WASM_WRAP: &str = include_str!("./wasm_wrap.js"); | ||
|
||
#[derive(Deserialize, Debug)] | ||
#[serde(rename_all = "camelCase")] | ||
struct WasmModuleInfo { | ||
import_list: Vec<String>, | ||
export_list: Vec<String>, | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct WasmCompiler { | ||
cache: Arc<Mutex<HashMap<Url, CompiledModule>>>, | ||
} | ||
|
||
impl WasmCompiler { | ||
/// Create a new V8 worker with snapshot of WASM compiler and setup compiler's runtime. | ||
fn setup_worker(global_state: ThreadSafeGlobalState) -> Worker { | ||
let (int, ext) = ThreadSafeState::create_channels(); | ||
let worker_state = | ||
ThreadSafeState::new(global_state.clone(), None, true, int) | ||
.expect("Unable to create worker state"); | ||
|
||
// Count how many times we start the compiler worker. | ||
global_state | ||
.metrics | ||
.compiler_starts | ||
.fetch_add(1, Ordering::SeqCst); | ||
|
||
let mut worker = Worker::new( | ||
"WASM".to_string(), | ||
startup_data::compiler_isolate_init(), | ||
worker_state, | ||
ext, | ||
); | ||
worker.execute("denoMain('WASM')").unwrap(); | ||
worker.execute("workerMain()").unwrap(); | ||
worker.execute("wasmCompilerMain()").unwrap(); | ||
worker | ||
} | ||
|
||
pub fn compile_async( | ||
self: &Self, | ||
global_state: ThreadSafeGlobalState, | ||
source_file: &SourceFile, | ||
) -> Box<CompiledModuleFuture> { | ||
let cache = self.cache.clone(); | ||
let maybe_cached = { cache.lock().unwrap().get(&source_file.url).cloned() }; | ||
if let Some(m) = maybe_cached { | ||
return Box::new(futures::future::ok(m.clone())); | ||
} | ||
let cache_ = self.cache.clone(); | ||
|
||
debug!(">>>>> wasm_compile_async START"); | ||
let base64_data = base64::encode(&source_file.source_code); | ||
let worker = WasmCompiler::setup_worker(global_state.clone()); | ||
let worker_ = worker.clone(); | ||
let url = source_file.url.clone(); | ||
|
||
let fut = worker | ||
.post_message( | ||
serde_json::to_string(&base64_data) | ||
.unwrap() | ||
.into_boxed_str() | ||
.into_boxed_bytes(), | ||
) | ||
.into_future() | ||
.then(move |_| worker) | ||
.then(move |result| { | ||
if let Err(err) = result { | ||
// TODO(ry) Need to forward the error instead of exiting. | ||
eprintln!("{}", err.to_string()); | ||
std::process::exit(1); | ||
} | ||
debug!("Sent message to worker"); | ||
worker_.get_message() | ||
}) | ||
.map_err(|_| panic!("not handled")) | ||
.and_then(move |maybe_msg: Option<Buf>| { | ||
debug!("Received message from worker"); | ||
let json_msg = maybe_msg.unwrap(); | ||
let module_info: WasmModuleInfo = | ||
serde_json::from_slice(&json_msg).unwrap(); | ||
debug!("WASM module info: {:#?}", &module_info); | ||
let code = wrap_wasm_code( | ||
&base64_data, | ||
&module_info.import_list, | ||
&module_info.export_list, | ||
); | ||
debug!("Generated code: {}", &code); | ||
let module = CompiledModule { | ||
code, | ||
name: url.to_string(), | ||
}; | ||
{ | ||
cache_.lock().unwrap().insert(url.clone(), module.clone()); | ||
} | ||
debug!("<<<<< wasm_compile_async END"); | ||
Ok(module) | ||
}); | ||
Box::new(fut) | ||
} | ||
} | ||
|
||
fn build_single_import(index: usize, origin: &str) -> String { | ||
let origin_json = serde_json::to_string(origin).unwrap(); | ||
format!( | ||
r#"import * as m{} from {}; | ||
importObject[{}] = m{}; | ||
"#, | ||
index, &origin_json, &origin_json, index | ||
) | ||
} | ||
|
||
fn build_imports(imports: &[String]) -> String { | ||
let mut code = String::from(""); | ||
for (index, origin) in imports.iter().enumerate() { | ||
code.push_str(&build_single_import(index, origin)); | ||
} | ||
code | ||
} | ||
|
||
fn build_single_export(name: &str) -> String { | ||
format!("export const {} = instance.exports.{};\n", name, name) | ||
} | ||
|
||
fn build_exports(exports: &[String]) -> String { | ||
let mut code = String::from(""); | ||
for e in exports { | ||
code.push_str(&build_single_export(e)); | ||
} | ||
code | ||
} | ||
|
||
fn wrap_wasm_code( | ||
base64_data: &str, | ||
imports: &[String], | ||
exports: &[String], | ||
) -> String { | ||
let imports_code = build_imports(imports); | ||
let exports_code = build_exports(exports); | ||
String::from(WASM_WRAP) | ||
.replace("//IMPORTS\n", &imports_code) | ||
.replace("//EXPORTS\n", &exports_code) | ||
.replace("BASE64_DATA", base64_data) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
const importObject = Object.create(null); | ||
//IMPORTS | ||
|
||
function base64ToUint8Array(data) { | ||
const binString = window.atob(data); | ||
const size = binString.length; | ||
const bytes = new Uint8Array(size); | ||
for (let i = 0; i < size; i++) { | ||
bytes[i] = binString.charCodeAt(i); | ||
} | ||
return bytes; | ||
} | ||
|
||
const buffer = base64ToUint8Array("BASE64_DATA"); | ||
const compiled = await WebAssembly.compile(buffer); | ||
|
||
const instance = new WebAssembly.Instance(compiled, importObject); | ||
|
||
//EXPORTS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.