Skip to content

Commit d876603

Browse files
authored
Move most of the wit-bindgen crate into generated code (#868)
* Prepare for the eventual removal of the `wit-bindgen` crate Instead of assuming that the entire crate exists start requesting individual items from the crate throughout the generator so the generated code can codegen precisely what's needed. * Start moving items from `wit-bindgen` into generated code * Move the `run_ctors_once` workaround into generate code Also make it optional * Move integer `as_*` helpers into generated code. * Move the `Resource` type and supporting traits into generated code.
1 parent a438745 commit d876603

File tree

14 files changed

+665
-466
lines changed

14 files changed

+665
-466
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/guest-rust/src/lib.rs

Lines changed: 7 additions & 290 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@
1010
1111
#![no_std]
1212

13-
extern crate alloc;
14-
15-
use alloc::boxed::Box;
16-
use core::fmt;
17-
use core::marker;
18-
use core::ops::{Deref, DerefMut};
19-
use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
20-
2113
/// Generate bindings for an input WIT document.
2214
///
2315
/// This macro is the bread-and-butter of the `wit-bindgen` crate. The macro
@@ -301,6 +293,10 @@ use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
301293
/// // for generated types. This is a niche option that is only here to
302294
/// // support the standard library itself depending on this crate one day.
303295
/// std_feature,
296+
///
297+
/// // Force a workaround to be emitted for pre-Rust-1.69.0 modules to
298+
/// // ensure that libc ctors run only once.
299+
/// run_ctors_once_workaround: true,
304300
/// });
305301
/// ```
306302
///
@@ -318,44 +314,8 @@ mod cabi_realloc;
318314

319315
#[doc(hidden)]
320316
pub mod rt {
321-
use crate::alloc::string::String;
322-
use crate::alloc::vec::Vec;
323-
324-
pub use crate::{Resource, RustResource, WasmResource};
325-
326-
/// Provide a hook for generated export functions to run static
327-
/// constructors at most once. wit-bindgen-rust generates a call to this
328-
/// function at the start of all component export functions. Importantly,
329-
/// it is not called as part of `cabi_realloc`, which is a *core* export
330-
/// func, but may not execute ctors, because the environment ctor in
331-
/// wasi-libc (before rust 1.69.0) calls an import func, which is not
332-
/// permitted by the Component Model when inside realloc.
333-
///
334-
/// We intend to remove this once rust 1.69.0 stabilizes.
335-
#[cfg(target_arch = "wasm32")]
336-
pub fn run_ctors_once() {
337-
static mut RUN: bool = false;
338-
unsafe {
339-
if !RUN {
340-
// This function is synthesized by `wasm-ld` to run all static
341-
// constructors. wasm-ld will either provide an implementation
342-
// of this symbol, or synthesize a wrapper around each
343-
// exported function to (unconditionally) run ctors. By using
344-
// this function, the linked module is opting into "manually"
345-
// running ctors.
346-
extern "C" {
347-
fn __wasm_call_ctors();
348-
}
349-
__wasm_call_ctors();
350-
RUN = true;
351-
}
352-
}
353-
}
354317

355-
use super::alloc::alloc::Layout;
356-
357-
// Re-export things from liballoc for convenient use.
358-
pub use super::alloc::{alloc, boxed, string, vec};
318+
extern crate alloc;
359319

360320
/// This function is called from generated bindings and will be deleted by
361321
/// the linker. The purpose of this function is to force a reference to the
@@ -405,6 +365,8 @@ pub mod rt {
405365
align: usize,
406366
new_len: usize,
407367
) -> *mut u8 {
368+
use self::alloc::alloc::{self, Layout};
369+
408370
let layout;
409371
let ptr = if old_len == 0 {
410372
if new_len == 0 {
@@ -432,249 +394,4 @@ pub mod rt {
432394
}
433395
return ptr;
434396
}
435-
436-
pub unsafe fn dealloc(ptr: i32, size: usize, align: usize) {
437-
if size == 0 {
438-
return;
439-
}
440-
let layout = Layout::from_size_align_unchecked(size, align);
441-
alloc::dealloc(ptr as *mut u8, layout);
442-
}
443-
444-
macro_rules! as_traits {
445-
($(($trait_:ident $func:ident $ty:ident <=> $($tys:ident)*))*) => ($(
446-
pub fn $func<T: $trait_>(t: T) -> $ty {
447-
t.$func()
448-
}
449-
450-
pub trait $trait_ {
451-
fn $func(self) -> $ty;
452-
}
453-
454-
impl<'a, T: Copy + $trait_> $trait_ for &'a T {
455-
fn $func(self) -> $ty{
456-
(*self).$func()
457-
}
458-
}
459-
460-
$(
461-
impl $trait_ for $tys {
462-
#[inline]
463-
fn $func(self) -> $ty {
464-
self as $ty
465-
}
466-
}
467-
)*
468-
469-
)*)
470-
}
471-
472-
as_traits! {
473-
(AsI64 as_i64 i64 <=> i64 u64)
474-
(AsI32 as_i32 i32 <=> i32 u32 i16 u16 i8 u8 char usize)
475-
(AsF32 as_f32 f32 <=> f32)
476-
(AsF64 as_f64 f64 <=> f64)
477-
}
478-
479-
pub unsafe fn string_lift(bytes: Vec<u8>) -> String {
480-
if cfg!(debug_assertions) {
481-
String::from_utf8(bytes).unwrap()
482-
} else {
483-
String::from_utf8_unchecked(bytes)
484-
}
485-
}
486-
487-
pub unsafe fn invalid_enum_discriminant<T>() -> T {
488-
if cfg!(debug_assertions) {
489-
panic!("invalid enum discriminant")
490-
} else {
491-
core::hint::unreachable_unchecked()
492-
}
493-
}
494-
495-
pub unsafe fn char_lift(val: u32) -> char {
496-
if cfg!(debug_assertions) {
497-
core::char::from_u32(val).unwrap()
498-
} else {
499-
core::char::from_u32_unchecked(val)
500-
}
501-
}
502-
503-
pub unsafe fn bool_lift(val: u8) -> bool {
504-
if cfg!(debug_assertions) {
505-
match val {
506-
0 => false,
507-
1 => true,
508-
_ => panic!("invalid bool discriminant"),
509-
}
510-
} else {
511-
core::mem::transmute::<u8, bool>(val)
512-
}
513-
}
514-
}
515-
516-
type RawRep<T> = Option<T>;
517-
518-
/// A type which represents a component model resource, either imported or
519-
/// exported into this component.
520-
///
521-
/// This is a low-level wrapper which handles the lifetime of the resource
522-
/// (namely this has a destructor). The `T` provided defines the component model
523-
/// intrinsics that this wrapper uses.
524-
///
525-
/// One of the chief purposes of this type is to provide `Deref` implementations
526-
/// to access the underlying data when it is owned.
527-
///
528-
/// This type is primarily used in generated code for exported and imported
529-
/// resources.
530-
#[repr(transparent)]
531-
pub struct Resource<T: WasmResource> {
532-
// NB: This would ideally be `u32` but it is not. The fact that this has
533-
// interior mutability is not exposed in the API of this type except for the
534-
// `take_handle` method which is supposed to in theory be private.
535-
//
536-
// This represents, almost all the time, a valid handle value. When it's
537-
// invalid it's stored as `u32::MAX`.
538-
handle: AtomicU32,
539-
_marker: marker::PhantomData<Box<T>>,
540-
}
541-
542-
/// A trait which all wasm resources implement, namely providing the ability to
543-
/// drop a resource.
544-
///
545-
/// This generally is implemented by generated code, not user-facing code.
546-
pub unsafe trait WasmResource {
547-
/// Invokes the `[resource-drop]...` intrinsic.
548-
unsafe fn drop(handle: u32);
549-
}
550-
551-
/// A trait which extends [`WasmResource`] used for Rust-defined resources, or
552-
/// those exported from this component.
553-
///
554-
/// This generally is implemented by generated code, not user-facing code.
555-
pub unsafe trait RustResource: WasmResource {
556-
/// Invokes the `[resource-new]...` intrinsic.
557-
unsafe fn new(rep: usize) -> u32;
558-
/// Invokes the `[resource-rep]...` intrinsic.
559-
unsafe fn rep(handle: u32) -> usize;
560-
}
561-
562-
impl<T: WasmResource> Resource<T> {
563-
#[doc(hidden)]
564-
pub unsafe fn from_handle(handle: u32) -> Self {
565-
debug_assert!(handle != u32::MAX);
566-
Self {
567-
handle: AtomicU32::new(handle),
568-
_marker: marker::PhantomData,
569-
}
570-
}
571-
572-
/// Takes ownership of the handle owned by `resource`.
573-
///
574-
/// Note that this ideally would be `into_handle` taking `Resource<T>` by
575-
/// ownership. The code generator does not enable that in all situations,
576-
/// unfortunately, so this is provided instead.
577-
///
578-
/// Also note that `take_handle` is in theory only ever called on values
579-
/// owned by a generated function. For example a generated function might
580-
/// take `Resource<T>` as an argument but then call `take_handle` on a
581-
/// reference to that argument. In that sense the dynamic nature of
582-
/// `take_handle` should only be exposed internally to generated code, not
583-
/// to user code.
584-
#[doc(hidden)]
585-
pub fn take_handle(resource: &Resource<T>) -> u32 {
586-
resource.handle.swap(u32::MAX, Relaxed)
587-
}
588-
589-
#[doc(hidden)]
590-
pub fn handle(resource: &Resource<T>) -> u32 {
591-
resource.handle.load(Relaxed)
592-
}
593-
594-
/// Creates a new Rust-defined resource from the underlying representation
595-
/// `T`.
596-
///
597-
/// This will move `T` onto the heap to create a single pointer to represent
598-
/// it which is then wrapped up in a component model resource.
599-
pub fn new(val: T) -> Resource<T>
600-
where
601-
T: RustResource,
602-
{
603-
let rep = Box::into_raw(Box::new(Some(val))) as usize;
604-
unsafe {
605-
let handle = T::new(rep);
606-
Resource::from_handle(handle)
607-
}
608-
}
609-
610-
#[doc(hidden)]
611-
pub unsafe fn dtor(rep: usize)
612-
where
613-
T: RustResource,
614-
{
615-
let _ = Box::from_raw(rep as *mut RawRep<T>);
616-
}
617-
618-
/// Takes back ownership of the object, dropping the resource handle.
619-
pub fn into_inner(resource: Self) -> T
620-
where
621-
T: RustResource,
622-
{
623-
unsafe {
624-
let rep = T::rep(resource.handle.load(Relaxed));
625-
RawRep::take(&mut *(rep as *mut RawRep<T>)).unwrap()
626-
}
627-
}
628-
629-
#[doc(hidden)]
630-
pub unsafe fn lift_borrow<'a>(rep: usize) -> &'a T
631-
where
632-
T: RustResource,
633-
{
634-
RawRep::as_ref(&*(rep as *const RawRep<T>)).unwrap()
635-
}
636-
}
637-
638-
impl<T: RustResource> Deref for Resource<T> {
639-
type Target = T;
640-
641-
fn deref(&self) -> &T {
642-
unsafe {
643-
let rep = T::rep(self.handle.load(Relaxed));
644-
RawRep::as_ref(&*(rep as *const RawRep<T>)).unwrap()
645-
}
646-
}
647-
}
648-
649-
impl<T: RustResource> DerefMut for Resource<T> {
650-
fn deref_mut(&mut self) -> &mut T {
651-
unsafe {
652-
let rep = T::rep(self.handle.load(Relaxed));
653-
RawRep::as_mut(&mut *(rep as *mut RawRep<T>)).unwrap()
654-
}
655-
}
656-
}
657-
658-
impl<T: WasmResource> fmt::Debug for Resource<T> {
659-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660-
f.debug_struct("Resource")
661-
.field("handle", &self.handle)
662-
.finish()
663-
}
664-
}
665-
666-
impl<T: WasmResource> Drop for Resource<T> {
667-
fn drop(&mut self) {
668-
unsafe {
669-
match self.handle.load(Relaxed) {
670-
// If this handle was "taken" then don't do anything in the
671-
// destructor.
672-
u32::MAX => {}
673-
674-
// ... but otherwise do actually destroy it with the imported
675-
// component model intrinsic as defined through `T`.
676-
other => T::drop(other),
677-
}
678-
}
679-
}
680397
}

crates/rust-macro/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ impl Parse for Config {
104104
}
105105
Opt::With(with) => opts.with.extend(with),
106106
Opt::TypeSectionSuffix(suffix) => {
107-
opts.type_section_suffix = Some(suffix.value())
107+
opts.type_section_suffix = Some(suffix.value());
108+
}
109+
Opt::RunCtorsOnceWorkaround(enable) => {
110+
opts.run_ctors_once_workaround = enable.value();
108111
}
109112
}
110113
}
@@ -220,6 +223,7 @@ mod kw {
220223
syn::custom_keyword!(additional_derives);
221224
syn::custom_keyword!(with);
222225
syn::custom_keyword!(type_section_suffix);
226+
syn::custom_keyword!(run_ctors_once_workaround);
223227
}
224228

