Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@
- Store them next to the output file.
- Pass an environment variable from a `build.rs` pointing to the target folder and go from there.
This seems to have failed. No build script instruction can reach the linker on Wasm.
- Deterministic output of JS files.

# Medium Priority

- Provide an absolutely minimal allocator.
- The `js_sys` proc-macro should remove the `extern "C" { ... }` part of the input on error to avoid
triggering the `unsafe` requirement downstream.
- Optimize linker file interactions by using memory mapped files instead of reading and writing
everything into memory.
- Run the assembly compiler on the proc-macro level so users see errors without having to engage the
linker.
- Parse the JS on the proc-macro level so users see errors. E.g. `oxc-parser`.
Expand Down
1 change: 1 addition & 0 deletions host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ anyhow = "1"
hashbrown = "0.16"
itertools = { version = "0.14", default-features = false }
js-bindgen-shared = { path = "shared" }
memmap2 = "0.9"
object = { version = "0.38", default-features = false, features = ["archive", "read_core", "std"] }
prettyplease = "0.2"
proc-macro2 = { version = "1", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions host/ld/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ rust-version = "1.91"
[dependencies]
hashbrown = { workspace = true }
itertools = { workspace = true }
memmap2 = { workspace = true }
object = { workspace = true }
wasm-encoder = { workspace = true }
wasmparser = { workspace = true }
Expand Down
22 changes: 17 additions & 5 deletions host/ld/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ mod wasm_ld;
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::fmt::Write as _;
use std::fs::File;
use std::io::{Error, Write as _};
use std::path::Path;
use std::process::{self, Command, Stdio};
use std::{env, fs};

use hashbrown::{HashMap, HashSet};
use itertools::{Itertools, Position};
use memmap2::Mmap;
use object::read::archive::ArchiveFile;
use wasm_encoder::{EntityType, ImportSection, Module, RawSection, Section};
use wasmparser::{CustomSectionReader, Encoding, Parser, Payload, TypeRef};
Expand Down Expand Up @@ -91,7 +93,7 @@ fn main() {
// We found a UNIX archive.
if input.as_encoded_bytes().ends_with(b".rlib") {
let archive_path = Path::new(&input);
let archive_data = match fs::read(archive_path) {
let archive_data = match map_file(archive_path) {
Ok(archive_data) => archive_data,
Err(error) => {
eprintln!(
Expand Down Expand Up @@ -153,7 +155,7 @@ fn main() {
}
} else if input.as_encoded_bytes().ends_with(b".o") {
let object_path = Path::new(&input);
let object = match fs::read(object_path) {
let object = match map_file(object_path) {
Ok(object) => object,
Err(error) => {
eprintln!(
Expand Down Expand Up @@ -298,7 +300,7 @@ fn post_processing(output_path: &Path, main_memory: MainMemory<'_>) {
// Unfortunately we don't receive the final output path adjustments Cargo makes.
// So for the JS file we just figure it out ourselves.
let package = env::var_os("CARGO_CRATE_NAME").expect("`CARGO_CRATE_NAME` should be present");
let wasm_input = fs::read(output_path).expect("output file should be readable");
let wasm_input = map_file(output_path).expect("output file should be readable");
let mut wasm_output = Vec::new();

let mut found_import: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
Expand Down Expand Up @@ -528,8 +530,6 @@ fn post_processing(output_path: &Path, main_memory: MainMemory<'_>) {
"missing JS embed: {expected_embed:?}"
);

fs::write(output_path, wasm_output).expect("output Wasm file should be writable");

let mut js_output = String::new();

// Create our `WebAssembly.Memory`.
Expand Down Expand Up @@ -612,13 +612,25 @@ fn post_processing(output_path: &Path, main_memory: MainMemory<'_>) {

js_output.push_str("}\n");

// Due to the limitations of mmap, writing back to the original file is actually dangerous,
// and we need to ensure that we will no longer read from it.
//
// Should we need to write to a new file, such as `package.bg.wasm`?
fs::write(output_path, wasm_output).expect("output Wasm file should be writable");

fs::write(
output_path.with_file_name(package).with_extension("js"),
js_output,
)
.expect("output JS file should be writable");
}

fn map_file(path: &Path) -> Result<Mmap, Error> {
let file = File::open(path)?;
// Safety: the file is not mutated while the mapping is in use.
unsafe { Mmap::map(&file) }
}

struct CustomSectionParser<'cs> {
name: &'cs str,
data: &'cs [u8],
Expand Down