Merge wasi-common into wasmtime

This commit merges [CraneStation/wasi-common] repo as a subdir of
this repo while preserving **all** of git history. There is an
initiative to pull `wasi-common` into [CraneStation/wasmtime], and
[CraneStation/wasmtime] becoming a monorepo. This came about for
several reasons with a common theme of convenience, namely,
having a monorepo:
1. cleans up the problem of dependencies (as we have seen first
   hand with dependabot enabled, it can cause some grief)
2. completely removes the problem of syncing the closely dependent
   repos (e.g., updating `wasi-common` with say a bugfix generally
   implies creating a "sync" commit for pulling in the changes into
   the "parent" repo, in this case, `wasmtime`)
3. mainly for the two reasons above, makes publishing to crates.io
   easier
4. hopefully streamlines the process of getting the community
   involved in contributing to `wasi-common` as now everything
   is one place

[CraneStation/wasi-common]: https://github.com/CraneStation/wasi-common
[CraneStation/wasmtime]: https://github.com/CraneStation/wasmtime
This commit is contained in:
Jakub Konka
2019-11-07 15:16:34 +01:00
120 changed files with 14853 additions and 5 deletions

View File

@@ -0,0 +1,34 @@
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate witx;
mod raw_types;
mod utils;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn witx_host_types(args: TokenStream) -> TokenStream {
TokenStream::from(raw_types::gen(
TokenStream2::from(args),
raw_types::Mode::Host,
))
}
#[proc_macro]
pub fn witx_wasi_types(args: TokenStream) -> TokenStream {
TokenStream::from(raw_types::gen(
TokenStream2::from(args),
raw_types::Mode::Wasi,
))
}
#[proc_macro]
pub fn witx_wasi32_types(args: TokenStream) -> TokenStream {
TokenStream::from(raw_types::gen(
TokenStream2::from(args),
raw_types::Mode::Wasi32,
))
}

View File

