Skip to content
Merged
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: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/bindgen-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ pub use ns::Ns;
/// `export` means I'm exporting functions to be called, and `import` means I'm
/// importing functions that I'm going to call, in both wasm modules and host
/// code. The enum here represents this user perspective.
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub enum Direction {
#[default]
Import,
Export,
}
Expand Down
3 changes: 3 additions & 0 deletions crates/gen-guest-c/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ test = false

[dependencies]
wit-bindgen-core = { workspace = true }
wit-component = { workspace = true }
wasm-encoder = { workspace = true }
anyhow = { workspace = true }
heck = { workspace = true }
clap = { workspace = true, optional = true }

Expand Down
71 changes: 71 additions & 0 deletions crates/gen-guest-c/src/component_type_object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use anyhow::{Context, Result};
use heck::SnakeCase;
use wasm_encoder::{
CodeSection, CustomSection, Encode, Function, FunctionSection, Module, TypeSection,
};
use wit_bindgen_core::{wit_parser::Interface, Direction};
use wit_component::InterfaceEncoder;

pub fn linking_symbol(iface: &Interface, direction: Direction) -> String {
format!(
"__component_type_object_force_link_{}_{}",
iface.name.to_snake_case(),
match direction {
Direction::Import => "import",
Direction::Export => "export",
}
)
}

pub fn object(iface: &Interface, direction: Direction) -> Result<Vec<u8>> {
let mut module = Module::new();

// Build a module with one function that's a "dummy function"
let mut types = TypeSection::new();
types.function([], []);
module.section(&types);
let mut funcs = FunctionSection::new();
funcs.function(0);
module.section(&funcs);
let mut code = CodeSection::new();
code.function(&Function::new([]));
module.section(&code);

let name = format!(
"component-type:{}:{}",
match direction {
Direction::Import => "import",
Direction::Export => "export",
},
iface.name
);
let data = InterfaceEncoder::new(iface)
.encode()
.with_context(|| format!("translating interface {} to component type", iface.name))?;
// Add our custom section
module.section(&CustomSection {
name: &name,
data: data.as_slice(),
});

// Append the `.linking` section
let mut data = Vec::new();
data.push(0x02); // version 2
{
let mut subsection = Vec::<u8>::new();
subsection.push(0x01); // syminfo count
subsection.push(0x00); // SYMTAB_FUNCTION
0u32.encode(&mut subsection); // flags
0u32.encode(&mut subsection); // index
linking_symbol(iface, direction).encode(&mut subsection); // name

data.push(0x08); // `WASM_SYMBOL_TABLE`
subsection.encode(&mut data);
}
module.section(&CustomSection {
name: "linking",
data: &data,
});

Ok(module.finish())
}
20 changes: 20 additions & 0 deletions crates/gen-guest-c/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod component_type_object;

use heck::*;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Write;
Expand Down Expand Up @@ -34,6 +36,8 @@ pub struct C {
types: HashMap<TypeId, wit_bindgen_core::Source>,

needs_string: bool,

direction: Direction,
}

