Skip to content

Commit

Permalink
Enable more than one handle type in *.witx (WebAssembly#421)
Browse files Browse the repository at this point in the history
This commit reworks how handle types work at the representation level in
`*.witx` file to follow the expected design of resources in the
interface types specification. Previously handle types were simply
`(handle)`, which meant that there could only be one actual handle type
in the world (structurally at least). After this commit, however, there
can be multiple handle types.

First abstract types must be introduced as a `resource`, for example:

    (resource $fd)

This declares that the module exports a type named `$fd` and it's
abstract in that the representation is not known. At the interface layer
you can't pass an `$fd` directly because it's representation is not
known. To do that, however, you can do:

    (param $x (handle $fd))

The `handle` type now refers to a particular `resource` that it refers
to. Values of type `handle T` can exist and are what's passed at the
boundaries.

This is all largely just an internal structuring concern at this point.
This has no ramifications for WASI which still has an `$fd` type for
functions that is represented with an `i32`. This is largely a
forward-looking change to allow multiple types of resources defined by
different modules and all used by one another.

This commit also updates `use` syntax where `use` will pull from either
the type or the resource namespace. Furthermore a new `(use ($foo as
$bar) ...)` syntax was added to locally renamed something within a module.
  • Loading branch information
alexcrichton authored Mar 30, 2021
1 parent fc3da39 commit 834679b
Show file tree
Hide file tree
Showing 13 changed files with 252 additions and 48 deletions.
3 changes: 2 additions & 1 deletion phases/ephemeral/witx/typenames.witx
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,9 @@
)
)

(resource $fd)
;;; A file descriptor handle.
(typename $fd (handle))
(typename $fd (handle $fd))

;;; A region of memory for scatter/gather reads.
(typename $iovec
Expand Down
3 changes: 2 additions & 1 deletion phases/old/snapshot_0/witx/typenames.witx
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,9 @@
)
)

(resource $fd)
;;; A file descriptor handle.
(typename $fd (handle))
(typename $fd (handle $fd))

;;; A region of memory for scatter/gather reads.
(typename $iovec
Expand Down
3 changes: 2 additions & 1 deletion phases/snapshot/witx/typenames.witx
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,9 @@
)
)

(resource $fd)
;;; A file descriptor handle.
(typename $fd (handle))
(typename $fd (handle $fd))

