chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

410
vendor/wit-parser/src/abi.rs vendored Normal file
View File

@@ -0,0 +1,410 @@
use crate::{Function, Handle, Int, Resolve, Type, TypeDefKind};
/// A core WebAssembly signature with params and results.
#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct WasmSignature {
/// The WebAssembly parameters of this function.
pub params: Vec<WasmType>,
/// The WebAssembly results of this function.
pub results: Vec<WasmType>,
/// Whether or not this signature is passing all of its parameters
/// indirectly through a pointer within `params`.
///
/// Note that `params` still reflects the true wasm parameters of this
/// function, this is auxiliary information for code generators if
/// necessary.
pub indirect_params: bool,
/// Whether or not this signature is using a return pointer to store the
/// result of the function, which is reflected either in `params` or
/// `results` depending on the context this function is used (e.g. an import
/// or an export).
pub retptr: bool,
}
/// Enumerates wasm types used by interface types when lowering/lifting.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum WasmType {
I32,
I64,
F32,
F64,
/// A pointer type. In core Wasm this typically lowers to either `i32` or
/// `i64` depending on the index type of the exported linear memory,
/// however bindings can use different source-level types to preserve
/// provenance.
///
/// Users that don't do anything special for pointers can treat this as
/// `i32`.
Pointer,
/// A type for values which can be either pointers or 64-bit integers.
/// This occurs in variants, when pointers and non-pointers are unified.
///
/// Users that don't do anything special for pointers can treat this as
/// `i64`.
PointerOrI64,
/// An array length type. In core Wasm this lowers to either `i32` or `i64`
/// depending on the index type of the exported linear memory.
///
/// Users that don't do anything special for pointers can treat this as
/// `i32`.
Length,
// NOTE: we don't lower interface types to any other Wasm type,
// e.g. externref, so we don't need to define them here.
}
fn join(a: WasmType, b: WasmType) -> WasmType {
use WasmType::*;
match (a, b) {
(I32, I32)
| (I64, I64)
| (F32, F32)
| (F64, F64)
| (Pointer, Pointer)
| (PointerOrI64, PointerOrI64)
| (Length, Length) => a,
(I32, F32) | (F32, I32) => I32,
// A length is at least an `i32`, maybe more, so it wins over
// 32-bit types.
(Length, I32 | F32) => Length,
(I32 | F32, Length) => Length,
// A length might be an `i64`, but might not be, so if we have
// 64-bit types, they win.
(Length, I64 | F64) => I64,
(I64 | F64, Length) => I64,
// Pointers have provenance and are at least an `i32`, so they
// win over 32-bit and length types.
(Pointer, I32 | F32 | Length) => Pointer,
(I32 | F32 | Length, Pointer) => Pointer,
// If we need 64 bits and provenance, we need to use the special
// `PointerOrI64`.
(Pointer, I64 | F64) => PointerOrI64,
(I64 | F64, Pointer) => PointerOrI64,
// PointerOrI64 wins over everything.
(PointerOrI64, _) => PointerOrI64,
(_, PointerOrI64) => PointerOrI64,
// Otherwise, `i64` wins.
(_, I64 | F64) | (I64 | F64, _) => I64,
}
}
impl From<Int> for WasmType {
fn from(i: Int) -> WasmType {
match i {
Int::U8 | Int::U16 | Int::U32 => WasmType::I32,
Int::U64 => WasmType::I64,
}
}
}
/// We use a different ABI for wasm importing functions exported by the host
/// than for wasm exporting functions imported by the host.
///
/// Note that this reflects the flavor of ABI we generate, and not necessarily
/// the way the resulting bindings will be used by end users. See the comments
/// on the `Direction` enum in gen-core for details.
///
/// The bindings ABI has a concept of a "guest" and a "host". There are two
/// variants of the ABI, one specialized for the "guest" importing and calling
/// a function defined and exported in the "host", and the other specialized for
/// the "host" importing and calling a function defined and exported in the "guest".
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum AbiVariant {
/// The guest is importing and calling the function.
GuestImport,
/// The guest is defining and exporting the function.
GuestExport,
GuestImportAsync,
GuestExportAsync,
GuestExportAsyncStackful,
}
impl AbiVariant {
pub fn is_async(&self) -> bool {
match self {
Self::GuestImport | Self::GuestExport => false,
Self::GuestImportAsync | Self::GuestExportAsync | Self::GuestExportAsyncStackful => {
true
}
}
}
}
pub struct FlatTypes<'a> {
types: &'a mut [WasmType],
cur: usize,
overflow: bool,
}
impl<'a> FlatTypes<'a> {
pub fn new(types: &'a mut [WasmType]) -> FlatTypes<'a> {
FlatTypes {
types,
cur: 0,
overflow: false,
}
}
pub fn push(&mut self, ty: WasmType) -> bool {
match self.types.get_mut(self.cur) {
Some(next) => {
*next = ty;
self.cur += 1;
true
}
None => {
self.overflow = true;
false
}
}
}
pub fn to_vec(&self) -> Vec<WasmType> {
self.types[..self.cur].to_vec()
}
}
impl Resolve {
pub const MAX_FLAT_PARAMS: usize = 16;
pub const MAX_FLAT_ASYNC_PARAMS: usize = 4;
pub const MAX_FLAT_RESULTS: usize = 1;
/// Get the WebAssembly type signature for this interface function
///
/// The first entry returned is the list of parameters and the second entry
/// is the list of results for the wasm function signature.
pub fn wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature {
// Note that one extra parameter is allocated in case a return pointer
// is needed down below for imports.
let mut storage = [WasmType::I32; Self::MAX_FLAT_PARAMS + 1];
let mut params = FlatTypes::new(&mut storage);
let ok = self.push_flat_list(func.params.iter().map(|(_, param)| param), &mut params);
assert_eq!(ok, !params.overflow);
let max = match variant {
AbiVariant::GuestImport
| AbiVariant::GuestExport
| AbiVariant::GuestExportAsync
| AbiVariant::GuestExportAsyncStackful => Self::MAX_FLAT_PARAMS,
AbiVariant::GuestImportAsync => Self::MAX_FLAT_ASYNC_PARAMS,
};
let indirect_params = !ok || params.cur > max;
if indirect_params {
params.types[0] = WasmType::Pointer;
params.cur = 1;
} else {
if matches!(
(&func.kind, variant),
(
crate::FunctionKind::Method(_) | crate::FunctionKind::AsyncMethod(_),
AbiVariant::GuestExport
| AbiVariant::GuestExportAsync
| AbiVariant::GuestExportAsyncStackful
)
) {
// Guest exported methods always receive resource rep as first argument
//
// TODO: Ideally you would distinguish between imported and exported
// resource Handles and then use either I32 or Pointer in abi::push_flat().
// But this contextual information isn't available, yet.
// See https://github.com/bytecodealliance/wasm-tools/pull/1438 for more details.
assert!(matches!(params.types[0], WasmType::I32));
params.types[0] = WasmType::Pointer;
}
}
let mut storage = [WasmType::I32; Self::MAX_FLAT_RESULTS];
let mut results = FlatTypes::new(&mut storage);
let mut retptr = false;
match variant {
AbiVariant::GuestImport | AbiVariant::GuestExport => {
if let Some(ty) = &func.result {
self.push_flat(ty, &mut results);
}
retptr = results.overflow;
// Rust/C don't support multi-value well right now, so if a
// function would have multiple results then instead truncate
// it. Imports take a return pointer to write into and exports
// return a pointer they wrote into.
if retptr {
results.cur = 0;
match variant {
AbiVariant::GuestImport => {
assert!(params.push(WasmType::Pointer));
}
AbiVariant::GuestExport => {
assert!(results.push(WasmType::Pointer));
}
_ => unreachable!(),
}
}
}
AbiVariant::GuestImportAsync => {
// If this function has a result, a pointer must be passed to
// get filled in by the async runtime.
if func.result.is_some() {
assert!(params.push(WasmType::Pointer));
retptr = true;
}
// The result of this function is a status code.
assert!(results.push(WasmType::I32));
}
AbiVariant::GuestExportAsync => {
// The result of this function is a status code. Note that the
// function results are entirely ignored here as they aren't
// part of the ABI and are handled in the `task.return`
// intrinsic.
assert!(results.push(WasmType::I32));
}
AbiVariant::GuestExportAsyncStackful => {
// No status code, and like async exports no result handling.
}
}
WasmSignature {
params: params.to_vec(),
indirect_params,
results: results.to_vec(),
retptr,
}
}
fn push_flat_list<'a>(
&self,
mut list: impl Iterator<Item = &'a Type>,
result: &mut FlatTypes<'_>,
) -> bool {
list.all(|ty| self.push_flat(ty, result))
}
/// Appends the flat wasm types representing `ty` onto the `result`
/// list provided.
pub fn push_flat(&self, ty: &Type, result: &mut FlatTypes<'_>) -> bool {
match ty {
Type::Bool
| Type::S8
| Type::U8
| Type::S16
| Type::U16
| Type::S32
| Type::U32
| Type::Char
| Type::ErrorContext => result.push(WasmType::I32),
Type::U64 | Type::S64 => result.push(WasmType::I64),
Type::F32 => result.push(WasmType::F32),
Type::F64 => result.push(WasmType::F64),
Type::String => result.push(WasmType::Pointer) && result.push(WasmType::Length),
Type::Id(id) => match &self.types[*id].kind {
TypeDefKind::Type(t) => self.push_flat(t, result),
TypeDefKind::Handle(Handle::Own(_) | Handle::Borrow(_)) => {
result.push(WasmType::I32)
}
TypeDefKind::Resource => todo!(),
TypeDefKind::Record(r) => {
self.push_flat_list(r.fields.iter().map(|f| &f.ty), result)
}
TypeDefKind::Tuple(t) => self.push_flat_list(t.types.iter(), result),
TypeDefKind::Flags(r) => {
self.push_flat_list((0..r.repr().count()).map(|_| &Type::U32), result)
}
TypeDefKind::List(_) => {
result.push(WasmType::Pointer) && result.push(WasmType::Length)
}
TypeDefKind::Map(_, _) => {
result.push(WasmType::Pointer) && result.push(WasmType::Length)
}
TypeDefKind::FixedSizeList(ty, size) => {
self.push_flat_list((0..*size).map(|_| ty), result)
}
TypeDefKind::Variant(v) => {
result.push(v.tag().into())
&& self.push_flat_variants(v.cases.iter().map(|c| c.ty.as_ref()), result)
}
TypeDefKind::Enum(e) => result.push(e.tag().into()),
TypeDefKind::Option(t) => {
result.push(WasmType::I32) && self.push_flat_variants([None, Some(t)], result)
}
TypeDefKind::Result(r) => {
result.push(WasmType::I32)
&& self.push_flat_variants([r.ok.as_ref(), r.err.as_ref()], result)
}
TypeDefKind::Future(_) => result.push(WasmType::I32),
TypeDefKind::Stream(_) => result.push(WasmType::I32),
TypeDefKind::Unknown => unreachable!(),
},
}
}
fn push_flat_variants<'a>(
&self,
tys: impl IntoIterator<Item = Option<&'a Type>>,
result: &mut FlatTypes<'_>,
) -> bool {
let mut temp = result.types[result.cur..].to_vec();
let mut temp = FlatTypes::new(&mut temp);
let start = result.cur;
// Push each case's type onto a temporary vector, and then
// merge that vector into our final list starting at
// `start`. Note that this requires some degree of
// "unification" so we can handle things like `Result<i32,
// f32>` where that turns into `[i32 i32]` where the second
// `i32` might be the `f32` bitcasted.
for ty in tys {
if let Some(ty) = ty {
if !self.push_flat(ty, &mut temp) {
result.overflow = true;
return false;
}
for (i, ty) in temp.types[..temp.cur].iter().enumerate() {
let i = i + start;
if i < result.cur {
result.types[i] = join(result.types[i], *ty);
} else if result.cur == result.types.len() {
result.overflow = true;
return false;
} else {
result.types[i] = *ty;
result.cur += 1;
}
}
temp.cur = 0;
}
}
true
}
}

