use crate::component::*;
use crate::prelude::*;
use crate::{EntityIndex, ModuleInternedTypeIndex, PrimaryMap, WasmValType};
use cranelift_entity::packed_option::PackedOption;
use serde_derive::{Deserialize, Serialize};
pub struct ComponentTranslation {
pub component: Component,
pub trampolines: PrimaryMap<TrampolineIndex, Trampoline>,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Component {
pub import_types: PrimaryMap<ImportIndex, (String, TypeDef)>,
pub imports: PrimaryMap<RuntimeImportIndex, (ImportIndex, Vec<String>)>,
pub exports: NameMap<String, ExportIndex>,
pub export_items: PrimaryMap<ExportIndex, Export>,
pub initializers: Vec<GlobalInitializer>,
pub num_runtime_instances: u32,
pub num_runtime_component_instances: u32,
pub num_runtime_memories: u32,
pub num_runtime_tables: u32,
pub num_runtime_reallocs: u32,
pub num_runtime_callbacks: u32,
pub num_runtime_post_returns: u32,
pub trampolines: PrimaryMap<TrampolineIndex, ModuleInternedTypeIndex>,
pub unsafe_intrinsics: [PackedOption<ModuleInternedTypeIndex>; UnsafeIntrinsic::len() as usize],
pub num_lowerings: u32,
pub num_resources: u32,
pub num_future_tables: usize,
pub num_stream_tables: usize,
pub num_error_context_tables: usize,
pub imported_resources: PrimaryMap<ResourceIndex, RuntimeImportIndex>,
pub defined_resource_instances: PrimaryMap<DefinedResourceIndex, RuntimeComponentInstanceIndex>,
pub options: PrimaryMap<OptionsIndex, CanonicalOptions>,
}
impl Component {
pub fn defined_resource_index(&self, idx: ResourceIndex) -> Option<DefinedResourceIndex> {
let idx = idx
.as_u32()
.checked_sub(self.imported_resources.len() as u32)?;
Some(DefinedResourceIndex::from_u32(idx))
}
pub fn resource_index(&self, idx: DefinedResourceIndex) -> ResourceIndex {
ResourceIndex::from_u32(self.imported_resources.len() as u32 + idx.as_u32())
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum GlobalInitializer {
InstantiateModule(InstantiateModule, Option<RuntimeComponentInstanceIndex>),
LowerImport {
index: LoweredIndex,
import: RuntimeImportIndex,
},
ExtractMemory(ExtractMemory),
ExtractRealloc(ExtractRealloc),
ExtractCallback(ExtractCallback),
ExtractPostReturn(ExtractPostReturn),
ExtractTable(ExtractTable),
Resource(Resource),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractMemory {
pub index: RuntimeMemoryIndex,
pub export: CoreExport<MemoryIndex>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractRealloc {
pub index: RuntimeReallocIndex,
pub def: CoreDef,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractCallback {
pub index: RuntimeCallbackIndex,
pub def: CoreDef,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractPostReturn {
pub index: RuntimePostReturnIndex,
pub def: CoreDef,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractTable {
pub index: RuntimeTableIndex,
pub export: CoreExport<TableIndex>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum InstantiateModule {
Static(StaticModuleIndex, Box<[CoreDef]>),
Import(
RuntimeImportIndex,
IndexMap<String, IndexMap<String, CoreDef>>,
),
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub enum CoreDef {
Export(CoreExport<EntityIndex>),
InstanceFlags(RuntimeComponentInstanceIndex),
Trampoline(TrampolineIndex),
UnsafeIntrinsic(UnsafeIntrinsic),
TaskMayBlock,
}
impl<T> From<CoreExport<T>> for CoreDef
where
EntityIndex: From<T>,
{
fn from(export: CoreExport<T>) -> CoreDef {
CoreDef::Export(export.map_index(|i| i.into()))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct CoreExport<T> {
pub instance: RuntimeInstanceIndex,
pub item: ExportItem<T>,
}
impl<T> CoreExport<T> {
pub fn map_index<U>(self, f: impl FnOnce(T) -> U) -> CoreExport<U> {
CoreExport {
instance: self.instance,
item: match self.item {
ExportItem::Index(i) => ExportItem::Index(f(i)),
ExportItem::Name(s) => ExportItem::Name(s),
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub enum ExportItem<T> {
Index(T),
Name(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Export {
LiftedFunction {
ty: TypeFuncIndex,
func: CoreDef,
options: OptionsIndex,
},
ModuleStatic {
ty: TypeModuleIndex,
index: StaticModuleIndex,
},
ModuleImport {
ty: TypeModuleIndex,
import: RuntimeImportIndex,
},
Instance {
ty: TypeComponentInstanceIndex,
exports: NameMap<String, ExportIndex>,
},
Type(TypeDef),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct LinearMemoryOptions {
pub memory: Option<RuntimeMemoryIndex>,
pub realloc: Option<RuntimeReallocIndex>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum CanonicalOptionsDataModel {
Gc {},
LinearMemory(LinearMemoryOptions),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CanonicalOptions {
pub instance: RuntimeComponentInstanceIndex,
pub string_encoding: StringEncoding,
pub callback: Option<RuntimeCallbackIndex>,
pub post_return: Option<RuntimePostReturnIndex>,
pub async_: bool,
pub cancellable: bool,
pub core_type: ModuleInternedTypeIndex,
pub data_model: CanonicalOptionsDataModel,
}
impl CanonicalOptions {
pub fn memory(&self) -> Option<RuntimeMemoryIndex> {
match self.data_model {
CanonicalOptionsDataModel::Gc {} => None,
CanonicalOptionsDataModel::LinearMemory(opts) => opts.memory,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[expect(missing_docs, reason = "self-describing variants")]
pub enum StringEncoding {
Utf8,
Utf16,
CompactUtf16,
}
impl StringEncoding {
pub fn from_u8(val: u8) -> Option<StringEncoding> {
if val == StringEncoding::Utf8 as u8 {
return Some(StringEncoding::Utf8);
}
if val == StringEncoding::Utf16 as u8 {
return Some(StringEncoding::Utf16);
}
if val == StringEncoding::CompactUtf16 as u8 {
return Some(StringEncoding::CompactUtf16);
}
None
}
}
#[expect(missing_docs, reason = "self-describing variants")]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum Transcode {
Copy(FixedEncoding),
Latin1ToUtf16,
Latin1ToUtf8,
Utf16ToCompactProbablyUtf16,
Utf16ToCompactUtf16,
Utf16ToLatin1,
Utf16ToUtf8,
Utf8ToCompactUtf16,
Utf8ToLatin1,
Utf8ToUtf16,
}
impl Transcode {
pub fn symbol_fragment(&self) -> &'static str {
match self {
Transcode::Copy(x) => match x {
FixedEncoding::Utf8 => "copy_utf8",
FixedEncoding::Utf16 => "copy_utf16",
FixedEncoding::Latin1 => "copy_latin1",
},
Transcode::Latin1ToUtf16 => "latin1_to_utf16",
Transcode::Latin1ToUtf8 => "latin1_to_utf8",
Transcode::Utf16ToCompactProbablyUtf16 => "utf16_to_compact_probably_utf16",
Transcode::Utf16ToCompactUtf16 => "utf16_to_compact_utf16",
Transcode::Utf16ToLatin1 => "utf16_to_latin1",
Transcode::Utf16ToUtf8 => "utf16_to_utf8",
Transcode::Utf8ToCompactUtf16 => "utf8_to_compact_utf16",
Transcode::Utf8ToLatin1 => "utf8_to_latin1",
Transcode::Utf8ToUtf16 => "utf8_to_utf16",
}
}
pub fn desc(&self) -> &'static str {
match self {
Transcode::Copy(FixedEncoding::Utf8) => "utf8-to-utf8",
Transcode::Copy(FixedEncoding::Utf16) => "utf16-to-utf16",
Transcode::Copy(FixedEncoding::Latin1) => "latin1-to-latin1",
Transcode::Latin1ToUtf16 => "latin1-to-utf16",
Transcode::Latin1ToUtf8 => "latin1-to-utf8",
Transcode::Utf16ToCompactProbablyUtf16 => "utf16-to-compact-probably-utf16",
Transcode::Utf16ToCompactUtf16 => "utf16-to-compact-utf16",
Transcode::Utf16ToLatin1 => "utf16-to-latin1",
Transcode::Utf16ToUtf8 => "utf16-to-utf8",
Transcode::Utf8ToCompactUtf16 => "utf8-to-compact-utf16",
Transcode::Utf8ToLatin1 => "utf8-to-latin1",
Transcode::Utf8ToUtf16 => "utf8-to-utf16",
}
}
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[expect(missing_docs, reason = "self-describing variants")]
pub enum FixedEncoding {
Utf8,
Utf16,
Latin1,
}
impl FixedEncoding {
pub fn width(&self) -> u8 {
match self {
FixedEncoding::Utf8 => 1,
FixedEncoding::Utf16 => 2,
FixedEncoding::Latin1 => 1,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Resource {
pub index: DefinedResourceIndex,
pub rep: WasmValType,
pub dtor: Option<CoreDef>,
pub instance: RuntimeComponentInstanceIndex,
}
#[derive(Debug)]
pub enum Trampoline {
LowerImport {
index: LoweredIndex,
lower_ty: TypeFuncIndex,
options: OptionsIndex,
},
Transcoder {
op: Transcode,
from: RuntimeMemoryIndex,
from64: bool,
to: RuntimeMemoryIndex,
to64: bool,
},
ResourceNew {
instance: RuntimeComponentInstanceIndex,
ty: TypeResourceTableIndex,
},
ResourceRep {
instance: RuntimeComponentInstanceIndex,
ty: TypeResourceTableIndex,
},
ResourceDrop {
instance: RuntimeComponentInstanceIndex,
ty: TypeResourceTableIndex,
},
BackpressureInc {
instance: RuntimeComponentInstanceIndex,
},
BackpressureDec {
instance: RuntimeComponentInstanceIndex,
},
TaskReturn {
instance: RuntimeComponentInstanceIndex,
results: TypeTupleIndex,
options: OptionsIndex,
},
TaskCancel {
instance: RuntimeComponentInstanceIndex,
},
WaitableSetNew {
instance: RuntimeComponentInstanceIndex,
},
WaitableSetWait {
instance: RuntimeComponentInstanceIndex,
options: OptionsIndex,
},
WaitableSetPoll {
instance: RuntimeComponentInstanceIndex,
options: OptionsIndex,
},
WaitableSetDrop {
instance: RuntimeComponentInstanceIndex,
},
WaitableJoin {
instance: RuntimeComponentInstanceIndex,
},
ThreadYield {
instance: RuntimeComponentInstanceIndex,
cancellable: bool,
},
SubtaskDrop {
instance: RuntimeComponentInstanceIndex,
},
SubtaskCancel {
instance: RuntimeComponentInstanceIndex,
async_: bool,
},
StreamNew {
instance: RuntimeComponentInstanceIndex,
ty: TypeStreamTableIndex,
},
StreamRead {
instance: RuntimeComponentInstanceIndex,
ty: TypeStreamTableIndex,
options: OptionsIndex,
},
StreamWrite {
instance: RuntimeComponentInstanceIndex,
ty: TypeStreamTableIndex,
options: OptionsIndex,
},
StreamCancelRead {
instance: RuntimeComponentInstanceIndex,
ty: TypeStreamTableIndex,
async_: bool,
},
StreamCancelWrite {
instance: RuntimeComponentInstanceIndex,
ty: TypeStreamTableIndex,
async_: bool,
},
StreamDropReadable {
instance: RuntimeComponentInstanceIndex,
ty: TypeStreamTableIndex,
},
StreamDropWritable {
instance: RuntimeComponentInstanceIndex,
ty: TypeStreamTableIndex,
},
FutureNew {
instance: RuntimeComponentInstanceIndex,
ty: TypeFutureTableIndex,
},
FutureRead {
instance: RuntimeComponentInstanceIndex,
ty: TypeFutureTableIndex,
options: OptionsIndex,
},
FutureWrite {
instance: RuntimeComponentInstanceIndex,
ty: TypeFutureTableIndex,
options: OptionsIndex,
},
FutureCancelRead {
instance: RuntimeComponentInstanceIndex,
ty: TypeFutureTableIndex,
async_: bool,
},
FutureCancelWrite {
instance: RuntimeComponentInstanceIndex,
ty: TypeFutureTableIndex,
async_: bool,
},
FutureDropReadable {
instance: RuntimeComponentInstanceIndex,
ty: TypeFutureTableIndex,
},
FutureDropWritable {
instance: RuntimeComponentInstanceIndex,
ty: TypeFutureTableIndex,
},
ErrorContextNew {
instance: RuntimeComponentInstanceIndex,
ty: TypeComponentLocalErrorContextTableIndex,
options: OptionsIndex,
},
ErrorContextDebugMessage {
instance: RuntimeComponentInstanceIndex,
ty: TypeComponentLocalErrorContextTableIndex,
options: OptionsIndex,
},
ErrorContextDrop {
instance: RuntimeComponentInstanceIndex,
ty: TypeComponentLocalErrorContextTableIndex,
},
ResourceTransferOwn,
ResourceTransferBorrow,
ResourceEnterCall,
ResourceExitCall,
PrepareCall {
memory: Option<RuntimeMemoryIndex>,
},
SyncStartCall {
callback: Option<RuntimeCallbackIndex>,
},
AsyncStartCall {
callback: Option<RuntimeCallbackIndex>,
post_return: Option<RuntimePostReturnIndex>,
},
FutureTransfer,
StreamTransfer,
ErrorContextTransfer,
Trap,
EnterSyncCall,
ExitSyncCall,
ContextGet {
instance: RuntimeComponentInstanceIndex,
slot: u32,
},
ContextSet {
instance: RuntimeComponentInstanceIndex,
slot: u32,
},
ThreadIndex,
ThreadNewIndirect {
instance: RuntimeComponentInstanceIndex,
start_func_ty_idx: ComponentTypeIndex,
start_func_table_idx: RuntimeTableIndex,
},
ThreadSwitchTo {
instance: RuntimeComponentInstanceIndex,
cancellable: bool,
},
ThreadSuspend {
instance: RuntimeComponentInstanceIndex,
cancellable: bool,
},
ThreadResumeLater {
instance: RuntimeComponentInstanceIndex,
},
ThreadYieldTo {
instance: RuntimeComponentInstanceIndex,
cancellable: bool,
},
}
impl Trampoline {
pub fn symbol_name(&self) -> String {
use Trampoline::*;
match self {
LowerImport { index, .. } => {
format!("component-lower-import[{}]", index.as_u32())
}
Transcoder {
op, from64, to64, ..
} => {
let op = op.symbol_fragment();
let from = if *from64 { "64" } else { "32" };
let to = if *to64 { "64" } else { "32" };
format!("component-transcode-{op}-m{from}-m{to}")
}
ResourceNew { ty, .. } => format!("component-resource-new[{}]", ty.as_u32()),
ResourceRep { ty, .. } => format!("component-resource-rep[{}]", ty.as_u32()),
ResourceDrop { ty, .. } => format!("component-resource-drop[{}]", ty.as_u32()),
BackpressureInc { .. } => format!("backpressure-inc"),
BackpressureDec { .. } => format!("backpressure-dec"),
TaskReturn { .. } => format!("task-return"),
TaskCancel { .. } => format!("task-cancel"),
WaitableSetNew { .. } => format!("waitable-set-new"),
WaitableSetWait { .. } => format!("waitable-set-wait"),
WaitableSetPoll { .. } => format!("waitable-set-poll"),
WaitableSetDrop { .. } => format!("waitable-set-drop"),
WaitableJoin { .. } => format!("waitable-join"),
ThreadYield { .. } => format!("thread-yield"),
SubtaskDrop { .. } => format!("subtask-drop"),
SubtaskCancel { .. } => format!("subtask-cancel"),
StreamNew { .. } => format!("stream-new"),
StreamRead { .. } => format!("stream-read"),
StreamWrite { .. } => format!("stream-write"),
StreamCancelRead { .. } => format!("stream-cancel-read"),
StreamCancelWrite { .. } => format!("stream-cancel-write"),
StreamDropReadable { .. } => format!("stream-drop-readable"),
StreamDropWritable { .. } => format!("stream-drop-writable"),
FutureNew { .. } => format!("future-new"),
FutureRead { .. } => format!("future-read"),
FutureWrite { .. } => format!("future-write"),
FutureCancelRead { .. } => format!("future-cancel-read"),
FutureCancelWrite { .. } => format!("future-cancel-write"),
FutureDropReadable { .. } => format!("future-drop-readable"),
FutureDropWritable { .. } => format!("future-drop-writable"),
ErrorContextNew { .. } => format!("error-context-new"),
ErrorContextDebugMessage { .. } => format!("error-context-debug-message"),
ErrorContextDrop { .. } => format!("error-context-drop"),
ResourceTransferOwn => format!("component-resource-transfer-own"),
ResourceTransferBorrow => format!("component-resource-transfer-borrow"),
ResourceEnterCall => format!("component-resource-enter-call"),
ResourceExitCall => format!("component-resource-exit-call"),
PrepareCall { .. } => format!("component-prepare-call"),
SyncStartCall { .. } => format!("component-sync-start-call"),
AsyncStartCall { .. } => format!("component-async-start-call"),
FutureTransfer => format!("future-transfer"),
StreamTransfer => format!("stream-transfer"),
ErrorContextTransfer => format!("error-context-transfer"),
Trap => format!("trap"),
EnterSyncCall => format!("enter-sync-call"),
ExitSyncCall => format!("exit-sync-call"),
ContextGet { .. } => format!("context-get"),
ContextSet { .. } => format!("context-set"),
ThreadIndex => format!("thread-index"),
ThreadNewIndirect { .. } => format!("thread-new-indirect"),
ThreadSwitchTo { .. } => format!("thread-switch-to"),
ThreadSuspend { .. } => format!("thread-suspend"),
ThreadResumeLater { .. } => format!("thread-resume-later"),
ThreadYieldTo { .. } => format!("thread-yield-to"),
}
}
}