///|
pub struct HotModuleContext {
subtypes : Array[SubType]
type_groups : Array[Array[SubType]]
func_type_idxs : Array[TypeIdx]
func_types : Array[FuncType]
func_type_slots : Array[TypeIdx?]
func_imports : Array[(@lib.Name, @lib.Name)?]
func_annotations : Array[Array[@lib.FuncAnnotation]]
tables : Array[TableType]
mems : Array[@lib.MemType]
globals : Array[GlobalType]
global_inits : Array[@lib.Expr?]
global_const_inits : Array[HotConstPayload?]
elems : Array[Elem]
datas : Array[Data]
tags : Array[TagType]
imported_tag_count : Int32
uses_custom_descriptors : Bool
} derive(Eq, Debug)
///|
/// Build a reusable body-validation environment from one immutable module
/// context. Function-local labels, locals and return state are intentionally
/// absent; the body validator adds them independently for each function.
pub fn hot_module_context_validation_env(
ctx : HotModuleContext,
) -> @validate.Env {
@validate.Env::new()
.with_exact_reference_inference(ctx.uses_custom_descriptors)
.with_types(ctx.subtypes)
.with_funcs_and_type_idxs(ctx.func_types, ctx.func_type_slots)
.with_tables(ctx.tables)
.with_mems(ctx.mems)
.with_globals(ctx.globals)
.with_elems(ctx.elems)
.with_datas(ctx.datas)
.with_tags(ctx.tags)
}
///|
fn hot_module_context_flatten_subtypes(
type_sec : @lib.TypeSec?,
) -> Array[SubType] {
let out : Array[SubType] = []
match type_sec {
Some(@lib.TypeSec(rec_types)) =>
for rec_type in rec_types {
match rec_type {
@lib.SingleRecType(subtype) => out.push(subtype)
@lib.GroupRecType(group) =>
for subtype in group {
out.push(subtype)
}
}
}
None => ()
}
out
}
///|
fn hot_module_context_resolve_func_types(
subtypes : Array[SubType],
func_type_idxs : Array[TypeIdx],
) -> Array[FuncType] {
let out : Array[FuncType] = []
for type_idx in func_type_idxs {
match type_idx {
@lib.TypeIdx(raw) =>
match subtypes.get(raw.reinterpret_as_int()) {
Some(subtype) =>
match subtype.get_comptype() {
@lib.FuncCompType(params, results) =>
out.push(@lib.FuncType::new(params, results))
_ =>
abort(
"hot module context function indices must resolve to function types",
)
}
None =>
abort(
"hot module context function indices must resolve within the subtype table",
)
}
@lib.RecIdx(_) =>
abort(
"hot module context function indices must not use rec-group indices",
)
}
}
out
}
///|
fn hot_module_context_func_type_slots(
func_type_idxs : Array[TypeIdx],
) -> Array[TypeIdx?] {
let out : Array[TypeIdx?] = []
for type_idx in func_type_idxs {
out.push(Some(type_idx))
}
out
}
///|
fn hot_module_context_func_annotations(
sec : @lib.FuncAnnotationSec?,
total_funcs : Int32,
) -> Array[Array[@lib.FuncAnnotation]] {
let by_func : Array[Array[@lib.FuncAnnotation]] = []
let mut idx = 0
while idx < total_funcs {
by_func.push([])
idx += 1
}
match sec {
Some(@lib.FuncAnnotationSec(entries)) =>
for entry in entries {
let @lib.FuncAnnotationAssoc(raw_idx, annotations) = entry
let slot = raw_idx.reinterpret_as_int()
if slot >= 0 && slot < by_func.length() {
by_func[slot] = annotations
}
}
None => ()
}
by_func
}
///|
fn hot_module_context_eval_const_instr(
instr : @lib.Instruction,
global_const_inits : Array[HotConstPayload?],
) -> HotConstPayload? {
match instr {
@lib.I32Const(value) => Some(HotConstPayload::i32_const(value))
@lib.I64Const(value) => Some(HotConstPayload::i64_const(value))
@lib.F32Const(value) => Some(HotConstPayload::f32_const(value))
@lib.F64Const(value) => Some(HotConstPayload::f64_const(value))
@lib.RefNull(ref_type) => Some(HotConstPayload::ref_null_const(ref_type))
@lib.StringConst(bytes) => Some(HotConstPayload::string_const(bytes))
@lib.GlobalGet(global_idx) =>
match global_idx {
@lib.GlobalIdx(raw) =>
match global_const_inits.get(raw.reinterpret_as_int()) {
Some(payload) => payload
None => None
}
}
_ => None
}
}
///|
fn hot_module_context_eval_global_const_init(
global_type : GlobalType,
init : @lib.Expr,
global_const_inits : Array[HotConstPayload?],
) -> HotConstPayload? {
let @lib.GlobalType(_, mutable) = global_type
if mutable {
return None
}
match init {
@lib.Expr([instr]) =>
hot_module_context_eval_const_instr(instr, global_const_inits)
_ => None
}
}
///|
pub fn hot_module_context_from_module(mod_ : Module) -> HotModuleContext {
let func_type_idxs : Array[TypeIdx] = []
let func_imports : Array[(@lib.Name, @lib.Name)?] = []
let tables : Array[TableType] = []
let mems : Array[@lib.MemType] = []
let globals : Array[GlobalType] = []
let global_inits : Array[@lib.Expr?] = []
let global_const_inits : Array[HotConstPayload?] = []
let elems : Array[Elem] = []
let datas : Array[Data] = []
let tags : Array[TagType] = []
match mod_.import_sec {
Some(@lib.ImportSec(imports)) =>
for import_ in imports {
match import_ {
@lib.Import(module_name, base_name, @lib.FuncExternType(type_idx)) => {
func_type_idxs.push(type_idx)
func_imports.push(Some((module_name, base_name)))
}
@lib.Import(_, _, @lib.TableExternType(table_type)) =>
tables.push(table_type)
@lib.Import(_, _, @lib.MemExternType(mem_type)) => mems.push(mem_type)
@lib.Import(_, _, @lib.GlobalExternType(global_type)) => {
globals.push(global_type)
global_inits.push(None)
global_const_inits.push(None)
}
@lib.Import(_, _, @lib.TagExternType(tag_type)) => tags.push(tag_type)
}
}
None => ()
}
let imported_tag_count = tags.length()
match mod_.func_sec {
Some(@lib.FuncSec(type_idxs)) =>
for type_idx in type_idxs {
func_type_idxs.push(type_idx)
func_imports.push(None)
}
None => ()
}
match mod_.table_sec {
Some(@lib.TableSec(defs)) =>
for table in defs {
let @lib.Table(table_type, _) = table
tables.push(table_type)
}
None => ()
}
match mod_.mem_sec {
Some(@lib.MemSec(defs)) =>
for mem_type in defs {
mems.push(mem_type)
}
None => ()
}
match mod_.global_sec {
Some(@lib.GlobalSec(defs)) =>
for global in defs {
let @lib.Global(global_type, init) = global
globals.push(global_type)
global_inits.push(Some(init))
global_const_inits.push(
hot_module_context_eval_global_const_init(
global_type, init, global_const_inits,
),
)
}
None => ()
}
match mod_.tag_sec {
Some(@lib.TagSec(defs)) =>
for tag_type in defs {
tags.push(tag_type)
}
None => ()
}
match mod_.elem_sec {
Some(@lib.ElemSec(defs)) =>
for elem in defs {
elems.push(elem)
}
None => ()
}
match mod_.data_sec {
Some(@lib.DataSec(defs)) =>
for data in defs {
datas.push(data)
}
None => ()
}
let subtypes = hot_module_context_flatten_subtypes(mod_.type_sec)
let type_groups : Array[Array[SubType]] = []
match mod_.type_sec {
Some(@lib.TypeSec(groups)) =>
for group in groups {
match group {
@lib.SingleRecType(subtype) => type_groups.push([subtype])
@lib.GroupRecType(subtypes) => type_groups.push(subtypes)
}
}
None => ()
}
{
subtypes,
type_groups,
func_type_idxs,
func_types: hot_module_context_resolve_func_types(subtypes, func_type_idxs),
func_type_slots: hot_module_context_func_type_slots(func_type_idxs),
func_imports,
func_annotations: hot_module_context_func_annotations(
mod_.func_annotation_sec,
func_type_idxs.length(),
),
tables,
mems,
globals,
global_inits,
global_const_inits,
elems,
datas,
tags,
imported_tag_count,
uses_custom_descriptors: @lib.module_uses_custom_descriptors(mod_),
}
}
///|
pub fn hot_module_resolve_subtype(
ctx : HotModuleContext,
idx : TypeIdx,
) -> SubType? {
match idx {
@lib.TypeIdx(raw) => ctx.subtypes.get(raw.reinterpret_as_int())
@lib.RecIdx(_) => None
}
}
///|
pub fn hot_module_subtype_count(ctx : HotModuleContext) -> Int32 {
ctx.subtypes.length()
}
///|
pub fn hot_module_resolve_type_func_type(
ctx : HotModuleContext,
idx : TypeIdx,
) -> FuncType? {
match hot_module_resolve_subtype(ctx, idx) {
Some(subtype) =>
match subtype.get_comptype() {
@lib.FuncCompType(params, results) =>
Some(@lib.FuncType::new(params, results))
_ => None
}
None => None
}
}
///|
pub fn hot_module_resolve_func_type_idx(
ctx : HotModuleContext,
func_idx : @lib.FuncIdx,
) -> TypeIdx? {
let @lib.FuncIdx(raw) = func_idx
ctx.func_type_idxs.get(raw.reinterpret_as_int())
}
///|
pub fn hot_module_resolve_func_type(
ctx : HotModuleContext,
func_idx : @lib.FuncIdx,
) -> FuncType? {
let @lib.FuncIdx(raw) = func_idx
ctx.func_types.get(raw.reinterpret_as_int())
}
///|
pub fn hot_module_resolve_func_import(
ctx : HotModuleContext,
func_idx : @lib.FuncIdx,
) -> (@lib.Name, @lib.Name)? {
let @lib.FuncIdx(raw) = func_idx
match ctx.func_imports.get(raw.reinterpret_as_int()) {
Some(import_) => import_
None => None
}
}
///|
pub fn hot_module_func_import_matches(
ctx : HotModuleContext,
func_idx : @lib.FuncIdx,
module_name : String,
base_name : String,
) -> Bool {
match hot_module_resolve_func_import(ctx, func_idx) {
Some((actual_module, actual_base)) =>
actual_module == @lib.Name::from_string(module_name) &&
actual_base == @lib.Name::from_string(base_name)
None => false
}
}
///|
pub fn hot_module_func_has_annotation(
ctx : HotModuleContext,
func_idx : @lib.FuncIdx,
name : String,
) -> Bool {
let @lib.FuncIdx(raw) = func_idx
match ctx.func_annotations.get(raw.reinterpret_as_int()) {
Some(annotations) =>
for annotation in annotations {
if annotation.name == name && annotation.args.length() == 0 {
return true
}
}
None => ()
}
false
}
///|
pub fn hot_module_resolve_block_results(
ctx : HotModuleContext,
block_type : BlockType,
) -> Array[ValType]? {
match block_type {
@lib.VoidBlockType => Some([])
@lib.ValTypeBlockType(val_type) => Some([val_type])
@lib.TypeIdxBlockType(type_idx) =>
match hot_module_resolve_type_func_type(ctx, type_idx) {
Some(@lib.FuncType(_, results)) => Some(results)
None => None
}
}
}
///|
pub fn hot_module_resolve_table_type(
ctx : HotModuleContext,
table_idx : @lib.TableIdx,
) -> TableType? {
let @lib.TableIdx(raw) = table_idx
ctx.tables.get(raw.reinterpret_as_int())
}
///|
pub fn hot_module_resolve_mem_type(
ctx : HotModuleContext,
mem_idx : @lib.MemIdx,
) -> @lib.MemType? {
let @lib.MemIdx(raw) = mem_idx
ctx.mems.get(raw.reinterpret_as_int())
}
///|
pub fn hot_module_resolve_global_type(
ctx : HotModuleContext,
global_idx : @lib.GlobalIdx,
) -> GlobalType? {
let @lib.GlobalIdx(raw) = global_idx
ctx.globals.get(raw.reinterpret_as_int())
}
///|
pub fn hot_module_resolve_global_init(
ctx : HotModuleContext,
global_idx : @lib.GlobalIdx,
) -> @lib.Expr? {
let @lib.GlobalIdx(raw) = global_idx
match ctx.global_inits.get(raw.reinterpret_as_int()) {
Some(init) => init
None => None
}
}
///|
pub fn hot_module_resolve_global_const_init(
ctx : HotModuleContext,
global_idx : @lib.GlobalIdx,
) -> HotConstPayload? {
let @lib.GlobalIdx(raw) = global_idx
match ctx.global_const_inits.get(raw.reinterpret_as_int()) {
Some(payload) => payload
None => None
}
}
///|
pub fn hot_module_resolve_tag_type(
ctx : HotModuleContext,
tag_idx : @lib.TagIdx,
) -> TagType? {
let @lib.TagIdx(raw) = tag_idx
ctx.tags.get(raw.reinterpret_as_int())
}
///|
pub fn hot_module_resolve_mem_index_type(
ctx : HotModuleContext,
mem_idx : @lib.MemIdx,
) -> ValType? {
match hot_module_resolve_mem_type(ctx, mem_idx) {
Some(@lib.MemType(limits, _)) => Some(limits.addr_valtype())
None => None
}
}
///|
pub fn hot_module_resolve_table_index_type(
ctx : HotModuleContext,
table_idx : @lib.TableIdx,
) -> ValType? {
match hot_module_resolve_table_type(ctx, table_idx) {
Some(@lib.TableType(_, limits)) => Some(limits.addr_valtype())
None => None
}
}
///|
pub fn hot_module_resolve_struct_fields(
ctx : HotModuleContext,
type_idx : TypeIdx,
) -> Array[FieldType]? {
match hot_module_resolve_subtype(ctx, type_idx) {
Some(subtype) =>
match subtype.get_comptype() {
@lib.StructCompType(fields) => Some(fields)
_ => None
}
None => None
}
}
///|
pub fn hot_module_resolve_array_field(
ctx : HotModuleContext,
type_idx : TypeIdx,
) -> FieldType? {
match hot_module_resolve_subtype(ctx, type_idx) {
Some(subtype) =>
match subtype.get_comptype() {
@lib.ArrayCompType(field) => Some(field)
_ => None
}
None => None
}
}
///|
pub fn hot_type_resolve_results(
func : HotFunc,
ctx : HotModuleContext,
type_id : TypeId,
) -> Array[ValType]? {
match hot_type_get(func, type_id) {
VoidType => Some([])
BlockResult(block_type) => hot_module_resolve_block_results(ctx, block_type)
BlockResultResolved(_, _, values) => Some(values.copy())
Value(val_type) => Some([val_type])
Results(values) => Some(values)
}
}