///|
// Controls for the proposal slices covered by the Binaryen 132 intake. Other
// established Wasm features retain their existing validation policy.
pub enum ProposalFeature {
AcquireReleaseAtomics
RelaxedAtomics
SharedEverything
Multibyte
RelaxedSimd
StackSwitching
} derive(Eq, Debug)
///|
pub fn controlled_proposal_features() -> Array[ProposalFeature] {
[
AcquireReleaseAtomics,
RelaxedAtomics,
SharedEverything,
Multibyte,
RelaxedSimd,
StackSwitching,
]
}
///|
pub fn proposal_feature_name(feature : ProposalFeature) -> String {
match feature {
AcquireReleaseAtomics => "acquire-release-atomics"
RelaxedAtomics => "relaxed-atomics"
SharedEverything => "shared-everything"
Multibyte => "multibyte"
RelaxedSimd => "relaxed-simd"
StackSwitching => "stack-switching"
}
}
///|
pub fn proposal_feature_by_name(name : String) -> ProposalFeature? {
for feature in controlled_proposal_features() {
if proposal_feature_name(feature) == name {
return Some(feature)
}
}
None
}
///|
fn proposal_note(
out : Array[ProposalFeature],
feature : ProposalFeature,
) -> Unit {
if !out.contains(feature) {
out.push(feature)
}
}
///|
fn proposal_heap(out : Array[ProposalFeature], heap : HeapType) -> Unit {
match heap {
AbsHeapTypeHeapType(ContAbsHeapType)
| AbsHeapTypeHeapType(NoContAbsHeapType)
| SharedAbsHeapTypeHeapType(ContAbsHeapType)
| SharedAbsHeapTypeHeapType(NoContAbsHeapType) =>
proposal_note(out, StackSwitching)
_ => ()
}
match heap {
SharedAbsHeapTypeHeapType(_)
| AbsHeapTypeHeapType(WaitqueueAbsHeapType)
| AbsHeapTypeHeapType(NoWaitqueueAbsHeapType) =>
proposal_note(out, SharedEverything)
DefTypeHeapType(DefType(rec, _)) => proposal_rec_type(out, rec)
_ => ()
}
}
///|
fn proposal_value(out : Array[ProposalFeature], value : ValType) -> Unit {
if value is RefTypeValType(ref_type) {
proposal_heap(out, ref_type.get_heap_type())
}
}
///|
fn proposal_field(out : Array[ProposalFeature], field : FieldType) -> Unit {
let FieldType(storage, _) = field
if storage is ValTypeStorageType(value) {
proposal_value(out, value)
}
}
///|
fn proposal_rec_type(out : Array[ProposalFeature], rec : RecType) -> Unit {
let subtypes = match rec {
SingleRecType(subtype) => [subtype]
GroupRecType(subtypes) => subtypes
}
for subtype in subtypes {
if subtype.metadata().is_shared() {
proposal_note(out, SharedEverything)
}
match subtype.get_comptype() {
FuncCompType(params, results) => {
for value in params {
proposal_value(out, value)
}
for value in results {
proposal_value(out, value)
}
}
ArrayCompType(field) => proposal_field(out, field)
StructCompType(fields) =>
for field in fields {
proposal_field(out, field)
}
ContCompType(_) => proposal_note(out, StackSwitching)
_ => ()
}
}
}
///|
fn proposal_block_type(out : Array[ProposalFeature], block : BlockType) -> Unit {
if block is ValTypeBlockType(value) {
proposal_value(out, value)
}
}
///|
fn proposal_instruction(
out : Array[ProposalFeature],
instruction : Instruction,
work : Array[Array[Instruction]],
) -> Unit {
let order = match instruction {
AtomicFence(order)
| I32AtomicLoad(order, _)
| I64AtomicLoad(order, _)
| I32AtomicLoad8U(order, _)
| I32AtomicLoad16U(order, _)
| I64AtomicLoad8U(order, _)
| I64AtomicLoad16U(order, _)
| I64AtomicLoad32U(order, _)
| I32AtomicStore(order, _)
| I64AtomicStore(order, _)
| I32AtomicStore8(order, _)
| I32AtomicStore16(order, _)
| I64AtomicStore8(order, _)
| I64AtomicStore16(order, _)
| I64AtomicStore32(order, _)
| AtomicRmw(order, _, _)
| AtomicCmpxchg(order, _, _)
| StructAtomicSet(order, _, _)
| ArrayAtomicSet(order, _)
| StructAtomicGet(order, _, _)
| StructAtomicGetS(order, _, _)
| StructAtomicGetU(order, _, _)
| StructAtomicRmw(order, _, _, _)
| StructAtomicCmpxchg(order, _, _)
| ArrayAtomicGet(order, _)
| ArrayAtomicGetS(order, _)
| ArrayAtomicGetU(order, _)
| ArrayAtomicRmw(order, _, _)
| ArrayAtomicCmpxchg(order, _) => Some(order)
_ => None
}
match order {
Some(AcqRel) => proposal_note(out, AcquireReleaseAtomics)
Some(Relaxed) => proposal_note(out, RelaxedAtomics)
_ => ()
}
match instruction {
ContNew(_)
| ContBind(_, _)
| Suspend(_)
| Resume(_, _)
| ResumeThrow(_, _, _)
| ResumeThrowRef(_, _)
| StackSwitch(_, _) => proposal_note(out, StackSwitching)
StructAtomicSet(_, _, _)
| ArrayAtomicSet(_, _)
| StructAtomicGet(_, _, _)
| StructAtomicGetS(_, _, _)
| StructAtomicGetU(_, _, _)
| StructAtomicRmw(_, _, _, _)
| StructAtomicCmpxchg(_, _, _)
| ArrayAtomicGet(_, _)
| ArrayAtomicGetS(_, _)
| ArrayAtomicGetU(_, _)
| ArrayAtomicRmw(_, _, _)
| ArrayAtomicCmpxchg(_, _)
| WaitqueueNew
| WaitqueueNotify
| StructWait(_, _) => proposal_note(out, SharedEverything)
Publish => proposal_note(out, SharedEverything)
RefI31Shared => proposal_note(out, SharedEverything)
ArrayLoad(_, _, _) | ArrayStore(_, _, _) => proposal_note(out, Multibyte)
I8x16RelaxedSwizzle
| I32x4RelaxedTruncF32x4S
| I32x4RelaxedTruncF32x4U
| I32x4RelaxedTruncZeroF64x2S
| I32x4RelaxedTruncZeroF64x2U
| F32x4RelaxedMadd
| F32x4RelaxedNmadd
| F64x2RelaxedMadd
| F64x2RelaxedNmadd
| I8x16RelaxedLaneselect
| I16x8RelaxedLaneselect
| I32x4RelaxedLaneselect
| I64x2RelaxedLaneselect
| F32x4RelaxedMin
| F32x4RelaxedMax
| F64x2RelaxedMin
| F64x2RelaxedMax
| I16x8RelaxedQ15mulrS
| I16x8RelaxedDotI8x16I7x16S
| I32x4RelaxedDotI8x16I7x16AddS => proposal_note(out, RelaxedSimd)
Block(block_type, Expr(body))
| Loop(block_type, Expr(body))
| TryTable(block_type, _, Expr(body)) => {
proposal_block_type(out, block_type)
work.push(body)
}
If(block_type, yes, no) => {
proposal_block_type(out, block_type)
work.push(yes)
if no is Some(no) {
work.push(no)
}
}
Try(block_type, Expr(body), catches, _) => {
proposal_block_type(out, block_type)
work.push(body)
for catch_ in catches {
match catch_ {
LegacyCatch(_, Expr(body)) | LegacyCatchAll(Expr(body)) =>
work.push(body)
}
}
}
RefNull(ref_type) => proposal_heap(out, ref_type.get_heap_type())
RefTest(_, _, heap)
| RefCast(_, _, heap)
| RefTestDesc(_, heap)
| RefCastDescEq(_, _, heap) => proposal_heap(out, heap)
BrOnCast(_, _, source, target) | BrOnCastFail(_, _, source, target) => {
proposal_heap(out, source)
proposal_heap(out, target)
}
BrOnCastDescEq(_, source, target) | BrOnCastDescEqFail(_, source, target) => {
proposal_heap(out, source.get_heap_type())
proposal_heap(out, target.get_heap_type())
}
Select(Some(values)) =>
for value in values {
proposal_value(out, value)
}
_ => ()
}
}
///|
fn proposal_tag(out : Array[ProposalFeature], tag : TagType, env : Env) -> Unit {
let TagType(type_idx) = tag
if env.resolve_comptype(type_idx) is Some(FuncCompType(_, results)) &&
!results.is_empty() {
proposal_note(out, StackSwitching)
}
}
///|
// Infer requirements from all declarations and expression regions, including
// initializers and unreachable code. Stable order is independent of traversal.
pub fn module_proposal_features(module_ : Module) -> Array[ProposalFeature] {
let out : Array[ProposalFeature] = []
let work : Array[Array[Instruction]] = []
let tag_declarations : Array[TagType] = []
if module_.type_sec is Some(TypeSec(groups)) {
for rec in groups {
proposal_rec_type(out, rec)
}
}
if module_.import_sec is Some(ImportSec(imports)) {
for import_ in imports {
let Import(_, _, kind) = import_
match kind {
GlobalExternType(GlobalType(value, _)) => proposal_value(out, value)
TableExternType(TableType(ref_type, _)) =>
proposal_heap(out, ref_type.get_heap_type())
TagExternType(tag) => tag_declarations.push(tag)
_ => ()
}
}
}
if module_.tag_sec is Some(TagSec(tags)) {
for tag in tags {
tag_declarations.push(tag)
}
}
if !tag_declarations.is_empty() {
let base_env = empty_env()
let (global_types, rec_stack) = env_collect_module_types(
base_env.global_types,
base_env.rec_stack,
module_,
)
let env = Env::{ ..base_env, global_types, rec_stack, }
for tag in tag_declarations {
proposal_tag(out, tag, env)
}
}
if module_.global_sec is Some(GlobalSec(globals)) {
for global_ in globals {
let Global(GlobalType(value, _), Expr(body)) = global_
proposal_value(out, value)
work.push(body)
}
}
if module_.table_sec is Some(TableSec(tables)) {
for table in tables {
let Table(TableType(ref_type, _), initial) = table
proposal_heap(out, ref_type.get_heap_type())
if initial is Some(Expr(body)) {
work.push(body)
}
}
}
if module_.elem_sec is Some(ElemSec(elements)) {
for element in elements {
let Elem(mode, kind) = element
if mode is Active(_, Expr(body)) {
work.push(body)
}
match kind {
FuncsElemKind(_) => ()
FuncExprsElemKind(expressions) =>
for expression in expressions {
let Expr(body) = expression
work.push(body)
}
TypedExprsElemKind(ref_type, expressions) => {
proposal_heap(out, ref_type.get_heap_type())
for expression in expressions {
let Expr(body) = expression
work.push(body)
}
}
}
}
}
if module_.data_sec is Some(DataSec(data)) {
for item in data {
let Data(mode, _) = item
if mode is Active(_, Expr(body)) {
work.push(body)
}
}
}
if module_.code_sec is Some(CodeSec(bodies)) {
for func in bodies {
let Func(locals, Expr(body)) = func
for run in locals.runs {
proposal_value(out, run.vt)
}
work.push(body)
}
}
while !work.is_empty() {
for instruction in work.pop().unwrap() {
proposal_instruction(out, instruction, work)
}
}
controlled_proposal_features().filter(fn(feature) { out.contains(feature) })
}