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