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