1use crate::component::concurrent::EnteredHostTask;
4#[cfg(feature = "component-model-async")]
5use crate::component::concurrent::{self, Accessor, HostTask, Status, table::TableId};
6use crate::component::func::{LiftContext, LowerContext};
7use crate::component::matching::InstanceType;
8use crate::component::storage::{slice_to_storage, slice_to_storage_mut};
9use crate::component::types::ComponentFunc;
10use crate::component::{ComponentNamedList, Instance, Lift, Lower, Val};
11use crate::prelude::*;
12use crate::runtime::vm::component::{
13 ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
14};
15use crate::runtime::vm::{VMOpaqueContext, VMStore};
16use crate::store::Asyncness;
17use crate::{AsContextMut, StoreContextMut, ValRaw};
18use alloc::sync::Arc;
19use core::any::Any;
20use core::mem::{self, MaybeUninit};
21#[cfg(feature = "async")]
22use core::pin::Pin;
23use core::ptr::NonNull;
24use wasmtime_environ::component::{
25 CanonicalAbiInfo, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex,
26};
27
28pub struct HostFunc {
34 entrypoint: VMLoweringCallee,
36
37 typecheck: fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>,
44
45 func: Box<dyn Any + Send + Sync>,
51
52 asyncness: Asyncness,
55}
56
57impl core::fmt::Debug for HostFunc {
58 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59 f.debug_struct("HostFunc").finish_non_exhaustive()
60 }
61}
62
63#[cfg(feature = "component-model-async")]
64type HostFuture<R> = Pin<Box<dyn Future<Output = Result<R>> + Send>>;
65
66trait HostOutput<R> {
69 fn finish_sync(self, store: &mut dyn VMStore, host_task: EnteredHostTask) -> Result<R>;
70
71 #[cfg(feature = "component-model-async")]
72 fn finish_async<T: 'static>(
73 self,
74 store: StoreContextMut<'_, T>,
75 instance: Instance,
76 host_task: EnteredHostTask,
77 may_require_realloc: bool,
78 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
79 + Send
80 + 'static,
81 ) -> Result<u32>;
82}
83
84impl<R> HostOutput<R> for Result<R> {
85 fn finish_sync(self, _store: &mut dyn VMStore, _host_task: EnteredHostTask) -> Result<R> {
86 self
87 }
88
89 #[cfg(feature = "component-model-async")]
90 fn finish_async<T: 'static>(
91 self,
92 store: StoreContextMut<'_, T>,
93 _instance: Instance,
94 _host_task: EnteredHostTask,
95 _may_require_realloc: bool,
96 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
97 + Send
98 + 'static,
99 ) -> Result<u32> {
100 let result = self?;
101 let task = store.0.current_materialized_host_task()?;
102 lower(store, Some(result), true, task)?;
103 Ok(Status::Returned.pack(None))
104 }
105}
106
107#[cfg(feature = "component-model-async")]
108impl<R: Send + Sync + 'static> HostOutput<R> for HostFuture<R> {
109 fn finish_sync(self, store: &mut dyn VMStore, host_task: EnteredHostTask) -> Result<R> {
110 concurrent::poll_and_block(store, host_task, self)
111 }
112
113 fn finish_async<T: 'static>(
114 self,
115 store: StoreContextMut<'_, T>,
116 instance: Instance,
117 host_task: EnteredHostTask,
118 may_require_realloc: bool,
119 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
120 + Send
121 + 'static,
122 ) -> Result<u32> {
123 instance.first_poll(store, host_task, may_require_realloc, self, lower)
124 }
125}
126
127impl HostFunc {
128 fn new<T, F, P, R>(asyncness: Asyncness, func: F) -> Result<Arc<HostFunc>>
137 where
138 T: 'static,
139 R: Send + Sync + 'static,
140 F: HostFn<T, P, R> + Send + Sync + 'static,
141 {
142 Ok(try_new::<Arc<_>>(HostFunc {
143 entrypoint: F::cabi_entrypoint,
144 typecheck: F::typecheck,
145 func: try_new::<Box<_>>(func)?,
146 asyncness,
147 })?)
148 }
149
150 pub(crate) fn func_wrap<T, F, P, R>(func: F) -> Result<Arc<HostFunc>>
152 where
153 T: 'static,
154 F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
155 P: ComponentNamedList + Lift + 'static,
156 R: ComponentNamedList + Lower + 'static,
157 {
158 Self::new(Asyncness::No, StaticHostFn::<_, false>::new(func))
159 }
160
161 #[cfg(feature = "async")]
163 pub(crate) fn func_wrap_async<T, F, P, R>(func: F) -> Result<Arc<HostFunc>>
164 where
165 T: 'static,
166 F: Fn(StoreContextMut<'_, T>, P) -> Box<dyn Future<Output = Result<R>> + Send + '_>
167 + Send
168 + Sync
169 + 'static,
170 P: ComponentNamedList + Lift + 'static,
171 R: ComponentNamedList + Lower + 'static,
172 {
173 Self::new(
174 Asyncness::Yes,
175 StaticHostFn::<_, false>::new(move |store: StoreContextMut<'_, T>, params| {
176 store
177 .block_on(|store| Pin::from(func(store, params)))
178 .and_then(|r| r)
179 }),
180 )
181 }
182
183 #[cfg(feature = "component-model-async")]
185 pub(crate) fn func_wrap_concurrent<T, F, P, R>(func: F) -> Result<Arc<HostFunc>>
186 where
187 T: 'static,
188 F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
189 + Send
190 + Sync
191 + 'static,
192 P: ComponentNamedList + Lift + 'static,
193 R: ComponentNamedList + Lower + 'static,
194 {
195 let func = Arc::new(func);
196 Self::new(
197 Asyncness::Yes,
198 StaticHostFn::<_, true>::new(move |store: StoreContextMut<'_, T>, params| {
199 let func = func.clone();
200 Box::pin(store.wrap_call(move |accessor| func(accessor, params))) as HostFuture<R>
201 }),
202 )
203 }
204
205 pub(crate) fn func_new<T, F>(func: F) -> Result<Arc<HostFunc>>
207 where
208 T: 'static,
209 F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()>
210 + Send
211 + Sync
212 + 'static,
213 {
214 Self::new(
215 Asyncness::No,
216 DynamicHostFn::<_, false>::new(
217 move |store, ty, mut params_and_results, result_start| {
218 let (params, results) = params_and_results.split_at_mut(result_start);
219 func(store, ty, params, results).map(move |()| params_and_results)
220 },
221 ),
222 )
223 }
224
225 #[cfg(feature = "async")]
227 pub(crate) fn func_new_async<T, F>(func: F) -> Result<Arc<HostFunc>>
228 where
229 T: 'static,
230 F: for<'a> Fn(
231 StoreContextMut<'a, T>,
232 ComponentFunc,
233 &'a [Val],
234 &'a mut [Val],
235 ) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
236 + Send
237 + Sync
238 + 'static,
239 {
240 Self::new(
241 Asyncness::Yes,
242 DynamicHostFn::<_, false>::new(
243 move |store, ty, mut params_and_results, result_start| {
244 let (params, results) = params_and_results.split_at_mut(result_start);
245 let result = store
246 .with_blocking(|store, cx| {
247 cx.block_on(Pin::from(func(store, ty, params, results)))
248 })
249 .and_then(|r| r);
250 result.map(move |()| params_and_results)
251 },
252 ),
253 )
254 }
255
256 #[cfg(feature = "component-model-async")]
258 pub(crate) fn func_new_concurrent<T, F>(func: F) -> Result<Arc<HostFunc>>
259 where
260 T: 'static,
261 F: for<'a> Fn(
262 &'a Accessor<T>,
263 ComponentFunc,
264 &'a [Val],
265 &'a mut [Val],
266 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
267 + Send
268 + Sync
269 + 'static,
270 {
271 let func = Arc::new(func);
272 Self::new(
273 Asyncness::Yes,
274 DynamicHostFn::<_, true>::new(
275 move |store, ty, mut params_and_results, result_start| {
276 let func = func.clone();
277 Box::pin(store.wrap_call(move |accessor| {
278 Box::pin(async move {
279 let (params, results) = params_and_results.split_at_mut(result_start);
280 func(accessor, ty, params, results).await?;
281 Ok(params_and_results)
282 })
283 })) as HostFuture<Vec<Val>>
284 },
285 ),
286 )
287 }
288
289 pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
290 (self.typecheck)(ty, types)
291 }
292
293 pub fn lowering(&self) -> VMLowering {
294 let data = NonNull::from(&*self.func).cast();
295 VMLowering {
296 callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
297 data: data.into(),
298 }
299 }
300
301 pub fn asyncness(&self) -> Asyncness {
302 self.asyncness
303 }
304}
305
306enum Source<'a> {
308 Flat(&'a [ValRaw]),
310 Memory(usize),
313}
314
315enum Destination<'a> {
317 Flat(&'a mut [MaybeUninit<ValRaw>]),
319 Memory(usize),
322}
323
324trait HostFn<T, P, R>
330where
331 T: 'static,
332 R: Send + Sync + 'static,
333{
334 #[cfg(feature = "component-model-async")]
337 const RESULT_MAY_REQUIRE_REALLOC: bool = true;
338
339 type Output: HostOutput<R>;
340
341 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>;
344
345 fn run(&self, store: StoreContextMut<'_, T>, params: P) -> Self::Output;
347
348 fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, source: Source<'_>) -> Result<P>;
351
352 fn lower_result(
355 cx: &mut LowerContext<'_, T>,
356 ty: TypeFuncIndex,
357 result: R,
358 dst: Destination<'_>,
359 ) -> Result<()>;
360
361 unsafe extern "C" fn cabi_entrypoint(
381 cx: NonNull<VMOpaqueContext>,
382 data: NonNull<u8>,
383 ty: u32,
384 options: u32,
385 storage: NonNull<MaybeUninit<ValRaw>>,
386 storage_len: usize,
387 ) -> bool
388 where
389 Self: Sized,
390 {
391 let cx = unsafe { VMComponentContext::from_opaque(cx) };
392 unsafe {
393 ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
394 let mut store = store.unchecked_context_mut();
395 let ty = TypeFuncIndex::from_u32(ty);
396 let options = OptionsIndex::from_u32(options);
397 let storage = NonNull::slice_from_raw_parts(storage, storage_len).as_mut();
398 let data = data.cast::<Self>().as_ref();
399 data.entrypoint(store.as_context_mut(), instance, ty, options, storage)
400 })
401 }
402 }
403
404 fn entrypoint(
407 &self,
408 mut store: StoreContextMut<'_, T>,
409 instance: Instance,
410 ty: TypeFuncIndex,
411 options: OptionsIndex,
412 storage: &mut [MaybeUninit<ValRaw>],
413 ) -> Result<()> {
414 let vminstance = instance.id().get(store.0);
415 let async_ = vminstance.component().env_component().options[options].async_;
416
417 if async_ {
418 #[cfg(feature = "component-model-async")]
419 {
420 self.call_async_lower(store.as_context_mut(), instance, ty, options, storage)
421 }
422 #[cfg(not(feature = "component-model-async"))]
423 unreachable!(
424 "async-lowered imports should have failed validation \
425 when `component-model-async` feature disabled"
426 );
427 } else {
428 self.call_sync_lower(store.as_context_mut(), instance, ty, options, storage)
429 }
430 }
431
432 fn call_sync_lower(
440 &self,
441 mut store: StoreContextMut<'_, T>,
442 instance: Instance,
443 ty: TypeFuncIndex,
444 options: OptionsIndex,
445 storage: &mut [MaybeUninit<ValRaw>],
446 ) -> Result<()> {
447 let entered_host_task = store.0.host_task_create()?;
448
449 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance)?;
450 let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_PARAMS, storage)?;
451
452 let ret = self
453 .run(store.as_context_mut(), params)
454 .finish_sync(store.0, entered_host_task)?;
455
456 if store.0.component_trapped() {
460 bail!(crate::Trap::CannotEnterComponent);
461 }
462
463 let mut lower = LowerContext::new(store, options, instance);
464 let fty = &lower.types[ty];
465 let result_tys = &lower.types[fty.results];
466 let dst = if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
467 Destination::Flat(&mut storage[..cnt])
468 } else {
469 let ptr = unsafe { rest[0].assume_init_ref() };
473 Destination::Memory(validate_inbounds_dynamic(
474 &result_tys.abi,
475 lower.as_slice(),
476 ptr,
477 )?)
478 };
479 lower.validate_scope_exit()?;
480 let materialized_host_task = lower.store.0.current_materialized_host_task()?;
482 lower
483 .store
484 .0
485 .host_task_delete(entered_host_task, materialized_host_task)?;
486 Self::lower_result(&mut lower, ty, ret, dst)
487 }
488
489 #[cfg(feature = "component-model-async")]
495 fn call_async_lower(
496 &self,
497 store: StoreContextMut<'_, T>,
498 instance: Instance,
499 ty: TypeFuncIndex,
500 options: OptionsIndex,
501 storage: &mut [MaybeUninit<ValRaw>],
502 ) -> Result<()> {
503 use wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS;
504
505 let (component, store) = instance.component_and_store_mut(store.0);
506 let mut store = StoreContextMut(store);
507 let types = component.types();
508 let fty = &types[ty];
509 let entered_host_task = store.0.host_task_create()?;
510
511 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance)?;
514 let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_ASYNC_PARAMS, storage)?;
515
516 let retptr = if !lift.types[fty.results].types.is_empty() {
518 let lower = LowerContext::new(store.as_context_mut(), options, instance);
519 let ptr = unsafe { rest[0].assume_init_ref() };
522 let result_tys = &lower.types[fty.results];
523 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice(), ptr)?
524 } else {
525 0
530 };
531
532 let rc = self.run(store.as_context_mut(), params).finish_async(
533 store.as_context_mut(),
534 instance,
535 entered_host_task,
536 Self::RESULT_MAY_REQUIRE_REALLOC,
537 move |store, ret, immediate, materialized_host_task| {
538 let mut lower = if Self::RESULT_MAY_REQUIRE_REALLOC {
539 LowerContext::new(store, options, instance)
540 } else {
541 LowerContext::new_without_realloc(store, options, instance)
542 };
543 lower.validate_scope_exit()?;
544 if immediate {
545 lower
546 .store
547 .0
548 .host_task_delete(entered_host_task, materialized_host_task)?;
549 }
550 if let Some(result) = ret {
556 Self::lower_result(&mut lower, ty, result, Destination::Memory(retptr))?;
557 }
558 Ok(())
559 },
560 )?;
561
562 storage[0].write(ValRaw::u32(rc));
563
564 Ok(())
565 }
566
567 fn load_params<'a>(
572 &self,
573 lift: &mut LiftContext<'_>,
574 ty: TypeFuncIndex,
575 max_flat_params: usize,
576 storage: &'a [MaybeUninit<ValRaw>],
577 ) -> Result<(P, &'a [MaybeUninit<ValRaw>])> {
578 let fty = &lift.types[ty];
579 let param_tys = &lift.types[fty.params];
580 let param_flat_count = param_tys.abi.flat_count(max_flat_params);
581 let src = match param_flat_count {
582 Some(cnt) => {
583 let params = &storage[..cnt];
584 Source::Flat(unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(params) })
588 }
589 None => {
590 let ptr = unsafe { storage[0].assume_init_ref() };
594 Source::Memory(validate_inbounds_dynamic(
595 ¶m_tys.abi,
596 lift.memory(),
597 ptr,
598 )?)
599 }
600 };
601 let params = Self::lift_params(lift, ty, src)?;
602 Ok((params, &storage[param_flat_count.unwrap_or(1)..]))
603 }
604}
605
606fn typecheck_async(host_async: bool, wit_async: bool) -> Result<()> {
619 if host_async == wit_async {
620 return Ok(());
621 }
622 if wit_async {
623 bail!(
624 "type mismatch with async: this import is declared `async func` in WIT, but was \
625 satisfied with a sync-style host function (`func_new`/`func_wrap`, or \
626 `func_new_async`/`func_wrap_async` — despite the name, these implement a \
627 *sync*-WIT-typed function via blocking host code, not an `async func` import); \
628 use `func_new_concurrent`/`func_wrap_concurrent` instead"
629 );
630 } else {
631 bail!(
632 "type mismatch with async: this import's WIT type is a plain (non-`async`) \
633 function, but was satisfied with `func_new_concurrent`/`func_wrap_concurrent`, \
634 which is only for `async func`-typed imports; use `func_new`/`func_wrap` (or \
635 `func_new_async`/`func_wrap_async` for blocking host code) instead"
636 );
637 }
638}
639
640#[repr(transparent)]
643struct StaticHostFn<F, const ASYNC: bool>(F);
644
645impl<F, const ASYNC: bool> StaticHostFn<F, ASYNC> {
646 fn new<T, P, R, O>(func: F) -> Self
647 where
648 T: 'static,
649 P: ComponentNamedList + Lift + 'static,
650 R: ComponentNamedList + Lower + 'static,
651 F: Fn(StoreContextMut<'_, T>, P) -> O,
652 O: HostOutput<R>,
653 {
654 Self(func)
655 }
656}
657
658impl<T, F, P, R, O, const ASYNC: bool> HostFn<T, P, R> for StaticHostFn<F, ASYNC>
659where
660 T: 'static,
661 F: Fn(StoreContextMut<'_, T>, P) -> O,
662 O: HostOutput<R>,
663 P: ComponentNamedList + Lift + 'static,
664 R: ComponentNamedList + Lower + 'static,
665{
666 #[cfg(feature = "component-model-async")]
667 const RESULT_MAY_REQUIRE_REALLOC: bool = R::MAY_REQUIRE_REALLOC;
668
669 type Output = O;
670
671 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
672 let ty = &types.types[ty];
673 typecheck_async(ASYNC, ty.async_)?;
674 P::typecheck(&InterfaceType::Tuple(ty.params), types)
675 .context("type mismatch with parameters")?;
676 R::typecheck(&InterfaceType::Tuple(ty.results), types)
677 .context("type mismatch with results")?;
678 Ok(())
679 }
680
681 fn run(&self, store: StoreContextMut<'_, T>, params: P) -> O {
682 (self.0)(store, params)
683 }
684
685 fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, src: Source<'_>) -> Result<P> {
686 let ty = InterfaceType::Tuple(cx.types[ty].params);
687 match src {
688 Source::Flat(storage) => {
689 let storage: &P::Lower = unsafe { slice_to_storage(storage) };
694 P::linear_lift_from_flat(cx, ty, storage)
695 }
696 Source::Memory(offset) => {
697 P::linear_lift_from_memory(cx, ty, &cx.memory()[offset..][..P::SIZE32])
698 }
699 }
700 }
701
702 fn lower_result(
703 cx: &mut LowerContext<'_, T>,
704 ty: TypeFuncIndex,
705 ret: R,
706 dst: Destination<'_>,
707 ) -> Result<()> {
708 let fty = &cx.types[ty];
709 let ty = InterfaceType::Tuple(fty.results);
710 match dst {
711 Destination::Flat(storage) => {
712 let storage: &mut MaybeUninit<R::Lower> = unsafe { slice_to_storage_mut(storage) };
716 ret.linear_lower_to_flat(cx, ty, storage)
717 }
718 Destination::Memory(ptr) => ret.linear_lower_to_memory(cx, ty, ptr),
719 }
720 }
721}
722
723struct DynamicHostFn<F, const ASYNC: bool>(F);
730
731impl<F, const ASYNC: bool> DynamicHostFn<F, ASYNC> {
732 fn new<T, O>(func: F) -> Self
733 where
734 T: 'static,
735 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> O,
736 O: HostOutput<Vec<Val>>,
737 {
738 Self(func)
739 }
740}
741
742impl<T, F, O, const ASYNC: bool> HostFn<T, (ComponentFunc, Vec<Val>), Vec<Val>>
743 for DynamicHostFn<F, ASYNC>
744where
745 T: 'static,
746 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> O,
747 O: HostOutput<Vec<Val>>,
748{
749 type Output = O;
750
751 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
755 let ty = &types.types[ty];
756 typecheck_async(ASYNC, ty.async_)
757 }
758
759 fn run(&self, store: StoreContextMut<'_, T>, (ty, mut params): (ComponentFunc, Vec<Val>)) -> O {
760 let offset = params.len();
761 for _ in 0..ty.results().len() {
762 params.push(Val::Bool(false));
763 }
764 (self.0)(store, ty, params, offset)
765 }
766
767 fn lift_params(
768 cx: &mut LiftContext<'_>,
769 ty: TypeFuncIndex,
770 src: Source<'_>,
771 ) -> Result<(ComponentFunc, Vec<Val>)> {
772 let param_tys = &cx.types[cx.types[ty].params];
773 let mut params = Vec::new();
774 match src {
775 Source::Flat(storage) => {
776 let mut iter = storage.iter();
777 for ty in param_tys.types.iter() {
778 params.push(Val::lift(cx, *ty, &mut iter)?);
779 }
780 assert!(iter.next().is_none());
781 }
782 Source::Memory(mut offset) => {
783 for ty in param_tys.types.iter() {
784 let abi = cx.types.canonical_abi(ty);
785 let size = usize::try_from(abi.size32).unwrap();
786 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
787 params.push(Val::load(cx, *ty, memory)?);
788 }
789 }
790 }
791
792 Ok((ComponentFunc::from(ty, &cx.instance_type()), params))
793 }
794
795 fn lower_result(
796 cx: &mut LowerContext<'_, T>,
797 ty: TypeFuncIndex,
798 result_vals: Vec<Val>,
799 dst: Destination<'_>,
800 ) -> Result<()> {
801 let fty = &cx.types[ty];
802 let param_tys = &cx.types[fty.params];
803 let result_tys = &cx.types[fty.results];
804 let result_vals = &result_vals[param_tys.types.len()..];
805 match dst {
806 Destination::Flat(storage) => {
807 let mut dst = storage.iter_mut();
808 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
809 val.lower(cx, *ty, &mut dst)?;
810 }
811 assert!(dst.next().is_none());
812 }
813 Destination::Memory(mut ptr) => {
814 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
815 let offset = cx.types.canonical_abi(ty).next_field32_size(&mut ptr);
816 val.store(cx, *ty, offset)?;
817 }
818 }
819 }
820 Ok(())
821 }
822}
823
824pub(crate) fn validate_inbounds_dynamic(
825 abi: &CanonicalAbiInfo,
826 memory: &[u8],
827 ptr: &ValRaw,
828) -> Result<usize> {
829 let ptr = usize::try_from(ptr.get_u32())?;
831 if ptr % usize::try_from(abi.align32)? != 0 {
832 bail!("pointer not aligned");
833 }
834 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
835 Some(n) => n,
836 None => bail!("pointer size overflow"),
837 };
838 if end > memory.len() {
839 bail!("pointer out of bounds")
840 }
841 Ok(ptr)
842}