Add a wasmtime::component::bindgen! macro (#5317)
* Import Wasmtime support from the `wit-bindgen` repo This commit imports the `wit-bindgen-gen-host-wasmtime-rust` crate from the `wit-bindgen` repository into the upstream Wasmtime repository. I've chosen to not import the full history here since the crate is relatively small and doesn't have a ton of complexity. While the history of the crate is quite long the current iteration of the crate's history is relatively short so there's not a ton of import there anyway. The thinking is that this can now continue to evolve in-tree. * Refactor `wasmtime-component-macro` a bit Make room for a `wit_bindgen` macro to slot in. * Add initial support for a `bindgen` macro * Add tests for `wasmtime::component::bindgen!` * Improve error forgetting `async` feature * Add end-to-end tests for bindgen * Add an audit of `unicase` * Add a license to the test-helpers crate * Add vet entry for `pulldown-cmark` * Update publish script with new crate * Try to fix publish script * Update audits * Update lock file
This commit is contained in:
1143
crates/wit-bindgen/src/lib.rs
Normal file
1143
crates/wit-bindgen/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
412
crates/wit-bindgen/src/rust.rs
Normal file
412
crates/wit-bindgen/src/rust.rs
Normal file
@@ -0,0 +1,412 @@
|
||||
use crate::types::TypeInfo;
|
||||
use heck::*;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use wit_parser::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum TypeMode {
|
||||
Owned,
|
||||
AllBorrowed(&'static str),
|
||||
}
|
||||
|
||||
pub trait RustGenerator<'a> {
|
||||
fn iface(&self) -> &'a Interface;
|
||||
|
||||
fn push_str(&mut self, s: &str);
|
||||
fn info(&self, ty: TypeId) -> TypeInfo;
|
||||
fn default_param_mode(&self) -> TypeMode;
|
||||
|
||||
fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
|
||||
match ty {
|
||||
Type::Id(t) => self.print_tyid(*t, mode),
|
||||
Type::Bool => self.push_str("bool"),
|
||||
Type::U8 => self.push_str("u8"),
|
||||
Type::U16 => self.push_str("u16"),
|
||||
Type::U32 => self.push_str("u32"),
|
||||
Type::U64 => self.push_str("u64"),
|
||||
Type::S8 => self.push_str("i8"),
|
||||
Type::S16 => self.push_str("i16"),
|
||||
Type::S32 => self.push_str("i32"),
|
||||
Type::S64 => self.push_str("i64"),
|
||||
Type::Float32 => self.push_str("f32"),
|
||||
Type::Float64 => self.push_str("f64"),
|
||||
Type::Char => self.push_str("char"),
|
||||
Type::String => match mode {
|
||||
TypeMode::AllBorrowed(lt) => {
|
||||
self.push_str("&");
|
||||
if lt != "'_" {
|
||||
self.push_str(lt);
|
||||
self.push_str(" ");
|
||||
}
|
||||
self.push_str("str");
|
||||
}
|
||||
TypeMode::Owned => self.push_str("String"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) {
|
||||
match ty {
|
||||
Some(ty) => self.print_ty(ty, mode),
|
||||
None => self.push_str("()"),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_tyid(&mut self, id: TypeId, mode: TypeMode) {
|
||||
let info = self.info(id);
|
||||
let lt = self.lifetime_for(&info, mode);
|
||||
let ty = &self.iface().types[id];
|
||||
if ty.name.is_some() {
|
||||
let name = if lt.is_some() {
|
||||
self.param_name(id)
|
||||
} else {
|
||||
self.result_name(id)
|
||||
};
|
||||
self.push_str(&name);
|
||||
|
||||
// If the type recursively owns data and it's a
|
||||
// variant/record/list, then we need to place the
|
||||
// lifetime parameter on the type as well.
|
||||
if info.has_list && needs_generics(self.iface(), &ty.kind) {
|
||||
self.print_generics(lt);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fn needs_generics(iface: &Interface, ty: &TypeDefKind) -> bool {
|
||||
match ty {
|
||||
TypeDefKind::Variant(_)
|
||||
| TypeDefKind::Record(_)
|
||||
| TypeDefKind::Option(_)
|
||||
| TypeDefKind::Result(_)
|
||||
| TypeDefKind::Future(_)
|
||||
| TypeDefKind::Stream(_)
|
||||
| TypeDefKind::List(_)
|
||||
| TypeDefKind::Flags(_)
|
||||
| TypeDefKind::Enum(_)
|
||||
| TypeDefKind::Tuple(_)
|
||||
| TypeDefKind::Union(_) => true,
|
||||
TypeDefKind::Type(Type::Id(t)) => needs_generics(iface, &iface.types[*t].kind),
|
||||
TypeDefKind::Type(Type::String) => true,
|
||||
TypeDefKind::Type(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match &ty.kind {
|
||||
TypeDefKind::List(t) => self.print_list(t, mode),
|
||||
|
||||
TypeDefKind::Option(t) => {
|
||||
self.push_str("Option<");
|
||||
self.print_ty(t, mode);
|
||||
self.push_str(">");
|
||||
}
|
||||
|
||||
TypeDefKind::Result(r) => {
|
||||
self.push_str("Result<");
|
||||
self.print_optional_ty(r.ok.as_ref(), mode);
|
||||
self.push_str(",");
|
||||
self.print_optional_ty(r.err.as_ref(), mode);
|
||||
self.push_str(">");
|
||||
}
|
||||
|
||||
TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"),
|
||||
|
||||
// Tuple-like records are mapped directly to Rust tuples of
|
||||
// types. Note the trailing comma after each member to
|
||||
// appropriately handle 1-tuples.
|
||||
TypeDefKind::Tuple(t) => {
|
||||
self.push_str("(");
|
||||
for ty in t.types.iter() {
|
||||
self.print_ty(ty, mode);
|
||||
self.push_str(",");
|
||||
}
|
||||
self.push_str(")");
|
||||
}
|
||||
TypeDefKind::Record(_) => {
|
||||
panic!("unsupported anonymous type reference: record")
|
||||
}
|
||||
TypeDefKind::Flags(_) => {
|
||||
panic!("unsupported anonymous type reference: flags")
|
||||
}
|
||||
TypeDefKind::Enum(_) => {
|
||||
panic!("unsupported anonymous type reference: enum")
|
||||
}
|
||||
TypeDefKind::Union(_) => {
|
||||
panic!("unsupported anonymous type reference: union")
|
||||
}
|
||||
TypeDefKind::Future(ty) => {
|
||||
self.push_str("Future<");
|
||||
self.print_optional_ty(ty.as_ref(), mode);
|
||||
self.push_str(">");
|
||||
}
|
||||
TypeDefKind::Stream(stream) => {
|
||||
self.push_str("Stream<");
|
||||
self.print_optional_ty(stream.element.as_ref(), mode);
|
||||
self.push_str(",");
|
||||
self.print_optional_ty(stream.end.as_ref(), mode);
|
||||
self.push_str(">");
|
||||
}
|
||||
|
||||
TypeDefKind::Type(t) => self.print_ty(t, mode),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_list(&mut self, ty: &Type, mode: TypeMode) {
|
||||
match mode {
|
||||
TypeMode::AllBorrowed(lt) => {
|
||||
self.push_str("&");
|
||||
if lt != "'_" {
|
||||
self.push_str(lt);
|
||||
self.push_str(" ");
|
||||
}
|
||||
self.push_str("[");
|
||||
self.print_ty(ty, mode);
|
||||
self.push_str("]");
|
||||
}
|
||||
TypeMode::Owned => {
|
||||
self.push_str("Vec<");
|
||||
self.print_ty(ty, mode);
|
||||
self.push_str(">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_generics(&mut self, lifetime: Option<&str>) {
|
||||
if lifetime.is_none() {
|
||||
return;
|
||||
}
|
||||
self.push_str("<");
|
||||
if let Some(lt) = lifetime {
|
||||
self.push_str(lt);
|
||||
self.push_str(",");
|
||||
}
|
||||
self.push_str(">");
|
||||
}
|
||||
|
||||
fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> {
|
||||
let info = self.info(ty);
|
||||
let mut result = Vec::new();
|
||||
if info.param {
|
||||
result.push((self.param_name(ty), self.default_param_mode()));
|
||||
}
|
||||
if info.result && (!info.param || self.uses_two_names(&info)) {
|
||||
result.push((self.result_name(ty), TypeMode::Owned));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Writes the camel-cased 'name' of the passed type to `out`, as used to name union variants.
|
||||
fn write_name(&self, ty: &Type, out: &mut String) {
|
||||
match ty {
|
||||
Type::Bool => out.push_str("Bool"),
|
||||
Type::U8 => out.push_str("U8"),
|
||||
Type::U16 => out.push_str("U16"),
|
||||
Type::U32 => out.push_str("U32"),
|
||||
Type::U64 => out.push_str("U64"),
|
||||
Type::S8 => out.push_str("I8"),
|
||||
Type::S16 => out.push_str("I16"),
|
||||
Type::S32 => out.push_str("I32"),
|
||||
Type::S64 => out.push_str("I64"),
|
||||
Type::Float32 => out.push_str("F32"),
|
||||
Type::Float64 => out.push_str("F64"),
|
||||
Type::Char => out.push_str("Char"),
|
||||
Type::String => out.push_str("String"),
|
||||
Type::Id(id) => {
|
||||
let ty = &self.iface().types[*id];
|
||||
match &ty.name {
|
||||
Some(name) => out.push_str(&name.to_upper_camel_case()),
|
||||
None => match &ty.kind {
|
||||
TypeDefKind::Option(ty) => {
|
||||
out.push_str("Optional");
|
||||
self.write_name(ty, out);
|
||||
}
|
||||
TypeDefKind::Result(_) => out.push_str("Result"),
|
||||
TypeDefKind::Tuple(_) => out.push_str("Tuple"),
|
||||
TypeDefKind::List(ty) => {
|
||||
self.write_name(ty, out);
|
||||
out.push_str("List")
|
||||
}
|
||||
TypeDefKind::Future(ty) => {
|
||||
self.write_optional_name(ty.as_ref(), out);
|
||||
out.push_str("Future");
|
||||
}
|
||||
TypeDefKind::Stream(s) => {
|
||||
self.write_optional_name(s.element.as_ref(), out);
|
||||
self.write_optional_name(s.end.as_ref(), out);
|
||||
out.push_str("Stream");
|
||||
}
|
||||
|
||||
TypeDefKind::Type(ty) => self.write_name(ty, out),
|
||||
TypeDefKind::Record(_) => out.push_str("Record"),
|
||||
TypeDefKind::Flags(_) => out.push_str("Flags"),
|
||||
TypeDefKind::Variant(_) => out.push_str("Variant"),
|
||||
TypeDefKind::Enum(_) => out.push_str("Enum"),
|
||||
TypeDefKind::Union(_) => out.push_str("Union"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_optional_name(&self, ty: Option<&Type>, out: &mut String) {
|
||||
match ty {
|
||||
Some(ty) => self.write_name(ty, out),
|
||||
None => out.push_str("()"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the names for the cases of the passed union.
|
||||
fn union_case_names(&self, union: &Union) -> Vec<String> {
|
||||
enum UsedState<'a> {
|
||||
/// This name has been used once before.
|
||||
///
|
||||
/// Contains a reference to the name given to the first usage so that a suffix can be added to it.
|
||||
Once(&'a mut String),
|
||||
/// This name has already been used multiple times.
|
||||
///
|
||||
/// Contains the number of times this has already been used.
|
||||
Multiple(usize),
|
||||
}
|
||||
|
||||
// A `Vec` of the names we're assigning each of the union's cases in order.
|
||||
let mut case_names = vec![String::new(); union.cases.len()];
|
||||
// A map from case names to their `UsedState`.
|
||||
let mut used = HashMap::new();
|
||||
for (case, name) in union.cases.iter().zip(case_names.iter_mut()) {
|
||||
self.write_name(&case.ty, name);
|
||||
|
||||
match used.get_mut(name.as_str()) {
|
||||
None => {
|
||||
// Initialise this name's `UsedState`, with a mutable reference to this name
|
||||
// in case we have to add a suffix to it later.
|
||||
used.insert(name.clone(), UsedState::Once(name));
|
||||
// Since this is the first (and potentially only) usage of this name,
|
||||
// we don't need to add a suffix here.
|
||||
}
|
||||
Some(state) => match state {
|
||||
UsedState::Multiple(n) => {
|
||||
// Add a suffix of the index of this usage.
|
||||
write!(name, "{n}").unwrap();
|
||||
// Add one to the number of times this type has been used.
|
||||
*n += 1;
|
||||
}
|
||||
UsedState::Once(first) => {
|
||||
// Add a suffix of 0 to the first usage.
|
||||
first.push('0');
|
||||
// We now get a suffix of 1.
|
||||
name.push('1');
|
||||
// Then update the state.
|
||||
*state = UsedState::Multiple(2);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
case_names
|
||||
}
|
||||
|
||||
fn param_name(&self, ty: TypeId) -> String {
|
||||
let info = self.info(ty);
|
||||
let name = self.iface().types[ty]
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_upper_camel_case();
|
||||
if self.uses_two_names(&info) {
|
||||
format!("{}Param", name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
fn result_name(&self, ty: TypeId) -> String {
|
||||
let info = self.info(ty);
|
||||
let name = self.iface().types[ty]
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_upper_camel_case();
|
||||
if self.uses_two_names(&info) {
|
||||
format!("{}Result", name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
fn uses_two_names(&self, info: &TypeInfo) -> bool {
|
||||
info.has_list
|
||||
&& 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> {
|
||||
match mode {
|
||||
TypeMode::AllBorrowed(s) if info.has_list => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_rust_ident(name: &str) -> String {
|
||||
match name {
|
||||
// Escape Rust keywords.
|
||||
// Source: https://doc.rust-lang.org/reference/keywords.html
|
||||
"as" => "as_".into(),
|
||||
"break" => "break_".into(),
|
||||
"const" => "const_".into(),
|
||||
"continue" => "continue_".into(),
|
||||
"crate" => "crate_".into(),
|
||||
"else" => "else_".into(),
|
||||
"enum" => "enum_".into(),
|
||||
"extern" => "extern_".into(),
|
||||
"false" => "false_".into(),
|
||||
"fn" => "fn_".into(),
|
||||
"for" => "for_".into(),
|
||||
"if" => "if_".into(),
|
||||
"impl" => "impl_".into(),
|
||||
"in" => "in_".into(),
|
||||
"let" => "let_".into(),
|
||||
"loop" => "loop_".into(),
|
||||
"match" => "match_".into(),
|
||||
"mod" => "mod_".into(),
|
||||
"move" => "move_".into(),
|
||||
"mut" => "mut_".into(),
|
||||
"pub" => "pub_".into(),
|
||||
"ref" => "ref_".into(),
|
||||
"return" => "return_".into(),
|
||||
"self" => "self_".into(),
|
||||
"static" => "static_".into(),
|
||||
"struct" => "struct_".into(),
|
||||
"super" => "super_".into(),
|
||||
"trait" => "trait_".into(),
|
||||
"true" => "true_".into(),
|
||||
"type" => "type_".into(),
|
||||
"unsafe" => "unsafe_".into(),
|
||||
"use" => "use_".into(),
|
||||
"where" => "where_".into(),
|
||||
"while" => "while_".into(),
|
||||
"async" => "async_".into(),
|
||||
"await" => "await_".into(),
|
||||
"dyn" => "dyn_".into(),
|
||||
"abstract" => "abstract_".into(),
|
||||
"become" => "become_".into(),
|
||||
"box" => "box_".into(),
|
||||
"do" => "do_".into(),
|
||||
"final" => "final_".into(),
|
||||
"macro" => "macro_".into(),
|
||||
"override" => "override_".into(),
|
||||
"priv" => "priv_".into(),
|
||||
"typeof" => "typeof_".into(),
|
||||
"unsized" => "unsized_".into(),
|
||||
"virtual" => "virtual_".into(),
|
||||
"yield" => "yield_".into(),
|
||||
"try" => "try_".into(),
|
||||
s => s.to_snake_case(),
|
||||
}
|
||||
}
|
||||
130
crates/wit-bindgen/src/source.rs
Normal file
130
crates/wit-bindgen/src/source.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use std::fmt::{self, Write};
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Helper structure to maintain indentation automatically when printing.
|
||||
#[derive(Default)]
|
||||
pub struct Source {
|
||||
s: String,
|
||||
indent: usize,
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub fn push_str(&mut self, src: &str) {
|
||||
let lines = src.lines().collect::<Vec<_>>();
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with('}') && self.s.ends_with(" ") {
|
||||
self.s.pop();
|
||||
self.s.pop();
|
||||
}
|
||||
self.s.push_str(if lines.len() == 1 {
|
||||
line
|
||||
} else {
|
||||
line.trim_start()
|
||||
});
|
||||
if trimmed.ends_with('{') {
|
||||
self.indent += 1;
|
||||
}
|
||||
if trimmed.starts_with('}') {
|
||||
// Note that a `saturating_sub` is used here to prevent a panic
|
||||
// here in the case of invalid code being generated in debug
|
||||
// mode. It's typically easier to debug those issues through
|
||||
// looking at the source code rather than getting a panic.
|
||||
self.indent = self.indent.saturating_sub(1);
|
||||
}
|
||||
if i != lines.len() - 1 || src.ends_with('\n') {
|
||||
self.newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn indent(&mut self, amt: usize) {
|
||||
self.indent += amt;
|
||||
}
|
||||
|
||||
pub fn deindent(&mut self, amt: usize) {
|
||||
self.indent -= amt;
|
||||
}
|
||||
|
||||
fn newline(&mut self) {
|
||||
self.s.push('\n');
|
||||
for _ in 0..self.indent {
|
||||
self.s.push_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_string(&mut self) -> &mut String {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Source {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.push_str(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Source {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &str {
|
||||
&self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Source> for String {
|
||||
fn from(s: Source) -> String {
|
||||
s.s
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Source;
|
||||
|
||||
#[test]
|
||||
fn simple_append() {
|
||||
let mut s = Source::default();
|
||||
s.push_str("x");
|
||||
assert_eq!(s.s, "x");
|
||||
s.push_str("y");
|
||||
assert_eq!(s.s, "xy");
|
||||
s.push_str("z ");
|
||||
assert_eq!(s.s, "xyz ");
|
||||
s.push_str(" a ");
|
||||
assert_eq!(s.s, "xyz a ");
|
||||
s.push_str("\na");
|
||||
assert_eq!(s.s, "xyz a \na");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn newline_remap() {
|
||||
let mut s = Source::default();
|
||||
s.push_str("function() {\n");
|
||||
s.push_str("y\n");
|
||||
s.push_str("}\n");
|
||||
assert_eq!(s.s, "function() {\n y\n}\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else() {
|
||||
let mut s = Source::default();
|
||||
s.push_str("if() {\n");
|
||||
s.push_str("y\n");
|
||||
s.push_str("} else if () {\n");
|
||||
s.push_str("z\n");
|
||||
s.push_str("}\n");
|
||||
assert_eq!(s.s, "if() {\n y\n} else if () {\n z\n}\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim_ws() {
|
||||
let mut s = Source::default();
|
||||
s.push_str(
|
||||
"function() {
|
||||
x
|
||||
}",
|
||||
);
|
||||
assert_eq!(s.s, "function() {\n x\n}");
|
||||
}
|
||||
}
|
||||
207
crates/wit-bindgen/src/types.rs
Normal file
207
crates/wit-bindgen/src/types.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
use std::collections::HashMap;
|
||||
use wit_parser::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Types {
|
||||
type_info: HashMap<TypeId, TypeInfo>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug, PartialEq)]
|
||||
pub struct TypeInfo {
|
||||
/// Whether or not this type is ever used (transitively) within the
|
||||
/// parameter of a function.
|
||||
pub param: bool,
|
||||
|
||||
/// Whether or not this type is ever used (transitively) within the
|
||||
/// result of a function.
|
||||
pub result: bool,
|
||||
|
||||
/// Whether or not this type is ever used (transitively) within the
|
||||
/// error case in the result of a function.
|
||||
pub error: bool,
|
||||
|
||||
/// Whether or not this type (transitively) has a list.
|
||||
pub has_list: bool,
|
||||
}
|
||||
|
||||
impl std::ops::BitOrAssign for TypeInfo {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
self.param |= rhs.param;
|
||||
self.result |= rhs.result;
|
||||
self.error |= rhs.error;
|
||||
self.has_list |= rhs.has_list;
|
||||
}
|
||||
}
|
||||
|
||||
impl Types {
|
||||
pub fn analyze(&mut self, iface: &Interface) {
|
||||
for (t, _) in iface.types.iter() {
|
||||
self.type_id_info(iface, t);
|
||||
}
|
||||
for f in iface.functions.iter() {
|
||||
for (_, ty) in f.params.iter() {
|
||||
self.set_param_result_ty(
|
||||
iface,
|
||||
ty,
|
||||
TypeInfo {
|
||||
param: true,
|
||||
..TypeInfo::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
for ty in f.results.iter_types() {
|
||||
self.set_param_result_ty(
|
||||
iface,
|
||||
ty,
|
||||
TypeInfo {
|
||||
result: true,
|
||||
..TypeInfo::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, id: TypeId) -> TypeInfo {
|
||||
self.type_info[&id]
|
||||
}
|
||||
|
||||
fn type_id_info(&mut self, iface: &Interface, ty: TypeId) -> TypeInfo {
|
||||
if let Some(info) = self.type_info.get(&ty) {
|
||||
return *info;
|
||||
}
|
||||
let mut info = TypeInfo::default();
|
||||
match &iface.types[ty].kind {
|
||||
TypeDefKind::Record(r) => {
|
||||
for field in r.fields.iter() {
|
||||
info |= self.type_info(iface, &field.ty);
|
||||
}
|
||||
}
|
||||
TypeDefKind::Tuple(t) => {
|
||||
for ty in t.types.iter() {
|
||||
info |= self.type_info(iface, ty);
|
||||
}
|
||||
}
|
||||
TypeDefKind::Flags(_) => {}
|
||||
TypeDefKind::Enum(_) => {}
|
||||
TypeDefKind::Variant(v) => {
|
||||
for case in v.cases.iter() {
|
||||
info |= self.optional_type_info(iface, case.ty.as_ref());
|
||||
}
|
||||
}
|
||||
TypeDefKind::List(ty) => {
|
||||
info = self.type_info(iface, ty);
|
||||
info.has_list = true;
|
||||
}
|
||||
TypeDefKind::Type(ty) => {
|
||||
info = self.type_info(iface, ty);
|
||||
}
|
||||
TypeDefKind::Option(ty) => {
|
||||
info = self.type_info(iface, ty);
|
||||
}
|
||||
TypeDefKind::Result(r) => {
|
||||
info = self.optional_type_info(iface, r.ok.as_ref());
|
||||
info |= self.optional_type_info(iface, r.err.as_ref());
|
||||
}
|
||||
TypeDefKind::Union(u) => {
|
||||
for case in u.cases.iter() {
|
||||
info |= self.type_info(iface, &case.ty);
|
||||
}
|
||||
}
|
||||
TypeDefKind::Future(ty) => {
|
||||
info = self.optional_type_info(iface, ty.as_ref());
|
||||
}
|
||||
TypeDefKind::Stream(stream) => {
|
||||
info = self.optional_type_info(iface, stream.element.as_ref());
|
||||
info |= self.optional_type_info(iface, stream.end.as_ref());
|
||||
}
|
||||
}
|
||||
self.type_info.insert(ty, info);
|
||||
info
|
||||
}
|
||||
|
||||
fn type_info(&mut self, iface: &Interface, ty: &Type) -> TypeInfo {
|
||||
let mut info = TypeInfo::default();
|
||||
match ty {
|
||||
Type::String => info.has_list = true,
|
||||
Type::Id(id) => return self.type_id_info(iface, *id),
|
||||
_ => {}
|
||||
}
|
||||
info
|
||||
}
|
||||
|
||||
fn optional_type_info(&mut self, iface: &Interface, ty: Option<&Type>) -> TypeInfo {
|
||||
match ty {
|
||||
Some(ty) => self.type_info(iface, ty),
|
||||
None => TypeInfo::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_param_result_id(&mut self, iface: &Interface, ty: TypeId, info: TypeInfo) {
|
||||
match &iface.types[ty].kind {
|
||||
TypeDefKind::Record(r) => {
|
||||
for field in r.fields.iter() {
|
||||
self.set_param_result_ty(iface, &field.ty, info)
|
||||
}
|
||||
}
|
||||
TypeDefKind::Tuple(t) => {
|
||||
for ty in t.types.iter() {
|
||||
self.set_param_result_ty(iface, ty, info)
|
||||
}
|
||||
}
|
||||
TypeDefKind::Flags(_) => {}
|
||||
TypeDefKind::Enum(_) => {}
|
||||
TypeDefKind::Variant(v) => {
|
||||
for case in v.cases.iter() {
|
||||
self.set_param_result_optional_ty(iface, case.ty.as_ref(), info)
|
||||
}
|
||||
}
|
||||
TypeDefKind::List(ty) | TypeDefKind::Type(ty) | TypeDefKind::Option(ty) => {
|
||||
self.set_param_result_ty(iface, ty, info)
|
||||
}
|
||||
TypeDefKind::Result(r) => {
|
||||
self.set_param_result_optional_ty(iface, r.ok.as_ref(), info);
|
||||
let mut info2 = info;
|
||||
info2.error = info.result;
|
||||
self.set_param_result_optional_ty(iface, r.err.as_ref(), info2);
|
||||
}
|
||||
TypeDefKind::Union(u) => {
|
||||
for case in u.cases.iter() {
|
||||
self.set_param_result_ty(iface, &case.ty, info)
|
||||
}
|
||||
}
|
||||
TypeDefKind::Future(ty) => self.set_param_result_optional_ty(iface, ty.as_ref(), info),
|
||||
TypeDefKind::Stream(stream) => {
|
||||
self.set_param_result_optional_ty(iface, stream.element.as_ref(), info);
|
||||
self.set_param_result_optional_ty(iface, stream.end.as_ref(), info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_param_result_ty(&mut self, iface: &Interface, ty: &Type, info: TypeInfo) {
|
||||
match ty {
|
||||
Type::Id(id) => {
|
||||
self.type_id_info(iface, *id);
|
||||
let cur = self.type_info.get_mut(id).unwrap();
|
||||
let prev = *cur;
|
||||
*cur |= info;
|
||||
if prev != *cur {
|
||||
self.set_param_result_id(iface, *id, info);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_param_result_optional_ty(
|
||||
&mut self,
|
||||
iface: &Interface,
|
||||
ty: Option<&Type>,
|
||||
info: TypeInfo,
|
||||
) {
|
||||
match ty {
|
||||
Some(ty) => self.set_param_result_ty(iface, ty, info),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user