1958
vendor/wit-parser/src/ast.rs vendored Normal file

File diff suppressed because it is too large Load Diff

751
vendor/wit-parser/src/ast/lex.rs vendored Normal file
View File

@@ -0,0 +1,751 @@
use anyhow::{Result, bail};
use std::char;
use std::fmt;
use std::str;
use unicode_xid::UnicodeXID;
use self::Token::*;
#[derive(Clone)]
pub struct Tokenizer<'a> {
input: &'a str,
span_offset: u32,
chars: CrlfFold<'a>,
require_f32_f64: bool,
}
#[derive(Clone)]
struct CrlfFold<'a> {
chars: str::CharIndices<'a>,
}
/// A span, designating a range of bytes where a token is located.
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub struct Span {
/// The start of the range.
pub start: u32,
/// The end of the range (exclusive).
pub end: u32,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum Token {
Whitespace,
Comment,
Equals,
Comma,
Colon,
Period,
Semicolon,
LeftParen,
RightParen,
LeftBrace,
RightBrace,
LessThan,
GreaterThan,
RArrow,
Star,
At,
Slash,
Plus,
Minus,
Use,
Type,
Func,
U8,
U16,
U32,
U64,
S8,
S16,
S32,
S64,
F32,
F64,
Char,
Record,
Resource,
Own,
Borrow,
Flags,
Variant,
Enum,
Bool,
String_,
Option_,
Result_,
Future,
Stream,
ErrorContext,
List,
Map,
Underscore,
As,
From_,
Static,
Interface,
Tuple,
Import,
Export,
World,
Package,
Constructor,
Async,
Id,
ExplicitId,
Integer,
Include,
With,
}
#[derive(Eq, PartialEq, Debug)]
#[allow(dead_code)]
pub enum Error {
InvalidCharInId(u32, char),
IdPartEmpty(u32),
InvalidEscape(u32, char),
Unexpected(u32, char),
UnterminatedComment(u32),
Wanted {
at: u32,
expected: &'static str,
found: &'static str,
},
}
// NB: keep in sync with `crates/wit-component/src/printing.rs`.
const REQUIRE_F32_F64_BY_DEFAULT: bool = true;
impl<'a> Tokenizer<'a> {
pub fn new(
input: &'a str,
span_offset: u32,
require_f32_f64: Option<bool>,
) -> Result<Tokenizer<'a>> {
detect_invalid_input(input)?;
let mut t = Tokenizer {
input,
span_offset,
chars: CrlfFold {
chars: input.char_indices(),
},
require_f32_f64: require_f32_f64.unwrap_or_else(|| {
match std::env::var("WIT_REQUIRE_F32_F64") {
Ok(s) => s == "1",
Err(_) => REQUIRE_F32_F64_BY_DEFAULT,
}
}),
};
// Eat utf-8 BOM
t.eatc('\u{feff}');
Ok(t)
}
pub fn expect_semicolon(&mut self) -> Result<()> {
self.expect(Token::Semicolon)?;
Ok(())
}
pub fn get_span(&self, span: Span) -> &'a str {
let start = usize::try_from(span.start - self.span_offset).unwrap();
let end = usize::try_from(span.end - self.span_offset).unwrap();
&self.input[start..end]
}
pub fn parse_id(&self, span: Span) -> Result<&'a str> {
let ret = self.get_span(span);
validate_id(span.start, &ret)?;
Ok(ret)
}
pub fn parse_explicit_id(&self, span: Span) -> Result<&'a str> {
let token = self.get_span(span);
let id_part = token.strip_prefix('%').unwrap();
validate_id(span.start, id_part)?;
Ok(id_part)
}
pub fn next(&mut self) -> Result<Option<(Span, Token)>, Error> {
loop {
match self.next_raw()? {
Some((_, Token::Whitespace)) | Some((_, Token::Comment)) => {}
other => break Ok(other),
}
}
}
/// Three possibilities when calling this method: an `Err(...)` indicates that lexing failed, an
/// `Ok(Some(...))` produces the next token, and `Ok(None)` indicates that there are no more
/// tokens available.
pub fn next_raw(&mut self) -> Result<Option<(Span, Token)>, Error> {
let (str_start, ch) = match self.chars.next() {
Some(pair) => pair,
None => return Ok(None),
};
let start = self.span_offset + u32::try_from(str_start).unwrap();
let token = match ch {
'\n' | '\t' | ' ' => {
// Eat all contiguous whitespace tokens
while self.eatc(' ') || self.eatc('\t') || self.eatc('\n') {}
Whitespace
}
'/' => {
// Eat a line comment if it's `//...`
if self.eatc('/') {
for (_, ch) in &mut self.chars {
if ch == '\n' {
break;
}
}
Comment
// eat a block comment if it's `/*...`
} else if self.eatc('*') {
let mut depth = 1;
while depth > 0 {
let (_, ch) = match self.chars.next() {
Some(pair) => pair,
None => return Err(Error::UnterminatedComment(start)),
};
match ch {
'/' if self.eatc('*') => depth += 1,
'*' if self.eatc('/') => depth -= 1,
_ => {}
}
}
Comment
} else {
Slash
}
}
'=' => Equals,
',' => Comma,
':' => Colon,
'.' => Period,
';' => Semicolon,
'(' => LeftParen,
')' => RightParen,
'{' => LeftBrace,
'}' => RightBrace,
'<' => LessThan,
'>' => GreaterThan,
'*' => Star,
'@' => At,
'-' => {
if self.eatc('>') {
RArrow
} else {
Minus
}
}
'+' => Plus,
'%' => {
let mut iter = self.chars.clone();
if let Some((_, ch)) = iter.next() {
if is_keylike_start(ch) {
self.chars = iter.clone();
while let Some((_, ch)) = iter.next() {
if !is_keylike_continue(ch) {
break;
}
self.chars = iter.clone();
}
}
}
ExplicitId
}
ch if is_keylike_start(ch) => {
let remaining = self.chars.chars.as_str().len();
let mut iter = self.chars.clone();
while let Some((_, ch)) = iter.next() {
if !is_keylike_continue(ch) {
break;
}
self.chars = iter.clone();
}
let str_end =
str_start + ch.len_utf8() + (remaining - self.chars.chars.as_str().len());
match &self.input[str_start..str_end] {
"use" => Use,
"type" => Type,
"func" => Func,
"u8" => U8,
"u16" => U16,
"u32" => U32,
"u64" => U64,
"s8" => S8,
"s16" => S16,
"s32" => S32,
"s64" => S64,
"f32" => F32,
"f64" => F64,
"float32" if !self.require_f32_f64 => F32,
"float64" if !self.require_f32_f64 => F64,
"char" => Char,
"resource" => Resource,
"own" => Own,
"borrow" => Borrow,
"record" => Record,
"flags" => Flags,
"variant" => Variant,
"enum" => Enum,
"bool" => Bool,
"string" => String_,
"option" => Option_,
"result" => Result_,
"future" => Future,
"stream" => Stream,
"error-context" => ErrorContext,
"list" => List,
"map" => Map,
"_" => Underscore,
"as" => As,
"from" => From_,
"static" => Static,
"interface" => Interface,
"tuple" => Tuple,
"world" => World,
"import" => Import,
"export" => Export,
"package" => Package,
"constructor" => Constructor,
"include" => Include,
"with" => With,
"async" => Async,
_ => Id,
}
}
ch if ch.is_ascii_digit() => {
let mut iter = self.chars.clone();
while let Some((_, ch)) = iter.next() {
if !ch.is_ascii_digit() {
break;
}
self.chars = iter.clone();
}
Integer
}
ch => return Err(Error::Unexpected(start, ch)),
};
let end = match self.chars.clone().next() {
Some((i, _)) => i,
None => self.input.len(),
};
let end = self.span_offset + u32::try_from(end).unwrap();
Ok(Some((Span { start, end }, token)))
}
pub fn eat(&mut self, expected: Token) -> Result<bool, Error> {
let mut other = self.clone();
match other.next()? {
Some((_span, found)) if expected == found => {
*self = other;
Ok(true)
}
Some(_) => Ok(false),
None => Ok(false),
}
}
pub fn expect(&mut self, expected: Token) -> Result<Span, Error> {
match self.next()? {
Some((span, found)) => {
if expected == found {
Ok(span)
} else {
Err(Error::Wanted {
at: span.start,
expected: expected.describe(),
found: found.describe(),
})
}
}
None => Err(Error::Wanted {
at: self.span_offset + u32::try_from(self.input.len()).unwrap(),
expected: expected.describe(),
found: "eof",
}),
}
}
fn eatc(&mut self, ch: char) -> bool {
let mut iter = self.chars.clone();
match iter.next() {
Some((_, ch2)) if ch == ch2 => {
self.chars = iter;
true
}
_ => false,
}
}
pub fn eof_span(&self) -> Span {
let end = self.span_offset + u32::try_from(self.input.len()).unwrap();
Span { start: end, end }
}
}
impl<'a> Iterator for CrlfFold<'a> {
type Item = (usize, char);
fn next(&mut self) -> Option<(usize, char)> {
self.chars.next().map(|(i, c)| {
if c == '\r' {
let mut attempt = self.chars.clone();
if let Some((_, '\n')) = attempt.next() {
self.chars = attempt;
return (i, '\n');
}
}
(i, c)
})
}
}
fn detect_invalid_input(input: &str) -> Result<()> {
// Disallow specific codepoints.
let mut line = 1;
for ch in input.chars() {
match ch {
'\n' => line += 1,
'\r' | '\t' => {}
// Bidirectional override codepoints can be used to craft source code that
// appears to have a different meaning than its actual meaning. See
// [CVE-2021-42574] for background and motivation.
//
// [CVE-2021-42574]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574
'\u{202a}' | '\u{202b}' | '\u{202c}' | '\u{202d}' | '\u{202e}' | '\u{2066}'
| '\u{2067}' | '\u{2068}' | '\u{2069}' => {
bail!(
"Input contains bidirectional override codepoint {:?} at line {}",
ch.escape_unicode(),
line
);
}
// Disallow several characters which are deprecated or discouraged in Unicode.
//
// U+149 deprecated; see Unicode 13.0.0, sec. 7.1 Latin, Compatibility Digraphs.
// U+673 deprecated; see Unicode 13.0.0, sec. 9.2 Arabic, Additional Vowel Marks.
// U+F77 and U+F79 deprecated; see Unicode 13.0.0, sec. 13.4 Tibetan, Vowels.
// U+17A3 and U+17A4 deprecated, and U+17B4 and U+17B5 discouraged; see
// Unicode 13.0.0, sec. 16.4 Khmer, Characters Whose Use Is Discouraged.
'\u{149}' | '\u{673}' | '\u{f77}' | '\u{f79}' | '\u{17a3}' | '\u{17a4}'
| '\u{17b4}' | '\u{17b5}' => {
bail!(
"Codepoint {:?} at line {} is discouraged by Unicode",
ch.escape_unicode(),
line
);
}
// Disallow control codes other than the ones explicitly recognized above,
// so that viewing a wit file on a terminal doesn't have surprising side
// effects or appear to have a different meaning than its actual meaning.
ch if ch.is_control() => {
bail!("Control code '{}' at line {}", ch.escape_unicode(), line);
}
_ => {}
}
}
Ok(())
}
fn is_keylike_start(ch: char) -> bool {
// Lex any XID start, `_`, or '-'. These aren't all valid identifier chars,
// but we'll diagnose that after we've lexed the full string.
UnicodeXID::is_xid_start(ch) || ch == '_' || ch == '-'
}
fn is_keylike_continue(ch: char) -> bool {
// Lex any XID continue (which includes `_`) or '-'.
UnicodeXID::is_xid_continue(ch) || ch == '-'
}
pub fn validate_id(start: u32, id: &str) -> Result<(), Error> {
// IDs must have at least one part.
if id.is_empty() {
return Err(Error::IdPartEmpty(start));
}
// Ids consist of parts separated by '-'s.
for (idx, part) in id.split('-').enumerate() {
// Parts must be non-empty and contain either all ASCII lowercase or
// all ASCII uppercase. Non-first segment can also start with a digit.
let Some(first_char) = part.chars().next() else {
return Err(Error::IdPartEmpty(start));
};
if idx == 0 && !first_char.is_ascii_alphabetic() {
return Err(Error::InvalidCharInId(start, first_char));
}
let mut upper = None;
for ch in part.chars() {
if ch.is_ascii_digit() {
// Digits are accepted in both uppercase and lowercase segments.
} else if ch.is_ascii_uppercase() {
if upper.is_none() {
upper = Some(true);
} else if let Some(false) = upper {
return Err(Error::InvalidCharInId(start, ch));
}
} else if ch.is_ascii_lowercase() {
if upper.is_none() {
upper = Some(false);
} else if let Some(true) = upper {
return Err(Error::InvalidCharInId(start, ch));
}
} else {
return Err(Error::InvalidCharInId(start, ch));
}
}
}
Ok(())
}
impl Token {
pub fn describe(&self) -> &'static str {
match self {
Whitespace => "whitespace",
Comment => "a comment",
Equals => "'='",
Comma => "','",
Colon => "':'",
Period => "'.'",
Semicolon => "';'",
LeftParen => "'('",
RightParen => "')'",
LeftBrace => "'{'",
RightBrace => "'}'",
LessThan => "'<'",
GreaterThan => "'>'",
Use => "keyword `use`",
Type => "keyword `type`",
Func => "keyword `func`",
U8 => "keyword `u8`",
U16 => "keyword `u16`",
U32 => "keyword `u32`",
U64 => "keyword `u64`",
S8 => "keyword `s8`",
S16 => "keyword `s16`",
S32 => "keyword `s32`",
S64 => "keyword `s64`",
F32 => "keyword `f32`",
F64 => "keyword `f64`",
Char => "keyword `char`",
Own => "keyword `own`",
Borrow => "keyword `borrow`",
Resource => "keyword `resource`",
Record => "keyword `record`",
Flags => "keyword `flags`",
Variant => "keyword `variant`",
Enum => "keyword `enum`",
Bool => "keyword `bool`",
String_ => "keyword `string`",
Option_ => "keyword `option`",
Result_ => "keyword `result`",
Future => "keyword `future`",
Stream => "keyword `stream`",
ErrorContext => "keyword `error-context`",
List => "keyword `list`",
Map => "keyword `map`",
Underscore => "keyword `_`",
Id => "an identifier",
ExplicitId => "an '%' identifier",
RArrow => "`->`",
Star => "`*`",
At => "`@`",
Slash => "`/`",
Plus => "`+`",
Minus => "`-`",
As => "keyword `as`",
From_ => "keyword `from`",
Static => "keyword `static`",
Interface => "keyword `interface`",
Tuple => "keyword `tuple`",
Import => "keyword `import`",
Export => "keyword `export`",
World => "keyword `world`",
Package => "keyword `package`",
Constructor => "keyword `constructor`",
Integer => "an integer",
Include => "keyword `include`",
With => "keyword `with`",
Async => "keyword `async`",
}
}
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Unexpected(_, ch) => write!(f, "unexpected character {ch:?}"),
Error::UnterminatedComment(_) => write!(f, "unterminated block comment"),
Error::Wanted {
expected, found, ..
} => write!(f, "expected {expected}, found {found}"),
Error::InvalidCharInId(_, ch) => write!(f, "invalid character in identifier {ch:?}"),
Error::IdPartEmpty(_) => write!(f, "identifiers must have characters between '-'s"),
Error::InvalidEscape(_, ch) => write!(f, "invalid escape in string {ch:?}"),
}
}
}
#[test]
fn test_validate_id() {
validate_id(0, "apple").unwrap();
validate_id(0, "apple-pear").unwrap();
validate_id(0, "apple-pear-grape").unwrap();
validate_id(0, "a0").unwrap();
validate_id(0, "a").unwrap();
validate_id(0, "a-a").unwrap();
validate_id(0, "bool").unwrap();
validate_id(0, "APPLE").unwrap();
validate_id(0, "APPLE-PEAR").unwrap();
validate_id(0, "APPLE-PEAR-GRAPE").unwrap();
validate_id(0, "apple-PEAR-grape").unwrap();
validate_id(0, "APPLE-pear-GRAPE").unwrap();
validate_id(0, "ENOENT").unwrap();
validate_id(0, "is-XML").unwrap();
validate_id(0, "apple-0").unwrap();
validate_id(0, "a0-000-3d4a-54FF").unwrap();
assert!(validate_id(0, "").is_err());
assert!(validate_id(0, "0").is_err());
assert!(validate_id(0, "%").is_err());
assert!(validate_id(0, "$").is_err());
assert!(validate_id(0, "0a").is_err());
assert!(validate_id(0, ".").is_err());
assert!(validate_id(0, "·").is_err());
assert!(validate_id(0, "a a").is_err());
assert!(validate_id(0, "_").is_err());
assert!(validate_id(0, "-").is_err());
assert!(validate_id(0, "a-").is_err());
assert!(validate_id(0, "-a").is_err());
assert!(validate_id(0, "Apple").is_err());
assert!(validate_id(0, "applE").is_err());
assert!(validate_id(0, "-apple-pear").is_err());
assert!(validate_id(0, "apple-pear-").is_err());
assert!(validate_id(0, "apple_pear").is_err());
assert!(validate_id(0, "apple.pear").is_err());
assert!(validate_id(0, "apple pear").is_err());
assert!(validate_id(0, "apple/pear").is_err());
assert!(validate_id(0, "apple|pear").is_err());
assert!(validate_id(0, "apple-Pear").is_err());
assert!(validate_id(0, "()()").is_err());
assert!(validate_id(0, "").is_err());
assert!(validate_id(0, "*").is_err());
assert!(validate_id(0, "apple\u{5f3}pear").is_err());
assert!(validate_id(0, "apple\u{200c}pear").is_err());
assert!(validate_id(0, "apple\u{200d}pear").is_err());
assert!(validate_id(0, "apple--pear").is_err());
assert!(validate_id(0, "_apple").is_err());
assert!(validate_id(0, "apple_").is_err());
assert!(validate_id(0, "_Znwj").is_err());
assert!(validate_id(0, "__i386").is_err());
assert!(validate_id(0, "__i386__").is_err());
assert!(validate_id(0, "Москва").is_err());
assert!(validate_id(0, "garçon-hühnervögel-Москва-東京").is_err());
assert!(validate_id(0, "a0-000-3d4A-54Ff").is_err());
assert!(validate_id(0, "😼").is_err(), "non-identifier");
assert!(validate_id(0, "\u{212b}").is_err(), "non-ascii");
}
#[test]
fn test_tokenizer() {
fn collect(s: &str) -> Result<Vec<Token>> {
let mut t = Tokenizer::new(s, 0, None)?;
let mut tokens = Vec::new();
while let Some(token) = t.next()? {
tokens.push(token.1);
}
Ok(tokens)
}
assert_eq!(collect("").unwrap(), vec![]);
assert_eq!(collect("_").unwrap(), vec![Token::Underscore]);
assert_eq!(collect("apple").unwrap(), vec![Token::Id]);
assert_eq!(collect("apple-pear").unwrap(), vec![Token::Id]);
assert_eq!(collect("apple--pear").unwrap(), vec![Token::Id]);
assert_eq!(collect("apple-Pear").unwrap(), vec![Token::Id]);
assert_eq!(collect("apple-pear-grape").unwrap(), vec![Token::Id]);
assert_eq!(collect("apple pear").unwrap(), vec![Token::Id, Token::Id]);
assert_eq!(collect("_a_p_p_l_e_").unwrap(), vec![Token::Id]);
assert_eq!(collect("garçon").unwrap(), vec![Token::Id]);
assert_eq!(collect("hühnervögel").unwrap(), vec![Token::Id]);
assert_eq!(collect("москва").unwrap(), vec![Token::Id]);
assert_eq!(collect("東京").unwrap(), vec![Token::Id]);
assert_eq!(
collect("garçon-hühnervögel-москва-東京").unwrap(),
vec![Token::Id]
);
assert_eq!(collect("a0").unwrap(), vec![Token::Id]);
assert_eq!(collect("a").unwrap(), vec![Token::Id]);
assert_eq!(collect("%a").unwrap(), vec![Token::ExplicitId]);
assert_eq!(collect("%a-a").unwrap(), vec![Token::ExplicitId]);
assert_eq!(collect("%bool").unwrap(), vec![Token::ExplicitId]);
assert_eq!(collect("%").unwrap(), vec![Token::ExplicitId]);
assert_eq!(collect("APPLE").unwrap(), vec![Token::Id]);
assert_eq!(collect("APPLE-PEAR").unwrap(), vec![Token::Id]);
assert_eq!(collect("APPLE-PEAR-GRAPE").unwrap(), vec![Token::Id]);
assert_eq!(collect("apple-PEAR-grape").unwrap(), vec![Token::Id]);
assert_eq!(collect("APPLE-pear-GRAPE").unwrap(), vec![Token::Id]);
assert_eq!(collect("ENOENT").unwrap(), vec![Token::Id]);
assert_eq!(collect("is-XML").unwrap(), vec![Token::Id]);
assert_eq!(collect("func").unwrap(), vec![Token::Func]);
assert_eq!(
collect("a: func()").unwrap(),
vec![
Token::Id,
Token::Colon,
Token::Func,
Token::LeftParen,
Token::RightParen
]
);
assert_eq!(collect("resource").unwrap(), vec![Token::Resource]);
assert_eq!(collect("own").unwrap(), vec![Token::Own]);
assert_eq!(
collect("own<some-id>").unwrap(),
vec![Token::Own, Token::LessThan, Token::Id, Token::GreaterThan]
);
assert_eq!(collect("borrow").unwrap(), vec![Token::Borrow]);
assert_eq!(
collect("borrow<some-id>").unwrap(),
vec![
Token::Borrow,
Token::LessThan,
Token::Id,
Token::GreaterThan
]
);
assert!(collect("\u{149}").is_err(), "strongly discouraged");
assert!(collect("\u{673}").is_err(), "strongly discouraged");
assert!(collect("\u{17a3}").is_err(), "strongly discouraged");
assert!(collect("\u{17a4}").is_err(), "strongly discouraged");
assert!(collect("\u{202a}").is_err(), "bidirectional override");
assert!(collect("\u{2068}").is_err(), "bidirectional override");
assert!(collect("\u{0}").is_err(), "control code");
assert!(collect("\u{b}").is_err(), "control code");
assert!(collect("\u{c}").is_err(), "control code");
assert!(collect("\u{85}").is_err(), "control code");
}

