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
65pub(crate) fn emit_missing_value_error_throw(
68 emitter: &mut FunctionEmitter,
69 ctx: &CodegenCtx,
70 message: &str,
71) {
72 emit_builtin_error_throw(emitter, ctx, "MissingValueError#constructor", message);
73}
74
75fn emit_builtin_error_throw(
76 emitter: &mut FunctionEmitter,
77 ctx: &CodegenCtx,
78 constructor: &str,
79 message: &str,
80) {
81 if ctx
82 .latch(emit_const_string_by_text(emitter, ctx, message))
83 .is_none()
84 {
85 return;
86 }
87 let Some(new_idx) = ctx.require(
88 ctx.symbols.prelude_func_idx(constructor),
89 "built-in error constructor imported from prelude",
90 ) else {
91 return;
92 };
93 emit_no_error_options(emitter);
94 emitter.instruction(Instruction::Call(new_idx));
95 emit_error_throw(emitter, ctx);
96}
97
98pub(crate) fn emit_no_error_options(emitter: &mut FunctionEmitter) {
101 emitter.instruction(Instruction::RefNull(wasm_encoder::HeapType::Abstract {
102 shared: false,
103 ty: wasm_encoder::AbstractHeapType::None,
104 }));
105}
106
107pub(crate) fn emit_error_throw(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
112 let Some(error_idx) = ctx.require(
113 ctx.symbols.intrinsic_type_indices().map(|i| i.error),
114 "error intrinsic registered",
115 ) else {
116 return;
117 };
118 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(error_idx)));
119 let Some(tag_idx) = ctx.require(ctx.symbols.error_tag_idx(), "error tag registered") else {
120 return;
121 };
122 emitter.instruction(Instruction::Throw(tag_idx));
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::TypedAst;
129 use crate::codegen::invariant_tests::{assert_internal, with_context};
130 use crate::codegen::{SymbolTable, tests::mock_symbols_with_intrinsics};
131
132 #[test]
133 fn missing_exception_metadata_is_a_compile_failure() {
134 for symbols in [SymbolTable::default(), mock_symbols_with_intrinsics()] {
135 with_context(&TypedAst::new(), &symbols, |ctx| {
136 emit_error_throw(&mut FunctionEmitter::new(ctx, &[]).unwrap(), ctx);
137 assert_internal(ctx.check_failure().unwrap_err());
138 });
139 }
140 }
141
142 #[test]
143 fn missing_type_error_constructor_is_a_compile_failure() {
144 let mut symbols = mock_symbols_with_intrinsics();
145 symbols.record_global(crate::mangle::prelude("string_vtable"), 0);
146 let mut strings = crate::codegen::StringPool::default();
147 strings.intern_text("test message");
148 with_context(&TypedAst::new(), &symbols, |ctx| {
149 let ctx = CodegenCtx {
150 strings: &strings,
151 failure: std::cell::Cell::new(None),
152 validator_steps_left: std::cell::Cell::new(ctx.validator_steps_left.get()),
153 validator_root: std::cell::Cell::new(None),
154 check_is_standalone: std::cell::Cell::new(false),
155 ..*ctx
156 };
157 emit_type_error_throw(
158 &mut FunctionEmitter::new(&ctx, &[]).unwrap(),
159 &ctx,
160 "test message",
161 );
162 assert_internal(ctx.check_failure().unwrap_err());
163 });
164 }
165}