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