pub struct FunctionEmitter<'a> {
pub runtime_type_params: BTreeMap<String, (u32, u32)>,
/* private fields */
}Fields§
§runtime_type_params: BTreeMap<String, (u32, u32)>Implementations§
Source§impl<'a> FunctionEmitter<'a>
impl<'a> FunctionEmitter<'a>
Sourcepub fn new(
ctx: &'a CodegenCtx<'a>,
params: &[(Ident, ValType)],
) -> Result<Self, CompilerFailure>
pub fn new( ctx: &'a CodegenCtx<'a>, params: &[(Ident, ValType)], ) -> Result<Self, CompilerFailure>
Parameters are implicit Wasm locals (0..N) — not in the locals declaration section.
pub fn define_local( &mut self, name: &Ident, ty: ValType, ) -> Result<u32, CompilerFailure>
Sourcepub fn evaluated_slot(&self, id: ExprId) -> Option<u32>
pub fn evaluated_slot(&self, id: ExprId) -> Option<u32>
The local holding id’s already-computed value, if the statement being
emitted registered one.
Sourcepub fn record_single_evaluation(
&mut self,
id: ExprId,
slot: u32,
) -> Result<(), CompilerFailure>
pub fn record_single_evaluation( &mut self, id: ExprId, slot: u32, ) -> Result<(), CompilerFailure>
Register slot as id’s value. Call after emitting id, so the
emission that fills the slot isn’t itself short-circuited.
Sourcepub fn single_evaluation_mark(&self) -> usize
pub fn single_evaluation_mark(&self) -> usize
Registrations are statement-scoped: later emission of a shared expression
must compute its value again. Take a mark before emitting a statement
that registers, and Self::end_single_evaluations with it afterwards.
Sourcepub fn end_single_evaluations(
&mut self,
mark: usize,
) -> Result<(), CompilerFailure>
pub fn end_single_evaluations( &mut self, mark: usize, ) -> Result<(), CompilerFailure>
Drop every registration taken since mark. See
Self::single_evaluation_mark.
pub fn add_anonymous_local( &mut self, ty: ValType, ) -> Result<u32, CompilerFailure>
pub fn require_local_slot(&self, name: &str) -> Result<u32, CompilerFailure>
Sourcepub fn narrowed_read_slot(&self, name: &str) -> Option<(u32, ValType)>
pub fn narrowed_read_slot(&self, name: &str) -> Option<(u32, ValType)>
The slot a narrowed read of name resolves to, and what it holds: the
innermost shadow when one is live, otherwise the binding’s storage,
which the caller then casts down to the narrowed type. Only
LocalNarrowRef wants this — an ordinary read wants
Self::local_slot, whose value is never stale.
Sourcepub fn local_slot(&self, name: &str) -> Option<u32>
pub fn local_slot(&self, name: &str) -> Option<u32>
The binding’s storage — what every read at the declared type sees.
Sourcepub fn write_slot(&mut self, name: &str) -> Result<u32, CompilerFailure>
pub fn write_slot(&mut self, name: &str) -> Result<u32, CompilerFailure>
The storage slot to write name through, dropping every shadow of it on
the way. Both halves are the point: writing through a shadow instead
loses the write (the storage keeps its old value) and rejects any value
wider than the narrowing — x = null into a (ref $string) slot is
invalid Wasm — and leaving a shadow behind hands the next narrowed read
the value from before the write.
pub fn set_return_target(&mut self, target: ReturnTarget)
pub fn return_target(&self) -> &ReturnTarget
pub fn set_this_local(&mut self, index: u32) -> Result<(), CompilerFailure>
pub fn this_local(&self) -> Option<u32>
pub fn set_ctor_class(&mut self, mangled: MangledName)
pub fn ctor_class(&self) -> Option<&MangledName>
Sourcepub fn rebind_in_innermost_scope(
&mut self,
name: &str,
index: u32,
ty: ValType,
) -> Result<(), CompilerFailure>
pub fn rebind_in_innermost_scope( &mut self, name: &str, index: u32, ty: ValType, ) -> Result<(), CompilerFailure>
Move name’s storage to index — for a param whose real home is the box
or typed local the prologue builds. This is a re-declaration, not a
narrowing: writes follow it.
Sourcepub fn install_narrow_shadow(
&mut self,
name: &str,
index: u32,
ty: ValType,
) -> Result<(), CompilerFailure>
pub fn install_narrow_shadow( &mut self, name: &str, index: u32, ty: ValType, ) -> Result<(), CompilerFailure>
Record the shadow an assignment’s narrowing just took. Narrowed reads resolve here until the next write clears it; writes never do.
Sourcepub fn register_narrow_source(
&mut self,
name: &str,
source: ExprId,
) -> Result<(), CompilerFailure>
pub fn register_narrow_source( &mut self, name: &str, source: ExprId, ) -> Result<(), CompilerFailure>
Register the deferred live-read source for a field/index narrowing. Every narrowed use reloads this source and checks the current value.
pub fn narrow_source(&self, name: &str) -> Option<ExprId>
Sourcepub fn clear_narrow_shadows(&mut self, name: &str)
pub fn clear_narrow_shadows(&mut self, name: &str)
Drop every shadow of name visible from here — a write to the storage
slot makes all of them stale. Stops at the scope that declares the name;
an outer binding of the same name is a different one.
Sourcepub fn clear_all_narrow_shadows(&mut self)
pub fn clear_all_narrow_shadows(&mut self)
Drop every narrowing shadow in scope. A shadow is sound only for reads
that follow its assignment both in the emitted code and at run time, and
a branching statement is where those two stop agreeing — see
stmt::branches, which is what calls this.
pub fn push_scope(&mut self)
Sourcepub fn pop_scope(&mut self) -> Result<(), CompilerFailure>
pub fn pop_scope(&mut self) -> Result<(), CompilerFailure>
Wasm local indices remain reserved after pop — locals are function-scoped, not block-scoped.
Sourcepub fn emit_boxed_param_prologue(
&mut self,
params: &[TypedParam],
typed_slots: &[u32],
) -> Result<(), CompilerFailure>
pub fn emit_boxed_param_prologue( &mut self, params: &[TypedParam], typed_slots: &[u32], ) -> Result<(), CompilerFailure>
typed_slots[i] is the Wasm slot holding the typed-form value of params[i] —
index i for top-level functions, a prologue-allocated local for closure bodies.
Boxing is body-internal; the Wasm signature is unchanged.
Sourcepub fn instruction(&mut self, inst: Instruction<'static>)
pub fn instruction(&mut self, inst: Instruction<'static>)
Prefer the structured helpers (emit_block/if/loop) to keep depth tracking consistent.
pub fn instructions( &mut self, insts: impl IntoIterator<Item = Instruction<'static>>, )
Sourcepub fn body_size(&mut self) -> usize
pub fn body_size(&mut self) -> usize
The encoded size the function body would have if it ended here: its
local declarations, its instructions and the closing end, as
Self::build encodes them. Instructions are only ever appended, so
each is encoded once, the first time it is measured.
Sourcepub fn body_limit_span(&self) -> Option<Span>
pub fn body_limit_span(&self) -> Option<Span>
The source whose code took Self::body_size past the Wasm
engine’s body-size limit.
Sourcepub fn locals_limit_span(&self) -> Option<Span>
pub fn locals_limit_span(&self) -> Option<Span>
The source being emitted when the function first needed more locals than the Wasm engine accepts.
Sourcepub fn local_count(&self) -> u32
pub fn local_count(&self) -> u32
Locals the function has so far, parameters included.
Sourcepub fn last_mapped_span(&self) -> Option<Span>
pub fn last_mapped_span(&self) -> Option<Span>
The source span most recently mapped to emitted code.
Sourcepub fn last_source_span(&self) -> Option<Span>
pub fn last_source_span(&self) -> Option<Span>
The most recently mapped span that locates source, skipping the placeholders of desugared code.
pub fn record_span(&mut self, span: Span)
pub fn emit_block(&mut self, ty: BlockType)
pub fn emit_if(&mut self, ty: BlockType)
pub fn emit_loop(&mut self, ty: BlockType)
Sourcepub fn emit_else(&mut self)
pub fn emit_else(&mut self)
else doesn’t change block depth — it’s a continuation of the surrounding if.
pub fn emit_end(&mut self)
Sourcepub fn bump_block_depth(&mut self)
pub fn bump_block_depth(&mut self)
For instructions like try_table that open a block frame but are emitted via
instruction() (which skips depth tracking); the matching emit_end() decrements.
Sourcepub fn wasm_result_type(
&self,
ctx: &CodegenCtx<'_>,
) -> Result<Option<ValType>, CompilerFailure>
pub fn wasm_result_type( &self, ctx: &CodegenCtx<'_>, ) -> Result<Option<ValType>, CompilerFailure>
The Wasm result this body’s signature declares, or None when it has none.
pub fn break_finally_floor(&self) -> usize
Sourcepub fn continue_finally_floor(&self) -> usize
pub fn continue_finally_floor(&self) -> usize
Like break_finally_floor but for continue; skips switch frames.
pub fn emit_while_open(&mut self)
Sourcepub fn emit_switch_open(&mut self)
pub fn emit_switch_open(&mut self)
Pushes an is_switch: true break frame so continue_label() skips to the enclosing loop.
pub fn emit_switch_close(&mut self)
pub fn emit_while_close(&mut self)
pub fn break_label(&self) -> Result<u32, CompilerFailure>
Sourcepub fn continue_label(&self) -> Result<u32, CompilerFailure>
pub fn continue_label(&self) -> Result<u32, CompilerFailure>
Walks past switch frames — continue inside a switch targets the enclosing loop.
pub fn return_label(&self) -> Result<u32, CompilerFailure>
pub fn branch_depth(&self, target_depth: u32) -> Result<u32, CompilerFailure>
Sourcepub fn emit_const_string(
&mut self,
string_type_idx: u32,
raw_string_type_idx: u32,
string_vtable_global_idx: u32,
consts_data_idx: u32,
code_units: u32,
)
pub fn emit_const_string( &mut self, string_type_idx: u32, raw_string_type_idx: u32, string_vtable_global_idx: u32, consts_data_idx: u32, code_units: u32, )
Segment offset is always 0 — one data segment per literal.
Sourcepub fn build(self) -> Result<Function, CompilerFailure>
pub fn build(self) -> Result<Function, CompilerFailure>
Consumes the emitter, returning bytes only after state and size checks pass. Any failure discards the owned instructions, scopes and completion frames.
Sourcepub fn build_with_lines(
self,
) -> Result<(Function, Vec<(u64, Span)>), CompilerFailure>
pub fn build_with_lines( self, ) -> Result<(Function, Vec<(u64, Span)>), CompilerFailure>
Like build, but also returns DWARF byte offsets. Offsets are measured
before each instruction is pushed, so they never decrease.