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 emitter.instruction(Instruction::Call(new_idx));
84 emit_error_throw(emitter, ctx);
85}
86
87pub(crate) fn emit_error_throw(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
92 let Some(error_idx) = ctx.require(
93 ctx.symbols.intrinsic_type_indices().map(|i| i.error),
94 "error intrinsic registered",
95 ) else {
96 return;
97 };
98 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(error_idx)));
99 let Some(tag_idx) = ctx.require(ctx.symbols.error_tag_idx(), "error tag registered") else {
100 return;
101 };
102 emitter.instruction(Instruction::Throw(tag_idx));
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::TypedAst;
109 use crate::codegen::invariant_tests::{assert_internal, with_context};
110 use crate::codegen::{SymbolTable, tests::mock_symbols_with_intrinsics};
111
112 #[test]
113 fn missing_exception_metadata_is_a_compile_failure() {
114 for symbols in [SymbolTable::default(), mock_symbols_with_intrinsics()] {
115 with_context(&TypedAst::new(), &symbols, |ctx| {
116 emit_error_throw(&mut FunctionEmitter::new(ctx, &[]).unwrap(), ctx);
117 assert_internal(ctx.check_failure().unwrap_err());
118 });
119 }
120 }
121
122 #[test]
123 fn missing_type_error_constructor_is_a_compile_failure() {
124 let mut symbols = mock_symbols_with_intrinsics();
125 symbols.record_global(crate::mangle::prelude("string_vtable"), 0);
126 let mut strings = crate::codegen::StringPool::default();
127 strings.intern_text("test message");
128 with_context(&TypedAst::new(), &symbols, |ctx| {
129 let ctx = CodegenCtx {
130 strings: &strings,
131 failure: std::cell::Cell::new(None),
132 validator_steps_left: std::cell::Cell::new(ctx.validator_steps_left.get()),
133 validator_root: std::cell::Cell::new(None),
134 check_is_standalone: std::cell::Cell::new(false),
135 ..*ctx
136 };
137 emit_type_error_throw(
138 &mut FunctionEmitter::new(&ctx, &[]).unwrap(),
139 &ctx,
140 "test message",
141 );
142 assert_internal(ctx.check_failure().unwrap_err());
143 });
144 }
145}