// The name section.
//
// Ported from the `name` custom section of wax/src/lib-wasm/wasm_output.ml.
//
// Names are the one part of a module the runtime ignores entirely, and the one
// part a reader depends on. Every index space gets its own subsection, and a
// name is emitted only where the SOURCE wrote one: a synthetic name -- the
// `` array, a generated block label -- exists so the checker has
// something to resolve, and putting it in the output would name a thing the
// author never named.
///|
/// Whether a name was generated rather than written.
///
/// Synthetic names are spelled with a leading `<`, which the grammar cannot
/// produce -- that is why the spelling was chosen.
fn is_synthetic(name : String) -> Bool {
name.has_prefix("<")
}
///|
/// Record a name for an index, unless it was generated.
fn note_name(into : Map[Int, Bytes], index : Int, name : String) -> Unit {
if !is_synthetic(name) {
into[index] = utf8_of(name)
}
}
///|
/// The names of every TYPE, and of every struct field.
///
/// Read off the checker's own table, which is where a source name and the index
/// it interned to are joined. A type that several names reach keeps the first,
/// because that is the one the reference emits.
fn Lowering::type_names(self : Lowering, m : @wasm_bin.Module) -> Unit {
// A NAME names its own emitted entry, not the store entry it shares with a
// like-shaped sibling -- which is the whole reason the two indices are
// different numbers. `by_name` holds exactly the names that reached the
// section: the declarations, and the synthesized types something referred to.
// A synthesized name is emitted precisely because it was materialised: the
// entry exists only for it, so nothing else can carry the name.
for name, i in self.layout.by_name {
if !m.names.types.contains(i) {
m.names.types[i] = utf8_of(name)
}
}
for entry in self.ctx.type_context.types.iter_entries() {
let (name, (idx, sub)) = entry
guard idx is Def(id) else { continue }
let i = match self.layout.by_name.get(name) {
Some(emitted) => emitted
None => emitted_type_index(id.to_int_for_tests_only())
}
// A struct's fields are named per TYPE, so they hang off the same index.
if sub.typ is Struct(fields) && !m.names.fields.contains(i) {
let per_field : Map[Int, Bytes] = Map([])
for k, f in fields {
note_name(per_field, k, f.desc.0.name)
}
if !per_field.is_empty() {
m.names.fields[i] = per_field
}
}
}
}
///|
/// The module's own name, if it declared one.
fn Lowering::module_name(
self : Lowering,
m : @wasm_bin.Module,
fields : @ast.Module[@basic.Location],
) -> Unit {
ignore(self)
for field in fields {
guard field.desc is ModuleAnnotation(attrs) else { continue }
for a in attrs {
if a.attr_name == "module" &&
a.attr_value is Some(v) &&
v.desc is Str(_, b) {
m.names.module_ = Some(b)
}
}
}
}
///|
/// The `target_features` custom section: one `+name` entry per declared
/// feature.
///
/// A `#![feature = "..."]` is a fact about the module that the binary format
/// has no other place for, so it survives as tool-conventions metadata. `+`
/// means "used"; the reference emits nothing else, and a repeated declaration
/// is idempotent rather than duplicated.
fn Lowering::target_features(
self : Lowering,
m : @wasm_bin.Module,
fields : @ast.Module[@basic.Location],
) -> Unit {
ignore(self)
for field in fields {
guard field.desc is ModuleAnnotation(attrs) else { continue }
for a in attrs {
guard a.attr_name == "feature" else { continue }
guard a.attr_value is Some(v) else { continue }
guard v.desc is Str(_, name) else { continue }
let entry = (43, name)
if !m.target_features.contains(entry) {
m.target_features.push(entry)
}
}
}
}
///|
/// The optimization level `#[run_once]` names.
let run_once_level : Int = 127
///|
/// A function's compilation priority, if it declared one.
///
/// `#[priority]` orders compilation and `#[optimization]` sets the effort; the
/// second is meaningless without the first, which is why they are one record
/// rather than two.
fn function_priority(attributes : Array[@ast.Attribute]) -> @wasm_bin.Priority? {
let mut compilation : Int? = None
let mut optimization : Int? = None
for a in attributes {
// `#[run_once]` carries no value: it is the spelling of one particular
// optimization level, the highest, and the section stores it as that
// number rather than as a flag of its own.
if a.attr_name == "run_once" {
optimization = Some(run_once_level)
continue
}
guard a.attr_value is Some(v) else { continue }
guard v.desc is Int(s) else { continue }
let n = @string.parse_int(s.split("_").join("")) catch { _ => continue }
match a.attr_name {
"priority" => compilation = Some(n)
"optimization" => optimization = Some(n)
_ => ()
}
}
compilation.map(c => ({ compilation: c, optimization } : @wasm_bin.Priority))
}