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