use wasm_encoder::{HeapType, Instruction};
use crate::codegen::CodegenCtx;
use crate::codegen::function_emitter::{FunctionEmitter, emit_const_string_by_text};
pub const NON_NULL_ASSERT_MESSAGE: &str = "non-null assertion failed: value is null or undefined";
pub const READ_ONLY_PROPERTY_MESSAGE: &str =
"cannot assign to a property backed by a getter with no setter";
pub const MISSING_RETURN_VALUE_MESSAGE: &str =
"function ended without returning a value: no `case` of an exhaustive `switch` matched";
pub(crate) fn body_end_may_be_reached(ta: &crate::TypedAst) -> bool {
!ta.exhaustive_switches.is_empty()
}
pub(crate) fn emit_type_error_throw(
emitter: &mut FunctionEmitter,
ctx: &CodegenCtx,
message: &str,
) {
emit_builtin_error_throw(emitter, ctx, "TypeError#constructor", message);
}
pub(crate) fn emit_reference_error_throw(
emitter: &mut FunctionEmitter,
ctx: &CodegenCtx,
message: &str,
) {
emit_builtin_error_throw(emitter, ctx, "ReferenceError#constructor", message);
}
fn emit_builtin_error_throw(
emitter: &mut FunctionEmitter,
ctx: &CodegenCtx,
constructor: &str,
message: &str,
) {
if ctx
.latch(emit_const_string_by_text(emitter, ctx, message))
.is_none()
{
return;
}
let Some(new_idx) = ctx.require(
ctx.symbols.prelude_func_idx(constructor),
"built-in error constructor imported from prelude",
) else {
return;
};
emitter.instruction(Instruction::Call(new_idx));
emit_error_throw(emitter, ctx);
}
pub(crate) fn emit_error_throw(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
let Some(error_idx) = ctx.require(
ctx.symbols.intrinsic_type_indices().map(|i| i.error),
"error intrinsic registered",
) else {
return;
};
emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(error_idx)));
let Some(tag_idx) = ctx.require(ctx.symbols.error_tag_idx(), "error tag registered") else {
return;
};
emitter.instruction(Instruction::Throw(tag_idx));
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TypedAst;
use crate::codegen::invariant_tests::{assert_internal, with_context};
use crate::codegen::{SymbolTable, tests::mock_symbols_with_intrinsics};
#[test]
fn missing_exception_metadata_is_a_compile_failure() {
for symbols in [SymbolTable::default(), mock_symbols_with_intrinsics()] {
with_context(&TypedAst::new(), &symbols, |ctx| {
emit_error_throw(&mut FunctionEmitter::new(ctx, &[]).unwrap(), ctx);
assert_internal(ctx.check_failure().unwrap_err());
});
}
}
#[test]
fn missing_type_error_constructor_is_a_compile_failure() {
let mut symbols = mock_symbols_with_intrinsics();
symbols.record_global(crate::mangle::prelude("string_vtable"), 0);
let mut strings = crate::codegen::StringPool::default();
strings.intern_text("test message");
with_context(&TypedAst::new(), &symbols, |ctx| {
let ctx = CodegenCtx {
strings: &strings,
failure: std::cell::Cell::new(None),
validator_steps_left: std::cell::Cell::new(ctx.validator_steps_left.get()),
validator_root: std::cell::Cell::new(None),
check_is_standalone: std::cell::Cell::new(false),
..*ctx
};
emit_type_error_throw(
&mut FunctionEmitter::new(&ctx, &[]).unwrap(),
&ctx,
"test message",
);
assert_internal(ctx.check_failure().unwrap_err());
});
}
}