Fix some wit-bindgen-related issues with generated bindings (#5692)

* Prefix component-bindgen-generated-functions with `call_`

This fixes clashes between Rust-native methods and the methods
themselves. For example right now `new` is a Rust-generated function for
constructing the wrapper but this can conflict with a world-exported
function called `new`.

Closes #5585

* Fix types being both shared and owned

This refactors some inherited cruft from the original `wit-bindgen`
repository to be more Wasmtime-specific and fixes a codegen case where
a type was used in both a shared and an owned context.

Closes #5688
This commit is contained in:
Alex Crichton
2023-02-02 11:54:35 -06:00
committed by GitHub
parent 63d80fc509
commit 545749b279
7 changed files with 79 additions and 57 deletions

View File

@@ -0,0 +1,3 @@
default world foo {
export new: func()
}

View File

@@ -0,0 +1,19 @@
interface http-types{
record request {
method: string
}
record response {
body: string
}
}
default world http-interface {
export http-handler: interface {
use self.http-types.{request,response}
handle-request: func(request: request) -> response
}
import http-fetch: interface {
use self.http-types.{request,response}
fetch-request: func(request: request) -> response
}
}

View File

@@ -101,7 +101,7 @@ impl Wasmtime {
fn import(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) { fn import(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) {
let snake = name.to_snake_case(); let snake = name.to_snake_case();
let mut gen = InterfaceGenerator::new(self, resolve, TypeMode::Owned); let mut gen = InterfaceGenerator::new(self, resolve);
let import = match item { let import = match item {
WorldItem::Function(func) => { WorldItem::Function(func) => {
gen.generate_function_trait_sig(TypeOwner::None, &func); gen.generate_function_trait_sig(TypeOwner::None, &func);
@@ -139,7 +139,7 @@ impl Wasmtime {
fn export(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) { fn export(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) {
let snake = name.to_snake_case(); let snake = name.to_snake_case();
let mut gen = InterfaceGenerator::new(self, resolve, TypeMode::AllBorrowed("'a")); let mut gen = InterfaceGenerator::new(self, resolve);
let (ty, getter) = match item { let (ty, getter) = match item {
WorldItem::Function(func) => { WorldItem::Function(func) => {
gen.define_rust_guest_export(None, func); gen.define_rust_guest_export(None, func);
@@ -450,21 +450,15 @@ struct InterfaceGenerator<'a> {
src: Source, src: Source,
gen: &'a mut Wasmtime, gen: &'a mut Wasmtime,
resolve: &'a Resolve, resolve: &'a Resolve,
default_param_mode: TypeMode,
current_interface: Option<InterfaceId>, current_interface: Option<InterfaceId>,
} }
impl<'a> InterfaceGenerator<'a> { impl<'a> InterfaceGenerator<'a> {
fn new( fn new(gen: &'a mut Wasmtime, resolve: &'a Resolve) -> InterfaceGenerator<'a> {
gen: &'a mut Wasmtime,
resolve: &'a Resolve,
default_param_mode: TypeMode,
) -> InterfaceGenerator<'a> {
InterfaceGenerator { InterfaceGenerator {
src: Source::default(), src: Source::default(),
gen, gen,
resolve, resolve,
default_param_mode,
current_interface: None, current_interface: None,
} }
} }
@@ -1159,7 +1153,7 @@ impl<'a> InterfaceGenerator<'a> {
self.rustdoc(&func.docs); self.rustdoc(&func.docs);
uwrite!( uwrite!(
self.src, self.src,
"pub {async_} fn {}<S: wasmtime::AsContextMut>(&self, mut store: S, ", "pub {async_} fn call_{}<S: wasmtime::AsContextMut>(&self, mut store: S, ",
func.name.to_snake_case(), func.name.to_snake_case(),
); );
for (i, param) in func.params.iter().enumerate() { for (i, param) in func.params.iter().enumerate() {
@@ -1351,10 +1345,6 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
self.current_interface self.current_interface
} }
fn default_param_mode(&self) -> TypeMode {
self.default_param_mode
}
fn push_str(&mut self, s: &str) { fn push_str(&mut self, s: &str) {
self.src.push_str(s); self.src.push_str(s);
} }

View File

@@ -15,7 +15,6 @@ pub trait RustGenerator<'a> {
fn push_str(&mut self, s: &str); fn push_str(&mut self, s: &str);
fn info(&self, ty: TypeId) -> TypeInfo; fn info(&self, ty: TypeId) -> TypeInfo;
fn default_param_mode(&self) -> TypeMode;
fn current_interface(&self) -> Option<InterfaceId>; fn current_interface(&self) -> Option<InterfaceId>;
fn print_ty(&mut self, ty: &Type, mode: TypeMode) { fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
@@ -209,10 +208,10 @@ pub trait RustGenerator<'a> {
fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> { fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> {
let info = self.info(ty); let info = self.info(ty);
let mut result = Vec::new(); let mut result = Vec::new();
if info.param { if info.borrowed {
result.push((self.param_name(ty), self.default_param_mode())); result.push((self.param_name(ty), TypeMode::AllBorrowed("'a")));
} }
if info.result && (!info.param || self.uses_two_names(&info)) { if info.owned && (!info.borrowed || self.uses_two_names(&info)) {
result.push((self.result_name(ty), TypeMode::Owned)); result.push((self.result_name(ty), TypeMode::Owned));
} }
return result; return result;
@@ -358,13 +357,7 @@ pub trait RustGenerator<'a> {
} }
fn uses_two_names(&self, info: &TypeInfo) -> bool { fn uses_two_names(&self, info: &TypeInfo) -> bool {
info.has_list info.has_list && info.borrowed && info.owned
&& info.param
&& info.result
&& match self.default_param_mode() {
TypeMode::AllBorrowed(_) => true,
TypeMode::Owned => false,
}
} }
fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> { fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> {

View File

@@ -8,13 +8,14 @@ pub struct Types {
#[derive(Default, Clone, Copy, Debug, PartialEq)] #[derive(Default, Clone, Copy, Debug, PartialEq)]
pub struct TypeInfo { pub struct TypeInfo {
/// Whether or not this type is ever used (transitively) within the /// Whether or not this type is ever used (transitively) within a borrowed
/// parameter of a function. /// context, or a parameter to an export function.
pub param: bool, pub borrowed: bool,
/// Whether or not this type is ever used (transitively) within the /// Whether or not this type is ever used (transitively) within an owned
/// result of a function. /// context, such as the result of an exported function or in the params or
pub result: bool, /// results of an imported function.
pub owned: bool,
/// Whether or not this type is ever used (transitively) within the /// Whether or not this type is ever used (transitively) within the
/// error case in the result of a function. /// error case in the result of a function.
@@ -26,8 +27,8 @@ pub struct TypeInfo {
impl std::ops::BitOrAssign for TypeInfo { impl std::ops::BitOrAssign for TypeInfo {
fn bitor_assign(&mut self, rhs: Self) { fn bitor_assign(&mut self, rhs: Self) {
self.param |= rhs.param; self.borrowed |= rhs.borrowed;
self.result |= rhs.result; self.owned |= rhs.owned;
self.error |= rhs.error; self.error |= rhs.error;
self.has_list |= rhs.has_list; self.has_list |= rhs.has_list;
} }
@@ -36,9 +37,14 @@ impl std::ops::BitOrAssign for TypeInfo {
impl Types { impl Types {
pub fn analyze(&mut self, resolve: &Resolve, world: WorldId) { pub fn analyze(&mut self, resolve: &Resolve, world: WorldId) {
let world = &resolve.worlds[world]; let world = &resolve.worlds[world];
for (_, item) in world.imports.iter().chain(world.exports.iter()) { for (import, (_, item)) in world
.imports
.iter()
.map(|i| (true, i))
.chain(world.exports.iter().map(|i| (false, i)))
{
match item { match item {
WorldItem::Function(f) => self.type_info_func(resolve, f), WorldItem::Function(f) => self.type_info_func(resolve, f, import),
WorldItem::Interface(id) => { WorldItem::Interface(id) => {
let iface = &resolve.interfaces[*id]; let iface = &resolve.interfaces[*id];
@@ -46,21 +52,26 @@ impl Types {
self.type_id_info(resolve, *t); self.type_id_info(resolve, *t);
} }
for (_, f) in iface.functions.iter() { for (_, f) in iface.functions.iter() {
self.type_info_func(resolve, f); self.type_info_func(resolve, f, import);
} }
} }
} }
} }
} }
fn type_info_func(&mut self, resolve: &Resolve, func: &Function) { fn type_info_func(&mut self, resolve: &Resolve, func: &Function, import: bool) {
let mut live = LiveTypes::default(); let mut live = LiveTypes::default();
for (_, ty) in func.params.iter() { for (_, ty) in func.params.iter() {
self.type_info(resolve, ty); self.type_info(resolve, ty);
live.add_type(resolve, ty); live.add_type(resolve, ty);
} }
for id in live.iter() { for id in live.iter() {
self.type_info.get_mut(&id).unwrap().param = true; let info = self.type_info.get_mut(&id).unwrap();
if import {
info.owned = true;
} else {
info.borrowed = true;
}
} }
let mut live = LiveTypes::default(); let mut live = LiveTypes::default();
for ty in func.results.iter_types() { for ty in func.results.iter_types() {
@@ -68,7 +79,7 @@ impl Types {
live.add_type(resolve, ty); live.add_type(resolve, ty);
} }
for id in live.iter() { for id in live.iter() {
self.type_info.get_mut(&id).unwrap().result = true; self.type_info.get_mut(&id).unwrap().owned = true;
} }
for ty in func.results.iter_types() { for ty in func.results.iter_types() {

View File

@@ -46,8 +46,8 @@ mod no_imports {
let linker = Linker::new(&engine); let linker = Linker::new(&engine);
let mut store = Store::new(&engine, ()); let mut store = Store::new(&engine, ());
let (no_imports, _) = NoImports::instantiate(&mut store, &component, &linker)?; let (no_imports, _) = NoImports::instantiate(&mut store, &component, &linker)?;
no_imports.bar(&mut store)?; no_imports.call_bar(&mut store)?;
no_imports.foo().foo(&mut store)?; no_imports.foo().call_foo(&mut store)?;
Ok(()) Ok(())
} }
} }
@@ -108,7 +108,7 @@ mod one_import {
foo::add_to_linker(&mut linker, |f: &mut MyImports| f)?; foo::add_to_linker(&mut linker, |f: &mut MyImports| f)?;
let mut store = Store::new(&engine, MyImports::default()); let mut store = Store::new(&engine, MyImports::default());
let (one_import, _) = OneImport::instantiate(&mut store, &component, &linker)?; let (one_import, _) = OneImport::instantiate(&mut store, &component, &linker)?;
one_import.bar(&mut store)?; one_import.call_bar(&mut store)?;
assert!(store.data().hit); assert!(store.data().hit);
Ok(()) Ok(())
} }

View File

@@ -80,19 +80,22 @@ mod empty_error {
assert_eq!( assert_eq!(
results results
.empty_error(&mut store, 0.0) .call_empty_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
0.0 0.0
); );
results results
.empty_error(&mut store, 1.0) .call_empty_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("() error returned"); .expect("() error returned");
let e = results.empty_error(&mut store, 2.0).err().expect("trap"); let e = results
.call_empty_error(&mut store, 2.0)
.err()
.expect("trap");
assert_eq!( assert_eq!(
format!("{}", e.source().expect("trap message is stored in source")), format!("{}", e.source().expect("trap message is stored in source")),
"empty_error: trap" "empty_error: trap"
@@ -188,20 +191,23 @@ mod string_error {
assert_eq!( assert_eq!(
results results
.string_error(&mut store, 0.0) .call_string_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
0.0 0.0
); );
let e = results let e = results
.string_error(&mut store, 1.0) .call_string_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("error returned"); .expect("error returned");
assert_eq!(e, "string_error: error"); assert_eq!(e, "string_error: error");
let e = results.string_error(&mut store, 2.0).err().expect("trap"); let e = results
.call_string_error(&mut store, 2.0)
.err()
.expect("trap");
assert_eq!( assert_eq!(
format!("{}", e.source().expect("trap message is stored in source")), format!("{}", e.source().expect("trap message is stored in source")),
"string_error: trap" "string_error: trap"
@@ -328,7 +334,7 @@ mod enum_error {
assert_eq!( assert_eq!(
results results
.foo() .foo()
.enum_error(&mut store, 0.0) .call_enum_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
0.0 0.0
@@ -336,7 +342,7 @@ mod enum_error {
let e = results let e = results
.foo() .foo()
.enum_error(&mut store, 1.0) .call_enum_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("error returned"); .expect("error returned");
@@ -344,7 +350,7 @@ mod enum_error {
let e = results let e = results
.foo() .foo()
.enum_error(&mut store, 2.0) .call_enum_error(&mut store, 2.0)
.err() .err()
.expect("trap"); .expect("trap");
assert_eq!( assert_eq!(
@@ -458,7 +464,7 @@ mod record_error {
assert_eq!( assert_eq!(
results results
.foo() .foo()
.record_error(&mut store, 0.0) .call_record_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
0.0 0.0
@@ -466,7 +472,7 @@ mod record_error {
let e = results let e = results
.foo() .foo()
.record_error(&mut store, 1.0) .call_record_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("error returned"); .expect("error returned");
@@ -480,7 +486,7 @@ mod record_error {
let e = results let e = results
.foo() .foo()
.record_error(&mut store, 2.0) .call_record_error(&mut store, 2.0)
.err() .err()
.expect("trap"); .expect("trap");
assert_eq!( assert_eq!(
@@ -594,7 +600,7 @@ mod variant_error {
assert_eq!( assert_eq!(
results results
.foo() .foo()
.variant_error(&mut store, 0.0) .call_variant_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
0.0 0.0
@@ -602,7 +608,7 @@ mod variant_error {
let e = results let e = results
.foo() .foo()
.variant_error(&mut store, 1.0) .call_variant_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("error returned"); .expect("error returned");
@@ -616,7 +622,7 @@ mod variant_error {
let e = results let e = results
.foo() .foo()
.variant_error(&mut store, 2.0) .call_variant_error(&mut store, 2.0)
.err() .err()
.expect("trap"); .expect("trap");
assert_eq!( assert_eq!(