1813
vendor/wit-parser/src/ast/resolve.rs vendored Normal file

File diff suppressed because it is too large Load Diff

249
vendor/wit-parser/src/ast/toposort.rs vendored Normal file
View File

@@ -0,0 +1,249 @@
use crate::ast::{Id, Span};
use anyhow::Result;
use indexmap::IndexMap;
use std::collections::BinaryHeap;
use std::fmt;
use std::mem;
#[derive(Default, Clone)]
struct State {
/// Number of outbound edges from this node which have still not been
/// processed into the topological ordering.
outbound_remaining: usize,
/// Indices of nodes that depend on this one, used when this node is added
/// to the binary heap to decrement `outbound_remaining`.
reverse_deps: Vec<usize>,
}
/// Performs a topological sort of the `deps` provided, returning the order in
/// which to visit the nodes in reverse-dep order.
///
/// This sort goes one level further as well to produce a stable ordering
/// regardless of the input edges so long as the structure of the graph has
/// changed. Notably the nodes are sorted, by name, in the output in addition to
/// being sorted in dependency order. This is done to assist with round-tripping
/// documents where new edges are discovered during world elaboration that
/// doesn't change the dependency graph but can change the dependency listings
/// between serializations.
///
/// The algorithm chosen here to do this is:
///
/// * Build some metadata about all nodes including their count of outbound
/// edges remaining to be added to the order and a reverse dependency list.
/// * Collect all nodes with 0 outbound edges into a binary heap.
/// * Pop from the binary heap and decrement outbound edges that depend on
/// this node.
/// * Iterate until the dependency ordering is the same size as the dependency
/// array.
///
/// This sort will also detect when dependencies are missing or when cycles are
/// present and return an error.
pub fn toposort<'a>(
kind: &str,
deps: &IndexMap<&'a str, Vec<Id<'a>>>,
) -> Result<Vec<&'a str>, Error> {
// Initialize a `State` per-node with the number of outbound edges and
// additionally filling out the `reverse_deps` array.
let mut states = vec![State::default(); deps.len()];
for (i, (_, edges)) in deps.iter().enumerate() {
states[i].outbound_remaining = edges.len();
for edge in edges {
let (j, _, _) = deps
.get_full(edge.name)
.ok_or_else(|| Error::NonexistentDep {
span: edge.span,
name: edge.name.to_string(),
kind: kind.to_string(),
highlighted: None,
})?;
states[j].reverse_deps.push(i);
}
}
let mut order = Vec::new();
let mut heap = BinaryHeap::new();
// Seed the `heap` with edges that have no outbound edges
//
// The heap here is keyed by `(usize, &str, usize)` where the first `usize`
// is unique which is what determines the order of the heap. The other two
// fields are metadata used when pulling from the heap. The first `usize` is
// the index of the item within the original dependency map which should
// reflect the original source order of the item. Note that this is stored
// in reverse order to ensure that when there are multiple items in the heap
// the first item in the original order is popped first.
for (i, dep) in deps.keys().enumerate() {
if states[i].outbound_remaining == 0 {
heap.push((deps.len() - i, *dep, i));
}
}
// Drain the binary heap which represents all nodes that have had all their
// dependencies processed. Iteratively add to the heap as well as nodes are
// removed.
while let Some((_order, node, i)) = heap.pop() {
order.push(node);
for i in mem::take(&mut states[i].reverse_deps) {
states[i].outbound_remaining -= 1;
if states[i].outbound_remaining == 0 {
let (dep, _) = deps.get_index(i).unwrap();
heap.push((deps.len() - i, *dep, i));
}
}
}
// If all nodes are present in order then a topological ordering was
// achieved and it can be returned.
if order.len() == deps.len() {
return Ok(order);
}
// ... otherwise there are still dependencies with remaining edges which
// means that a cycle must be present, so find the cycle and report the
// error.
for (i, state) in states.iter().enumerate() {
if state.outbound_remaining == 0 {
continue;
}
let (_, edges) = deps.get_index(i).unwrap();
for dep in edges {
let (j, _, _) = deps.get_full(dep.name).unwrap();
if states[j].outbound_remaining == 0 {
continue;
}
return Err(Error::Cycle {
span: dep.span,
name: dep.name.to_string(),
kind: kind.to_string(),
highlighted: None,
});
}
}
unreachable!()
}
#[derive(Debug)]
pub enum Error {
NonexistentDep {
span: Span,
name: String,
kind: String,
highlighted: Option<String>,
},
Cycle {
span: Span,
name: String,
kind: String,
highlighted: Option<String>,
},
}
impl Error {
pub(crate) fn highlighted(&self) -> Option<&str> {
match self {
Error::NonexistentDep { highlighted, .. } | Error::Cycle { highlighted, .. } => {
highlighted.as_deref()
}
}
}
pub(crate) fn set_highlighted(&mut self, string: String) {
match self {
Error::NonexistentDep { highlighted, .. } | Error::Cycle { highlighted, .. } => {
*highlighted = Some(string);
}
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(s) = self.highlighted() {
return f.write_str(s);
}
match self {
Error::NonexistentDep { kind, name, .. } => {
write!(f, "{kind} `{name}` does not exist")
}
Error::Cycle { kind, name, .. } => {
write!(f, "{kind} `{name}` depends on itself")
}
}
}
}
impl std::error::Error for Error {}
#[cfg(test)]
mod tests {
use super::*;
fn id(name: &str) -> Id<'_> {
Id {
name,
span: Span { start: 0, end: 0 },
}
}
#[test]
fn smoke() {
let empty: Vec<&str> = Vec::new();
assert_eq!(toposort("", &IndexMap::new()).unwrap(), empty);
let mut nonexistent = IndexMap::new();
nonexistent.insert("a", vec![id("b")]);
assert!(matches!(
toposort("", &nonexistent),
Err(Error::NonexistentDep { .. })
));
let mut one = IndexMap::new();
one.insert("a", vec![]);
assert_eq!(toposort("", &one).unwrap(), ["a"]);
let mut two = IndexMap::new();
two.insert("a", vec![]);
two.insert("b", vec![id("a")]);
assert_eq!(toposort("", &two).unwrap(), ["a", "b"]);
let mut two = IndexMap::new();
two.insert("a", vec![id("b")]);
two.insert("b", vec![]);
assert_eq!(toposort("", &two).unwrap(), ["b", "a"]);
}
#[test]
fn cycles() {
let mut cycle = IndexMap::new();
cycle.insert("a", vec![id("a")]);
assert!(matches!(toposort("", &cycle), Err(Error::Cycle { .. })));
let mut cycle = IndexMap::new();
cycle.insert("a", vec![id("b")]);
cycle.insert("b", vec![id("c")]);
cycle.insert("c", vec![id("a")]);
assert!(matches!(toposort("", &cycle), Err(Error::Cycle { .. })));
}
#[test]
fn depend_twice() {
let mut two = IndexMap::new();
two.insert("b", vec![id("a"), id("a")]);
two.insert("a", vec![]);
assert_eq!(toposort("", &two).unwrap(), ["a", "b"]);
}
#[test]
fn preserve_order() {
let mut order = IndexMap::new();
order.insert("a", vec![]);
order.insert("b", vec![]);
assert_eq!(toposort("", &order).unwrap(), ["a", "b"]);
let mut order = IndexMap::new();
order.insert("b", vec![]);
order.insert("a", vec![]);
assert_eq!(toposort("", &order).unwrap(), ["b", "a"]);
}
}

