1#[cfg(feature = "gc")]
2mod enabled;
3#[cfg(feature = "gc")]
4pub use enabled::*;
5
6#[cfg(not(feature = "gc"))]
7mod disabled;
8#[cfg(not(feature = "gc"))]
9pub use disabled::*;
10
11mod data;
12mod func_ref;
13mod gc_ref;
14mod gc_runtime;
15mod host_data;
16mod i31;
17
18pub use data::*;
19pub use func_ref::*;
20pub use gc_ref::*;
21pub use gc_runtime::*;
22pub use host_data::*;
23pub use i31::*;
24
25use crate::prelude::*;
26use crate::runtime::vm::{GcHeapAllocationIndex, VMMemoryDefinition};
27use crate::store::Asyncness;
28use core::any::Any;
29use core::mem::MaybeUninit;
30use core::{alloc::Layout, num::NonZeroU32};
31use wasmtime_environ::{GcArrayLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex};
32
33pub struct GcStore {
41 pub allocation_index: GcHeapAllocationIndex,
44
45 pub gc_heap: Box<dyn GcHeap>,
47
48 pub host_data_table: ExternRefHostDataTable,
50
51 pub func_ref_table: FuncRefTable,
53
54 pub last_post_gc_allocated_bytes: Option<usize>,
58
59 #[cfg(gc_zeal)]
64 gc_zeal_alloc_counter: Option<NonZeroU32>,
65
66 #[cfg(gc_zeal)]
68 gc_zeal_alloc_counter_init: Option<NonZeroU32>,
69}
70
71impl GcStore {
72 pub fn new(
74 allocation_index: GcHeapAllocationIndex,
75 gc_heap: Box<dyn GcHeap>,
76 gc_zeal_alloc_counter: Option<NonZeroU32>,
77 ) -> Self {
78 let host_data_table = ExternRefHostDataTable::default();
79 let func_ref_table = FuncRefTable::default();
80
81 let _ = &gc_zeal_alloc_counter;
82
83 Self {
84 allocation_index,
85 gc_heap,
86 host_data_table,
87 func_ref_table,
88 last_post_gc_allocated_bytes: None,
89 #[cfg(gc_zeal)]
90 gc_zeal_alloc_counter,
91 #[cfg(gc_zeal)]
92 gc_zeal_alloc_counter_init: gc_zeal_alloc_counter,
93 }
94 }
95
96 pub fn vmmemory_definition(&self) -> VMMemoryDefinition {
98 self.gc_heap.vmmemory()
99 }
100
101 pub fn gc_heap_capacity(&self) -> usize {
103 self.gc_heap.heap_slice().len()
104 }
105
106 pub async fn gc(
108 &mut self,
109 asyncness: Asyncness,
110 roots: GcRootsIter<'_>,
111 yield_fn: impl AsyncFn(),
112 ) -> Result<()> {
113 let collection = self.gc_heap.gc(roots, &mut self.host_data_table);
114 collect_async(collection, asyncness, yield_fn).await?;
115 self.last_post_gc_allocated_bytes = Some({
116 let size = self.gc_heap.allocated_bytes();
117 log::trace!("After collection, GC heap's allocated bytes = {size:#x} bytes");
118 size
119 });
120 Ok(())
121 }
122
123 pub fn kind(&self, gc_ref: &VMGcRef) -> Result<VMGcKind> {
125 debug_assert!(!gc_ref.is_i31());
126 Ok(self.header(gc_ref)?.kind())
127 }
128
129 pub fn header(&self, gc_ref: &VMGcRef) -> Result<&VMGcHeader> {
131 debug_assert!(!gc_ref.is_i31());
132 self.gc_heap.header(gc_ref)
133 }
134
135 pub fn clone_gc_ref(&mut self, gc_ref: &VMGcRef) -> VMGcRef {
137 if gc_ref.is_i31() {
138 gc_ref.copy_i31()
139 } else {
140 self.gc_heap.clone_gc_ref(gc_ref)
141 }
142 }
143
144 pub fn init_gc_ref(
147 &mut self,
148 destination: &mut MaybeUninit<Option<VMGcRef>>,
149 source: Option<&VMGcRef>,
150 ) -> Result<()> {
151 let destination = destination.write(None);
154 self.write_gc_ref(destination, source)
155 }
156
157 pub(crate) fn needs_init_barrier(gc_ref: Option<&VMGcRef>) -> bool {
160 assert!(cfg!(feature = "gc") || gc_ref.is_none());
161 gc_ref.is_some_and(|r| !r.is_i31())
162 }
163
164 pub(crate) fn needs_write_barrier(
167 dest: &mut Option<VMGcRef>,
168 gc_ref: Option<&VMGcRef>,
169 ) -> bool {
170 assert!(cfg!(feature = "gc") || gc_ref.is_none());
171 assert!(cfg!(feature = "gc") || dest.is_none());
172 dest.as_ref().is_some_and(|r| !r.is_i31()) || gc_ref.is_some_and(|r| !r.is_i31())
173 }
174
175 pub(crate) fn write_gc_ref_optional_store(
183 store: Option<&mut Self>,
184 dest: &mut Option<VMGcRef>,
185 gc_ref: Option<&VMGcRef>,
186 ) -> Result<()> {
187 if Self::needs_write_barrier(dest, gc_ref) {
188 store.unwrap().write_gc_ref(dest, gc_ref)
189 } else {
190 *dest = gc_ref.map(|r| r.copy_i31());
191 Ok(())
192 }
193 }
194
195 pub fn write_gc_ref(
198 &mut self,
199 destination: &mut Option<VMGcRef>,
200 source: Option<&VMGcRef>,
201 ) -> Result<()> {
202 if Self::needs_write_barrier(destination, source) {
206 self.gc_heap
207 .write_gc_ref(&mut self.host_data_table, destination, source)?;
208 } else {
209 *destination = source.map(|s| s.copy_i31());
210 }
211 Ok(())
212 }
213
214 pub fn drop_gc_ref(&mut self, gc_ref: VMGcRef) {
216 if !gc_ref.is_i31() {
217 self.gc_heap.drop_gc_ref(&mut self.host_data_table, gc_ref);
218 }
219 }
220
221 #[must_use]
226 pub fn expose_gc_ref_to_wasm(&mut self, gc_ref: VMGcRef) -> Result<NonZeroU32> {
227 let raw = gc_ref.as_raw_non_zero_u32();
228 if !gc_ref.is_i31() {
229 log::trace!("exposing GC ref to Wasm: {gc_ref:p}");
230 self.gc_heap.expose_gc_ref_to_wasm(gc_ref)?;
231 }
232 Ok(raw)
233 }
234
235 pub fn alloc_externref(
247 &mut self,
248 value: Box<dyn Any + Send + Sync>,
249 ) -> Result<Result<VMExternRef, (Box<dyn Any + Send + Sync>, u64)>> {
250 let host_data_id = self.host_data_table.alloc(value);
251 match self.gc_heap.alloc_externref(host_data_id)? {
252 Ok(x) => Ok(Ok(x)),
253 Err(n) => Ok(Err((self.host_data_table.dealloc(host_data_id)?, n))),
254 }
255 }
256
257 pub fn externref_host_data(&self, externref: &VMExternRef) -> Result<&(dyn Any + Send + Sync)> {
263 let host_data_id = self.gc_heap.externref_host_data(externref)?;
264 self.host_data_table.get(host_data_id)
265 }
266
267 pub fn externref_host_data_mut(
273 &mut self,
274 externref: &VMExternRef,
275 ) -> Result<&mut (dyn Any + Send + Sync)> {
276 let host_data_id = self.gc_heap.externref_host_data(externref)?;
277 self.host_data_table.get_mut(host_data_id)
278 }
279
280 pub fn alloc_raw(
282 &mut self,
283 header: VMGcHeader,
284 layout: Layout,
285 ) -> Result<Result<VMGcRef, u64>> {
286 #[cfg(gc_zeal)]
289 if let Some(counter) = self.gc_zeal_alloc_counter.take() {
290 match NonZeroU32::new(counter.get() - 1) {
291 Some(c) => self.gc_zeal_alloc_counter = Some(c),
292 None => {
293 log::trace!("gc_zeal: allocation counter reached zero, forcing GC");
294 self.gc_zeal_alloc_counter = self.gc_zeal_alloc_counter_init;
295 return Ok(Err(0));
296 }
297 }
298 }
299
300 self.gc_heap.alloc_raw(header, layout)
301 }
302
303 pub fn ensure_trace_info(&mut self, ty: VMSharedTypeIndex) {
305 self.gc_heap.ensure_trace_info(ty)
306 }
307
308 pub fn alloc_uninit_struct(
315 &mut self,
316 ty: VMSharedTypeIndex,
317 layout: &GcStructLayout,
318 ) -> Result<Result<VMStructRef, u64>> {
319 self.gc_heap
320 .alloc_uninit_struct_or_exn(ty, layout)
321 .map(|r| r.map(|r| r.into_structref_unchecked()))
322 }
323
324 pub fn dealloc_uninit_struct(&mut self, structref: VMStructRef) -> Result<()> {
326 self.gc_heap.dealloc_uninit_struct_or_exn(structref.into())
327 }
328
329 pub fn gc_object_data(&mut self, gc_ref: &VMGcRef) -> Result<&mut VMGcObjectData> {
333 self.gc_heap.gc_object_data_mut(gc_ref)
334 }
335
336 pub fn alloc_uninit_array(
343 &mut self,
344 ty: VMSharedTypeIndex,
345 len: u32,
346 layout: &GcArrayLayout,
347 ) -> Result<Result<VMArrayRef, u64>> {
348 self.gc_heap.alloc_uninit_array(ty, len, layout)
349 }
350
351 pub fn dealloc_uninit_array(&mut self, arrayref: VMArrayRef) -> Result<()> {
353 self.gc_heap.dealloc_uninit_array(arrayref)
354 }
355
356 pub fn array_len(&self, arrayref: &VMArrayRef) -> Result<u32> {
358 self.gc_heap.array_len(arrayref)
359 }
360
361 pub fn alloc_uninit_exn(
369 &mut self,
370 ty: VMSharedTypeIndex,
371 layout: &GcStructLayout,
372 ) -> Result<Result<VMExnRef, u64>> {
373 self.gc_heap
374 .alloc_uninit_struct_or_exn(ty, layout)
375 .map(|r| r.map(|r| r.into_exnref_unchecked()))
376 }
377
378 pub fn dealloc_uninit_exn(&mut self, exnref: VMExnRef) -> Result<()> {
380 self.gc_heap.dealloc_uninit_struct_or_exn(exnref.into())
381 }
382
383 #[cfg(feature = "gc")]
384 pub(crate) fn replace_gc_zeal_alloc_counter(
385 &mut self,
386 new_value: Option<NonZeroU32>,
387 ) -> Option<NonZeroU32> {
388 #[cfg(gc_zeal)]
389 return core::mem::replace(&mut self.gc_zeal_alloc_counter, new_value);
390
391 #[cfg(not(gc_zeal))]
392 {
393 let _ = new_value;
394 return None;
395 }
396 }
397}