Shuffle around the wiggle crates (#1414)
* Shuffle around the wiggle crates This commit reorganizes the wiggle crates slightly by performing the following transforms: * The `crates/wiggle` crate, previously named `wiggle`, was moved to `crates/wiggle/crates/macro` and is renamed to `wiggle-macro`. * The `crates/wiggle/crates/runtime` crate, previously named `wiggle-runtime`, was moved to `crates/wiggle` and is renamed to `wiggle`. * The new `wiggle` crate depends on `wiggle-macro` and reexports the macro. The goal here is that consumers only deal with the `wiggle` crate itself. No more crates depend on `wiggle-runtime` and all dependencies are entirely on just the `wiggle` crate. * Remove the `crates/wiggle/crates` directory Move everything into `crates/wiggle` directly, like `wasi-common` * Add wiggle-macro to test-all script * Fixup a test
This commit is contained in:
25
crates/wiggle/generate/Cargo.toml
Normal file
25
crates/wiggle/generate/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "wiggle-generate"
|
||||
version = "0.13.0"
|
||||
authors = ["Pat Hickey <phickey@fastly.com>", "Jakub Konka <kubkon@jakubkonka.com>", "Alex Crichton <alex@alexcrichton.com>"]
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
edition = "2018"
|
||||
description = "Library crate for wiggle code generator."
|
||||
categories = ["wasm"]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
readme = "README.md"
|
||||
include = ["src/**/*", "LICENSE"]
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
witx = { version = "0.8.4", path = "../../wasi-common/wig/WASI/tools/witx" }
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
heck = "0.3"
|
||||
anyhow = "1"
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
220
crates/wiggle/generate/LICENSE
Normal file
220
crates/wiggle/generate/LICENSE
Normal file
@@ -0,0 +1,220 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
--- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
11
crates/wiggle/generate/README.md
Normal file
11
crates/wiggle/generate/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# wiggle-generate
|
||||
|
||||
This is a library crate that implements all of the component parts of
|
||||
the `wiggle` proc-macro crate.
|
||||
|
||||
Code lives in a separate non-proc-macro crate so that it can be reused in
|
||||
other settings, e.g. the `lucet-wiggle` crate.
|
||||
|
||||
Code generated by this crate should not have any references to a particular
|
||||
WebAssembly runtime or engine. It should instead expose traits that may be
|
||||
implemented by an engine. Today, it is consumed by both Lucet and Wasmtime.
|
||||
117
crates/wiggle/generate/src/config.rs
Normal file
117
crates/wiggle/generate/src/config.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use proc_macro2::Span;
|
||||
use syn::{
|
||||
braced, bracketed,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Error, Ident, LitStr, Result, Token,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub witx: WitxConf,
|
||||
pub ctx: CtxConf,
|
||||
pub emit_metadata: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConfigField {
|
||||
Witx(WitxConf),
|
||||
Ctx(CtxConf),
|
||||
}
|
||||
|
||||
impl ConfigField {
|
||||
pub fn parse_pair(ident: &str, value: ParseStream, err_loc: Span) -> Result<Self> {
|
||||
match ident {
|
||||
"witx" => Ok(ConfigField::Witx(value.parse()?)),
|
||||
"ctx" => Ok(ConfigField::Ctx(value.parse()?)),
|
||||
_ => Err(Error::new(err_loc, "expected `witx` or `ctx`")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for ConfigField {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let id: Ident = input.parse()?;
|
||||
let _colon: Token![:] = input.parse()?;
|
||||
Self::parse_pair(id.to_string().as_ref(), input, id.span())
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn build(fields: impl Iterator<Item = ConfigField>, err_loc: Span) -> Result<Self> {
|
||||
let mut witx = None;
|
||||
let mut ctx = None;
|
||||
for f in fields {
|
||||
match f {
|
||||
ConfigField::Witx(c) => {
|
||||
witx = Some(c);
|
||||
}
|
||||
ConfigField::Ctx(c) => {
|
||||
ctx = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Config {
|
||||
witx: witx
|
||||
.take()
|
||||
.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
|
||||
ctx: ctx
|
||||
.take()
|
||||
.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
|
||||
emit_metadata: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Config {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let contents;
|
||||
let _lbrace = braced!(contents in input);
|
||||
let fields: Punctuated<ConfigField, Token![,]> =
|
||||
contents.parse_terminated(ConfigField::parse)?;
|
||||
Ok(Config::build(fields.into_iter(), input.span())?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WitxConf {
|
||||
pub paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl WitxConf {
|
||||
pub fn make_paths_relative_to<P: AsRef<Path>>(&mut self, root: P) {
|
||||
self.paths.iter_mut().for_each(|p| {
|
||||
if !p.is_absolute() {
|
||||
*p = PathBuf::from(root.as_ref()).join(p.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for WitxConf {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let content;
|
||||
let _ = bracketed!(content in input);
|
||||
let path_lits: Punctuated<LitStr, Token![,]> = content.parse_terminated(Parse::parse)?;
|
||||
let paths = path_lits
|
||||
.iter()
|
||||
.map(|lit| PathBuf::from(lit.value()))
|
||||
.collect();
|
||||
Ok(WitxConf { paths })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CtxConf {
|
||||
pub name: Ident,
|
||||
}
|
||||
|
||||
impl Parse for CtxConf {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(CtxConf {
|
||||
name: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
264
crates/wiggle/generate/src/funcs.rs
Normal file
264
crates/wiggle/generate/src/funcs.rs
Normal file
@@ -0,0 +1,264 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::lifetimes::anon_lifetime;
|
||||
use crate::module_trait::passed_by_reference;
|
||||
use crate::names::Names;
|
||||
|
||||
pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
|
||||
let funcname = func.name.as_str();
|
||||
|
||||
let ident = names.func(&func.name);
|
||||
let ctx_type = names.ctx_type();
|
||||
let coretype = func.core_type();
|
||||
|
||||
let params = coretype.args.iter().map(|arg| {
|
||||
let name = names.func_core_arg(arg);
|
||||
let atom = names.atom_type(arg.repr());
|
||||
quote!(#name : #atom)
|
||||
});
|
||||
|
||||
let abi_args = quote!(
|
||||
ctx: &#ctx_type, memory: &dyn wiggle::GuestMemory,
|
||||
#(#params),*
|
||||
);
|
||||
let abi_ret = if let Some(ret) = &coretype.ret {
|
||||
match ret.signifies {
|
||||
witx::CoreParamSignifies::Value(atom) => names.atom_type(atom),
|
||||
_ => unreachable!("ret should always be passed by value"),
|
||||
}
|
||||
} else if func.noreturn {
|
||||
// Ideally we would return `quote!(!)` here, but, we'd have to change
|
||||
// the error handling logic in all the marshalling code to never return,
|
||||
// and instead provide some other way to bail to the context...
|
||||
// noreturn func
|
||||
unimplemented!("noreturn funcs not supported yet!")
|
||||
} else {
|
||||
quote!(())
|
||||
};
|
||||
|
||||
let err_type = coretype.ret.map(|ret| ret.param.tref);
|
||||
let err_val = err_type
|
||||
.clone()
|
||||
.map(|_res| quote!(#abi_ret::from(e)))
|
||||
.unwrap_or_else(|| quote!(()));
|
||||
|
||||
let error_handling = |location: &str| -> TokenStream {
|
||||
if let Some(tref) = &err_type {
|
||||
let abi_ret = match tref.type_().passed_by() {
|
||||
witx::TypePassedBy::Value(atom) => names.atom_type(atom),
|
||||
_ => unreachable!("err should always be passed by value"),
|
||||
};
|
||||
let err_typename = names.type_ref(&tref, anon_lifetime());
|
||||
quote! {
|
||||
let e = wiggle::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e.into()) };
|
||||
let err: #err_typename = wiggle::GuestErrorType::from_error(e, ctx);
|
||||
return #abi_ret::from(err);
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
panic!("error: {:?}", e)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let marshal_args = func
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| marshal_arg(names, p, error_handling(p.name.as_str())));
|
||||
let trait_args = func.params.iter().map(|param| {
|
||||
let name = names.func_param(¶m.name);
|
||||
if passed_by_reference(&*param.tref.type_()) {
|
||||
quote!(&#name)
|
||||
} else {
|
||||
quote!(#name)
|
||||
}
|
||||
});
|
||||
|
||||
let (trait_rets, trait_bindings) = if func.results.len() < 2 {
|
||||
(quote!({}), quote!(_))
|
||||
} else {
|
||||
let trait_rets = func
|
||||
.results
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|result| names.func_param(&result.name));
|
||||
let tuple = quote!((#(#trait_rets),*));
|
||||
(tuple.clone(), tuple)
|
||||
};
|
||||
|
||||
// Return value pointers need to be validated before the api call, then
|
||||
// assigned to afterwards. marshal_result returns these two statements as a pair.
|
||||
let marshal_rets = func
|
||||
.results
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|result| marshal_result(names, result, &error_handling));
|
||||
let marshal_rets_pre = marshal_rets.clone().map(|(pre, _post)| pre);
|
||||
let marshal_rets_post = marshal_rets.map(|(_pre, post)| post);
|
||||
|
||||
let success = if let Some(ref err_type) = err_type {
|
||||
let err_typename = names.type_ref(&err_type, anon_lifetime());
|
||||
quote! {
|
||||
let success:#err_typename = wiggle::GuestErrorType::success();
|
||||
#abi_ret::from(success)
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
quote!(pub fn #ident(#abi_args) -> #abi_ret {
|
||||
#(#marshal_args)*
|
||||
#(#marshal_rets_pre)*
|
||||
let #trait_bindings = match ctx.#ident(#(#trait_args),*) {
|
||||
Ok(#trait_bindings) => #trait_rets,
|
||||
Err(e) => { return #err_val; },
|
||||
};
|
||||
#(#marshal_rets_post)*
|
||||
#success
|
||||
})
|
||||
}
|
||||
|
||||
fn marshal_arg(
|
||||
names: &Names,
|
||||
param: &witx::InterfaceFuncParam,
|
||||
error_handling: TokenStream,
|
||||
) -> TokenStream {
|
||||
let tref = ¶m.tref;
|
||||
let interface_typename = names.type_ref(&tref, anon_lifetime());
|
||||
|
||||
let try_into_conversion = {
|
||||
let name = names.func_param(¶m.name);
|
||||
quote! {
|
||||
let #name: #interface_typename = {
|
||||
use ::std::convert::TryInto;
|
||||
match #name.try_into() {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
#error_handling
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let read_conversion = {
|
||||
let pointee_type = names.type_ref(tref, anon_lifetime());
|
||||
let arg_name = names.func_ptr_binding(¶m.name);
|
||||
let name = names.func_param(¶m.name);
|
||||
quote! {
|
||||
let #name = match wiggle::GuestPtr::<#pointee_type>::new(memory, #arg_name as u32).read() {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
#error_handling
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
match &*tref.type_() {
|
||||
witx::Type::Enum(_e) => try_into_conversion,
|
||||
witx::Type::Flags(_f) => try_into_conversion,
|
||||
witx::Type::Int(_i) => try_into_conversion,
|
||||
witx::Type::Builtin(b) => match b {
|
||||
witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => {
|
||||
try_into_conversion
|
||||
}
|
||||
witx::BuiltinType::S8 | witx::BuiltinType::S16 => {
|
||||
let name = names.func_param(¶m.name);
|
||||
quote! {
|
||||
let #name: #interface_typename = match (#name as i32).try_into() {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
#error_handling
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
witx::BuiltinType::U32
|
||||
| witx::BuiltinType::S32
|
||||
| witx::BuiltinType::U64
|
||||
| witx::BuiltinType::S64
|
||||
| witx::BuiltinType::USize
|
||||
| witx::BuiltinType::F32
|
||||
| witx::BuiltinType::F64 => {
|
||||
let name = names.func_param(¶m.name);
|
||||
quote! {
|
||||
let #name = #name as #interface_typename;
|
||||
}
|
||||
}
|
||||
witx::BuiltinType::String => {
|
||||
let lifetime = anon_lifetime();
|
||||
let ptr_name = names.func_ptr_binding(¶m.name);
|
||||
let len_name = names.func_len_binding(¶m.name);
|
||||
let name = names.func_param(¶m.name);
|
||||
quote! {
|
||||
let #name = wiggle::GuestPtr::<#lifetime, str>::new(memory, (#ptr_name as u32, #len_name as u32));
|
||||
}
|
||||
}
|
||||
},
|
||||
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(pointee, anon_lifetime());
|
||||
let name = names.func_param(¶m.name);
|
||||
quote! {
|
||||
let #name = wiggle::GuestPtr::<#pointee_type>::new(memory, #name as u32);
|
||||
}
|
||||
}
|
||||
witx::Type::Struct(_) => read_conversion,
|
||||
witx::Type::Array(arr) => {
|
||||
let pointee_type = names.type_ref(arr, anon_lifetime());
|
||||
let ptr_name = names.func_ptr_binding(¶m.name);
|
||||
let len_name = names.func_len_binding(¶m.name);
|
||||
let name = names.func_param(¶m.name);
|
||||
quote! {
|
||||
let #name = wiggle::GuestPtr::<[#pointee_type]>::new(memory, (#ptr_name as u32, #len_name as u32));
|
||||
}
|
||||
}
|
||||
witx::Type::Union(_u) => read_conversion,
|
||||
witx::Type::Handle(_h) => {
|
||||
let name = names.func_param(¶m.name);
|
||||
let handle_type = names.type_ref(tref, anon_lifetime());
|
||||
quote!( let #name = #handle_type::from(#name); )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn marshal_result<F>(
|
||||
names: &Names,
|
||||
result: &witx::InterfaceFuncParam,
|
||||
error_handling: F,
|
||||
) -> (TokenStream, TokenStream)
|
||||
where
|
||||
F: Fn(&str) -> TokenStream,
|
||||
{
|
||||
let tref = &result.tref;
|
||||
|
||||
let write_val_to_ptr = {
|
||||
let pointee_type = names.type_ref(tref, anon_lifetime());
|
||||
// core type is given func_ptr_binding name.
|
||||
let ptr_name = names.func_ptr_binding(&result.name);
|
||||
let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str()));
|
||||
let pre = quote! {
|
||||
let #ptr_name = wiggle::GuestPtr::<#pointee_type>::new(memory, #ptr_name as u32);
|
||||
};
|
||||
// trait binding returns func_param name.
|
||||
let val_name = names.func_param(&result.name);
|
||||
let post = quote! {
|
||||
if let Err(e) = #ptr_name.write(#val_name) {
|
||||
#ptr_err_handling
|
||||
}
|
||||
};
|
||||
(pre, post)
|
||||
};
|
||||
|
||||
match &*tref.type_() {
|
||||
witx::Type::Builtin(b) => match b {
|
||||
witx::BuiltinType::String => unimplemented!("string result types"),
|
||||
_ => write_val_to_ptr,
|
||||
},
|
||||
witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } | witx::Type::Array { .. } => {
|
||||
unimplemented!("pointer/array result types")
|
||||
}
|
||||
_ => write_val_to_ptr,
|
||||
}
|
||||
}
|
||||
59
crates/wiggle/generate/src/lib.rs
Normal file
59
crates/wiggle/generate/src/lib.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
pub mod config;
|
||||
mod funcs;
|
||||
mod lifetimes;
|
||||
mod module_trait;
|
||||
mod names;
|
||||
mod types;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub use config::Config;
|
||||
pub use funcs::define_func;
|
||||
pub use module_trait::define_module_trait;
|
||||
pub use names::Names;
|
||||
pub use types::define_datatype;
|
||||
|
||||
pub fn generate(doc: &witx::Document, config: &Config) -> TokenStream {
|
||||
let names = Names::new(config); // TODO parse the names from the invocation of the macro, or from a file?
|
||||
|
||||
let types = doc.typenames().map(|t| define_datatype(&names, &t));
|
||||
|
||||
let modules = doc.modules().map(|module| {
|
||||
let modname = names.module(&module.name);
|
||||
let fs = module.funcs().map(|f| define_func(&names, &f));
|
||||
let modtrait = define_module_trait(&names, &module);
|
||||
let ctx_type = names.ctx_type();
|
||||
quote!(
|
||||
pub mod #modname {
|
||||
use super::#ctx_type;
|
||||
use super::types::*;
|
||||
#(#fs)*
|
||||
|
||||
#modtrait
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
let metadata = if config.emit_metadata {
|
||||
let doc_text = &format!("{}", doc);
|
||||
quote! {
|
||||
pub mod metadata {
|
||||
pub const DOC_TEXT: &str = #doc_text;
|
||||
pub fn document() -> wiggle::witx::Document {
|
||||
wiggle::witx::parse(DOC_TEXT).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
quote!(
|
||||
pub mod types {
|
||||
#(#types)*
|
||||
}
|
||||
#(#modules)*
|
||||
#metadata
|
||||
)
|
||||
}
|
||||
83
crates/wiggle/generate/src/lifetimes.rs
Normal file
83
crates/wiggle/generate/src/lifetimes.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub trait LifetimeExt {
|
||||
fn is_transparent(&self) -> bool;
|
||||
fn needs_lifetime(&self) -> bool;
|
||||
}
|
||||
|
||||
impl LifetimeExt for witx::TypeRef {
|
||||
fn is_transparent(&self) -> bool {
|
||||
self.type_().is_transparent()
|
||||
}
|
||||
fn needs_lifetime(&self) -> bool {
|
||||
self.type_().needs_lifetime()
|
||||
}
|
||||
}
|
||||
|
||||
impl LifetimeExt for witx::Type {
|
||||
fn is_transparent(&self) -> bool {
|
||||
match self {
|
||||
witx::Type::Builtin(b) => b.is_transparent(),
|
||||
witx::Type::Struct(s) => s.is_transparent(),
|
||||
witx::Type::Enum { .. }
|
||||
| witx::Type::Flags { .. }
|
||||
| witx::Type::Int { .. }
|
||||
| witx::Type::Handle { .. } => true,
|
||||
witx::Type::Union { .. }
|
||||
| witx::Type::Pointer { .. }
|
||||
| witx::Type::ConstPointer { .. }
|
||||
| witx::Type::Array { .. } => false,
|
||||
}
|
||||
}
|
||||
fn needs_lifetime(&self) -> bool {
|
||||
match self {
|
||||
witx::Type::Builtin(b) => b.needs_lifetime(),
|
||||
witx::Type::Struct(s) => s.needs_lifetime(),
|
||||
witx::Type::Union(u) => u.needs_lifetime(),
|
||||
witx::Type::Enum { .. }
|
||||
| witx::Type::Flags { .. }
|
||||
| witx::Type::Int { .. }
|
||||
| witx::Type::Handle { .. } => false,
|
||||
witx::Type::Pointer { .. }
|
||||
| witx::Type::ConstPointer { .. }
|
||||
| witx::Type::Array { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LifetimeExt for witx::BuiltinType {
|
||||
fn is_transparent(&self) -> bool {
|
||||
!self.needs_lifetime()
|
||||
}
|
||||
fn needs_lifetime(&self) -> bool {
|
||||
match self {
|
||||
witx::BuiltinType::String => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LifetimeExt for witx::StructDatatype {
|
||||
fn is_transparent(&self) -> bool {
|
||||
self.members.iter().all(|m| m.tref.is_transparent())
|
||||
}
|
||||
fn needs_lifetime(&self) -> bool {
|
||||
self.members.iter().any(|m| m.tref.needs_lifetime())
|
||||
}
|
||||
}
|
||||
|
||||
impl LifetimeExt for witx::UnionDatatype {
|
||||
fn is_transparent(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn needs_lifetime(&self) -> bool {
|
||||
self.variants
|
||||
.iter()
|
||||
.any(|m| m.tref.as_ref().map(|t| t.needs_lifetime()).unwrap_or(false))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn anon_lifetime() -> TokenStream {
|
||||
quote!('_)
|
||||
}
|
||||
72
crates/wiggle/generate/src/module_trait.rs
Normal file
72
crates/wiggle/generate/src/module_trait.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::lifetimes::{anon_lifetime, LifetimeExt};
|
||||
use crate::names::Names;
|
||||
use witx::Module;
|
||||
|
||||
pub fn passed_by_reference(ty: &witx::Type) -> bool {
|
||||
let passed_by = match ty.passed_by() {
|
||||
witx::TypePassedBy::Value { .. } => false,
|
||||
witx::TypePassedBy::Pointer { .. } | witx::TypePassedBy::PointerLengthPair { .. } => true,
|
||||
};
|
||||
match ty {
|
||||
witx::Type::Builtin(b) => match &*b {
|
||||
witx::BuiltinType::String => true,
|
||||
_ => passed_by,
|
||||
},
|
||||
witx::Type::Pointer(_) | witx::Type::ConstPointer(_) | witx::Type::Array(_) => true,
|
||||
_ => passed_by,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream {
|
||||
let traitname = names.trait_name(&m.name);
|
||||
let traitmethods = m.funcs().map(|f| {
|
||||
// Check if we're returning an entity anotated with a lifetime,
|
||||
// in which case, we'll need to annotate the function itself, and
|
||||
// hence will need an explicit lifetime (rather than anonymous)
|
||||
let (lifetime, is_anonymous) = if f
|
||||
.params
|
||||
.iter()
|
||||
.chain(&f.results)
|
||||
.any(|ret| ret.tref.needs_lifetime())
|
||||
{
|
||||
(quote!('a), false)
|
||||
} else {
|
||||
(anon_lifetime(), true)
|
||||
};
|
||||
let funcname = names.func(&f.name);
|
||||
let args = f.params.iter().map(|arg| {
|
||||
let arg_name = names.func_param(&arg.name);
|
||||
let arg_typename = names.type_ref(&arg.tref, lifetime.clone());
|
||||
let arg_type = if passed_by_reference(&*arg.tref.type_()) {
|
||||
quote!(&#arg_typename)
|
||||
} else {
|
||||
quote!(#arg_typename)
|
||||
};
|
||||
quote!(#arg_name: #arg_type)
|
||||
});
|
||||
let rets = f
|
||||
.results
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|ret| names.type_ref(&ret.tref, lifetime.clone()));
|
||||
let err = f
|
||||
.results
|
||||
.get(0)
|
||||
.map(|err_result| names.type_ref(&err_result.tref, lifetime.clone()))
|
||||
.unwrap_or(quote!(()));
|
||||
|
||||
if is_anonymous {
|
||||
quote!(fn #funcname(&self, #(#args),*) -> Result<(#(#rets),*), #err>;)
|
||||
} else {
|
||||
quote!(fn #funcname<#lifetime>(&self, #(#args),*) -> Result<(#(#rets),*), #err>;)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
pub trait #traitname {
|
||||
#(#traitmethods)*
|
||||
}
|
||||
}
|
||||
}
|
||||
144
crates/wiggle/generate/src/names.rs
Normal file
144
crates/wiggle/generate/src/names.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use heck::{CamelCase, ShoutySnakeCase, SnakeCase};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use witx::{AtomType, BuiltinType, Id, TypeRef};
|
||||
|
||||
use crate::lifetimes::LifetimeExt;
|
||||
use crate::Config;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Names {
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Names {
|
||||
pub fn new(config: &Config) -> Names {
|
||||
Names {
|
||||
config: config.clone(),
|
||||
}
|
||||
}
|
||||
pub fn ctx_type(&self) -> Ident {
|
||||
self.config.ctx.name.clone()
|
||||
}
|
||||
pub fn type_(&self, id: &Id) -> TokenStream {
|
||||
let ident = format_ident!("{}", id.as_str().to_camel_case());
|
||||
quote!(#ident)
|
||||
}
|
||||
pub fn builtin_type(&self, b: BuiltinType, lifetime: TokenStream) -> TokenStream {
|
||||
match b {
|
||||
BuiltinType::String => quote!(wiggle::GuestPtr<#lifetime, str>),
|
||||
BuiltinType::U8 => quote!(u8),
|
||||
BuiltinType::U16 => quote!(u16),
|
||||
BuiltinType::U32 => quote!(u32),
|
||||
BuiltinType::U64 => quote!(u64),
|
||||
BuiltinType::S8 => quote!(i8),
|
||||
BuiltinType::S16 => quote!(i16),
|
||||
BuiltinType::S32 => quote!(i32),
|
||||
BuiltinType::S64 => quote!(i64),
|
||||
BuiltinType::F32 => quote!(f32),
|
||||
BuiltinType::F64 => quote!(f64),
|
||||
BuiltinType::Char8 => quote!(u8),
|
||||
BuiltinType::USize => quote!(u32),
|
||||
}
|
||||
}
|
||||
pub fn atom_type(&self, atom: AtomType) -> TokenStream {
|
||||
match atom {
|
||||
AtomType::I32 => quote!(i32),
|
||||
AtomType::I64 => quote!(i64),
|
||||
AtomType::F32 => quote!(f32),
|
||||
AtomType::F64 => quote!(f64),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_ref(&self, tref: &TypeRef, lifetime: TokenStream) -> TokenStream {
|
||||
match tref {
|
||||
TypeRef::Name(nt) => {
|
||||
let ident = self.type_(&nt.name);
|
||||
if nt.tref.needs_lifetime() {
|
||||
quote!(#ident<#lifetime>)
|
||||
} else {
|
||||
quote!(#ident)
|
||||
}
|
||||
}
|
||||
TypeRef::Value(ty) => match &**ty {
|
||||
witx::Type::Builtin(builtin) => self.builtin_type(*builtin, lifetime.clone()),
|
||||
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = self.type_ref(&pointee, lifetime.clone());
|
||||
quote!(wiggle::GuestPtr<#lifetime, #pointee_type>)
|
||||
}
|
||||
witx::Type::Array(pointee) => {
|
||||
let pointee_type = self.type_ref(&pointee, lifetime.clone());
|
||||
quote!(wiggle::GuestPtr<#lifetime, [#pointee_type]>)
|
||||
}
|
||||
_ => unimplemented!("anonymous type ref {:?}", tref),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enum_variant(&self, id: &Id) -> Ident {
|
||||
// FIXME this is a hack - just a proof of concept.
|
||||
if id.as_str().starts_with('2') {
|
||||
format_ident!("TooBig")
|
||||
} else if id.as_str() == "type" {
|
||||
format_ident!("Type")
|
||||
} else {
|
||||
format_ident!("{}", id.as_str().to_camel_case())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flag_member(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}", id.as_str().to_shouty_snake_case())
|
||||
}
|
||||
|
||||
pub fn int_member(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}", id.as_str().to_shouty_snake_case())
|
||||
}
|
||||
|
||||
pub fn struct_member(&self, id: &Id) -> Ident {
|
||||
// FIXME this is a hack - just a proof of concept.
|
||||
if id.as_str() == "type" {
|
||||
format_ident!("type_")
|
||||
} else {
|
||||
format_ident!("{}", id.as_str().to_snake_case())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}", id.as_str().to_snake_case())
|
||||
}
|
||||
|
||||
pub fn trait_name(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}", id.as_str().to_camel_case())
|
||||
}
|
||||
|
||||
pub fn func(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}", id.as_str().to_snake_case())
|
||||
}
|
||||
|
||||
pub fn func_param(&self, id: &Id) -> Ident {
|
||||
// FIXME this is a hack - just a proof of concept.
|
||||
if id.as_str() == "in" {
|
||||
format_ident!("in_")
|
||||
} else {
|
||||
format_ident!("{}", id.as_str().to_snake_case())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn func_core_arg(&self, arg: &witx::CoreParamType) -> Ident {
|
||||
match arg.signifies {
|
||||
witx::CoreParamSignifies::Value { .. } => self.func_param(&arg.param.name),
|
||||
witx::CoreParamSignifies::PointerTo => self.func_ptr_binding(&arg.param.name),
|
||||
witx::CoreParamSignifies::LengthOf => self.func_len_binding(&arg.param.name),
|
||||
}
|
||||
}
|
||||
|
||||
/// For when you need a {name}_ptr binding for passing a value by reference:
|
||||
pub fn func_ptr_binding(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}_ptr", id.as_str().to_snake_case())
|
||||
}
|
||||
|
||||
/// For when you need a {name}_len binding for passing an array:
|
||||
pub fn func_len_binding(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}_len", id.as_str().to_snake_case())
|
||||
}
|
||||
}
|
||||
113
crates/wiggle/generate/src/types/enum.rs
Normal file
113
crates/wiggle/generate/src/types/enum.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use super::{atom_token, int_repr_tokens};
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream {
|
||||
let ident = names.type_(&name);
|
||||
|
||||
let repr = int_repr_tokens(e.repr);
|
||||
let abi_repr = atom_token(match e.repr {
|
||||
witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32,
|
||||
witx::IntRepr::U64 => witx::AtomType::I64,
|
||||
});
|
||||
|
||||
let mut variant_names = vec![];
|
||||
let mut tryfrom_repr_cases = vec![];
|
||||
let mut to_repr_cases = vec![];
|
||||
let mut to_display = vec![];
|
||||
|
||||
for (n, variant) in e.variants.iter().enumerate() {
|
||||
let variant_name = names.enum_variant(&variant.name);
|
||||
let docs = variant.docs.trim();
|
||||
let ident_str = ident.to_string();
|
||||
let variant_str = variant_name.to_string();
|
||||
tryfrom_repr_cases.push(quote!(#n => Ok(#ident::#variant_name)));
|
||||
to_repr_cases.push(quote!(#ident::#variant_name => #n as #repr));
|
||||
to_display.push(quote!(#ident::#variant_name => format!("{} ({}::{}({}))", #docs, #ident_str, #variant_str, #repr::from(*self))));
|
||||
variant_names.push(variant_name);
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[repr(#repr)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub enum #ident {
|
||||
#(#variant_names),*
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
let to_str = match self {
|
||||
#(#to_display,)*
|
||||
};
|
||||
write!(f, "{}", to_str)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#repr> for #ident {
|
||||
type Error = wiggle::GuestError;
|
||||
fn try_from(value: #repr) -> Result<#ident, wiggle::GuestError> {
|
||||
match value as usize {
|
||||
#(#tryfrom_repr_cases),*,
|
||||
_ => Err(wiggle::GuestError::InvalidEnumValue(stringify!(#ident))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle::GuestError> {
|
||||
#ident::try_from(value as #repr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for #repr {
|
||||
fn from(e: #ident) -> #repr {
|
||||
match e {
|
||||
#(#to_repr_cases),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for #abi_repr {
|
||||
fn from(e: #ident) -> #abi_repr {
|
||||
#repr::from(e) as #abi_repr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle::GuestType<'a> for #ident {
|
||||
fn guest_size() -> u32 {
|
||||
#repr::guest_size()
|
||||
}
|
||||
|
||||
fn guest_align() -> usize {
|
||||
#repr::guest_align()
|
||||
}
|
||||
|
||||
fn read(location: &wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> {
|
||||
use std::convert::TryFrom;
|
||||
let reprval = #repr::read(&location.cast())?;
|
||||
let value = #ident::try_from(reprval)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self)
|
||||
-> Result<(), wiggle::GuestError>
|
||||
{
|
||||
#repr::write(&location.cast(), #repr::from(val))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl <'a> wiggle::GuestTypeTransparent<'a> for #ident {
|
||||
#[inline]
|
||||
fn validate(location: *mut #ident) -> Result<(), wiggle::GuestError> {
|
||||
use std::convert::TryFrom;
|
||||
// Validate value in memory using #ident::try_from(reprval)
|
||||
let reprval = unsafe { (location as *mut #repr).read() };
|
||||
let _val = #ident::try_from(reprval)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
181
crates/wiggle/generate/src/types/flags.rs
Normal file
181
crates/wiggle/generate/src/types/flags.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use super::{atom_token, int_repr_tokens};
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
use quote::quote;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream {
|
||||
let ident = names.type_(&name);
|
||||
let repr = int_repr_tokens(f.repr);
|
||||
let abi_repr = atom_token(match f.repr {
|
||||
witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32,
|
||||
witx::IntRepr::U64 => witx::AtomType::I64,
|
||||
});
|
||||
|
||||
let mut names_ = vec![];
|
||||
let mut values_ = vec![];
|
||||
for (i, f) in f.flags.iter().enumerate() {
|
||||
let name = names.flag_member(&f.name);
|
||||
let value = 1u128
|
||||
.checked_shl(u32::try_from(i).expect("flag value overflow"))
|
||||
.expect("flag value overflow");
|
||||
let value_token = Literal::u128_unsuffixed(value);
|
||||
names_.push(name);
|
||||
values_.push(value_token);
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(#repr);
|
||||
|
||||
impl #ident {
|
||||
#(pub const #names_: #ident = #ident(#values_);)*
|
||||
|
||||
#[inline]
|
||||
pub const fn empty() -> Self {
|
||||
#ident(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn all() -> Self {
|
||||
#ident(#(#values_)|*)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, other: &#ident) -> bool {
|
||||
!*self & *other == Self::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
let mut first = true;
|
||||
#(
|
||||
if self.0 & #values_ == #values_ {
|
||||
if !first {
|
||||
f.write_str("|")?;
|
||||
}
|
||||
first = false;
|
||||
f.write_fmt(format_args!("{}", stringify!(#names_).to_lowercase()))?;
|
||||
}
|
||||
)*
|
||||
if first {
|
||||
f.write_str("empty")?;
|
||||
}
|
||||
f.write_fmt(format_args!(" ({:#x})", self.0))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitAnd for #ident {
|
||||
type Output = Self;
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
#ident(self.0 & rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitAndAssign for #ident {
|
||||
fn bitand_assign(&mut self, rhs: Self) {
|
||||
*self = *self & rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitOr for #ident {
|
||||
type Output = Self;
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
#ident(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitOrAssign for #ident {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitXor for #ident {
|
||||
type Output = Self;
|
||||
fn bitxor(self, rhs: Self) -> Self::Output {
|
||||
#ident(self.0 ^ rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitXorAssign for #ident {
|
||||
fn bitxor_assign(&mut self, rhs: Self) {
|
||||
*self = *self ^ rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Not for #ident {
|
||||
type Output = Self;
|
||||
fn not(self) -> Self::Output {
|
||||
#ident(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#repr> for #ident {
|
||||
type Error = wiggle::GuestError;
|
||||
fn try_from(value: #repr) -> Result<Self, wiggle::GuestError> {
|
||||
if #repr::from(!#ident::all()) & value != 0 {
|
||||
Err(wiggle::GuestError::InvalidFlagValue(stringify!(#ident)))
|
||||
} else {
|
||||
Ok(#ident(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle::GuestError> {
|
||||
#ident::try_from(value as #repr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for #repr {
|
||||
fn from(e: #ident) -> #repr {
|
||||
e.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for #abi_repr {
|
||||
fn from(e: #ident) -> #abi_repr {
|
||||
#repr::from(e) as #abi_repr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle::GuestType<'a> for #ident {
|
||||
fn guest_size() -> u32 {
|
||||
#repr::guest_size()
|
||||
}
|
||||
|
||||
fn guest_align() -> usize {
|
||||
#repr::guest_align()
|
||||
}
|
||||
|
||||
fn read(location: &wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> {
|
||||
use std::convert::TryFrom;
|
||||
let reprval = #repr::read(&location.cast())?;
|
||||
let value = #ident::try_from(reprval)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle::GuestError> {
|
||||
let val: #repr = #repr::from(val);
|
||||
#repr::write(&location.cast(), val)
|
||||
}
|
||||
}
|
||||
unsafe impl <'a> wiggle::GuestTypeTransparent<'a> for #ident {
|
||||
#[inline]
|
||||
fn validate(location: *mut #ident) -> Result<(), wiggle::GuestError> {
|
||||
use std::convert::TryFrom;
|
||||
// Validate value in memory using #ident::try_from(reprval)
|
||||
let reprval = unsafe { (location as *mut #repr).read() };
|
||||
let _val = #ident::try_from(reprval)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
83
crates/wiggle/generate/src/types/handle.rs
Normal file
83
crates/wiggle/generate/src/types/handle.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use witx::Layout;
|
||||
|
||||
pub(super) fn define_handle(
|
||||
names: &Names,
|
||||
name: &witx::Id,
|
||||
h: &witx::HandleDatatype,
|
||||
) -> TokenStream {
|
||||
let ident = names.type_(name);
|
||||
let size = h.mem_size_align().size as u32;
|
||||
let align = h.mem_size_align().align as usize;
|
||||
quote! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(u32);
|
||||
|
||||
impl #ident {
|
||||
pub unsafe fn inner(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for u32 {
|
||||
fn from(e: #ident) -> u32 {
|
||||
e.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for i32 {
|
||||
fn from(e: #ident) -> i32 {
|
||||
e.0 as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for #ident {
|
||||
fn from(e: u32) -> #ident {
|
||||
#ident(e)
|
||||
}
|
||||
}
|
||||
impl From<i32> for #ident {
|
||||
fn from(e: i32) -> #ident {
|
||||
#ident(e as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "{}({})", stringify!(#ident), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle::GuestType<'a> for #ident {
|
||||
fn guest_size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn guest_align() -> usize {
|
||||
#align
|
||||
}
|
||||
|
||||
fn read(location: &wiggle::GuestPtr<'a, #ident>) -> Result<#ident, wiggle::GuestError> {
|
||||
Ok(#ident(u32::read(&location.cast())?))
|
||||
}
|
||||
|
||||
fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> {
|
||||
u32::write(&location.cast(), val.0)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> wiggle::GuestTypeTransparent<'a> for #ident {
|
||||
#[inline]
|
||||
fn validate(_location: *mut #ident) -> Result<(), wiggle::GuestError> {
|
||||
// All bit patterns accepted
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
93
crates/wiggle/generate/src/types/int.rs
Normal file
93
crates/wiggle/generate/src/types/int.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use super::{atom_token, int_repr_tokens};
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(super) fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStream {
|
||||
let ident = names.type_(&name);
|
||||
let repr = int_repr_tokens(i.repr);
|
||||
let abi_repr = atom_token(match i.repr {
|
||||
witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32,
|
||||
witx::IntRepr::U64 => witx::AtomType::I64,
|
||||
});
|
||||
let consts = i
|
||||
.consts
|
||||
.iter()
|
||||
.map(|r#const| {
|
||||
let const_ident = names.int_member(&r#const.name);
|
||||
let value = r#const.value;
|
||||
quote!(pub const #const_ident: #ident = #ident(#value))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(#repr);
|
||||
|
||||
impl #ident {
|
||||
#(#consts;)*
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#repr> for #ident {
|
||||
type Error = wiggle::GuestError;
|
||||
fn try_from(value: #repr) -> Result<Self, wiggle::GuestError> {
|
||||
Ok(#ident(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle::GuestError> {
|
||||
#ident::try_from(value as #repr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for #repr {
|
||||
fn from(e: #ident) -> #repr {
|
||||
e.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for #abi_repr {
|
||||
fn from(e: #ident) -> #abi_repr {
|
||||
#repr::from(e) as #abi_repr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle::GuestType<'a> for #ident {
|
||||
fn guest_size() -> u32 {
|
||||
#repr::guest_size()
|
||||
}
|
||||
|
||||
fn guest_align() -> usize {
|
||||
#repr::guest_align()
|
||||
}
|
||||
|
||||
fn read(location: &wiggle::GuestPtr<'a, #ident>) -> Result<#ident, wiggle::GuestError> {
|
||||
Ok(#ident(#repr::read(&location.cast())?))
|
||||
|
||||
}
|
||||
|
||||
fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle::GuestError> {
|
||||
#repr::write(&location.cast(), val.0)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> wiggle::GuestTypeTransparent<'a> for #ident {
|
||||
#[inline]
|
||||
fn validate(_location: *mut #ident) -> Result<(), wiggle::GuestError> {
|
||||
// All bit patterns accepted
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
90
crates/wiggle/generate/src/types/mod.rs
Normal file
90
crates/wiggle/generate/src/types/mod.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
mod r#enum;
|
||||
mod flags;
|
||||
mod handle;
|
||||
mod int;
|
||||
mod r#struct;
|
||||
mod union;
|
||||
|
||||
use crate::lifetimes::LifetimeExt;
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream {
|
||||
match &namedtype.tref {
|
||||
witx::TypeRef::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to),
|
||||
witx::TypeRef::Value(v) => match &**v {
|
||||
witx::Type::Enum(e) => r#enum::define_enum(names, &namedtype.name, &e),
|
||||
witx::Type::Int(i) => int::define_int(names, &namedtype.name, &i),
|
||||
witx::Type::Flags(f) => flags::define_flags(names, &namedtype.name, &f),
|
||||
witx::Type::Struct(s) => r#struct::define_struct(names, &namedtype.name, &s),
|
||||
witx::Type::Union(u) => union::define_union(names, &namedtype.name, &u),
|
||||
witx::Type::Handle(h) => handle::define_handle(names, &namedtype.name, &h),
|
||||
witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b),
|
||||
witx::Type::Pointer(p) => {
|
||||
define_witx_pointer(names, &namedtype.name, quote!(wiggle::GuestPtr), p)
|
||||
}
|
||||
witx::Type::ConstPointer(p) => {
|
||||
define_witx_pointer(names, &namedtype.name, quote!(wiggle::GuestPtr), p)
|
||||
}
|
||||
witx::Type::Array(arr) => define_witx_array(names, &namedtype.name, &arr),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream {
|
||||
let ident = names.type_(name);
|
||||
let rhs = names.type_(&to.name);
|
||||
if to.tref.needs_lifetime() {
|
||||
quote!(pub type #ident<'a> = #rhs<'a>;)
|
||||
} else {
|
||||
quote!(pub type #ident = #rhs;)
|
||||
}
|
||||
}
|
||||
|
||||
fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream {
|
||||
let ident = names.type_(name);
|
||||
let built = names.builtin_type(builtin, quote!('a));
|
||||
if builtin.needs_lifetime() {
|
||||
quote!(pub type #ident<'a> = #built;)
|
||||
} else {
|
||||
quote!(pub type #ident = #built;)
|
||||
}
|
||||
}
|
||||
|
||||
fn define_witx_pointer(
|
||||
names: &Names,
|
||||
name: &witx::Id,
|
||||
pointer_type: TokenStream,
|
||||
pointee: &witx::TypeRef,
|
||||
) -> TokenStream {
|
||||
let ident = names.type_(name);
|
||||
let pointee_type = names.type_ref(pointee, quote!('a));
|
||||
|
||||
quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;)
|
||||
}
|
||||
|
||||
fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream {
|
||||
let ident = names.type_(name);
|
||||
let pointee_type = names.type_ref(arr_raw, quote!('a));
|
||||
quote!(pub type #ident<'a> = wiggle::GuestPtr<'a, [#pointee_type]>;)
|
||||
}
|
||||
|
||||
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 atom_token(atom: witx::AtomType) -> TokenStream {
|
||||
match atom {
|
||||
witx::AtomType::I32 => quote!(i32),
|
||||
witx::AtomType::I64 => quote!(i64),
|
||||
witx::AtomType::F32 => quote!(f32),
|
||||
witx::AtomType::F64 => quote!(f64),
|
||||
}
|
||||
}
|
||||
134
crates/wiggle/generate/src/types/struct.rs
Normal file
134
crates/wiggle/generate/src/types/struct.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use crate::lifetimes::{anon_lifetime, LifetimeExt};
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use witx::Layout;
|
||||
|
||||
pub(super) fn define_struct(
|
||||
names: &Names,
|
||||
name: &witx::Id,
|
||||
s: &witx::StructDatatype,
|
||||
) -> TokenStream {
|
||||
let ident = names.type_(name);
|
||||
let size = s.mem_size_align().size as u32;
|
||||
let align = s.mem_size_align().align as usize;
|
||||
|
||||
let member_names = s.members.iter().map(|m| names.struct_member(&m.name));
|
||||
let member_decls = s.members.iter().map(|m| {
|
||||
let name = names.struct_member(&m.name);
|
||||
let type_ = match &m.tref {
|
||||
witx::TypeRef::Name(nt) => names.type_(&nt.name),
|
||||
witx::TypeRef::Value(ty) => match &**ty {
|
||||
witx::Type::Builtin(builtin) => names.builtin_type(*builtin, quote!('a)),
|
||||
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, quote!('a));
|
||||
quote!(wiggle::GuestPtr<'a, #pointee_type>)
|
||||
}
|
||||
_ => unimplemented!("other anonymous struct members"),
|
||||
},
|
||||
};
|
||||
quote!(pub #name: #type_)
|
||||
});
|
||||
|
||||
let member_reads = s.member_layout().into_iter().map(|ml| {
|
||||
let name = names.struct_member(&ml.member.name);
|
||||
let offset = ml.offset as u32;
|
||||
let location = quote!(location.cast::<u8>().add(#offset)?.cast());
|
||||
match &ml.member.tref {
|
||||
witx::TypeRef::Name(nt) => {
|
||||
let type_ = names.type_(&nt.name);
|
||||
quote! {
|
||||
let #name = <#type_ as wiggle::GuestType>::read(&#location)?;
|
||||
}
|
||||
}
|
||||
witx::TypeRef::Value(ty) => match &**ty {
|
||||
witx::Type::Builtin(builtin) => {
|
||||
let type_ = names.builtin_type(*builtin, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <#type_ as wiggle::GuestType>::read(&#location)?;
|
||||
}
|
||||
}
|
||||
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <wiggle::GuestPtr::<#pointee_type> as wiggle::GuestType>::read(&#location)?;
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("other anonymous struct members"),
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let member_writes = s.member_layout().into_iter().map(|ml| {
|
||||
let name = names.struct_member(&ml.member.name);
|
||||
let offset = ml.offset as u32;
|
||||
quote! {
|
||||
wiggle::GuestType::write(
|
||||
&location.cast::<u8>().add(#offset)?.cast(),
|
||||
val.#name,
|
||||
)?;
|
||||
}
|
||||
});
|
||||
|
||||
let (struct_lifetime, extra_derive) = if s.needs_lifetime() {
|
||||
(quote!(<'a>), quote!())
|
||||
} else {
|
||||
(quote!(), quote!(, PartialEq))
|
||||
};
|
||||
|
||||
let transparent = if s.is_transparent() {
|
||||
let member_validate = s.member_layout().into_iter().map(|ml| {
|
||||
let offset = ml.offset;
|
||||
let typename = names.type_ref(&ml.member.tref, anon_lifetime());
|
||||
quote! {
|
||||
// SAFETY: caller has validated bounds and alignment of `location`.
|
||||
// member_layout gives correctly-aligned pointers inside that area.
|
||||
#typename::validate(
|
||||
unsafe { (location as *mut u8).add(#offset) as *mut _ }
|
||||
)?;
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
unsafe impl<'a> wiggle::GuestTypeTransparent<'a> for #ident {
|
||||
#[inline]
|
||||
fn validate(location: *mut #ident) -> Result<(), wiggle::GuestError> {
|
||||
#(#member_validate)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[derive(Clone, Debug #extra_derive)]
|
||||
pub struct #ident #struct_lifetime {
|
||||
#(#member_decls),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle::GuestType<'a> for #ident #struct_lifetime {
|
||||
fn guest_size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn guest_align() -> usize {
|
||||
#align
|
||||
}
|
||||
|
||||
fn read(location: &wiggle::GuestPtr<'a, Self>) -> Result<Self, wiggle::GuestError> {
|
||||
#(#member_reads)*
|
||||
Ok(#ident { #(#member_names),* })
|
||||
}
|
||||
|
||||
fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> {
|
||||
#(#member_writes)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#transparent
|
||||
}
|
||||
}
|
||||
109
crates/wiggle/generate/src/types/union.rs
Normal file
109
crates/wiggle/generate/src/types/union.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use crate::lifetimes::LifetimeExt;
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use witx::Layout;
|
||||
|
||||
pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream {
|
||||
let ident = names.type_(name);
|
||||
let size = u.mem_size_align().size as u32;
|
||||
let align = u.mem_size_align().align as usize;
|
||||
let ulayout = u.union_layout();
|
||||
let contents_offset = ulayout.contents_offset as u32;
|
||||
|
||||
let lifetime = quote!('a);
|
||||
|
||||
let variants = u.variants.iter().map(|v| {
|
||||
let var_name = names.enum_variant(&v.name);
|
||||
if let Some(tref) = &v.tref {
|
||||
let var_type = names.type_ref(&tref, lifetime.clone());
|
||||
quote!(#var_name(#var_type))
|
||||
} else {
|
||||
quote!(#var_name)
|
||||
}
|
||||
});
|
||||
|
||||
let tagname = names.type_(&u.tag.name);
|
||||
|
||||
let read_variant = u.variants.iter().map(|v| {
|
||||
let variantname = names.enum_variant(&v.name);
|
||||
if let Some(tref) = &v.tref {
|
||||
let varianttype = names.type_ref(tref, lifetime.clone());
|
||||
quote! {
|
||||
#tagname::#variantname => {
|
||||
let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
|
||||
let variant_val = <#varianttype as wiggle::GuestType>::read(&variant_ptr.cast())?;
|
||||
Ok(#ident::#variantname(variant_val))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! { #tagname::#variantname => Ok(#ident::#variantname), }
|
||||
}
|
||||
});
|
||||
|
||||
let write_variant = u.variants.iter().map(|v| {
|
||||
let variantname = names.enum_variant(&v.name);
|
||||
let write_tag = quote! {
|
||||
location.cast().write(#tagname::#variantname)?;
|
||||
};
|
||||
if let Some(tref) = &v.tref {
|
||||
let varianttype = names.type_ref(tref, lifetime.clone());
|
||||
quote! {
|
||||
#ident::#variantname(contents) => {
|
||||
#write_tag
|
||||
let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
|
||||
<#varianttype as wiggle::GuestType>::write(&variant_ptr.cast(), contents)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#ident::#variantname => {
|
||||
#write_tag
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let (enum_lifetime, extra_derive) = if u.needs_lifetime() {
|
||||
(quote!(<'a>), quote!())
|
||||
} else {
|
||||
(quote!(), quote!(, PartialEq))
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[derive(Clone, Debug #extra_derive)]
|
||||
pub enum #ident #enum_lifetime {
|
||||
#(#variants),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle::GuestType<'a> for #ident #enum_lifetime {
|
||||
fn guest_size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn guest_align() -> usize {
|
||||
#align
|
||||
}
|
||||
|
||||
fn read(location: &wiggle::GuestPtr<'a, Self>)
|
||||
-> Result<Self, wiggle::GuestError>
|
||||
{
|
||||
let tag = location.cast().read()?;
|
||||
match tag {
|
||||
#(#read_variant)*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self)
|
||||
-> Result<(), wiggle::GuestError>
|
||||
{
|
||||
match val {
|
||||
#(#write_variant)*
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user