1use wasm_encoder::{BlockType, Function, HeapType, Instruction, RefType, ValType};
5
6use crate::codegen::function_emitter::expr::emit_expr;
7use crate::codegen::function_emitter::json::emit_inline_const_raw_string;
8use crate::codegen::function_emitter::{FunctionEmitter, cast};
9use crate::codegen::{CodegenCtx, cast_diagnostics as diagnostic};
10use crate::{ExprId, Ident, Span, Type};
11
12pub const TYPE_TAG_STRINGS: &[&str] = &[
13 "string",
14 "number",
15 "boolean",
16 "function",
17 "object",
18 "null",
19 "undefined",
20];
21pub(super) const RECURSIVE_VALIDATOR_CAPACITY: i32 = 1024;
22
23#[derive(Clone, Copy)]
24struct ValidatorState {
25 visited: u32,
26 validator_ids: u32,
27 depth: u32,
28}
29
30#[derive(Clone, Copy)]
31struct FieldConformanceLocals {
32 object: u32,
33 field: u32,
34}
35
36#[derive(Clone, Copy)]
37struct CollectionStorage {
38 backing: u32,
39 backing_type: u32,
40 order_len_field: u32,
41 order: u32,
42}
43
44pub fn error_prefix(target_ty: &Type) -> String {
45 format!("type mismatch: expected {target_ty}, got ")
46}
47
48pub fn emit_cast(
57 emitter: &mut FunctionEmitter,
58 ctx: &CodegenCtx,
59 value: ExprId,
60 target_ty: &Type,
61 check: Option<&Type>,
62 cast_span: crate::Span,
63) -> Result<(), crate::compiler_error::CompilerFailure> {
64 let source_ty = ctx
65 .ta
66 .try_expr(value)
67 .map_err(crate::codegen::arena_failure)?
68 .ty
69 .clone();
70
71 if matches!(target_ty.peel(), Type::Unknown) {
73 emit_expr(emitter, ctx, value)?;
74 cast::emit_box(emitter, ctx, &source_ty)?;
75 return Ok(());
76 }
77
78 let Some(check_shape) = check else {
79 if &source_ty
80 != ctx
81 .ta
82 .source_type(value)
83 .map_err(crate::codegen::arena_failure)?
84 {
85 emit_expr(emitter, ctx, value)?;
86 let enclosing = map_cast_span(emitter, cast_span);
87 emit_checked_cast_on_stack(emitter, ctx, &source_ty, target_ty)?;
88 restore_span(emitter, enclosing);
89 return Ok(());
90 }
91 emit_expr(emitter, ctx, value)?;
93 cast::emit_box(emitter, ctx, &source_ty)?;
94 cast::emit_cast_to(emitter, ctx, target_ty)?;
95 return Ok(());
96 };
97
98 let mark = emitter.single_evaluation_mark();
99 let key = capture_session_key(emitter, ctx, value)?;
100 emit_expr(emitter, ctx, value)?;
101 emitter.end_single_evaluations(mark)?;
102 let enclosing = map_cast_span(emitter, cast_span);
103 cast::emit_box(emitter, ctx, &source_ty)?;
104
105 let object_idx = ctx
106 .symbols
107 .intrinsic_type_indices()
108 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
109 .object;
110 let scratch_ty = ValType::Ref(RefType {
111 nullable: true,
112 heap_type: HeapType::Concrete(object_idx),
113 });
114 let scratch = emitter.add_anonymous_local(scratch_ty)?;
115 emitter.instruction(Instruction::LocalSet(scratch));
116 let previous_diagnostic = diagnostic::begin(emitter, ctx);
117 diagnostic::session_key(emitter, key);
118
119 emit_structural_test(emitter, ctx, scratch, check_shape, target_ty)?;
120
121 let target_val = ctx.symbols.value_type(target_ty)?;
122 let block_ty = BlockType::Result(target_val);
123 emitter.emit_if(block_ty);
124 emitter.instruction(Instruction::LocalGet(scratch));
125 cast::emit_cast_to(emitter, ctx, target_ty)?;
126 emitter.emit_else();
127 emit_cast_throw(emitter, ctx, scratch, target_ty);
128 emitter.emit_end();
129 emitter.cast_diagnostic = previous_diagnostic;
130 restore_span(emitter, enclosing);
131 Ok(())
132}
133
134fn map_cast_span(emitter: &mut FunctionEmitter, cast_span: crate::Span) -> Option<crate::Span> {
136 let enclosing = emitter.last_mapped_span();
137 emitter.record_span(cast_span);
138 enclosing
139}
140
141fn restore_span(emitter: &mut FunctionEmitter, enclosing: Option<crate::Span>) {
142 if let Some(span) = enclosing {
143 emitter.record_span(span);
144 }
145}
146
147fn capture_session_key(
148 emitter: &mut FunctionEmitter,
149 ctx: &CodegenCtx,
150 value: ExprId,
151) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
152 let crate::TypedExprKind::GenericCall { mangled, args, .. } = &ctx
153 .ta
154 .try_expr(value)
155 .map_err(crate::codegen::arena_failure)?
156 .kind
157 else {
158 return Ok(None);
159 };
160 if !crate::stdlib::session::declaration::is_checked_get(mangled) {
161 return Ok(None);
162 }
163 let key = match args.first() {
164 Some(value) => value,
165 None => return Ok(None),
166 }
167 .expr;
168 emit_expr(emitter, ctx, key)?;
169 let slot = emitter.add_anonymous_local(
170 ctx.symbols.value_type(
171 &ctx.ta
172 .try_expr(key)
173 .map_err(crate::codegen::arena_failure)?
174 .ty,
175 )?,
176 )?;
177 emitter.instruction(Instruction::LocalSet(slot));
178 emitter.record_single_evaluation(key, slot)?;
179 Ok(Some(slot))
180}
181
182pub fn emit_non_null_assert(
185 emitter: &mut FunctionEmitter,
186 ctx: &CodegenCtx,
187 value: ExprId,
188 target_ty: &Type,
189) -> Result<(), crate::compiler_error::CompilerFailure> {
190 let source_ty = ctx
191 .ta
192 .try_expr(value)
193 .map_err(crate::codegen::arena_failure)?
194 .ty
195 .clone();
196 emit_expr(emitter, ctx, value)?;
197 emit_non_null_assert_on_stack(emitter, ctx, &source_ty, target_ty)?;
198 Ok(())
199}
200
201pub fn emit_non_null_assert_on_stack(
205 emitter: &mut FunctionEmitter,
206 ctx: &CodegenCtx,
207 source_ty: &Type,
208 target_ty: &Type,
209) -> Result<(), crate::compiler_error::CompilerFailure> {
210 cast::emit_box(emitter, ctx, source_ty)?;
211
212 let object_idx = object_idx_of(ctx)?;
213 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx))?;
214 emitter.instruction(Instruction::LocalSet(scratch));
215
216 emitter.instruction(Instruction::LocalGet(scratch));
217 crate::codegen::function_emitter::expr::emit_is_nullish(emitter, ctx)?;
218 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(target_ty)?));
219 crate::codegen::throw::emit_type_error_throw(
220 emitter,
221 ctx,
222 crate::codegen::throw::NON_NULL_ASSERT_MESSAGE,
223 );
224 emitter.emit_else();
225 emitter.instruction(Instruction::LocalGet(scratch));
226 cast::emit_cast_to(emitter, ctx, target_ty)?;
227 emitter.emit_end();
228
229 Ok(())
230}
231
232pub(super) fn emit_structural_test(
246 emitter: &mut FunctionEmitter,
247 ctx: &CodegenCtx,
248 value_local: u32,
249 ty: &Type,
250 named: &Type,
251) -> Result<(), crate::compiler_error::CompilerFailure> {
252 ctx.checking(named, || {
253 emit_structural_test_inner(
254 emitter,
255 ctx,
256 value_local,
257 ty,
258 &mut std::collections::BTreeSet::new(),
259 None,
260 )?;
261 ctx.check_function_limits(emitter)
264 })
265}
266
267fn emit_structural_test_inner(
268 emitter: &mut FunctionEmitter,
269 ctx: &CodegenCtx,
270 value_local: u32,
271 ty: &Type,
272 interface_stack: &mut std::collections::BTreeSet<Type>,
273 validator_state: Option<ValidatorState>,
274) -> Result<(), crate::compiler_error::CompilerFailure> {
275 ctx.charge_validator_step(emitter)?;
276 let checkpoint = diagnostic::checkpoint(emitter, ctx);
277 let i32_block = BlockType::Result(ValType::I32);
278 match ty.lowered() {
279 Type::Union(members) => {
280 let best = diagnostic::union_start(emitter, ctx);
281 let mut first = true;
282 for m in members {
283 diagnostic::union_next(emitter, ctx);
284 emit_structural_test_inner(
285 emitter,
286 ctx,
287 value_local,
288 m,
289 interface_stack,
290 validator_state,
291 )?;
292 diagnostic::union_keep(emitter, ctx, best);
293 if first {
294 first = false;
295 } else {
296 emitter.instruction(Instruction::I32Or);
297 }
298 }
299 if first {
300 emitter.instruction(Instruction::I32Const(0));
301 }
302 diagnostic::union_end(emitter, best, checkpoint);
303 }
304 Type::Null => {
305 emitter.instruction(Instruction::LocalGet(value_local));
306 emitter.instruction(Instruction::RefIsNull);
307 }
308 Type::Undefined | Type::Void => {
309 emitter.instruction(Instruction::LocalGet(value_local));
310 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
311 }
312 Type::Unknown if ty.is_empty_object() => {
314 emitter.instruction(Instruction::LocalGet(value_local));
315 emitter.instruction(Instruction::RefIsNull);
316 emitter.instruction(Instruction::LocalGet(value_local));
317 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
318 emitter.instruction(Instruction::I32Or);
319 emitter.instruction(Instruction::I32Eqz);
320 }
321 Type::Unknown => emitter.instruction(Instruction::I32Const(1)),
322 Type::Number => emit_ref_test(emitter, value_local, boxed_number_idx(ctx)?),
323 Type::Boolean => emit_ref_test(emitter, value_local, boxed_boolean_idx(ctx)?),
324 Type::String => emit_ref_test(emitter, value_local, string_idx(ctx)?),
325 Type::NumberEnum { .. } | Type::StringEnum { .. } => {
326 if let Some(crate::FieldNarrowingTest::Shape(members)) =
327 ctx.ta.runtime_type_tests.get(ty.lowered())
328 && members.lowered() != ty.lowered()
329 {
330 emit_structural_test_inner(
331 emitter,
332 ctx,
333 value_local,
334 members,
335 interface_stack,
336 validator_state,
337 )?;
338 } else {
339 emit_representation_test(emitter, ctx, value_local, ty)?;
340 }
341 }
342 Type::BigInt => emit_ref_test(
343 emitter,
344 value_local,
345 ctx.symbols
346 .bigint_type_idx()
347 .ok_or_else(|| crate::codegen::internal_failure("$BigInt intrinsic registered"))?,
348 ),
349 Type::Uint8Array => emit_ref_test(
350 emitter,
351 value_local,
352 ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
353 crate::codegen::internal_failure("$Uint8Array intrinsic registered")
354 })?,
355 ),
356 Type::Function { has_rest, .. } => {
357 emit_function_structural_test(emitter, ctx, value_local, ty, *has_rest)?;
358 }
359 Type::TypeVar(name) | Type::GenericParam { name, .. } => {
362 super::runtime_descriptors::test_parameter(emitter, ctx, name, value_local)?;
363 }
364 Type::NumberLiteral(n) => {
365 let boxed = boxed_number_idx(ctx)?;
366 emitter.instruction(Instruction::LocalGet(value_local));
367 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
368 emitter.emit_if(i32_block);
369 emitter.instruction(Instruction::LocalGet(value_local));
370 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed)));
371 emitter.instruction(Instruction::StructGet {
372 struct_type_index: boxed,
373 field_index: 1,
374 });
375 emitter.instruction(Instruction::F64Const(n.0.into()));
376 emitter.instruction(Instruction::F64Eq);
377 emitter.emit_else();
378 emitter.instruction(Instruction::I32Const(0));
379 emitter.emit_end();
380 }
381 Type::BooleanLiteral(value) => {
382 let boxed = boxed_boolean_idx(ctx)?;
383 emitter.instruction(Instruction::LocalGet(value_local));
384 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
385 emitter.emit_if(i32_block);
386 emitter.instruction(Instruction::LocalGet(value_local));
387 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed)));
388 emitter.instruction(Instruction::StructGet {
389 struct_type_index: boxed,
390 field_index: 1,
391 });
392 emitter.instruction(Instruction::I32Const(i32::from(*value)));
393 emitter.instruction(Instruction::I32Eq);
394 emitter.emit_else();
395 emitter.instruction(Instruction::I32Const(0));
396 emitter.emit_end();
397 }
398 Type::StringLiteral(s) => {
399 let str_idx = string_idx(ctx)?;
400 let intr = ctx
401 .symbols
402 .intrinsic_type_indices()
403 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
404 emitter.instruction(Instruction::LocalGet(value_local));
405 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(str_idx)));
406 emitter.emit_if(i32_block);
407 emitter.instruction(Instruction::LocalGet(value_local));
408 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(str_idx)));
409 emitter.instruction(Instruction::StructGet {
410 struct_type_index: str_idx,
411 field_index: 1,
412 });
413 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
414 nullable: false,
415 heap_type: HeapType::Concrete(intr.raw_string),
416 }))?;
417 emitter.instruction(Instruction::LocalSet(raw_local));
418 crate::codegen::function_emitter::json::emit_raw_string_matches_literal(
419 emitter, intr, raw_local, s,
420 );
421 emitter.emit_else();
422 emitter.instruction(Instruction::I32Const(0));
423 emitter.emit_end();
424 }
425 Type::Object { fields, index } => {
426 let shape_idx = ctx.symbols.object_shape_type_idx().ok_or_else(|| {
427 crate::codegen::internal_failure("$ObjectShape intrinsic registered")
428 })?;
429 emitter.instruction(Instruction::LocalGet(value_local));
430 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(shape_idx)));
431 emitter.emit_if(i32_block);
432 let obj_local = emitter.add_anonymous_local(ValType::Ref(RefType {
433 nullable: false,
434 heap_type: HeapType::Concrete(shape_idx),
435 }))?;
436 emitter.instruction(Instruction::LocalGet(value_local));
437 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(shape_idx)));
438 emitter.instruction(Instruction::LocalSet(obj_local));
439 let field_local =
440 emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
441 emitter.instruction(Instruction::I32Const(1)); for (fname, f) in fields {
443 emit_field_conformance(
444 emitter,
445 ctx,
446 FieldConformanceLocals {
447 object: obj_local,
448 field: field_local,
449 },
450 fname,
451 f,
452 interface_stack,
453 validator_state,
454 )?;
455 emitter.instruction(Instruction::I32And);
456 }
457 if let Some(index) = index {
458 emit_index_conformance(
459 emitter,
460 ctx,
461 obj_local,
462 index,
463 interface_stack,
464 validator_state,
465 )?;
466 emitter.instruction(Instruction::I32And);
467 }
468 emitter.emit_else();
469 emitter.instruction(Instruction::I32Const(0));
470 emitter.emit_end();
471 }
472 Type::Array(elem) => {
473 let intr = ctx
474 .symbols
475 .intrinsic_type_indices()
476 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
477 let array_idx = ctx
478 .symbols
479 .array_type_idx()
480 .ok_or_else(|| crate::codegen::internal_failure("$Array intrinsic registered"))?;
481 emitter.instruction(Instruction::LocalGet(value_local));
482 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(array_idx)));
483 emitter.emit_if(i32_block);
484 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
485 nullable: false,
486 heap_type: HeapType::Concrete(intr.raw_array),
487 }))?;
488 emitter.instruction(Instruction::LocalGet(value_local));
489 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(array_idx)));
490 emitter.instruction(Instruction::StructGet {
491 struct_type_index: array_idx,
492 field_index: 1,
493 });
494 emitter.instruction(Instruction::LocalSet(raw_local));
495 let acc = emitter.add_anonymous_local(ValType::I32)?;
496 let i = emitter.add_anonymous_local(ValType::I32)?;
497 let len = emitter.add_anonymous_local(ValType::I32)?;
498 let elem_local = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
499 emitter.instruction(Instruction::I32Const(1));
500 emitter.instruction(Instruction::LocalSet(acc));
501 emitter.instruction(Instruction::I32Const(0));
502 emitter.instruction(Instruction::LocalSet(i));
503 emitter.instruction(Instruction::LocalGet(value_local));
504 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(array_idx)));
505 emitter.instruction(Instruction::StructGet {
506 struct_type_index: array_idx,
507 field_index: 2,
508 });
509 emitter.instruction(Instruction::LocalSet(len));
510 emitter.emit_block(BlockType::Empty);
511 emitter.emit_loop(BlockType::Empty);
512 emitter.instruction(Instruction::LocalGet(i));
513 emitter.instruction(Instruction::LocalGet(len));
514 emitter.instruction(Instruction::I32GeU);
515 emitter.instruction(Instruction::BrIf(1));
516 emitter.instruction(Instruction::LocalGet(raw_local));
517 emitter.instruction(Instruction::LocalGet(i));
518 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
519 emitter.instruction(Instruction::LocalSet(elem_local));
520 emitter.instruction(Instruction::LocalGet(acc));
521 let parent_path = diagnostic::index(emitter, ctx, i);
522 emit_structural_test_inner(
523 emitter,
524 ctx,
525 elem_local,
526 elem,
527 interface_stack,
528 validator_state,
529 )?;
530 diagnostic::pop(emitter, parent_path);
531 emitter.instruction(Instruction::I32And);
532 emitter.instruction(Instruction::LocalSet(acc));
533 emitter.instruction(Instruction::LocalGet(i));
534 emitter.instruction(Instruction::I32Const(1));
535 emitter.instruction(Instruction::I32Add);
536 emitter.instruction(Instruction::LocalSet(i));
537 emitter.instruction(Instruction::Br(0));
538 emitter.emit_end();
539 emitter.emit_end();
540 emitter.instruction(Instruction::LocalGet(acc));
541 emitter.emit_else();
542 emitter.instruction(Instruction::I32Const(0));
543 emitter.emit_end();
544 }
545 Type::Tuple(elems) => {
546 let intr = ctx
547 .symbols
548 .intrinsic_type_indices()
549 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
550 let array_idx = ctx
551 .symbols
552 .array_type_idx()
553 .ok_or_else(|| crate::codegen::internal_failure("$Array intrinsic registered"))?;
554 emitter.instruction(Instruction::LocalGet(value_local));
555 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(array_idx)));
556 emitter.emit_if(i32_block);
557 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
558 nullable: false,
559 heap_type: HeapType::Concrete(intr.raw_array),
560 }))?;
561 emitter.instruction(Instruction::LocalGet(value_local));
562 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(array_idx)));
563 emitter.instruction(Instruction::StructGet {
564 struct_type_index: array_idx,
565 field_index: 1,
566 });
567 emitter.instruction(Instruction::LocalSet(raw_local));
568 let length = emitter.add_anonymous_local(ValType::I32)?;
570 emitter.instruction(Instruction::LocalGet(value_local));
571 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(array_idx)));
572 emitter.instruction(Instruction::StructGet {
573 struct_type_index: array_idx,
574 field_index: 2,
575 });
576 emitter.instruction(Instruction::LocalTee(length));
577 emitter.instruction(Instruction::I32Const(
578 crate::codegen::wasm_u32(elems.len())?.cast_signed(),
579 ));
580 emitter.instruction(Instruction::I32LeU);
581 emitter.instruction(Instruction::LocalGet(length));
582 emitter.instruction(Instruction::I32Const(
583 crate::codegen::wasm_u32(elems.required_len())?.cast_signed(),
584 ));
585 emitter.instruction(Instruction::I32GeU);
586 emitter.instruction(Instruction::I32And);
587 emitter.emit_if(i32_block);
588 let elem_local = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
589 emitter.instruction(Instruction::I32Const(1));
590 for (idx, et) in elems.iter().enumerate() {
591 if idx >= elems.required_len() {
592 emitter.instruction(Instruction::LocalGet(length));
593 emitter.instruction(Instruction::I32Const(
594 crate::codegen::wasm_u32(idx)?.cast_signed(),
595 ));
596 emitter.instruction(Instruction::I32LeU);
597 emitter.emit_if(i32_block);
598 emitter.instruction(Instruction::I32Const(1));
599 emitter.emit_else();
600 }
601 let index = emitter.add_anonymous_local(ValType::I32)?;
602 emitter.instruction(Instruction::I32Const(
603 crate::codegen::wasm_u32(idx)?.cast_signed(),
604 ));
605 emitter.instruction(Instruction::LocalSet(index));
606 let parent_path = diagnostic::index(emitter, ctx, index);
607 emitter.instruction(Instruction::LocalGet(raw_local));
608 emitter.instruction(Instruction::I32Const(
609 crate::codegen::wasm_u32(idx)?.cast_signed(),
610 ));
611 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
612 emitter.instruction(Instruction::LocalSet(elem_local));
613 emit_structural_test_inner(
614 emitter,
615 ctx,
616 elem_local,
617 et,
618 interface_stack,
619 validator_state,
620 )?;
621 diagnostic::pop(emitter, parent_path);
622 if idx >= elems.required_len() {
623 emitter.emit_end();
624 }
625 emitter.instruction(Instruction::I32And);
626 }
627 emitter.emit_else();
628 emitter.instruction(Instruction::I32Const(0));
629 emitter.emit_end();
630 emitter.emit_else();
631 emitter.instruction(Instruction::I32Const(0));
632 emitter.emit_end();
633 }
634 Type::AliasRef { .. } => {
637 let func_idx = ctx
638 .symbols
639 .runtime_validator_idx(ty.lowered())
640 .ok_or_else(|| {
641 crate::codegen::internal_failure(
642 "recursive runtime validator pre-allocated during discovery",
643 )
644 })?;
645 emitter.instruction(Instruction::LocalGet(value_local));
646 let visited = emit_validator_state(emitter, ctx, validator_state)?;
647 super::runtime_descriptors::validator_environment(emitter, ctx, ty)?;
648 emitter.instruction(Instruction::Call(func_idx));
649 diagnostic::load_recursive(emitter, ctx, visited);
650 }
651 Type::InterfaceRef { .. } => emit_interface_ref_test(
652 emitter,
653 ctx,
654 value_local,
655 ty,
656 interface_stack,
657 validator_state,
658 )?,
659 Type::ClassRef { mangled, .. } => {
664 let vtable_global = ctx
665 .symbols
666 .class_vtable_global_idx(mangled)
667 .ok_or_else(|| crate::codegen::internal_failure("class vtable global recorded"))?;
668 emitter.instruction(Instruction::LocalGet(value_local));
669 cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
670 if let Some(validator) = ctx.symbols.runtime_validator_idx(ty.lowered()) {
671 emitter.emit_if(BlockType::Result(ValType::I32));
672 emitter.instruction(Instruction::LocalGet(value_local));
673 let visited = emit_validator_state(emitter, ctx, validator_state)?;
674 super::runtime_descriptors::validator_environment(emitter, ctx, ty)?;
675 emitter.instruction(Instruction::Call(validator));
676 diagnostic::load_recursive(emitter, ctx, visited);
677 emitter.emit_else();
678 emitter.instruction(Instruction::I32Const(0));
679 emitter.emit_end();
680 }
681 }
682 _ => emitter.instruction(Instruction::I32Const(0)),
684 }
685 diagnostic::finish(emitter, ctx, checkpoint)?;
686 Ok(())
687}
688
689fn emit_function_structural_test(
696 emitter: &mut FunctionEmitter,
697 ctx: &CodegenCtx,
698 value_local: u32,
699 ty: &Type,
700 has_rest: bool,
701) -> Result<(), crate::compiler_error::CompilerFailure> {
702 let signature = crate::codegen::closures::classify(ty)?;
703 let closure_struct = ctx
704 .symbols
705 .closure_struct_type_idx(signature)
706 .ok_or_else(|| {
707 crate::codegen::internal_failure("closure signature registered during analysis")
708 })?;
709 let target = super::closure_coercions::DefaultsFitTarget::exact(signature, has_rest);
710 let returns_unknown = matches!(ty.lowered(), Type::Function { ret, .. } if matches!(ret.lowered(), Type::Unknown));
713 let target = if returns_unknown {
714 target.either_convention()
715 } else {
716 target
717 };
718 let fit = |emitter: &mut FunctionEmitter| {
719 super::closure_coercions::emit_defaults_fit(emitter, ctx, value_local, target)
720 };
721 emit_ref_test(emitter, value_local, closure_struct);
722 emitter.emit_if(BlockType::Result(ValType::I32));
723 super::closure_coercions::emit_may_have_argument_metadata(
724 emitter,
725 ctx,
726 value_local,
727 closure_struct,
728 )?;
729 emitter.emit_if(BlockType::Result(ValType::I32));
730 fit(emitter)?;
731 emitter.emit_else();
732 emitter.instruction(Instruction::I32Const(i32::from(!has_rest)));
733 emitter.emit_end();
734 emitter.emit_else();
735 if has_rest {
736 emitter.instruction(Instruction::I32Const(0));
737 } else {
738 fit(emitter)?;
739 }
740 emitter.emit_end();
741 Ok(())
742}
743
744fn emit_interface_ref_test(
745 emitter: &mut FunctionEmitter,
746 ctx: &CodegenCtx,
747 value_local: u32,
748 ty: &Type,
749 interface_stack: &mut std::collections::BTreeSet<Type>,
750 validator_state: Option<ValidatorState>,
751) -> Result<(), crate::compiler_error::CompilerFailure> {
752 let key = ty.lowered().clone();
753 if ctx.symbols.generic_runtime_validator(&key).is_some() {
754 emit_validator_or_representation(emitter, ctx, value_local, ty, &key, validator_state)?;
755 return Ok(());
756 }
757 let _: () = match ctx.ta.runtime_type_tests.get(&key) {
758 Some(crate::FieldNarrowingTest::Shape(shape)) => emit_structural_test_inner(
759 emitter,
760 ctx,
761 value_local,
762 shape,
763 interface_stack,
764 validator_state,
765 )?,
766 Some(crate::FieldNarrowingTest::Interface(test)) if interface_stack.insert(key.clone()) => {
767 emit_interface_test_inner(
768 emitter,
769 ctx,
770 value_local,
771 test,
772 interface_stack,
773 validator_state,
774 )?;
775 interface_stack.remove(&key);
776 }
777 _ => {
778 emit_validator_or_representation(emitter, ctx, value_local, ty, &key, validator_state)?;
779 }
780 };
781 Ok(())
782}
783
784fn emit_validator_or_representation(
785 emitter: &mut FunctionEmitter,
786 ctx: &CodegenCtx,
787 value_local: u32,
788 ty: &Type,
789 key: &Type,
790 validator_state: Option<ValidatorState>,
791) -> Result<(), crate::compiler_error::CompilerFailure> {
792 if let Some(func_idx) = ctx.symbols.runtime_validator_idx(key) {
793 emitter.instruction(Instruction::LocalGet(value_local));
794 let visited = emit_validator_state(emitter, ctx, validator_state)?;
795 super::runtime_descriptors::validator_environment(emitter, ctx, key)?;
796 emitter.instruction(Instruction::Call(func_idx));
797 diagnostic::load_recursive(emitter, ctx, visited);
798 } else {
799 emit_representation_test(emitter, ctx, value_local, ty)?;
800 };
801 Ok(())
802}
803
804pub(crate) fn emit_checked_cast_on_stack(
807 emitter: &mut FunctionEmitter,
808 ctx: &CodegenCtx,
809 source_ty: &Type,
810 target_ty: &Type,
811) -> Result<(), crate::compiler_error::CompilerFailure> {
812 cast::emit_box(emitter, ctx, source_ty)?;
813 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
814 emitter.instruction(Instruction::LocalSet(scratch));
815 let previous_diagnostic = emitter.cast_diagnostic.take();
816 if diagnostic::has_nested_paths(target_ty) {
817 diagnostic::begin(emitter, ctx);
818 }
819 match ctx.ta.runtime_type_tests.get(target_ty.lowered()) {
820 Some(crate::FieldNarrowingTest::Interface(test)) => {
821 emit_interface_test(emitter, ctx, scratch, target_ty, test)?;
822 }
823 Some(crate::FieldNarrowingTest::Shape(shape)) => {
824 emit_structural_test(emitter, ctx, scratch, shape, target_ty)?;
825 }
826 _ if matches!(target_ty.lowered(), Type::Function { .. }) => {
830 emit_structural_test(emitter, ctx, scratch, target_ty, target_ty)?;
831 }
832 _ => emit_target_test(emitter, ctx, scratch, target_ty)?,
833 }
834 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(target_ty)?));
835 emitter.instruction(Instruction::LocalGet(scratch));
836 cast::emit_cast_to(emitter, ctx, target_ty)?;
837 emitter.emit_else();
838 emit_cast_throw(emitter, ctx, scratch, target_ty);
839 emitter.emit_end();
840 emitter.cast_diagnostic = previous_diagnostic;
841 Ok(())
842}
843
844pub(crate) fn emit_checked_parameter_cast_on_stack(
850 emitter: &mut FunctionEmitter,
851 ctx: &CodegenCtx,
852 source_ty: &Type,
853 target_ty: &Type,
854) -> Result<(), crate::compiler_error::CompilerFailure> {
855 if !is_interface_parameter(target_ty) {
856 emit_checked_cast_on_stack(emitter, ctx, source_ty, target_ty)?;
857 return Ok(());
858 }
859
860 cast::emit_box(emitter, ctx, source_ty)?;
861 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
862 emitter.instruction(Instruction::LocalSet(scratch));
863 emit_representation_test(emitter, ctx, scratch, target_ty)?;
864 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(target_ty)?));
865 emitter.instruction(Instruction::LocalGet(scratch));
866 cast::emit_cast_to(emitter, ctx, target_ty)?;
867 emitter.emit_else();
868 emit_cast_throw(emitter, ctx, scratch, target_ty);
869 emitter.emit_end();
870 Ok(())
871}
872
873fn is_interface_parameter(ty: &Type) -> bool {
874 match ty.lowered() {
875 Type::InterfaceRef { .. } => true,
876 Type::Union(members) => {
877 let mut non_null = members
878 .iter()
879 .filter(|member| !matches!(member.lowered(), Type::Null | Type::Undefined));
880 non_null
881 .next()
882 .is_some_and(|member| matches!(member.lowered(), Type::InterfaceRef { .. }))
883 && non_null.next().is_none()
884 }
885 _ => false,
886 }
887}
888
889pub(crate) fn emit_operation_cast_on_stack(
892 emitter: &mut FunctionEmitter,
893 ctx: &CodegenCtx,
894 source_ty: &Type,
895 target_ty: &Type,
896) -> Result<(), crate::compiler_error::CompilerFailure> {
897 cast::emit_box(emitter, ctx, source_ty)?;
898 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
899 emitter.instruction(Instruction::LocalSet(scratch));
900 let previous_diagnostic = emitter.cast_diagnostic.take();
902 if matches!(target_ty.lowered(), Type::Function { .. }) {
903 let signature = crate::codegen::closures::classify(target_ty.lowered())?;
904 emit_representation_test(emitter, ctx, scratch, target_ty)?;
905 let opposite = crate::codegen::closures::ClosureSig {
906 is_void: !signature.is_void,
907 ..signature
908 };
909 if let Some(structure) = ctx.symbols.closure_struct_type_idx(opposite) {
910 emit_ref_test(emitter, scratch, structure);
911 emitter.instruction(Instruction::I32Or);
912 }
913 if matches!(
914 target_ty.lowered(),
915 Type::Function {
916 has_rest: false,
917 ..
918 }
919 ) {
920 emitter.emit_if(BlockType::Result(ValType::I32));
921 emitter.instruction(Instruction::I32Const(1));
922 emitter.emit_else();
923 super::closure_coercions::emit_defaults_fit(
924 emitter,
925 ctx,
926 scratch,
927 super::closure_coercions::DefaultsFitTarget::any_convention(signature.arity),
928 )?;
929 emitter.emit_end();
930 }
931 } else {
932 emit_target_test(emitter, ctx, scratch, target_ty)?;
933 }
934 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(target_ty)?));
935 emitter.instruction(Instruction::LocalGet(scratch));
936 cast::emit_cast_to(emitter, ctx, target_ty)?;
937 emitter.emit_else();
938 emit_cast_throw(emitter, ctx, scratch, target_ty);
939 emitter.emit_end();
940 emitter.cast_diagnostic = previous_diagnostic;
941 Ok(())
942}
943
944fn emit_target_test(
948 emitter: &mut FunctionEmitter,
949 ctx: &CodegenCtx,
950 value_local: u32,
951 target_ty: &Type,
952) -> Result<(), crate::compiler_error::CompilerFailure> {
953 if matches!(
954 ctx.symbols.value_type(target_ty)?,
955 ValType::F64 | ValType::I32
956 ) {
957 emit_structural_test(emitter, ctx, value_local, target_ty, target_ty)
958 } else {
959 emit_representation_test(emitter, ctx, value_local, target_ty)
960 }
961}
962
963fn emit_representation_test(
968 emitter: &mut FunctionEmitter,
969 ctx: &CodegenCtx,
970 value_local: u32,
971 target_ty: &Type,
972) -> Result<(), crate::compiler_error::CompilerFailure> {
973 let ValType::Ref(target) = ctx.symbols.value_type(target_ty)? else {
974 return Err(crate::codegen::internal_failure(
975 "representation test requires a reference type",
976 ));
977 };
978 let allows_null = cast::target_allows_null(ctx, target_ty)?;
979 if allows_null {
980 emitter.instruction(Instruction::LocalGet(value_local));
981 emitter.instruction(Instruction::RefIsNull);
982 emitter.emit_if(BlockType::Result(ValType::I32));
983 emitter.instruction(Instruction::I32Const(1));
984 emitter.emit_else();
985 }
986 emitter.instruction(Instruction::LocalGet(value_local));
987 emitter.instruction(Instruction::RefTestNonNull(target.heap_type));
988 if allows_null {
989 emitter.emit_end();
990 };
991 Ok(())
992}
993
994fn emit_field_conformance(
1014 emitter: &mut FunctionEmitter,
1015 ctx: &CodegenCtx,
1016 locals: FieldConformanceLocals,
1017 fname: &str,
1018 field: &crate::ObjectField,
1019 interface_stack: &mut std::collections::BTreeSet<Type>,
1020 validator_state: Option<ValidatorState>,
1021) -> Result<(), crate::compiler_error::CompilerFailure> {
1022 let checkpoint = diagnostic::checkpoint(emitter, ctx);
1023 let parent_path = diagnostic::field(emitter, ctx, fname);
1024 let i32_block = BlockType::Result(ValType::I32);
1025 let intrinsics = ctx
1026 .symbols
1027 .intrinsic_type_indices()
1028 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1029 let name_global = ctx
1030 .symbols
1031 .field_name_string_global_idx(fname)
1032 .ok_or_else(|| {
1033 crate::codegen::internal_failure(
1034 "per-name string global recorded for cast-target field",
1035 )
1036 })?;
1037 let index_local = emitter.add_anonymous_local(ValType::I32)?;
1038 crate::codegen::function_emitter::expr::emit_object_field_index_by_name(
1039 emitter,
1040 ctx,
1041 locals.object,
1042 name_global,
1043 );
1044 emitter.instruction(Instruction::LocalTee(index_local));
1045 emitter.instruction(Instruction::I32Const(0));
1046 emitter.instruction(Instruction::I32GeS);
1047 emitter.emit_if(i32_block);
1048 crate::codegen::function_emitter::expr::emit_field_slot_get(
1049 emitter,
1050 intrinsics,
1051 locals.object,
1052 index_local,
1053 );
1054 emitter.instruction(Instruction::LocalSet(locals.field));
1055 emit_structural_test_inner(
1056 emitter,
1057 ctx,
1058 locals.field,
1059 &field.ty,
1060 interface_stack,
1061 validator_state,
1062 )?;
1063 let may_be_absent = field.optional || admits_undefined(&field.ty);
1066 if may_be_absent {
1067 emitter.instruction(Instruction::LocalGet(locals.field));
1068 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
1069 emitter.instruction(Instruction::I32Or);
1070 } else {
1071 emitter.instruction(Instruction::LocalGet(locals.object));
1073 emitter.instruction(Instruction::StructGet {
1074 struct_type_index: intrinsics.object_shape,
1075 field_index: 1,
1076 });
1077 emitter.instruction(Instruction::LocalGet(index_local));
1078 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
1079 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
1080 emitter.instruction(Instruction::LocalGet(locals.field));
1081 crate::codegen::function_emitter::expr::emit_is_nullish(emitter, ctx)?;
1082 emitter.instruction(Instruction::I32Eqz);
1083 emitter.instruction(Instruction::I32Or);
1084 emitter.instruction(Instruction::I32And);
1085 }
1086 emitter.emit_else();
1087 let getter = crate::codegen::classes::accessor_getter_name(fname);
1088 if may_be_absent {
1089 emitter.instruction(Instruction::I32Const(1));
1091 } else if crate::codegen::function_emitter::expr::accessor_branch_emittable(ctx, &getter) {
1092 let _ = crate::codegen::function_emitter::expr::emit_is_accessor_backed(
1093 emitter,
1094 ctx,
1095 locals.object,
1096 &getter,
1097 crate::AccessorKind::Get,
1098 )?;
1099 } else {
1100 emitter.instruction(Instruction::I32Const(0));
1101 }
1102 emitter.emit_end();
1103 diagnostic::finish(emitter, ctx, checkpoint)?;
1104 diagnostic::pop(emitter, parent_path);
1105 Ok(())
1106}
1107
1108pub fn emit_narrowed_field_read(
1109 emitter: &mut FunctionEmitter,
1110 ctx: &CodegenCtx,
1111 check: &crate::FieldNarrowingCheck,
1112 result_ty: &Type,
1113) -> Result<(), crate::compiler_error::CompilerFailure> {
1114 emit_narrowed_field_read_as(emitter, ctx, check, result_ty, result_ty)?;
1115 Ok(())
1116}
1117
1118pub(crate) fn emit_narrowed_field_read_as(
1119 emitter: &mut FunctionEmitter,
1120 ctx: &CodegenCtx,
1121 check: &crate::FieldNarrowingCheck,
1122 result_ty: &Type,
1123 check_ty: &Type,
1124) -> Result<(), crate::compiler_error::CompilerFailure> {
1125 let result_val = ctx.symbols.value_type(result_ty)?;
1126 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
1127 emitter.instruction(Instruction::LocalSet(scratch));
1128 let runtime_test = ctx.ta.runtime_type_tests.get(check_ty.lowered());
1132 let test = if check
1133 .minimal_test_target
1134 .as_ref()
1135 .is_some_and(|target| target.lowered() == check_ty.lowered())
1136 {
1137 if matches!(check.test, crate::FieldNarrowingTest::NonNull) {
1138 &check.test
1139 } else {
1140 &crate::FieldNarrowingTest::Representation
1141 }
1142 } else {
1143 match runtime_test {
1144 Some(crate::FieldNarrowingTest::Representation)
1145 if !matches!(&check.test, crate::FieldNarrowingTest::Representation) =>
1146 {
1147 &check.test
1148 }
1149 Some(test) => test,
1150 None => &check.test,
1151 }
1152 };
1153 let previous_diagnostic = emitter.cast_diagnostic.take();
1156 let nested_check = match test {
1157 crate::FieldNarrowingTest::Shape(ty) => diagnostic::has_nested_paths(ty),
1158 crate::FieldNarrowingTest::Interface(_) => true,
1159 _ => false,
1160 };
1161 if nested_check {
1162 diagnostic::begin(emitter, ctx);
1163 }
1164 match test {
1165 crate::FieldNarrowingTest::NonNull => {
1171 if cast::target_allows_null(ctx, check_ty)? {
1172 emitter.instruction(Instruction::I32Const(1));
1173 } else {
1174 emit_is_non_null(emitter, scratch);
1175 }
1176 }
1177 crate::FieldNarrowingTest::Shape(shape) => {
1178 emit_structural_test(emitter, ctx, scratch, shape, check_ty)?;
1179 if !cast::target_allows_null(ctx, check_ty)? {
1183 emit_is_non_null(emitter, scratch);
1184 emitter.instruction(Instruction::I32And);
1185 }
1186 }
1187 crate::FieldNarrowingTest::Interface(test) => {
1188 emit_interface_test(emitter, ctx, scratch, check_ty, test)?;
1189 }
1190 crate::FieldNarrowingTest::Substituted => {
1191 emit_representation_test(emitter, ctx, scratch, check_ty)?;
1192 }
1193 crate::FieldNarrowingTest::Representation => {
1194 emit_representation_test(emitter, ctx, scratch, check_ty)?;
1195 }
1196 }
1197 emitter.emit_if(BlockType::Result(result_val));
1198 emitter.instruction(Instruction::LocalGet(scratch));
1199 cast::emit_cast_to(emitter, ctx, result_ty)?;
1200 emitter.emit_else();
1201 emit_inline_string(emitter, ctx, &check.message);
1202 diagnostic::append_failure(emitter, ctx);
1203 emit_type_error_from_message(emitter, ctx);
1204 emitter.emit_end();
1205 emitter.cast_diagnostic = previous_diagnostic;
1206 Ok(())
1207}
1208
1209fn emit_interface_test(
1211 emitter: &mut FunctionEmitter,
1212 ctx: &CodegenCtx,
1213 value_local: u32,
1214 ty: &Type,
1215 test: &crate::InterfaceNarrowingTest,
1216) -> Result<(), crate::compiler_error::CompilerFailure> {
1217 ctx.checking(ty, || {
1218 emit_interface_test_inner(
1219 emitter,
1220 ctx,
1221 value_local,
1222 test,
1223 &mut std::collections::BTreeSet::new(),
1224 None,
1225 )?;
1226 ctx.check_function_limits(emitter)
1227 })
1228}
1229
1230fn emit_interface_test_inner(
1231 emitter: &mut FunctionEmitter,
1232 ctx: &CodegenCtx,
1233 value_local: u32,
1234 test: &crate::InterfaceNarrowingTest,
1235 interface_stack: &mut std::collections::BTreeSet<Type>,
1236 validator_state: Option<ValidatorState>,
1237) -> Result<(), crate::compiler_error::CompilerFailure> {
1238 ctx.charge_validator_step(emitter)?;
1239 let checkpoint = diagnostic::checkpoint(emitter, ctx);
1240 if test.nullable || test.undefined {
1241 emitter.instruction(Instruction::LocalGet(value_local));
1242 if test.nullable && test.undefined {
1243 crate::codegen::function_emitter::expr::emit_is_nullish(emitter, ctx)?;
1244 } else if test.undefined {
1245 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
1246 } else {
1247 emitter.instruction(Instruction::RefIsNull);
1248 }
1249 emitter.emit_if(BlockType::Result(ValType::I32));
1250 emitter.instruction(Instruction::I32Const(1));
1251 emitter.emit_else();
1252 }
1253
1254 if !test.shape_allowed {
1255 emit_non_shape_interface_test(emitter, ctx, value_local, test, validator_state)?;
1256 if test.nullable || test.undefined {
1257 emitter.emit_end();
1258 }
1259 return Ok(());
1260 }
1261
1262 let shape_idx = ctx
1263 .symbols
1264 .object_shape_type_idx()
1265 .ok_or_else(|| crate::codegen::internal_failure("$ObjectShape intrinsic registered"))?;
1266 emitter.instruction(Instruction::LocalGet(value_local));
1267 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(shape_idx)));
1268 emitter.emit_if(BlockType::Result(ValType::I32));
1269 let object_local = emitter.add_anonymous_local(ValType::Ref(RefType {
1270 nullable: false,
1271 heap_type: HeapType::Concrete(shape_idx),
1272 }))?;
1273 emitter.instruction(Instruction::LocalGet(value_local));
1274 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(shape_idx)));
1275 emitter.instruction(Instruction::LocalSet(object_local));
1276 let field_local = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
1277 emitter.instruction(Instruction::I32Const(1));
1278 for (name, field) in &test.members {
1279 if test.methods.contains(name) {
1280 let parent_path = diagnostic::field(emitter, ctx, name);
1281 let name_global = ctx
1282 .symbols
1283 .field_name_string_global_idx(name)
1284 .ok_or_else(|| {
1285 crate::codegen::internal_failure("interface narrowing method name registered")
1286 })?;
1287 crate::codegen::function_emitter::expr::emit_object_field_read_by_name(
1288 emitter,
1289 ctx,
1290 object_local,
1291 name_global,
1292 );
1293 emitter.instruction(Instruction::LocalSet(field_local));
1294 emit_structural_test_inner(
1295 emitter,
1296 ctx,
1297 field_local,
1298 &field.ty,
1299 interface_stack,
1300 validator_state,
1301 )?;
1302 diagnostic::pop(emitter, parent_path);
1303 } else {
1304 emit_field_conformance(
1305 emitter,
1306 ctx,
1307 FieldConformanceLocals {
1308 object: object_local,
1309 field: field_local,
1310 },
1311 name,
1312 field,
1313 interface_stack,
1314 validator_state,
1315 )?;
1316 }
1317 emitter.instruction(Instruction::I32And);
1318 }
1319 if let Some(index) = &test.index {
1320 emit_index_conformance(
1321 emitter,
1322 ctx,
1323 object_local,
1324 index,
1325 interface_stack,
1326 validator_state,
1327 )?;
1328 emitter.instruction(Instruction::I32And);
1329 }
1330 emit_non_shape_interface_test(emitter, ctx, value_local, test, validator_state)?;
1335 emitter.instruction(Instruction::I32Or);
1336 emitter.emit_else();
1337 emit_non_shape_interface_test(emitter, ctx, value_local, test, validator_state)?;
1338 emitter.emit_end();
1339
1340 if test.nullable || test.undefined {
1341 emitter.emit_end();
1342 }
1343 diagnostic::finish(emitter, ctx, checkpoint)?;
1344 Ok(())
1345}
1346
1347fn emit_non_shape_interface_test(
1348 emitter: &mut FunctionEmitter,
1349 ctx: &CodegenCtx,
1350 value_local: u32,
1351 test: &crate::InterfaceNarrowingTest,
1352 validator_state: Option<ValidatorState>,
1353) -> Result<(), crate::compiler_error::CompilerFailure> {
1354 use crate::InterfaceCarrier;
1355
1356 let matches_any = emitter.add_anonymous_local(ValType::I32)?;
1357 emitter.instruction(Instruction::I32Const(0));
1358 emitter.instruction(Instruction::LocalSet(matches_any));
1359 for carrier in &test.non_shape_carriers {
1360 let intr = ctx
1361 .symbols
1362 .intrinsic_type_indices()
1363 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1364 match carrier {
1365 InterfaceCarrier::ArrayAny => emit_structural_test_inner(
1366 emitter,
1367 ctx,
1368 value_local,
1369 &Type::Array(Box::new(Type::Unknown)),
1370 &mut std::collections::BTreeSet::new(),
1371 validator_state,
1372 )?,
1373 InterfaceCarrier::Array(ty) => {
1374 emit_structural_test_inner(
1375 emitter,
1376 ctx,
1377 value_local,
1378 ty,
1379 &mut std::collections::BTreeSet::new(),
1380 validator_state,
1381 )?;
1382 }
1383 InterfaceCarrier::Number => emit_ref_test(emitter, value_local, boxed_number_idx(ctx)?),
1384 InterfaceCarrier::Boolean => {
1385 emit_ref_test(emitter, value_local, boxed_boolean_idx(ctx)?);
1386 }
1387 InterfaceCarrier::String => emit_ref_test(emitter, value_local, string_idx(ctx)?),
1388 InterfaceCarrier::BigInt => emit_ref_test(emitter, value_local, intr.bigint),
1389 InterfaceCarrier::Uint8Array => emit_ref_test(emitter, value_local, intr.uint8_array),
1390 InterfaceCarrier::MapAny => emit_ref_test(emitter, value_local, intr.map),
1391 InterfaceCarrier::Map(key, value) => {
1392 emit_map_carrier_test(emitter, ctx, value_local, key, value, validator_state)?;
1393 }
1394 InterfaceCarrier::SetAny => emit_ref_test(emitter, value_local, intr.set),
1395 InterfaceCarrier::Set(element) => {
1396 emit_set_carrier_test(emitter, ctx, value_local, element, validator_state)?;
1397 }
1398 InterfaceCarrier::RegExp => emit_ref_test(emitter, value_local, intr.regex),
1399 InterfaceCarrier::RegExpMatch => {
1400 emit_ref_test(emitter, value_local, intr.regex_match_box);
1401 }
1402 InterfaceCarrier::TemporalInstant => {
1403 emit_ref_test(emitter, value_local, intr.temporal_instant);
1404 }
1405 InterfaceCarrier::TemporalDuration => {
1406 emit_ref_test(emitter, value_local, intr.temporal_duration);
1407 }
1408 InterfaceCarrier::TemporalZonedDateTime => {
1409 emit_ref_test(emitter, value_local, intr.temporal_zdt);
1410 }
1411 InterfaceCarrier::TemporalPlainDate => {
1412 emit_ref_test(emitter, value_local, intr.temporal_plain_date);
1413 }
1414 InterfaceCarrier::TemporalPlainTime => {
1415 emit_ref_test(emitter, value_local, intr.temporal_plain_time);
1416 }
1417 InterfaceCarrier::TemporalPlainDateTime => {
1418 emit_ref_test(emitter, value_local, intr.temporal_plain_date_time);
1419 }
1420 InterfaceCarrier::TemporalPlainYearMonth => {
1421 emit_ref_test(emitter, value_local, intr.temporal_plain_year_month);
1422 }
1423 InterfaceCarrier::TemporalPlainMonthDay => {
1424 emit_ref_test(emitter, value_local, intr.temporal_plain_month_day);
1425 }
1426 InterfaceCarrier::ObjectShape => {
1427 emit_ref_test(emitter, value_local, intr.object_shape);
1428 }
1429 InterfaceCarrier::Url => emit_ref_test(emitter, value_local, intr.url),
1430 InterfaceCarrier::FsStat => emit_ref_test(emitter, value_local, intr.fs_stat),
1431 InterfaceCarrier::FsPeek => emit_ref_test(emitter, value_local, intr.fs_peek),
1432 InterfaceCarrier::FsDirEntry => emit_ref_test(emitter, value_local, intr.fs_dir_entry),
1433 InterfaceCarrier::FsInfo => emit_ref_test(emitter, value_local, intr.fs_info),
1434 InterfaceCarrier::FsMountInfo => {
1435 emit_ref_test(emitter, value_local, intr.fs_mount_info);
1436 }
1437 InterfaceCarrier::FsFileWriter => {
1438 emit_ref_test(emitter, value_local, intr.fs_file_writer);
1439 }
1440 InterfaceCarrier::HttpResponse => {
1441 emit_ref_test(emitter, value_local, intr.http_response);
1442 }
1443 InterfaceCarrier::HttpDownloadResult => {
1444 emit_ref_test(emitter, value_local, intr.http_download_result);
1445 }
1446 InterfaceCarrier::SessionEntry => {
1447 emit_ref_test(emitter, value_local, intr.session_entry);
1448 }
1449 InterfaceCarrier::SessionPage => emit_ref_test(emitter, value_local, intr.session_page),
1450 }
1451 emitter.instruction(Instruction::LocalGet(matches_any));
1452 emitter.instruction(Instruction::I32Or);
1453 emitter.instruction(Instruction::LocalSet(matches_any));
1454 }
1455 emitter.instruction(Instruction::LocalGet(matches_any));
1456 Ok(())
1457}
1458
1459fn emit_set_carrier_test(
1460 emitter: &mut FunctionEmitter,
1461 ctx: &CodegenCtx,
1462 value_local: u32,
1463 element: &Type,
1464 validator_state: Option<ValidatorState>,
1465) -> Result<(), crate::compiler_error::CompilerFailure> {
1466 let intr = ctx
1467 .symbols
1468 .intrinsic_type_indices()
1469 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1470 emit_ref_test(emitter, value_local, intr.set);
1471 emitter.emit_if(BlockType::Result(ValType::I32));
1472 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
1473 nullable: false,
1474 heap_type: HeapType::Concrete(intr.set),
1475 }))?;
1476 let elements = emitter.add_anonymous_local(ValType::Ref(RefType {
1477 nullable: false,
1478 heap_type: HeapType::Concrete(intr.raw_array),
1479 }))?;
1480 let order = emitter.add_anonymous_local(ValType::Ref(RefType {
1481 nullable: false,
1482 heap_type: HeapType::Concrete(intr.raw_index_array),
1483 }))?;
1484 emitter.instruction(Instruction::LocalGet(value_local));
1485 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(intr.set)));
1486 emitter.instruction(Instruction::LocalTee(backing));
1487 emitter.instruction(Instruction::StructGet {
1488 struct_type_index: intr.set,
1489 field_index: 1,
1490 });
1491 emitter.instruction(Instruction::LocalSet(elements));
1492 emitter.instruction(Instruction::LocalGet(backing));
1493 emitter.instruction(Instruction::StructGet {
1494 struct_type_index: intr.set,
1495 field_index: 3,
1496 });
1497 emitter.instruction(Instruction::LocalSet(order));
1498 emit_ordered_collection_members_test(
1499 emitter,
1500 ctx,
1501 CollectionStorage {
1502 backing,
1503 backing_type: intr.set,
1504 order_len_field: 4,
1505 order,
1506 },
1507 &[(elements, element)],
1508 validator_state,
1509 )?;
1510 emitter.emit_else();
1511 emitter.instruction(Instruction::I32Const(0));
1512 emitter.emit_end();
1513 Ok(())
1514}
1515
1516fn emit_map_carrier_test(
1517 emitter: &mut FunctionEmitter,
1518 ctx: &CodegenCtx,
1519 value_local: u32,
1520 key: &Type,
1521 value: &Type,
1522 validator_state: Option<ValidatorState>,
1523) -> Result<(), crate::compiler_error::CompilerFailure> {
1524 let intr = ctx
1525 .symbols
1526 .intrinsic_type_indices()
1527 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1528 emit_ref_test(emitter, value_local, intr.map);
1529 emitter.emit_if(BlockType::Result(ValType::I32));
1530 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
1531 nullable: false,
1532 heap_type: HeapType::Concrete(intr.map),
1533 }))?;
1534 let keys = emitter.add_anonymous_local(ValType::Ref(RefType {
1535 nullable: false,
1536 heap_type: HeapType::Concrete(intr.raw_array),
1537 }))?;
1538 let values = emitter.add_anonymous_local(ValType::Ref(RefType {
1539 nullable: false,
1540 heap_type: HeapType::Concrete(intr.raw_array),
1541 }))?;
1542 let order = emitter.add_anonymous_local(ValType::Ref(RefType {
1543 nullable: false,
1544 heap_type: HeapType::Concrete(intr.raw_index_array),
1545 }))?;
1546 emitter.instruction(Instruction::LocalGet(value_local));
1547 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(intr.map)));
1548 emitter.instruction(Instruction::LocalTee(backing));
1549 emitter.instruction(Instruction::StructGet {
1550 struct_type_index: intr.map,
1551 field_index: 1,
1552 });
1553 emitter.instruction(Instruction::LocalSet(keys));
1554 emitter.instruction(Instruction::LocalGet(backing));
1555 emitter.instruction(Instruction::StructGet {
1556 struct_type_index: intr.map,
1557 field_index: 2,
1558 });
1559 emitter.instruction(Instruction::LocalSet(values));
1560 emitter.instruction(Instruction::LocalGet(backing));
1561 emitter.instruction(Instruction::StructGet {
1562 struct_type_index: intr.map,
1563 field_index: 4,
1564 });
1565 emitter.instruction(Instruction::LocalSet(order));
1566 emit_ordered_collection_members_test(
1567 emitter,
1568 ctx,
1569 CollectionStorage {
1570 backing,
1571 backing_type: intr.map,
1572 order_len_field: 5,
1573 order,
1574 },
1575 &[(keys, key), (values, value)],
1576 validator_state,
1577 )?;
1578 emitter.emit_else();
1579 emitter.instruction(Instruction::I32Const(0));
1580 emitter.emit_end();
1581 Ok(())
1582}
1583
1584fn emit_ordered_collection_members_test(
1585 emitter: &mut FunctionEmitter,
1586 ctx: &CodegenCtx,
1587 storage: CollectionStorage,
1588 payloads: &[(u32, &Type)],
1589 validator_state: Option<ValidatorState>,
1590) -> Result<(), crate::compiler_error::CompilerFailure> {
1591 let intr = ctx
1592 .symbols
1593 .intrinsic_type_indices()
1594 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1595 let acc = emitter.add_anonymous_local(ValType::I32)?;
1596 let cursor = emitter.add_anonymous_local(ValType::I32)?;
1597 let slot = emitter.add_anonymous_local(ValType::I32)?;
1598 let member = emitter.add_anonymous_local(scratch_object_ty(intr.object))?;
1599 emitter.instruction(Instruction::I32Const(1));
1600 emitter.instruction(Instruction::LocalSet(acc));
1601 emitter.instruction(Instruction::I32Const(0));
1602 emitter.instruction(Instruction::LocalSet(cursor));
1603 emitter.emit_block(BlockType::Empty);
1604 emitter.emit_loop(BlockType::Empty);
1605 emitter.instruction(Instruction::LocalGet(cursor));
1606 emitter.instruction(Instruction::LocalGet(storage.backing));
1607 emitter.instruction(Instruction::StructGet {
1608 struct_type_index: storage.backing_type,
1609 field_index: storage.order_len_field,
1610 });
1611 emitter.instruction(Instruction::I32GeU);
1612 emitter.instruction(Instruction::BrIf(1));
1613 emitter.instruction(Instruction::LocalGet(storage.order));
1614 emitter.instruction(Instruction::LocalGet(cursor));
1615 emitter.instruction(Instruction::ArrayGet(intr.raw_index_array));
1616 emitter.instruction(Instruction::LocalTee(slot));
1617 emitter.instruction(Instruction::I32Const(0));
1618 emitter.instruction(Instruction::I32GeS);
1619 emitter.emit_if(BlockType::Empty);
1620 for (position, (payload, ty)) in payloads.iter().enumerate() {
1621 let parent_path = diagnostic::index(emitter, ctx, cursor);
1622 let member_path = diagnostic::field(
1623 emitter,
1624 ctx,
1625 if payloads.len() == 1 || position == 1 {
1626 "value"
1627 } else {
1628 "key"
1629 },
1630 );
1631 emitter.instruction(Instruction::LocalGet(*payload));
1632 emitter.instruction(Instruction::LocalGet(slot));
1633 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
1634 emitter.instruction(Instruction::LocalSet(member));
1635 emitter.instruction(Instruction::LocalGet(acc));
1636 emit_structural_test_inner(
1637 emitter,
1638 ctx,
1639 member,
1640 ty,
1641 &mut std::collections::BTreeSet::new(),
1642 validator_state,
1643 )?;
1644 diagnostic::pop(emitter, member_path);
1645 diagnostic::pop(emitter, parent_path);
1646 emitter.instruction(Instruction::I32And);
1647 emitter.instruction(Instruction::LocalSet(acc));
1648 }
1649 emitter.emit_end();
1650 emitter.instruction(Instruction::LocalGet(cursor));
1651 emitter.instruction(Instruction::I32Const(1));
1652 emitter.instruction(Instruction::I32Add);
1653 emitter.instruction(Instruction::LocalSet(cursor));
1654 emitter.instruction(Instruction::Br(0));
1655 emitter.emit_end();
1656 emitter.emit_end();
1657 emitter.instruction(Instruction::LocalGet(acc));
1658 Ok(())
1659}
1660
1661fn emit_validator_state(
1662 emitter: &mut FunctionEmitter,
1663 ctx: &CodegenCtx,
1664 validator_state: Option<ValidatorState>,
1665) -> Result<u32, crate::compiler_error::CompilerFailure> {
1666 let intr = ctx
1667 .symbols
1668 .intrinsic_type_indices()
1669 .ok_or_else(|| super::internal_failure("intrinsic types are not declared"))?;
1670 let visited = if let Some(state) = validator_state {
1671 state.visited
1672 } else {
1673 let local = emitter.add_anonymous_local(ValType::Ref(RefType {
1674 nullable: false,
1675 heap_type: HeapType::Concrete(intr.raw_array),
1676 }))?;
1677 emitter.instruction(Instruction::I32Const(RECURSIVE_VALIDATOR_CAPACITY * 2 + 2));
1678 emitter.instruction(Instruction::ArrayNewDefault(intr.raw_array));
1679 emitter.instruction(Instruction::LocalSet(local));
1680 local
1681 };
1682 diagnostic::save_recursive(emitter, ctx, visited)?;
1683 emitter.instruction(Instruction::LocalGet(visited));
1684 if let Some(state) = validator_state {
1685 emitter.instruction(Instruction::LocalGet(state.validator_ids));
1686 emitter.instruction(Instruction::LocalGet(state.depth));
1687 emitter.instruction(Instruction::I32Const(1));
1688 emitter.instruction(Instruction::I32Add);
1689 } else {
1690 emitter.instruction(Instruction::I32Const(RECURSIVE_VALIDATOR_CAPACITY));
1691 emitter.instruction(Instruction::ArrayNewDefault(intr.raw_index_array));
1692 emitter.instruction(Instruction::I32Const(0));
1693 }
1694 Ok(visited)
1695}
1696
1697pub(crate) fn emit_runtime_validator_body(
1704 ctx: &CodegenCtx,
1705 key: &Type,
1706 body: &Type,
1707 rejects_polymorphic_edge: bool,
1708 validator_id: i32,
1709) -> Result<Function, crate::compiler_error::CompilerFailure> {
1710 let intr = ctx
1711 .symbols
1712 .intrinsic_type_indices()
1713 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1714 let object_idx = intr.object;
1715 let object_ref_null = ValType::Ref(RefType {
1716 nullable: true,
1717 heap_type: HeapType::Concrete(object_idx),
1718 });
1719 let raw_array_ref = ValType::Ref(RefType {
1720 nullable: false,
1721 heap_type: HeapType::Concrete(intr.raw_array),
1722 });
1723 let raw_index_array_ref = ValType::Ref(RefType {
1724 nullable: false,
1725 heap_type: HeapType::Concrete(intr.raw_index_array),
1726 });
1727 let ident = |name: &str| Ident {
1728 name: name.to_string(),
1729 span: Span::at(ctx.file),
1730 };
1731 let mut emitter = FunctionEmitter::new(
1732 ctx,
1733 &[
1734 (ident("v"), object_ref_null),
1735 (ident("visited"), raw_array_ref),
1736 (ident("validator_ids"), raw_index_array_ref),
1737 (ident("depth"), ValType::I32),
1738 (
1739 ident("types"),
1740 super::runtime_descriptors::environment_type(ctx.symbols)?,
1741 ),
1742 ],
1743 )?;
1744 super::runtime_descriptors::bind(
1745 &mut emitter,
1746 &super::runtime_descriptors::parameters(key),
1747 4,
1748 )?;
1749 if rejects_polymorphic_edge {
1750 emitter.instruction(Instruction::I32Const(0));
1751 return emitter.build();
1752 }
1753 diagnostic::enter_recursive(&mut emitter, ctx);
1754 let checkpoint = diagnostic::checkpoint(&mut emitter, ctx);
1755 let parameters = super::runtime_descriptors::parameters(key);
1756 let seen = emitter.add_anonymous_local(ValType::I32)?;
1757 let index = emitter.add_anonymous_local(ValType::I32)?;
1758 emitter.instruction(Instruction::I32Const(0));
1759 emitter.instruction(Instruction::LocalSet(seen));
1760 emitter.instruction(Instruction::I32Const(0));
1761 emitter.instruction(Instruction::LocalSet(index));
1762 emitter.emit_block(BlockType::Empty);
1763 emitter.emit_loop(BlockType::Empty);
1764 emitter.instruction(Instruction::LocalGet(index));
1765 emitter.instruction(Instruction::LocalGet(3));
1766 emitter.instruction(Instruction::I32GeU);
1767 emitter.instruction(Instruction::BrIf(1));
1768 emitter.instruction(Instruction::LocalGet(1));
1769 emitter.instruction(Instruction::LocalGet(index));
1770 emitter.instruction(Instruction::I32Const(2));
1771 emitter.instruction(Instruction::I32Mul);
1772 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
1773 emitter.instruction(Instruction::LocalGet(0));
1774 emitter.instruction(Instruction::RefEq);
1775 emitter.instruction(Instruction::LocalGet(2));
1776 emitter.instruction(Instruction::LocalGet(index));
1777 emitter.instruction(Instruction::ArrayGet(intr.raw_index_array));
1778 emitter.instruction(Instruction::I32Const(validator_id));
1779 emitter.instruction(Instruction::I32Eq);
1780 emitter.instruction(Instruction::I32And);
1781 emitter.emit_if(BlockType::Empty);
1782 emit_same_validator_environment(&mut emitter, ctx, index, parameters.len())?;
1783 emitter.instruction(Instruction::LocalSet(seen));
1784 emitter.instruction(Instruction::LocalGet(seen));
1785 emitter.emit_if(BlockType::Empty);
1786 emitter.instruction(Instruction::Br(3));
1787 emitter.emit_end();
1788 emitter.emit_end();
1789 emitter.instruction(Instruction::LocalGet(index));
1790 emitter.instruction(Instruction::I32Const(1));
1791 emitter.instruction(Instruction::I32Add);
1792 emitter.instruction(Instruction::LocalSet(index));
1793 emitter.instruction(Instruction::Br(0));
1794 emitter.emit_end();
1795 emitter.emit_end();
1796 emitter.instruction(Instruction::LocalGet(seen));
1797 emitter.emit_if(BlockType::Result(ValType::I32));
1798 emitter.instruction(Instruction::I32Const(1));
1799 emitter.emit_else();
1800 emitter.instruction(Instruction::LocalGet(3));
1801 emitter.instruction(Instruction::I32Const(RECURSIVE_VALIDATOR_CAPACITY));
1802 emitter.instruction(Instruction::I32GeU);
1803 emitter.emit_if(BlockType::Result(ValType::I32));
1804 emitter.instruction(Instruction::I32Const(0));
1805 emitter.emit_else();
1806 emitter.instruction(Instruction::LocalGet(1));
1807 emitter.instruction(Instruction::LocalGet(3));
1808 emitter.instruction(Instruction::I32Const(2));
1809 emitter.instruction(Instruction::I32Mul);
1810 emitter.instruction(Instruction::LocalGet(0));
1811 emitter.instruction(Instruction::ArraySet(intr.raw_array));
1812 if !parameters.is_empty() {
1813 emit_store_validator_environment(&mut emitter, ctx)?;
1814 }
1815 emitter.instruction(Instruction::LocalGet(2));
1816 emitter.instruction(Instruction::LocalGet(3));
1817 emitter.instruction(Instruction::I32Const(validator_id));
1818 emitter.instruction(Instruction::ArraySet(intr.raw_index_array));
1819 let mut interface_stack = std::collections::BTreeSet::new();
1820 if key.lowered() == body.lowered()
1821 && let Some(crate::FieldNarrowingTest::Interface(test)) =
1822 ctx.ta.runtime_type_tests.get(key.lowered())
1823 {
1824 emit_interface_test_inner(
1825 &mut emitter,
1826 ctx,
1827 0,
1828 test,
1829 &mut interface_stack,
1830 Some(ValidatorState {
1831 visited: 1,
1832 validator_ids: 2,
1833 depth: 3,
1834 }),
1835 )?;
1836 } else {
1837 emit_structural_test_inner(
1838 &mut emitter,
1839 ctx,
1840 0,
1841 body,
1842 &mut interface_stack,
1843 Some(ValidatorState {
1844 visited: 1,
1845 validator_ids: 2,
1846 depth: 3,
1847 }),
1848 )?;
1849 }
1850 emitter.emit_end();
1851 emitter.emit_end();
1852 diagnostic::finish(&mut emitter, ctx, checkpoint)?;
1853 diagnostic::save_recursive(&mut emitter, ctx, 1)?;
1854 ctx.check_function_limits(&mut emitter)?;
1855 emitter.build()
1856}
1857
1858fn emit_is_non_null(emitter: &mut FunctionEmitter, value_local: u32) {
1859 emitter.instruction(Instruction::LocalGet(value_local));
1860 emitter.instruction(Instruction::RefIsNull);
1861 emitter.instruction(Instruction::I32Eqz);
1862}
1863
1864fn emit_ref_test(emitter: &mut FunctionEmitter, value_local: u32, type_idx: u32) {
1865 emitter.instruction(Instruction::LocalGet(value_local));
1866 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(type_idx)));
1867}
1868
1869fn boxed_number_idx(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1870 ctx.symbols
1871 .boxed_number_type_idx()
1872 .ok_or_else(|| crate::codegen::internal_failure("$BoxedNumber intrinsic registered"))
1873}
1874
1875fn boxed_boolean_idx(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1876 ctx.symbols
1877 .boxed_boolean_type_idx()
1878 .ok_or_else(|| crate::codegen::internal_failure("$BoxedBoolean intrinsic registered"))
1879}
1880
1881fn string_idx(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1882 ctx.symbols
1883 .string_type_idx()
1884 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic registered"))
1885}
1886
1887fn object_idx_of(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1888 Ok(ctx
1889 .symbols
1890 .intrinsic_type_indices()
1891 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
1892 .object)
1893}
1894
1895fn scratch_object_ty(object_idx: u32) -> ValType {
1896 ValType::Ref(RefType {
1897 nullable: true,
1898 heap_type: HeapType::Concrete(object_idx),
1899 })
1900}
1901
1902fn emit_cast_throw(
1903 emitter: &mut FunctionEmitter,
1904 ctx: &CodegenCtx,
1905 scratch: u32,
1906 target_ty: &Type,
1907) {
1908 ctx.latch(emit_cast_throw_checked(emitter, ctx, scratch, target_ty));
1909}
1910
1911fn emit_cast_throw_checked(
1912 emitter: &mut FunctionEmitter,
1913 ctx: &CodegenCtx,
1914 scratch: u32,
1915 target_ty: &Type,
1916) -> Result<(), crate::compiler_error::CompilerFailure> {
1917 let prefix = error_prefix(target_ty);
1918 emit_inline_string(emitter, ctx, &prefix);
1919
1920 emit_runtime_type_string(emitter, ctx, scratch);
1921
1922 let concat_idx = ctx
1923 .symbols
1924 .prelude_func_idx("string_concat")
1925 .ok_or_else(|| crate::codegen::internal_failure("string_concat imported from prelude"))?;
1926 emitter.instruction(Instruction::Call(concat_idx));
1927
1928 diagnostic::append_failure(emitter, ctx);
1929
1930 emit_type_error_from_message(emitter, ctx);
1931
1932 Ok(())
1933}
1934
1935fn emit_type_error_from_message(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
1936 ctx.latch(emit_type_error_from_message_checked(emitter, ctx));
1937}
1938
1939fn emit_type_error_from_message_checked(
1940 emitter: &mut FunctionEmitter,
1941 ctx: &CodegenCtx,
1942) -> Result<(), crate::compiler_error::CompilerFailure> {
1943 let new_idx = ctx
1944 .symbols
1945 .prelude_func_idx("TypeError#constructor")
1946 .ok_or_else(|| {
1947 crate::codegen::internal_failure("TypeError#constructor imported from prelude")
1948 })?;
1949 emitter.instruction(Instruction::Call(new_idx));
1950
1951 crate::codegen::throw::emit_error_throw(emitter, ctx);
1954
1955 Ok(())
1956}
1957
1958pub(super) fn emit_inline_string(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, text: &str) {
1959 ctx.latch(emit_inline_string_checked(emitter, ctx, text));
1960}
1961
1962fn emit_inline_string_checked(
1963 emitter: &mut FunctionEmitter,
1964 ctx: &CodegenCtx,
1965 text: &str,
1966) -> Result<(), crate::compiler_error::CompilerFailure> {
1967 let intr = ctx
1968 .symbols
1969 .intrinsic_type_indices()
1970 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1971 let string_vtable_idx = ctx
1972 .symbols
1973 .prelude_global_idx("string_vtable")
1974 .ok_or_else(|| crate::codegen::internal_failure("string_vtable imported"))?;
1975 emitter.instruction(Instruction::GlobalGet(string_vtable_idx));
1976 ctx.latch(emit_inline_const_raw_string(emitter, ctx, text));
1977 emitter.instruction(Instruction::I64Const(0));
1978 emitter.instruction(Instruction::StructNew(intr.string));
1979
1980 Ok(())
1981}
1982
1983fn emit_runtime_type_string(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, scratch: u32) {
1986 ctx.latch(emit_runtime_type_string_checked(emitter, ctx, scratch));
1987}
1988
1989fn emit_runtime_type_string_checked(
1990 emitter: &mut FunctionEmitter,
1991 ctx: &CodegenCtx,
1992 scratch: u32,
1993) -> Result<(), crate::compiler_error::CompilerFailure> {
1994 let string_type_idx = ctx
1995 .symbols
1996 .string_type_idx()
1997 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic registered"))?;
1998 let string_ref = ValType::Ref(RefType {
1999 nullable: false,
2000 heap_type: HeapType::Concrete(string_type_idx),
2001 });
2002 let block = BlockType::Result(string_ref);
2003
2004 emitter.instruction(Instruction::LocalGet(scratch));
2005 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
2006 emitter.emit_if(block);
2007 emit_type_tag(emitter, ctx, "undefined")?;
2008 emitter.emit_else();
2009
2010 emitter.instruction(Instruction::LocalGet(scratch));
2012 emitter.instruction(Instruction::RefIsNull);
2013 emitter.emit_if(block);
2014 emit_type_tag(emitter, ctx, "null")?;
2015 emitter.emit_else();
2016
2017 emitter.instruction(Instruction::LocalGet(scratch));
2018 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
2019 string_type_idx,
2020 )));
2021 emitter.emit_if(block);
2022 emit_type_tag(emitter, ctx, "string")?;
2023 emitter.emit_else();
2024
2025 let num_idx = ctx
2026 .symbols
2027 .boxed_number_type_idx()
2028 .ok_or_else(|| crate::codegen::internal_failure("$BoxedNumber intrinsic registered"))?;
2029 emitter.instruction(Instruction::LocalGet(scratch));
2030 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(num_idx)));
2031 emitter.emit_if(block);
2032 emit_type_tag(emitter, ctx, "number")?;
2033 emitter.emit_else();
2034
2035 let bool_idx = ctx
2036 .symbols
2037 .boxed_boolean_type_idx()
2038 .ok_or_else(|| crate::codegen::internal_failure("$BoxedBoolean intrinsic registered"))?;
2039 emitter.instruction(Instruction::LocalGet(scratch));
2040 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(bool_idx)));
2041 emitter.emit_if(block);
2042 emit_type_tag(emitter, ctx, "boolean")?;
2043 emitter.emit_else();
2044
2045 let closure_idx = ctx
2046 .symbols
2047 .closure_type_idx()
2048 .ok_or_else(|| crate::codegen::internal_failure("$Closure intrinsic registered"))?;
2049 emitter.instruction(Instruction::LocalGet(scratch));
2050 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
2051 emitter.emit_if(block);
2052 emit_type_tag(emitter, ctx, "function")?;
2053 emitter.emit_else();
2054
2055 emit_type_tag(emitter, ctx, "object")?;
2056
2057 for _ in 0..5 {
2058 emitter.emit_end();
2059 }
2060
2061 emitter.emit_end();
2062 Ok(())
2063}
2064
2065fn admits_undefined(ty: &Type) -> bool {
2067 match ty.lowered() {
2068 Type::Undefined | Type::Void => true,
2069 Type::Union(members) => members.iter().any(admits_undefined),
2070 _ => false,
2071 }
2072}
2073
2074fn emit_type_tag(
2077 emitter: &mut FunctionEmitter,
2078 ctx: &CodegenCtx,
2079 tag: &str,
2080) -> Result<(), crate::compiler_error::CompilerFailure> {
2081 match ctx.strings.lookup_text(tag) {
2082 Some(pool_idx) => {
2083 crate::codegen::function_emitter::emit_pooled_string(emitter, ctx, pool_idx)
2084 }
2085 None => emit_inline_string_checked(emitter, ctx, tag),
2086 }
2087}
2088
2089fn emit_same_validator_environment(
2092 emitter: &mut FunctionEmitter,
2093 ctx: &CodegenCtx,
2094 index: u32,
2095 count: usize,
2096) -> Result<(), crate::compiler_error::CompilerFailure> {
2097 emitter.instruction(Instruction::I32Const(1));
2098 if count == 0 {
2099 return Ok(());
2100 }
2101 let intr = ctx
2102 .symbols
2103 .intrinsic_type_indices()
2104 .ok_or_else(|| crate::codegen::internal_failure("intrinsics"))?;
2105 let closure = ctx
2106 .symbols
2107 .closure_struct_type_idx(super::field_guards::signature())
2108 .ok_or_else(|| crate::codegen::internal_failure("descriptor closure"))?;
2109 for slot in 0..count {
2110 emitter.instruction(Instruction::LocalGet(1));
2111 emitter.instruction(Instruction::LocalGet(index));
2112 emitter.instruction(Instruction::I32Const(2));
2113 emitter.instruction(Instruction::I32Mul);
2114 emitter.instruction(Instruction::I32Const(1));
2115 emitter.instruction(Instruction::I32Add);
2116 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
2117 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(closure)));
2118 emitter.instruction(Instruction::StructGet {
2119 struct_type_index: closure,
2120 field_index: 2,
2121 });
2122 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
2123 intr.object_fields,
2124 )));
2125 emitter.instruction(Instruction::I32Const(slot as i32));
2126 emitter.instruction(Instruction::ArrayGet(intr.object_fields));
2127 emitter.instruction(Instruction::LocalGet(4));
2128 emitter.instruction(Instruction::I32Const(slot as i32));
2129 emitter.instruction(Instruction::ArrayGet(intr.object_fields));
2130 emitter.instruction(Instruction::RefEq);
2131 emitter.instruction(Instruction::I32And);
2132 }
2133 Ok(())
2134}
2135
2136fn emit_store_validator_environment(
2137 emitter: &mut FunctionEmitter,
2138 ctx: &CodegenCtx,
2139) -> Result<(), crate::compiler_error::CompilerFailure> {
2140 let intr = ctx
2141 .symbols
2142 .intrinsic_type_indices()
2143 .ok_or_else(|| crate::codegen::internal_failure("intrinsics"))?;
2144 let closure = ctx
2145 .symbols
2146 .closure_struct_type_idx(super::field_guards::signature())
2147 .ok_or_else(|| crate::codegen::internal_failure("descriptor closure"))?;
2148 emitter.instruction(Instruction::LocalGet(1));
2149 emitter.instruction(Instruction::LocalGet(3));
2150 emitter.instruction(Instruction::I32Const(2));
2151 emitter.instruction(Instruction::I32Mul);
2152 emitter.instruction(Instruction::I32Const(1));
2153 emitter.instruction(Instruction::I32Add);
2154 emitter.instruction(Instruction::GlobalGet(
2155 ctx.symbols
2156 .closure_vtable_global_idx()
2157 .ok_or_else(|| crate::codegen::internal_failure("closure vtable"))?,
2158 ));
2159 emitter.instruction(Instruction::RefFunc(
2160 *ctx.symbols
2161 .type_descriptor_functions
2162 .get(&Type::Unknown)
2163 .ok_or_else(|| {
2164 crate::codegen::internal_failure(
2165 "validator environment descriptor is not registered",
2166 )
2167 })?,
2168 ));
2169 emitter.instruction(Instruction::LocalGet(4));
2170 emitter.instruction(Instruction::I64Const(0));
2171 emitter.instruction(Instruction::StructNew(closure));
2172 emitter.instruction(Instruction::ArraySet(intr.raw_array));
2173
2174 Ok(())
2175}
2176
2177fn emit_index_conformance(
2178 emitter: &mut FunctionEmitter,
2179 ctx: &CodegenCtx,
2180 object: u32,
2181 index: &crate::IndexSignature,
2182 interface_stack: &mut std::collections::BTreeSet<Type>,
2183 validator_state: Option<ValidatorState>,
2184) -> Result<(), crate::compiler_error::CompilerFailure> {
2185 emitter.instruction(Instruction::LocalGet(object));
2186 let Some(symbol) = ctx.require(
2187 ctx.symbols
2188 .prelude_func_idx("ObjectConstructor##recordValues"),
2189 "record validator imported",
2190 ) else {
2191 return Ok(());
2192 };
2193 emitter.instruction(Instruction::Call(symbol));
2194 let values = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
2195 emitter.instruction(Instruction::LocalSet(values));
2196 emit_structural_test_inner(
2197 emitter,
2198 ctx,
2199 values,
2200 &Type::Array(index.value.clone()),
2201 interface_stack,
2202 validator_state,
2203 )?;
2204 Ok(())
2205}
2206
2207#[cfg(test)]
2208mod representation_invariant_tests {
2209 use super::*;
2210 use crate::codegen::invariant_tests::{assert_internal, with_context};
2211
2212 #[test]
2213 fn primitive_representation_test_returns_internal_failure() {
2214 with_context(
2215 &crate::TypedAst::new(),
2216 &crate::codegen::SymbolTable::default(),
2217 |ctx| {
2218 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
2219 assert_internal(
2220 emit_representation_test(&mut emitter, ctx, 0, &Type::Number).unwrap_err(),
2221 );
2222 },
2223 );
2224 }
2225}