Skip to content

Commit df4cb6e

Browse files
Expose default value constructors for Wasm types in host API (#10500)
* Expose default value constructors for Wasm types in host API * Added doc comments
1 parent 0a18000 commit df4cb6e

File tree

6 files changed

+97
-112
lines changed

6 files changed

+97
-112
lines changed

crates/fuzzing/src/oracles.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,11 @@ pub fn make_api_calls(api: generators::api::ApiCalls) {
680680
let nth = nth % funcs.len();
681681
let f = &funcs[nth];
682682
let ty = f.ty(&store);
683-
if let Ok(params) = dummy::dummy_values(ty.params()) {
683+
if let Some(params) = ty
684+
.params()
685+
.map(|p| p.default_value())
686+
.collect::<Option<Vec<_>>>()
687+
{
684688
let mut results = vec![Val::I32(0); ty.results().len()];
685689
let _ = f.call(store, &params, &mut results);
686690
}
@@ -1129,17 +1133,17 @@ pub fn call_async(wasm: &[u8], config: &generators::Config, mut poll_amts: &[u32
11291133
log::info!("yielding {} times in import", poll_amt);
11301134
YieldN(poll_amt).await;
11311135
for (ret_ty, result) in ty.results().zip(results) {
1132-
*result = dummy::dummy_value(ret_ty)?;
1136+
*result = ret_ty.default_value().unwrap();
11331137
}
11341138
Ok(())
11351139
})
11361140
})
11371141
.into()
11381142
}
1139-
other_ty => match dummy::dummy_extern(&mut store, other_ty) {
1140-
Ok(item) => item,
1141-
Err(e) => {
1142-
log::warn!("couldn't create import: {}", e);
1143+
other_ty => match other_ty.default_value(&mut store) {
1144+
Some(item) => item,
1145+
None => {
1146+
log::warn!("couldn't create import for {import:?}");
11431147
return;
11441148
}
11451149
},
@@ -1187,11 +1191,11 @@ pub fn call_async(wasm: &[u8], config: &generators::Config, mut poll_amts: &[u32
11871191
let ty = func.ty(&store);
11881192
let params = ty
11891193
.params()
1190-
.map(|ty| dummy::dummy_value(ty).unwrap())
1194+
.map(|ty| ty.default_value().unwrap())
11911195
.collect::<Vec<_>>();
11921196
let mut results = ty
11931197
.results()
1194-
.map(|ty| dummy::dummy_value(ty).unwrap())
1198+
.map(|ty| ty.default_value().unwrap())
11951199
.collect::<Vec<_>>();
11961200

11971201
log::info!("invoking export {:?}", name);

crates/fuzzing/src/oracles/dummy.rs

Lines changed: 11 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,23 @@
11
//! Dummy implementations of things that a Wasm module can import.
22
3-
use anyhow::ensure;
43
use wasmtime::*;
54

65
/// Create a set of dummy functions/globals/etc for the given imports.
76
pub fn dummy_linker<T>(store: &mut Store<T>, module: &Module) -> Result<Linker<T>> {
87
let mut linker = Linker::new(store.engine());
98
linker.allow_shadowing(true);
109
for import in module.imports() {
11-
let extern_ = dummy_extern(store, import.ty())?;
10+
let extern_ = import
11+
.ty()
12+
.default_value(&mut *store)
13+
.ok_or(anyhow::anyhow!("ERROR"))?;
1214
linker
1315
.define(&store, import.module(), import.name(), extern_)
1416
.unwrap();
1517
}
1618
Ok(linker)
1719
}
1820

19-
/// Construct a dummy `Extern` from its type signature
20-
pub fn dummy_extern<T>(store: &mut Store<T>, ty: ExternType) -> Result<Extern> {
21-
Ok(match ty {
22-
ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty)?),
23-
ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)?),
24-
ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_ty)?),
25-
ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(store, mem_ty)?),
26-
ExternType::Tag(_tag_ty) => todo!(), // FIXME: #10252
27-
})
28-
}
29-
30-
/// Construct a dummy function for the given function type
31-
pub fn dummy_func<T>(store: &mut Store<T>, ty: FuncType) -> Result<Func> {
32-
let dummy_results = ty.results().map(dummy_value).collect::<Result<Vec<_>>>()?;
33-
Ok(Func::new(store, ty.clone(), move |_, _, results| {
34-
for (slot, dummy) in results.iter_mut().zip(&dummy_results) {
35-
*slot = *dummy;
36-
}
37-
Ok(())
38-
}))
39-
}
40-
41-
/// Construct a dummy value for the given value type.
42-
pub fn dummy_value(val_ty: ValType) -> Result<Val> {
43-
Ok(match val_ty {
44-
ValType::I32 => Val::I32(0),
45-
ValType::I64 => Val::I64(0),
46-
ValType::F32 => Val::F32(0),
47-
ValType::F64 => Val::F64(0),
48-
ValType::V128 => Val::V128(0.into()),
49-
ValType::Ref(r) => {
50-
ensure!(
51-
r.is_nullable(),
52-
"cannot construct a dummy value of type `{r}`"
53-
);
54-
Val::null_ref(r.heap_type())
55-
}
56-
})
57-
}
58-
59-
/// Construct a sequence of dummy values for the given types.
60-
pub fn dummy_values(val_tys: impl IntoIterator<Item = ValType>) -> Result<Vec<Val>> {
61-
val_tys.into_iter().map(dummy_value).collect()
62-
}
63-
64-
/// Construct a dummy global for the given global type.
65-
pub fn dummy_global<T>(store: &mut Store<T>, ty: GlobalType) -> Result<Global> {
66-
let val = dummy_value(ty.content().clone())?;
67-
Global::new(store, ty, val)
68-
}
69-
70-
/// Construct a dummy table for the given table type.
71-
pub fn dummy_table<T>(store: &mut Store<T>, ty: TableType) -> Result<Table> {
72-
let init_val = dummy_value(ty.element().clone().into())?;
73-
Table::new(store, ty, init_val.ref_().unwrap())
74-
}
75-
76-
/// Construct a dummy memory for the given memory type.
77-
pub fn dummy_memory<T>(store: &mut Store<T>, ty: MemoryType) -> Result<Memory> {
78-
Memory::new(store, ty)
79-
}
80-
8121
#[cfg(test)]
8222
mod tests {
8323

@@ -93,7 +33,8 @@ mod tests {
9333
#[test]
9434
fn dummy_table_import() {
9535
let mut store = store();
96-
let table = dummy_table(&mut store, TableType::new(RefType::EXTERNREF, 10, None)).unwrap();
36+
let table_type = TableType::new(RefType::EXTERNREF, 10, None);
37+
let table = table_type.default_value(&mut store).unwrap();
9738
assert_eq!(table.size(&store), 10);
9839
for i in 0..10 {
9940
assert!(table.get(&mut store, i).unwrap().unwrap_extern().is_none());
@@ -103,24 +44,25 @@ mod tests {
10344
#[test]
10445
fn dummy_global_import() {
10546
let mut store = store();
106-
let global =
107-
dummy_global(&mut store, GlobalType::new(ValType::I32, Mutability::Const)).unwrap();
47+
let global_type = GlobalType::new(ValType::I32, Mutability::Const);
48+
let global = global_type.default_value(&mut store).unwrap();
10849
assert!(global.ty(&store).content().is_i32());
10950
assert_eq!(global.ty(&store).mutability(), Mutability::Const);
11051
}
11152

11253
#[test]
11354
fn dummy_memory_import() {
11455
let mut store = store();
115-
let memory = dummy_memory(&mut store, MemoryType::new(1, None)).unwrap();
56+
let memory_type = MemoryType::new(1, None);
57+
let memory = memory_type.default_value(&mut store).unwrap();
11658
assert_eq!(memory.size(&store), 1);
11759
}
11860

11961
#[test]
12062
fn dummy_function_import() {
12163
let mut store = store();
12264
let func_ty = FuncType::new(store.engine(), vec![ValType::I32], vec![ValType::I64]);
123-
let func = dummy_func(&mut store, func_ty.clone()).unwrap();
65+
let func = func_ty.default_value(&mut store).unwrap();
12466
let actual_ty = func.ty(&store);
12567
assert!(FuncType::eq(&actual_ty, &func_ty));
12668
}

crates/wasmtime/src/runtime/linker.rs

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::store::StoreOpaque;
55
use crate::{prelude::*, IntoFunc};
66
use crate::{
77
AsContext, AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType,
8-
Instance, Module, StoreContextMut, Val, ValRaw, ValType,
8+
Instance, Module, StoreContextMut, Val, ValRaw,
99
};
1010
use alloc::sync::Arc;
1111
use core::fmt::{self, Debug};
@@ -269,48 +269,28 @@ impl<T> Linker<T> {
269269
/// # let module = Module::new(&engine, "(module (import \"unknown\" \"import\" (func)))")?;
270270
/// # let mut store = Store::new(&engine, ());
271271
/// let mut linker = Linker::new(&engine);
272-
/// linker.define_unknown_imports_as_default_values(&module)?;
272+
/// linker.define_unknown_imports_as_default_values(&mut store, &module)?;
273273
/// linker.instantiate(&mut store, &module)?;
274274
/// # Ok(())
275275
/// # }
276276
/// ```
277277
pub fn define_unknown_imports_as_default_values(
278278
&mut self,
279+
store: &mut impl AsContextMut<Data = T>,
279280
module: &Module,
280281
) -> anyhow::Result<()> {
281282
for import in module.imports() {
282283
if let Err(import_err) = self._get_by_import(&import) {
283-
if let ExternType::Func(func_ty) = import_err.ty() {
284-
let result_tys: Vec<_> = func_ty.results().collect();
285-
286-
for ty in &result_tys {
287-
if ty.as_ref().map_or(false, |r| !r.is_nullable()) {
288-
bail!("no default value exists for type `{ty}`")
289-
}
290-
}
291-
292-
self.func_new(
293-
import.module(),
294-
import.name(),
295-
func_ty,
296-
move |_caller, _args, results| {
297-
for (result, ty) in results.iter_mut().zip(&result_tys) {
298-
*result = match ty {
299-
ValType::I32 => Val::I32(0),
300-
ValType::I64 => Val::I64(0),
301-
ValType::F32 => Val::F32(0.0_f32.to_bits()),
302-
ValType::F64 => Val::F64(0.0_f64.to_bits()),
303-
ValType::V128 => Val::V128(0_u128.into()),
304-
ValType::Ref(r) => {
305-
debug_assert!(r.is_nullable());
306-
Val::null_ref(r.heap_type())
307-
}
308-
};
309-
}
310-
Ok(())
311-
},
312-
)?;
313-
}
284+
let default_extern =
285+
import_err.ty().default_value(&mut *store).ok_or_else(|| {
286+
anyhow!("no default value exists for type `{:?}`", import_err.ty())
287+
})?;
288+
self.define(
289+
store.as_context(),
290+
import.module(),
291+
import.name(),
292+
default_extern,
293+
)?;
314294
}
315295
}
316296
Ok(())

crates/wasmtime/src/runtime/types.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
use crate::prelude::*;
2+
use crate::runtime::externals::Global as RuntimeGlobal;
3+
use crate::runtime::externals::Table as RuntimeTable;
4+
use crate::runtime::Memory as RuntimeMemory;
25
use crate::{type_registry::RegisteredType, Engine};
6+
use crate::{AsContextMut, Extern, Func, Val};
37
use core::fmt::{self, Display, Write};
48
use wasmtime_environ::{
59
EngineOrModuleTypeIndex, EntityType, Global, IndexType, Limits, Memory, ModuleTypes, Table,
@@ -341,6 +345,23 @@ impl ValType {
341345
WasmValType::Ref(r) => Self::Ref(RefType::from_wasm_type(engine, r)),
342346
}
343347
}
348+
/// Construct a default value. Returns None for non-nullable Ref types, which have no default.
349+
pub fn default_value(&self) -> Option<Val> {
350+
match self {
351+
ValType::I32 => Some(Val::I32(0)),
352+
ValType::I64 => Some(Val::I64(0)),
353+
ValType::F32 => Some(Val::F32(0)),
354+
ValType::F64 => Some(Val::F64(0)),
355+
ValType::V128 => Some(Val::V128(0.into())),
356+
ValType::Ref(r) => {
357+
if r.is_nullable() {
358+
Some(Val::null_ref(r.heap_type()))
359+
} else {
360+
None
361+
}
362+
}
363+
}
364+
}
344365
}
345366

346367
/// Opaque references to data in the Wasm heap or to host data.
@@ -1227,6 +1248,16 @@ impl ExternType {
12271248
EntityType::Tag(ty) => TagType::from_wasmtime_tag(engine, ty).into(),
12281249
}
12291250
}
1251+
/// Construct a default value, if possible for the underlying type. Tags do not have a default value.
1252+
pub fn default_value(&self, store: impl AsContextMut) -> Option<Extern> {
1253+
match self {
1254+
ExternType::Func(func_ty) => func_ty.default_value(store).map(Extern::Func),
1255+
ExternType::Global(global_ty) => global_ty.default_value(store).map(Extern::Global),
1256+
ExternType::Table(table_ty) => table_ty.default_value(store).map(Extern::Table),
1257+
ExternType::Memory(mem_ty) => mem_ty.default_value(store).map(Extern::Memory),
1258+
ExternType::Tag(_) => None, // FIXME: #10252
1259+
}
1260+
}
12301261
}
12311262

12321263
impl From<FuncType> for ExternType {
@@ -2402,6 +2433,19 @@ impl FuncType {
24022433
debug_assert!(registered_type.is_func());
24032434
Self { registered_type }
24042435
}
2436+
/// Construct a func which returns results of default value, if each result type has a default value.
2437+
pub fn default_value(&self, mut store: impl AsContextMut) -> Option<Func> {
2438+
let dummy_results = self
2439+
.results()
2440+
.map(|ty| ty.default_value())
2441+
.collect::<Option<Vec<_>>>()?;
2442+
Some(Func::new(&mut store, self.clone(), move |_, _, results| {
2443+
for (slot, dummy) in results.iter_mut().zip(dummy_results.iter()) {
2444+
*slot = *dummy;
2445+
}
2446+
Ok(())
2447+
}))
2448+
}
24052449
}
24062450

24072451
// Global Types
@@ -2457,6 +2501,11 @@ impl GlobalType {
24572501
};
24582502
GlobalType::new(ty, mutability)
24592503
}
2504+
///
2505+
pub fn default_value(&self, store: impl AsContextMut) -> Option<RuntimeGlobal> {
2506+
let val = self.content().default_value()?;
2507+
RuntimeGlobal::new(store, self.clone(), val).ok()
2508+
}
24602509
}
24612510

24622511
// Tag Types
@@ -2587,6 +2636,12 @@ impl TableType {
25872636
pub(crate) fn wasmtime_table(&self) -> &Table {
25882637
&self.ty
25892638
}
2639+
///
2640+
pub fn default_value(&self, store: impl AsContextMut) -> Option<RuntimeTable> {
2641+
let val: ValType = self.element().clone().into();
2642+
let init_val = val.default_value()?.ref_()?;
2643+
RuntimeTable::new(store, self.clone(), init_val).ok()
2644+
}
25902645
}
25912646

25922647
// Memory Types
@@ -2918,6 +2973,10 @@ impl MemoryType {
29182973
pub(crate) fn wasmtime_memory(&self) -> &Memory {
29192974
&self.ty
29202975
}
2976+
///
2977+
pub fn default_value(&self, store: impl AsContextMut) -> Option<RuntimeMemory> {
2978+
RuntimeMemory::new(store, self.clone()).ok()
2979+
}
29212980
}
29222981

29232982
// Import Types

src/commands/run.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ impl RunCommand {
421421
if self.run.common.wasm.unknown_imports_default == Some(true) {
422422
match linker {
423423
CliLinker::Core(linker) => {
424-
linker.define_unknown_imports_as_default_values(module.unwrap_core())?;
424+
linker.define_unknown_imports_as_default_values(store, module.unwrap_core())?;
425425
}
426426
_ => bail!("cannot use `--default-values-unknown-imports` with components"),
427427
}

tests/all/linker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ fn test_default_value_unknown_import() -> Result<()> {
420420
let module = Module::new(store.engine(), WAT).expect("failed to create module");
421421
let mut linker = Linker::new(store.engine());
422422

423-
linker.define_unknown_imports_as_default_values(&module)?;
423+
linker.define_unknown_imports_as_default_values(&mut store, &module)?;
424424
let instance = linker.instantiate(&mut store, &module)?;
425425

426426
// "run" calls an import function which will not be defined, so it should

0 commit comments

Comments
 (0)