struct Func {
Expand Down Expand Up @@ -700,6 +704,7 @@ impl Return {

impl Generator for C {
fn preprocess_one(&mut self, iface: &Interface, dir: Direction) {
self.direction = dir;
let variant = Self::abi_variant(dir);
self.sizes.fill(iface);
self.in_import = variant == AbiVariant::GuestImport;
Expand Down Expand Up @@ -1156,11 +1161,20 @@ impl Generator for C {
",
iface.name.to_shouty_snake_case(),
);
let linking_symbol = component_type_object::linking_symbol(iface, self.direction);
uwrite!(
self.src.c,
"\
#include <stdlib.h>
#include <{}.h>

// The following symbols are never called, but they are sufficient
// to get the custom sections in the component type object linked
// into the wasm when this compilation unit is linked.
extern void {linking_symbol}(void);
void {linking_symbol}_public_use_in_this_compilation_unit(void) {{
{linking_symbol}();
}}
",
iface.name.to_kebab_case(),
);
Expand Down Expand Up @@ -1289,6 +1303,12 @@ impl Generator for C {
&format!("{}.h", iface.name.to_kebab_case()),
self.src.h.as_bytes(),
);
files.push(
&format!("{}_component_type.o", iface.name.to_kebab_case()),
component_type_object::object(iface, self.direction)
.unwrap()
.as_slice(),
);
}
}

Expand Down
53 changes: 47 additions & 6 deletions crates/test-helpers/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ fn main() {
.adapter_file(&wasi_adapter)
.expect("adapter failed to get loaded")
.encode()
.expect("module can be translated to a component");
.expect(&format!(
"module {:?} can be translated to a component",
file
));

let dep_file = file.with_extension("d");
let deps = fs::read_to_string(&dep_file).expect("failed to read dep file");
Expand Down Expand Up @@ -118,7 +121,9 @@ fn main() {
cmd.arg("--sysroot").arg(path.join("share/wasi-sysroot"));
cmd.arg(c_impl)
.arg(out_dir.join("imports.c"))
.arg(out_dir.join("imports_component_type.o"))
.arg(out_dir.join("exports.c"))
.arg(out_dir.join("exports_component_type.o"))
.arg("-I")
.arg(&out_dir)
.arg("-Wall")
Expand Down Expand Up @@ -149,6 +154,22 @@ fn main() {
test_dir.file_stem().unwrap().to_str().unwrap().to_string(),
out_wasm.to_str().unwrap().to_string(),
));

// Validate that the module can be translated to a component, using
// the component-type custom sections. We don't yet consume this component
// anywhere.
let module = fs::read(&out_wasm).expect("failed to read wasm file");
ComponentEncoder::default()
.module(module.as_slice())
.expect("pull custom sections from module")
.validate(true)
.adapter_file(&wasi_adapter)
.expect("adapter failed to get loaded")
.encode()
.expect(&format!(
"module {:?} can be translated to a component",
out_wasm
));
}
}

Expand Down Expand Up @@ -230,15 +251,35 @@ fn main() {
panic!("failed to build");
}

let out_wasm = out_dir.join("target/generated/wasm/teavm-wasm/classes.wasm");

wasms.push((
"java",
test_dir.file_stem().unwrap().to_str().unwrap().to_string(),
out_dir
.join("target/generated/wasm/teavm-wasm/classes.wasm")
.to_str()
.unwrap()
.to_string(),
out_wasm.to_str().unwrap().to_string(),
));

let imports = [Interface::parse_file(test_dir.join("imports.wit")).unwrap()];
let interface = Interface::parse_file(test_dir.join("exports.wit")).unwrap();

// Validate that the module can be translated to a component, using
// wit interfaces explicitly passed to ComponentEncoder, because the
// TeaVM guest doesnt yet support putting component types into custom
// sections.
let module = fs::read(&out_wasm).expect("failed to read wasm file");
ComponentEncoder::default()
.imports(&imports)
.interface(&interface)
.module(module.as_slice())
.expect("pull custom sections from module")
.validate(true)
.adapter_file(&wasi_adapter)
.expect("adapter failed to get loaded")
.encode()
.expect(&format!(
"module {:?} can be translated to a component",
out_wasm
));
}
}

Expand Down
29 changes: 29 additions & 0 deletions crates/wasi_snapshot_preview1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@ pub extern "C" fn environ_sizes_get(environc: *mut Size, environ_buf_size: *mut
unreachable()
}

#[no_mangle]
pub extern "C" fn args_get(args: *mut *mut u8, args_buf: *mut u8) -> Errno {
unreachable()
}

#[no_mangle]
pub extern "C" fn args_sizes_get(argc: *mut Size, arg_buf_size: *mut Size) -> Errno {
unreachable()
}

#[no_mangle]
pub extern "C" fn clock_time_get(
clockid: Clockid,
precision: Timestamp,
out: *mut Timestamp,
) -> Errno {
unreachable()
}

#[no_mangle]
pub extern "C" fn fd_write(
fd: Fd,
Expand All @@ -38,6 +57,16 @@ pub extern "C" fn fd_write(
unreachable()
}

#[no_mangle]
pub extern "C" fn fd_seek(fd: Fd, offset: Filedelta, whence: Whence, filesize: *mut Size) -> Errno {
unreachable()
}

#[no_mangle]
pub extern "C" fn fd_close(fd: Fd) -> Errno {
unreachable()
}

#[no_mangle]
pub extern "C" fn proc_exit(rval: Exitcode) -> ! {
unreachable()
Expand Down