submilli_engine/codegen/
throw.rs1use wasm_encoder::{HeapType, Instruction};
16
17use crate::codegen::CodegenCtx;
18use crate::codegen::function_emitter::{FunctionEmitter, emit_const_string_by_text};
19
20pub const NON_NULL_ASSERT_MESSAGE: &str = "non-null assertion failed: value is null or undefined";
22
23pub const READ_ONLY_PROPERTY_MESSAGE: &str =
27 "cannot assign to a property backed by a getter with no setter";
28
29pub const MISSING_RETURN_VALUE_MESSAGE: &str =
35 "function ended without returning a value: no `case` of an exhaustive `switch` matched";
36
37pub(crate) fn body_end_may_be_reached(ta: &crate::TypedAst) -> bool {
41 !ta.exhaustive_switches.is_empty()
42}
43
44pub(crate) fn emit_type_error_throw(
48 emitter: &mut FunctionEmitter,
49 ctx: &CodegenCtx,
50 message: &str,
51) {
52 emit_builtin_error_throw(emitter, ctx, "TypeError#constructor", message);
53}
54
55pub(crate) fn emit_reference_error_throw(
58 emitter: &mut FunctionEmitter,
59 ctx: &CodegenCtx,
60 message: &str,
61) {
62 emit_builtin_error_throw(emitter, ctx, "ReferenceError#constructor", message);
63}
64
65fn emit_builtin_error_throw(
66 emitter: &mut FunctionEmitter,
67 ctx: &CodegenCtx,
68 constructor: &str,
69 message: &str,
70) {
71 if ctx
72 .latch(emit_const_string_by_text(emitter, ctx, message))
73 .is_none()
74 {
75 return;
76 }
77 let Some(new_idx) = ctx.require(
78 ctx.symbols.prelude_func_idx(constructor),
79 "built-in error constructor imported from prelude",
80 ) else {
81 return;
82 };
83 emit_no_error_options(emitter);
84 emitter.instruction(Instruction::Call(new_idx));
85 emit_error_throw(emitter, ctx);
86}
87
88pub(crate) fn emit_no_error_options(emitter: &mut FunctionEmitter) {
91 emitter.instruction(Instruction::RefNull(wasm_encoder::HeapType::Abstract {
92 shared: false,
93 ty: wasm_encoder::AbstractHeapType::None,
94 }));
95}
96
97pub(crate) fn emit_error_throw(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
102 let Some(error_idx) = ctx.require(
103 ctx.symbols.intrinsic_type_indices().map(|i| i.error),
104 "error intrinsic registered",
105 ) else {
106 return;
107 };
108 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(error_idx)));
109 let Some(tag_idx) = ctx.require(ctx.symbols.error_tag_idx(), "error tag registered") else {
110 return;
111 };
112 emitter.instruction(Instruction::Throw(tag_idx));
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::TypedAst;
119 use crate::codegen::invariant_tests::{assert_internal, with_context};
120 use crate::codegen::{SymbolTable, tests::mock_symbols_with_intrinsics};
121
122 #[test]
123 fn missing_exception_metadata_is_a_compile_failure() {
124 for symbols in [SymbolTable::default(), mock_symbols_with_intrinsics()] {
125 with_context(&TypedAst::new(), &symbols, |ctx| {
126 emit_error_throw(&mut FunctionEmitter::new(ctx, &[]).unwrap(), ctx);
127 assert_internal(ctx.check_failure().unwrap_err());
128 });
129 }
130 }
131
132 #[test]
133 fn missing_type_error_constructor_is_a_compile_failure() {
134 let mut symbols = mock_symbols_with_intrinsics();
135 symbols.record_global(crate::mangle::prelude("string_vtable"), 0);
136 let mut strings = crate::codegen::StringPool::default();
137 strings.intern_text("test message");
138 with_context(&TypedAst::new(), &symbols, |ctx| {
139 let ctx = CodegenCtx {
140 strings: &strings,
141 failure: std::cell::Cell::new(None),
142 validator_steps_left: std::cell::Cell::new(ctx.validator_steps_left.get()),
143 validator_root: std::cell::Cell::new(None),
144 check_is_standalone: std::cell::Cell::new(false),
145 ..*ctx
146 };
147 emit_type_error_throw(
148 &mut FunctionEmitter::new(&ctx, &[]).unwrap(),
149 &ctx,
150 "test message",
151 );
152 assert_internal(ctx.check_failure().unwrap_err());
153 });
154 }
155}