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