225229
#[derive(Clone)]
@@ -281,6 +285,7 @@ enum Opt {
281285
AdditionalDerives(Vec<syn::Path>),
282286
With(HashMap<String, String>),
283287
TypeSectionSuffix(syn::LitStr),
288+
RunCtorsOnceWorkaround(syn::LitBool),
284289
}
285290

286291
impl Parse for Opt {
@@ -390,6 +395,10 @@ impl Parse for Opt {
390395
input.parse::<kw::type_section_suffix>()?;
391396
input.parse::<Token![:]>()?;
392397
Ok(Opt::TypeSectionSuffix(input.parse()?))
398+
} else if l.peek(kw::run_ctors_once_workaround) {
399+
input.parse::<kw::run_ctors_once_workaround>()?;
400+
input.parse::<Token![:]>()?;
401+
Ok(Opt::RunCtorsOnceWorkaround(input.parse()?))
393402
} else {
394403
Err(l.error())
395404
}

crates/rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ wit-component = { workspace = true }
2222
wasm-metadata = { workspace = true }
2323
heck = { workspace = true }
2424
clap = { workspace = true, optional = true }
25+
indexmap = { workspace = true }
2526

2627
[dev-dependencies]
2728
wit-bindgen = { path = '../guest-rust' }

0 commit comments

Comments
 (0)