1864
vendor/wit-parser/src/decoding.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1413
vendor/wit-parser/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

264
vendor/wit-parser/src/live.rs vendored Normal file
View File

@@ -0,0 +1,264 @@
use crate::{
Function, FunctionKind, InterfaceId, Resolve, Type, TypeDef, TypeDefKind, TypeId, WorldId,
WorldItem,
};
use indexmap::IndexSet;
#[derive(Default)]
pub struct LiveTypes {
set: IndexSet<TypeId>,
}
impl LiveTypes {
pub fn iter(&self) -> impl Iterator<Item = TypeId> + '_ {
self.set.iter().copied()
}
pub fn len(&self) -> usize {
self.set.len()
}
pub fn contains(&self, id: TypeId) -> bool {
self.set.contains(&id)
}
pub fn add_interface(&mut self, resolve: &Resolve, iface: InterfaceId) {
self.visit_interface(resolve, iface);
}
pub fn add_world(&mut self, resolve: &Resolve, world: WorldId) {
self.visit_world(resolve, world);
}
pub fn add_world_item(&mut self, resolve: &Resolve, item: &WorldItem) {
self.visit_world_item(resolve, item);
}
pub fn add_func(&mut self, resolve: &Resolve, func: &Function) {
self.visit_func(resolve, func);
}
pub fn add_type_id(&mut self, resolve: &Resolve, ty: TypeId) {
self.visit_type_id(resolve, ty);
}
pub fn add_type(&mut self, resolve: &Resolve, ty: &Type) {
self.visit_type(resolve, ty);
}
}
impl TypeIdVisitor for LiveTypes {
fn before_visit_type_id(&mut self, id: TypeId) -> bool {
!self.set.contains(&id)
}
fn after_visit_type_id(&mut self, id: TypeId) {
assert!(self.set.insert(id));
}
}
/// Helper trait to walk the structure of a type and visit all `TypeId`s that
/// it refers to, possibly transitively.
pub trait TypeIdVisitor {
/// Callback invoked just before a type is visited.
///
/// If this function returns `false` the type is not visited, otherwise it's
/// recursed into.
fn before_visit_type_id(&mut self, id: TypeId) -> bool {
let _ = id;
true
}
/// Callback invoked once a type is finished being visited.
fn after_visit_type_id(&mut self, id: TypeId) {
let _ = id;
}
fn visit_interface(&mut self, resolve: &Resolve, iface: InterfaceId) {
let iface = &resolve.interfaces[iface];
for (_, id) in iface.types.iter() {
self.visit_type_id(resolve, *id);
}
for (_, func) in iface.functions.iter() {
self.visit_func(resolve, func);
}
}
fn visit_world(&mut self, resolve: &Resolve, world: WorldId) {
let world = &resolve.worlds[world];
for (_, item) in world.imports.iter().chain(world.exports.iter()) {
self.visit_world_item(resolve, item);
}
}
fn visit_world_item(&mut self, resolve: &Resolve, item: &WorldItem) {
match item {
WorldItem::Interface { id, .. } => self.visit_interface(resolve, *id),
WorldItem::Function(f) => self.visit_func(resolve, f),
WorldItem::Type(t) => self.visit_type_id(resolve, *t),
}
}
fn visit_func(&mut self, resolve: &Resolve, func: &Function) {
match func.kind {
// This resource is live as it's attached to a static method but
// it's not guaranteed to be present in either params or results, so
// be sure to attach it here.
FunctionKind::Static(id) | FunctionKind::AsyncStatic(id) => {
self.visit_type_id(resolve, id)
}
// The resource these are attached to is in the params/results, so
// no need to re-add it here.
FunctionKind::Method(_)
| FunctionKind::AsyncMethod(_)
| FunctionKind::Constructor(_) => {}
FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {}
}
for (_, ty) in func.params.iter() {
self.visit_type(resolve, ty);
}
if let Some(ty) = &func.result {
self.visit_type(resolve, ty);
}
}
fn visit_type_id(&mut self, resolve: &Resolve, ty: TypeId) {
if self.before_visit_type_id(ty) {
self.visit_type_def(resolve, &resolve.types[ty]);
self.after_visit_type_id(ty);
}
}
fn visit_type_def(&mut self, resolve: &Resolve, ty: &TypeDef) {
match &ty.kind {
TypeDefKind::Type(t)
| TypeDefKind::List(t)
| TypeDefKind::FixedSizeList(t, ..)
| TypeDefKind::Option(t)
| TypeDefKind::Future(Some(t))
| TypeDefKind::Stream(Some(t)) => self.visit_type(resolve, t),
TypeDefKind::Map(k, v) => {
self.visit_type(resolve, k);
self.visit_type(resolve, v);
}
TypeDefKind::Handle(handle) => match handle {
crate::Handle::Own(ty) => self.visit_type_id(resolve, *ty),
crate::Handle::Borrow(ty) => self.visit_type_id(resolve, *ty),
},
TypeDefKind::Resource => {}
TypeDefKind::Record(r) => {
for field in r.fields.iter() {
self.visit_type(resolve, &field.ty);
}
}
TypeDefKind::Tuple(r) => {
for ty in r.types.iter() {
self.visit_type(resolve, ty);
}
}
TypeDefKind::Variant(v) => {
for case in v.cases.iter() {
if let Some(ty) = &case.ty {
self.visit_type(resolve, ty);
}
}
}
TypeDefKind::Result(r) => {
if let Some(ty) = &r.ok {
self.visit_type(resolve, ty);
}
if let Some(ty) = &r.err {
self.visit_type(resolve, ty);
}
}
TypeDefKind::Flags(_)
| TypeDefKind::Enum(_)
| TypeDefKind::Future(None)
| TypeDefKind::Stream(None) => {}
TypeDefKind::Unknown => unreachable!(),
}
}
fn visit_type(&mut self, resolve: &Resolve, ty: &Type) {
match ty {
Type::Id(id) => self.visit_type_id(resolve, *id),
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::{LiveTypes, Resolve};
fn live(wit: &str, ty: &str) -> Vec<String> {
let mut resolve = Resolve::default();
resolve.push_str("test.wit", wit).unwrap();
let (_, interface) = resolve.interfaces.iter().next_back().unwrap();
let ty = interface.types[ty];
let mut live = LiveTypes::default();
live.add_type_id(&resolve, ty);
live.iter()
.filter_map(|ty| resolve.types[ty].name.clone())
.collect()
}
#[test]
fn no_deps() {
let types = live(
"
package foo:bar;
interface foo {
type t = u32;
}
",
"t",
);
assert_eq!(types, ["t"]);
}
#[test]
fn one_dep() {
let types = live(
"
package foo:bar;
interface foo {
type t = u32;
type u = t;
}
",
"u",
);
assert_eq!(types, ["t", "u"]);
}
#[test]
fn chain() {
let types = live(
"
package foo:bar;
interface foo {
resource t1;
record t2 {
x: t1,
}
variant t3 {
x(t2),
}
flags t4 { a }
enum t5 { a }
type t6 = tuple<t5, t4, t3>;
}
",
"t6",
);
assert_eq!(types, ["t5", "t4", "t1", "t2", "t3", "t6"]);
}
}

772
vendor/wit-parser/src/metadata.rs vendored Normal file
View File

@@ -0,0 +1,772 @@
//! Implementation of encoding/decoding package metadata (docs/stability) in a
//! custom section.
//!
//! This module contains the particulars for how this custom section is encoded
//! and decoded at this time. As of the time of this writing the component model
//! binary format does not have any means of storing documentation and/or item
//! stability inline with items themselves. These are important to preserve when
//! round-tripping WIT through the WebAssembly binary format, however, so this
//! module implements this with a custom section.
//!
//! The custom section, named `SECTION_NAME`, is stored within the component
//! that encodes a WIT package. This section is itself JSON-encoded with a small
//! version header to help forwards/backwards compatibility. The hope is that
//! one day this custom section will be obsoleted by extensions to the binary
//! format to store this information inline.
use crate::{
Docs, Function, InterfaceId, PackageId, Resolve, Stability, TypeDefKind, TypeId, WorldId,
WorldItem, WorldKey,
};
use anyhow::{Result, bail};
use indexmap::IndexMap;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
type StringMap<V> = IndexMap<String, V>;
/// Current supported format of the custom section.
///
/// This byte is a prefix byte intended to be a general version marker for the
/// entire custom section. This is bumped when backwards-incompatible changes
/// are made to prevent older implementations from loading newer versions.
///
/// The history of this is:
///
/// * [????/??/??] 0 - the original format added
/// * [2024/04/19] 1 - extensions were added for item stability and
/// additionally having world imports/exports have the same name.
#[cfg(feature = "serde")]
const PACKAGE_DOCS_SECTION_VERSION: u8 = 1;
/// At this time the v1 format was just written. For compatibility with older
/// tools we'll still try to emit the v0 format by default, if the input is
/// compatible. This will be turned off in the future once enough published
/// versions support the v1 format.
const TRY_TO_EMIT_V0_BY_DEFAULT: bool = false;
/// Represents serializable doc comments parsed from a WIT package.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct PackageMetadata {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
docs: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
worlds: StringMap<WorldMetadata>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
interfaces: StringMap<InterfaceMetadata>,
}
impl PackageMetadata {
pub const SECTION_NAME: &'static str = "package-docs";
/// Extract package docs for the given package.
pub fn extract(resolve: &Resolve, package: PackageId) -> Self {
let package = &resolve.packages[package];
let worlds = package
.worlds
.iter()
.map(|(name, id)| (name.to_string(), WorldMetadata::extract(resolve, *id)))
.filter(|(_, item)| !item.is_empty())
.collect();
let interfaces = package
.interfaces
.iter()
.map(|(name, id)| (name.to_string(), InterfaceMetadata::extract(resolve, *id)))
.filter(|(_, item)| !item.is_empty())
.collect();
Self {
docs: package.docs.contents.as_deref().map(Into::into),
worlds,
interfaces,
}
}
/// Inject package docs for the given package.
///
/// This will override any existing docs in the [`Resolve`].
pub fn inject(&self, resolve: &mut Resolve, package: PackageId) -> Result<()> {
for (name, docs) in &self.worlds {
let Some(&id) = resolve.packages[package].worlds.get(name) else {
bail!("missing world {name:?}");
};
docs.inject(resolve, id)?;
}
for (name, docs) in &self.interfaces {
let Some(&id) = resolve.packages[package].interfaces.get(name) else {
bail!("missing interface {name:?}");
};
docs.inject(resolve, id)?;
}
if let Some(docs) = &self.docs {
resolve.packages[package].docs.contents = Some(docs.to_string());
}
Ok(())
}
/// Encode package docs as a package-docs custom section.
#[cfg(feature = "serde")]
pub fn encode(&self) -> Result<Vec<u8>> {
// Version byte, followed by JSON encoding of docs.
//
// Note that if this document is compatible with the v0 format then
// that's preferred to keep older tools working at this time.
// Eventually this branch will be removed and v1 will unconditionally
// be used.
let mut data = vec![
if TRY_TO_EMIT_V0_BY_DEFAULT && self.is_compatible_with_v0() {
0
} else {
PACKAGE_DOCS_SECTION_VERSION
},
];
serde_json::to_writer(&mut data, self)?;
Ok(data)
}
/// Decode package docs from package-docs custom section content.
#[cfg(feature = "serde")]
pub fn decode(data: &[u8]) -> Result<Self> {
match data.first().copied() {
// Our serde structures transparently support v0 and the current
// version, so allow either here.
Some(0) | Some(PACKAGE_DOCS_SECTION_VERSION) => {}
version => {
bail!(
"expected package-docs version {PACKAGE_DOCS_SECTION_VERSION}, got {version:?}"
);
}
}
Ok(serde_json::from_slice(&data[1..])?)
}
#[cfg(feature = "serde")]
fn is_compatible_with_v0(&self) -> bool {
self.worlds.iter().all(|(_, w)| w.is_compatible_with_v0())
&& self
.interfaces
.iter()
.all(|(_, w)| w.is_compatible_with_v0())
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
struct WorldMetadata {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
docs: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Stability::is_unknown")
)]
stability: Stability,
/// Metadata for named interface, e.g.:
///
/// ```wit
/// world foo {
/// import x: interface {}
/// }
/// ```
///
/// In the v0 format this was called "interfaces", hence the
/// `serde(rename)`. When support was originally added here imports/exports
/// could not overlap in their name, but now they can. This map has thus
/// been repurposed as:
///
/// * If an interface is imported, it goes here.
/// * If an interface is exported, and no interface was imported with the
/// same name, it goes here.
///
/// Otherwise exports go inside the `interface_exports` map.
///
/// In the future when v0 support is dropped this should become only
/// imports, not either imports-or-exports.
#[cfg_attr(
feature = "serde",
serde(
default,
rename = "interfaces",
skip_serializing_if = "StringMap::is_empty"
)
)]
interface_imports_or_exports: StringMap<InterfaceMetadata>,
/// All types in this interface.
///
/// Note that at this time types are only imported, never exported.
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
types: StringMap<TypeMetadata>,
/// Same as `interface_imports_or_exports`, but for functions.
#[cfg_attr(
feature = "serde",
serde(default, rename = "funcs", skip_serializing_if = "StringMap::is_empty")
)]
func_imports_or_exports: StringMap<FunctionMetadata>,
/// The "export half" of `interface_imports_or_exports`.
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
interface_exports: StringMap<InterfaceMetadata>,
/// The "export half" of `func_imports_or_exports`.
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
func_exports: StringMap<FunctionMetadata>,
/// Stability annotations for interface imports that aren't inline, for
/// example:
///
/// ```wit
/// world foo {
/// @since(version = 1.0.0)
/// import an-interface;
/// }
/// ```
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
interface_import_stability: StringMap<Stability>,
/// Same as `interface_import_stability`, but for exports.
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
interface_export_stability: StringMap<Stability>,
}
impl WorldMetadata {
fn extract(resolve: &Resolve, id: WorldId) -> Self {
let world = &resolve.worlds[id];
let mut interface_imports_or_exports = StringMap::default();
let mut types = StringMap::default();
let mut func_imports_or_exports = StringMap::default();
let mut interface_exports = StringMap::default();
let mut func_exports = StringMap::default();
let mut interface_import_stability = StringMap::default();
let mut interface_export_stability = StringMap::default();
for ((key, item), import) in world
.imports
.iter()
.map(|p| (p, true))
.chain(world.exports.iter().map(|p| (p, false)))
{
match key {
// For all named imports with kebab-names extract their
// docs/stability and insert it into one of our maps.
WorldKey::Name(name) => match item {
WorldItem::Interface { id, .. } => {
let data = InterfaceMetadata::extract(resolve, *id);
if data.is_empty() {
continue;
}
let map = if import {
&mut interface_imports_or_exports
} else if !TRY_TO_EMIT_V0_BY_DEFAULT
|| interface_imports_or_exports.contains_key(name)
{
&mut interface_exports
} else {
&mut interface_imports_or_exports
};
let prev = map.insert(name.to_string(), data);
assert!(prev.is_none());
}
WorldItem::Type(id) => {
let data = TypeMetadata::extract(resolve, *id);
if !data.is_empty() {
types.insert(name.to_string(), data);
}
}
WorldItem::Function(f) => {
let data = FunctionMetadata::extract(f);
if data.is_empty() {
continue;
}
let map = if import {
&mut func_imports_or_exports
} else if !TRY_TO_EMIT_V0_BY_DEFAULT
|| func_imports_or_exports.contains_key(name)
{
&mut func_exports
} else {
&mut func_imports_or_exports
};
let prev = map.insert(name.to_string(), data);
assert!(prev.is_none());
}
},
// For interface imports/exports extract the stability and
// record it if necessary.
WorldKey::Interface(_) => {
let stability = match item {
WorldItem::Interface { stability, .. } => stability,
_ => continue,
};
if stability.is_unknown() {
continue;
}
let map = if import {
&mut interface_import_stability
} else {
&mut interface_export_stability
};
let name = resolve.name_world_key(key);
map.insert(name, stability.clone());
}
}
}
Self {
docs: world.docs.contents.clone(),
stability: world.stability.clone(),
interface_imports_or_exports,
types,
func_imports_or_exports,
interface_exports,
func_exports,
interface_import_stability,
interface_export_stability,
}
}
fn inject(&self, resolve: &mut Resolve, id: WorldId) -> Result<()> {
// Inject docs/stability for all kebab-named interfaces, both imports
// and exports.
for ((name, data), only_export) in self
.interface_imports_or_exports
.iter()
.map(|p| (p, false))
.chain(self.interface_exports.iter().map(|p| (p, true)))
{
let key = WorldKey::Name(name.to_string());
let world = &mut resolve.worlds[id];
let item = if only_export {
world.exports.get_mut(&key)
} else {
match world.imports.get_mut(&key) {
Some(item) => Some(item),
None => world.exports.get_mut(&key),
}
};
let Some(WorldItem::Interface { id, stability }) = item else {
bail!("missing interface {name:?}");
};
*stability = data.stability.clone();
let id = *id;
data.inject(resolve, id)?;
}
// Process all types, which are always imported, for this world.
for (name, data) in &self.types {
let key = WorldKey::Name(name.to_string());
let Some(WorldItem::Type(id)) = resolve.worlds[id].imports.get(&key) else {
bail!("missing type {name:?}");
};
data.inject(resolve, *id)?;
}
// Build a map of `name_world_key` for interface imports/exports to the
// actual key. This map is then consluted in the next loop.
let world = &resolve.worlds[id];
let stabilities = world
.imports
.iter()
.map(|i| (i, true))
.chain(world.exports.iter().map(|i| (i, false)))
.filter_map(|((key, item), import)| match item {
WorldItem::Interface { .. } => {
Some(((resolve.name_world_key(key), import), key.clone()))
}
_ => None,
})
.collect::<IndexMap<_, _>>();
let world = &mut resolve.worlds[id];
// Update the stability of an interface imports/exports that aren't
// kebab-named.
for ((name, stability), import) in self
.interface_import_stability
.iter()
.map(|p| (p, true))
.chain(self.interface_export_stability.iter().map(|p| (p, false)))
{
let key = match stabilities.get(&(name.clone(), import)) {
Some(key) => key.clone(),
None => bail!("missing interface `{name}`"),
};
let item = if import {
world.imports.get_mut(&key)
} else {
world.exports.get_mut(&key)
};
match item {
Some(WorldItem::Interface { stability: s, .. }) => *s = stability.clone(),
_ => bail!("item `{name}` wasn't an interface"),
}
}
// Update the docs/stability of all functions imported/exported from
// this world.
for ((name, data), only_export) in self
.func_imports_or_exports
.iter()
.map(|p| (p, false))
.chain(self.func_exports.iter().map(|p| (p, true)))
{
let key = WorldKey::Name(name.to_string());
let item = if only_export {
world.exports.get_mut(&key)
} else {
match world.imports.get_mut(&key) {
Some(item) => Some(item),
None => world.exports.get_mut(&key),
}
};
match item {
Some(WorldItem::Function(f)) => data.inject(f)?,
_ => bail!("missing func {name:?}"),
}
}
if let Some(docs) = &self.docs {
world.docs.contents = Some(docs.to_string());
}
world.stability = self.stability.clone();
Ok(())
}
fn is_empty(&self) -> bool {
self.docs.is_none()
&& self.interface_imports_or_exports.is_empty()
&& self.types.is_empty()
&& self.func_imports_or_exports.is_empty()
&& self.stability.is_unknown()
&& self.interface_exports.is_empty()
&& self.func_exports.is_empty()
&& self.interface_import_stability.is_empty()
&& self.interface_export_stability.is_empty()
}
#[cfg(feature = "serde")]
fn is_compatible_with_v0(&self) -> bool {
self.stability.is_unknown()
&& self
.interface_imports_or_exports
.iter()
.all(|(_, w)| w.is_compatible_with_v0())
&& self
.func_imports_or_exports
.iter()
.all(|(_, w)| w.is_compatible_with_v0())
&& self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
// These maps weren't present in v0, so we're only compatible if
// they're empty.
&& self.interface_exports.is_empty()
&& self.func_exports.is_empty()
&& self.interface_import_stability.is_empty()
&& self.interface_export_stability.is_empty()
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
struct InterfaceMetadata {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
docs: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Stability::is_unknown")
)]
stability: Stability,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
funcs: StringMap<FunctionMetadata>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
types: StringMap<TypeMetadata>,
}
impl InterfaceMetadata {
fn extract(resolve: &Resolve, id: InterfaceId) -> Self {
let interface = &resolve.interfaces[id];
let funcs = interface
.functions
.iter()
.map(|(name, func)| (name.to_string(), FunctionMetadata::extract(func)))
.filter(|(_, item)| !item.is_empty())
.collect();
let types = interface
.types
.iter()
.map(|(name, id)| (name.to_string(), TypeMetadata::extract(resolve, *id)))
.filter(|(_, item)| !item.is_empty())
.collect();
Self {
docs: interface.docs.contents.clone(),
stability: interface.stability.clone(),
funcs,
types,
}
}
fn inject(&self, resolve: &mut Resolve, id: InterfaceId) -> Result<()> {
for (name, data) in &self.types {
let Some(&id) = resolve.interfaces[id].types.get(name) else {
bail!("missing type {name:?}");
};
data.inject(resolve, id)?;
}
let interface = &mut resolve.interfaces[id];
for (name, data) in &self.funcs {
let Some(f) = interface.functions.get_mut(name) else {
bail!("missing func {name:?}");
};
data.inject(f)?;
}
if let Some(docs) = &self.docs {
interface.docs.contents = Some(docs.to_string());
}
interface.stability = self.stability.clone();
Ok(())
}
fn is_empty(&self) -> bool {
self.docs.is_none()
&& self.funcs.is_empty()
&& self.types.is_empty()
&& self.stability.is_unknown()
}
#[cfg(feature = "serde")]
fn is_compatible_with_v0(&self) -> bool {
self.stability.is_unknown()
&& self.funcs.iter().all(|(_, w)| w.is_compatible_with_v0())
&& self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged, deny_unknown_fields))]
enum FunctionMetadata {
/// In the v0 format function metadata was only a string so this variant
/// is preserved for the v0 format. In the future this can be removed
/// entirely in favor of just the below struct variant.
///
/// Note that this is an untagged enum so the name `JustDocs` is just for
/// rust.
JustDocs(Option<String>),
/// In the v1+ format we're tracking at least docs but also the stability
/// of functions.
DocsAndStabilty {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
docs: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Stability::is_unknown")
)]
stability: Stability,
},
}
impl FunctionMetadata {
fn extract(func: &Function) -> Self {
if TRY_TO_EMIT_V0_BY_DEFAULT && func.stability.is_unknown() {
FunctionMetadata::JustDocs(func.docs.contents.clone())
} else {
FunctionMetadata::DocsAndStabilty {
docs: func.docs.contents.clone(),
stability: func.stability.clone(),
}
}
}
fn inject(&self, func: &mut Function) -> Result<()> {
match self {
FunctionMetadata::JustDocs(docs) => {
func.docs.contents = docs.clone();
}
FunctionMetadata::DocsAndStabilty { docs, stability } => {
func.docs.contents = docs.clone();
func.stability = stability.clone();
}
}
Ok(())
}
fn is_empty(&self) -> bool {
match self {
FunctionMetadata::JustDocs(docs) => docs.is_none(),
FunctionMetadata::DocsAndStabilty { docs, stability } => {
docs.is_none() && stability.is_unknown()
}
}
}
#[cfg(feature = "serde")]
fn is_compatible_with_v0(&self) -> bool {
match self {
FunctionMetadata::JustDocs(_) => true,
FunctionMetadata::DocsAndStabilty { .. } => false,
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
struct TypeMetadata {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
docs: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Stability::is_unknown")
)]
stability: Stability,
// record fields, variant cases, etc.
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "StringMap::is_empty")
)]
items: StringMap<String>,
}
impl TypeMetadata {
fn extract(resolve: &Resolve, id: TypeId) -> Self {
fn extract_items<T>(items: &[T], f: impl Fn(&T) -> (&String, &Docs)) -> StringMap<String> {
items
.iter()
.flat_map(|item| {
let (name, docs) = f(item);
Some((name.to_string(), docs.contents.clone()?))
})
.collect()
}
let ty = &resolve.types[id];
let items = match &ty.kind {
TypeDefKind::Record(record) => {
extract_items(&record.fields, |item| (&item.name, &item.docs))
}
TypeDefKind::Flags(flags) => {
extract_items(&flags.flags, |item| (&item.name, &item.docs))
}
TypeDefKind::Variant(variant) => {
extract_items(&variant.cases, |item| (&item.name, &item.docs))
}
TypeDefKind::Enum(enum_) => {
extract_items(&enum_.cases, |item| (&item.name, &item.docs))
}
// other types don't have inner items
_ => IndexMap::default(),
};
Self {
docs: ty.docs.contents.clone(),
stability: ty.stability.clone(),
items,
}
}
fn inject(&self, resolve: &mut Resolve, id: TypeId) -> Result<()> {
let ty = &mut resolve.types[id];
if !self.items.is_empty() {
match &mut ty.kind {
TypeDefKind::Record(record) => {
self.inject_items(&mut record.fields, |item| (&item.name, &mut item.docs))?
}
TypeDefKind::Flags(flags) => {
self.inject_items(&mut flags.flags, |item| (&item.name, &mut item.docs))?
}
TypeDefKind::Variant(variant) => {
self.inject_items(&mut variant.cases, |item| (&item.name, &mut item.docs))?
}
TypeDefKind::Enum(enum_) => {
self.inject_items(&mut enum_.cases, |item| (&item.name, &mut item.docs))?
}
_ => {
bail!("got 'items' for unexpected type {ty:?}");
}
}
}
if let Some(docs) = &self.docs {
ty.docs.contents = Some(docs.to_string());
}
ty.stability = self.stability.clone();
Ok(())
}
fn inject_items<T: std::fmt::Debug>(
&self,
items: &mut [T],
f: impl Fn(&mut T) -> (&String, &mut Docs),
) -> Result<()> {
let mut unused_docs = self.items.len();
for item in items.iter_mut() {
let (name, item_docs) = f(item);
if let Some(docs) = self.items.get(name.as_str()) {
item_docs.contents = Some(docs.to_string());
unused_docs -= 1;
}
}
if unused_docs > 0 {
bail!(
"not all 'items' match type items; {item_docs:?} vs {items:?}",
item_docs = self.items
);
}
Ok(())
}
fn is_empty(&self) -> bool {
self.docs.is_none() && self.items.is_empty() && self.stability.is_unknown()
}
#[cfg(feature = "serde")]
fn is_compatible_with_v0(&self) -> bool {
self.stability.is_unknown()
}
}

