use crate::Abi;
use crate::component::dfg::AbstractInstantiations;
use crate::component::*;
use crate::prelude::*;
use crate::{
DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, EngineOrModuleTypeIndex,
EntityIndex, FactInlineIntrinsic, FuncKey, KnownEntity, KnownGlobal, ModuleEnvironment,
ModuleInternedTypeIndex, ModuleTranslation, ModuleTypesBuilder, PrimaryMap, ScopeVec, TagIndex,
Tunables, TypeConvert, WasmHeapType, WasmResult, WasmValType,
};
use core::str::FromStr;
use cranelift_entity::{EntityRef, SecondaryMap};
use indexmap::IndexMap;
use std::collections::{HashMap, HashSet};
use std::mem;
use wasmparser::component_types::{
AliasableResourceId, ComponentCoreModuleTypeId, ComponentDefinedTypeId, ComponentEntityType,
ComponentFuncTypeId, ComponentInstanceTypeId, ComponentValType,
};
use wasmparser::types::Types;
use wasmparser::{Chunk, ComponentExternName, Encoding, Parser, Payload, Validator};
mod adapt;
pub use self::adapt::*;
mod inline;
pub struct Translator<'a, 'data> {
result: Translation<'data>,
parser: Parser,
lexical_scopes: Vec<LexicalScope<'data>>,
validator: &'a mut Validator,
types: PreInliningComponentTypes<'a>,
tunables: &'a Tunables,
scope_vec: &'data ScopeVec<u8>,
static_modules: PrimaryMap<StaticModuleIndex, ModuleTranslation<'data>>,
static_components: PrimaryMap<StaticComponentIndex, Translation<'data>>,
unsafe_intrinsics_import: Option<&'a str>,
}
struct LexicalScope<'data> {
parser: Parser,
translation: Translation<'data>,
closure_args: ClosedOverVars,
}
#[derive(Default)]
struct Translation<'data> {
initializers: Vec<LocalInitializer<'data>>,
exports: IndexMap<&'data str, (ComponentItem, wasmparser::ComponentExternName<'data>)>,
types: Option<Types>,
}
enum LocalInitializer<'data> {
Import(ComponentExternName<'data>, ComponentEntityType),
IntrinsicsImport,
Lower {
func: ComponentFuncIndex,
lower_ty: ComponentFuncTypeId,
options: LocalCanonicalOptions,
},
Lift(ComponentFuncTypeId, FuncIndex, LocalCanonicalOptions),
Resource(AliasableResourceId, WasmValType, Option<FuncIndex>),
ResourceNew(AliasableResourceId, ModuleInternedTypeIndex),
ResourceRep(AliasableResourceId, ModuleInternedTypeIndex),
ResourceDrop(AliasableResourceId, ModuleInternedTypeIndex),
BackpressureInc {
func: ModuleInternedTypeIndex,
},
BackpressureDec {
func: ModuleInternedTypeIndex,
},
TaskReturn {
result: Option<ComponentValType>,
options: LocalCanonicalOptions,
},
TaskCancel {
func: ModuleInternedTypeIndex,
},
WaitableSetNew {
func: ModuleInternedTypeIndex,
},
WaitableSetWait {
options: LocalCanonicalOptions,
},
WaitableSetPoll {
options: LocalCanonicalOptions,
},
WaitableSetDrop {
func: ModuleInternedTypeIndex,
},
WaitableJoin {
func: ModuleInternedTypeIndex,
},
SubtaskDrop {
func: ModuleInternedTypeIndex,
},
SubtaskCancel {
func: ModuleInternedTypeIndex,
async_: bool,
},
StreamNew {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
},
StreamRead {
ty: ComponentDefinedTypeId,
options: LocalCanonicalOptions,
},
StreamWrite {
ty: ComponentDefinedTypeId,
options: LocalCanonicalOptions,
},
StreamCancelRead {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
async_: bool,
},
StreamCancelWrite {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
async_: bool,
},
StreamDropReadable {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
},
StreamDropWritable {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
},
FutureNew {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
},
FutureRead {
ty: ComponentDefinedTypeId,
options: LocalCanonicalOptions,
},
FutureWrite {
ty: ComponentDefinedTypeId,
options: LocalCanonicalOptions,
},
FutureCancelRead {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
async_: bool,
},
FutureCancelWrite {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
async_: bool,
},
FutureDropReadable {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
},
FutureDropWritable {
ty: ComponentDefinedTypeId,
func: ModuleInternedTypeIndex,
},
ErrorContextNew {
options: LocalCanonicalOptions,
},
ErrorContextDebugMessage {
options: LocalCanonicalOptions,
},
ErrorContextDrop {
func: ModuleInternedTypeIndex,
},
ContextGet {
func: ModuleInternedTypeIndex,
i: u32,
},
ContextSet {
func: ModuleInternedTypeIndex,
i: u32,
},
ThreadIndex {
func: ModuleInternedTypeIndex,
},
ThreadNewIndirect {
func: ModuleInternedTypeIndex,
start_func_ty: ComponentTypeIndex,
start_func_table_index: TableIndex,
},
ThreadResumeLater {
func: ModuleInternedTypeIndex,
},
ThreadSuspend {
func: ModuleInternedTypeIndex,
cancellable: bool,
},
ThreadYield {
func: ModuleInternedTypeIndex,
cancellable: bool,
},
ThreadSuspendThenResume {
func: ModuleInternedTypeIndex,
cancellable: bool,
},
ThreadYieldThenResume {
func: ModuleInternedTypeIndex,
cancellable: bool,
},
ThreadSuspendThenPromote {
func: ModuleInternedTypeIndex,
cancellable: bool,
},
ThreadYieldThenPromote {
func: ModuleInternedTypeIndex,
cancellable: bool,
},
ModuleStatic(StaticModuleIndex, ComponentCoreModuleTypeId),
ModuleInstantiate(ModuleIndex, HashMap<&'data str, ModuleInstanceIndex>),
ModuleSynthetic(HashMap<&'data str, EntityIndex>),
ComponentStatic(StaticComponentIndex, ClosedOverVars),
ComponentInstantiate(
ComponentIndex,
HashMap<&'data str, ComponentItem>,
ComponentInstanceTypeId,
),
ComponentSynthetic(
HashMap<&'data str, (ComponentItem, wasmparser::ComponentExternName<'data>)>,
ComponentInstanceTypeId,
),
AliasExportFunc(ModuleInstanceIndex, &'data str),
AliasExportTable(ModuleInstanceIndex, &'data str),
AliasExportGlobal(ModuleInstanceIndex, &'data str),
AliasExportMemory(ModuleInstanceIndex, &'data str),
AliasExportTag(ModuleInstanceIndex, &'data str),
AliasComponentExport(ComponentInstanceIndex, &'data str),
AliasModule(ClosedOverModule),
AliasComponent(ClosedOverComponent),
Export(ComponentItem),
}
#[derive(Default)]
struct ClosedOverVars {
components: PrimaryMap<ComponentUpvarIndex, ClosedOverComponent>,
modules: PrimaryMap<ModuleUpvarIndex, ClosedOverModule>,
}
enum ClosedOverComponent {
Local(ComponentIndex),
Upvar(ComponentUpvarIndex),
}
enum ClosedOverModule {
Local(ModuleIndex),
Upvar(ModuleUpvarIndex),
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum LocalDataModel {
Gc {},
LinearMemory {
memory: Option<MemoryIndex>,
realloc: Option<FuncIndex>,
},
}
struct LocalCanonicalOptions {
string_encoding: StringEncoding,
post_return: Option<FuncIndex>,
async_: bool,
cancellable: bool,
callback: Option<FuncIndex>,
core_type: ModuleInternedTypeIndex,
data_model: LocalDataModel,
}
enum Action {
KeepGoing,
Skip(usize),
Done,
}
impl<'a, 'data> Translator<'a, 'data> {
pub fn new(
tunables: &'a Tunables,
validator: &'a mut Validator,
types: &'a mut ComponentTypesBuilder,
scope_vec: &'data ScopeVec<u8>,
) -> Self {
let mut parser = Parser::new(0);
parser.set_features(*validator.features());
Self {
result: Translation::default(),
tunables,
validator,
types: PreInliningComponentTypes::new(types),
parser,
lexical_scopes: Vec::new(),
static_components: Default::default(),
static_modules: Default::default(),
scope_vec,
unsafe_intrinsics_import: None,
}
}
pub fn expose_unsafe_intrinsics(&mut self, name: &'a str) -> &mut Self {
assert!(self.unsafe_intrinsics_import.is_none());
self.unsafe_intrinsics_import = Some(name);
self
}
pub fn translate(
mut self,
component: &'data [u8],
) -> Result<(
ComponentTranslation,
PrimaryMap<StaticModuleIndex, ModuleTranslation<'data>>,
)> {
let mut remaining = component;
loop {
let payload = match self.parser.parse(remaining, true)? {
Chunk::Parsed { payload, consumed } => {
remaining = &remaining[consumed..];
payload
}
Chunk::NeedMoreData(_) => unreachable!(),
};
match self.translate_payload(payload, component)? {
Action::KeepGoing => {}
Action::Skip(n) => remaining = &remaining[n..],
Action::Done => break,
}
}
assert!(remaining.is_empty());
assert!(self.lexical_scopes.is_empty());
let mut component = inline::run(
self.types.types_mut_for_inlining(),
&self.result,
&self.static_modules,
&self.static_components,
)?;
self.partition_adapter_modules(&mut component);
let translation =
component.finish(self.types.types_mut_for_inlining(), self.result.types_ref())?;
self.analyze_imports(&translation);
Ok((translation, self.static_modules))
}
fn analyze_imports(&mut self, translation: &ComponentTranslation) {
let mut instantiations = SecondaryMap::<StaticModuleIndex, AbstractInstantiations>::new();
let mut instances = StaticInstances::new();
for init in &translation.component.initializers {
match init {
GlobalInitializer::InstantiateModule(instantiation, _) => match instantiation {
InstantiateModule::Static(module, args) => {
instantiations[*module].join(AbstractInstantiations::One(&*args));
instances.push(Some((*module, &args[..])));
}
_ => {
instances.push(None);
}
},
_ => continue,
}
}
for item in translation.component.export_items.values() {
if let Export::ModuleStatic { index, .. } = item {
instantiations[*index].join(AbstractInstantiations::Many)
}
}
let ambiguous = ambiguous_entities(
&self.static_modules,
translation,
&instantiations,
&instances,
);
for (module, translation) in self.static_modules.iter_mut() {
for i in 0..translation.module.num_defined_globals() {
let index = DefinedGlobalIndex::new(i);
let global = translation.module.global_index(index);
if !ambiguous
.entities
.contains(&(module, EntityIndex::Global(global)))
{
translation.globals_known_to_importers.insert(index);
}
}
for i in 0..translation.module.num_defined_memories() {
let index = DefinedMemoryIndex::new(i);
let memory = translation.module.memory_index(index);
if !ambiguous
.entities
.contains(&(module, EntityIndex::Memory(memory)))
{
translation.memories_known_to_importers.insert(index);
}
}
for i in 0..translation.module.num_defined_tables() {
let index = DefinedTableIndex::new(i);
let table = translation.module.table_index(index);
if !ambiguous
.entities
.contains(&(module, EntityIndex::Table(table)))
{
translation.tables_known_to_importers.insert(index);
}
}
}
for (module, instantiations) in instantiations.iter() {
let args = match instantiations {
dfg::AbstractInstantiations::Many | dfg::AbstractInstantiations::None => continue,
dfg::AbstractInstantiations::One(args) => args,
};
for (i, arg) in args.iter().enumerate() {
macro_rules! record_known_entity {
($variant:ident, $imported:expr, $defined_index:ident, $known:ident, $wrap:expr) => {{
let Some((arg_module, EntityIndex::$variant(arg_entity))) =
unambiguous_entity(
&self.static_modules,
&instances,
&ambiguous.entities,
arg,
)
else {
continue;
};
let index = self.static_modules[arg_module]
.module
.$defined_index(arg_entity)
.expect(
"`resolve_core_export` only returns entities that their module \
defines",
);
assert!(self.static_modules[module].$known[$imported].is_none());
self.static_modules[module].$known[$imported] = Some($wrap(KnownEntity {
module: arg_module,
index,
}));
}};
}
match self.static_modules[module].module.import_index(i).unwrap() {
EntityIndex::Function(imported_func) => {
debug_assert!(
self.static_modules[module]
.module
.defined_func_index(imported_func)
.is_none()
);
let known_func = match arg {
CoreDef::InstanceFlags(_) => {
unreachable!("instance flags are not a function")
}
CoreDef::Trampoline(index) => match translation.trampolines[*index] {
Trampoline::EnterSyncCall => {
FactInlineIntrinsic::EnterSyncCall.into()
}
Trampoline::ExitSyncCall => {
FactInlineIntrinsic::ExitSyncCall.into()
}
Trampoline::Trap(trap) => FactInlineIntrinsic::Trap(trap).into(),
_ => continue,
},
CoreDef::UnsafeIntrinsic(i) => {
FuncKey::UnsafeIntrinsic(Abi::Wasm, *i).into()
}
CoreDef::Export(export) => {
let Some((arg_module, arg_entity)) =
resolve_core_export(&self.static_modules, &instances, export)
else {
continue;
};
let EntityIndex::Function(arg_func) = arg_entity else {
unreachable!("function imports must be functions")
};
let arg_module_def_func = self.static_modules[arg_module]
.module
.defined_func_index(arg_func)
.expect(
"`resolve_core_export` only returns entities that their \
module defines",
);
FuncKey::DefinedWasmFunction(arg_module, arg_module_def_func).into()
}
};
assert!(
self.static_modules[module].known_imported_functions[imported_func]
.is_none()
);
self.static_modules[module].known_imported_functions[imported_func] =
Some(known_func);
}
EntityIndex::Global(imported_global) => match component_flags(arg) {
Some(flags) => {
if ambiguous.flags.contains(&flags) {
continue;
}
assert!(
self.static_modules[module].known_imported_globals[imported_global]
.is_none()
);
self.static_modules[module].known_imported_globals[imported_global] =
Some(flags);
}
None => record_known_entity!(
Global,
imported_global,
defined_global_index,
known_imported_globals,
KnownGlobal::Defined
),
},
EntityIndex::Memory(imported_memory) => record_known_entity!(
Memory,
imported_memory,
defined_memory_index,
known_imported_memories,
core::convert::identity
),
EntityIndex::Table(imported_table) => record_known_entity!(
Table,
imported_table,
defined_table_index,
known_imported_tables,
core::convert::identity
),
EntityIndex::Tag(_) => {}
}
}
}
}
fn translate_payload(
&mut self,
payload: Payload<'data>,
component: &'data [u8],
) -> Result<Action> {
match payload {
Payload::Version {
num,
encoding,
range,
} => {
self.validator.version(num, encoding, &range)?;
match encoding {
Encoding::Component => {}
Encoding::Module => {
bail!("attempted to parse a wasm module with a component parser");
}
}
}
Payload::End(offset) => {
assert!(self.result.types.is_none());
self.result.types = Some(self.validator.end(offset)?);
let LexicalScope {
parser,
translation,
closure_args,
} = match self.lexical_scopes.pop() {
Some(frame) => frame,
None => return Ok(Action::Done),
};
self.parser = parser;
let component = mem::replace(&mut self.result, translation);
let static_idx = self.static_components.push(component);
self.result
.initializers
.push(LocalInitializer::ComponentStatic(static_idx, closure_args));
}
Payload::ComponentTypeSection(s) => {
let mut component_type_index =
self.validator.types(0).unwrap().component_type_count();
self.validator.component_type_section(&s)?;
let types = self.validator.types(0).unwrap();
for ty in s {
match ty? {
wasmparser::ComponentType::Resource { rep, dtor } => {
let rep = self.types.convert_valtype(rep)?;
let id = types
.component_any_type_at(component_type_index)
.unwrap_resource();
let dtor = dtor.map(FuncIndex::from_u32);
self.result
.initializers
.push(LocalInitializer::Resource(id, rep, dtor));
}
wasmparser::ComponentType::Defined(_)
| wasmparser::ComponentType::Func(_)
| wasmparser::ComponentType::Instance(_)
| wasmparser::ComponentType::Component(_) => {}
}
component_type_index += 1;
}
}
Payload::CoreTypeSection(s) => {
self.validator.core_type_section(&s)?;
}
Payload::ComponentImportSection(s) => {
self.validator.component_import_section(&s)?;
for import in s {
let import = import?;
let types = self.validator.types(0).unwrap();
let ty = types
.component_item_for_import(import.name.name)
.unwrap()
.ty;
if self.is_unsafe_intrinsics_import(import.name.name) {
self.check_unsafe_intrinsics_import(import.name.name, ty)?;
self.result
.initializers
.push(LocalInitializer::IntrinsicsImport);
} else {
self.result
.initializers
.push(LocalInitializer::Import(import.name, ty));
}
}
}
Payload::ComponentCanonicalSection(s) => {
let types = self.validator.types(0).unwrap();
let mut core_func_index = types.function_count();
self.validator.component_canonical_section(&s)?;
for func in s {
let init = match func? {
wasmparser::CanonicalFunction::Lift {
type_index,
core_func_index,
options,
} => {
let ty = self
.validator
.types(0)
.unwrap()
.component_any_type_at(type_index)
.unwrap_func();
let func = FuncIndex::from_u32(core_func_index);
let options = self.canonical_options(&options, core_func_index)?;
LocalInitializer::Lift(ty, func, options)
}
wasmparser::CanonicalFunction::Lower {
func_index,
options,
} => {
let lower_ty = self
.validator
.types(0)
.unwrap()
.component_function_at(func_index);
let func = ComponentFuncIndex::from_u32(func_index);
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::Lower {
func,
options,
lower_ty,
}
}
wasmparser::CanonicalFunction::ResourceNew { resource } => {
let resource = self
.validator
.types(0)
.unwrap()
.component_any_type_at(resource)
.unwrap_resource();
let ty = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ResourceNew(resource, ty)
}
wasmparser::CanonicalFunction::ResourceDrop { resource } => {
let resource = self
.validator
.types(0)
.unwrap()
.component_any_type_at(resource)
.unwrap_resource();
let ty = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ResourceDrop(resource, ty)
}
wasmparser::CanonicalFunction::ResourceRep { resource } => {
let resource = self
.validator
.types(0)
.unwrap()
.component_any_type_at(resource)
.unwrap_resource();
let ty = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ResourceRep(resource, ty)
}
wasmparser::CanonicalFunction::ThreadSpawnRef { .. }
| wasmparser::CanonicalFunction::ThreadSpawnIndirect { .. }
| wasmparser::CanonicalFunction::ThreadAvailableParallelism => {
bail!("unsupported intrinsic")
}
wasmparser::CanonicalFunction::BackpressureInc => {
let core_type = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::BackpressureInc { func: core_type }
}
wasmparser::CanonicalFunction::BackpressureDec => {
let core_type = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::BackpressureDec { func: core_type }
}
wasmparser::CanonicalFunction::TaskReturn { result, options } => {
let result = result.map(|ty| match ty {
wasmparser::ComponentValType::Primitive(ty) => {
ComponentValType::Primitive(ty)
}
wasmparser::ComponentValType::Type(ty) => ComponentValType::Type(
self.validator
.types(0)
.unwrap()
.component_defined_type_at(ty),
),
});
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::TaskReturn { result, options }
}
wasmparser::CanonicalFunction::TaskCancel => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::TaskCancel { func }
}
wasmparser::CanonicalFunction::WaitableSetNew => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::WaitableSetNew { func }
}
wasmparser::CanonicalFunction::WaitableSetWait {
cancellable,
memory,
} => {
let core_type = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::WaitableSetWait {
options: LocalCanonicalOptions {
core_type,
cancellable,
async_: false,
data_model: LocalDataModel::LinearMemory {
memory: Some(MemoryIndex::from_u32(memory)),
realloc: None,
},
post_return: None,
callback: None,
string_encoding: StringEncoding::Utf8,
},
}
}
wasmparser::CanonicalFunction::WaitableSetPoll {
cancellable,
memory,
} => {
let core_type = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::WaitableSetPoll {
options: LocalCanonicalOptions {
core_type,
async_: false,
cancellable,
data_model: LocalDataModel::LinearMemory {
memory: Some(MemoryIndex::from_u32(memory)),
realloc: None,
},
post_return: None,
callback: None,
string_encoding: StringEncoding::Utf8,
},
}
}
wasmparser::CanonicalFunction::WaitableSetDrop => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::WaitableSetDrop { func }
}
wasmparser::CanonicalFunction::WaitableJoin => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::WaitableJoin { func }
}
wasmparser::CanonicalFunction::SubtaskDrop => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::SubtaskDrop { func }
}
wasmparser::CanonicalFunction::SubtaskCancel { async_ } => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::SubtaskCancel { func, async_ }
}
wasmparser::CanonicalFunction::StreamNew { ty } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::StreamNew { ty, func }
}
wasmparser::CanonicalFunction::StreamRead { ty, options } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::StreamRead { ty, options }
}
wasmparser::CanonicalFunction::StreamWrite { ty, options } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::StreamWrite { ty, options }
}
wasmparser::CanonicalFunction::StreamCancelRead { ty, async_ } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::StreamCancelRead { ty, func, async_ }
}
wasmparser::CanonicalFunction::StreamCancelWrite { ty, async_ } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::StreamCancelWrite { ty, func, async_ }
}
wasmparser::CanonicalFunction::StreamDropReadable { ty } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::StreamDropReadable { ty, func }
}
wasmparser::CanonicalFunction::StreamDropWritable { ty } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::StreamDropWritable { ty, func }
}
wasmparser::CanonicalFunction::FutureNew { ty } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::FutureNew { ty, func }
}
wasmparser::CanonicalFunction::FutureRead { ty, options } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::FutureRead { ty, options }
}
wasmparser::CanonicalFunction::FutureWrite { ty, options } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::FutureWrite { ty, options }
}
wasmparser::CanonicalFunction::FutureCancelRead { ty, async_ } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::FutureCancelRead { ty, func, async_ }
}
wasmparser::CanonicalFunction::FutureCancelWrite { ty, async_ } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::FutureCancelWrite { ty, func, async_ }
}
wasmparser::CanonicalFunction::FutureDropReadable { ty } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::FutureDropReadable { ty, func }
}
wasmparser::CanonicalFunction::FutureDropWritable { ty } => {
let ty = self
.validator
.types(0)
.unwrap()
.component_defined_type_at(ty);
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::FutureDropWritable { ty, func }
}
wasmparser::CanonicalFunction::ErrorContextNew { options } => {
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::ErrorContextNew { options }
}
wasmparser::CanonicalFunction::ErrorContextDebugMessage { options } => {
let options = self.canonical_options(&options, core_func_index)?;
core_func_index += 1;
LocalInitializer::ErrorContextDebugMessage { options }
}
wasmparser::CanonicalFunction::ErrorContextDrop => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ErrorContextDrop { func }
}
wasmparser::CanonicalFunction::ContextGet { slot, ty } => {
if ty != wasmparser::ValType::I32 {
bail!("unsupported context.get type: {ty:?}");
}
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ContextGet { i: slot, func }
}
wasmparser::CanonicalFunction::ContextSet { slot, ty } => {
if ty != wasmparser::ValType::I32 {
bail!("unsupported context.set type: {ty:?}");
}
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ContextSet { i: slot, func }
}
wasmparser::CanonicalFunction::ThreadIndex => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadIndex { func }
}
wasmparser::CanonicalFunction::ThreadNewIndirect {
func_ty_index,
table_index,
} => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadNewIndirect {
func,
start_func_ty: ComponentTypeIndex::from_u32(func_ty_index),
start_func_table_index: TableIndex::from_u32(table_index),
}
}
wasmparser::CanonicalFunction::ThreadResumeLater => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadResumeLater { func }
}
wasmparser::CanonicalFunction::ThreadSuspend { cancellable } => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadSuspend { func, cancellable }
}
wasmparser::CanonicalFunction::ThreadYield { cancellable } => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadYield { func, cancellable }
}
wasmparser::CanonicalFunction::ThreadSuspendThenResume { cancellable } => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadSuspendThenResume { func, cancellable }
}
wasmparser::CanonicalFunction::ThreadYieldThenResume { cancellable } => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadYieldThenResume { func, cancellable }
}
wasmparser::CanonicalFunction::ThreadSuspendThenPromote { cancellable } => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadSuspendThenPromote { func, cancellable }
}
wasmparser::CanonicalFunction::ThreadYieldThenPromote { cancellable } => {
let func = self.core_func_signature(core_func_index)?;
core_func_index += 1;
LocalInitializer::ThreadYieldThenPromote { func, cancellable }
}
};
self.result.initializers.push(init);
}
}
Payload::ModuleSection {
parser,
unchecked_range,
} => {
let index = self.validator.types(0).unwrap().module_count();
self.validator.module_section(&unchecked_range)?;
let static_module_index = self.static_modules.next_key();
let mut translation = ModuleEnvironment::new(
self.tunables,
self.validator,
self.types.module_types_builder(),
static_module_index,
)
.translate(
parser,
component
.get(unchecked_range.start as usize..unchecked_range.end as usize)
.ok_or_else(|| {
format_err!(
"section range {}..{} is out of bounds (bound = {})",
unchecked_range.start,
unchecked_range.end,
component.len()
)
.context("wasm component contains an invalid module section")
})?,
)?;
translation.wasm_module_offset = unchecked_range.start;
let static_module_index2 = self.static_modules.push(translation);
assert_eq!(static_module_index, static_module_index2);
let types = self.validator.types(0).unwrap();
let ty = types.module_at(index);
self.result
.initializers
.push(LocalInitializer::ModuleStatic(static_module_index, ty));
return Ok(Action::Skip(
(unchecked_range.end - unchecked_range.start) as usize,
));
}
Payload::ComponentSection {
parser,
unchecked_range,
} => {
self.validator.component_section(&unchecked_range)?;
self.lexical_scopes.push(LexicalScope {
parser: mem::replace(&mut self.parser, parser),
translation: mem::take(&mut self.result),
closure_args: ClosedOverVars::default(),
});
}
Payload::InstanceSection(s) => {
self.validator.instance_section(&s)?;
for instance in s {
let init = match instance? {
wasmparser::Instance::Instantiate { module_index, args } => {
let index = ModuleIndex::from_u32(module_index);
self.instantiate_module(index, &args)
}
wasmparser::Instance::FromExports(exports) => {
self.instantiate_module_from_exports(&exports)
}
};
self.result.initializers.push(init);
}
}
Payload::ComponentInstanceSection(s) => {
let mut index = self.validator.types(0).unwrap().component_instance_count();
self.validator.component_instance_section(&s)?;
for instance in s {
let types = self.validator.types(0).unwrap();
let ty = types.component_instance_at(index);
let init = match instance? {
wasmparser::ComponentInstance::Instantiate {
component_index,
args,
} => {
let index = ComponentIndex::from_u32(component_index);
self.instantiate_component(index, &args, ty)?
}
wasmparser::ComponentInstance::FromExports(exports) => {
self.instantiate_component_from_exports(&exports, ty)?
}
};
self.result.initializers.push(init);
index += 1;
}
}
Payload::ComponentExportSection(s) => {
self.validator.component_export_section(&s)?;
for export in s {
let export = export?;
let item = self.kind_to_item(export.kind, export.index)?;
let prev = self
.result
.exports
.insert(export.name.name, (item, export.name));
assert!(prev.is_none());
self.result
.initializers
.push(LocalInitializer::Export(item));
}
}
Payload::ComponentStartSection { start, range } => {
self.validator.component_start_section(&start, &range)?;
unimplemented!("component start section");
}
Payload::ComponentAliasSection(s) => {
self.validator.component_alias_section(&s)?;
for alias in s {
let init = match alias? {
wasmparser::ComponentAlias::InstanceExport {
kind: _,
instance_index,
name,
} => {
let instance = ComponentInstanceIndex::from_u32(instance_index);
LocalInitializer::AliasComponentExport(instance, name)
}
wasmparser::ComponentAlias::Outer { kind, count, index } => {
self.alias_component_outer(kind, count, index);
continue;
}
wasmparser::ComponentAlias::CoreInstanceExport {
kind,
instance_index,
name,
} => {
let instance = ModuleInstanceIndex::from_u32(instance_index);
self.alias_module_instance_export(kind, instance, name)
}
};
self.result.initializers.push(init);
}
}
Payload::CustomSection { .. } => {}
other => {
self.validator.payload(&other)?;
panic!("unimplemented section {other:?}");
}
}
Ok(Action::KeepGoing)
}
fn instantiate_module(
&mut self,
module: ModuleIndex,
raw_args: &[wasmparser::InstantiationArg<'data>],
) -> LocalInitializer<'data> {
let mut args = HashMap::with_capacity(raw_args.len());
for arg in raw_args {
match arg.kind {
wasmparser::InstantiationArgKind::Instance => {
let idx = ModuleInstanceIndex::from_u32(arg.index);
args.insert(arg.name, idx);
}
}
}
LocalInitializer::ModuleInstantiate(module, args)
}
fn instantiate_module_from_exports(
&mut self,
exports: &[wasmparser::Export<'data>],
) -> LocalInitializer<'data> {
let mut map = HashMap::with_capacity(exports.len());
for export in exports {
let idx = match export.kind {
wasmparser::ExternalKind::Func | wasmparser::ExternalKind::FuncExact => {
let index = FuncIndex::from_u32(export.index);
EntityIndex::Function(index)
}
wasmparser::ExternalKind::Table => {
let index = TableIndex::from_u32(export.index);
EntityIndex::Table(index)
}
wasmparser::ExternalKind::Memory => {
let index = MemoryIndex::from_u32(export.index);
EntityIndex::Memory(index)
}
wasmparser::ExternalKind::Global => {
let index = GlobalIndex::from_u32(export.index);
EntityIndex::Global(index)
}
wasmparser::ExternalKind::Tag => {
let index = TagIndex::from_u32(export.index);
EntityIndex::Tag(index)
}
};
map.insert(export.name, idx);
}
LocalInitializer::ModuleSynthetic(map)
}
fn instantiate_component(
&mut self,
component: ComponentIndex,
raw_args: &[wasmparser::ComponentInstantiationArg<'data>],
ty: ComponentInstanceTypeId,
) -> Result<LocalInitializer<'data>> {
let mut args = HashMap::with_capacity(raw_args.len());
for arg in raw_args {
let idx = self.kind_to_item(arg.kind, arg.index)?;
args.insert(arg.name, idx);
}
Ok(LocalInitializer::ComponentInstantiate(component, args, ty))
}
fn instantiate_component_from_exports(
&mut self,
exports: &[wasmparser::ComponentExport<'data>],
ty: ComponentInstanceTypeId,
) -> Result<LocalInitializer<'data>> {
let mut map = HashMap::with_capacity(exports.len());
for export in exports {
let idx = self.kind_to_item(export.kind, export.index)?;
map.insert(export.name.name, (idx, export.name));
}
Ok(LocalInitializer::ComponentSynthetic(map, ty))
}
fn kind_to_item(
&mut self,
kind: wasmparser::ComponentExternalKind,
index: u32,
) -> Result<ComponentItem> {
Ok(match kind {
wasmparser::ComponentExternalKind::Func => {
let index = ComponentFuncIndex::from_u32(index);
ComponentItem::Func(index)
}
wasmparser::ComponentExternalKind::Module => {
let index = ModuleIndex::from_u32(index);
ComponentItem::Module(index)
}
wasmparser::ComponentExternalKind::Instance => {
let index = ComponentInstanceIndex::from_u32(index);
ComponentItem::ComponentInstance(index)
}
wasmparser::ComponentExternalKind::Component => {
let index = ComponentIndex::from_u32(index);
ComponentItem::Component(index)
}
wasmparser::ComponentExternalKind::Value => {
unimplemented!("component values");
}
wasmparser::ComponentExternalKind::Type => {
let types = self.validator.types(0).unwrap();
let ty = types.component_any_type_at(index);
ComponentItem::Type(ty)
}
})
}
fn alias_module_instance_export(
&mut self,
kind: wasmparser::ExternalKind,
instance: ModuleInstanceIndex,
name: &'data str,
) -> LocalInitializer<'data> {
match kind {
wasmparser::ExternalKind::Func | wasmparser::ExternalKind::FuncExact => {
LocalInitializer::AliasExportFunc(instance, name)
}
wasmparser::ExternalKind::Memory => LocalInitializer::AliasExportMemory(instance, name),
wasmparser::ExternalKind::Table => LocalInitializer::AliasExportTable(instance, name),
wasmparser::ExternalKind::Global => LocalInitializer::AliasExportGlobal(instance, name),
wasmparser::ExternalKind::Tag => LocalInitializer::AliasExportTag(instance, name),
}
}
fn alias_component_outer(
&mut self,
kind: wasmparser::ComponentOuterAliasKind,
count: u32,
index: u32,
) {
match kind {
wasmparser::ComponentOuterAliasKind::CoreType
| wasmparser::ComponentOuterAliasKind::Type => {}
wasmparser::ComponentOuterAliasKind::CoreModule => {
let index = ModuleIndex::from_u32(index);
let mut module = ClosedOverModule::Local(index);
let depth = self.lexical_scopes.len() - (count as usize);
for frame in self.lexical_scopes[depth..].iter_mut() {
module = ClosedOverModule::Upvar(frame.closure_args.modules.push(module));
}
self.result
.initializers
.push(LocalInitializer::AliasModule(module));
}
wasmparser::ComponentOuterAliasKind::Component => {
let index = ComponentIndex::from_u32(index);
let mut component = ClosedOverComponent::Local(index);
let depth = self.lexical_scopes.len() - (count as usize);
for frame in self.lexical_scopes[depth..].iter_mut() {
component =
ClosedOverComponent::Upvar(frame.closure_args.components.push(component));
}
self.result
.initializers
.push(LocalInitializer::AliasComponent(component));
}
}
}
fn canonical_options(
&mut self,
opts: &[wasmparser::CanonicalOption],
core_func_index: u32,
) -> WasmResult<LocalCanonicalOptions> {
let core_type = self.core_func_signature(core_func_index)?;
let mut string_encoding = StringEncoding::Utf8;
let mut post_return = None;
let mut async_ = false;
let mut callback = None;
let mut memory = None;
let mut realloc = None;
let mut gc = false;
for opt in opts {
match opt {
wasmparser::CanonicalOption::UTF8 => {
string_encoding = StringEncoding::Utf8;
}
wasmparser::CanonicalOption::UTF16 => {
string_encoding = StringEncoding::Utf16;
}
wasmparser::CanonicalOption::CompactUTF16 => {
string_encoding = StringEncoding::CompactUtf16;
}
wasmparser::CanonicalOption::Memory(idx) => {
let idx = MemoryIndex::from_u32(*idx);
memory = Some(idx);
}
wasmparser::CanonicalOption::Realloc(idx) => {
let idx = FuncIndex::from_u32(*idx);
realloc = Some(idx);
}
wasmparser::CanonicalOption::PostReturn(idx) => {
let idx = FuncIndex::from_u32(*idx);
post_return = Some(idx);
}
wasmparser::CanonicalOption::Async => async_ = true,
wasmparser::CanonicalOption::Callback(idx) => {
let idx = FuncIndex::from_u32(*idx);
callback = Some(idx);
}
wasmparser::CanonicalOption::CoreType(idx) => {
if cfg!(debug_assertions) {
let types = self.validator.types(0).unwrap();
let core_ty_id = types.core_type_at_in_component(*idx).unwrap_sub();
let interned = self
.types
.module_types_builder()
.intern_type(types, core_ty_id)?;
debug_assert_eq!(interned, core_type);
}
}
wasmparser::CanonicalOption::Gc => {
gc = true;
}
}
}
Ok(LocalCanonicalOptions {
string_encoding,
post_return,
cancellable: false,
async_,
callback,
core_type,
data_model: if gc {
LocalDataModel::Gc {}
} else {
LocalDataModel::LinearMemory { memory, realloc }
},
})
}
fn core_func_signature(&mut self, index: u32) -> WasmResult<ModuleInternedTypeIndex> {
let types = self.validator.types(0).unwrap();
let id = types.core_function_at(index);
self.types.module_types_builder().intern_type(types, id)
}
fn is_unsafe_intrinsics_import(&self, import: &str) -> bool {
self.lexical_scopes.is_empty()
&& self
.unsafe_intrinsics_import
.is_some_and(|name| import == name)
}
fn check_unsafe_intrinsics_import(&self, import: &str, ty: ComponentEntityType) -> Result<()> {
let types = &self.validator.types(0).unwrap();
let ComponentEntityType::Instance(instance_ty) = ty else {
bail!("bad unsafe intrinsics import: import `{import}` must be an instance import")
};
let instance_ty = &types[instance_ty];
ensure!(
instance_ty.defined_resources.is_empty(),
"bad unsafe intrinsics import: import `{import}` cannot define any resources"
);
ensure!(
instance_ty.explicit_resources.is_empty(),
"bad unsafe intrinsics import: import `{import}` cannot export any resources"
);
for (name, ty) in &instance_ty.exports {
let ComponentEntityType::Func(func_ty) = ty.ty else {
bail!(
"bad unsafe intrinsics import: imported instance `{import}` must \
only export functions"
)
};
let func_ty = &types[func_ty];
fn ty_eq(a: &InterfaceType, b: &wasmparser::component_types::ComponentValType) -> bool {
use wasmparser::{PrimitiveValType as P, component_types::ComponentValType as C};
match (a, b) {
(InterfaceType::U8, C::Primitive(P::U8)) => true,
(InterfaceType::U8, _) => false,
(InterfaceType::U16, C::Primitive(P::U16)) => true,
(InterfaceType::U16, _) => false,
(InterfaceType::U32, C::Primitive(P::U32)) => true,
(InterfaceType::U32, _) => false,
(InterfaceType::U64, C::Primitive(P::U64)) => true,
(InterfaceType::U64, _) => false,
(ty, _) => unreachable!("no unsafe intrinsics use {ty:?}"),
}
}
fn check_types<'a>(
expected: impl ExactSizeIterator<Item = &'a InterfaceType>,
actual: impl ExactSizeIterator<Item = &'a wasmparser::component_types::ComponentValType>,
kind: &str,
import: &str,
name: &str,
) -> Result<()> {
let expected_len = expected.len();
let actual_len = actual.len();
ensure!(
expected_len == actual_len,
"bad unsafe intrinsics import at `{import}`: function `{name}` must have \
{expected_len} {kind}, found {actual_len}"
);
for (i, (actual_ty, expected_ty)) in actual.zip(expected).enumerate() {
ensure!(
ty_eq(expected_ty, actual_ty),
"bad unsafe intrinsics import at `{import}`: {kind}[{i}] for function \
`{name}` must be `{expected_ty:?}`, found `{actual_ty:?}`"
);
}
Ok(())
}
let intrinsic = UnsafeIntrinsic::from_str(name)
.with_context(|| format!("bad unsafe intrinsics import at `{import}`"))?;
check_types(
intrinsic.component_params().iter(),
func_ty.params.iter().map(|(_name, ty)| ty),
"parameters",
&import,
&name,
)?;
check_types(
intrinsic.component_results().iter(),
func_ty.result.iter(),
"results",
&import,
&name,
)?;
}
Ok(())
}
}
impl Translation<'_> {
fn types_ref(&self) -> wasmparser::types::TypesRef<'_> {
self.types.as_ref().unwrap().as_ref()
}
}
mod pre_inlining {
use super::*;
pub struct PreInliningComponentTypes<'a> {
types: &'a mut ComponentTypesBuilder,
}
impl<'a> PreInliningComponentTypes<'a> {
pub fn new(types: &'a mut ComponentTypesBuilder) -> Self {
Self { types }
}
pub fn module_types_builder(&mut self) -> &mut ModuleTypesBuilder {
self.types.module_types_builder_mut()
}
pub fn types(&self) -> &ComponentTypesBuilder {
self.types
}
pub fn types_mut_for_inlining(&mut self) -> &mut ComponentTypesBuilder {
self.types
}
}
impl TypeConvert for PreInliningComponentTypes<'_> {
fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType {
self.types.lookup_heap_type(index)
}
fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
self.types.lookup_type_index(index)
}
}
}
use pre_inlining::PreInliningComponentTypes;
type StaticInstances<'a> =
PrimaryMap<RuntimeInstanceIndex, Option<(StaticModuleIndex, &'a [CoreDef])>>;
#[derive(Default)]
struct Ambiguous {
entities: HashSet<(StaticModuleIndex, EntityIndex)>,
flags: HashSet<KnownGlobal>,
}
fn component_flags(def: &CoreDef) -> Option<KnownGlobal> {
match def {
CoreDef::InstanceFlags(instance) => Some(KnownGlobal::ComponentInstanceFlags(*instance)),
CoreDef::Export(_) | CoreDef::Trampoline(_) | CoreDef::UnsafeIntrinsic(_) => None,
}
}
fn resolve_core_export(
static_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
instances: &StaticInstances<'_>,
export: &CoreExport<EntityIndex>,
) -> Option<(StaticModuleIndex, EntityIndex)> {
let mut instance = export.instance;
let mut item = &export.item;
loop {
let (module, args) = instances[instance]?;
let index = match item {
ExportItem::Index(index) => *index,
ExportItem::Name(_) => return None,
};
if !static_modules[module].module.is_imported(index) {
return Some((module, index));
}
let position = static_modules[module]
.module
.import_position(index)
.expect("imported entities always have an associated import initializer");
match &args[position] {
CoreDef::Export(next) => {
assert!(next.instance < instance);
instance = next.instance;
item = &next.item;
}
CoreDef::InstanceFlags(_) | CoreDef::Trampoline(_) | CoreDef::UnsafeIntrinsic(_) => {
return None;
}
}
}
}
fn unambiguous_entity(
static_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
instances: &StaticInstances<'_>,
ambiguous: &HashSet<(StaticModuleIndex, EntityIndex)>,
def: &CoreDef,
) -> Option<(StaticModuleIndex, EntityIndex)> {
let CoreDef::Export(export) = def else {
return None;
};
let entity = resolve_core_export(static_modules, instances, export)?;
if ambiguous.contains(&entity) {
return None;
}
Some(entity)
}
fn ambiguous_entities(
static_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
translation: &ComponentTranslation,
instantiations: &SecondaryMap<StaticModuleIndex, dfg::AbstractInstantiations<'_>>,
instances: &StaticInstances<'_>,
) -> Ambiguous {
let mut ambiguous = Ambiguous::default();
let mut mark = |def: &CoreDef| match def {
CoreDef::Export(export) => {
if let Some(entity) = resolve_core_export(static_modules, instances, export) {
ambiguous.entities.insert(entity);
}
}
CoreDef::InstanceFlags(_) => {
ambiguous.flags.insert(component_flags(def).unwrap());
}
CoreDef::Trampoline(_) | CoreDef::UnsafeIntrinsic(_) => {}
};
for init in &translation.component.initializers {
match init {
GlobalInitializer::InstantiateModule(instantiation, _) => match instantiation {
InstantiateModule::Static(module, args) => {
if !matches!(instantiations[*module], dfg::AbstractInstantiations::One(_)) {
for arg in args.iter() {
mark(arg);
}
}
}
InstantiateModule::Import(_, args) => {
for arg in args.values().flat_map(|args| args.values()) {
mark(arg);
}
}
},
GlobalInitializer::ExtractMemory(_)
| GlobalInitializer::ExtractTable(_)
| GlobalInitializer::ExtractRealloc(_)
| GlobalInitializer::ExtractCallback(_)
| GlobalInitializer::ExtractPostReturn(_)
| GlobalInitializer::Resource(_)
| GlobalInitializer::LowerImport { .. } => {}
}
}
ambiguous
}