@@ -0,0 +1,250 @@
//! Translate witx types to Rust.
use crate::utils;
use proc_macro2::{Delimiter, Group, Literal, TokenStream, TokenTree};
use quote::{format_ident, quote};
use std::convert::TryFrom;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Mode {
Host,
Wasi32,
Wasi,
}
impl Mode {
pub fn include_target_types(&self) -> bool {
match self {
Mode::Host | Mode::Wasi32 => true,
Mode::Wasi => false,
}
}
}
pub fn gen(args: TokenStream, mode: Mode) -> TokenStream {
let mut output = TokenStream::new();
let (path, _phase) = utils::witx_path_from_args(args);
let doc = match witx::load(&path) {
Ok(doc) => doc,
Err(e) => {
panic!("error opening file {}: {}", path, e);
}
};
gen_datatypes(&mut output, &doc, mode);
output
}
fn gen_datatypes(output: &mut TokenStream, doc: &witx::Document, mode: Mode) {
for datatype in doc.datatypes() {
if mode.include_target_types() != type_has_target_size(doc, &datatype) {
continue;
}
gen_datatype(output, doc, mode, &datatype);
}
}
fn gen_datatype(
output: &mut TokenStream,
doc: &witx::Document,
mode: Mode,
datatype: &witx::Datatype,
) {
match &datatype.variant {
witx::DatatypeVariant::Alias(a) => {
if a.name.as_str() == "size_t" {
let wasi_name = format_ident!("__wasi_{}", a.name.as_str());
match mode {
Mode::Host => output.extend(quote!(pub type #wasi_name = usize;)),
Mode::Wasi => panic!("size_t has target-specific size"),
Mode::Wasi32 => output.extend(quote!(pub type #wasi_name = u32;)),
}
} else {
let wasi_name = format_ident!("__wasi_{}", a.name.as_str());
let to = ident_tokens(mode, &a.to);
output.extend(quote!(pub type #wasi_name = #to;));
}
}
witx::DatatypeVariant::Enum(e) => {
let wasi_name = format_ident!("__wasi_{}", e.name.as_str());
let repr = int_repr_tokens(e.repr);
output.extend(quote!(pub type #wasi_name = #repr;));
for (index, variant) in e.variants.iter().enumerate() {
let value_name = format_ident!("__WASI_{}", variant.as_str());
let index_name = Literal::usize_unsuffixed(index);
output.extend(quote!(pub const #value_name: #wasi_name = #index_name;));
}
}
witx::DatatypeVariant::Flags(f) => {
let wasi_name = format_ident!("__wasi_{}", f.name.as_str());
let repr = int_repr_tokens(f.repr);
output.extend(quote!(pub type #wasi_name = #repr;));
for (index, flag) in f.flags.iter().enumerate() {
let value_name = format_ident!("__WASI_{}", flag.as_str());
let flag_value = Literal::u128_unsuffixed(
1u128
.checked_shl(u32::try_from(index).expect("flag value overflow"))
.expect("flag value overflow"),
);
output.extend(quote!(pub const #value_name: #wasi_name = #flag_value;));
}
}
witx::DatatypeVariant::Struct(s) => {
output.extend(quote!(#[repr(C)]));
// Types which contain unions can't trivially implement Debug,
// Hash, or Eq, because the type itself doesn't record which
// union member is active.
if struct_has_union(&doc, s) {
output.extend(quote!(#[derive(Copy, Clone)]));
output.extend(quote!(#[allow(missing_debug_implementations)]));
} else {
output.extend(quote!(#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]));
}
let wasi_name = format_ident!("__wasi_{}", s.name.as_str());
output.extend(quote!(pub struct #wasi_name));
let mut inner = TokenStream::new();
for member in &s.members {
let member_name = format_ident!("r#{}", member.name.as_str());
let member_type = ident_tokens(mode, &member.type_);
inner.extend(quote!(pub #member_name: #member_type,));
}
let braced = Group::new(Delimiter::Brace, inner);
output.extend(TokenStream::from(TokenTree::Group(braced)));
}
witx::DatatypeVariant::Union(u) => {
output.extend(quote!(#[repr(C)]));
output.extend(quote!(#[derive(Copy, Clone)]));
output.extend(quote!(#[allow(missing_debug_implementations)]));
let wasi_name = format_ident!("__wasi_{}", u.name.as_str());
output.extend(quote!(pub union #wasi_name));
let mut inner = TokenStream::new();
for variant in &u.variants {
let variant_name = format_ident!("r#{}", variant.name.as_str());
let variant_type = ident_tokens(mode, &variant.type_);
inner.extend(quote!(pub #variant_name: #variant_type,));
}
let braced = Group::new(Delimiter::Brace, inner);
output.extend(TokenStream::from(TokenTree::Group(braced)));
}
}
}
fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream {
match int_repr {
witx::IntRepr::U8 => quote!(u8),
witx::IntRepr::U16 => quote!(u16),
witx::IntRepr::U32 => quote!(u32),
witx::IntRepr::U64 => quote!(u64),
}
}
fn builtin_tokens(mode: Mode, builtin: witx::BuiltinType) -> TokenStream {
match builtin {
witx::BuiltinType::String => match mode {
Mode::Host => quote!((*const u8, usize)),
Mode::Wasi => panic!("strings have target-specific size"),
Mode::Wasi32 => quote!((u32, u32)),
},
witx::BuiltinType::U8 => quote!(u8),
witx::BuiltinType::U16 => quote!(u16),
witx::BuiltinType::U32 => quote!(u32),
witx::BuiltinType::U64 => quote!(u64),
witx::BuiltinType::S8 => quote!(i8),
witx::BuiltinType::S16 => quote!(i16),
witx::BuiltinType::S32 => quote!(i32),
witx::BuiltinType::S64 => quote!(i64),
witx::BuiltinType::F32 => quote!(f32),
witx::BuiltinType::F64 => quote!(f64),
}
}
fn ident_tokens(mode: Mode, ident: &witx::DatatypeIdent) -> TokenStream {
match ident {
witx::DatatypeIdent::Builtin(builtin) => builtin_tokens(mode, *builtin),
witx::DatatypeIdent::Ident(ident) => TokenStream::from(TokenTree::Ident(format_ident!(
"__wasi_{}",
ident.name.as_str()
))),
witx::DatatypeIdent::Pointer(pointee) => {
let pointee = ident_tokens(mode, pointee);
match mode {
Mode::Host => quote!(*mut #pointee),
Mode::Wasi => panic!("pointers have target-specific size"),
Mode::Wasi32 => quote!(u32),
}
}
witx::DatatypeIdent::ConstPointer(pointee) => {
let pointee = ident_tokens(mode, pointee);
match mode {
Mode::Host => quote!(*const #pointee),
Mode::Wasi => panic!("pointers have target-specific size"),
Mode::Wasi32 => quote!(u32),
}
}
witx::DatatypeIdent::Array(element) => {
let element_name = ident_tokens(mode, element);
match mode {
Mode::Host => quote!((*const #element_name, usize)),
Mode::Wasi => panic!("arrays have target-specific size"),
Mode::Wasi32 => quote!((u32, u32)),
}
}
}
}
/// Test whether the given struct contains any union members.
fn struct_has_union(doc: &witx::Document, s: &witx::StructDatatype) -> bool {
s.members.iter().any(|member| match &member.type_ {
witx::DatatypeIdent::Ident(ident) => match &doc.datatype(&ident.name).unwrap().variant {
witx::DatatypeVariant::Union(_) => true,
witx::DatatypeVariant::Struct(s) => struct_has_union(doc, &s),
_ => false,
},
_ => false,
})
}
/// Test whether the given type has a target-specific size.
fn type_has_target_size(doc: &witx::Document, type_: &witx::Datatype) -> bool {
match &type_.variant {
witx::DatatypeVariant::Alias(a) => {
a.name.as_str() == "size_t" || ident_has_target_size(doc, &a.to)
}
witx::DatatypeVariant::Enum(_) => false,
witx::DatatypeVariant::Flags(_) => false,
witx::DatatypeVariant::Struct(s) => s
.members
.iter()
.any(|m| ident_has_target_size(doc, &m.type_)),
witx::DatatypeVariant::Union(u) => u
.variants
.iter()
.any(|v| ident_has_target_size(doc, &v.type_)),
}
}
/// Test whether the given type ident has a target-specific size.
fn ident_has_target_size(doc: &witx::Document, ident: &witx::DatatypeIdent) -> bool {
match ident {
witx::DatatypeIdent::Ident(ident) => {
type_has_target_size(doc, &doc.datatype(&ident.name).unwrap())
}
witx::DatatypeIdent::Builtin(builtin) => {
if let witx::BuiltinType::String = builtin {
true
} else {
false
}
}
witx::DatatypeIdent::Pointer(_) | witx::DatatypeIdent::ConstPointer(_) => true,
witx::DatatypeIdent::Array(element) => ident_has_target_size(doc, element),
}
}

View File

@@ -0,0 +1,54 @@
use proc_macro2::{Literal, TokenStream, TokenTree};
/// Given the input tokens to a macro invocation, return the path to the
/// witx file to process.
pub(crate) fn witx_path_from_args(args: TokenStream) -> (String, String) {
let mut strings = Vec::new();
for arg in args {
if let TokenTree::Literal(literal) = arg {
let parsed = parse_string_literal(literal);
strings.push(parsed);
} else {
panic!("arguments must be string literals");
}
}
if strings.len() != 2 {
panic!("expected two string literals");
}
let phase = &strings[0];
let id = &strings[1];
let path = witx_path(phase, id);
(path, phase.clone())
}
fn witx_path(phase: &str, id: &str) -> String {
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
format!("{}/WASI/phases/{}/witx/{}.witx", root, phase, id)
}
// Convert a `Literal` holding a string literal into the `String`.
//
// FIXME: It feels like there should be an easier way to do this.
fn parse_string_literal(literal: Literal) -> String {
let s = literal.to_string();
assert!(
s.starts_with('"') && s.ends_with('"'),
"string literal must be enclosed in double-quotes"
);
let trimmed = s[1..s.len() - 1].to_owned();
assert!(
!trimmed.contains('"'),
"string literal must not contain embedded quotes for now"
);
assert!(
!trimmed.contains('\\'),
"string literal must not contain embedded backslashes for now"
);
trimmed
}