4636
vendor/wit-parser/src/resolve.rs vendored Normal file

File diff suppressed because it is too large Load Diff

227
vendor/wit-parser/src/resolve/clone.rs vendored Normal file
View File

@@ -0,0 +1,227 @@
//! A helper, non public, module to assist with cloning items within a
//! `Resolve`.
//!
//! Cloning items is not as simple as calling `Clone` due to the nature of how
//! `TypeId` tracks relationships between interfaces and types. A full deep
//! clone requires walking the full structure and allocating new id links. This
//! is akin to, for example, creating a deep copy of an `Rc<T>` by calling
//! `Clone for T`.
//!
//! This is currently used when merging worlds together to help copy anonymously
//! named items from one world to another.
//!
//! The general structure of this module is that each method takes a mutable
//! reference to an AST item and updates it as necessary internally, delegating
//! to other methods for internal AST items.
//!
//! This module does not at the time of this writing have full support for
//! cloning everything within a `Resolve`.
use crate::*;
use std::collections::HashMap;
/// Represents the results of cloning types and/or interfaces as part of a
/// `Resolve::merge_worlds` operation.
#[derive(Default)]
pub struct CloneMaps {
pub(super) types: HashMap<TypeId, TypeId>,
pub(super) interfaces: HashMap<InterfaceId, InterfaceId>,
}
impl CloneMaps {
/// The types cloned during a `Resolve::merge_worlds` operation.
///
/// The key is the original type, and the value is the clone.
pub fn types(&self) -> &HashMap<TypeId, TypeId> {
&self.types
}
/// The interfaces cloned during a `Resolve::merge_worlds` operation.
///
/// The key is the original interface, and the value is the clone.
pub fn interfaces(&self) -> &HashMap<InterfaceId, InterfaceId> {
&self.interfaces
}
}
pub struct Cloner<'a> {
pub resolve: &'a mut Resolve,
prev_owner: TypeOwner,
new_owner: TypeOwner,
/// This map keeps track, in the current scope of types, of all copied
/// types. This deduplicates copying types to ensure that they're only
/// copied at most once.
pub types: HashMap<TypeId, TypeId>,
/// If `None` then it's inferred from `self.new_owner`.
pub new_package: Option<PackageId>,
}
impl<'a> Cloner<'a> {
pub fn new(
resolve: &'a mut Resolve,
prev_owner: TypeOwner,
new_owner: TypeOwner,
) -> Cloner<'a> {
Cloner {
prev_owner,
new_owner,
resolve,
types: Default::default(),
new_package: None,
}
}
pub fn register_world_type_overlap(&mut self, from: WorldId, into: WorldId) {
let into = &self.resolve.worlds[into];
let from = &self.resolve.worlds[from];
for (name, into_import) in into.imports.iter() {
let WorldKey::Name(_) = name else { continue };
let WorldItem::Type(into_id) = into_import else {
continue;
};
let Some(WorldItem::Type(from_id)) = from.imports.get(name) else {
continue;
};
self.types.insert(*from_id, *into_id);
}
}
pub fn world_item(&mut self, key: &WorldKey, item: &mut WorldItem, clone_maps: &mut CloneMaps) {
match key {
WorldKey::Name(_) => {}
WorldKey::Interface(_) => return,
}
match item {
WorldItem::Type(t) => {
self.type_id(t);
}
WorldItem::Function(f) => {
self.function(f);
}
WorldItem::Interface { id, .. } => {
let old = *id;
self.interface(id, &mut clone_maps.types);
clone_maps.interfaces.insert(old, *id);
}
}
}
fn type_id(&mut self, ty: &mut TypeId) {
if !self.types.contains_key(ty) {
let mut new = self.resolve.types[*ty].clone();
self.type_def(&mut new);
let id = self.resolve.types.alloc(new);
self.types.insert(*ty, id);
}
*ty = self.types[ty];
}
fn type_def(&mut self, def: &mut TypeDef) {
if def.owner != TypeOwner::None {
assert_eq!(def.owner, self.prev_owner);
def.owner = self.new_owner;
}
match &mut def.kind {
TypeDefKind::Type(Type::Id(id)) => {
if self.resolve.types[*id].owner == self.prev_owner {
self.type_id(id);
} else {
// ..
}
}
TypeDefKind::Type(_)
| TypeDefKind::Resource
| TypeDefKind::Flags(_)
| TypeDefKind::Enum(_) => {}
TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)) => {
self.type_id(ty);
}
TypeDefKind::Option(ty)
| TypeDefKind::List(ty)
| TypeDefKind::FixedSizeList(ty, ..) => {
self.ty(ty);
}
TypeDefKind::Map(k, v) => {
self.ty(k);
self.ty(v);
}
TypeDefKind::Tuple(list) => {
for ty in list.types.iter_mut() {
self.ty(ty);
}
}
TypeDefKind::Record(r) => {
for field in r.fields.iter_mut() {
self.ty(&mut field.ty);
}
}
TypeDefKind::Variant(r) => {
for case in r.cases.iter_mut() {
if let Some(ty) = &mut case.ty {
self.ty(ty);
}
}
}
TypeDefKind::Result(r) => {
if let Some(ok) = &mut r.ok {
self.ty(ok);
}
if let Some(err) = &mut r.err {
self.ty(err);
}
}
TypeDefKind::Future(ty) | TypeDefKind::Stream(ty) => {
if let Some(ty) = ty {
self.ty(ty);
}
}
TypeDefKind::Unknown => {}
}
}
fn ty(&mut self, ty: &mut Type) {
match ty {
Type::Id(id) => self.type_id(id),
_ => {}
}
}
fn function(&mut self, func: &mut Function) {
if let Some(id) = func.kind.resource_mut() {
self.type_id(id);
}
for (_, ty) in func.params.iter_mut() {
self.ty(ty);
}
if let Some(ty) = &mut func.result {
self.ty(ty);
}
}
fn interface(&mut self, id: &mut InterfaceId, cloned_types: &mut HashMap<TypeId, TypeId>) {
let mut new = self.resolve.interfaces[*id].clone();
let next_id = self.resolve.interfaces.next_id();
let mut clone = Cloner::new(
self.resolve,
TypeOwner::Interface(*id),
TypeOwner::Interface(next_id),
);
for id in new.types.values_mut() {
clone.type_id(id);
}
for func in new.functions.values_mut() {
clone.function(func);
}
cloned_types.extend(clone.types);
new.package = Some(self.new_package.unwrap_or_else(|| match self.new_owner {
TypeOwner::Interface(id) => self.resolve.interfaces[id].package.unwrap(),
TypeOwner::World(id) => self.resolve.worlds[id].package.unwrap(),
TypeOwner::None => unreachable!(),
}));
*id = self.resolve.interfaces.alloc(new);
assert_eq!(*id, next_id);
}
}