;;; A region of memory for scatter/gather reads.
(typename $iovec
Expand Down
53 changes: 50 additions & 3 deletions tools/witx/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ pub struct Module {
types: Vec<Rc<NamedType>>,
type_map: HashMap<Id, Rc<NamedType>>,

resources: Vec<Rc<Resource>>,
resource_map: HashMap<Id, Rc<Resource>>,

funcs: Vec<Rc<Function>>,
func_map: HashMap<Id, Rc<Function>>,

Expand All @@ -61,6 +64,8 @@ impl Module {
module_id,
types: Default::default(),
type_map: Default::default(),
resources: Default::default(),
resource_map: Default::default(),
funcs: Default::default(),
func_map: Default::default(),
constants: Default::default(),
Expand All @@ -80,6 +85,14 @@ impl Module {
self.types.push(ty);
}

pub(crate) fn push_resource(&mut self, r: Rc<Resource>) {
assert!(self
.resource_map
.insert(r.name.clone(), r.clone())
.is_none());
self.resources.push(r);
}

pub(crate) fn push_func(&mut self, func: Rc<Function>) {
assert!(self
.func_map
Expand All @@ -100,6 +113,14 @@ impl Module {
self.types.iter()
}

pub fn resource(&self, name: &Id) -> Option<Rc<Resource>> {
self.resource_map.get(name).cloned()
}

pub fn resources<'a>(&'a self) -> impl Iterator<Item = &'a Rc<Resource>> + 'a {
self.resources.iter()
}

/// All of the (unique) types used as "err" variant of results returned from
/// functions.
pub fn error_types<'a>(&'a self) -> impl Iterator<Item = TypeRef> + 'a {
Expand Down Expand Up @@ -499,11 +520,37 @@ impl Case {
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct HandleDatatype {}
pub struct Resource {
/// The local name within the module this resource is defined within. This
/// may differ from the id of the resource itself.
pub name: Id,
/// The unique id assigned to this resource.
pub resource_id: ResourceId,
/// Documentation in the defining module, if any.
pub docs: String,
}

/// A unique id used to determine whether two handles are nominally referring
/// to the same resource.
///
/// An id is composed of the definition location (a module id) and the original
/// name within that module.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ResourceId {
pub name: Id,
pub module_id: ModuleId,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct HandleDatatype {
/// The resource that this handle references, used for determining if two
/// handle types are nominally equal to one another.
pub resource_id: ResourceId,
}

impl HandleDatatype {
pub fn type_equal(&self, _other: &HandleDatatype) -> bool {
true
pub fn type_equal(&self, other: &HandleDatatype) -> bool {
self.resource_id == other.resource_id
}
}

Expand Down
58 changes: 53 additions & 5 deletions tools/witx/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ mod kw {
wast::custom_keyword!(noreturn);
wast::custom_keyword!(pointer);
wast::custom_keyword!(record);
wast::custom_keyword!(r#as = "as");
wast::custom_keyword!(r#const = "const");
wast::custom_keyword!(r#enum = "enum");
wast::custom_keyword!(r#union = "union");
wast::custom_keyword!(r#use = "use");
wast::custom_keyword!(repr);
wast::custom_keyword!(resource);
wast::custom_keyword!(s16);
wast::custom_keyword!(s32);
wast::custom_keyword!(s64);
Expand Down Expand Up @@ -227,6 +229,7 @@ impl<'a> Parse<'a> for TopLevelModule<'a> {
if parser.peek2::<kw::r#use>()
|| parser.peek2::<annotation::witx>()
|| parser.peek2::<kw::typename>()
|| parser.peek2::<kw::resource>()
{
decls.push(Documented {
comments,
Expand Down Expand Up @@ -279,6 +282,7 @@ impl<'a> Parse<'a> for TopLevelSyntax<'a> {
#[derive(Debug, Clone)]
pub enum DeclSyntax<'a> {
Typename(TypenameSyntax<'a>),
Resource(ResourceSyntax<'a>),
Const(Documented<'a, ConstSyntax<'a>>),
}

Expand All @@ -289,6 +293,8 @@ impl<'a> Parse<'a> for DeclSyntax<'a> {
Ok(DeclSyntax::Typename(parser.parse()?))
} else if l.peek::<annotation::witx>() {
Ok(DeclSyntax::Const(parser.parse()?))
} else if l.peek::<kw::resource>() {
Ok(DeclSyntax::Resource(parser.parse()?))
} else {
Err(l.error())
}
Expand All @@ -313,7 +319,7 @@ impl<'a> Parse<'a> for UseSyntax<'a> {

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UsedNames<'a> {
List(Vec<wast::Id<'a>>),
List(Vec<UseName<'a>>),
All(wast::Span),
}

Expand All @@ -333,6 +339,32 @@ impl<'a> Parse<'a> for UsedNames<'a> {
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UseName<'a> {
pub other_name: wast::Id<'a>,
pub our_name: wast::Id<'a>,
}

impl<'a> Parse<'a> for UseName<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let (other_name, our_name) = if parser.peek::<wast::Id>() {
let name = parser.parse()?;
(name, name)
} else {
parser.parens(|p| {
let other_name = p.parse()?;
p.parse::<kw::r#as>()?;
let our_name = p.parse()?;
Ok((other_name, our_name))
})?
};
Ok(UseName {
other_name,
our_name,
})
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypenameSyntax<'a> {
pub ident: wast::Id<'a>,
Expand All @@ -357,7 +389,7 @@ pub enum TypedefSyntax<'a> {
Record(RecordSyntax<'a>),
Union(UnionSyntax<'a>),
Variant(VariantSyntax<'a>),
Handle(HandleSyntax),
Handle(HandleSyntax<'a>),
List(Box<TypedefSyntax<'a>>),
Pointer(Box<TypedefSyntax<'a>>),
ConstPointer(Box<TypedefSyntax<'a>>),
Expand Down Expand Up @@ -521,6 +553,19 @@ impl<'a> Parse<'a> for ConstSyntax<'a> {
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResourceSyntax<'a> {
pub ident: wast::Id<'a>,
}

impl<'a> Parse<'a> for ResourceSyntax<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<kw::resource>()?;
let ident = parser.parse()?;
Ok(ResourceSyntax { ident })
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FlagsSyntax<'a> {
pub repr: Option<BuiltinType>,
Expand Down Expand Up @@ -656,12 +701,15 @@ impl<'a> Parse<'a> for CaseSyntax<'a> {
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandleSyntax {}
pub struct HandleSyntax<'a> {
pub resource: wast::Id<'a>,
}

impl<'a> Parse<'a> for HandleSyntax {
impl<'a> Parse<'a> for HandleSyntax<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<kw::handle>()?;
Ok(HandleSyntax {})
let resource = parser.parse()?;
Ok(HandleSyntax { resource })
}
}

Expand Down
15 changes: 0 additions & 15 deletions tools/witx/src/toplevel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,19 +165,4 @@ mod test {
e => panic!("wrong error: {:?}", e),
}
}

#[test]
fn use_invalid() {
match parse_witx_with("/a", &MockFs::new(&[("/a", "(use bbbbbbb)")]))
.err()
.unwrap()
{
WitxError::Parse(e) => {
let err = e.to_string();
assert!(err.contains("expected an identifier"), "bad error: {}", err);
assert!(err.contains("/a:1:6"));
}
e => panic!("wrong error: {:?}", e),
}
}
}
Loading

0 comments on commit 834679b

Please sign in to comment.