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 returns_unknown =
704 matches!(ty.peel(), Type::Function { ret, .. } if matches!(ret.peel(), Type::Unknown));
705 let target = if returns_unknown {
706 target.either_convention()
707 } else {
708 target
709 };
710 let fit = |emitter: &mut FunctionEmitter| {
711 super::closure_coercions::emit_defaults_fit(emitter, ctx, value_local, target)
712 };
713 emit_ref_test(emitter, value_local, closure_struct);
714 emitter.emit_if(BlockType::Result(ValType::I32));
715 super::closure_coercions::emit_may_have_argument_metadata(
716 emitter,
717 ctx,
718 value_local,
719 closure_struct,
720 )?;
721 emitter.emit_if(BlockType::Result(ValType::I32));
722 fit(emitter)?;
723 emitter.emit_else();
724 emitter.instruction(Instruction::I32Const(i32::from(!has_rest)));
725 emitter.emit_end();
726 emitter.emit_else();
727 if has_rest {
728 emitter.instruction(Instruction::I32Const(0));
729 } else {
730 fit(emitter)?;
731 }
732 emitter.emit_end();
733 Ok(())
734}
735
736fn emit_interface_ref_test(
737 emitter: &mut FunctionEmitter,
738 ctx: &CodegenCtx,
739 value_local: u32,
740 ty: &Type,
741 interface_stack: &mut std::collections::BTreeSet<Type>,
742 validator_state: Option<ValidatorState>,
743) -> Result<(), crate::compiler_error::CompilerFailure> {
744 let key = ty.peel().clone();
745 if ctx.symbols.generic_runtime_validator(&key).is_some() {
746 emit_validator_or_representation(emitter, ctx, value_local, ty, &key, validator_state)?;
747 return Ok(());
748 }
749 let _: () = match ctx.ta.runtime_type_tests.get(&key) {
750 Some(crate::FieldNarrowingTest::Shape(shape)) => emit_structural_test_inner(
751 emitter,
752 ctx,
753 value_local,
754 shape,
755 interface_stack,
756 validator_state,
757 )?,
758 Some(crate::FieldNarrowingTest::Interface(test)) if interface_stack.insert(key.clone()) => {
759 emit_interface_test_inner(
760 emitter,
761 ctx,
762 value_local,
763 test,
764 interface_stack,
765 validator_state,
766 )?;
767 interface_stack.remove(&key);
768 }
769 _ => {
770 emit_validator_or_representation(emitter, ctx, value_local, ty, &key, validator_state)?;
771 }
772 };
773 Ok(())
774}
775
776fn emit_validator_or_representation(
777 emitter: &mut FunctionEmitter,
778 ctx: &CodegenCtx,
779 value_local: u32,
780 ty: &Type,
781 key: &Type,
782 validator_state: Option<ValidatorState>,
783) -> Result<(), crate::compiler_error::CompilerFailure> {
784 if let Some(func_idx) = ctx.symbols.runtime_validator_idx(key) {
785 emitter.instruction(Instruction::LocalGet(value_local));
786 let visited = emit_validator_state(emitter, ctx, validator_state)?;
787 super::runtime_descriptors::validator_environment(emitter, ctx, key)?;
788 emitter.instruction(Instruction::Call(func_idx));
789 diagnostic::load_recursive(emitter, ctx, visited);
790 } else {
791 emit_representation_test(emitter, ctx, value_local, ty)?;
792 };
793 Ok(())
794}
795
796pub(crate) fn emit_checked_cast_on_stack(
799 emitter: &mut FunctionEmitter,
800 ctx: &CodegenCtx,
801 source_ty: &Type,
802 target_ty: &Type,
803) -> Result<(), crate::compiler_error::CompilerFailure> {
804 cast::emit_box(emitter, ctx, source_ty)?;
805 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
806 emitter.instruction(Instruction::LocalSet(scratch));
807 let previous_diagnostic = emitter.cast_diagnostic.take();
808 if diagnostic::has_nested_paths(target_ty) {
809 diagnostic::begin(emitter, ctx);
810 }
811 match ctx.ta.runtime_type_tests.get(target_ty.peel()) {
812 Some(crate::FieldNarrowingTest::Interface(test)) => {
813 emit_interface_test(emitter, ctx, scratch, target_ty, test)?;
814 }
815 Some(crate::FieldNarrowingTest::Shape(shape)) => {
816 emit_structural_test(emitter, ctx, scratch, shape, target_ty)?;
817 }
818 _ if matches!(target_ty.peel(), Type::Function { .. }) => {
822 emit_structural_test(emitter, ctx, scratch, target_ty, target_ty)?;
823 }
824 _ => emit_target_test(emitter, ctx, scratch, target_ty)?,
825 }
826 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(target_ty)?));
827 emitter.instruction(Instruction::LocalGet(scratch));
828 cast::emit_cast_to(emitter, ctx, target_ty)?;
829 emitter.emit_else();
830 emit_cast_throw(emitter, ctx, scratch, target_ty);
831 emitter.emit_end();
832 emitter.cast_diagnostic = previous_diagnostic;
833 Ok(())
834}
835
836pub(crate) fn emit_checked_parameter_cast_on_stack(
842 emitter: &mut FunctionEmitter,
843 ctx: &CodegenCtx,
844 source_ty: &Type,
845 target_ty: &Type,
846) -> Result<(), crate::compiler_error::CompilerFailure> {
847 if !is_interface_parameter(target_ty) {
848 emit_checked_cast_on_stack(emitter, ctx, source_ty, target_ty)?;
849 return Ok(());
850 }
851
852 cast::emit_box(emitter, ctx, source_ty)?;
853 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
854 emitter.instruction(Instruction::LocalSet(scratch));
855 emit_representation_test(emitter, ctx, scratch, target_ty)?;
856 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(target_ty)?));
857 emitter.instruction(Instruction::LocalGet(scratch));
858 cast::emit_cast_to(emitter, ctx, target_ty)?;
859 emitter.emit_else();
860 emit_cast_throw(emitter, ctx, scratch, target_ty);
861 emitter.emit_end();
862 Ok(())
863}
864
865fn is_interface_parameter(ty: &Type) -> bool {
866 match ty.peel() {
867 Type::InterfaceRef { .. } => true,
868 Type::Union(members) => {
869 let mut non_null = members
870 .iter()
871 .filter(|member| !matches!(member.peel(), Type::Null | Type::Undefined));
872 non_null
873 .next()
874 .is_some_and(|member| matches!(member.peel(), Type::InterfaceRef { .. }))
875 && non_null.next().is_none()
876 }
877 _ => false,
878 }
879}
880
881pub(crate) fn emit_operation_cast_on_stack(
884 emitter: &mut FunctionEmitter,
885 ctx: &CodegenCtx,
886 source_ty: &Type,
887 target_ty: &Type,
888) -> Result<(), crate::compiler_error::CompilerFailure> {
889 cast::emit_box(emitter, ctx, source_ty)?;
890 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
891 emitter.instruction(Instruction::LocalSet(scratch));
892 let previous_diagnostic = emitter.cast_diagnostic.take();
894 if matches!(target_ty.peel(), Type::Function { .. }) {
895 let signature = crate::codegen::closures::classify(target_ty.peel())?;
896 emit_representation_test(emitter, ctx, scratch, target_ty)?;
897 let opposite = crate::codegen::closures::ClosureSig {
898 is_void: !signature.is_void,
899 ..signature
900 };
901 if let Some(structure) = ctx.symbols.closure_struct_type_idx(opposite) {
902 emit_ref_test(emitter, scratch, structure);
903 emitter.instruction(Instruction::I32Or);
904 }
905 if matches!(
906 target_ty.peel(),
907 Type::Function {
908 has_rest: false,
909 ..
910 }
911 ) {
912 emitter.emit_if(BlockType::Result(ValType::I32));
913 emitter.instruction(Instruction::I32Const(1));
914 emitter.emit_else();
915 super::closure_coercions::emit_defaults_fit(
916 emitter,
917 ctx,
918 scratch,
919 super::closure_coercions::DefaultsFitTarget::any_convention(signature.arity),
920 )?;
921 emitter.emit_end();
922 }
923 } else {
924 emit_target_test(emitter, ctx, scratch, target_ty)?;
925 }
926 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(target_ty)?));
927 emitter.instruction(Instruction::LocalGet(scratch));
928 cast::emit_cast_to(emitter, ctx, target_ty)?;
929 emitter.emit_else();
930 emit_cast_throw(emitter, ctx, scratch, target_ty);
931 emitter.emit_end();
932 emitter.cast_diagnostic = previous_diagnostic;
933 Ok(())
934}
935
936fn emit_target_test(
940 emitter: &mut FunctionEmitter,
941 ctx: &CodegenCtx,
942 value_local: u32,
943 target_ty: &Type,
944) -> Result<(), crate::compiler_error::CompilerFailure> {
945 if matches!(
946 ctx.symbols.value_type(target_ty)?,
947 ValType::F64 | ValType::I32
948 ) {
949 emit_structural_test(emitter, ctx, value_local, target_ty, target_ty)
950 } else {
951 emit_representation_test(emitter, ctx, value_local, target_ty)
952 }
953}
954
955fn emit_representation_test(
960 emitter: &mut FunctionEmitter,
961 ctx: &CodegenCtx,
962 value_local: u32,
963 target_ty: &Type,
964) -> Result<(), crate::compiler_error::CompilerFailure> {
965 let ValType::Ref(target) = ctx.symbols.value_type(target_ty)? else {
966 return Err(crate::codegen::internal_failure(
967 "representation test requires a reference type",
968 ));
969 };
970 let allows_null = cast::target_allows_null(ctx, target_ty)?;
971 if allows_null {
972 emitter.instruction(Instruction::LocalGet(value_local));
973 emitter.instruction(Instruction::RefIsNull);
974 emitter.emit_if(BlockType::Result(ValType::I32));
975 emitter.instruction(Instruction::I32Const(1));
976 emitter.emit_else();
977 }
978 emitter.instruction(Instruction::LocalGet(value_local));
979 emitter.instruction(Instruction::RefTestNonNull(target.heap_type));
980 if allows_null {
981 emitter.emit_end();
982 };
983 Ok(())
984}
985
986fn emit_field_conformance(
1006 emitter: &mut FunctionEmitter,
1007 ctx: &CodegenCtx,
1008 locals: FieldConformanceLocals,
1009 fname: &str,
1010 field: &crate::ObjectField,
1011 interface_stack: &mut std::collections::BTreeSet<Type>,
1012 validator_state: Option<ValidatorState>,
1013) -> Result<(), crate::compiler_error::CompilerFailure> {
1014 let checkpoint = diagnostic::checkpoint(emitter, ctx);
1015 let parent_path = diagnostic::field(emitter, ctx, fname);
1016 let i32_block = BlockType::Result(ValType::I32);
1017 let intrinsics = ctx
1018 .symbols
1019 .intrinsic_type_indices()
1020 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1021 let name_global = ctx
1022 .symbols
1023 .field_name_string_global_idx(fname)
1024 .ok_or_else(|| {
1025 crate::codegen::internal_failure(
1026 "per-name string global recorded for cast-target field",
1027 )
1028 })?;
1029 let index_local = emitter.add_anonymous_local(ValType::I32)?;
1030 crate::codegen::function_emitter::expr::emit_object_field_index_by_name(
1031 emitter,
1032 ctx,
1033 locals.object,
1034 name_global,
1035 );
1036 emitter.instruction(Instruction::LocalTee(index_local));
1037 emitter.instruction(Instruction::I32Const(0));
1038 emitter.instruction(Instruction::I32GeS);
1039 emitter.emit_if(i32_block);
1040 crate::codegen::function_emitter::expr::emit_field_slot_get(
1041 emitter,
1042 intrinsics,
1043 locals.object,
1044 index_local,
1045 );
1046 emitter.instruction(Instruction::LocalSet(locals.field));
1047 emit_structural_test_inner(
1048 emitter,
1049 ctx,
1050 locals.field,
1051 &field.ty,
1052 interface_stack,
1053 validator_state,
1054 )?;
1055 let may_be_absent = field.optional || admits_undefined(&field.ty);
1058 if may_be_absent {
1059 emitter.instruction(Instruction::LocalGet(locals.field));
1060 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
1061 emitter.instruction(Instruction::I32Or);
1062 } else {
1063 emitter.instruction(Instruction::LocalGet(locals.object));
1065 emitter.instruction(Instruction::StructGet {
1066 struct_type_index: intrinsics.object_shape,
1067 field_index: 1,
1068 });
1069 emitter.instruction(Instruction::LocalGet(index_local));
1070 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
1071 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
1072 emitter.instruction(Instruction::LocalGet(locals.field));
1073 crate::codegen::function_emitter::expr::emit_is_nullish(emitter, ctx)?;
1074 emitter.instruction(Instruction::I32Eqz);
1075 emitter.instruction(Instruction::I32Or);
1076 emitter.instruction(Instruction::I32And);
1077 }
1078 emitter.emit_else();
1079 let getter = crate::codegen::classes::accessor_getter_name(fname);
1080 if may_be_absent {
1081 emitter.instruction(Instruction::I32Const(1));
1083 } else if crate::codegen::function_emitter::expr::accessor_branch_emittable(ctx, &getter) {
1084 let _ = crate::codegen::function_emitter::expr::emit_is_accessor_backed(
1085 emitter,
1086 ctx,
1087 locals.object,
1088 &getter,
1089 crate::AccessorKind::Get,
1090 )?;
1091 } else {
1092 emitter.instruction(Instruction::I32Const(0));
1093 }
1094 emitter.emit_end();
1095 diagnostic::finish(emitter, ctx, checkpoint)?;
1096 diagnostic::pop(emitter, parent_path);
1097 Ok(())
1098}
1099
1100pub fn emit_narrowed_field_read(
1101 emitter: &mut FunctionEmitter,
1102 ctx: &CodegenCtx,
1103 check: &crate::FieldNarrowingCheck,
1104 result_ty: &Type,
1105) -> Result<(), crate::compiler_error::CompilerFailure> {
1106 emit_narrowed_field_read_as(emitter, ctx, check, result_ty, result_ty)?;
1107 Ok(())
1108}
1109
1110pub(crate) fn emit_narrowed_field_read_as(
1111 emitter: &mut FunctionEmitter,
1112 ctx: &CodegenCtx,
1113 check: &crate::FieldNarrowingCheck,
1114 result_ty: &Type,
1115 check_ty: &Type,
1116) -> Result<(), crate::compiler_error::CompilerFailure> {
1117 let result_val = ctx.symbols.value_type(result_ty)?;
1118 let scratch = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
1119 emitter.instruction(Instruction::LocalSet(scratch));
1120 let runtime_test = ctx.ta.runtime_type_tests.get(check_ty.peel());
1124 let test = if check
1125 .minimal_test_target
1126 .as_ref()
1127 .is_some_and(|target| target.peel() == check_ty.peel())
1128 {
1129 if matches!(check.test, crate::FieldNarrowingTest::NonNull) {
1130 &check.test
1131 } else {
1132 &crate::FieldNarrowingTest::Representation
1133 }
1134 } else {
1135 match runtime_test {
1136 Some(crate::FieldNarrowingTest::Representation)
1137 if !matches!(&check.test, crate::FieldNarrowingTest::Representation) =>
1138 {
1139 &check.test
1140 }
1141 Some(test) => test,
1142 None => &check.test,
1143 }
1144 };
1145 let previous_diagnostic = emitter.cast_diagnostic.take();
1148 let nested_check = match test {
1149 crate::FieldNarrowingTest::Shape(ty) => diagnostic::has_nested_paths(ty),
1150 crate::FieldNarrowingTest::Interface(_) => true,
1151 _ => false,
1152 };
1153 if nested_check {
1154 diagnostic::begin(emitter, ctx);
1155 }
1156 match test {
1157 crate::FieldNarrowingTest::NonNull => {
1163 if cast::target_allows_null(ctx, check_ty)? {
1164 emitter.instruction(Instruction::I32Const(1));
1165 } else {
1166 emit_is_non_null(emitter, scratch);
1167 }
1168 }
1169 crate::FieldNarrowingTest::Shape(shape) => {
1170 emit_structural_test(emitter, ctx, scratch, shape, check_ty)?;
1171 if !cast::target_allows_null(ctx, check_ty)? {
1175 emit_is_non_null(emitter, scratch);
1176 emitter.instruction(Instruction::I32And);
1177 }
1178 }
1179 crate::FieldNarrowingTest::Interface(test) => {
1180 emit_interface_test(emitter, ctx, scratch, check_ty, test)?;
1181 }
1182 crate::FieldNarrowingTest::Substituted => {
1183 emit_representation_test(emitter, ctx, scratch, check_ty)?;
1184 }
1185 crate::FieldNarrowingTest::Representation => {
1186 emit_representation_test(emitter, ctx, scratch, check_ty)?;
1187 }
1188 }
1189 emitter.emit_if(BlockType::Result(result_val));
1190 emitter.instruction(Instruction::LocalGet(scratch));
1191 cast::emit_cast_to(emitter, ctx, result_ty)?;
1192 emitter.emit_else();
1193 emit_inline_string(emitter, ctx, &check.message);
1194 diagnostic::append_failure(emitter, ctx);
1195 emit_type_error_from_message(emitter, ctx);
1196 emitter.emit_end();
1197 emitter.cast_diagnostic = previous_diagnostic;
1198 Ok(())
1199}
1200
1201fn emit_interface_test(
1203 emitter: &mut FunctionEmitter,
1204 ctx: &CodegenCtx,
1205 value_local: u32,
1206 ty: &Type,
1207 test: &crate::InterfaceNarrowingTest,
1208) -> Result<(), crate::compiler_error::CompilerFailure> {
1209 ctx.checking(ty, || {
1210 emit_interface_test_inner(
1211 emitter,
1212 ctx,
1213 value_local,
1214 test,
1215 &mut std::collections::BTreeSet::new(),
1216 None,
1217 )?;
1218 ctx.check_function_limits(emitter)
1219 })
1220}
1221
1222fn emit_interface_test_inner(
1223 emitter: &mut FunctionEmitter,
1224 ctx: &CodegenCtx,
1225 value_local: u32,
1226 test: &crate::InterfaceNarrowingTest,
1227 interface_stack: &mut std::collections::BTreeSet<Type>,
1228 validator_state: Option<ValidatorState>,
1229) -> Result<(), crate::compiler_error::CompilerFailure> {
1230 ctx.charge_validator_step(emitter)?;
1231 let checkpoint = diagnostic::checkpoint(emitter, ctx);
1232 if test.nullable || test.undefined {
1233 emitter.instruction(Instruction::LocalGet(value_local));
1234 if test.nullable && test.undefined {
1235 crate::codegen::function_emitter::expr::emit_is_nullish(emitter, ctx)?;
1236 } else if test.undefined {
1237 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
1238 } else {
1239 emitter.instruction(Instruction::RefIsNull);
1240 }
1241 emitter.emit_if(BlockType::Result(ValType::I32));
1242 emitter.instruction(Instruction::I32Const(1));
1243 emitter.emit_else();
1244 }
1245
1246 if !test.shape_allowed {
1247 emit_non_shape_interface_test(emitter, ctx, value_local, test, validator_state)?;
1248 if test.nullable || test.undefined {
1249 emitter.emit_end();
1250 }
1251 return Ok(());
1252 }
1253
1254 let shape_idx = ctx
1255 .symbols
1256 .object_shape_type_idx()
1257 .ok_or_else(|| crate::codegen::internal_failure("$ObjectShape intrinsic registered"))?;
1258 emitter.instruction(Instruction::LocalGet(value_local));
1259 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(shape_idx)));
1260 emitter.emit_if(BlockType::Result(ValType::I32));
1261 let object_local = emitter.add_anonymous_local(ValType::Ref(RefType {
1262 nullable: false,
1263 heap_type: HeapType::Concrete(shape_idx),
1264 }))?;
1265 emitter.instruction(Instruction::LocalGet(value_local));
1266 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(shape_idx)));
1267 emitter.instruction(Instruction::LocalSet(object_local));
1268 let field_local = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
1269 emitter.instruction(Instruction::I32Const(1));
1270 for (name, field) in &test.members {
1271 if test.methods.contains(name) {
1272 let parent_path = diagnostic::field(emitter, ctx, name);
1273 let name_global = ctx
1274 .symbols
1275 .field_name_string_global_idx(name)
1276 .ok_or_else(|| {
1277 crate::codegen::internal_failure("interface narrowing method name registered")
1278 })?;
1279 crate::codegen::function_emitter::expr::emit_object_field_read_by_name(
1280 emitter,
1281 ctx,
1282 object_local,
1283 name_global,
1284 );
1285 emitter.instruction(Instruction::LocalSet(field_local));
1286 emit_structural_test_inner(
1287 emitter,
1288 ctx,
1289 field_local,
1290 &field.ty,
1291 interface_stack,
1292 validator_state,
1293 )?;
1294 diagnostic::pop(emitter, parent_path);
1295 } else {
1296 emit_field_conformance(
1297 emitter,
1298 ctx,
1299 FieldConformanceLocals {
1300 object: object_local,
1301 field: field_local,
1302 },
1303 name,
1304 field,
1305 interface_stack,
1306 validator_state,
1307 )?;
1308 }
1309 emitter.instruction(Instruction::I32And);
1310 }
1311 if let Some(index) = &test.index {
1312 emit_index_conformance(
1313 emitter,
1314 ctx,
1315 object_local,
1316 index,
1317 interface_stack,
1318 validator_state,
1319 )?;
1320 emitter.instruction(Instruction::I32And);
1321 }
1322 emit_non_shape_interface_test(emitter, ctx, value_local, test, validator_state)?;
1327 emitter.instruction(Instruction::I32Or);
1328 emitter.emit_else();
1329 emit_non_shape_interface_test(emitter, ctx, value_local, test, validator_state)?;
1330 emitter.emit_end();
1331
1332 if test.nullable || test.undefined {
1333 emitter.emit_end();
1334 }
1335 diagnostic::finish(emitter, ctx, checkpoint)?;
1336 Ok(())
1337}
1338
1339fn emit_non_shape_interface_test(
1340 emitter: &mut FunctionEmitter,
1341 ctx: &CodegenCtx,
1342 value_local: u32,
1343 test: &crate::InterfaceNarrowingTest,
1344 validator_state: Option<ValidatorState>,
1345) -> Result<(), crate::compiler_error::CompilerFailure> {
1346 use crate::InterfaceCarrier;
1347
1348 let matches_any = emitter.add_anonymous_local(ValType::I32)?;
1349 emitter.instruction(Instruction::I32Const(0));
1350 emitter.instruction(Instruction::LocalSet(matches_any));
1351 for carrier in &test.non_shape_carriers {
1352 let intr = ctx
1353 .symbols
1354 .intrinsic_type_indices()
1355 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1356 match carrier {
1357 InterfaceCarrier::ArrayAny => emit_structural_test_inner(
1358 emitter,
1359 ctx,
1360 value_local,
1361 &Type::Array(Box::new(Type::Unknown)),
1362 &mut std::collections::BTreeSet::new(),
1363 validator_state,
1364 )?,
1365 InterfaceCarrier::Array(ty) => {
1366 emit_structural_test_inner(
1367 emitter,
1368 ctx,
1369 value_local,
1370 ty,
1371 &mut std::collections::BTreeSet::new(),
1372 validator_state,
1373 )?;
1374 }
1375 InterfaceCarrier::Number => emit_ref_test(emitter, value_local, boxed_number_idx(ctx)?),
1376 InterfaceCarrier::Boolean => {
1377 emit_ref_test(emitter, value_local, boxed_boolean_idx(ctx)?);
1378 }
1379 InterfaceCarrier::String => emit_ref_test(emitter, value_local, string_idx(ctx)?),
1380 InterfaceCarrier::BigInt => emit_ref_test(emitter, value_local, intr.bigint),
1381 InterfaceCarrier::Uint8Array => emit_ref_test(emitter, value_local, intr.uint8_array),
1382 InterfaceCarrier::MapAny => emit_ref_test(emitter, value_local, intr.map),
1383 InterfaceCarrier::Map(key, value) => {
1384 emit_map_carrier_test(emitter, ctx, value_local, key, value, validator_state)?;
1385 }
1386 InterfaceCarrier::SetAny => emit_ref_test(emitter, value_local, intr.set),
1387 InterfaceCarrier::Set(element) => {
1388 emit_set_carrier_test(emitter, ctx, value_local, element, validator_state)?;
1389 }
1390 InterfaceCarrier::RegExp => emit_ref_test(emitter, value_local, intr.regex),
1391 InterfaceCarrier::RegExpMatch => {
1392 emit_ref_test(emitter, value_local, intr.regex_match_box);
1393 }
1394 InterfaceCarrier::TemporalInstant => {
1395 emit_ref_test(emitter, value_local, intr.temporal_instant);
1396 }
1397 InterfaceCarrier::TemporalDuration => {
1398 emit_ref_test(emitter, value_local, intr.temporal_duration);
1399 }
1400 InterfaceCarrier::TemporalZonedDateTime => {
1401 emit_ref_test(emitter, value_local, intr.temporal_zdt);
1402 }
1403 InterfaceCarrier::TemporalPlainDate => {
1404 emit_ref_test(emitter, value_local, intr.temporal_plain_date);
1405 }
1406 InterfaceCarrier::TemporalPlainTime => {
1407 emit_ref_test(emitter, value_local, intr.temporal_plain_time);
1408 }
1409 InterfaceCarrier::TemporalPlainDateTime => {
1410 emit_ref_test(emitter, value_local, intr.temporal_plain_date_time);
1411 }
1412 InterfaceCarrier::TemporalPlainYearMonth => {
1413 emit_ref_test(emitter, value_local, intr.temporal_plain_year_month);
1414 }
1415 InterfaceCarrier::TemporalPlainMonthDay => {
1416 emit_ref_test(emitter, value_local, intr.temporal_plain_month_day);
1417 }
1418 InterfaceCarrier::ObjectShape => {
1419 emit_ref_test(emitter, value_local, intr.object_shape);
1420 }
1421 InterfaceCarrier::Url => emit_ref_test(emitter, value_local, intr.url),
1422 InterfaceCarrier::FsStat => emit_ref_test(emitter, value_local, intr.fs_stat),
1423 InterfaceCarrier::FsPeek => emit_ref_test(emitter, value_local, intr.fs_peek),
1424 InterfaceCarrier::FsDirEntry => emit_ref_test(emitter, value_local, intr.fs_dir_entry),
1425 InterfaceCarrier::FsInfo => emit_ref_test(emitter, value_local, intr.fs_info),
1426 InterfaceCarrier::FsMountInfo => {
1427 emit_ref_test(emitter, value_local, intr.fs_mount_info);
1428 }
1429 InterfaceCarrier::FsFileWriter => {
1430 emit_ref_test(emitter, value_local, intr.fs_file_writer);
1431 }
1432 InterfaceCarrier::HttpResponse => {
1433 emit_ref_test(emitter, value_local, intr.http_response);
1434 }
1435 InterfaceCarrier::HttpDownloadResult => {
1436 emit_ref_test(emitter, value_local, intr.http_download_result);
1437 }
1438 InterfaceCarrier::SessionEntry => {
1439 emit_ref_test(emitter, value_local, intr.session_entry);
1440 }
1441 InterfaceCarrier::SessionPage => emit_ref_test(emitter, value_local, intr.session_page),
1442 }
1443 emitter.instruction(Instruction::LocalGet(matches_any));
1444 emitter.instruction(Instruction::I32Or);
1445 emitter.instruction(Instruction::LocalSet(matches_any));
1446 }
1447 emitter.instruction(Instruction::LocalGet(matches_any));
1448 Ok(())
1449}
1450
1451fn emit_set_carrier_test(
1452 emitter: &mut FunctionEmitter,
1453 ctx: &CodegenCtx,
1454 value_local: u32,
1455 element: &Type,
1456 validator_state: Option<ValidatorState>,
1457) -> Result<(), crate::compiler_error::CompilerFailure> {
1458 let intr = ctx
1459 .symbols
1460 .intrinsic_type_indices()
1461 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1462 emit_ref_test(emitter, value_local, intr.set);
1463 emitter.emit_if(BlockType::Result(ValType::I32));
1464 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
1465 nullable: false,
1466 heap_type: HeapType::Concrete(intr.set),
1467 }))?;
1468 let elements = emitter.add_anonymous_local(ValType::Ref(RefType {
1469 nullable: false,
1470 heap_type: HeapType::Concrete(intr.raw_array),
1471 }))?;
1472 let order = emitter.add_anonymous_local(ValType::Ref(RefType {
1473 nullable: false,
1474 heap_type: HeapType::Concrete(intr.raw_index_array),
1475 }))?;
1476 emitter.instruction(Instruction::LocalGet(value_local));
1477 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(intr.set)));
1478 emitter.instruction(Instruction::LocalTee(backing));
1479 emitter.instruction(Instruction::StructGet {
1480 struct_type_index: intr.set,
1481 field_index: 1,
1482 });
1483 emitter.instruction(Instruction::LocalSet(elements));
1484 emitter.instruction(Instruction::LocalGet(backing));
1485 emitter.instruction(Instruction::StructGet {
1486 struct_type_index: intr.set,
1487 field_index: 3,
1488 });
1489 emitter.instruction(Instruction::LocalSet(order));
1490 emit_ordered_collection_members_test(
1491 emitter,
1492 ctx,
1493 CollectionStorage {
1494 backing,
1495 backing_type: intr.set,
1496 order_len_field: 4,
1497 order,
1498 },
1499 &[(elements, element)],
1500 validator_state,
1501 )?;
1502 emitter.emit_else();
1503 emitter.instruction(Instruction::I32Const(0));
1504 emitter.emit_end();
1505 Ok(())
1506}
1507
1508fn emit_map_carrier_test(
1509 emitter: &mut FunctionEmitter,
1510 ctx: &CodegenCtx,
1511 value_local: u32,
1512 key: &Type,
1513 value: &Type,
1514 validator_state: Option<ValidatorState>,
1515) -> Result<(), crate::compiler_error::CompilerFailure> {
1516 let intr = ctx
1517 .symbols
1518 .intrinsic_type_indices()
1519 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1520 emit_ref_test(emitter, value_local, intr.map);
1521 emitter.emit_if(BlockType::Result(ValType::I32));
1522 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
1523 nullable: false,
1524 heap_type: HeapType::Concrete(intr.map),
1525 }))?;
1526 let keys = emitter.add_anonymous_local(ValType::Ref(RefType {
1527 nullable: false,
1528 heap_type: HeapType::Concrete(intr.raw_array),
1529 }))?;
1530 let values = emitter.add_anonymous_local(ValType::Ref(RefType {
1531 nullable: false,
1532 heap_type: HeapType::Concrete(intr.raw_array),
1533 }))?;
1534 let order = emitter.add_anonymous_local(ValType::Ref(RefType {
1535 nullable: false,
1536 heap_type: HeapType::Concrete(intr.raw_index_array),
1537 }))?;
1538 emitter.instruction(Instruction::LocalGet(value_local));
1539 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(intr.map)));
1540 emitter.instruction(Instruction::LocalTee(backing));
1541 emitter.instruction(Instruction::StructGet {
1542 struct_type_index: intr.map,
1543 field_index: 1,
1544 });
1545 emitter.instruction(Instruction::LocalSet(keys));
1546 emitter.instruction(Instruction::LocalGet(backing));
1547 emitter.instruction(Instruction::StructGet {
1548 struct_type_index: intr.map,
1549 field_index: 2,
1550 });
1551 emitter.instruction(Instruction::LocalSet(values));
1552 emitter.instruction(Instruction::LocalGet(backing));
1553 emitter.instruction(Instruction::StructGet {
1554 struct_type_index: intr.map,
1555 field_index: 4,
1556 });
1557 emitter.instruction(Instruction::LocalSet(order));
1558 emit_ordered_collection_members_test(
1559 emitter,
1560 ctx,
1561 CollectionStorage {
1562 backing,
1563 backing_type: intr.map,
1564 order_len_field: 5,
1565 order,
1566 },
1567 &[(keys, key), (values, value)],
1568 validator_state,
1569 )?;
1570 emitter.emit_else();
1571 emitter.instruction(Instruction::I32Const(0));
1572 emitter.emit_end();
1573 Ok(())
1574}
1575
1576fn emit_ordered_collection_members_test(
1577 emitter: &mut FunctionEmitter,
1578 ctx: &CodegenCtx,
1579 storage: CollectionStorage,
1580 payloads: &[(u32, &Type)],
1581 validator_state: Option<ValidatorState>,
1582) -> Result<(), crate::compiler_error::CompilerFailure> {
1583 let intr = ctx
1584 .symbols
1585 .intrinsic_type_indices()
1586 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1587 let acc = emitter.add_anonymous_local(ValType::I32)?;
1588 let cursor = emitter.add_anonymous_local(ValType::I32)?;
1589 let slot = emitter.add_anonymous_local(ValType::I32)?;
1590 let member = emitter.add_anonymous_local(scratch_object_ty(intr.object))?;
1591 emitter.instruction(Instruction::I32Const(1));
1592 emitter.instruction(Instruction::LocalSet(acc));
1593 emitter.instruction(Instruction::I32Const(0));
1594 emitter.instruction(Instruction::LocalSet(cursor));
1595 emitter.emit_block(BlockType::Empty);
1596 emitter.emit_loop(BlockType::Empty);
1597 emitter.instruction(Instruction::LocalGet(cursor));
1598 emitter.instruction(Instruction::LocalGet(storage.backing));
1599 emitter.instruction(Instruction::StructGet {
1600 struct_type_index: storage.backing_type,
1601 field_index: storage.order_len_field,
1602 });
1603 emitter.instruction(Instruction::I32GeU);
1604 emitter.instruction(Instruction::BrIf(1));
1605 emitter.instruction(Instruction::LocalGet(storage.order));
1606 emitter.instruction(Instruction::LocalGet(cursor));
1607 emitter.instruction(Instruction::ArrayGet(intr.raw_index_array));
1608 emitter.instruction(Instruction::LocalTee(slot));
1609 emitter.instruction(Instruction::I32Const(0));
1610 emitter.instruction(Instruction::I32GeS);
1611 emitter.emit_if(BlockType::Empty);
1612 for (position, (payload, ty)) in payloads.iter().enumerate() {
1613 let parent_path = diagnostic::index(emitter, ctx, cursor);
1614 let member_path = diagnostic::field(
1615 emitter,
1616 ctx,
1617 if payloads.len() == 1 || position == 1 {
1618 "value"
1619 } else {
1620 "key"
1621 },
1622 );
1623 emitter.instruction(Instruction::LocalGet(*payload));
1624 emitter.instruction(Instruction::LocalGet(slot));
1625 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
1626 emitter.instruction(Instruction::LocalSet(member));
1627 emitter.instruction(Instruction::LocalGet(acc));
1628 emit_structural_test_inner(
1629 emitter,
1630 ctx,
1631 member,
1632 ty,
1633 &mut std::collections::BTreeSet::new(),
1634 validator_state,
1635 )?;
1636 diagnostic::pop(emitter, member_path);
1637 diagnostic::pop(emitter, parent_path);
1638 emitter.instruction(Instruction::I32And);
1639 emitter.instruction(Instruction::LocalSet(acc));
1640 }
1641 emitter.emit_end();
1642 emitter.instruction(Instruction::LocalGet(cursor));
1643 emitter.instruction(Instruction::I32Const(1));
1644 emitter.instruction(Instruction::I32Add);
1645 emitter.instruction(Instruction::LocalSet(cursor));
1646 emitter.instruction(Instruction::Br(0));
1647 emitter.emit_end();
1648 emitter.emit_end();
1649 emitter.instruction(Instruction::LocalGet(acc));
1650 Ok(())
1651}
1652
1653fn emit_validator_state(
1654 emitter: &mut FunctionEmitter,
1655 ctx: &CodegenCtx,
1656 validator_state: Option<ValidatorState>,
1657) -> Result<u32, crate::compiler_error::CompilerFailure> {
1658 let intr = ctx
1659 .symbols
1660 .intrinsic_type_indices()
1661 .ok_or_else(|| super::internal_failure("intrinsic types are not declared"))?;
1662 let visited = if let Some(state) = validator_state {
1663 state.visited
1664 } else {
1665 let local = emitter.add_anonymous_local(ValType::Ref(RefType {
1666 nullable: false,
1667 heap_type: HeapType::Concrete(intr.raw_array),
1668 }))?;
1669 emitter.instruction(Instruction::I32Const(RECURSIVE_VALIDATOR_CAPACITY * 2 + 2));
1670 emitter.instruction(Instruction::ArrayNewDefault(intr.raw_array));
1671 emitter.instruction(Instruction::LocalSet(local));
1672 local
1673 };
1674 diagnostic::save_recursive(emitter, ctx, visited)?;
1675 emitter.instruction(Instruction::LocalGet(visited));
1676 if let Some(state) = validator_state {
1677 emitter.instruction(Instruction::LocalGet(state.validator_ids));
1678 emitter.instruction(Instruction::LocalGet(state.depth));
1679 emitter.instruction(Instruction::I32Const(1));
1680 emitter.instruction(Instruction::I32Add);
1681 } else {
1682 emitter.instruction(Instruction::I32Const(RECURSIVE_VALIDATOR_CAPACITY));
1683 emitter.instruction(Instruction::ArrayNewDefault(intr.raw_index_array));
1684 emitter.instruction(Instruction::I32Const(0));
1685 }
1686 Ok(visited)
1687}
1688
1689pub(crate) fn emit_runtime_validator_body(
1696 ctx: &CodegenCtx,
1697 key: &Type,
1698 body: &Type,
1699 rejects_polymorphic_edge: bool,
1700 validator_id: i32,
1701) -> Result<Function, crate::compiler_error::CompilerFailure> {
1702 let intr = ctx
1703 .symbols
1704 .intrinsic_type_indices()
1705 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1706 let object_idx = intr.object;
1707 let object_ref_null = ValType::Ref(RefType {
1708 nullable: true,
1709 heap_type: HeapType::Concrete(object_idx),
1710 });
1711 let raw_array_ref = ValType::Ref(RefType {
1712 nullable: false,
1713 heap_type: HeapType::Concrete(intr.raw_array),
1714 });
1715 let raw_index_array_ref = ValType::Ref(RefType {
1716 nullable: false,
1717 heap_type: HeapType::Concrete(intr.raw_index_array),
1718 });
1719 let ident = |name: &str| Ident {
1720 name: name.to_string(),
1721 span: Span::at(ctx.file),
1722 };
1723 let mut emitter = FunctionEmitter::new(
1724 ctx,
1725 &[
1726 (ident("v"), object_ref_null),
1727 (ident("visited"), raw_array_ref),
1728 (ident("validator_ids"), raw_index_array_ref),
1729 (ident("depth"), ValType::I32),
1730 (
1731 ident("types"),
1732 super::runtime_descriptors::environment_type(ctx.symbols)?,
1733 ),
1734 ],
1735 )?;
1736 super::runtime_descriptors::bind(
1737 &mut emitter,
1738 &super::runtime_descriptors::parameters(key),
1739 4,
1740 )?;
1741 if rejects_polymorphic_edge {
1742 emitter.instruction(Instruction::I32Const(0));
1743 return emitter.build();
1744 }
1745 diagnostic::enter_recursive(&mut emitter, ctx);
1746 let checkpoint = diagnostic::checkpoint(&mut emitter, ctx);
1747 let parameters = super::runtime_descriptors::parameters(key);
1748 let seen = emitter.add_anonymous_local(ValType::I32)?;
1749 let index = emitter.add_anonymous_local(ValType::I32)?;
1750 emitter.instruction(Instruction::I32Const(0));
1751 emitter.instruction(Instruction::LocalSet(seen));
1752 emitter.instruction(Instruction::I32Const(0));
1753 emitter.instruction(Instruction::LocalSet(index));
1754 emitter.emit_block(BlockType::Empty);
1755 emitter.emit_loop(BlockType::Empty);
1756 emitter.instruction(Instruction::LocalGet(index));
1757 emitter.instruction(Instruction::LocalGet(3));
1758 emitter.instruction(Instruction::I32GeU);
1759 emitter.instruction(Instruction::BrIf(1));
1760 emitter.instruction(Instruction::LocalGet(1));
1761 emitter.instruction(Instruction::LocalGet(index));
1762 emitter.instruction(Instruction::I32Const(2));
1763 emitter.instruction(Instruction::I32Mul);
1764 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
1765 emitter.instruction(Instruction::LocalGet(0));
1766 emitter.instruction(Instruction::RefEq);
1767 emitter.instruction(Instruction::LocalGet(2));
1768 emitter.instruction(Instruction::LocalGet(index));
1769 emitter.instruction(Instruction::ArrayGet(intr.raw_index_array));
1770 emitter.instruction(Instruction::I32Const(validator_id));
1771 emitter.instruction(Instruction::I32Eq);
1772 emitter.instruction(Instruction::I32And);
1773 emitter.emit_if(BlockType::Empty);
1774 emit_same_validator_environment(&mut emitter, ctx, index, parameters.len())?;
1775 emitter.instruction(Instruction::LocalSet(seen));
1776 emitter.instruction(Instruction::LocalGet(seen));
1777 emitter.emit_if(BlockType::Empty);
1778 emitter.instruction(Instruction::Br(3));
1779 emitter.emit_end();
1780 emitter.emit_end();
1781 emitter.instruction(Instruction::LocalGet(index));
1782 emitter.instruction(Instruction::I32Const(1));
1783 emitter.instruction(Instruction::I32Add);
1784 emitter.instruction(Instruction::LocalSet(index));
1785 emitter.instruction(Instruction::Br(0));
1786 emitter.emit_end();
1787 emitter.emit_end();
1788 emitter.instruction(Instruction::LocalGet(seen));
1789 emitter.emit_if(BlockType::Result(ValType::I32));
1790 emitter.instruction(Instruction::I32Const(1));
1791 emitter.emit_else();
1792 emitter.instruction(Instruction::LocalGet(3));
1793 emitter.instruction(Instruction::I32Const(RECURSIVE_VALIDATOR_CAPACITY));
1794 emitter.instruction(Instruction::I32GeU);
1795 emitter.emit_if(BlockType::Result(ValType::I32));
1796 emitter.instruction(Instruction::I32Const(0));
1797 emitter.emit_else();
1798 emitter.instruction(Instruction::LocalGet(1));
1799 emitter.instruction(Instruction::LocalGet(3));
1800 emitter.instruction(Instruction::I32Const(2));
1801 emitter.instruction(Instruction::I32Mul);
1802 emitter.instruction(Instruction::LocalGet(0));
1803 emitter.instruction(Instruction::ArraySet(intr.raw_array));
1804 if !parameters.is_empty() {
1805 emit_store_validator_environment(&mut emitter, ctx)?;
1806 }
1807 emitter.instruction(Instruction::LocalGet(2));
1808 emitter.instruction(Instruction::LocalGet(3));
1809 emitter.instruction(Instruction::I32Const(validator_id));
1810 emitter.instruction(Instruction::ArraySet(intr.raw_index_array));
1811 let mut interface_stack = std::collections::BTreeSet::new();
1812 if key.peel() == body.peel()
1813 && let Some(crate::FieldNarrowingTest::Interface(test)) =
1814 ctx.ta.runtime_type_tests.get(key.peel())
1815 {
1816 emit_interface_test_inner(
1817 &mut emitter,
1818 ctx,
1819 0,
1820 test,
1821 &mut interface_stack,
1822 Some(ValidatorState {
1823 visited: 1,
1824 validator_ids: 2,
1825 depth: 3,
1826 }),
1827 )?;
1828 } else {
1829 emit_structural_test_inner(
1830 &mut emitter,
1831 ctx,
1832 0,
1833 body,
1834 &mut interface_stack,
1835 Some(ValidatorState {
1836 visited: 1,
1837 validator_ids: 2,
1838 depth: 3,
1839 }),
1840 )?;
1841 }
1842 emitter.emit_end();
1843 emitter.emit_end();
1844 diagnostic::finish(&mut emitter, ctx, checkpoint)?;
1845 diagnostic::save_recursive(&mut emitter, ctx, 1)?;
1846 ctx.check_function_limits(&mut emitter)?;
1847 emitter.build()
1848}
1849
1850fn emit_is_non_null(emitter: &mut FunctionEmitter, value_local: u32) {
1851 emitter.instruction(Instruction::LocalGet(value_local));
1852 emitter.instruction(Instruction::RefIsNull);
1853 emitter.instruction(Instruction::I32Eqz);
1854}
1855
1856fn emit_ref_test(emitter: &mut FunctionEmitter, value_local: u32, type_idx: u32) {
1857 emitter.instruction(Instruction::LocalGet(value_local));
1858 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(type_idx)));
1859}
1860
1861fn boxed_number_idx(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1862 ctx.symbols
1863 .boxed_number_type_idx()
1864 .ok_or_else(|| crate::codegen::internal_failure("$BoxedNumber intrinsic registered"))
1865}
1866
1867fn boxed_boolean_idx(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1868 ctx.symbols
1869 .boxed_boolean_type_idx()
1870 .ok_or_else(|| crate::codegen::internal_failure("$BoxedBoolean intrinsic registered"))
1871}
1872
1873fn string_idx(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1874 ctx.symbols
1875 .string_type_idx()
1876 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic registered"))
1877}
1878
1879fn object_idx_of(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
1880 Ok(ctx
1881 .symbols
1882 .intrinsic_type_indices()
1883 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
1884 .object)
1885}
1886
1887fn scratch_object_ty(object_idx: u32) -> ValType {
1888 ValType::Ref(RefType {
1889 nullable: true,
1890 heap_type: HeapType::Concrete(object_idx),
1891 })
1892}
1893
1894fn emit_cast_throw(
1895 emitter: &mut FunctionEmitter,
1896 ctx: &CodegenCtx,
1897 scratch: u32,
1898 target_ty: &Type,
1899) {
1900 ctx.latch(emit_cast_throw_checked(emitter, ctx, scratch, target_ty));
1901}
1902
1903fn emit_cast_throw_checked(
1904 emitter: &mut FunctionEmitter,
1905 ctx: &CodegenCtx,
1906 scratch: u32,
1907 target_ty: &Type,
1908) -> Result<(), crate::compiler_error::CompilerFailure> {
1909 let prefix = error_prefix(target_ty);
1910 emit_inline_string(emitter, ctx, &prefix);
1911
1912 emit_runtime_type_string(emitter, ctx, scratch);
1913
1914 let concat_idx = ctx
1915 .symbols
1916 .prelude_func_idx("string_concat")
1917 .ok_or_else(|| crate::codegen::internal_failure("string_concat imported from prelude"))?;
1918 emitter.instruction(Instruction::Call(concat_idx));
1919
1920 diagnostic::append_failure(emitter, ctx);
1921
1922 emit_type_error_from_message(emitter, ctx);
1923
1924 Ok(())
1925}
1926
1927fn emit_type_error_from_message(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
1928 ctx.latch(emit_type_error_from_message_checked(emitter, ctx));
1929}
1930
1931fn emit_type_error_from_message_checked(
1932 emitter: &mut FunctionEmitter,
1933 ctx: &CodegenCtx,
1934) -> Result<(), crate::compiler_error::CompilerFailure> {
1935 let new_idx = ctx
1936 .symbols
1937 .prelude_func_idx("TypeError#constructor")
1938 .ok_or_else(|| {
1939 crate::codegen::internal_failure("TypeError#constructor imported from prelude")
1940 })?;
1941 emitter.instruction(Instruction::Call(new_idx));
1942
1943 crate::codegen::throw::emit_error_throw(emitter, ctx);
1946
1947 Ok(())
1948}
1949
1950pub(super) fn emit_inline_string(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, text: &str) {
1951 ctx.latch(emit_inline_string_checked(emitter, ctx, text));
1952}
1953
1954fn emit_inline_string_checked(
1955 emitter: &mut FunctionEmitter,
1956 ctx: &CodegenCtx,
1957 text: &str,
1958) -> Result<(), crate::compiler_error::CompilerFailure> {
1959 let intr = ctx
1960 .symbols
1961 .intrinsic_type_indices()
1962 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1963 let string_vtable_idx = ctx
1964 .symbols
1965 .prelude_global_idx("string_vtable")
1966 .ok_or_else(|| crate::codegen::internal_failure("string_vtable imported"))?;
1967 emitter.instruction(Instruction::GlobalGet(string_vtable_idx));
1968 ctx.latch(emit_inline_const_raw_string(emitter, ctx, text));
1969 emitter.instruction(Instruction::I64Const(0));
1970 emitter.instruction(Instruction::StructNew(intr.string));
1971
1972 Ok(())
1973}
1974
1975fn emit_runtime_type_string(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, scratch: u32) {
1978 ctx.latch(emit_runtime_type_string_checked(emitter, ctx, scratch));
1979}
1980
1981fn emit_runtime_type_string_checked(
1982 emitter: &mut FunctionEmitter,
1983 ctx: &CodegenCtx,
1984 scratch: u32,
1985) -> Result<(), crate::compiler_error::CompilerFailure> {
1986 let string_type_idx = ctx
1987 .symbols
1988 .string_type_idx()
1989 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic registered"))?;
1990 let string_ref = ValType::Ref(RefType {
1991 nullable: false,
1992 heap_type: HeapType::Concrete(string_type_idx),
1993 });
1994 let block = BlockType::Result(string_ref);
1995
1996 emitter.instruction(Instruction::LocalGet(scratch));
1997 crate::codegen::function_emitter::expr::emit_is_undefined(emitter, ctx)?;
1998 emitter.emit_if(block);
1999 emit_type_tag(emitter, ctx, "undefined")?;
2000 emitter.emit_else();
2001
2002 emitter.instruction(Instruction::LocalGet(scratch));
2004 emitter.instruction(Instruction::RefIsNull);
2005 emitter.emit_if(block);
2006 emit_type_tag(emitter, ctx, "null")?;
2007 emitter.emit_else();
2008
2009 emitter.instruction(Instruction::LocalGet(scratch));
2010 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
2011 string_type_idx,
2012 )));
2013 emitter.emit_if(block);
2014 emit_type_tag(emitter, ctx, "string")?;
2015 emitter.emit_else();
2016
2017 let num_idx = ctx
2018 .symbols
2019 .boxed_number_type_idx()
2020 .ok_or_else(|| crate::codegen::internal_failure("$BoxedNumber intrinsic registered"))?;
2021 emitter.instruction(Instruction::LocalGet(scratch));
2022 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(num_idx)));
2023 emitter.emit_if(block);
2024 emit_type_tag(emitter, ctx, "number")?;
2025 emitter.emit_else();
2026
2027 let bool_idx = ctx
2028 .symbols
2029 .boxed_boolean_type_idx()
2030 .ok_or_else(|| crate::codegen::internal_failure("$BoxedBoolean intrinsic registered"))?;
2031 emitter.instruction(Instruction::LocalGet(scratch));
2032 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(bool_idx)));
2033 emitter.emit_if(block);
2034 emit_type_tag(emitter, ctx, "boolean")?;
2035 emitter.emit_else();
2036
2037 let closure_idx = ctx
2038 .symbols
2039 .closure_type_idx()
2040 .ok_or_else(|| crate::codegen::internal_failure("$Closure intrinsic registered"))?;
2041 emitter.instruction(Instruction::LocalGet(scratch));
2042 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
2043 emitter.emit_if(block);
2044 emit_type_tag(emitter, ctx, "function")?;
2045 emitter.emit_else();
2046
2047 emit_type_tag(emitter, ctx, "object")?;
2048
2049 for _ in 0..5 {
2050 emitter.emit_end();
2051 }
2052
2053 emitter.emit_end();
2054 Ok(())
2055}
2056
2057fn admits_undefined(ty: &Type) -> bool {
2059 match ty.peel() {
2060 Type::Undefined | Type::Void => true,
2061 Type::Union(members) => members.iter().any(admits_undefined),
2062 _ => false,
2063 }
2064}
2065
2066fn emit_type_tag(
2069 emitter: &mut FunctionEmitter,
2070 ctx: &CodegenCtx,
2071 tag: &str,
2072) -> Result<(), crate::compiler_error::CompilerFailure> {
2073 match ctx.strings.lookup_text(tag) {
2074 Some(pool_idx) => {
2075 crate::codegen::function_emitter::emit_pooled_string(emitter, ctx, pool_idx)
2076 }
2077 None => emit_inline_string_checked(emitter, ctx, tag),
2078 }
2079}
2080
2081fn emit_same_validator_environment(
2084 emitter: &mut FunctionEmitter,
2085 ctx: &CodegenCtx,
2086 index: u32,
2087 count: usize,
2088) -> Result<(), crate::compiler_error::CompilerFailure> {
2089 emitter.instruction(Instruction::I32Const(1));
2090 if count == 0 {
2091 return Ok(());
2092 }
2093 let intr = ctx
2094 .symbols
2095 .intrinsic_type_indices()
2096 .ok_or_else(|| crate::codegen::internal_failure("intrinsics"))?;
2097 let closure = ctx
2098 .symbols
2099 .closure_struct_type_idx(super::field_guards::signature())
2100 .ok_or_else(|| crate::codegen::internal_failure("descriptor closure"))?;
2101 for slot in 0..count {
2102 emitter.instruction(Instruction::LocalGet(1));
2103 emitter.instruction(Instruction::LocalGet(index));
2104 emitter.instruction(Instruction::I32Const(2));
2105 emitter.instruction(Instruction::I32Mul);
2106 emitter.instruction(Instruction::I32Const(1));
2107 emitter.instruction(Instruction::I32Add);
2108 emitter.instruction(Instruction::ArrayGet(intr.raw_array));
2109 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(closure)));
2110 emitter.instruction(Instruction::StructGet {
2111 struct_type_index: closure,
2112 field_index: 2,
2113 });
2114 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
2115 intr.object_fields,
2116 )));
2117 emitter.instruction(Instruction::I32Const(slot as i32));
2118 emitter.instruction(Instruction::ArrayGet(intr.object_fields));
2119 emitter.instruction(Instruction::LocalGet(4));
2120 emitter.instruction(Instruction::I32Const(slot as i32));
2121 emitter.instruction(Instruction::ArrayGet(intr.object_fields));
2122 emitter.instruction(Instruction::RefEq);
2123 emitter.instruction(Instruction::I32And);
2124 }
2125 Ok(())
2126}
2127
2128fn emit_store_validator_environment(
2129 emitter: &mut FunctionEmitter,
2130 ctx: &CodegenCtx,
2131) -> Result<(), crate::compiler_error::CompilerFailure> {
2132 let intr = ctx
2133 .symbols
2134 .intrinsic_type_indices()
2135 .ok_or_else(|| crate::codegen::internal_failure("intrinsics"))?;
2136 let closure = ctx
2137 .symbols
2138 .closure_struct_type_idx(super::field_guards::signature())
2139 .ok_or_else(|| crate::codegen::internal_failure("descriptor closure"))?;
2140 emitter.instruction(Instruction::LocalGet(1));
2141 emitter.instruction(Instruction::LocalGet(3));
2142 emitter.instruction(Instruction::I32Const(2));
2143 emitter.instruction(Instruction::I32Mul);
2144 emitter.instruction(Instruction::I32Const(1));
2145 emitter.instruction(Instruction::I32Add);
2146 emitter.instruction(Instruction::GlobalGet(
2147 ctx.symbols
2148 .closure_vtable_global_idx()
2149 .ok_or_else(|| crate::codegen::internal_failure("closure vtable"))?,
2150 ));
2151 emitter.instruction(Instruction::RefFunc(
2152 *ctx.symbols
2153 .type_descriptor_functions
2154 .get(&Type::Unknown)
2155 .ok_or_else(|| {
2156 crate::codegen::internal_failure(
2157 "validator environment descriptor is not registered",
2158 )
2159 })?,
2160 ));
2161 emitter.instruction(Instruction::LocalGet(4));
2162 emitter.instruction(Instruction::I64Const(0));
2163 emitter.instruction(Instruction::StructNew(closure));
2164 emitter.instruction(Instruction::ArraySet(intr.raw_array));
2165
2166 Ok(())
2167}
2168
2169fn emit_index_conformance(
2170 emitter: &mut FunctionEmitter,
2171 ctx: &CodegenCtx,
2172 object: u32,
2173 index: &crate::IndexSignature,
2174 interface_stack: &mut std::collections::BTreeSet<Type>,
2175 validator_state: Option<ValidatorState>,
2176) -> Result<(), crate::compiler_error::CompilerFailure> {
2177 emitter.instruction(Instruction::LocalGet(object));
2178 let Some(symbol) = ctx.require(
2179 ctx.symbols
2180 .prelude_func_idx("ObjectConstructor##recordValues"),
2181 "record validator imported",
2182 ) else {
2183 return Ok(());
2184 };
2185 emitter.instruction(Instruction::Call(symbol));
2186 let values = emitter.add_anonymous_local(scratch_object_ty(object_idx_of(ctx)?))?;
2187 emitter.instruction(Instruction::LocalSet(values));
2188 emit_structural_test_inner(
2189 emitter,
2190 ctx,
2191 values,
2192 &Type::Array(index.value.clone()),
2193 interface_stack,
2194 validator_state,
2195 )?;
2196 Ok(())
2197}
2198
2199#[cfg(test)]
2200mod representation_invariant_tests {
2201 use super::*;
2202 use crate::codegen::invariant_tests::{assert_internal, with_context};
2203
2204 #[test]
2205 fn primitive_representation_test_returns_internal_failure() {
2206 with_context(
2207 &crate::TypedAst::new(),
2208 &crate::codegen::SymbolTable::default(),
2209 |ctx| {
2210 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
2211 assert_internal(
2212 emit_representation_test(&mut emitter, ctx, 0, &Type::Number).unwrap_err(),
2213 );
2214 },
2215 );
2216 }
2217}