140
vendor/wit-parser/src/serde_.rs vendored Normal file
View File

@@ -0,0 +1,140 @@
use crate::Type;
use id_arena::{Arena, Id};
use indexmap::IndexMap;
use semver::Version;
use serde::ser::{SerializeMap, SerializeSeq, Serializer};
use serde::{Deserialize, Serialize, de::Error};
pub fn serialize_none<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_none()
}
pub fn serialize_arena<T, S>(arena: &Arena<T>, serializer: S) -> Result<S::Ok, S::Error>
where
T: Serialize,
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(arena.len()))?;
for (_, item) in arena.iter() {
seq.serialize_element(&item)?;
}
seq.end()
}
pub fn serialize_id<T, S>(id: &Id<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u64(id.index() as u64)
}
pub fn serialize_optional_id<T, S>(id: &Option<Id<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match id {
Some(id) => serialize_id(&id, serializer),
None => serializer.serialize_none(),
}
}
pub fn serialize_id_map<K, T, S>(map: &IndexMap<K, Id<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
K: Serialize,
S: Serializer,
{
let mut s = serializer.serialize_map(Some(map.len()))?;
for (key, id) in map.iter() {
s.serialize_entry(key, &(id.index() as u64))?;
}
s.end()
}
impl Serialize for Type {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Type::Bool => serializer.serialize_str("bool"),
Type::U8 => serializer.serialize_str("u8"),
Type::U16 => serializer.serialize_str("u16"),
Type::U32 => serializer.serialize_str("u32"),
Type::U64 => serializer.serialize_str("u64"),
Type::S8 => serializer.serialize_str("s8"),
Type::S16 => serializer.serialize_str("s16"),
Type::S32 => serializer.serialize_str("s32"),
Type::S64 => serializer.serialize_str("s64"),
Type::F32 => serializer.serialize_str("f32"),
Type::F64 => serializer.serialize_str("f64"),
Type::Char => serializer.serialize_str("char"),
Type::String => serializer.serialize_str("string"),
Type::ErrorContext => serializer.serialize_str("error-context"),
Type::Id(type_id) => serializer.serialize_u64(type_id.index() as u64),
}
}
}
pub fn serialize_params<S>(params: &[(String, Type)], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(params.len()))?;
for (name, typ) in params.iter() {
let param = Param {
name: name.to_string(),
typ: *typ,
};
seq.serialize_element(&param)?;
}
seq.end()
}
#[derive(Debug, Clone, PartialEq, serde_derive::Serialize)]
struct Param {
#[serde(skip_serializing_if = "String::is_empty")]
pub name: String,
#[serde(rename = "type")]
pub typ: Type,
}
pub fn serialize_version<S>(version: &Version, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
version.to_string().serialize(serializer)
}
pub fn deserialize_version<'de, D>(deserializer: D) -> Result<Version, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let version: String = String::deserialize(deserializer)?;
version.parse().map_err(|e| D::Error::custom(e))
}
pub fn serialize_optional_version<S>(
version: &Option<Version>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
version
.as_ref()
.map(|s| s.to_string())
.serialize(serializer)
}
pub fn deserialize_optional_version<'de, D>(deserializer: D) -> Result<Option<Version>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
match <Option<String>>::deserialize(deserializer)? {
Some(version) => Ok(Some(version.parse().map_err(|e| D::Error::custom(e))?)),
None => Ok(None),
}
}

