///|
fn feature_value_uses_exact(value : ValType) -> Bool {
match value {
RefTypeValType(ref_type) => ref_type.is_exact()
_ => false
}
}
///|
fn feature_block_uses_exact(block_type : BlockType) -> Bool {
match block_type {
ValTypeBlockType(value) => feature_value_uses_exact(value)
_ => false
}
}
///|
fn feature_subtype_uses_descriptors(subtype : SubType) -> Bool {
if subtype.describes_type() != None || subtype.descriptor_type() != None {
return true
}
match subtype.get_comptype() {
FuncCompType(params, results) =>
params.any(feature_value_uses_exact) ||
results.any(feature_value_uses_exact)
StructCompType(fields) =>
fields.any(fn(field) { feature_value_uses_exact(field.0.unpack()) })
ArrayCompType(field) => feature_value_uses_exact(field.0.unpack())
ContCompType(_) => false
}
}
///|
fn feature_body_uses_descriptors(body : Array[Instruction]) -> Bool {
for instr in body {
let uses = match instr {
StructNewDesc(_)
| StructNewDefaultDesc(_)
| RefGetDesc(_)
| RefTestDesc(_, _)
| RefCastDescEq(_, _, _)
| BrOnCastDescEq(_, _, _)
| BrOnCastDescEqFail(_, _, _) => true
RefNull(ref_type) => ref_type.is_exact()
RefTest(_, exact, _) | RefCast(_, exact, _) => exact
BrOnCast(_, cast, _, _) | BrOnCastFail(_, cast, _, _) =>
cast.source_exact() || cast.target_exact()
Select(Some(types)) => types.any(feature_value_uses_exact)
Block(block_type, Expr(body))
| Loop(block_type, Expr(body))
| TryTable(block_type, _, Expr(body)) =>
feature_block_uses_exact(block_type) ||
feature_body_uses_descriptors(body)
If(block_type, then_body, else_body) =>
feature_block_uses_exact(block_type) ||
feature_body_uses_descriptors(then_body) ||
(match else_body {
Some(body) => feature_body_uses_descriptors(body)
None => false
})
Try(block_type, Expr(body), catches, _) =>
feature_block_uses_exact(block_type) ||
feature_body_uses_descriptors(body) ||
catches.any(fn(catch_) {
match catch_ {
LegacyCatch(_, Expr(body)) | LegacyCatchAll(Expr(body)) =>
feature_body_uses_descriptors(body)
}
})
_ => false
}
if uses {
return true
}
}
false
}
///|
/// Detect the custom-descriptors proposal in encoded module constructs. This
/// includes exact references even when no descriptor-bearing type is declared.
/// Ordinary GC allocation instructions alone do not opt into the proposal.
pub fn module_uses_custom_descriptors(mod_ : Module) -> Bool {
match mod_.type_sec {
Some(TypeSec(groups)) =>
for group in groups {
match group {
SingleRecType(subtype) =>
if feature_subtype_uses_descriptors(subtype) {
return true
}
GroupRecType(subtypes) =>
if subtypes.any(feature_subtype_uses_descriptors) {
return true
}
}
}
None => ()
}
match mod_.import_sec {
Some(ImportSec(imports)) =>
for import_ in imports {
let Import(_, _, type_) = import_
match type_ {
TableExternType(TableType(ref_type, _)) =>
if ref_type.is_exact() {
return true
}
GlobalExternType(GlobalType(value, _)) =>
if feature_value_uses_exact(value) {
return true
}
_ => ()
}
}
None => ()
}
match mod_.table_sec {
Some(TableSec(tables)) =>
for table in tables {
let Table(TableType(ref_type, _), init) = table
if ref_type.is_exact() {
return true
}
match init {
Some(Expr(body)) =>
if feature_body_uses_descriptors(body) {
return true
}
None => ()
}
}
None => ()
}
match mod_.global_sec {
Some(GlobalSec(globals)) =>
for global in globals {
let Global(GlobalType(value, _), Expr(body)) = global
if feature_value_uses_exact(value) ||
feature_body_uses_descriptors(body) {
return true
}
}
None => ()
}
match mod_.elem_sec {
Some(ElemSec(elems)) =>
for elem in elems {
let Elem(mode, kind) = elem
match mode {
Active(_, Expr(body)) =>
if feature_body_uses_descriptors(body) {
return true
}
_ => ()
}
match kind {
TypedExprsElemKind(ref_type, exprs) => {
if ref_type.is_exact() {
return true
}
for expr in exprs {
if feature_body_uses_descriptors(expr.0) {
return true
}
}
}
FuncExprsElemKind(exprs) =>
for expr in exprs {
if feature_body_uses_descriptors(expr.0) {
return true
}
}
FuncsElemKind(_) => ()
}
}
None => ()
}
match mod_.code_sec {
Some(CodeSec(funcs)) =>
for func in funcs {
let Func(locals, Expr(body)) = func
for run in locals.runs() {
if feature_value_uses_exact(run.vt) {
return true
}
}
if feature_body_uses_descriptors(body) {
return true
}
}
None => ()
}
match mod_.data_sec {
Some(DataSec(segments)) =>
for segment in segments {
let Data(mode, _) = segment
match mode {
Active(_, Expr(body)) =>
if feature_body_uses_descriptors(body) {
return true
}
Passive => ()
}
}
None => ()
}
false
}