626
vendor/wit-parser/src/sizealign.rs vendored Normal file
View File

@@ -0,0 +1,626 @@
use std::{
cmp::Ordering,
num::NonZeroUsize,
ops::{Add, AddAssign},
};
use crate::{FlagsRepr, Int, Resolve, Type, TypeDef, TypeDefKind};
/// Architecture specific alignment
#[derive(Eq, PartialEq, Clone, Copy)]
pub enum Alignment {
/// This represents 4 byte alignment on 32bit and 8 byte alignment on 64bit architectures
Pointer,
/// This alignment is architecture independent (derived from integer or float types)
Bytes(NonZeroUsize),
}
impl Default for Alignment {
fn default() -> Self {
Alignment::Bytes(NonZeroUsize::new(1).unwrap())
}
}
impl std::fmt::Debug for Alignment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Alignment::Pointer => f.write_str("ptr"),
Alignment::Bytes(b) => f.write_fmt(format_args!("{}", b.get())),
}
}
}
impl PartialOrd for Alignment {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Alignment {
/// Needed for determining the max alignment of an object from its parts.
/// The ordering is: Bytes(1) < Bytes(2) < Bytes(4) < Pointer < Bytes(8)
/// as a Pointer is either four or eight byte aligned, depending on the architecture
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Alignment::Pointer, Alignment::Pointer) => std::cmp::Ordering::Equal,
(Alignment::Pointer, Alignment::Bytes(b)) => {
if b.get() > 4 {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
}
(Alignment::Bytes(b), Alignment::Pointer) => {
if b.get() > 4 {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Less
}
}
(Alignment::Bytes(a), Alignment::Bytes(b)) => a.cmp(b),
}
}
}
impl Alignment {
/// for easy migration this gives you the value for wasm32
pub fn align_wasm32(&self) -> usize {
match self {
Alignment::Pointer => 4,
Alignment::Bytes(bytes) => bytes.get(),
}
}
pub fn align_wasm64(&self) -> usize {
match self {
Alignment::Pointer => 8,
Alignment::Bytes(bytes) => bytes.get(),
}
}
pub fn format(&self, ptrsize_expr: &str) -> String {
match self {
Alignment::Pointer => ptrsize_expr.into(),
Alignment::Bytes(bytes) => format!("{}", bytes.get()),
}
}
}
/// Architecture specific measurement of position,
/// the combined amount in bytes is
/// `bytes + pointers * core::mem::size_of::<*const u8>()`
#[derive(Default, Clone, Copy, Eq, PartialEq)]
pub struct ArchitectureSize {
/// architecture independent bytes
pub bytes: usize,
/// amount of pointer sized units to add
pub pointers: usize,
}
impl Add<ArchitectureSize> for ArchitectureSize {
type Output = ArchitectureSize;
fn add(self, rhs: ArchitectureSize) -> Self::Output {
ArchitectureSize::new(self.bytes + rhs.bytes, self.pointers + rhs.pointers)
}
}
impl AddAssign<ArchitectureSize> for ArchitectureSize {
fn add_assign(&mut self, rhs: ArchitectureSize) {
self.bytes += rhs.bytes;
self.pointers += rhs.pointers;
}
}
impl From<Alignment> for ArchitectureSize {
fn from(align: Alignment) -> Self {
match align {
Alignment::Bytes(bytes) => ArchitectureSize::new(bytes.get(), 0),
Alignment::Pointer => ArchitectureSize::new(0, 1),
}
}
}
impl std::fmt::Debug for ArchitectureSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.format("ptrsz"))
}
}
impl ArchitectureSize {
pub fn new(bytes: usize, pointers: usize) -> Self {
Self { bytes, pointers }
}
pub fn max<B: std::borrow::Borrow<Self>>(&self, other: B) -> Self {
let other = other.borrow();
let self32 = self.size_wasm32();
let self64 = self.size_wasm64();
let other32 = other.size_wasm32();
let other64 = other.size_wasm64();
if self32 >= other32 && self64 >= other64 {
*self
} else if self32 <= other32 && self64 <= other64 {
*other
} else {
// we can assume a combination of bytes and pointers, so align to at least pointer size
let new32 = align_to(self32.max(other32), 4);
let new64 = align_to(self64.max(other64), 8);
ArchitectureSize::new(new32 + new32 - new64, (new64 - new32) / 4)
}
}
pub fn add_bytes(&self, b: usize) -> Self {
Self::new(self.bytes + b, self.pointers)
}
/// The effective offset/size is
/// `constant_bytes() + core::mem::size_of::<*const u8>() * pointers_to_add()`
pub fn constant_bytes(&self) -> usize {
self.bytes
}
pub fn pointers_to_add(&self) -> usize {
self.pointers
}
/// Shortcut for compatibility with previous versions
pub fn size_wasm32(&self) -> usize {
self.bytes + self.pointers * 4
}
pub fn size_wasm64(&self) -> usize {
self.bytes + self.pointers * 8
}
/// prefer this over >0
pub fn is_empty(&self) -> bool {
self.bytes == 0 && self.pointers == 0
}
// create a suitable expression in bytes from a pointer size argument
pub fn format(&self, ptrsize_expr: &str) -> String {
self.format_term(ptrsize_expr, false)
}
// create a suitable expression in bytes from a pointer size argument,
// extended API with optional brackets around the sum
pub fn format_term(&self, ptrsize_expr: &str, suppress_brackets: bool) -> String {
if self.pointers != 0 {
if self.bytes > 0 {
// both
if suppress_brackets {
format!(
"{}+{}*{ptrsize_expr}",
self.constant_bytes(),
self.pointers_to_add()
)
} else {
format!(
"({}+{}*{ptrsize_expr})",
self.constant_bytes(),
self.pointers_to_add()
)
}
} else if self.pointers == 1 {
// one pointer
ptrsize_expr.into()
} else {
// only pointer
if suppress_brackets {
format!("{}*{ptrsize_expr}", self.pointers_to_add())
} else {
format!("({}*{ptrsize_expr})", self.pointers_to_add())
}
}
} else {
// only bytes
format!("{}", self.constant_bytes())
}
}
}
/// Information per structure element
#[derive(Default)]
pub struct ElementInfo {
pub size: ArchitectureSize,
pub align: Alignment,
}
impl From<Alignment> for ElementInfo {
fn from(align: Alignment) -> Self {
ElementInfo {
size: align.into(),
align,
}
}
}
impl ElementInfo {
fn new(size: ArchitectureSize, align: Alignment) -> Self {
Self { size, align }
}
}
/// Collect size and alignment for sub-elements of a structure
#[derive(Default)]
pub struct SizeAlign {
map: Vec<ElementInfo>,
}
impl SizeAlign {
pub fn fill(&mut self, resolve: &Resolve) {
self.map = Vec::new();
for (_, ty) in resolve.types.iter() {
let pair = self.calculate(ty);
self.map.push(pair);
}
}
fn calculate(&self, ty: &TypeDef) -> ElementInfo {
match &ty.kind {
TypeDefKind::Type(t) => ElementInfo::new(self.size(t), self.align(t)),
TypeDefKind::FixedSizeList(t, size) => {
let field_align = self.align(t);
let field_size = self.size(t);
ElementInfo::new(
ArchitectureSize::new(
field_size.bytes.checked_mul(*size as usize).unwrap(),
field_size.pointers.checked_mul(*size as usize).unwrap(),
),
field_align,
)
}
TypeDefKind::List(_) => {
ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
}
TypeDefKind::Map(_, _) => {
ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
}
TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)),
TypeDefKind::Tuple(t) => self.record(t.types.iter()),
TypeDefKind::Flags(f) => match f.repr() {
FlagsRepr::U8 => int_size_align(Int::U8),
FlagsRepr::U16 => int_size_align(Int::U16),
FlagsRepr::U32(n) => ElementInfo::new(
ArchitectureSize::new(n * 4, 0),
Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
),
},
TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| c.ty.as_ref())),
TypeDefKind::Enum(e) => self.variant(e.tag(), []),
TypeDefKind::Option(t) => self.variant(Int::U8, [Some(t)]),
TypeDefKind::Result(r) => self.variant(Int::U8, [r.ok.as_ref(), r.err.as_ref()]),
// A resource is represented as an index.
// A future is represented as an index.
// A stream is represented as an index.
// An error is represented as an index.
TypeDefKind::Handle(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) => {
int_size_align(Int::U32)
}
// This shouldn't be used for anything since raw resources aren't part of the ABI -- just handles to
// them.
TypeDefKind::Resource => ElementInfo::new(
ArchitectureSize::new(usize::MAX, 0),
Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap()),
),
TypeDefKind::Unknown => unreachable!(),
}
}
pub fn size(&self, ty: &Type) -> ArchitectureSize {
match ty {
Type::Bool | Type::U8 | Type::S8 => ArchitectureSize::new(1, 0),
Type::U16 | Type::S16 => ArchitectureSize::new(2, 0),
Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
ArchitectureSize::new(4, 0)
}
Type::U64 | Type::S64 | Type::F64 => ArchitectureSize::new(8, 0),
Type::String => ArchitectureSize::new(0, 2),
Type::Id(id) => self.map[id.index()].size,
}
}
pub fn align(&self, ty: &Type) -> Alignment {
match ty {
Type::Bool | Type::U8 | Type::S8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
Type::U16 | Type::S16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
Alignment::Bytes(NonZeroUsize::new(4).unwrap())
}
Type::U64 | Type::S64 | Type::F64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
Type::String => Alignment::Pointer,
Type::Id(id) => self.map[id.index()].align,
}
}
pub fn field_offsets<'a>(
&self,
types: impl IntoIterator<Item = &'a Type>,
) -> Vec<(ArchitectureSize, &'a Type)> {
let mut cur = ArchitectureSize::default();
types
.into_iter()
.map(|ty| {
let ret = align_to_arch(cur, self.align(ty));
cur = ret + self.size(ty);
(ret, ty)
})
.collect()
}
pub fn payload_offset<'a>(
&self,
tag: Int,
cases: impl IntoIterator<Item = Option<&'a Type>>,
) -> ArchitectureSize {
let mut max_align = Alignment::default();
for ty in cases {
if let Some(ty) = ty {
max_align = max_align.max(self.align(ty));
}
}
let tag_size = int_size_align(tag).size;
align_to_arch(tag_size, max_align)
}
pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
let mut size = ArchitectureSize::default();
let mut align = Alignment::default();
for ty in types {
let field_size = self.size(ty);
let field_align = self.align(ty);
size = align_to_arch(size, field_align) + field_size;
align = align.max(field_align);
}
ElementInfo::new(align_to_arch(size, align), align)
}
pub fn params<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
self.record(types.into_iter())
}
fn variant<'a>(
&self,
tag: Int,
types: impl IntoIterator<Item = Option<&'a Type>>,
) -> ElementInfo {
let ElementInfo {
size: discrim_size,
align: discrim_align,
} = int_size_align(tag);
let mut case_size = ArchitectureSize::default();
let mut case_align = Alignment::default();
for ty in types {
if let Some(ty) = ty {
case_size = case_size.max(&self.size(ty));
case_align = case_align.max(self.align(ty));
}
}
let align = discrim_align.max(case_align);
let discrim_aligned = align_to_arch(discrim_size, case_align);
let size_sum = discrim_aligned + case_size;
ElementInfo::new(align_to_arch(size_sum, align), align)
}
}
fn int_size_align(i: Int) -> ElementInfo {
match i {
Int::U8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
Int::U16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
Int::U32 => Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
Int::U64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
}
.into()
}
/// Increase `val` to a multiple of `align`;
/// `align` must be a power of two
pub(crate) fn align_to(val: usize, align: usize) -> usize {
(val + align - 1) & !(align - 1)
}
/// Increase `val` to a multiple of `align`, with special handling for pointers;
/// `align` must be a power of two or `Alignment::Pointer`
pub fn align_to_arch(val: ArchitectureSize, align: Alignment) -> ArchitectureSize {
match align {
Alignment::Pointer => {
let new32 = align_to(val.bytes, 4);
if new32 != align_to(new32, 8) {
ArchitectureSize::new(new32 - 4, val.pointers + 1)
} else {
ArchitectureSize::new(new32, val.pointers)
}
}
Alignment::Bytes(align_bytes) => {
let align_bytes = align_bytes.get();
if align_bytes > 4 && (val.pointers & 1) != 0 {
let new_bytes = align_to(val.bytes, align_bytes);
if (new_bytes - val.bytes) >= 4 {
// up to four extra bytes fit together with a the extra 32 bit pointer
// and the 64 bit pointer is always 8 bytes (so no change in value)
ArchitectureSize::new(new_bytes - 8, val.pointers + 1)
} else {
// there is no room to combine, so the odd pointer aligns to 8 bytes
ArchitectureSize::new(new_bytes + 8, val.pointers - 1)
}
} else {
ArchitectureSize::new(align_to(val.bytes, align_bytes), val.pointers)
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn align() {
// u8 + ptr
assert_eq!(
align_to_arch(ArchitectureSize::new(1, 0), Alignment::Pointer),
ArchitectureSize::new(0, 1)
);
// u8 + u64
assert_eq!(
align_to_arch(
ArchitectureSize::new(1, 0),
Alignment::Bytes(NonZeroUsize::new(8).unwrap())
),
ArchitectureSize::new(8, 0)
);
// u8 + u32
assert_eq!(
align_to_arch(
ArchitectureSize::new(1, 0),
Alignment::Bytes(NonZeroUsize::new(4).unwrap())
),
ArchitectureSize::new(4, 0)
);
// ptr + u64
assert_eq!(
align_to_arch(
ArchitectureSize::new(0, 1),
Alignment::Bytes(NonZeroUsize::new(8).unwrap())
),
ArchitectureSize::new(8, 0)
);
// u32 + ptr
assert_eq!(
align_to_arch(ArchitectureSize::new(4, 0), Alignment::Pointer),
ArchitectureSize::new(0, 1)
);
// u32, ptr + u64
assert_eq!(
align_to_arch(
ArchitectureSize::new(0, 2),
Alignment::Bytes(NonZeroUsize::new(8).unwrap())
),
ArchitectureSize::new(0, 2)
);
// ptr, u8 + u64
assert_eq!(
align_to_arch(
ArchitectureSize::new(1, 1),
Alignment::Bytes(NonZeroUsize::new(8).unwrap())
),
ArchitectureSize::new(0, 2)
);
// ptr, u8 + ptr
assert_eq!(
align_to_arch(ArchitectureSize::new(1, 1), Alignment::Pointer),
ArchitectureSize::new(0, 2)
);
// ptr, ptr, u8 + u64
assert_eq!(
align_to_arch(
ArchitectureSize::new(1, 2),
Alignment::Bytes(NonZeroUsize::new(8).unwrap())
),
ArchitectureSize::new(8, 2)
);
assert_eq!(
align_to_arch(
ArchitectureSize::new(30, 3),
Alignment::Bytes(NonZeroUsize::new(8).unwrap())
),
ArchitectureSize::new(40, 2)
);
assert_eq!(
ArchitectureSize::new(12, 0).max(&ArchitectureSize::new(0, 2)),
ArchitectureSize::new(8, 1)
);
assert_eq!(
ArchitectureSize::new(10, 0).max(&ArchitectureSize::new(0, 2)),
ArchitectureSize::new(8, 1)
);
assert_eq!(
align_to_arch(
ArchitectureSize::new(2, 0),
Alignment::Bytes(NonZeroUsize::new(8).unwrap())
),
ArchitectureSize::new(8, 0)
);
assert_eq!(
align_to_arch(ArchitectureSize::new(2, 0), Alignment::Pointer),
ArchitectureSize::new(0, 1)
);
}
#[test]
fn resource_size() {
// keep it identical to the old behavior
let obj = SizeAlign::default();
let elem = obj.calculate(&TypeDef {
name: None,
kind: TypeDefKind::Resource,
owner: crate::TypeOwner::None,
docs: Default::default(),
stability: Default::default(),
});
assert_eq!(elem.size, ArchitectureSize::new(usize::MAX, 0));
assert_eq!(
elem.align,
Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap())
);
}
#[test]
fn result_ptr_10() {
let mut obj = SizeAlign::default();
let mut resolve = Resolve::default();
let tuple = crate::Tuple {
types: vec![Type::U16, Type::U16, Type::U16, Type::U16, Type::U16],
};
let id = resolve.types.alloc(TypeDef {
name: None,
kind: TypeDefKind::Tuple(tuple),
owner: crate::TypeOwner::None,
docs: Default::default(),
stability: Default::default(),
});
obj.fill(&resolve);
let my_result = crate::Result_ {
ok: Some(Type::String),
err: Some(Type::Id(id)),
};
let elem = obj.calculate(&TypeDef {
name: None,
kind: TypeDefKind::Result(my_result),
owner: crate::TypeOwner::None,
docs: Default::default(),
stability: Default::default(),
});
assert_eq!(elem.size, ArchitectureSize::new(8, 2));
assert_eq!(elem.align, Alignment::Pointer);
}
#[test]
fn result_ptr_64bit() {
let obj = SizeAlign::default();
let my_record = crate::Record {
fields: vec![
crate::Field {
name: String::new(),
ty: Type::String,
docs: Default::default(),
},
crate::Field {
name: String::new(),
ty: Type::U64,
docs: Default::default(),
},
],
};
let elem = obj.calculate(&TypeDef {
name: None,
kind: TypeDefKind::Record(my_record),
owner: crate::TypeOwner::None,
docs: Default::default(),
stability: Default::default(),
});
assert_eq!(elem.size, ArchitectureSize::new(8, 2));
assert_eq!(elem.align, Alignment::Bytes(NonZeroUsize::new(8).unwrap()));
}
}