1use crate::codegen::CodegenCtx;
12use crate::codegen::bounds::{
13 emit_checked_index, emit_checked_index_with_length, stash_array_length, stash_index_operand,
14};
15use crate::codegen::cast_check::emit_structural_test;
16use crate::codegen::function_emitter::FunctionEmitter;
17use crate::codegen::function_emitter::cast;
18use crate::codegen::symbol_table::{MethodSlotAbi, may_hold_null};
19use crate::typechecker::infer::narrowing::{
20 BindingId, ReferencePath, cast_info_for, falsy_part, truthy_part,
21};
22use crate::typed_ast::field_runtime_type_is_testable;
23use crate::{
24 BinOp, ExprId, Ident, Intrinsic, Type, TypedExprKind, TypedObjectFieldSource,
25 TypedObjectMember, UnOp,
26};
27use std::collections::HashMap;
28use wasm_encoder::{BlockType, HeapType, Ieee64, Instruction, RefType, ValType};
29
30pub fn emit_expr(
35 emitter: &mut FunctionEmitter,
36 ctx: &CodegenCtx,
37 id: ExprId,
38) -> Result<(), crate::compiler_error::CompilerFailure> {
39 emit_expr_value(emitter, ctx, id)?;
40 crate::codegen::field_guards::attach(
43 emitter,
44 ctx,
45 &ctx.ta
46 .try_expr(id)
47 .map_err(crate::codegen::arena_failure)?
48 .ty,
49 )?;
50 Ok(())
51}
52
53fn emit_receiver(
54 emitter: &mut FunctionEmitter,
55 ctx: &CodegenCtx,
56 id: ExprId,
57) -> Result<(), crate::compiler_error::CompilerFailure> {
58 emit_expr(emitter, ctx, id)?;
59 let source = &ctx
60 .ta
61 .try_expr(id)
62 .map_err(crate::codegen::arena_failure)?
63 .ty;
64 let target = ctx
65 .ta
66 .source_type(id)
67 .map_err(crate::codegen::arena_failure)?;
68 let _: () = if source != target {
69 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, source, target)?;
70 };
71 Ok(())
72}
73
74fn emit_expr_value(
75 emitter: &mut FunctionEmitter,
76 ctx: &CodegenCtx,
77 id: ExprId,
78) -> Result<(), crate::compiler_error::CompilerFailure> {
79 let expr = ctx.ta.try_expr(id).map_err(crate::codegen::arena_failure)?;
80 emitter.record_span(expr.span);
81 if let Some(slot) = emitter.evaluated_slot(id) {
84 emitter.instruction(Instruction::LocalGet(slot));
85 return Ok(());
86 }
87 let _: () = match &expr.kind {
88 TypedExprKind::Number(v) => {
89 emitter.instruction(Instruction::F64Const(Ieee64::from(*v)));
90 }
91 TypedExprKind::BigInt(digits) => emit_bigint_literal(emitter, ctx, digits)?,
92 TypedExprKind::Boolean(b) => {
93 emitter.instruction(Instruction::I32Const(if *b { 1 } else { 0 }));
94 }
95 TypedExprKind::String(_) => {
99 let pool_idx = ctx.strings.locations.get(&id).copied().ok_or_else(|| {
100 crate::codegen::internal_failure("a string literal was not interned")
101 })?;
102 super::emit_pooled_string(emitter, ctx, pool_idx)?;
103 }
104 TypedExprKind::Regex { source, flags } => {
105 emit_regex_literal(emitter, ctx, source, flags)?;
106 }
107 TypedExprKind::Sequence { stmts, result } => {
108 for &stmt in stmts {
109 super::stmt::emit_statement(emitter, ctx, stmt)?;
110 }
111 emit_expr(emitter, ctx, *result)?;
112 }
113 TypedExprKind::EffectThen { effect, result } => {
114 emit_expr(emitter, ctx, *effect)?;
115 emitter.instruction(Instruction::Drop);
116 emit_expr(emitter, ctx, *result)?;
117 }
118 TypedExprKind::Binary { op, lhs, rhs } => {
119 emit_binary(emitter, ctx, *op, *lhs, *rhs, &expr.ty)?;
120 }
121 TypedExprKind::Unary { op, operand } => {
122 emit_unary(emitter, ctx, *op, *operand, &expr.ty)?;
123 }
124 TypedExprKind::LocalRef { ident, boxed } => {
125 if ctx.ta.parameter_inputs.contains(&id) {
126 let (slot, source) = emitter
127 .parameter_input_slots
128 .get(&ident.name)
129 .cloned()
130 .ok_or_else(|| {
131 crate::codegen::internal_failure("parameter input slot is missing")
132 })?;
133 emitter.instruction(Instruction::LocalGet(slot));
134 cast::emit_coerce_to_slot(emitter, ctx, &source, &expr.ty)?;
135 return Ok(());
136 }
137 let slot = emitter.require_local_slot(&ident.name)?;
145 if *boxed {
146 let box_idx = ctx.symbols.box_type_idx(&expr.ty)?.ok_or_else(|| {
147 crate::codegen::internal_failure("box type registered for every boxed LocalRef")
148 })?;
149 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
150 cast::emit_unerase(emitter, ctx, &expr.ty)?;
151 } else {
152 emitter.instruction(Instruction::LocalGet(slot));
153 }
154 }
155 TypedExprKind::LocalNarrowRef { .. } if inferred_never(ctx, id)? => {
158 emitter.instruction(Instruction::Unreachable);
159 }
160 TypedExprKind::LocalNarrowRef { binding, path, .. } => {
161 emit_local_narrow_ref(emitter, ctx, binding, path, &expr.ty)?;
162 }
163 TypedExprKind::GlobalRef { mangled, .. } => {
164 emit_global_ref(emitter, ctx, mangled, &expr.ty)?;
165 }
166 TypedExprKind::FunctionRef { mangled, .. } => {
167 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
168 emit_function_ref(emitter, ctx, mangled, &expr.ty)?;
169 }
170 TypedExprKind::Call { mangled, args, .. } => {
171 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
172 emit_symbol_call(emitter, ctx, mangled, args, &expr.ty)?;
173 }
174 TypedExprKind::SuperCtorCall { parent, args } => {
175 let this = emitter.this_local().ok_or_else(|| {
179 crate::codegen::internal_failure("super(...) only inside a constructor body")
180 })?;
181 emitter.instruction(Instruction::LocalGet(this));
182 let ctor_abi = ctx.symbols.class_ctor_abi(parent).map(<[ValType]>::to_vec);
183 emit_args_into_slots(emitter, ctx, args, ctor_abi.as_deref())?;
184 let init = ctx
185 .symbols
186 .class_ctor_init_func_idx(parent)
187 .ok_or_else(|| crate::codegen::internal_failure("parent ctor init fn allocated"))?;
188 emitter.instruction(Instruction::Call(init));
189 if let Some(mangled) = emitter.ctor_class().cloned() {
192 emit_class_field_setup(emitter, ctx, &mangled)?;
193 }
194 emit_undefined(emitter, ctx)?;
195 }
196 TypedExprKind::SuperMethodCall { owner, name, args } => {
197 let this = emitter.this_local().ok_or_else(|| {
202 crate::codegen::internal_failure("super.method() only inside a method body")
203 })?;
204 emitter.instruction(Instruction::LocalGet(this));
205 let abi = ctx.symbols.class_method_abi(owner, &name.name).cloned();
206 emit_args_into_slots(
207 emitter,
208 ctx,
209 args,
210 abi.as_ref().map(|a| a.params.as_slice()),
211 )?;
212 let func = ctx
213 .symbols
214 .class_method_func_idx(owner, &name.name)
215 .ok_or_else(|| {
216 crate::codegen::internal_failure("parent method body fn allocated")
217 })?;
218 emitter.instruction(Instruction::Call(func));
219 emit_slot_return_cast(emitter, ctx, &expr.ty.clone(), abi.as_ref())?;
220 }
221 TypedExprKind::McpCall { server, tool, args } => {
222 super::mcp::emit_mcp_call(emitter, ctx, server, tool, args)?;
226 }
227 TypedExprKind::CallClosure { callee, args } => {
228 emit_indirect_closure_call(emitter, ctx, *callee, args, &expr.ty)?;
234 }
235 TypedExprKind::GenericCall {
236 mangled,
237 type_args,
238 args,
239 return_cast,
240 type_predicate: _,
241 } => {
242 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
243 emit_generic_call(
244 emitter,
245 ctx,
246 &expr.ty,
247 mangled,
248 type_args,
249 args,
250 return_cast,
251 )?;
252 }
253 TypedExprKind::IntrinsicCall { kind, args } => {
254 emit_intrinsic_call(emitter, ctx, *kind, args, &expr.ty)?;
255 }
256 TypedExprKind::MethodCall {
257 receiver,
258 iface,
259 name,
260 args,
261 type_predicate: _,
262 } => {
263 emit_method_call(
264 emitter, ctx, *receiver, iface, &name.name, args, None, None, &expr.ty,
265 )?;
266 }
267 TypedExprKind::GenericMethodCall {
268 receiver,
269 iface,
270 name,
271 args,
272 return_cast,
273 type_predicate: _,
274 } => {
275 let plain_args: Vec<ExprId> = args.iter().map(|a| a.expr).collect();
276 emit_method_call(
277 emitter,
278 ctx,
279 *receiver,
280 iface,
281 &name.name,
282 &plain_args,
283 Some(args),
284 return_cast.as_ref(),
285 &expr.ty,
286 )?;
287 }
288 TypedExprKind::ObjectLiteral { members, fields } => {
289 emit_object_literal(emitter, ctx, &expr.ty, members, fields)?;
290 }
291 TypedExprKind::FieldAccess { receiver, name } => {
292 emit_field_access(emitter, ctx, id, &expr.ty, receiver, name)?;
293 }
294 TypedExprKind::InterfacePropertyAccess {
295 receiver,
296 iface,
297 name,
298 } => emit_interface_property(emitter, ctx, receiver, iface, name)?,
299 TypedExprKind::ArrayLiteral { elements, .. } => {
300 emit_array_literal(emitter, ctx, elements)?;
301 }
302 TypedExprKind::TupleLiteral { elements, .. } => {
303 emit_tuple_literal(emitter, ctx, elements)?;
304 }
305 TypedExprKind::IndexAccess { receiver, index } => {
306 emit_index_access(emitter, ctx, id, receiver, index, &expr.ty)?;
307 }
308 TypedExprKind::Undefined => emit_undefined(emitter, ctx)?,
309 TypedExprKind::Null => {
310 let _ = ctx; emitter.instruction(Instruction::RefNull(HeapType::Abstract {
322 shared: false,
323 ty: wasm_encoder::AbstractHeapType::None,
324 }));
325 }
326 TypedExprKind::This => {
327 let slot = emitter.this_local().ok_or_else(|| {
328 crate::codegen::internal_failure("`this` has a local receiver or captured binding")
329 })?;
330 if emitter.dynamic_this {
331 emitter.instruction(Instruction::LocalGet(slot));
332 emitter.instruction(Instruction::RefIsNull);
333 emitter.emit_if(BlockType::Empty);
334 crate::codegen::throw::emit_type_error_throw(
335 emitter,
336 ctx,
337 "Unbound function has no this receiver",
338 );
339 emitter.emit_end();
340 }
341 emitter.instruction(Instruction::LocalGet(slot));
342 if emitter.dynamic_this {
343 crate::codegen::cast_check::emit_operation_cast_on_stack(
344 emitter,
345 ctx,
346 &Type::Unknown,
347 &expr.ty,
348 )?;
349 }
350 }
351 TypedExprKind::NumberEnumMember { value, .. } => {
352 let boxed_idx = ctx
359 .symbols
360 .boxed_number_type_idx()
361 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
362 let vtable_global = ctx
363 .symbols
364 .prelude_global_idx("boxed_number_vtable")
365 .ok_or_else(|| {
366 crate::codegen::internal_failure("boxed_number_vtable imported from prelude")
367 })?;
368 emitter.instruction(Instruction::GlobalGet(vtable_global));
369 emitter.instruction(Instruction::F64Const(Ieee64::from(*value)));
370 emitter.instruction(Instruction::StructNew(boxed_idx));
371 }
372 TypedExprKind::StringEnumMember { .. } => {
373 let pool_idx = ctx.strings.locations.get(&id).copied().ok_or_else(|| {
378 crate::codegen::internal_failure("a string enum value was not interned")
379 })?;
380 super::emit_pooled_string(emitter, ctx, pool_idx)?;
381 }
382 TypedExprKind::TypeofTag { value, tag } => {
383 emit_typeof_tag(emitter, ctx, *value, *tag)?;
384 }
385 TypedExprKind::InstanceOf { value, class } => match class.peel() {
386 Type::ClassRef { mangled, .. } => {
387 let vtable_global =
388 ctx.symbols
389 .class_vtable_global_idx(mangled)
390 .ok_or_else(|| {
391 crate::codegen::internal_failure("class vtable global recorded")
392 })?;
393 emit_expr(emitter, ctx, *value)?;
394 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
395 cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
396 }
397 Type::Uint8Array => {
401 let idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
402 crate::codegen::internal_failure("$Uint8Array intrinsic registered")
403 })?;
404 emit_expr(emitter, ctx, *value)?;
405 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
406 }
407 _ => {
408 return Err(crate::codegen::internal_failure(format!(
409 "instanceof codegen with a non-class RHS: {class:?}"
410 )));
411 }
412 },
413 TypedExprKind::Narrowed {
414 path,
415 source,
416 binding,
417 cast_info,
418 inner,
419 } => {
420 emitter.push_scope();
424 if path.chain.is_empty() {
425 let shadow_val = ctx.symbols.value_type(&cast_info.to_ty)?;
426 let shadow = emitter.define_local(binding, shadow_val)?;
427 emit_expr(emitter, ctx, *source)?;
428 crate::codegen::function_emitter::cast::emit_narrowing_cast(
429 emitter, ctx, cast_info,
430 )?;
431 emitter.instruction(Instruction::LocalSet(shadow));
432 } else {
433 emitter.register_narrow_source(&binding.name, *source)?;
434 }
435 emit_expr(emitter, ctx, *inner)?;
436 emitter.pop_scope()?;
437 }
438 TypedExprKind::Closure {
439 params,
440 captured,
441 runtime_generics,
442 ..
443 } => {
444 emit_closure_value(
445 emitter,
446 ctx,
447 id,
448 &expr.ty,
449 params,
450 captured,
451 runtime_generics,
452 )?;
453 }
454 TypedExprKind::Ternary { cond, then_, else_ } => {
459 let result_ty = expr.ty.clone();
460 let block_ty = conditional_block_type(ctx, &result_ty)?;
461 let cond_ty = ctx
462 .ta
463 .try_expr(*cond)
464 .map_err(crate::codegen::arena_failure)?
465 .ty
466 .clone();
467 emit_expr(emitter, ctx, *cond)?;
468 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
469 emitter.emit_if(block_ty);
470 emit_conditional_operand(emitter, ctx, *then_, &result_ty)?;
471 emitter.emit_else();
472 emit_conditional_operand(emitter, ctx, *else_, &result_ty)?;
473 emitter.emit_end();
474 }
475 TypedExprKind::NullishCoalesce { lhs, rhs } => {
478 emit_nullish_coalesce(emitter, ctx, lhs, rhs, &expr.ty)?;
479 }
480 TypedExprKind::OptionalChain { base, parts } => {
488 let result_ty = expr.ty.clone();
489 let base_ty = ctx
490 .ta
491 .try_expr(*base)
492 .map_err(crate::codegen::arena_failure)?
493 .ty
494 .clone();
495 let receiver = if matches!(parts.first(), Some(crate::TypedChainPart::Call { .. })) {
496 emit_callee(emitter, ctx, *base)?
497 } else {
498 emit_expr(emitter, ctx, *base)?;
499 None
500 };
501 emitter.call_receiver = receiver;
502 emit_chain_parts(
503 emitter,
504 ctx,
505 parts,
506 0,
507 &base_ty,
508 &result_ty,
509 ctx.ta.runtime_chain_types.get(&id).map(Vec::as_slice),
510 )?;
511 }
512 TypedExprKind::PostfixUnary { op, target } => {
513 if let crate::PostfixTarget::Index {
514 receiver, index, ..
515 } = target
516 && ctx
517 .ta
518 .source_type(*receiver)
519 .map_err(crate::codegen::arena_failure)?
520 .is_structural_object()
521 {
522 emit_object_index_postfix(
523 emitter,
524 ctx,
525 *receiver,
526 *index,
527 *op,
528 ctx.ta
529 .source_type(id)
530 .map_err(crate::codegen::arena_failure)?,
531 )?;
532 cast::emit_coerce_to_slot(emitter, ctx, &Type::Unknown, &expr.ty)?;
533 } else {
534 emit_postfix_unary(emitter, ctx, *op, target, &expr.ty)?;
535 }
536 }
537 TypedExprKind::NonNullAssert { value } => {
538 crate::codegen::cast_check::emit_non_null_assert(emitter, ctx, *value, &expr.ty)?;
539 }
540 TypedExprKind::Cast {
541 value,
542 target_ty,
543 check,
544 } => {
545 crate::codegen::cast_check::emit_cast(
546 emitter,
547 ctx,
548 *value,
549 target_ty,
550 check.as_deref(),
551 expr.span,
552 )?;
553 }
554 };
555 if matches!(expr.ty, Type::Never) && !completes_when_never(&expr.kind) {
560 emitter.instruction(Instruction::Unreachable);
561 }
562 Ok(())
563}
564
565fn completes_when_never(kind: &TypedExprKind) -> bool {
571 matches!(
572 kind,
573 TypedExprKind::LocalNarrowRef { .. }
574 | TypedExprKind::FieldAccess { .. }
575 | TypedExprKind::IndexAccess { .. }
576 )
577}
578
579pub(crate) fn emit_undefined(
581 emitter: &mut FunctionEmitter<'_>,
582 ctx: &CodegenCtx<'_>,
583) -> Result<(), crate::compiler_error::CompilerFailure> {
584 let global = ctx
585 .symbols
586 .prelude_global_idx(crate::runtime::prelude::undefined::GLOBAL_NAME)
587 .ok_or_else(|| crate::codegen::internal_failure("undefined global was not imported"))?;
588 emitter.instruction(Instruction::GlobalGet(global));
589 Ok(())
590}
591
592pub(crate) fn emit_is_nullish(
594 emitter: &mut FunctionEmitter<'_>,
595 ctx: &CodegenCtx<'_>,
596) -> Result<(), crate::compiler_error::CompilerFailure> {
597 let intrinsic = ctx
598 .symbols
599 .intrinsic_type_indices()
600 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
601 let value = emitter.add_anonymous_local(ValType::Ref(RefType {
602 nullable: true,
603 heap_type: HeapType::Concrete(intrinsic.object),
604 }))?;
605 emitter.instruction(Instruction::LocalSet(value));
606 emitter.instructions(crate::codegen::nullish::is_nullish(
607 &[Instruction::LocalGet(value)],
608 intrinsic,
609 ));
610 Ok(())
611}
612
613pub(crate) fn emit_is_undefined(
614 emitter: &mut FunctionEmitter<'_>,
615 ctx: &CodegenCtx<'_>,
616) -> Result<(), crate::compiler_error::CompilerFailure> {
617 let intrinsic = ctx
618 .symbols
619 .intrinsic_type_indices()
620 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
621 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
622 intrinsic.undefined,
623 )));
624 Ok(())
625}
626
627fn emit_global_ref(
628 emitter: &mut FunctionEmitter,
629 ctx: &CodegenCtx,
630 mangled: &crate::MangledName,
631 result_ty: &Type,
632) -> Result<(), crate::compiler_error::CompilerFailure> {
633 if let Some(iface) = static_interface(ctx, result_ty) {
642 emit_static_value(emitter, ctx, iface)?;
643 if let ValType::Ref(RefType {
644 nullable: false, ..
645 }) = ctx.symbols.value_type(result_ty)?
646 {
647 emitter.instruction(Instruction::RefAsNonNull);
648 }
649 return Ok(());
650 }
651 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
652 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
653 crate::codegen::internal_failure("top-level let/const recorded during codegen")
654 })?;
655 emitter.instruction(Instruction::GlobalGet(idx));
656 if let ValType::Ref(RefType {
657 nullable: false, ..
658 }) = ctx.symbols.value_type(result_ty)?
659 {
660 emitter.instruction(Instruction::RefAsNonNull);
661 }
662
663 Ok(())
664}
665
666fn static_interface<'a>(ctx: &CodegenCtx, ty: &'a Type) -> Option<&'a crate::MangledName> {
668 match ty.peel() {
669 Type::InterfaceRef { mangled, .. }
670 if ctx.symbols.iface_dispatch(mangled) == Some(crate::Dispatch::Static) =>
671 {
672 Some(mangled)
673 }
674 _ => None,
675 }
676}
677
678fn emit_static_value(
679 emitter: &mut FunctionEmitter,
680 ctx: &CodegenCtx,
681 iface: &crate::MangledName,
682) -> Result<(), crate::compiler_error::CompilerFailure> {
683 let tag = ctx.symbols.static_value_tag(iface).ok_or_else(|| {
684 crate::codegen::internal_failure("a static binding read as a value has a recorded tag")
685 })?;
686 super::emit_inline_string_literal(emitter, ctx, tag)?;
687 let helper = ctx
688 .symbols
689 .prelude_func_idx("ObjectConstructor##staticValue")
690 .ok_or_else(|| crate::codegen::internal_failure("the static value helper is imported"))?;
691 emitter.instruction(Instruction::Call(helper));
692 Ok(())
693}
694
695fn emit_function_ref(
696 emitter: &mut FunctionEmitter,
697 ctx: &CodegenCtx,
698 mangled: &crate::MangledName,
699 result_ty: &Type,
700) -> Result<(), crate::compiler_error::CompilerFailure> {
701 let closure_struct_idx = ctx
712 .symbols
713 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
714 .ok_or_else(|| {
715 crate::codegen::internal_failure(
716 "closure struct type registered for every function-as-value",
717 )
718 })?;
719 let adapter_idx = ctx.symbols.adapter_func_idx(mangled).ok_or_else(|| {
720 crate::codegen::internal_failure("adapter func recorded for every function-as-value")
721 })?;
722 let closure_global_idx = ctx
723 .symbols
724 .adapter_closure_global_idx(mangled)
725 .ok_or_else(|| {
726 crate::codegen::internal_failure("closure global recorded for every function-as-value")
727 })?;
728 let vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
729 crate::codegen::internal_failure(
730 "closure vtable global emitted whenever closures or adapters exist",
731 )
732 })?;
733 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
734 emitter.instruction(Instruction::RefIsNull);
735 emitter.emit_if(BlockType::Empty);
736 emitter.instruction(Instruction::GlobalGet(vtable_idx));
737 emitter.instruction(Instruction::RefFunc(adapter_idx));
738 emitter.instruction(Instruction::GlobalGet(vtable_idx));
739 if let Some(metadata) = ctx.symbols.function_argument_metadata.get(mangled) {
740 crate::codegen::call_arguments::wrap(emitter, ctx, metadata)?;
741 }
742 emitter.instruction(Instruction::I64Const(0));
743 emitter.instruction(Instruction::StructNew(closure_struct_idx));
744 emitter.instruction(Instruction::GlobalSet(closure_global_idx));
745 emitter.emit_end();
746 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
747 if ctx.symbols.is_shared_closure_global(mangled) {
748 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
749 closure_struct_idx,
750 )));
751 } else {
752 emitter.instruction(Instruction::RefAsNonNull);
753 }
754
755 Ok(())
756}
757
758fn emit_symbol_call(
759 emitter: &mut FunctionEmitter,
760 ctx: &CodegenCtx,
761 mangled: &crate::MangledName,
762 args: &[ExprId],
763 result_ty: &Type,
764) -> Result<(), crate::compiler_error::CompilerFailure> {
765 let target = ctx.symbols.top_level_fn(mangled).ok_or_else(|| {
778 crate::codegen::internal_failure(format!(
779 "Call references unknown top-level fn `{}`",
780 mangled.as_str()
781 ))
782 })?;
783 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
784 let Type::ClassRef { mangled: class, .. } = result_ty.peel() else {
785 return Err(crate::codegen::internal_failure("constructor class"));
786 };
787 emit_args_into_slots(emitter, ctx, args, ctx.symbols.class_ctor_abi(class))?;
788 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
789 emitter.instruction(Instruction::Call(target.wasm_idx));
790 } else {
791 emit_direct_call(
792 emitter,
793 ctx,
794 target.is_host,
795 target.wasm_idx,
796 &target.params,
797 &target.ret,
798 args,
799 )?;
800 }
801 if result_ty.is_void() {
802 cast::emit_box(emitter, ctx, &target.ret)?;
803 }
804
805 Ok(())
806}
807
808fn emit_interface_property(
809 emitter: &mut FunctionEmitter,
810 ctx: &CodegenCtx,
811 receiver: &ExprId,
812 iface: &crate::MangledName,
813 name: &Ident,
814) -> Result<(), crate::compiler_error::CompilerFailure> {
815 let key = crate::mangle::extend(iface, &name.name);
821 if ctx.symbols.iface_dispatch(iface) == Some(crate::Dispatch::Static) {
824 let global_idx = ctx.symbols.global_idx(&key).ok_or_else(|| {
825 crate::codegen::internal_failure(
826 "static interface property recorded as a global import",
827 )
828 })?;
829 emitter.instruction(Instruction::GlobalGet(global_idx));
830 return Ok(());
831 }
832 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
833 emit_receiver(emitter, ctx, *receiver)?;
834 emit_inline_length(emitter, ctx, struct_idx);
835 return Ok(());
836 }
837 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
838 crate::codegen::internal_failure(
839 "interface property getter recorded during the dependency-import pass",
840 )
841 })?;
842 emit_receiver(emitter, ctx, *receiver)?;
843 if matches!(
856 ctx.ta
857 .source_type(*receiver)
858 .map_err(crate::codegen::arena_failure)?
859 .peel(),
860 Type::InterfaceRef { .. }
861 ) {
862 emitter.instruction(Instruction::RefAsNonNull);
863 }
864 emitter.instruction(Instruction::Call(func_idx));
865
866 Ok(())
867}
868
869fn emit_index_access(
870 emitter: &mut FunctionEmitter,
871 ctx: &CodegenCtx,
872 id: ExprId,
873 receiver: &ExprId,
874 index: &ExprId,
875 result_ty: &Type,
876) -> Result<(), crate::compiler_error::CompilerFailure> {
877 let recv_ty = ctx
882 .ta
883 .source_type(*receiver)
884 .map_err(crate::codegen::arena_failure)?
885 .clone();
886 if recv_ty.is_structural_object() {
887 emit_expr(emitter, ctx, *receiver)?;
888 cast::emit_box(
889 emitter,
890 ctx,
891 &ctx.ta
892 .try_expr(*receiver)
893 .map_err(crate::codegen::arena_failure)?
894 .ty,
895 )?;
896 emit_expr(emitter, ctx, *index)?;
897 crate::codegen::cast_check::emit_operation_cast_on_stack(
898 emitter,
899 ctx,
900 &ctx.ta
901 .try_expr(*index)
902 .map_err(crate::codegen::arena_failure)?
903 .ty,
904 &Type::String,
905 )?;
906 let Some(symbol) = ctx.require(
907 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
908 "dynamic read imported",
909 ) else {
910 return Ok(());
911 };
912 emitter.instruction(Instruction::Call(symbol));
913 let checked_ty = ctx
914 .ta
915 .source_type(id)
916 .map_err(crate::codegen::arena_failure)?;
917 crate::codegen::cast_check::emit_checked_cast_on_stack(
918 emitter,
919 ctx,
920 &Type::Unknown,
921 checked_ty,
922 )?;
923 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
924 } else if recv_ty.peel() == &Type::Uint8Array {
925 emit_expr(emitter, ctx, *receiver)?;
926 emit_uint8_index_with_receiver_on_stack(emitter, ctx, *index)?;
927 } else {
928 emit_expr(emitter, ctx, *receiver)?;
929 emit_bounds_checked_index_with_receiver_on_stack(
930 emitter,
931 ctx,
932 *index,
933 result_ty,
934 is_optional_tuple_position(ctx, &recv_ty, *index)?,
935 )?;
936 }
937
938 Ok(())
939}
940
941fn emit_nullish_coalesce(
942 emitter: &mut FunctionEmitter,
943 ctx: &CodegenCtx,
944 lhs: &ExprId,
945 rhs: &ExprId,
946 result_ty: &Type,
947) -> Result<(), crate::compiler_error::CompilerFailure> {
948 let result_ty = result_ty.clone();
949
950 let result_val = ctx.symbols.value_type(&result_ty)?;
951 let lhs_ty = ctx
952 .ta
953 .try_expr(*lhs)
954 .map_err(crate::codegen::arena_failure)?
955 .ty
956 .clone();
957 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
958 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
959 if lhs_is_ref {
960 let tmp = emitter.add_anonymous_local(lhs_val)?;
961 emit_expr(emitter, ctx, *lhs)?;
962 emitter.instruction(Instruction::LocalTee(tmp));
963 emit_is_nullish(emitter, ctx)?;
964 emitter.emit_if(BlockType::Result(result_val));
965 emit_conditional_operand(emitter, ctx, *rhs, &result_ty)?;
966 emitter.emit_else();
967 emitter.instruction(Instruction::LocalGet(tmp));
968 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, &result_ty)?;
978 emitter.emit_end();
979 } else {
980 emit_expr(emitter, ctx, *lhs)?;
982 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
983 emitter, ctx, &lhs_ty, &result_ty,
984 )?;
985 }
986
987 Ok(())
988}
989
990fn conditional_block_type(
993 ctx: &CodegenCtx,
994 result_ty: &Type,
995) -> Result<BlockType, crate::compiler_error::CompilerFailure> {
996 Ok(BlockType::Result(ctx.symbols.value_type(result_ty)?))
997}
998
999fn emit_conditional_operand(
1003 emitter: &mut FunctionEmitter,
1004 ctx: &CodegenCtx,
1005 operand: ExprId,
1006 result_ty: &Type,
1007) -> Result<(), crate::compiler_error::CompilerFailure> {
1008 let operand_ty = ctx
1009 .ta
1010 .try_expr(operand)
1011 .map_err(crate::codegen::arena_failure)?
1012 .ty
1013 .clone();
1014 emit_expr(emitter, ctx, operand)?;
1015 cast::emit_coerce_to_slot(emitter, ctx, &operand_ty, result_ty)
1016}
1017
1018fn emit_generic_call(
1025 emitter: &mut FunctionEmitter,
1026 ctx: &CodegenCtx,
1027 result_ty: &Type,
1028 mangled: &crate::MangledName,
1029 type_args: &[Type],
1030 args: &[crate::GenericArgument],
1031 return_cast: &Option<Type>,
1032) -> Result<(), crate::compiler_error::CompilerFailure> {
1033 let (wasm_idx, target_params, target_return) = match ctx.symbols.top_level_fn(mangled) {
1048 Some(target) => (target.wasm_idx, target.params.clone(), target.ret.clone()),
1049 None => {
1050 return Err(crate::codegen::internal_failure(
1051 "generic call target is not registered",
1052 ));
1053 }
1054 };
1055 for (i, arg) in args.iter().enumerate() {
1056 emit_expr(emitter, ctx, arg.expr)?;
1057 if arg.is_generic {
1058 let arg_ty = ctx
1059 .ta
1060 .try_expr(arg.expr)
1061 .map_err(crate::codegen::arena_failure)?
1062 .ty
1063 .clone();
1064 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &arg_ty)?;
1065 } else if let Some(param_ty) = target_params.get(i) {
1066 let arg_ty = ctx
1071 .ta
1072 .try_expr(arg.expr)
1073 .map_err(crate::codegen::arena_failure)?
1074 .ty
1075 .clone();
1076 cast::emit_coerce_to_slot(emitter, ctx, &arg_ty, param_ty)?;
1077 }
1078 }
1079 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
1080 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
1081 }
1082 if ctx.symbols.runtime_generic_functions.contains(mangled) {
1083 crate::codegen::runtime_descriptors::environment(emitter, ctx, type_args)?;
1084 }
1085 emitter.instruction(Instruction::Call(wasm_idx));
1086 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
1087 emit_undefined(emitter, ctx)?;
1088 }
1089 if let Some(ty) = return_cast {
1090 cast::emit_cast_to(emitter, ctx, ty)?;
1091 } else {
1092 cast::emit_coerce_to_slot(emitter, ctx, &target_return, result_ty)?;
1093 }
1094
1095 Ok(())
1096}
1097
1098fn emit_object_literal(
1099 emitter: &mut FunctionEmitter,
1100 ctx: &CodegenCtx,
1101 result_ty: &Type,
1102 members: &[TypedObjectMember],
1103 fields: &[crate::TypedObjectFieldOrigin],
1104) -> Result<(), crate::compiler_error::CompilerFailure> {
1105 if members
1106 .iter()
1107 .any(|member| matches!(member, TypedObjectMember::Computed { .. }))
1108 {
1109 emit_computed_object(emitter, ctx, members)?;
1110 return Ok(());
1111 }
1112 if !matches!(result_ty, Type::Object { .. } | Type::InterfaceRef { .. }) {
1113 return Err(crate::codegen::internal_failure(
1114 "object literal requires an object representation",
1115 ));
1116 }
1117 let structural_ty = crate::typed_ast::object_literal_layout(result_ty, fields);
1118 if members
1119 .iter()
1120 .any(|member| matches!(member, TypedObjectMember::Spread { .. }))
1121 {
1122 emit_object_spread(emitter, ctx, members, &structural_ty)?;
1123 return Ok(());
1124 }
1125 let vtable_idx = ctx
1126 .symbols
1127 .vtable_global_idx(&structural_ty)
1128 .ok_or_else(|| {
1129 crate::codegen::internal_failure("vtable global recorded during object-emission pass")
1130 })?;
1131 let object_shape_idx = ctx
1132 .symbols
1133 .object_subtype_idx(&structural_ty)
1134 .ok_or_else(|| {
1135 crate::codegen::internal_failure(
1136 "object shape type recorded during object-emission pass",
1137 )
1138 })?;
1139 let Type::Object {
1140 fields: declared_fields,
1141 ..
1142 } = &structural_ty
1143 else {
1144 return Err(crate::codegen::internal_failure(
1145 "object literal has no structural representation",
1146 ));
1147 };
1148 let shape_key: Vec<_> = declared_fields
1149 .iter()
1150 .map(|(name, field)| crate::codegen::field_names::FieldName {
1151 name: name.clone(),
1152 optional: field.optional,
1153 is_accessor: false,
1154 is_private: false,
1155 })
1156 .collect();
1157 let intrinsics = ctx
1158 .symbols
1159 .intrinsic_type_indices()
1160 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1161
1162 let evaluated = evaluate_object_members(emitter, ctx, members)?;
1164
1165 emitter.instruction(Instruction::GlobalGet(vtable_idx));
1166 crate::codegen::field_names::emit_instance_names(emitter, ctx, &shape_key, |name| {
1167 fields.iter().any(|field| {
1168 field.name.name == name && !matches!(field.source, TypedObjectFieldSource::Absent(_))
1169 })
1170 })?;
1171
1172 let by_name: std::collections::BTreeMap<&str, &crate::TypedObjectFieldOrigin> =
1177 fields.iter().map(|f| (f.name.name.as_str(), f)).collect();
1178 for (name, field) in declared_fields {
1179 if let Some(origin) = by_name.get(name.as_str()) {
1180 let (TypedObjectFieldSource::Literal(value) | TypedObjectFieldSource::Absent(value)) =
1181 &origin.source
1182 else {
1183 return Err(crate::codegen::internal_failure(
1184 "spread field reached plain object lowering",
1185 ));
1186 };
1187 emitter.instruction(Instruction::LocalGet(*evaluated.get(value).ok_or_else(
1188 || crate::codegen::internal_failure("object field value was not evaluated"),
1189 )?));
1190 } else {
1191 if !field.optional {
1192 return Err(crate::codegen::internal_failure(
1193 "required object field is missing from the literal",
1194 ));
1195 }
1196 emit_undefined(emitter, ctx)?;
1197 }
1198 }
1199 emitter.instruction(Instruction::ArrayNewFixed {
1200 array_type_index: intrinsics.object_fields,
1201 array_size: crate::codegen::wasm_u32(declared_fields.len())?,
1202 });
1203 emitter.instruction(Instruction::RefNull(HeapType::ANY));
1204 emitter.instruction(Instruction::StructNew(object_shape_idx));
1205
1206 Ok(())
1207}
1208
1209fn emit_field_access(
1210 emitter: &mut FunctionEmitter,
1211 ctx: &CodegenCtx,
1212 id: ExprId,
1213 result_ty: &Type,
1214 receiver: &ExprId,
1215 name: &Ident,
1216) -> Result<(), crate::compiler_error::CompilerFailure> {
1217 let receiver_ty = ctx
1218 .ta
1219 .source_type(*receiver)
1220 .map_err(crate::codegen::arena_failure)?
1221 .clone();
1222 if ctx.ta.has_string_index(&receiver_ty) {
1223 emit_expr(emitter, ctx, *receiver)?;
1224 cast::emit_box(
1225 emitter,
1226 ctx,
1227 &ctx.ta
1228 .try_expr(*receiver)
1229 .map_err(crate::codegen::arena_failure)?
1230 .ty,
1231 )?;
1232 let Some(symbol) = ctx.require(
1233 ctx.symbols.field_name_string_global_idx(&name.name),
1234 "property name collected",
1235 ) else {
1236 return Ok(());
1237 };
1238 emitter.instruction(Instruction::GlobalGet(symbol));
1239 let Some(symbol) = ctx.require(
1240 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
1241 "record read imported",
1242 ) else {
1243 return Ok(());
1244 };
1245 emitter.instruction(Instruction::Call(symbol));
1246 let checked_ty = ctx
1247 .ta
1248 .source_type(id)
1249 .map_err(crate::codegen::arena_failure)?;
1250 crate::codegen::cast_check::emit_checked_cast_on_stack(
1251 emitter,
1252 ctx,
1253 &Type::Unknown,
1254 checked_ty,
1255 )?;
1256 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
1257 return Ok(());
1258 }
1259 if let Type::ClassRef { mangled, .. } = receiver_ty.peel() {
1261 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
1262 emit_receiver(emitter, ctx, *receiver)?;
1263 emit_class_method_value(
1264 emitter,
1265 ctx,
1266 &receiver_ty,
1267 &name.name,
1268 result_ty,
1269 ctx.ta
1270 .source_type(id)
1271 .map_err(crate::codegen::arena_failure)?,
1272 )?;
1273 return Ok(());
1274 }
1275 if ctx.symbols.class_field_slot(mangled, &name.name).is_none() {
1277 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
1278 emit_method_call(
1279 emitter,
1280 ctx,
1281 *receiver,
1282 mangled,
1283 &getter,
1284 &[],
1285 None,
1286 None,
1287 &ctx.ta
1288 .try_expr(id)
1289 .map_err(crate::codegen::arena_failure)?
1290 .ty,
1291 )?;
1292 return Ok(());
1293 }
1294 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
1295 crate::codegen::internal_failure("class struct type recorded in classes::emit")
1296 })?;
1297 let slot = ctx
1298 .symbols
1299 .class_field_slot(mangled, &name.name)
1300 .ok_or_else(|| {
1301 crate::codegen::internal_failure("class field slot recorded in classes::emit")
1302 })?;
1303 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1304 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1305 })?;
1306 emit_receiver(emitter, ctx, *receiver)?;
1307 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&receiver_ty)?)?;
1308 emitter.instruction(Instruction::LocalTee(object));
1309 emitter.instruction(Instruction::StructGet {
1310 struct_type_index: struct_idx,
1311 field_index: 2,
1312 });
1313 emitter.instruction(Instruction::I32Const(slot as i32));
1314 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
1315 if ctx
1316 .symbols
1317 .class_field_narrowing_check(mangled, &name.name)
1318 .is_some()
1319 {
1320 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
1321 }
1322 emit_class_field_slot_cast_as(
1323 emitter,
1324 ctx,
1325 mangled,
1326 &name.name,
1327 &ctx.ta
1328 .try_expr(id)
1329 .map_err(crate::codegen::arena_failure)?
1330 .ty,
1331 ctx.ta
1332 .source_type(id)
1333 .map_err(crate::codegen::arena_failure)?,
1334 )?;
1335 return Ok(());
1336 }
1337 let intrinsics = ctx
1338 .symbols
1339 .intrinsic_type_indices()
1340 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1341 let field_ty = ctx
1344 .ta
1345 .try_expr(id)
1346 .map_err(crate::codegen::arena_failure)?
1347 .ty
1348 .clone();
1349 emit_receiver(emitter, ctx, *receiver)?;
1352 let rcv_local = stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
1353 emit_object_property_read_as(
1354 emitter,
1355 ctx,
1356 rcv_local,
1357 &name.name,
1358 &field_ty,
1359 ctx.ta
1360 .source_type(id)
1361 .map_err(crate::codegen::arena_failure)?,
1362 )?;
1363
1364 Ok(())
1365}
1366
1367fn emit_closure_value(
1368 emitter: &mut FunctionEmitter,
1369 ctx: &CodegenCtx,
1370 id: ExprId,
1371 result_ty: &Type,
1372 params: &[crate::TypedParam],
1373 captured: &[crate::CapturedVar],
1374 runtime_generics: &[String],
1375) -> Result<(), crate::compiler_error::CompilerFailure> {
1376 let closure_struct_idx = ctx
1394 .symbols
1395 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
1396 .ok_or_else(|| {
1397 crate::codegen::internal_failure(
1398 "closure struct type registered for every closure-typed expression",
1399 )
1400 })?;
1401 let env_type_idx = ctx.symbols.env_type_idx(id).ok_or_else(|| {
1402 crate::codegen::internal_failure("env type registered for every closure expression")
1403 })?;
1404 let closure_func_idx = ctx
1405 .symbols
1406 .closure_func_idx(id)
1407 .ok_or_else(|| crate::codegen::internal_failure("closure body function index allocated"))?;
1408 let closure_vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
1409 crate::codegen::internal_failure("closure vtable global emitted whenever closures exist")
1410 })?;
1411
1412 emitter.instruction(Instruction::GlobalGet(closure_vtable_idx));
1414
1415 emitter.instruction(Instruction::RefFunc(closure_func_idx));
1417
1418 for c in captured {
1421 emit_captured_load(emitter, ctx, c)?;
1422 }
1423 if !runtime_generics.is_empty() {
1424 let types: Vec<_> = runtime_generics
1425 .iter()
1426 .cloned()
1427 .map(Type::TypeVar)
1428 .collect();
1429 crate::codegen::runtime_descriptors::environment(emitter, ctx, &types)?;
1430 }
1431 let self_environment = if ctx.ta.closure_names.contains_key(&id) {
1432 emitter.instruction(Instruction::RefNull(HeapType::Concrete(
1433 ctx.symbols
1434 .intrinsic_type_indices()
1435 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
1436 .object,
1437 )));
1438 Some(emitter.add_anonymous_local(ValType::Ref(RefType {
1439 nullable: false,
1440 heap_type: HeapType::Concrete(env_type_idx),
1441 }))?)
1442 } else {
1443 None
1444 };
1445 emitter.instruction(Instruction::StructNew(env_type_idx));
1446 if let Some(local) = self_environment {
1447 emitter.instruction(Instruction::LocalTee(local));
1448 }
1449 if let Some(metadata) = crate::codegen::call_arguments::typed_metadata(params) {
1450 crate::codegen::call_arguments::wrap(emitter, ctx, &metadata)?;
1451 }
1452
1453 if ctx.ta.closure_this.contains_key(&id) {
1454 crate::codegen::this_binding::wrap(emitter, ctx)?;
1455 }
1456 emitter.instruction(Instruction::I64Const(0));
1460 emitter.instruction(Instruction::StructNew(closure_struct_idx));
1461 if let Some(environment) = self_environment {
1462 let closure = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1463 emitter.instruction(Instruction::LocalSet(closure));
1464 emitter.instruction(Instruction::LocalGet(environment));
1465 emitter.instruction(Instruction::LocalGet(closure));
1466 emitter.instruction(Instruction::StructSet {
1467 struct_type_index: env_type_idx,
1468 field_index: crate::codegen::wasm_u32(
1469 captured
1470 .len()
1471 .checked_add(usize::from(!runtime_generics.is_empty()))
1472 .ok_or_else(|| {
1473 crate::codegen::internal_failure("closure capture count overflow")
1474 })?,
1475 )?,
1476 });
1477 emitter.instruction(Instruction::LocalGet(closure));
1478 }
1479
1480 Ok(())
1481}
1482
1483fn inferred_never(
1486 ctx: &CodegenCtx,
1487 id: ExprId,
1488) -> Result<bool, crate::compiler_error::CompilerFailure> {
1489 let ty = ctx
1490 .ta
1491 .source_type(id)
1492 .map_err(crate::codegen::arena_failure)?;
1493 Ok(matches!(ty, Type::Never))
1494}
1495
1496fn emit_local_narrow_ref(
1497 emitter: &mut FunctionEmitter<'_>,
1498 ctx: &CodegenCtx<'_>,
1499 binding: &Ident,
1500 path: &ReferencePath,
1501 narrowed_ty: &Type,
1502) -> Result<(), crate::compiler_error::CompilerFailure> {
1503 if !path.chain.is_empty()
1504 && let Some(source) = emitter.narrow_source(&binding.name)
1505 {
1506 emit_expr(emitter, ctx, source)?;
1509 crate::codegen::cast_check::emit_checked_cast_on_stack(
1510 emitter,
1511 ctx,
1512 &ctx.ta
1513 .try_expr(source)
1514 .map_err(crate::codegen::arena_failure)?
1515 .ty,
1516 narrowed_ty,
1517 )?;
1518 return Ok(());
1519 }
1520 let Some((slot, slot_ty)) = emitter.narrowed_read_slot(&binding.name) else {
1521 if let BindingId::Local { name, .. } = &path.root
1526 && let Some((slot, slot_ty)) = emitter.narrowed_read_slot(name)
1527 {
1528 emitter.instruction(Instruction::LocalGet(slot));
1529 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1530 if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1531 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1532 }
1533 return Ok(());
1534 }
1535 if let BindingId::Global(mangled) = &path.root {
1536 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1537 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1538 })?;
1539 emitter.instruction(Instruction::GlobalGet(idx));
1540 let source_ty = ctx.symbols.global_type(mangled).ok_or_else(|| {
1541 crate::codegen::internal_failure("language globals retain their declared type")
1542 })?;
1543 crate::codegen::cast_check::emit_checked_cast_on_stack(
1544 emitter,
1545 ctx,
1546 source_ty,
1547 narrowed_ty,
1548 )?;
1549 return Ok(());
1550 }
1551 if let Some(source) = emitter.narrow_source(&binding.name) {
1552 emit_expr(emitter, ctx, source)?;
1553 let source_ty = &ctx
1554 .ta
1555 .try_expr(source)
1556 .map_err(crate::codegen::arena_failure)?
1557 .ty;
1558 let cast_info = cast_info_for(source_ty.clone(), narrowed_ty.clone());
1559 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1560 return Ok(());
1561 }
1562 return Err(crate::codegen::internal_failure(format!(
1563 "narrow binding `{}` (path {path:?}) not registered in codegen scope — \
1564 the region's source names a shadow from a scope that already closed",
1565 binding.name,
1566 )));
1567 };
1568 emitter.instruction(Instruction::LocalGet(slot));
1569 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1570 let _: () = if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1571 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1572 };
1573 Ok(())
1574}
1575
1576fn unbox_if_boxed(
1584 emitter: &mut FunctionEmitter<'_>,
1585 ctx: &CodegenCtx<'_>,
1586 slot_ty: ValType,
1587) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1588 let ValType::Ref(RefType {
1589 heap_type: HeapType::Concrete(idx),
1590 ..
1591 }) = slot_ty
1592 else {
1593 return Ok(slot_ty);
1594 };
1595 let Some(payload) = ctx.symbols.box_payload_type(idx) else {
1596 return Ok(slot_ty);
1597 };
1598 let local = emitter.add_anonymous_local(slot_ty)?;
1599 emitter.instruction(Instruction::LocalSet(local));
1600 super::binding_cells::read(emitter, ctx, local, idx)?;
1601 Ok(payload)
1602}
1603
1604fn emit_array_literal(
1607 emitter: &mut FunctionEmitter,
1608 ctx: &CodegenCtx,
1609 elements: &[crate::TypedArrayElement],
1610) -> Result<(), crate::compiler_error::CompilerFailure> {
1611 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1632 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1633 })?;
1634 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1635 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1636 })?;
1637 let array_vtable_global = ctx
1638 .symbols
1639 .prelude_global_idx("array_vtable")
1640 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1641
1642 let has_spread = elements
1643 .iter()
1644 .any(|e| matches!(e, crate::TypedArrayElement::Spread(_)));
1645
1646 if has_spread {
1647 emit_spread_array_literal(emitter, ctx, elements)?;
1648 } else {
1649 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1650 for el in elements {
1651 let elem_id = el.expr_id();
1652 emit_expr(emitter, ctx, elem_id)?;
1653 let elem_ty = ctx
1654 .ta
1655 .try_expr(elem_id)
1656 .map_err(crate::codegen::arena_failure)?
1657 .ty
1658 .clone();
1659 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &elem_ty)?;
1660 }
1661 emitter.instruction(Instruction::ArrayNewFixed {
1662 array_type_index: raw_array_idx,
1663 array_size: crate::codegen::wasm_u32(elements.len())?,
1664 });
1665 emitter.instruction(Instruction::I32Const(
1666 i32::try_from(elements.len())
1667 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1668 ));
1669 emitter.instruction(Instruction::StructNew(array_idx));
1670 }
1671 Ok(())
1672}
1673
1674fn emit_tuple_literal(
1675 emitter: &mut FunctionEmitter,
1676 ctx: &CodegenCtx,
1677 elements: &[ExprId],
1678) -> Result<(), crate::compiler_error::CompilerFailure> {
1679 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1681 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1682 })?;
1683 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1684 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1685 })?;
1686 let array_vtable_global = ctx
1687 .symbols
1688 .prelude_global_idx("array_vtable")
1689 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1690 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1691 for &elem_id in elements {
1692 emit_expr(emitter, ctx, elem_id)?;
1693 crate::codegen::function_emitter::cast::emit_box(
1694 emitter,
1695 ctx,
1696 &ctx.ta
1697 .try_expr(elem_id)
1698 .map_err(crate::codegen::arena_failure)?
1699 .ty,
1700 )?;
1701 }
1702 emitter.instruction(Instruction::ArrayNewFixed {
1703 array_type_index: raw_array_idx,
1704 array_size: crate::codegen::wasm_u32(elements.len())?,
1705 });
1706 emitter.instruction(Instruction::I32Const(
1707 i32::try_from(elements.len())
1708 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1709 ));
1710 emitter.instruction(Instruction::StructNew(array_idx));
1711 Ok(())
1712}
1713
1714fn emit_spread_array_literal(
1715 emitter: &mut FunctionEmitter,
1716 ctx: &CodegenCtx,
1717 elements: &[crate::TypedArrayElement],
1718) -> Result<(), crate::compiler_error::CompilerFailure> {
1719 let array = ctx
1720 .symbols
1721 .array_type_idx()
1722 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?;
1723 let raw = ctx
1724 .symbols
1725 .raw_array_type_idx()
1726 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?;
1727 let raw_type = ValType::Ref(RefType {
1728 nullable: false,
1729 heap_type: HeapType::Concrete(raw),
1730 });
1731 let total = emitter.add_anonymous_local(ValType::I32)?;
1732 let offset = emitter.add_anonymous_local(ValType::I32)?;
1733 let destination = emitter.add_anonymous_local(raw_type)?;
1734 emitter.instruction(Instruction::I32Const(0));
1735 emitter.instruction(Instruction::LocalSet(total));
1736 let mut chunks = Vec::with_capacity(elements.len());
1739 for element in elements {
1740 emit_literal_chunk(emitter, ctx, element, array, raw)?;
1741 let chunk = emitter.add_anonymous_local(raw_type)?;
1742 emitter.instruction(Instruction::LocalTee(chunk));
1743 emitter.instruction(Instruction::ArrayLen);
1744 emitter.instruction(Instruction::LocalGet(total));
1745 emitter.instruction(Instruction::I32Add);
1746 emitter.instruction(Instruction::LocalSet(total));
1747 chunks.push(chunk);
1748 }
1749 emitter.instruction(Instruction::LocalGet(total));
1750 emitter.instruction(Instruction::ArrayNewDefault(raw));
1751 emitter.instruction(Instruction::LocalSet(destination));
1752 emitter.instruction(Instruction::I32Const(0));
1753 emitter.instruction(Instruction::LocalSet(offset));
1754 for chunk in chunks {
1755 emitter.instruction(Instruction::LocalGet(destination));
1756 emitter.instruction(Instruction::LocalGet(offset));
1757 emitter.instruction(Instruction::LocalGet(chunk));
1758 emitter.instruction(Instruction::I32Const(0));
1759 emitter.instruction(Instruction::LocalGet(chunk));
1760 emitter.instruction(Instruction::ArrayLen);
1761 emitter.instruction(Instruction::ArrayCopy {
1762 array_type_index_dst: raw,
1763 array_type_index_src: raw,
1764 });
1765 emitter.instruction(Instruction::LocalGet(offset));
1766 emitter.instruction(Instruction::LocalGet(chunk));
1767 emitter.instruction(Instruction::ArrayLen);
1768 emitter.instruction(Instruction::I32Add);
1769 emitter.instruction(Instruction::LocalSet(offset));
1770 }
1771 emitter.instruction(Instruction::GlobalGet(
1772 ctx.symbols
1773 .prelude_global_idx("array_vtable")
1774 .ok_or_else(|| crate::codegen::internal_failure("array vtable declared"))?,
1775 ));
1776 emitter.instruction(Instruction::LocalGet(destination));
1777 emitter.instruction(Instruction::LocalGet(total));
1778 emitter.instruction(Instruction::StructNew(array));
1779 Ok(())
1780}
1781
1782fn emit_literal_chunk(
1783 emitter: &mut FunctionEmitter,
1784 ctx: &CodegenCtx,
1785 element: &crate::TypedArrayElement,
1786 array: u32,
1787 raw: u32,
1788) -> Result<(), crate::compiler_error::CompilerFailure> {
1789 if let crate::TypedArrayElement::Value(value) = element {
1790 emit_expr(emitter, ctx, *value)?;
1791 cast::emit_box(
1792 emitter,
1793 ctx,
1794 &ctx.ta
1795 .try_expr(*value)
1796 .map_err(crate::codegen::arena_failure)?
1797 .ty,
1798 )?;
1799 emitter.instruction(Instruction::ArrayNewFixed {
1800 array_type_index: raw,
1801 array_size: 1,
1802 });
1803 return Ok(());
1804 }
1805 emit_receiver(emitter, ctx, element.expr_id())?;
1808 let raw_type = ValType::Ref(RefType {
1809 nullable: false,
1810 heap_type: HeapType::Concrete(raw),
1811 });
1812 let source = emitter.add_anonymous_local(raw_type)?;
1813 let snapshot = emitter.add_anonymous_local(raw_type)?;
1814 let length = stash_array_length(emitter, array)?;
1815 emitter.instruction(Instruction::StructGet {
1816 struct_type_index: array,
1817 field_index: 1,
1818 });
1819 emitter.instruction(Instruction::LocalSet(source));
1820 emitter.instruction(Instruction::LocalGet(length));
1821 emitter.instruction(Instruction::ArrayNewDefault(raw));
1822 emitter.instruction(Instruction::LocalTee(snapshot));
1823 emitter.instruction(Instruction::I32Const(0));
1824 emitter.instruction(Instruction::LocalGet(source));
1825 emitter.instruction(Instruction::I32Const(0));
1826 emitter.instruction(Instruction::LocalGet(length));
1827 emitter.instruction(Instruction::ArrayCopy {
1828 array_type_index_dst: raw,
1829 array_type_index_src: raw,
1830 });
1831 emitter.instruction(Instruction::LocalGet(snapshot));
1832 Ok(())
1833}
1834
1835fn emit_postfix_unary(
1839 emitter: &mut FunctionEmitter<'_>,
1840 ctx: &CodegenCtx<'_>,
1841 op: crate::PostfixOp,
1842 target: &crate::PostfixTarget,
1843 result_ty: &Type,
1844) -> Result<(), crate::compiler_error::CompilerFailure> {
1845 let _: () = match target {
1846 crate::PostfixTarget::Local {
1847 ident,
1848 boxed,
1849 target_ty,
1850 } => {
1851 let slot = emitter.write_slot(&ident.name)?;
1853 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1854 let box_idx = if *boxed {
1855 Some(ctx.symbols.box_type_idx(target_ty)?.ok_or_else(|| {
1856 crate::codegen::internal_failure("box type registered for postfix operand")
1857 })?)
1858 } else {
1859 None
1860 };
1861 if let Some(box_idx) = box_idx {
1862 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
1863 } else {
1864 emitter.instruction(Instruction::LocalGet(slot));
1865 }
1866 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1867 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1868 emit_postfix_numeric(emitter, ctx, result_ty);
1869 emitter.instruction(Instruction::LocalSet(old));
1870 if box_idx.is_some() {
1871 emitter.instruction(Instruction::LocalGet(slot));
1872 }
1873 emitter.instruction(Instruction::LocalGet(old));
1874 emit_postfix_delta(emitter, ctx, op, result_ty);
1875 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1876 if let Some(box_idx) = box_idx {
1877 emitter.instruction(Instruction::StructSet {
1878 struct_type_index: box_idx,
1879 field_index: 0,
1880 });
1881 } else {
1882 emitter.instruction(Instruction::LocalSet(slot));
1883 }
1884 emitter.instruction(Instruction::LocalGet(old));
1885 }
1886 crate::PostfixTarget::Global {
1887 mangled, target_ty, ..
1888 } => {
1889 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1890 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1891 })?;
1892 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1893 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
1894 emitter.instruction(Instruction::GlobalGet(idx));
1895 if let ValType::Ref(RefType {
1897 nullable: false, ..
1898 }) = ctx.symbols.value_type(target_ty)?
1899 {
1900 emitter.instruction(Instruction::RefAsNonNull);
1901 }
1902 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1903 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1904 emit_postfix_numeric(emitter, ctx, result_ty);
1905 emitter.instruction(Instruction::LocalSet(old));
1906 emitter.instruction(Instruction::LocalGet(old));
1907 emit_postfix_delta(emitter, ctx, op, result_ty);
1908 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1909 emitter.instruction(Instruction::GlobalSet(idx));
1910 emitter.instruction(Instruction::LocalGet(old));
1911 }
1912 crate::PostfixTarget::Field {
1913 receiver,
1914 name,
1915 target_ty,
1916 } => {
1917 let receiver_ty = ctx
1922 .ta
1923 .source_type(*receiver)
1924 .map_err(crate::codegen::arena_failure)?
1925 .clone();
1926 if let Type::ClassRef { mangled, .. } = receiver_ty.peel() {
1927 emit_class_field_postfix(
1928 emitter, ctx, *receiver, mangled, &name.name, op, target_ty,
1929 )?;
1930 return Ok(());
1931 }
1932 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1934 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1935 })?;
1936 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
1937 emit_receiver(emitter, ctx, *receiver)?;
1938 let rcv_local =
1939 stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
1940 emit_object_property_read(emitter, ctx, rcv_local, &name.name, target_ty)?;
1941 emit_postfix_numeric(emitter, ctx, target_ty);
1942 emitter.instruction(Instruction::LocalSet(tmp));
1943 emitter.instruction(Instruction::LocalGet(tmp));
1944 emit_postfix_delta(emitter, ctx, op, target_ty);
1945 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
1946 emitter.instruction(Instruction::LocalSet(updated));
1947 emit_object_property_write_value(
1948 emitter,
1949 ctx,
1950 rcv_local,
1951 name,
1952 &ShapeArgument::Local {
1953 slot: updated,
1954 ty: target_ty.clone(),
1955 },
1956 )?;
1957 emitter.instruction(Instruction::LocalGet(tmp));
1958 }
1959 crate::PostfixTarget::Index {
1960 receiver, index, ..
1961 } => {
1962 emit_index_postfix(emitter, ctx, *receiver, *index, op)?;
1963 }
1964 };
1965 Ok(())
1966}
1967
1968fn emit_index_postfix(
1969 emitter: &mut FunctionEmitter,
1970 ctx: &CodegenCtx,
1971 receiver: ExprId,
1972 index: ExprId,
1973 op: crate::PostfixOp,
1974) -> Result<(), crate::compiler_error::CompilerFailure> {
1975 let is_uint8 = ctx
1976 .ta
1977 .source_type(receiver)
1978 .map_err(crate::codegen::arena_failure)?
1979 .peel()
1980 == &Type::Uint8Array;
1981 let result_ty = if is_uint8 {
1982 Type::Number
1983 } else {
1984 Type::Unknown
1985 };
1986 emit_expr(emitter, ctx, receiver)?;
1987 cast::emit_box(
1988 emitter,
1989 ctx,
1990 &ctx.ta
1991 .try_expr(receiver)
1992 .map_err(crate::codegen::arena_failure)?
1993 .ty,
1994 )?;
1995 let receiver_local = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
1996 emitter.instruction(Instruction::LocalSet(receiver_local));
1997 emit_expr(emitter, ctx, index)?;
1998 let key = emitter.add_anonymous_local(
1999 ctx.symbols.value_type(
2000 &ctx.ta
2001 .try_expr(index)
2002 .map_err(crate::codegen::arena_failure)?
2003 .ty,
2004 )?,
2005 )?;
2006 emitter.instruction(Instruction::LocalSet(key));
2007 let (backing, position, raw_type) = emit_index_location(
2008 emitter,
2009 ctx,
2010 receiver_local,
2011 key,
2012 &ctx.ta
2013 .try_expr(index)
2014 .map_err(crate::codegen::arena_failure)?
2015 .ty,
2016 is_uint8,
2017 )?;
2018 emitter.instruction(Instruction::LocalGet(backing));
2019 emitter.instruction(Instruction::LocalGet(position));
2020 if is_uint8 {
2021 emitter.instruction(Instruction::ArrayGetU(raw_type));
2022 emitter.instruction(Instruction::F64ConvertI32U);
2023 } else {
2024 emitter.instruction(Instruction::ArrayGet(raw_type));
2025 }
2026 emit_postfix_numeric(emitter, ctx, &result_ty);
2027 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2028 emitter.instruction(Instruction::LocalTee(old));
2029 emit_postfix_delta(emitter, ctx, op, &result_ty);
2030 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2031 emitter.instruction(Instruction::LocalSet(updated));
2032 let (backing, position, _) = emit_index_location(
2035 emitter,
2036 ctx,
2037 receiver_local,
2038 key,
2039 &ctx.ta
2040 .try_expr(index)
2041 .map_err(crate::codegen::arena_failure)?
2042 .ty,
2043 is_uint8,
2044 )?;
2045 emitter.instruction(Instruction::LocalGet(backing));
2046 emitter.instruction(Instruction::LocalGet(position));
2047 emitter.instruction(Instruction::LocalGet(updated));
2048 if is_uint8 {
2049 emitter.instruction(Instruction::I32TruncSatF64S);
2050 }
2051 emitter.instruction(Instruction::ArraySet(raw_type));
2052 emitter.instruction(Instruction::LocalGet(old));
2053 Ok(())
2054}
2055
2056fn emit_index_location(
2057 emitter: &mut FunctionEmitter,
2058 ctx: &CodegenCtx,
2059 receiver: u32,
2060 key: u32,
2061 key_ty: &Type,
2062 is_uint8: bool,
2063) -> Result<(u32, u32, u32), crate::compiler_error::CompilerFailure> {
2064 let (container, raw, ty) = if is_uint8 {
2065 (
2066 ctx.symbols
2067 .uint8_array_type_idx()
2068 .ok_or_else(|| crate::codegen::internal_failure("Uint8Array declared"))?,
2069 ctx.symbols
2070 .raw_uint8_array_type_idx()
2071 .ok_or_else(|| crate::codegen::internal_failure("raw Uint8Array declared"))?,
2072 Type::Uint8Array,
2073 )
2074 } else {
2075 (
2076 ctx.symbols
2077 .array_type_idx()
2078 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?,
2079 ctx.symbols
2080 .raw_array_type_idx()
2081 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?,
2082 Type::Array(Box::new(Type::Unknown)),
2083 )
2084 };
2085 emitter.instruction(Instruction::LocalGet(receiver));
2086 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, &Type::Unknown, &ty)?;
2087 emitter.instruction(Instruction::LocalGet(key));
2088 emit_index_number(emitter, ctx, key_ty)?;
2089 let operand = stash_index_operand(emitter)?;
2090 let length = (!is_uint8)
2091 .then(|| stash_array_length(emitter, container))
2092 .transpose()?;
2093 emitter.instruction(Instruction::StructGet {
2094 struct_type_index: container,
2095 field_index: 1,
2096 });
2097 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
2098 nullable: false,
2099 heap_type: HeapType::Concrete(raw),
2100 }))?;
2101 emitter.instruction(Instruction::LocalSet(backing));
2102 let position = match length {
2103 Some(length) => emit_checked_index_with_length(emitter, ctx, length, operand)?,
2104 None => emit_checked_index(emitter, ctx, backing, operand)?,
2105 };
2106 Ok((backing, position, raw))
2107}
2108
2109fn emit_postfix_numeric(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, ty: &Type) {
2110 ctx.latch(emit_postfix_numeric_checked(emitter, ctx, ty));
2111}
2112
2113fn emit_postfix_numeric_checked(
2114 emitter: &mut FunctionEmitter,
2115 ctx: &CodegenCtx,
2116 ty: &Type,
2117) -> Result<(), crate::compiler_error::CompilerFailure> {
2118 if matches!(ty, Type::Unknown) {
2119 let function = ctx
2120 .symbols
2121 .prelude_func_idx("__value_numeric")
2122 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2123 emitter.instruction(Instruction::Call(function));
2124 }
2125
2126 Ok(())
2127}
2128
2129fn emit_postfix_delta(
2130 emitter: &mut FunctionEmitter<'_>,
2131 ctx: &CodegenCtx<'_>,
2132 op: crate::PostfixOp,
2133 ty: &Type,
2134) {
2135 ctx.latch(emit_postfix_delta_checked(emitter, ctx, op, ty));
2136}
2137
2138fn emit_postfix_delta_checked(
2139 emitter: &mut FunctionEmitter<'_>,
2140 ctx: &CodegenCtx<'_>,
2141 op: crate::PostfixOp,
2142 ty: &Type,
2143) -> Result<(), crate::compiler_error::CompilerFailure> {
2144 if matches!(ty, Type::Unknown) {
2145 let name = if matches!(op, crate::PostfixOp::Inc) {
2146 "inc"
2147 } else {
2148 "dec"
2149 };
2150 let function = ctx
2151 .symbols
2152 .prelude_func_idx(&format!("__value_{name}"))
2153 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2154 emitter.instruction(Instruction::Call(function));
2155 return Ok(());
2156 }
2157 if ty.is_bigint() {
2158 emit_bigint_pm_one(emitter, ctx, op);
2159 return Ok(());
2160 }
2161 emitter.instruction(Instruction::F64Const(Ieee64::from(1.0)));
2162 emitter.instruction(match op {
2163 crate::PostfixOp::Inc => Instruction::F64Add,
2164 crate::PostfixOp::Dec => Instruction::F64Sub,
2165 crate::PostfixOp::NonNullAssert => {
2166 return Err(crate::codegen::internal_failure(
2167 "non-null assertion is not PostfixUnary",
2168 ));
2169 }
2170 });
2171
2172 Ok(())
2173}
2174
2175#[allow(clippy::too_many_arguments)]
2180fn emit_class_field_postfix(
2181 emitter: &mut FunctionEmitter<'_>,
2182 ctx: &CodegenCtx<'_>,
2183 receiver: ExprId,
2184 mangled: &crate::MangledName,
2185 field: &str,
2186 op: crate::PostfixOp,
2187 target_ty: &Type,
2188) -> Result<(), crate::compiler_error::CompilerFailure> {
2189 let intrinsics = ctx
2190 .symbols
2191 .intrinsic_type_indices()
2192 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
2193 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
2194 crate::codegen::internal_failure("class struct type recorded in classes::emit")
2195 })?;
2196 let slot = ctx
2199 .symbols
2200 .class_field_slot(mangled, field)
2201 .ok_or_else(|| {
2202 crate::codegen::internal_failure(
2203 "data-field slot recorded in classes::emit — accessors are rejected in infer",
2204 )
2205 })?;
2206 let fields_local = emitter.add_anonymous_local(ValType::Ref(RefType {
2207 nullable: false,
2208 heap_type: HeapType::Concrete(intrinsics.object_fields),
2209 }))?;
2210 let old = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2211 let object = emitter.add_anonymous_local(
2212 ctx.symbols.value_type(
2213 ctx.ta
2214 .source_type(receiver)
2215 .map_err(crate::codegen::arena_failure)?,
2216 )?,
2217 )?;
2218 emit_receiver(emitter, ctx, receiver)?;
2219 emitter.instruction(Instruction::LocalTee(object));
2220 emitter.instruction(Instruction::StructGet {
2221 struct_type_index: struct_idx,
2222 field_index: 2,
2223 });
2224 emitter.instruction(Instruction::LocalSet(fields_local));
2225 emitter.instruction(Instruction::LocalGet(fields_local));
2226 emitter.instruction(Instruction::I32Const(slot as i32));
2227 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
2228 if ctx
2229 .symbols
2230 .class_field_narrowing_check(mangled, field)
2231 .is_some()
2232 {
2233 crate::codegen::field_guards::check(emitter, ctx, object, mangled, field)?;
2234 }
2235 emit_class_field_slot_cast(emitter, ctx, mangled, field, target_ty)?;
2236 emit_postfix_numeric(emitter, ctx, target_ty);
2237 emitter.instruction(Instruction::LocalSet(old));
2238 emitter.instruction(Instruction::LocalGet(fields_local));
2239 emitter.instruction(Instruction::I32Const(slot as i32));
2240 emitter.instruction(Instruction::LocalGet(old));
2241 emit_postfix_delta(emitter, ctx, op, target_ty);
2242 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, target_ty)?;
2243 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
2244 emitter.instruction(Instruction::LocalGet(old));
2245 Ok(())
2246}
2247
2248fn emit_chain_parts(
2256 emitter: &mut FunctionEmitter<'_>,
2257 ctx: &CodegenCtx<'_>,
2258 parts: &[crate::TypedChainPart],
2259 idx: usize,
2260 receiver_ty: &Type,
2261 result_ty: &Type,
2262 source_types: Option<&[Type]>,
2263) -> Result<(), crate::compiler_error::CompilerFailure> {
2264 if source_types.is_some_and(|types| parts.len().checked_add(1) != Some(types.len())) {
2265 return Err(crate::codegen::internal_failure(
2266 "optional-chain source type count mismatch",
2267 ));
2268 }
2269 if idx == parts.len() {
2270 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
2275 emitter,
2276 ctx,
2277 receiver_ty,
2278 result_ty,
2279 )?;
2280 return Ok(());
2281 }
2282 let part = parts.get(idx).ok_or_else(|| {
2283 crate::codegen::internal_failure("optional-chain part index is out of bounds")
2284 })?;
2285 let next = idx
2286 .checked_add(1)
2287 .ok_or_else(|| crate::codegen::internal_failure("optional-chain index overflow"))?;
2288 let source_ty = source_types.and_then(|types| types.get(idx));
2289 let check_ty = source_types.and_then(|types| types.get(next));
2290 let saved_receiver = if matches!(
2291 part,
2292 crate::TypedChainPart::Field { .. }
2293 | crate::TypedChainPart::Index { .. }
2294 | crate::TypedChainPart::InterfaceProperty { .. }
2295 ) {
2296 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2297 emitter.instruction(Instruction::LocalTee(slot));
2298 Some(slot)
2299 } else if matches!(part, crate::TypedChainPart::NonNull { .. }) {
2300 emitter.call_receiver
2301 } else {
2302 None
2303 };
2304 let part_result_ty = part.result_ty().clone();
2305 let recv_val = part
2308 .is_optional()
2309 .then(|| ctx.symbols.value_type(receiver_ty))
2310 .transpose()?;
2311 let _: () = if let Some(recv_val @ ValType::Ref(_)) = recv_val {
2317 let block_ty = BlockType::Result(ctx.symbols.value_type(result_ty)?);
2318 let tmp = emitter.add_anonymous_local(recv_val)?;
2319 emitter.instruction(Instruction::LocalTee(tmp));
2320 emit_is_nullish(emitter, ctx)?;
2321 emitter.emit_if(block_ty);
2322 emit_undefined_for_chain_result(emitter, ctx, result_ty)?;
2323 emitter.emit_else();
2324 emitter.instruction(Instruction::LocalGet(tmp));
2325 emitter.instruction(Instruction::RefAsNonNull);
2326 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2327 emitter.call_receiver = saved_receiver;
2328 emit_chain_parts(
2329 emitter,
2330 ctx,
2331 parts,
2332 next,
2333 &part_result_ty,
2334 result_ty,
2335 source_types,
2336 )?;
2337 emitter.emit_end();
2338 } else {
2339 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2340 emitter.call_receiver = saved_receiver;
2341 emit_chain_parts(
2342 emitter,
2343 ctx,
2344 parts,
2345 next,
2346 &part_result_ty,
2347 result_ty,
2348 source_types,
2349 )?;
2350 };
2351 Ok(())
2352}
2353
2354fn emit_chain_operation(
2355 emitter: &mut FunctionEmitter,
2356 ctx: &CodegenCtx,
2357 part: &crate::TypedChainPart,
2358 receiver_ty: &Type,
2359 source_ty: Option<&Type>,
2360 check_ty: Option<&Type>,
2361) -> Result<(), crate::compiler_error::CompilerFailure> {
2362 if let crate::TypedChainPart::MethodCall {
2363 iface,
2364 name,
2365 result_ty,
2366 span,
2367 ..
2368 } = part
2369 && !crate::codegen::is_host_package(iface.as_str())
2370 && let Some(args) = ctx.ta.authored_call_arguments(*span)
2371 {
2372 cast::emit_box(emitter, ctx, receiver_ty)?;
2373 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2374 cast::emit_cast_to(emitter, ctx, result_ty)?;
2375 return Ok(());
2376 }
2377 if receiver_ty == &Type::Unknown {
2378 match part {
2379 crate::TypedChainPart::MethodCall {
2380 iface,
2381 name,
2382 args,
2383 result_ty,
2384 span,
2385 ..
2386 } if crate::codegen::runtime_values::dynamic_member_interface(iface) => {
2387 let args = ctx.ta.authored_call_arguments(*span).unwrap_or(args);
2388 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2389 return Ok(());
2390 }
2391 crate::TypedChainPart::InterfaceProperty { iface, name, .. }
2392 if crate::codegen::runtime_values::dynamic_member_interface(iface) =>
2393 {
2394 emit_live_member_on_stack(emitter, ctx, iface, &name.name, None)?;
2395 return Ok(());
2396 }
2397 _ => {}
2398 }
2399 }
2400 let operation_ty =
2401 crate::typechecker::infer::narrowing::strip_nullish(source_ty.unwrap_or(receiver_ty));
2402 if !matches!(
2403 part,
2404 crate::TypedChainPart::Index { .. }
2405 | crate::TypedChainPart::Call { .. }
2406 | crate::TypedChainPart::NonNull { .. }
2407 ) {
2408 let receiver_slot = ctx.symbols.value_type(receiver_ty)?;
2409 if receiver_slot == ctx.symbols.value_type(&operation_ty)? {
2410 if matches!(receiver_slot, ValType::Ref(_)) {
2411 cast::emit_cast_to(emitter, ctx, &operation_ty)?;
2412 }
2413 } else {
2414 crate::codegen::cast_check::emit_operation_cast_on_stack(
2415 emitter,
2416 ctx,
2417 receiver_ty,
2418 &operation_ty,
2419 )?;
2420 }
2421 }
2422 emit_chain_access(
2423 emitter,
2424 ctx,
2425 part,
2426 &operation_ty,
2427 check_ty.unwrap_or(part.result_ty()),
2428 )?;
2429 Ok(())
2430}
2431
2432fn emit_live_member_on_stack(
2433 emitter: &mut FunctionEmitter,
2434 ctx: &CodegenCtx,
2435 iface: &crate::MangledName,
2436 name: &str,
2437 args: Option<&[ExprId]>,
2438) -> Result<(), crate::compiler_error::CompilerFailure> {
2439 super::emit_const_string_by_text(emitter, ctx, name)?;
2440 super::emit_const_string_by_text(emitter, ctx, iface.as_str())?;
2441 let helper = if args.is_some() {
2442 "__value_member"
2443 } else {
2444 "__value_property"
2445 };
2446 emitter.instruction(Instruction::Call(
2447 ctx.symbols
2448 .prelude_func_idx(helper)
2449 .ok_or_else(|| crate::codegen::internal_failure("live member helper collected"))?,
2450 ));
2451 let Some(args) = args else {
2452 return Ok(());
2453 };
2454 let intr = ctx
2455 .symbols
2456 .intrinsic_type_indices()
2457 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2458 emitter.instruction(Instruction::GlobalGet(
2459 ctx.symbols
2460 .prelude_global_idx("array_vtable")
2461 .ok_or_else(|| crate::codegen::internal_failure("array vtable"))?,
2462 ));
2463 for &arg in args {
2464 emit_expr(emitter, ctx, arg)?;
2465 cast::emit_box(
2466 emitter,
2467 ctx,
2468 &ctx.ta
2469 .try_expr(arg)
2470 .map_err(crate::codegen::arena_failure)?
2471 .ty,
2472 )?;
2473 }
2474 emitter.instruction(Instruction::ArrayNewFixed {
2475 array_type_index: intr.raw_array,
2476 array_size: crate::codegen::wasm_u32(args.len())?,
2477 });
2478 emitter.instruction(Instruction::I32Const(i32::try_from(args.len()).map_err(
2479 |_| crate::codegen::internal_failure("argument array too large"),
2480 )?));
2481 emitter.instruction(Instruction::StructNew(intr.array));
2482 emitter.instruction(Instruction::Call(
2483 ctx.symbols
2484 .prelude_func_idx("__value_invoke")
2485 .ok_or_else(|| crate::codegen::internal_failure("live invocation collected"))?,
2486 ));
2487 Ok(())
2488}
2489
2490fn inline_length_struct_idx(
2493 ctx: &CodegenCtx<'_>,
2494 iface: &crate::MangledName,
2495 prop: &str,
2496) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
2497 let key = crate::mangle::extend(iface, prop);
2498 if !ctx.symbols.is_intrinsic_member(&key) {
2499 return Ok(None);
2500 }
2501 let index = if *iface == crate::mangle::prelude("String") && prop == "length" {
2502 ctx.symbols.string_type_idx()
2503 } else if *iface == crate::mangle::prelude("Array") && prop == "length" {
2504 ctx.symbols.array_type_idx()
2505 } else {
2506 return Err(crate::codegen::internal_failure(format!(
2507 "no inline lowering for intrinsic member `{}`",
2508 key.as_str()
2509 )));
2510 };
2511 index
2512 .map(Some)
2513 .ok_or_else(|| crate::codegen::internal_failure("length receiver intrinsic missing"))
2514}
2515
2516fn emit_inline_length(emitter: &mut FunctionEmitter<'_>, ctx: &CodegenCtx<'_>, struct_idx: u32) {
2518 let is_array = ctx.symbols.array_type_idx() == Some(struct_idx);
2519 emitter.instruction(Instruction::StructGet {
2520 struct_type_index: struct_idx,
2521 field_index: if is_array { 2 } else { 1 },
2522 });
2523 if !is_array {
2524 emitter.instruction(Instruction::ArrayLen);
2525 }
2526 emitter.instruction(Instruction::F64ConvertI32U);
2527}
2528
2529fn emit_undefined_for_chain_result(
2531 emitter: &mut FunctionEmitter<'_>,
2532 ctx: &CodegenCtx<'_>,
2533 result_ty: &Type,
2534) -> Result<(), crate::compiler_error::CompilerFailure> {
2535 emit_undefined(emitter, ctx)?;
2536 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)
2537}
2538
2539fn emit_class_field_slot_cast(
2543 emitter: &mut FunctionEmitter,
2544 ctx: &CodegenCtx,
2545 class: &crate::MangledName,
2546 field: &str,
2547 result_ty: &Type,
2548) -> Result<(), crate::compiler_error::CompilerFailure> {
2549 emit_class_field_slot_cast_as(emitter, ctx, class, field, result_ty, result_ty)?;
2550 Ok(())
2551}
2552
2553fn emit_class_field_slot_cast_as(
2554 emitter: &mut FunctionEmitter<'_>,
2555 ctx: &CodegenCtx<'_>,
2556 class: &crate::MangledName,
2557 field: &str,
2558 result_ty: &Type,
2559 check_ty: &Type,
2560) -> Result<(), crate::compiler_error::CompilerFailure> {
2561 let _: () = match ctx.symbols.class_field_narrowing_check(class, field) {
2562 Some(check) => {
2563 crate::codegen::cast_check::emit_narrowed_field_read_as(
2564 emitter, ctx, check, result_ty, check_ty,
2565 )?;
2566 }
2567 None => crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?,
2568 };
2569 Ok(())
2570}
2571
2572fn emit_object_spread(
2573 emitter: &mut FunctionEmitter,
2574 ctx: &CodegenCtx,
2575 sources: &[TypedObjectMember],
2576 shape: &Type,
2577) -> Result<(), crate::compiler_error::CompilerFailure> {
2578 let intrinsics = ctx
2579 .symbols
2580 .intrinsic_type_indices()
2581 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2582 let merge = ctx
2583 .symbols
2584 .prelude_func_idx("ObjectConstructor##spread")
2585 .ok_or_else(|| crate::codegen::internal_failure("spread helper collected"))?;
2586 let null_object = Instruction::RefNull(HeapType::Concrete(intrinsics.object));
2587 emitter.instruction(null_object.clone());
2588 for (index, source) in sources.iter().enumerate() {
2589 let final_shape = (index + 1 == sources.len()).then_some(shape);
2590 emit_receiver(emitter, ctx, source.expr_id())?;
2594 let narrowed_ty = ctx
2595 .ta
2596 .source_type(source.expr_id())
2597 .map_err(crate::codegen::arena_failure)?;
2598 let accumulator = if may_hold_non_object(narrowed_ty) {
2602 Some(emit_object_source_test(
2603 emitter,
2604 ctx,
2605 narrowed_ty,
2606 intrinsics.object,
2607 intrinsics.object_shape,
2608 )?)
2609 } else {
2610 None
2611 };
2612 let source_local =
2613 stash_receiver_as_object_shape(emitter, narrowed_ty, intrinsics.object_shape)?;
2614 emitter.instruction(Instruction::LocalGet(source_local));
2615 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2616 if matches!(source, TypedObjectMember::Spread { by_name: true, .. }) {
2617 emit_spread_mask(emitter, ctx, source_local, source.expr_id(), shape)?;
2618 } else {
2619 emitter.instruction(null_object.clone());
2620 }
2621 emitter.instruction(Instruction::Call(merge));
2622 if let Some(accumulator) = accumulator {
2623 emitter.emit_else();
2624 emitter.instruction(Instruction::LocalGet(accumulator));
2625 emitter.instruction(null_object.clone());
2626 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2627 emitter.instruction(null_object.clone());
2628 emitter.instruction(Instruction::Call(merge));
2629 emitter.emit_end();
2630 }
2631 }
2632 Ok(())
2633}
2634
2635fn emit_spread_shape_argument(
2638 emitter: &mut FunctionEmitter,
2639 ctx: &CodegenCtx,
2640 final_shape: Option<&Type>,
2641 null_object: &Instruction<'static>,
2642) {
2643 match final_shape {
2644 Some(shape) => emit_spread_shape(emitter, ctx, shape),
2645 None => emitter.instruction(null_object.clone()),
2646 }
2647}
2648
2649fn may_hold_non_object(ty: &Type) -> bool {
2653 match ty.peel() {
2654 Type::Union(members) => members.iter().any(may_hold_non_object),
2655 Type::Null
2656 | Type::Undefined
2657 | Type::Boolean
2658 | Type::BooleanLiteral(_)
2659 | Type::Number
2660 | Type::NumberLiteral(_)
2661 | Type::String
2662 | Type::StringLiteral(_) => true,
2663 _ => false,
2664 }
2665}
2666
2667fn emit_object_source_test(
2672 emitter: &mut FunctionEmitter,
2673 ctx: &CodegenCtx,
2674 source_ty: &Type,
2675 object: u32,
2676 object_shape: u32,
2677) -> Result<u32, crate::compiler_error::CompilerFailure> {
2678 let source_type = match ctx.symbols.value_type(source_ty)? {
2679 ValType::Ref(reference) => ValType::Ref(RefType {
2680 nullable: true,
2681 ..reference
2682 }),
2683 other => other,
2684 };
2685 let source = emitter.add_anonymous_local(source_type)?;
2686 emitter.instruction(Instruction::LocalSet(source));
2687 let accumulator = emitter.add_anonymous_local(object_ref(object))?;
2688 emitter.instruction(Instruction::LocalSet(accumulator));
2689 emitter.instruction(Instruction::LocalGet(source));
2690 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
2691 object_shape,
2692 )));
2693 emitter.emit_if(BlockType::Result(ValType::Ref(RefType {
2694 nullable: false,
2695 heap_type: HeapType::Concrete(object_shape),
2696 })));
2697 emitter.instruction(Instruction::LocalGet(accumulator));
2698 emitter.instruction(Instruction::LocalGet(source));
2699 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
2700 object_shape,
2701 )));
2702 Ok(accumulator)
2703}
2704
2705fn emit_spread_mask(
2709 emitter: &mut FunctionEmitter,
2710 ctx: &CodegenCtx,
2711 source: u32,
2712 source_expr: crate::ExprId,
2713 shape: &Type,
2714) -> Result<(), crate::compiler_error::CompilerFailure> {
2715 let checked = ctx
2716 .ta
2717 .spread_mask_fields
2718 .get(&source_expr)
2719 .ok_or_else(|| crate::codegen::internal_failure("by-name spread fields recorded"))?;
2720 let omitted = ctx.ta.spread_omitted_fields.get(&source_expr);
2723 let fields: Vec<(&String, Option<&Type>)> = checked
2724 .iter()
2725 .map(|(name, ty)| (name, Some(ty)))
2726 .chain(omitted.into_iter().flatten().map(|name| (name, None)))
2727 .collect();
2728 let intrinsics = ctx
2729 .symbols
2730 .intrinsic_type_indices()
2731 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2732 let vtable = ctx
2733 .symbols
2734 .vtable_global_idx(shape)
2735 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2736 emitter.instruction(Instruction::GlobalGet(vtable));
2737 for (name, _) in &fields {
2738 let global = ctx
2739 .symbols
2740 .field_name_string_global_idx(name)
2741 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2742 emitter.instruction(Instruction::GlobalGet(global));
2743 }
2744 emitter.instruction(Instruction::ArrayNewFixed {
2745 array_type_index: intrinsics.field_names,
2746 array_size: crate::codegen::wasm_u32(fields.len())?,
2747 });
2748 for (name, ty) in fields.iter().copied() {
2749 let global = ctx
2750 .symbols
2751 .field_name_string_global_idx(name)
2752 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2753 let Some(ty) = ty else {
2754 emitter.instruction(Instruction::GlobalGet(global));
2755 continue;
2756 };
2757 let index = emitter.add_anonymous_local(ValType::I32)?;
2758 let value = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
2759 emit_object_field_index_by_name(emitter, ctx, source, global);
2760 emitter.instruction(Instruction::LocalTee(index));
2761 emitter.instruction(Instruction::I32Const(0));
2762 emitter.instruction(Instruction::I32GeS);
2763 emitter.emit_if(BlockType::Result(ValType::I32));
2764 emit_field_slot_get(emitter, intrinsics, source, index);
2765 emitter.instruction(Instruction::LocalSet(value));
2766 emit_field_holds_value_of(emitter, ctx, source, index, value, ty)?;
2767 emitter.emit_else();
2768 emitter.instruction(Instruction::I32Const(0));
2769 emitter.emit_end();
2770 emitter.emit_if(BlockType::Result(object_ref(intrinsics.object)));
2771 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intrinsics.object)));
2772 emitter.emit_else();
2773 emitter.instruction(Instruction::GlobalGet(global));
2774 emitter.emit_end();
2775 }
2776 emitter.instruction(Instruction::ArrayNewFixed {
2777 array_type_index: intrinsics.object_fields,
2778 array_size: crate::codegen::wasm_u32(fields.len())?,
2779 });
2780 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2781 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2782 Ok(())
2783}
2784
2785fn emit_spread_shape(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, shape: &Type) {
2788 ctx.latch(emit_spread_shape_checked(emitter, ctx, shape));
2789}
2790
2791fn emit_spread_shape_checked(
2792 emitter: &mut FunctionEmitter,
2793 ctx: &CodegenCtx,
2794 shape: &Type,
2795) -> Result<(), crate::compiler_error::CompilerFailure> {
2796 let Type::Object { fields, .. } = shape else {
2797 return Err(crate::codegen::internal_failure("structural spread shape"));
2798 };
2799 let intrinsics = ctx
2800 .symbols
2801 .intrinsic_type_indices()
2802 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2803 let names: Vec<_> = fields
2804 .iter()
2805 .map(|(name, field)| crate::codegen::field_names::FieldName {
2806 name: name.clone(),
2807 optional: field.optional,
2808 is_accessor: false,
2809 is_private: false,
2810 })
2811 .collect();
2812 let names_global = ctx
2813 .symbols
2814 .field_names_global_idx(&names)
2815 .ok_or_else(|| crate::codegen::internal_failure("spread shape names collected"))?;
2816 let vtable = ctx
2817 .symbols
2818 .vtable_global_idx(shape)
2819 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2820 emitter.instruction(Instruction::GlobalGet(vtable));
2821 emitter.instruction(Instruction::GlobalGet(names_global));
2822 emitter.instruction(Instruction::I32Const(
2823 crate::codegen::wasm_u32(fields.len())? as i32,
2824 ));
2825 emitter.instruction(Instruction::ArrayNewDefault(intrinsics.object_fields));
2826 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2827 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2828
2829 Ok(())
2830}
2831
2832pub(super) fn stash_receiver_as_object_shape(
2848 emitter: &mut FunctionEmitter<'_>,
2849 receiver_ty: &Type,
2850 object_shape: u32,
2851) -> Result<u32, crate::compiler_error::CompilerFailure> {
2852 coerce_field_receiver_to_object_shape(emitter, receiver_ty, object_shape);
2853 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
2854 nullable: false,
2855 heap_type: HeapType::Concrete(object_shape),
2856 }))?;
2857 emitter.instruction(Instruction::LocalSet(rcv_local));
2858 Ok(rcv_local)
2859}
2860
2861fn coerce_field_receiver_to_object_shape(
2862 emitter: &mut FunctionEmitter<'_>,
2863 receiver_ty: &Type,
2864 object_shape: u32,
2865) {
2866 let universal = match receiver_ty.peel() {
2867 Type::InterfaceRef { .. } | Type::Unknown => true,
2868 Type::Union(members) => members
2869 .iter()
2870 .any(|m| !matches!(m.peel(), Type::Object { .. })),
2871 _ => false,
2872 };
2873 if universal {
2874 emitter.instruction(Instruction::RefAsNonNull);
2875 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
2876 object_shape,
2877 )));
2878 }
2879}
2880
2881fn emit_chain_access(
2887 emitter: &mut FunctionEmitter<'_>,
2888 ctx: &CodegenCtx<'_>,
2889 part: &crate::TypedChainPart,
2890 receiver_ty: &Type,
2891 check_ty: &Type,
2892) -> Result<(), crate::compiler_error::CompilerFailure> {
2893 let _: () = match part {
2894 crate::TypedChainPart::Field {
2895 name, result_ty, ..
2896 } => {
2897 if ctx.ta.has_string_index(receiver_ty) {
2898 let Some(symbol) = ctx.require(
2899 ctx.symbols.field_name_string_global_idx(&name.name),
2900 "property name collected",
2901 ) else {
2902 return Ok(());
2903 };
2904 emitter.instruction(Instruction::GlobalGet(symbol));
2905 let Some(symbol) = ctx.require(
2906 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
2907 "record read imported",
2908 ) else {
2909 return Ok(());
2910 };
2911 emitter.instruction(Instruction::Call(symbol));
2912 crate::codegen::cast_check::emit_checked_cast_on_stack(
2913 emitter,
2914 ctx,
2915 &Type::Unknown,
2916 check_ty,
2917 )?;
2918 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
2919 return Ok(());
2920 }
2921 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
2922 crate::codegen::internal_failure("intrinsics declared by codegen entry")
2923 })?;
2924 if let Type::ClassRef { mangled, .. } = receiver_ty.peel() {
2928 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
2929 emit_class_method_value(
2930 emitter,
2931 ctx,
2932 receiver_ty,
2933 &name.name,
2934 result_ty,
2935 check_ty,
2936 )?;
2937 return Ok(());
2938 }
2939 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
2940 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
2941 emit_class_vtable_dispatch(emitter, ctx, mangled, &getter, &[], result_ty)?;
2942 return Ok(());
2943 };
2944 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
2945 crate::codegen::internal_failure("class struct type recorded in classes::emit")
2946 })?;
2947 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
2948 nullable: false,
2949 heap_type: HeapType::Concrete(struct_idx),
2950 }))?;
2951 emitter.instruction(Instruction::LocalTee(object));
2952 emitter.instruction(Instruction::StructGet {
2953 struct_type_index: struct_idx,
2954 field_index: 2,
2955 });
2956 emitter.instruction(Instruction::I32Const(slot as i32));
2957 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
2958 if ctx
2959 .symbols
2960 .class_field_narrowing_check(mangled, &name.name)
2961 .is_some()
2962 {
2963 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
2964 }
2965 emit_class_field_slot_cast_as(
2966 emitter, ctx, mangled, &name.name, result_ty, check_ty,
2967 )?;
2968 return Ok(());
2969 }
2970 let rcv_local =
2971 stash_receiver_as_object_shape(emitter, receiver_ty, intrinsics.object_shape)?;
2972 emit_object_property_read_as(emitter, ctx, rcv_local, &name.name, result_ty, check_ty)?;
2973 }
2974 crate::TypedChainPart::Index { idx, result_ty, .. } => {
2975 if receiver_ty.is_structural_object() {
2976 emit_expr(emitter, ctx, *idx)?;
2977 crate::codegen::cast_check::emit_operation_cast_on_stack(
2978 emitter,
2979 ctx,
2980 &ctx.ta
2981 .try_expr(*idx)
2982 .map_err(crate::codegen::arena_failure)?
2983 .ty,
2984 &Type::String,
2985 )?;
2986 let Some(symbol) = ctx.require(
2987 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
2988 "record read imported",
2989 ) else {
2990 return Ok(());
2991 };
2992 emitter.instruction(Instruction::Call(symbol));
2993 crate::codegen::cast_check::emit_checked_cast_on_stack(
2994 emitter,
2995 ctx,
2996 &Type::Unknown,
2997 check_ty,
2998 )?;
2999 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3000 } else if receiver_ty.peel() == &Type::Uint8Array {
3001 emit_uint8_index_with_receiver_on_stack(emitter, ctx, *idx)?;
3002 } else {
3003 let source_ty = match receiver_ty.peel() {
3004 Type::Array(element) => element.as_ref(),
3005 Type::Tuple(elements) => match &ctx
3006 .ta
3007 .try_expr(*idx)
3008 .map_err(crate::codegen::arena_failure)?
3009 .kind
3010 {
3011 TypedExprKind::Number(index) => {
3012 elements.get(*index as usize).unwrap_or(result_ty)
3013 }
3014 _ => result_ty,
3015 },
3016 _ => result_ty,
3017 };
3018 emit_bounds_checked_index_with_receiver_on_stack(
3019 emitter,
3020 ctx,
3021 *idx,
3022 source_ty,
3023 is_optional_tuple_position(ctx, receiver_ty, *idx)?,
3024 )?;
3025 if source_ty != result_ty {
3026 crate::codegen::cast_check::emit_checked_cast_on_stack(
3027 emitter, ctx, source_ty, result_ty,
3028 )?;
3029 }
3030 }
3031 }
3032 crate::TypedChainPart::InterfaceProperty { iface, name, .. } => {
3033 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
3044 emit_inline_length(emitter, ctx, struct_idx);
3045 return Ok(());
3046 }
3047 let key = crate::mangle::extend(iface, &name.name);
3048 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
3049 crate::codegen::internal_failure(
3050 "interface property getter recorded during the dependency-import pass",
3051 )
3052 })?;
3053 if matches!(receiver_ty.peel(), Type::InterfaceRef { .. }) {
3054 emitter.instruction(Instruction::RefAsNonNull);
3055 }
3056 emitter.instruction(Instruction::Call(func_idx));
3057 }
3058 crate::TypedChainPart::Call { args, .. } => {
3059 emit_indirect_closure_call_with_receiver_on_stack(
3066 emitter,
3067 ctx,
3068 receiver_ty,
3069 args,
3070 part.result_ty(),
3071 )?;
3072 }
3073 crate::TypedChainPart::MethodCall {
3074 iface,
3075 name,
3076 args,
3077 result_ty,
3078 ..
3079 } => {
3080 emit_method_call_with_receiver_on_stack(
3090 emitter,
3091 ctx,
3092 receiver_ty,
3093 iface,
3094 &name.name,
3095 args,
3096 None,
3097 None,
3098 result_ty,
3099 )?;
3100 }
3101 crate::TypedChainPart::NonNull { result_ty, .. } => {
3102 crate::codegen::cast_check::emit_non_null_assert_on_stack(
3103 emitter,
3104 ctx,
3105 receiver_ty,
3106 result_ty,
3107 )?;
3108 }
3109 };
3110 Ok(())
3111}
3112
3113fn emit_captured_load(
3124 emitter: &mut FunctionEmitter,
3125 _ctx: &CodegenCtx,
3126 c: &crate::CapturedVar,
3127) -> Result<(), crate::compiler_error::CompilerFailure> {
3128 let slot = emitter.local_slot(&c.name.name).ok_or_else(|| {
3129 crate::codegen::internal_failure("captured binding is missing from the outer scope")
3130 })?;
3131 emitter.instruction(Instruction::LocalGet(slot));
3132 Ok(())
3133}
3134
3135fn emit_binary(
3136 emitter: &mut FunctionEmitter,
3137 ctx: &CodegenCtx,
3138 op: BinOp,
3139 lhs: ExprId,
3140 rhs: ExprId,
3141 result_ty: &Type,
3142) -> Result<(), crate::compiler_error::CompilerFailure> {
3143 let admits_never_operand = matches!(
3144 op,
3145 BinOp::Add
3146 | BinOp::Sub
3147 | BinOp::Mul
3148 | BinOp::Div
3149 | BinOp::Rem
3150 | BinOp::Pow
3151 | BinOp::Lt
3152 | BinOp::Gt
3153 | BinOp::Le
3154 | BinOp::Ge
3155 );
3156 if admits_never_operand && try_emit_unreachable_for_never_operand(emitter, ctx, &[lhs, rhs])? {
3157 return Ok(());
3158 }
3159 let _: () = match op {
3160 BinOp::BitAnd
3165 | BinOp::BitOr
3166 | BinOp::BitXor
3167 | BinOp::Shl
3168 | BinOp::Shr
3169 | BinOp::UnsignedShr => {
3170 if has_numeric_operands(ctx, &[lhs, rhs], result_ty)? {
3171 emit_number_bitwise(emitter, ctx, op, lhs, rhs)?;
3172 } else {
3173 let name = op.bitwise_name().ok_or_else(|| {
3174 crate::codegen::internal_failure("missing bitwise host operation")
3175 })?;
3176 emit_bitwise_host(emitter, ctx, name, &[lhs, rhs], result_ty)?;
3177 }
3178 }
3179 BinOp::Add => match &result_ty.widen_literal() {
3182 Type::Number => {
3183 emit_primitive_operand(emitter, ctx, lhs)?;
3184 emit_primitive_operand(emitter, ctx, rhs)?;
3185 emitter.instruction(Instruction::F64Add);
3186 }
3187 Type::String => {
3188 emit_primitive_operand(emitter, ctx, lhs)?;
3189 emit_primitive_operand(emitter, ctx, rhs)?;
3190 let idx = ctx
3191 .symbols
3192 .prelude_func_idx("string_concat")
3193 .ok_or_else(|| {
3194 crate::codegen::internal_failure("string_concat imported from prelude")
3195 })?;
3196 emitter.instruction(Instruction::Call(idx));
3197 }
3198 Type::BigInt => emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "add")?,
3200 other => {
3201 return Err(crate::codegen::internal_failure(format!(
3202 "typechecker rejects `+` for {other:?}"
3203 )));
3204 }
3205 },
3206 BinOp::Sub | BinOp::Mul | BinOp::Div => {
3207 if result_ty.is_bigint() {
3209 let name = match op {
3210 BinOp::Sub => "sub",
3211 BinOp::Mul => "mul",
3212 BinOp::Div => "div",
3213 _ => {
3214 return Err(crate::codegen::internal_failure(
3215 "invalid compiler emission dispatch",
3216 ));
3217 }
3218 };
3219 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, name)?;
3220 return Ok(());
3221 }
3222 if !matches!(result_ty, Type::Number) {
3223 return Err(crate::codegen::internal_failure(
3224 "numeric operator has a non-number result type",
3225 ));
3226 }
3227 emit_primitive_operand(emitter, ctx, lhs)?;
3228 emit_primitive_operand(emitter, ctx, rhs)?;
3229 let inst = match op {
3230 BinOp::Sub => Instruction::F64Sub,
3231 BinOp::Mul => Instruction::F64Mul,
3232 BinOp::Div => Instruction::F64Div,
3233 _ => {
3234 return Err(crate::codegen::internal_failure(
3235 "invalid compiler emission dispatch",
3236 ));
3237 }
3238 };
3239 emitter.instruction(inst);
3240 }
3241 BinOp::Pow => {
3242 if result_ty.is_bigint() {
3247 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "pow")?;
3248 return Ok(());
3249 }
3250 if !matches!(result_ty, Type::Number) {
3251 return Err(crate::codegen::internal_failure(
3252 "numeric operator has a non-number result type",
3253 ));
3254 }
3255 emit_primitive_operand(emitter, ctx, lhs)?;
3256 emit_primitive_operand(emitter, ctx, rhs)?;
3257 let idx = ctx
3258 .symbols
3259 .func_idx(&crate::runtime::prelude::math::math_key("pow"))
3260 .ok_or_else(|| crate::codegen::internal_failure("Math#pow host import recorded"))?;
3261 emitter.instruction(Instruction::Call(idx));
3262 }
3263 BinOp::Rem => {
3264 if result_ty.is_bigint() {
3266 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "mod")?;
3267 return Ok(());
3268 }
3269 let a = emitter.add_anonymous_local(ValType::F64)?;
3273 let b = emitter.add_anonymous_local(ValType::F64)?;
3274 emit_primitive_operand(emitter, ctx, lhs)?;
3275 emitter.instruction(Instruction::LocalSet(a));
3276 emit_primitive_operand(emitter, ctx, rhs)?;
3277 emitter.instruction(Instruction::LocalSet(b));
3278 emitter.instruction(Instruction::LocalGet(a));
3280 emitter.instruction(Instruction::LocalGet(a));
3281 emitter.instruction(Instruction::LocalGet(b));
3282 emitter.instruction(Instruction::F64Div);
3283 emitter.instruction(Instruction::F64Trunc);
3284 emitter.instruction(Instruction::LocalGet(b));
3285 emitter.instruction(Instruction::F64Mul);
3286 emitter.instruction(Instruction::F64Sub);
3287 }
3288 BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
3289 let operand_ty = ctx
3292 .ta
3293 .try_expr(lhs)
3294 .map_err(crate::codegen::arena_failure)?
3295 .ty
3296 .primitive_behavior()
3297 .clone();
3298 if operand_ty.is_bigint() {
3299 emit_bigint_cmp_inline(emitter, ctx, lhs, rhs, op)?;
3300 return Ok(());
3301 }
3302 emit_primitive_operand(emitter, ctx, lhs)?;
3303 emit_primitive_operand(emitter, ctx, rhs)?;
3304 if matches!(operand_ty, Type::String | Type::StringLiteral(_)) {
3305 let idx = ctx.symbols.prelude_func_idx("string_cmp").ok_or_else(|| {
3306 crate::codegen::internal_failure("string_cmp imported from prelude")
3307 })?;
3308 emitter.instruction(Instruction::Call(idx));
3309 emitter.instruction(Instruction::I32Const(0));
3310 emitter.instruction(match op {
3311 BinOp::Lt => Instruction::I32LtS,
3312 BinOp::Gt => Instruction::I32GtS,
3313 BinOp::Le => Instruction::I32LeS,
3314 BinOp::Ge => Instruction::I32GeS,
3315 _ => {
3316 return Err(crate::codegen::internal_failure(
3317 "invalid compiler emission dispatch",
3318 ));
3319 }
3320 });
3321 } else {
3322 emitter.instruction(match op {
3323 BinOp::Lt => Instruction::F64Lt,
3324 BinOp::Gt => Instruction::F64Gt,
3325 BinOp::Le => Instruction::F64Le,
3326 BinOp::Ge => Instruction::F64Ge,
3327 _ => {
3328 return Err(crate::codegen::internal_failure(
3329 "invalid compiler emission dispatch",
3330 ));
3331 }
3332 });
3333 }
3334 }
3335 BinOp::Eq | BinOp::NotEq => emit_equality(emitter, ctx, op, lhs, rhs)?,
3336 BinOp::And | BinOp::Or => emit_logical(emitter, ctx, op, lhs, rhs, result_ty)?,
3337 BinOp::In => emit_in_operator(emitter, ctx, lhs, rhs)?,
3338 BinOp::NullishCoalesce => {
3342 return Err(crate::codegen::internal_failure(
3343 "BinOp::NullishCoalesce should be lifted into TypedExprKind::NullishCoalesce by infer",
3344 ));
3345 }
3346 };
3347 Ok(())
3348}
3349
3350fn emit_equality(
3353 emitter: &mut FunctionEmitter,
3354 ctx: &CodegenCtx,
3355 op: BinOp,
3356 lhs: ExprId,
3357 rhs: ExprId,
3358) -> Result<(), crate::compiler_error::CompilerFailure> {
3359 let lhs_is_null_lit = matches!(
3366 ctx.ta
3367 .try_expr(lhs)
3368 .map_err(crate::codegen::arena_failure)?
3369 .kind,
3370 TypedExprKind::Null
3371 );
3372 let rhs_is_null_lit = matches!(
3373 ctx.ta
3374 .try_expr(rhs)
3375 .map_err(crate::codegen::arena_failure)?
3376 .kind,
3377 TypedExprKind::Null
3378 );
3379 if lhs_is_null_lit && rhs_is_null_lit {
3380 let v: i32 = if matches!(op, BinOp::Eq) { 1 } else { 0 };
3381 emitter.instruction(Instruction::I32Const(v));
3382 return Ok(());
3383 }
3384 if lhs_is_null_lit || rhs_is_null_lit {
3385 let non_null_side = if lhs_is_null_lit { rhs } else { lhs };
3386 let non_null_ty = ctx
3387 .ta
3388 .try_expr(non_null_side)
3389 .map_err(crate::codegen::arena_failure)?
3390 .ty
3391 .clone();
3392 let non_null_val = ctx.symbols.value_type(&non_null_ty)?;
3393 if matches!(non_null_val, ValType::F64 | ValType::I32) {
3401 emit_expr(emitter, ctx, non_null_side)?;
3402 emitter.instruction(Instruction::Drop);
3403 let v: i32 = if matches!(op, BinOp::NotEq) { 1 } else { 0 };
3404 emitter.instruction(Instruction::I32Const(v));
3405 return Ok(());
3406 }
3407 emit_expr(emitter, ctx, non_null_side)?;
3408 emitter.instruction(Instruction::RefIsNull);
3409 if matches!(op, BinOp::NotEq) {
3410 emitter.instruction(Instruction::I32Eqz);
3411 }
3412 return Ok(());
3413 }
3414 let lhs_ty = ctx
3415 .ta
3416 .try_expr(lhs)
3417 .map_err(crate::codegen::arena_failure)?
3418 .ty
3419 .clone();
3420 let rhs_ty = ctx
3421 .ta
3422 .try_expr(rhs)
3423 .map_err(crate::codegen::arena_failure)?
3424 .ty
3425 .clone();
3426 let operand_val = ctx.symbols.value_type(lhs_ty.primitive_behavior())?;
3427 if may_hold_null(&lhs_ty)
3433 || may_hold_null(&rhs_ty)
3434 || operand_val != ctx.symbols.value_type(rhs_ty.primitive_behavior())?
3435 {
3436 emit_boxed_eq(emitter, ctx, lhs, rhs, op)?;
3437 return Ok(());
3438 }
3439 if lhs_ty.is_bigint() {
3443 emit_bigint_cmp_eq_inline(emitter, ctx, lhs, rhs, op)?;
3444 return Ok(());
3445 }
3446 let string_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
3447 crate::codegen::internal_failure("string type registered with intrinsics")
3448 })?;
3449 match operand_val {
3455 ValType::F64 => {
3456 emit_primitive_operand(emitter, ctx, lhs)?;
3457 emit_primitive_operand(emitter, ctx, rhs)?;
3458 emitter.instruction(if matches!(op, BinOp::Eq) {
3459 Instruction::F64Eq
3460 } else {
3461 Instruction::F64Ne
3462 });
3463 }
3464 ValType::I32 => {
3465 emit_expr(emitter, ctx, lhs)?;
3466 emit_expr(emitter, ctx, rhs)?;
3467 emitter.instruction(if matches!(op, BinOp::Eq) {
3468 Instruction::I32Eq
3469 } else {
3470 Instruction::I32Ne
3471 });
3472 }
3473 ValType::Ref(RefType {
3474 heap_type: HeapType::Concrete(idx),
3475 ..
3476 }) if idx == string_idx => {
3477 emit_expr(emitter, ctx, lhs)?;
3478 emit_expr(emitter, ctx, rhs)?;
3479 let func = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
3480 crate::codegen::internal_failure("string_eq imported from prelude")
3481 })?;
3482 emitter.instruction(Instruction::Call(func));
3483 if matches!(op, BinOp::NotEq) {
3484 emitter.instruction(Instruction::I32Eqz);
3485 }
3486 }
3487 ValType::Ref(_) => {
3488 emit_expr(emitter, ctx, lhs)?;
3499 cast::emit_box(emitter, ctx, &lhs_ty)?;
3500 emit_expr(emitter, ctx, rhs)?;
3501 cast::emit_box(emitter, ctx, &rhs_ty)?;
3502 emit_vtable_equality(emitter, ctx, op);
3503 }
3504 other => {
3505 return Err(crate::codegen::internal_failure(format!(
3506 "typechecker rejects equality on Wasm value-type `{other:?}`"
3507 )));
3508 }
3509 }
3510 Ok(())
3511}
3512
3513fn emit_logical(
3514 emitter: &mut FunctionEmitter,
3515 ctx: &CodegenCtx,
3516 op: BinOp,
3517 lhs: ExprId,
3518 rhs: ExprId,
3519 result_ty: &Type,
3520) -> Result<(), crate::compiler_error::CompilerFailure> {
3521 let result_val = ctx.symbols.value_type(result_ty)?;
3527 let lhs_ty = ctx
3528 .ta
3529 .try_expr(lhs)
3530 .map_err(crate::codegen::arena_failure)?
3531 .ty
3532 .clone();
3533 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
3534 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
3535 let tmp = emitter.add_anonymous_local(lhs_val)?;
3536 emit_expr(emitter, ctx, lhs)?;
3537 emitter.instruction(Instruction::LocalTee(tmp));
3538 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &lhs_ty)?;
3539 let emit_rhs_branch =
3540 |emitter: &mut FunctionEmitter| -> Result<(), crate::compiler_error::CompilerFailure> {
3541 let rhs_ty = ctx
3542 .ta
3543 .try_expr(rhs)
3544 .map_err(crate::codegen::arena_failure)?
3545 .ty
3546 .clone();
3547 emit_expr(emitter, ctx, rhs)?;
3548 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3549 emitter, ctx, &rhs_ty, result_ty,
3550 )?;
3551 Ok(())
3552 };
3553 let kept_lhs_ty = match op {
3557 BinOp::And => falsy_part(&lhs_ty),
3558 _ => truthy_part(&lhs_ty),
3559 };
3560 let lhs_is_never_kept = matches!(kept_lhs_ty.peel(), Type::Never);
3561 let emit_kept_lhs_branch = |emitter: &mut FunctionEmitter| {
3562 if lhs_is_never_kept {
3563 emitter.instruction(Instruction::Unreachable);
3564 return Ok(());
3565 }
3566 emitter.instruction(Instruction::LocalGet(tmp));
3567 if lhs_is_ref {
3568 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?;
3572 } else {
3573 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3574 emitter, ctx, &lhs_ty, result_ty,
3575 )?;
3576 }
3577 Ok::<(), crate::compiler_error::CompilerFailure>(())
3578 };
3579 emitter.emit_if(BlockType::Result(result_val));
3580 match op {
3581 BinOp::And => emit_rhs_branch(emitter)?,
3582 _ => emit_kept_lhs_branch(emitter)?,
3583 }
3584 emitter.emit_else();
3585 match op {
3586 BinOp::And => emit_kept_lhs_branch(emitter)?,
3587 _ => emit_rhs_branch(emitter)?,
3588 }
3589 emitter.emit_end();
3590 Ok(())
3591}
3592
3593fn try_emit_unreachable_for_never_operand(
3598 emitter: &mut FunctionEmitter,
3599 ctx: &CodegenCtx,
3600 operands: &[ExprId],
3601) -> Result<bool, crate::compiler_error::CompilerFailure> {
3602 let mut has_never_operand = false;
3603 for &operand in operands {
3604 let ty = &ctx
3605 .ta
3606 .try_expr(operand)
3607 .map_err(crate::codegen::arena_failure)?
3608 .ty;
3609 has_never_operand |= matches!(ty.peel(), Type::Never);
3610 }
3611 if !has_never_operand {
3612 return Ok(false);
3613 }
3614 for &operand in operands {
3615 emit_expr(emitter, ctx, operand)?;
3616 emitter.instruction(Instruction::Drop);
3617 }
3618 emitter.instruction(Instruction::Unreachable);
3619 Ok(true)
3620}
3621
3622fn emit_primitive_operand(
3623 emitter: &mut FunctionEmitter,
3624 ctx: &CodegenCtx,
3625 expr: ExprId,
3626) -> Result<(), crate::compiler_error::CompilerFailure> {
3627 emit_expr(emitter, ctx, expr)?;
3628 let ty = &ctx
3629 .ta
3630 .try_expr(expr)
3631 .map_err(crate::codegen::arena_failure)?
3632 .ty;
3633 let _: () = if matches!(ty.primitive_behavior(), Type::Number)
3634 && ctx.symbols.value_type(ty)? != ValType::F64
3635 {
3636 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
3637 };
3638 Ok(())
3639}
3640
3641fn emit_in_operator(
3649 emitter: &mut FunctionEmitter,
3650 ctx: &CodegenCtx,
3651 lhs: ExprId,
3652 rhs: ExprId,
3653) -> Result<(), crate::compiler_error::CompilerFailure> {
3654 emit_expr(emitter, ctx, lhs)?;
3655 crate::codegen::cast_check::emit_operation_cast_on_stack(
3656 emitter,
3657 ctx,
3658 &ctx.ta
3659 .try_expr(lhs)
3660 .map_err(crate::codegen::arena_failure)?
3661 .ty,
3662 &Type::String,
3663 )?;
3664 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
3665 emitter.instruction(Instruction::LocalSet(key));
3666 emit_expr(emitter, ctx, rhs)?;
3667 cast::emit_box(
3668 emitter,
3669 ctx,
3670 &ctx.ta
3671 .try_expr(rhs)
3672 .map_err(crate::codegen::arena_failure)?
3673 .ty,
3674 )?;
3675 emitter.instruction(Instruction::LocalGet(key));
3676 let Some(symbol) = ctx.require(
3677 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
3678 "presence helper imported",
3679 ) else {
3680 return Ok(());
3681 };
3682 emitter.instruction(Instruction::Call(symbol));
3683 Ok(())
3684}
3685
3686pub(crate) fn emit_class_field_setup(
3691 emitter: &mut FunctionEmitter,
3692 ctx: &CodegenCtx,
3693 mangled: &crate::MangledName,
3694) -> Result<(), crate::compiler_error::CompilerFailure> {
3695 emit_class_field_setup_phase(emitter, ctx, mangled, FieldSetupPhase::All)
3696}
3697
3698#[derive(Clone, Copy)]
3699pub(crate) enum FieldSetupPhase {
3700 All,
3701 Initializers,
3702 ParameterProperties,
3703}
3704
3705pub(crate) fn emit_class_field_setup_phase(
3706 emitter: &mut FunctionEmitter,
3707 ctx: &CodegenCtx,
3708 mangled: &crate::MangledName,
3709 phase: FieldSetupPhase,
3710) -> Result<(), crate::compiler_error::CompilerFailure> {
3711 use crate::codegen::symbol_table::FieldSetup;
3712 let setup: Vec<FieldSetup> = match ctx.symbols.class_field_setup(mangled) {
3713 Some(s) if !s.is_empty() => s.to_vec(),
3714 _ => return Ok(()),
3715 };
3716 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3717 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3718 })?;
3719 let intrinsics = ctx
3720 .symbols
3721 .intrinsic_type_indices()
3722 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3723 let this = emitter.this_local().ok_or_else(|| {
3724 crate::codegen::internal_failure("field setup runs inside a constructor init fn")
3725 })?;
3726 for step in &setup {
3727 let parameter_property = matches!(step, FieldSetup::ParamCopy { .. });
3728 if matches!(phase, FieldSetupPhase::Initializers) && parameter_property
3729 || matches!(phase, FieldSetupPhase::ParameterProperties) && !parameter_property
3730 {
3731 continue;
3732 }
3733 let field = match step {
3734 FieldSetup::Init { field, .. }
3735 | FieldSetup::ParamCopy { field, .. }
3736 | FieldSetup::Reset { field } => field,
3737 };
3738 let slot = ctx
3739 .symbols
3740 .class_field_slot(mangled, field)
3741 .ok_or_else(|| {
3742 crate::codegen::internal_failure("class field slot recorded in classes::emit")
3743 })?;
3744 match step {
3745 FieldSetup::Init { value, .. } => {
3746 let value_ty = ctx
3747 .ta
3748 .try_expr(*value)
3749 .map_err(crate::codegen::arena_failure)?
3750 .ty
3751 .clone();
3752 emit_expr(emitter, ctx, *value)?;
3753 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &value_ty)?;
3754 }
3755 FieldSetup::ParamCopy { param, .. } => {
3756 let local = emitter.require_local_slot(¶m.name.name)?;
3757 if param.boxed {
3758 let box_type = ctx.symbols.box_type_idx(¶m.ty)?.ok_or_else(|| {
3759 crate::codegen::internal_failure(
3760 "constructor parameter cell is not registered",
3761 )
3762 })?;
3763 super::binding_cells::read(emitter, ctx, local, box_type)?;
3764 cast::emit_unerase(emitter, ctx, ¶m.ty)?;
3765 } else {
3766 emitter.instruction(Instruction::LocalGet(local));
3767 }
3768 cast::emit_box(emitter, ctx, ¶m.ty)?;
3769 }
3770 FieldSetup::Reset { .. } => {
3771 emit_undefined(emitter, ctx)?;
3772 }
3773 }
3774 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
3775 emitter.instruction(Instruction::LocalSet(stored));
3776 emitter.instruction(Instruction::LocalGet(this));
3777 emitter.instruction(Instruction::StructGet {
3778 struct_type_index: struct_idx,
3779 field_index: 2,
3780 });
3781 emitter.instruction(Instruction::I32Const(slot as i32));
3782 emitter.instruction(Instruction::LocalGet(stored));
3783 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
3784 let index = emitter.add_anonymous_local(ValType::I32)?;
3785 emitter.instruction(Instruction::I32Const(slot as i32));
3786 emitter.instruction(Instruction::LocalSet(index));
3787 crate::codegen::field_names::emit_mark_present(emitter, ctx, this, index)?;
3788 }
3789 Ok(())
3790}
3791
3792pub(crate) fn emit_field_slot_get(
3796 emitter: &mut FunctionEmitter,
3797 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
3798 object_local: u32,
3799 index_local: u32,
3800) {
3801 emitter.instruction(Instruction::LocalGet(object_local));
3802 emitter.instruction(Instruction::StructGet {
3803 struct_type_index: intrinsics.object_shape,
3804 field_index: 2,
3805 });
3806 emitter.instruction(Instruction::LocalGet(index_local));
3807 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3808}
3809
3810pub(crate) fn emit_object_field_read_by_name(
3811 emitter: &mut FunctionEmitter,
3812 ctx: &CodegenCtx,
3813 object_local: u32,
3814 name_global: u32,
3815) {
3816 ctx.latch(emit_object_field_read_by_name_checked(
3817 emitter,
3818 ctx,
3819 object_local,
3820 name_global,
3821 ));
3822}
3823
3824fn emit_object_field_read_by_name_checked(
3825 emitter: &mut FunctionEmitter,
3826 ctx: &CodegenCtx,
3827 object_local: u32,
3828 name_global: u32,
3829) -> Result<(), crate::compiler_error::CompilerFailure> {
3830 let intrinsics = ctx
3831 .symbols
3832 .intrinsic_type_indices()
3833 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3834 let Some(index_local) = ctx.latch(emitter.add_anonymous_local(ValType::I32)) else {
3835 return Ok(());
3836 };
3837 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
3838 emitter.instruction(Instruction::LocalSet(index_local));
3839 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
3840 nullable: true,
3841 heap_type: HeapType::Concrete(intrinsics.object),
3842 })));
3843 emitter.instruction(Instruction::LocalGet(index_local));
3844 emitter.instruction(Instruction::I32Const(0));
3845 emitter.instruction(Instruction::I32LtS);
3846 emitter.emit_if(BlockType::Empty);
3847 emit_undefined(emitter, ctx)?;
3848 emitter.instruction(Instruction::Br(1));
3849 emitter.emit_end();
3850 emitter.instruction(Instruction::LocalGet(object_local));
3851 emitter.instruction(Instruction::StructGet {
3852 struct_type_index: intrinsics.object_shape,
3853 field_index: 2,
3854 });
3855 emitter.instruction(Instruction::LocalGet(index_local));
3856 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3857 emitter.emit_end();
3858
3859 Ok(())
3860}
3861
3862pub(crate) fn emit_object_field_write_by_name(
3863 emitter: &mut FunctionEmitter,
3864 ctx: &CodegenCtx,
3865 object_local: u32,
3866 name_global: u32,
3867) -> Result<(), crate::compiler_error::CompilerFailure> {
3868 let intrinsics = ctx
3869 .symbols
3870 .intrinsic_type_indices()
3871 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3872 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
3873 nullable: true,
3874 heap_type: HeapType::Concrete(intrinsics.object),
3875 }))?;
3876 let index_local = emitter.add_anonymous_local(ValType::I32)?;
3877 emitter.instruction(Instruction::LocalSet(value_local));
3878 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
3879 emitter.instruction(Instruction::LocalTee(index_local));
3880 emitter.instruction(Instruction::I32Const(0));
3881 emitter.instruction(Instruction::I32GeS);
3882 emitter.emit_if(BlockType::Empty);
3883 emitter.instruction(Instruction::LocalGet(object_local));
3884 emitter.instruction(Instruction::StructGet {
3885 struct_type_index: intrinsics.object_shape,
3886 field_index: 2,
3887 });
3888 emitter.instruction(Instruction::LocalGet(index_local));
3889 emitter.instruction(Instruction::LocalGet(value_local));
3890 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
3891 crate::codegen::field_names::emit_mark_present(emitter, ctx, object_local, index_local)?;
3892 emitter.emit_else();
3893 emitter.instruction(Instruction::LocalGet(object_local));
3894 emitter.instruction(Instruction::GlobalGet(name_global));
3895 emitter.instruction(Instruction::LocalGet(value_local));
3896 let insert = ctx
3897 .symbols
3898 .prelude_func_idx("ObjectConstructor##insertField")
3899 .ok_or_else(|| {
3900 crate::codegen::internal_failure("field insertion helper is not imported")
3901 })?;
3902 emitter.instruction(Instruction::Call(insert));
3903 emitter.emit_end();
3904 Ok(())
3905}
3906
3907pub(crate) fn accessor_branch_emittable(ctx: &CodegenCtx, getter_name: &str) -> bool {
3914 ctx.symbols
3915 .field_name_string_global_idx(getter_name)
3916 .is_some()
3917}
3918
3919pub(crate) fn emit_is_accessor_backed(
3936 emitter: &mut FunctionEmitter,
3937 ctx: &CodegenCtx,
3938 object_local: u32,
3939 accessor_name: &str,
3940 kind: crate::AccessorKind,
3941) -> Result<u32, crate::compiler_error::CompilerFailure> {
3942 let intrinsics = ctx
3943 .symbols
3944 .intrinsic_type_indices()
3945 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3946 let accessor_global = ctx
3947 .symbols
3948 .field_name_string_global_idx(accessor_name)
3949 .ok_or_else(|| {
3950 crate::codegen::internal_failure("caller checked the accessor name has a string global")
3951 })?;
3952 let closure_struct_idx = ctx
3953 .symbols
3954 .closure_struct_type_idx(crate::codegen::classes::accessor_closure_sig(kind))
3955 .ok_or_else(|| {
3956 crate::codegen::internal_failure(
3957 "accessor closure struct collected by analysis::note_shaped_property_access",
3958 )
3959 })?;
3960 let index_local = emitter.add_anonymous_local(ValType::I32)?;
3961 emit_field_index_by_name(emitter, ctx, object_local, accessor_global, true);
3962 emitter.instruction(Instruction::LocalTee(index_local));
3963 emitter.instruction(Instruction::I32Const(0));
3964 emitter.instruction(Instruction::I32GeS);
3965 emitter.instruction(Instruction::LocalGet(object_local));
3966 emitter.instruction(Instruction::StructGet {
3967 struct_type_index: intrinsics.object,
3968 field_index: 0,
3969 });
3970 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
3971 intrinsics.class_vtable,
3972 )));
3973 emitter.instruction(Instruction::I32And);
3974 emitter.emit_if(BlockType::Result(ValType::I32));
3975 emitter.instruction(Instruction::LocalGet(object_local));
3976 emitter.instruction(Instruction::StructGet {
3977 struct_type_index: intrinsics.object_shape,
3978 field_index: 1,
3979 });
3980 emitter.instruction(Instruction::LocalGet(index_local));
3981 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
3982 crate::codegen::field_names::emit_name_is_accessor(emitter, ctx)?;
3983 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
3984 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
3985 closure_struct_idx,
3986 )));
3987 emitter.instruction(Instruction::I32And);
3988 emitter.emit_else();
3989 emitter.instruction(Instruction::I32Const(0));
3990 emitter.emit_end();
3991 Ok(index_local)
3992}
3993
3994pub(crate) fn emit_object_property_read(
4004 emitter: &mut FunctionEmitter,
4005 ctx: &CodegenCtx,
4006 object_local: u32,
4007 prop_name: &str,
4008 field_ty: &Type,
4009) -> Result<(), crate::compiler_error::CompilerFailure> {
4010 emit_object_property_read_as(emitter, ctx, object_local, prop_name, field_ty, field_ty)?;
4011 Ok(())
4012}
4013
4014fn emit_object_property_read_as(
4015 emitter: &mut FunctionEmitter,
4016 ctx: &CodegenCtx,
4017 object_local: u32,
4018 prop_name: &str,
4019 field_ty: &Type,
4020 check_ty: &Type,
4021) -> Result<(), crate::compiler_error::CompilerFailure> {
4022 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4023 let name_global = ctx
4024 .symbols
4025 .field_name_string_global_idx(prop_name)
4026 .ok_or_else(|| {
4027 crate::codegen::internal_failure(
4028 "per-name string global recorded during field-name-strings emission",
4029 )
4030 })?;
4031 if !accessor_branch_emittable(ctx, &getter) {
4032 emit_object_field_read_by_name(emitter, ctx, object_local, name_global);
4033 emit_dynamic_narrowed_property_read(
4034 emitter,
4035 ctx,
4036 object_local,
4037 prop_name,
4038 field_ty,
4039 check_ty,
4040 )?;
4041 return Ok(());
4042 }
4043 let intrinsics = ctx
4044 .symbols
4045 .intrinsic_type_indices()
4046 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4047 let result_vt = ctx.symbols.value_type(field_ty)?;
4048 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4049 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4050 emitter.instruction(Instruction::LocalSet(index_local));
4051 emitter.instruction(Instruction::LocalGet(index_local));
4052 emitter.instruction(Instruction::I32Const(0));
4053 emitter.instruction(Instruction::I32GeS);
4054 emitter.emit_if(BlockType::Result(result_vt));
4055 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4057 emit_dynamic_narrowed_property_read(emitter, ctx, object_local, prop_name, field_ty, check_ty)?;
4058 emitter.emit_else();
4059 let getter_slot_local = emit_is_accessor_backed(
4060 emitter,
4061 ctx,
4062 object_local,
4063 &getter,
4064 crate::AccessorKind::Get,
4065 )?;
4066 emitter.emit_if(BlockType::Result(result_vt));
4067 emitter.instruction(Instruction::LocalGet(object_local));
4069 emit_interface_method_via_shape_with_receiver_on_stack(
4070 emitter,
4071 ctx,
4072 &getter,
4073 &[],
4074 field_ty,
4075 Some(getter_slot_local),
4076 )?;
4077 emitter.emit_else();
4078 emit_undefined(emitter, ctx)?;
4084 cast::emit_cast_to(emitter, ctx, field_ty)?;
4085 emitter.emit_end();
4086 emitter.emit_end();
4087 Ok(())
4088}
4089
4090fn emit_dynamic_narrowed_property_read(
4091 emitter: &mut FunctionEmitter,
4092 ctx: &CodegenCtx,
4093 object_local: u32,
4094 prop_name: &str,
4095 field_ty: &Type,
4096 check_ty: &Type,
4097) -> Result<(), crate::compiler_error::CompilerFailure> {
4098 let checks: Vec<_> = ctx
4099 .symbols
4100 .class_field_narrowing_checks(prop_name)
4101 .filter_map(|(class, check)| {
4102 ctx.symbols
4103 .class_vtable_global_idx(class)
4104 .map(|vtable| (vtable, check))
4105 })
4106 .collect();
4107 if checks.is_empty() {
4108 cast::emit_cast_to(emitter, ctx, field_ty)?;
4109 return Ok(());
4110 }
4111
4112 let intrinsics = ctx
4113 .symbols
4114 .intrinsic_type_indices()
4115 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4116 let raw = emitter.add_anonymous_local(ValType::Ref(RefType {
4117 nullable: true,
4118 heap_type: HeapType::Concrete(intrinsics.object),
4119 }))?;
4120 emitter.instruction(Instruction::LocalSet(raw));
4121 emit_dynamic_narrowing_check(
4122 emitter,
4123 ctx,
4124 object_local,
4125 raw,
4126 field_ty,
4127 check_ty,
4128 &checks,
4129 0,
4130 )?;
4131 Ok(())
4132}
4133
4134#[allow(clippy::too_many_arguments)]
4135fn emit_dynamic_narrowing_check(
4136 emitter: &mut FunctionEmitter,
4137 ctx: &CodegenCtx,
4138 object_local: u32,
4139 raw: u32,
4140 field_ty: &Type,
4141 check_ty: &Type,
4142 checks: &[(u32, &crate::FieldNarrowingCheck)],
4143 index: usize,
4144) -> Result<(), crate::compiler_error::CompilerFailure> {
4145 let Some((vtable, check)) = checks.get(index) else {
4146 emitter.instruction(Instruction::LocalGet(raw));
4147 cast::emit_cast_to(emitter, ctx, field_ty)?;
4148 return Ok(());
4149 };
4150 emitter.instruction(Instruction::LocalGet(object_local));
4151 crate::codegen::function_emitter::cast::emit_nominal_instance_test(emitter, ctx, *vtable)?;
4152 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(field_ty)?));
4153 emitter.instruction(Instruction::LocalGet(raw));
4154 crate::codegen::cast_check::emit_narrowed_field_read_as(
4155 emitter, ctx, check, field_ty, check_ty,
4156 )?;
4157 emitter.emit_else();
4158 emit_dynamic_narrowing_check(
4159 emitter,
4160 ctx,
4161 object_local,
4162 raw,
4163 field_ty,
4164 check_ty,
4165 checks,
4166 index + 1,
4167 )?;
4168 emitter.emit_end();
4169 Ok(())
4170}
4171
4172pub(crate) fn emit_object_property_write(
4178 emitter: &mut FunctionEmitter,
4179 ctx: &CodegenCtx,
4180 object_local: u32,
4181 prop: &Ident,
4182 value: ExprId,
4183) -> Result<(), crate::compiler_error::CompilerFailure> {
4184 emit_object_property_write_value(
4185 emitter,
4186 ctx,
4187 object_local,
4188 prop,
4189 &ShapeArgument::Expression(value),
4190 )?;
4191 Ok(())
4192}
4193
4194fn emit_object_property_write_value(
4195 emitter: &mut FunctionEmitter,
4196 ctx: &CodegenCtx,
4197 object_local: u32,
4198 prop: &Ident,
4199 value: &ShapeArgument,
4200) -> Result<(), crate::compiler_error::CompilerFailure> {
4201 let prop_name = prop.name.as_str();
4202 let setter = crate::codegen::classes::accessor_setter_name(prop_name);
4203 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4204 let has_setter_name = ctx.symbols.field_name_string_global_idx(&setter).is_some();
4205 let has_getter_name = ctx.symbols.field_name_string_global_idx(&getter).is_some();
4206 let name_global = ctx
4207 .symbols
4208 .field_name_string_global_idx(prop_name)
4209 .ok_or_else(|| {
4210 crate::codegen::internal_failure(
4211 "per-name string global recorded during field-name-strings emission",
4212 )
4213 })?;
4214 if !has_setter_name && !has_getter_name {
4218 let value_ty = value.ty(ctx)?;
4219 value.emit(emitter, ctx)?;
4220 cast::emit_box(emitter, ctx, &value_ty)?;
4221 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4222 return Ok(());
4223 }
4224 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4225 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4226 emitter.instruction(Instruction::LocalSet(index_local));
4227 emitter.instruction(Instruction::LocalGet(index_local));
4228 emitter.instruction(Instruction::I32Const(0));
4229 emitter.instruction(Instruction::I32GeS);
4230 emitter.emit_if(BlockType::Empty);
4231 let value_ty = value.ty(ctx)?;
4234 value.emit(emitter, ctx)?;
4235 cast::emit_box(emitter, ctx, &value_ty)?;
4236 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4237 emitter.emit_else();
4238 let arms: Vec<(&str, AccessorWrite)> = [
4243 has_setter_name.then_some((setter.as_str(), AccessorWrite::Setter)),
4244 has_getter_name.then_some((getter.as_str(), AccessorWrite::ReadOnly)),
4245 ]
4246 .into_iter()
4247 .flatten()
4248 .collect();
4249 for (accessor, arm) in &arms {
4250 let slot_index_local =
4251 emit_is_accessor_backed(emitter, ctx, object_local, accessor, arm.scanned_kind())?;
4252 emitter.emit_if(BlockType::Empty);
4253 match arm {
4254 AccessorWrite::Setter => {
4255 emitter.instruction(Instruction::LocalGet(object_local));
4256 emit_shape_method_with_arguments(
4257 emitter,
4258 ctx,
4259 accessor,
4260 std::slice::from_ref(value),
4261 &Type::Void,
4262 Some(slot_index_local),
4263 )?;
4264 emitter.instruction(Instruction::Drop);
4265 }
4266 AccessorWrite::ReadOnly => {
4269 value.emit(emitter, ctx)?;
4270 emitter.instruction(Instruction::Drop);
4271 emitter.record_span(prop.span);
4274 crate::codegen::throw::emit_type_error_throw(
4275 emitter,
4276 ctx,
4277 crate::codegen::throw::READ_ONLY_PROPERTY_MESSAGE,
4278 );
4279 }
4280 }
4281 emitter.emit_else();
4282 }
4283 let value_ty = value.ty(ctx)?;
4284 value.emit(emitter, ctx)?;
4285 cast::emit_box(emitter, ctx, &value_ty)?;
4286 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4287 for _ in &arms {
4288 emitter.emit_end();
4289 }
4290 emitter.emit_end();
4291 Ok(())
4292}
4293
4294fn emit_bounds_checked_index_with_receiver_on_stack(
4308 emitter: &mut FunctionEmitter,
4309 ctx: &CodegenCtx,
4310 idx: ExprId,
4311 result_ty: &Type,
4312 allow_missing: bool,
4313) -> Result<(), crate::compiler_error::CompilerFailure> {
4314 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
4315 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4316 })?;
4317 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
4318 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4319 })?;
4320 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4321 nullable: false,
4322 heap_type: HeapType::Concrete(raw_array_idx),
4323 }))?;
4324 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4325 emitter.instruction(Instruction::LocalSet(receiver));
4326 emit_expr(emitter, ctx, idx)?;
4327 let key = emitter.add_anonymous_local(
4328 ctx.symbols.value_type(
4329 &ctx.ta
4330 .try_expr(idx)
4331 .map_err(crate::codegen::arena_failure)?
4332 .ty,
4333 )?,
4334 )?;
4335 emitter.instruction(Instruction::LocalSet(key));
4336 emitter.instruction(Instruction::LocalGet(receiver));
4337 crate::codegen::cast_check::emit_operation_cast_on_stack(
4338 emitter,
4339 ctx,
4340 &Type::Unknown,
4341 &Type::Array(Box::new(Type::Unknown)),
4342 )?;
4343 emitter.instruction(Instruction::LocalGet(key));
4344 emit_index_number(
4345 emitter,
4346 ctx,
4347 &ctx.ta
4348 .try_expr(idx)
4349 .map_err(crate::codegen::arena_failure)?
4350 .ty,
4351 )?;
4352 let idx_f64_local = stash_index_operand(emitter)?;
4353 let length = stash_array_length(emitter, array_idx)?;
4354 emitter.instruction(Instruction::StructGet {
4355 struct_type_index: array_idx,
4356 field_index: 1,
4357 });
4358 emitter.instruction(Instruction::LocalSet(raw_arr_local));
4359 if allow_missing {
4360 emitter.instruction(Instruction::LocalGet(idx_f64_local));
4361 emitter.instruction(Instruction::LocalGet(length));
4362 emitter.instruction(Instruction::F64ConvertI32U);
4363 emitter.instruction(Instruction::F64Ge);
4364 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
4365 emit_undefined(emitter, ctx)?;
4366 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
4367 emitter.emit_else();
4368 }
4369 let idx_local = emit_checked_index_with_length(emitter, ctx, length, idx_f64_local)?;
4370 emitter.instruction(Instruction::LocalGet(raw_arr_local));
4371 emitter.instruction(Instruction::LocalGet(idx_local));
4372 emitter.instruction(Instruction::ArrayGet(raw_array_idx));
4373 cast::emit_cast_to(emitter, ctx, result_ty)?;
4374 if allow_missing {
4375 emitter.emit_end();
4376 }
4377 Ok(())
4378}
4379
4380fn is_optional_tuple_position(
4381 ctx: &CodegenCtx<'_>,
4382 receiver: &Type,
4383 index: ExprId,
4384) -> Result<bool, crate::compiler_error::CompilerFailure> {
4385 let expression = ctx
4386 .ta
4387 .try_expr(index)
4388 .map_err(crate::codegen::arena_failure)?;
4389 let TypedExprKind::Number(index) = expression.kind else {
4390 return Ok(false);
4391 };
4392 Ok(index.fract() == 0.0 && tuple_permits_missing_index(receiver, index))
4393}
4394
4395fn tuple_permits_missing_index(receiver: &Type, index: f64) -> bool {
4396 match receiver.peel() {
4397 Type::Tuple(elements) => {
4398 index >= elements.required_len() as f64 && index < elements.len() as f64
4399 }
4400 Type::Union(members) => members
4401 .iter()
4402 .any(|member| tuple_permits_missing_index(member, index)),
4403 _ => false,
4404 }
4405}
4406
4407fn emit_uint8_index_with_receiver_on_stack(
4408 emitter: &mut FunctionEmitter,
4409 ctx: &CodegenCtx,
4410 index: ExprId,
4411) -> Result<(), crate::compiler_error::CompilerFailure> {
4412 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
4413 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4414 })?;
4415 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
4416 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4417 })?;
4418 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4419 nullable: false,
4420 heap_type: HeapType::Concrete(raw_uint8_idx),
4421 }))?;
4422 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4423 emitter.instruction(Instruction::LocalSet(receiver));
4424 emit_expr(emitter, ctx, index)?;
4425 let key = emitter.add_anonymous_local(
4426 ctx.symbols.value_type(
4427 &ctx.ta
4428 .try_expr(index)
4429 .map_err(crate::codegen::arena_failure)?
4430 .ty,
4431 )?,
4432 )?;
4433 emitter.instruction(Instruction::LocalSet(key));
4434 emitter.instruction(Instruction::LocalGet(receiver));
4435 crate::codegen::cast_check::emit_operation_cast_on_stack(
4436 emitter,
4437 ctx,
4438 &Type::Unknown,
4439 &Type::Uint8Array,
4440 )?;
4441 emitter.instruction(Instruction::LocalGet(key));
4442 emit_index_number(
4443 emitter,
4444 ctx,
4445 &ctx.ta
4446 .try_expr(index)
4447 .map_err(crate::codegen::arena_failure)?
4448 .ty,
4449 )?;
4450 let idx_f64_local = stash_index_operand(emitter)?;
4451 emitter.instruction(Instruction::StructGet {
4452 struct_type_index: uint8_idx,
4453 field_index: 1,
4454 });
4455 emitter.instruction(Instruction::LocalSet(raw_local));
4456 let idx_local = emit_checked_index(emitter, ctx, raw_local, idx_f64_local)?;
4457 emitter.instruction(Instruction::LocalGet(raw_local));
4458 emitter.instruction(Instruction::LocalGet(idx_local));
4459 emitter.instruction(Instruction::ArrayGetU(raw_uint8_idx));
4460 emitter.instruction(Instruction::F64ConvertI32U);
4461 Ok(())
4462}
4463
4464#[derive(Clone, Copy)]
4466enum AccessorWrite {
4467 Setter,
4468 ReadOnly,
4469}
4470
4471impl AccessorWrite {
4472 fn scanned_kind(self) -> crate::AccessorKind {
4475 match self {
4476 AccessorWrite::Setter => crate::AccessorKind::Set,
4477 AccessorWrite::ReadOnly => crate::AccessorKind::Get,
4478 }
4479 }
4480}
4481
4482pub(crate) fn emit_object_field_index_by_name(
4483 emitter: &mut FunctionEmitter,
4484 ctx: &CodegenCtx,
4485 object_local: u32,
4486 name_global: u32,
4487) {
4488 emit_field_index_by_name(emitter, ctx, object_local, name_global, false);
4489}
4490
4491fn emit_field_index_by_name(
4492 emitter: &mut FunctionEmitter,
4493 ctx: &CodegenCtx,
4494 object_local: u32,
4495 name_global: u32,
4496 accessor: bool,
4497) {
4498 ctx.latch(emit_field_index_by_name_checked(
4499 emitter,
4500 ctx,
4501 object_local,
4502 name_global,
4503 accessor,
4504 ));
4505}
4506
4507fn emit_field_index_by_name_checked(
4508 emitter: &mut FunctionEmitter,
4509 ctx: &CodegenCtx,
4510 object_local: u32,
4511 name_global: u32,
4512 accessor: bool,
4513) -> Result<(), crate::compiler_error::CompilerFailure> {
4514 emitter.instruction(Instruction::LocalGet(object_local));
4515 emitter.instruction(Instruction::GlobalGet(name_global));
4516 emitter.instruction(Instruction::I32Const(i32::from(accessor)));
4517 emitter.instruction(Instruction::Call(
4518 ctx.symbols
4519 .field_lookup_function
4520 .ok_or_else(|| crate::codegen::internal_failure("field lookup allocated"))?,
4521 ));
4522
4523 Ok(())
4524}
4525
4526fn emit_direct_call(
4532 emitter: &mut FunctionEmitter,
4533 ctx: &CodegenCtx,
4534 is_host: bool,
4535 wasm_idx: u32,
4536 params: &[Type],
4537 ret: &Type,
4538 args: &[ExprId],
4539) -> Result<(), crate::compiler_error::CompilerFailure> {
4540 if params.len() != args.len() {
4541 return Err(crate::codegen::internal_failure(
4542 "direct-call argument count mismatch",
4543 ));
4544 }
4545 let string_type_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
4546 crate::codegen::internal_failure("$string type registered before call codegen")
4547 })?;
4548 let raw_string_type_idx = ctx.symbols.raw_string_type_idx().ok_or_else(|| {
4549 crate::codegen::internal_failure("$rawString type registered before call codegen")
4550 })?;
4551 let mut evaluated = Vec::with_capacity(args.len());
4552 for &arg in args {
4553 emit_expr(emitter, ctx, arg)?;
4554 let slot = emitter.add_anonymous_local(
4555 ctx.symbols.value_type(
4556 &ctx.ta
4557 .try_expr(arg)
4558 .map_err(crate::codegen::arena_failure)?
4559 .ty,
4560 )?,
4561 )?;
4562 emitter.instruction(Instruction::LocalSet(slot));
4563 evaluated.push(slot);
4564 }
4565 for (i, (&arg_id, local)) in args.iter().zip(evaluated).enumerate() {
4566 emitter.instruction(Instruction::LocalGet(local));
4567 if let Some(param_ty) = params.get(i) {
4573 let arg_ty = ctx
4574 .ta
4575 .try_expr(arg_id)
4576 .map_err(crate::codegen::arena_failure)?
4577 .ty
4578 .clone();
4579 let default = ctx
4580 .symbols
4581 .host_call_defaults(wasm_idx, args.len())
4582 .get(i)
4583 .and_then(Option::as_ref);
4584 crate::codegen::argument_defaults::emit_argument(
4585 emitter,
4586 ctx,
4587 &arg_ty,
4588 ctx.symbols.value_type(param_ty)?,
4589 default,
4590 )?;
4591 }
4592 if is_host
4597 && params
4598 .get(i)
4599 .is_some_and(|p| matches!(p.peel(), Type::String))
4600 {
4601 emitter.instruction(Instruction::StructGet {
4602 struct_type_index: string_type_idx,
4603 field_index: 1,
4604 });
4605 }
4606 }
4607 emitter.instruction(Instruction::Call(wasm_idx));
4608 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
4609 emit_undefined(emitter, ctx)?;
4610 }
4611 let _: () = if is_host && matches!(ret.peel(), Type::String) {
4612 let scratch = emitter.add_anonymous_local(ValType::Ref(RefType {
4613 nullable: false,
4614 heap_type: HeapType::Concrete(raw_string_type_idx),
4615 }))?;
4616 emitter.instruction(Instruction::LocalSet(scratch));
4617 let vtable_global = ctx
4618 .symbols
4619 .prelude_global_idx("string_vtable")
4620 .ok_or_else(|| {
4621 crate::codegen::internal_failure("string_vtable imported from prelude")
4622 })?;
4623 emitter.instruction(Instruction::GlobalGet(vtable_global));
4624 emitter.instruction(Instruction::LocalGet(scratch));
4625 emitter.instruction(Instruction::I64Const(0));
4626 emitter.instruction(Instruction::StructNew(string_type_idx));
4627 };
4628 Ok(())
4629}
4630
4631fn emit_indirect_closure_call(
4640 emitter: &mut FunctionEmitter,
4641 ctx: &CodegenCtx,
4642 callee: ExprId,
4643 args: &[ExprId],
4644 result_ty: &Type,
4645) -> Result<(), crate::compiler_error::CompilerFailure> {
4646 let callee_ty = ctx
4647 .ta
4648 .source_type(callee)
4649 .map_err(crate::codegen::arena_failure)?
4650 .clone();
4651 let receiver = emit_callee(emitter, ctx, callee)?;
4652 emitter.call_receiver = receiver;
4653 emit_indirect_closure_call_with_receiver_on_stack(emitter, ctx, &callee_ty, args, result_ty)?;
4654 Ok(())
4655}
4656
4657fn emit_callee(
4659 emitter: &mut FunctionEmitter,
4660 ctx: &CodegenCtx,
4661 callee: ExprId,
4662) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
4663 let reference = reference_expression(ctx.ta, callee)?;
4664 let receiver = match &ctx
4665 .ta
4666 .try_expr(reference)
4667 .map_err(crate::codegen::arena_failure)?
4668 .kind
4669 {
4670 TypedExprKind::FieldAccess { receiver, .. }
4671 | TypedExprKind::IndexAccess { receiver, .. }
4672 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => Some(*receiver),
4673 _ => None,
4674 };
4675 let mark = emitter.single_evaluation_mark();
4676 let slot = receiver
4677 .map(|receiver| {
4678 emit_expr(emitter, ctx, receiver)?;
4679 let slot = emitter.add_anonymous_local(
4680 ctx.symbols.value_type(
4681 &ctx.ta
4682 .try_expr(receiver)
4683 .map_err(crate::codegen::arena_failure)?
4684 .ty,
4685 )?,
4686 )?;
4687 emitter.instruction(Instruction::LocalSet(slot));
4688 emitter.record_single_evaluation(receiver, slot)?;
4689 Ok::<_, crate::compiler_error::CompilerFailure>(slot)
4690 })
4691 .transpose()?;
4692 emit_expr(emitter, ctx, callee)?;
4693 emitter.end_single_evaluations(mark)?;
4694 Ok(
4695 if matches!(
4696 ctx.ta
4697 .try_expr(reference)
4698 .map_err(crate::codegen::arena_failure)?
4699 .kind,
4700 TypedExprKind::OptionalChain { .. }
4701 ) {
4702 emitter.call_receiver.take()
4703 } else {
4704 slot
4705 },
4706 )
4707}
4708
4709fn reference_expression(
4710 ast: &crate::TypedAst,
4711 mut id: ExprId,
4712) -> Result<ExprId, crate::compiler_error::CompilerFailure> {
4713 loop {
4714 match ast
4715 .try_expr(id)
4716 .map_err(crate::codegen::arena_failure)?
4717 .kind
4718 {
4719 TypedExprKind::Cast { value, .. } | TypedExprKind::NonNullAssert { value } => {
4720 id = value;
4721 }
4722 _ => return Ok(id),
4723 }
4724 }
4725}
4726
4727fn emit_indirect_closure_call_with_receiver_on_stack(
4733 emitter: &mut FunctionEmitter,
4734 ctx: &CodegenCtx,
4735 callee_ty: &Type,
4736 args: &[ExprId],
4737 result_ty: &Type,
4738) -> Result<(), crate::compiler_error::CompilerFailure> {
4739 let receiver = emitter.call_receiver.take();
4740 let callee = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4741 emitter.instruction(Instruction::LocalSet(callee));
4742 let mut arguments = Vec::with_capacity(args.len());
4743 for &arg in args {
4744 emit_expr(emitter, ctx, arg)?;
4745 cast::emit_box(
4746 emitter,
4747 ctx,
4748 &ctx.ta
4749 .try_expr(arg)
4750 .map_err(crate::codegen::arena_failure)?
4751 .ty,
4752 )?;
4753 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4754 emitter.instruction(Instruction::LocalSet(slot));
4755 arguments.push(slot);
4756 }
4757 emitter.instruction(Instruction::LocalGet(callee));
4758 crate::codegen::cast_check::emit_operation_cast_on_stack(
4759 emitter,
4760 ctx,
4761 &Type::Unknown,
4762 callee_ty,
4763 )?;
4764 let callee_ty = callee_ty.peel();
4769 let closure_sig = crate::codegen::closures::classify(callee_ty)?;
4770 let closure_struct_idx = ctx
4771 .symbols
4772 .closure_struct_type_idx(closure_sig)
4773 .ok_or_else(|| {
4774 crate::codegen::internal_failure(
4775 "closure struct type registered for every closure-typed callee",
4776 )
4777 })?;
4778 let fn_type_idx = ctx
4779 .symbols
4780 .closure_func_type_idx(closure_sig)
4781 .ok_or_else(|| {
4782 crate::codegen::internal_failure(
4783 "closure funcref type registered for every closure-typed callee",
4784 )
4785 })?;
4786 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4787 nullable: false,
4788 heap_type: HeapType::Concrete(closure_struct_idx),
4789 }))?;
4790 emitter.instruction(Instruction::LocalTee(closure_local));
4791 emitter.instruction(Instruction::StructGet {
4792 struct_type_index: closure_struct_idx,
4793 field_index: 2,
4794 });
4795 if let Some(receiver) = receiver {
4796 crate::codegen::this_binding::bind(emitter, ctx, receiver)?;
4797 }
4798 for argument in arguments {
4806 emitter.instruction(Instruction::LocalGet(argument));
4807 }
4808 emitter.instruction(Instruction::LocalGet(closure_local));
4809 emitter.instruction(Instruction::StructGet {
4810 struct_type_index: closure_struct_idx,
4811 field_index: 1,
4812 });
4813 emitter.instruction(Instruction::CallRef(fn_type_idx));
4814 let Type::Function { ret, .. } = callee_ty.peel() else {
4817 return Err(crate::codegen::internal_failure(
4818 "indirect call requires a function type",
4819 ));
4820 };
4821 let _: () = if !ret.is_void() {
4822 crate::codegen::cast_check::emit_checked_cast_on_stack(
4823 emitter,
4824 ctx,
4825 &Type::Unknown,
4826 result_ty,
4827 )?;
4828 };
4829 Ok(())
4830}
4831
4832fn emit_typeof_tag(
4835 emitter: &mut FunctionEmitter,
4836 ctx: &CodegenCtx,
4837 value: ExprId,
4838 tag: crate::TypeofTagKind,
4839) -> Result<(), crate::compiler_error::CompilerFailure> {
4840 let _: () = match tag {
4841 crate::TypeofTagKind::BigInt => {
4842 emit_expr(emitter, ctx, value)?;
4843 cast::emit_box(
4844 emitter,
4845 ctx,
4846 &ctx.ta
4847 .try_expr(value)
4848 .map_err(crate::codegen::arena_failure)?
4849 .ty,
4850 )?;
4851 let intrinsic = ctx
4852 .symbols
4853 .bigint_type_idx()
4854 .ok_or_else(|| crate::codegen::internal_failure("bigint intrinsic missing"))?;
4855 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(intrinsic)));
4856 }
4857 crate::TypeofTagKind::Undefined => {
4858 emit_expr(emitter, ctx, value)?;
4859 cast::emit_box(
4860 emitter,
4861 ctx,
4862 &ctx.ta
4863 .try_expr(value)
4864 .map_err(crate::codegen::arena_failure)?
4865 .ty,
4866 )?;
4867 emit_is_undefined(emitter, ctx)?;
4868 }
4869 crate::TypeofTagKind::Number => {
4870 let boxed = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
4876 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
4877 })?;
4878 emit_expr(emitter, ctx, value)?;
4879 cast::emit_box(
4880 emitter,
4881 ctx,
4882 &ctx.ta
4883 .try_expr(value)
4884 .map_err(crate::codegen::arena_failure)?
4885 .ty,
4886 )?;
4887 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
4888 }
4889 crate::TypeofTagKind::String => {
4890 let string = ctx.symbols.string_type_idx().ok_or_else(|| {
4891 crate::codegen::internal_failure("$string intrinsic type registered")
4892 })?;
4893 emit_expr(emitter, ctx, value)?;
4894 cast::emit_box(
4895 emitter,
4896 ctx,
4897 &ctx.ta
4898 .try_expr(value)
4899 .map_err(crate::codegen::arena_failure)?
4900 .ty,
4901 )?;
4902 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
4903 }
4904 crate::TypeofTagKind::Boolean => {
4905 let boxed = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
4906 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
4907 })?;
4908 emit_expr(emitter, ctx, value)?;
4909 cast::emit_box(
4910 emitter,
4911 ctx,
4912 &ctx.ta
4913 .try_expr(value)
4914 .map_err(crate::codegen::arena_failure)?
4915 .ty,
4916 )?;
4917 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
4918 }
4919 crate::TypeofTagKind::Function => {
4920 let closure_idx = ctx.symbols.closure_type_idx().ok_or_else(|| {
4921 crate::codegen::internal_failure("$Closure intrinsic type registered")
4922 })?;
4923 emit_expr(emitter, ctx, value)?;
4924 cast::emit_box(
4925 emitter,
4926 ctx,
4927 &ctx.ta
4928 .try_expr(value)
4929 .map_err(crate::codegen::arena_failure)?
4930 .ty,
4931 )?;
4932 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
4933 }
4934 crate::TypeofTagKind::Object => {
4935 let scratch_ty = ctx.symbols.value_type(&Type::Unknown)?;
4936 let nullable = matches!(scratch_ty, ValType::Ref(rt) if rt.nullable);
4937 let scratch = emitter.add_anonymous_local(scratch_ty)?;
4938 emit_expr(emitter, ctx, value)?;
4939 cast::emit_box(
4940 emitter,
4941 ctx,
4942 &ctx.ta
4943 .try_expr(value)
4944 .map_err(crate::codegen::arena_failure)?
4945 .ty,
4946 )?;
4947 emitter.instruction(Instruction::LocalSet(scratch));
4948
4949 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
4956 crate::codegen::internal_failure("intrinsics declared by codegen entry")
4957 })?;
4958 emitter.instruction(Instruction::LocalGet(scratch));
4959 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4960 intrinsics.object,
4961 )));
4962 for primitive in [
4963 intrinsics.string,
4964 intrinsics.boxed_number,
4965 intrinsics.boxed_boolean,
4966 intrinsics.bigint,
4967 intrinsics.closure,
4968 intrinsics.undefined,
4969 ] {
4970 emitter.instruction(Instruction::LocalGet(scratch));
4971 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(primitive)));
4972 emitter.instruction(Instruction::I32Eqz);
4973 emitter.instruction(Instruction::I32And);
4974 }
4975 if nullable {
4976 emitter.instruction(Instruction::LocalGet(scratch));
4981 emitter.instruction(Instruction::RefIsNull);
4982 emitter.instruction(Instruction::I32Or);
4983 }
4984 }
4985 };
4986 Ok(())
4987}
4988
4989fn has_numeric_operands(
4992 ctx: &CodegenCtx,
4993 operands: &[ExprId],
4994 result_ty: &Type,
4995) -> Result<bool, crate::compiler_error::CompilerFailure> {
4996 if !matches!(result_ty.peel(), Type::Number | Type::NumberLiteral(_)) {
4997 return Ok(false);
4998 }
4999 for &operand in operands {
5000 let ty = &ctx
5001 .ta
5002 .try_expr(operand)
5003 .map_err(crate::codegen::arena_failure)?
5004 .ty;
5005 if !matches!(
5006 ty.primitive_behavior(),
5007 Type::Number | Type::NumberLiteral(_)
5008 ) {
5009 return Ok(false);
5010 }
5011 }
5012 Ok(true)
5013}
5014
5015fn emit_number_bitwise(
5016 emitter: &mut FunctionEmitter,
5017 ctx: &CodegenCtx,
5018 op: BinOp,
5019 lhs: ExprId,
5020 rhs: ExprId,
5021) -> Result<(), crate::compiler_error::CompilerFailure> {
5022 emit_primitive_operand(emitter, ctx, lhs)?;
5023 emit_to_int32(emitter, ctx)?;
5024 emit_primitive_operand(emitter, ctx, rhs)?;
5025 emit_to_int32(emitter, ctx)?;
5026 let (instruction, unsigned) = match op {
5028 BinOp::BitAnd => (Instruction::I32And, false),
5029 BinOp::BitOr => (Instruction::I32Or, false),
5030 BinOp::BitXor => (Instruction::I32Xor, false),
5031 BinOp::Shl => (Instruction::I32Shl, false),
5032 BinOp::Shr => (Instruction::I32ShrS, false),
5033 BinOp::UnsignedShr => (Instruction::I32ShrU, true),
5034 _ => {
5035 return Err(crate::codegen::internal_failure(
5036 "a numeric bitwise operation has a bitwise operator",
5037 ));
5038 }
5039 };
5040 emitter.instruction(instruction);
5041 emitter.instruction(if unsigned {
5042 Instruction::F64ConvertI32U
5043 } else {
5044 Instruction::F64ConvertI32S
5045 });
5046 Ok(())
5047}
5048
5049fn emit_to_int32(
5053 emitter: &mut FunctionEmitter,
5054 ctx: &CodegenCtx,
5055) -> Result<(), crate::compiler_error::CompilerFailure> {
5056 let value = emitter.add_anonymous_local(ValType::F64)?;
5057 emitter.instruction(Instruction::LocalTee(value));
5058 emitter.instruction(Instruction::F64Abs);
5059 emitter.instruction(Instruction::F64Const(Ieee64::from(
5060 9_223_372_036_854_775_808.0,
5061 )));
5062 emitter.instruction(Instruction::F64Lt);
5063 emitter.emit_if(BlockType::Result(ValType::I32));
5064 emitter.instruction(Instruction::LocalGet(value));
5065 emitter.instruction(Instruction::I64TruncSatF64S);
5066 emitter.instruction(Instruction::I32WrapI64);
5067 emitter.emit_else();
5068 emitter.instruction(Instruction::LocalGet(value));
5069 cast::emit_box(emitter, ctx, &Type::Number)?;
5070 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
5071 cast::emit_box(emitter, ctx, &Type::Number)?;
5072 let host = ctx
5073 .symbols
5074 .func_idx(&crate::mangle::prelude("__value_bitor"))
5075 .ok_or_else(|| crate::codegen::internal_failure("missing bitwise host import: bitor"))?;
5076 emitter.instruction(Instruction::Call(host));
5077 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5078 emitter.instruction(Instruction::I32TruncF64S);
5079 emitter.emit_end();
5080 Ok(())
5081}
5082
5083fn emit_bitwise_host(
5084 emitter: &mut FunctionEmitter,
5085 ctx: &CodegenCtx,
5086 name: &str,
5087 operands: &[ExprId],
5088 result_ty: &Type,
5089) -> Result<(), crate::compiler_error::CompilerFailure> {
5090 for &operand in operands {
5091 emit_expr(emitter, ctx, operand)?;
5092 let ty = &ctx
5093 .ta
5094 .try_expr(operand)
5095 .map_err(crate::codegen::arena_failure)?
5096 .ty;
5097 cast::emit_box(emitter, ctx, ty)?;
5098 }
5099 let mangled = crate::mangle::prelude(&format!("__value_{name}"));
5100 let host = ctx.symbols.func_idx(&mangled).ok_or_else(|| {
5101 crate::codegen::internal_failure(format!("missing bitwise host import: {name}"))
5102 })?;
5103 emitter.instruction(Instruction::Call(host));
5104 cast::emit_cast_to(emitter, ctx, result_ty)
5105}
5106
5107fn emit_unary(
5108 emitter: &mut FunctionEmitter,
5109 ctx: &CodegenCtx,
5110 op: UnOp,
5111 operand: ExprId,
5112 result_ty: &Type,
5113) -> Result<(), crate::compiler_error::CompilerFailure> {
5114 if matches!(op, UnOp::Neg | UnOp::Pos)
5115 && try_emit_unreachable_for_never_operand(emitter, ctx, &[operand])?
5116 {
5117 return Ok(());
5118 }
5119 let _: () = match op {
5120 UnOp::BitNot if has_numeric_operands(ctx, &[operand], result_ty)? => {
5121 emit_primitive_operand(emitter, ctx, operand)?;
5122 emit_to_int32(emitter, ctx)?;
5123 emitter.instruction(Instruction::I32Const(-1));
5124 emitter.instruction(Instruction::I32Xor);
5125 emitter.instruction(Instruction::F64ConvertI32S);
5126 }
5127 UnOp::BitNot => emit_bitwise_host(emitter, ctx, "bitnot", &[operand], result_ty)?,
5128 UnOp::Neg => {
5129 if ctx
5131 .ta
5132 .try_expr(operand)
5133 .map_err(crate::codegen::arena_failure)?
5134 .ty
5135 .is_bigint()
5136 {
5137 emit_primitive_operand(emitter, ctx, operand)?;
5138 emit_bigint_extract_to_stack(emitter, ctx);
5139 let host_idx = ctx
5140 .symbols
5141 .func_idx(&crate::mangle::host(
5142 crate::runtime::BIGINT_MODULE_NAME,
5143 "neg",
5144 ))
5145 .ok_or_else(|| {
5146 crate::codegen::internal_failure(
5147 "submilli:bigint.neg import recorded by codegen bootstrap",
5148 )
5149 })?;
5150 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
5151 return Ok(());
5152 }
5153 emit_primitive_operand(emitter, ctx, operand)?;
5154 emitter.instruction(Instruction::F64Neg);
5155 }
5156 UnOp::Pos => {
5157 emit_primitive_operand(emitter, ctx, operand)?;
5161 if ctx
5162 .ta
5163 .try_expr(operand)
5164 .map_err(crate::codegen::arena_failure)?
5165 .ty
5166 .is_string_shaped()
5167 {
5168 let host_idx = ctx
5169 .symbols
5170 .func_idx(&crate::mangle::host(
5171 crate::runtime::NUMBER_MODULE_NAME,
5172 "toNumber",
5173 ))
5174 .ok_or_else(|| {
5175 crate::codegen::internal_failure(
5176 "submilli:number.toNumber import recorded by codegen analysis",
5177 )
5178 })?;
5179 emitter.instruction(Instruction::Call(host_idx));
5180 }
5181 }
5182 UnOp::Not => {
5183 let operand_ty = ctx
5189 .ta
5190 .try_expr(operand)
5191 .map_err(crate::codegen::arena_failure)?
5192 .ty
5193 .clone();
5194 emit_expr(emitter, ctx, operand)?;
5195 crate::codegen::function_emitter::cast::emit_condition_to_i32(
5196 emitter,
5197 ctx,
5198 &operand_ty,
5199 )?;
5200 emitter.instruction(Instruction::I32Eqz);
5201 }
5202 };
5203 Ok(())
5204}
5205
5206#[allow(clippy::too_many_arguments)]
5210pub(crate) fn emit_method_call(
5211 emitter: &mut FunctionEmitter,
5212 ctx: &CodegenCtx,
5213 receiver: ExprId,
5214 iface: &crate::MangledName,
5215 method: &str,
5216 args: &[ExprId],
5217 generic_args: Option<&[crate::GenericArgument]>,
5218 return_cast: Option<&Type>,
5219 call_ret_ty: &Type,
5220) -> Result<(), crate::compiler_error::CompilerFailure> {
5221 let recv_ty = ctx
5222 .ta
5223 .source_type(receiver)
5224 .map_err(crate::codegen::arena_failure)?
5225 .clone();
5226 if !emit_dropped_static_receiver(emitter, ctx, receiver, iface)? {
5227 emit_receiver(emitter, ctx, receiver)?;
5228 }
5229 emit_method_call_with_receiver_on_stack(
5230 emitter,
5231 ctx,
5232 &recv_ty,
5233 iface,
5234 method,
5235 args,
5236 generic_args,
5237 return_cast,
5238 call_ret_ty,
5239 )?;
5240 Ok(())
5241}
5242
5243fn emit_dropped_static_receiver(
5247 emitter: &mut FunctionEmitter,
5248 ctx: &CodegenCtx,
5249 receiver: ExprId,
5250 iface: &crate::MangledName,
5251) -> Result<bool, crate::compiler_error::CompilerFailure> {
5252 let expr = ctx
5253 .ta
5254 .try_expr(receiver)
5255 .map_err(crate::codegen::arena_failure)?;
5256 if !matches!(expr.kind, TypedExprKind::GlobalRef { .. })
5257 || static_interface(ctx, &expr.ty) != Some(iface)
5258 {
5259 return Ok(false);
5260 }
5261 match ctx.symbols.value_type(&expr.ty)? {
5262 ValType::Ref(RefType { heap_type, .. }) => {
5263 emitter.instruction(Instruction::RefNull(heap_type));
5264 }
5265 other => {
5266 return Err(crate::codegen::internal_failure(format!(
5267 "static-interface binding lowered to {other:?}"
5268 )));
5269 }
5270 }
5271 Ok(true)
5272}
5273
5274#[allow(clippy::too_many_arguments)]
5281fn emit_method_call_with_receiver_on_stack(
5282 emitter: &mut FunctionEmitter,
5283 ctx: &CodegenCtx,
5284 recv_ty: &Type,
5285 iface: &crate::MangledName,
5286 method: &str,
5287 args: &[ExprId],
5288 generic_args: Option<&[crate::GenericArgument]>,
5289 return_cast: Option<&Type>,
5290 call_ret_ty: &Type,
5291) -> Result<(), crate::compiler_error::CompilerFailure> {
5292 let concrete_receiver = crate::typechecker::infer::narrowing::strip_nullish(recv_ty);
5293 crate::codegen::field_guards::attach(emitter, ctx, &concrete_receiver)?;
5294 let direct_key = crate::mangle::extend(iface, method);
5295 if let Some(func_idx) = ctx.symbols.func_idx(&direct_key) {
5296 if matches!(recv_ty.primitive_behavior(), Type::Number)
5297 && ctx.symbols.value_type(recv_ty)? != ValType::F64
5298 {
5299 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5300 }
5301 match ctx.symbols.iface_dispatch(iface) {
5310 Some(crate::Dispatch::Direct) => {
5311 if matches!(recv_ty.peel(), Type::InterfaceRef { .. }) {
5323 emitter.instruction(Instruction::RefAsNonNull);
5324 }
5325 }
5326 Some(crate::Dispatch::Static) => {
5327 emitter.instruction(Instruction::Drop);
5328 }
5329 None | Some(crate::Dispatch::VTable) => {}
5334 }
5335 let abi = wrapper_abi_for_call(
5345 ctx,
5346 &direct_key,
5347 args,
5348 generic_args,
5349 return_cast,
5350 call_ret_ty,
5351 )?;
5352 let defaults = ctx.symbols.host_call_defaults(func_idx, args.len());
5353 emit_args_into_slots_with_defaults(emitter, ctx, args, Some(&abi.params), defaults)?;
5354 emitter.instruction(Instruction::Call(func_idx));
5355 emit_slot_return_cast(emitter, ctx, call_ret_ty, Some(&abi))?;
5356 return Ok(());
5357 }
5358 if ctx.symbols.is_rewritable_class_method(iface, method) {
5361 emit_interface_method_via_shape_with_receiver_on_stack(
5362 emitter,
5363 ctx,
5364 method,
5365 args,
5366 call_ret_ty,
5367 None,
5368 )?;
5369 return Ok(());
5370 }
5371 if ctx.symbols.class_method_slot(iface, method).is_some() {
5376 emit_class_vtable_dispatch(emitter, ctx, iface, method, args, call_ret_ty)?;
5377 return Ok(());
5378 }
5379 if let Some(slot) = vtable_slot_for_method(method) {
5380 emit_vtable_dispatch_on_object_stack(emitter, ctx, slot);
5381 return Ok(());
5382 }
5383 if matches!(recv_ty.peel(), Type::InterfaceRef { .. }) {
5392 emit_interface_method_via_shape_with_receiver_on_stack(
5393 emitter,
5394 ctx,
5395 method,
5396 args,
5397 call_ret_ty,
5398 None,
5399 )?;
5400 return Ok(());
5401 }
5402 Err(crate::codegen::internal_failure(format!(
5403 "method dispatch was not registered for `{iface}.{method}`"
5404 )))
5405}
5406
5407fn emit_class_method_value(
5408 emitter: &mut FunctionEmitter,
5409 ctx: &CodegenCtx,
5410 receiver_ty: &Type,
5411 method: &str,
5412 result_ty: &Type,
5413 check_ty: &Type,
5414) -> Result<(), crate::compiler_error::CompilerFailure> {
5415 let object_shape = ctx
5416 .symbols
5417 .intrinsic_type_indices()
5418 .ok_or_else(|| {
5419 crate::codegen::internal_failure("class method value requires object shape intrinsic")
5420 })?
5421 .object_shape;
5422 let receiver = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
5423 emit_object_property_read_as(emitter, ctx, receiver, method, result_ty, check_ty)
5424}
5425
5426pub(in crate::codegen) fn emit_interface_method_via_shape_with_receiver_on_stack(
5442 emitter: &mut FunctionEmitter,
5443 ctx: &CodegenCtx,
5444 method: &str,
5445 args: &[ExprId],
5446 call_ret_ty: &Type,
5447 slot_index_local: Option<u32>,
5448) -> Result<(), crate::compiler_error::CompilerFailure> {
5449 let args: Vec<_> = args
5450 .iter()
5451 .copied()
5452 .map(ShapeArgument::Expression)
5453 .collect();
5454 emit_shape_method_with_arguments(emitter, ctx, method, &args, call_ret_ty, slot_index_local)?;
5455 Ok(())
5456}
5457
5458enum ShapeArgument {
5459 Expression(ExprId),
5460 Local { slot: u32, ty: Type },
5461}
5462
5463impl ShapeArgument {
5464 fn ty(&self, ctx: &CodegenCtx) -> Result<Type, crate::compiler_error::CompilerFailure> {
5465 Ok(match self {
5466 Self::Expression(id) => ctx
5467 .ta
5468 .try_expr(*id)
5469 .map_err(crate::codegen::arena_failure)?
5470 .ty
5471 .clone(),
5472 Self::Local { ty, .. } => ty.clone(),
5473 })
5474 }
5475
5476 fn emit(
5477 &self,
5478 emitter: &mut FunctionEmitter,
5479 ctx: &CodegenCtx,
5480 ) -> Result<(), crate::compiler_error::CompilerFailure> {
5481 let _: () = match self {
5482 Self::Expression(id) => emit_expr(emitter, ctx, *id)?,
5483 Self::Local { slot, .. } => {
5484 emitter.instruction(Instruction::LocalGet(*slot));
5485 }
5486 };
5487 Ok(())
5488 }
5489}
5490
5491fn emit_shape_method_with_arguments(
5492 emitter: &mut FunctionEmitter,
5493 ctx: &CodegenCtx,
5494 method: &str,
5495 args: &[ShapeArgument],
5496 call_ret_ty: &Type,
5497 slot_index_local: Option<u32>,
5498) -> Result<(), crate::compiler_error::CompilerFailure> {
5499 let intrinsics = ctx
5500 .symbols
5501 .intrinsic_type_indices()
5502 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5503
5504 let arg_tys: Vec<Type> = args.iter().map(|a| a.ty(ctx)).collect::<Result<_, _>>()?;
5507 let fn_ty = Type::Function {
5508 params: arg_tys,
5509 ret: Box::new(call_ret_ty.clone()),
5510 predicate: None,
5511 has_rest: false,
5515 optional: 0,
5516 };
5517 let closure_sig = crate::codegen::closures::classify(&fn_ty)?;
5518 let closure_struct_idx = ctx
5519 .symbols
5520 .closure_struct_type_idx(closure_sig)
5521 .ok_or_else(|| {
5522 crate::codegen::internal_failure(
5523 "closure struct registered for the method's signature (SUB-166)",
5524 )
5525 })?;
5526 let fn_type_idx = ctx
5527 .symbols
5528 .closure_func_type_idx(closure_sig)
5529 .ok_or_else(|| {
5530 crate::codegen::internal_failure(
5531 "closure funcref type registered for the method's signature",
5532 )
5533 })?;
5534
5535 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5536 nullable: false,
5537 heap_type: HeapType::Concrete(intrinsics.object_shape),
5538 }))?;
5539 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5540 nullable: false,
5541 heap_type: HeapType::Concrete(closure_struct_idx),
5542 }))?;
5543
5544 emitter.instruction(Instruction::RefAsNonNull);
5548 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
5549 intrinsics.object_shape,
5550 )));
5551 emitter.instruction(Instruction::LocalSet(rcv_local));
5552
5553 if let Some(index_local) = slot_index_local {
5556 emit_field_slot_get(emitter, intrinsics, rcv_local, index_local);
5557 } else {
5558 let name_global = ctx
5559 .symbols
5560 .field_name_string_global_idx(method)
5561 .ok_or_else(|| {
5562 crate::codegen::internal_failure(
5563 ("per-name string global recorded during field-name-strings emission \
5564 for interface method names")
5565 .to_string(),
5566 )
5567 })?;
5568 emit_object_field_read_by_name(emitter, ctx, rcv_local, name_global);
5569 }
5570
5571 let method_value = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5572 emitter.instruction(Instruction::LocalSet(method_value));
5573 let mut evaluated_args = Vec::with_capacity(args.len());
5574 for arg in args {
5575 arg.emit(emitter, ctx)?;
5576 cast::emit_box(emitter, ctx, &arg.ty(ctx)?)?;
5577 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5578 emitter.instruction(Instruction::LocalSet(slot));
5579 evaluated_args.push(slot);
5580 }
5581 emitter.instruction(Instruction::LocalGet(method_value));
5582 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5583 intrinsics.closure,
5584 )));
5585 emitter.instruction(Instruction::I32Eqz);
5586 emitter.emit_if(BlockType::Empty);
5587 crate::codegen::throw::emit_type_error_throw(emitter, ctx, "Value is not callable");
5588 emitter.emit_end();
5589 emitter.instruction(Instruction::LocalGet(method_value));
5590
5591 crate::codegen::closure_coercions::emit_erased_cast(
5595 emitter,
5596 ctx,
5597 crate::codegen::closures::ClosureSig::of(args.len(), call_ret_ty)?,
5598 )?;
5599 emitter.instruction(Instruction::LocalTee(closure_local));
5600
5601 emitter.instruction(Instruction::StructGet {
5603 struct_type_index: closure_struct_idx,
5604 field_index: 2,
5605 });
5606
5607 crate::codegen::this_binding::bind(emitter, ctx, rcv_local)?;
5608 for slot in evaluated_args {
5611 emitter.instruction(Instruction::LocalGet(slot));
5612 }
5613
5614 emitter.instruction(Instruction::LocalGet(closure_local));
5616 emitter.instruction(Instruction::StructGet {
5617 struct_type_index: closure_struct_idx,
5618 field_index: 1,
5619 });
5620 emitter.instruction(Instruction::CallRef(fn_type_idx));
5621
5622 let _: () = if !call_ret_ty.is_void() {
5625 crate::codegen::cast_check::emit_checked_cast_on_stack(
5626 emitter,
5627 ctx,
5628 &Type::Unknown,
5629 call_ret_ty,
5630 )?;
5631 };
5632 Ok(())
5633}
5634
5635fn vtable_slot_for_method(method: &str) -> Option<u32> {
5646 match method {
5647 "toString" => Some(0),
5648 "toJson" => Some(1),
5649 "equals" => Some(2),
5650 "hash" => Some(3),
5651 _ => None,
5652 }
5653}
5654
5655pub(super) fn emit_vtable_dispatch_on_object_stack(
5666 emitter: &mut FunctionEmitter,
5667 ctx: &CodegenCtx,
5668 slot: u32,
5669) {
5670 let Some(intrinsics) = ctx.require(
5671 ctx.symbols.intrinsic_type_indices(),
5672 "intrinsics declared by codegen entry",
5673 ) else {
5674 return;
5675 };
5676 let fn_type_idx = match slot {
5681 0 => intrinsics.to_string_fn,
5682 1 => intrinsics.to_json_fn,
5683 _ => {
5684 ctx.fail("vtable string dispatch requires toString or toJson slot");
5685 return;
5686 }
5687 };
5688 let Some(obj_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5689 nullable: false,
5690 heap_type: HeapType::Concrete(intrinsics.object),
5691 }))) else {
5692 return;
5693 };
5694 let Some(fn_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5695 nullable: false,
5696 heap_type: HeapType::Concrete(fn_type_idx),
5697 }))) else {
5698 return;
5699 };
5700 emitter.instruction(Instruction::RefAsNonNull);
5706 emitter.instruction(Instruction::LocalTee(obj_local));
5707 emitter.instruction(Instruction::StructGet {
5708 struct_type_index: intrinsics.object,
5709 field_index: 0,
5710 });
5711 emitter.instruction(Instruction::StructGet {
5712 struct_type_index: intrinsics.vtable,
5713 field_index: slot,
5714 });
5715 emitter.instruction(Instruction::LocalSet(fn_local));
5716 emitter.instruction(Instruction::LocalGet(obj_local));
5717 emitter.instruction(Instruction::LocalGet(fn_local));
5718 emitter.instruction(Instruction::CallRef(fn_type_idx));
5719}
5720
5721fn emit_args_into_slots(
5725 emitter: &mut FunctionEmitter,
5726 ctx: &CodegenCtx,
5727 args: &[ExprId],
5728 slots: Option<&[ValType]>,
5729) -> Result<(), crate::compiler_error::CompilerFailure> {
5730 emit_args_into_slots_with_defaults(emitter, ctx, args, slots, &[])
5731}
5732
5733fn emit_args_into_slots_with_defaults(
5734 emitter: &mut FunctionEmitter,
5735 ctx: &CodegenCtx,
5736 args: &[ExprId],
5737 slots: Option<&[ValType]>,
5738 defaults: &[Option<crate::DefaultValue>],
5739) -> Result<(), crate::compiler_error::CompilerFailure> {
5740 if slots.is_some_and(|slots| slots.len() != args.len()) {
5741 return Err(crate::codegen::internal_failure(
5742 "call ABI argument count mismatch",
5743 ));
5744 }
5745 let locals: Vec<_> = args
5746 .iter()
5747 .map(|&arg| {
5748 emit_expr(emitter, ctx, arg)?;
5749 let local = emitter.add_anonymous_local(
5750 ctx.symbols.value_type(
5751 &ctx.ta
5752 .try_expr(arg)
5753 .map_err(crate::codegen::arena_failure)?
5754 .ty,
5755 )?,
5756 )?;
5757 emitter.instruction(Instruction::LocalSet(local));
5758 Ok::<_, crate::compiler_error::CompilerFailure>(local)
5759 })
5760 .collect::<Result<_, _>>()?;
5761 for (i, (&arg, local)) in args.iter().zip(locals).enumerate() {
5762 emitter.instruction(Instruction::LocalGet(local));
5763 if let Some(slot) = slots.and_then(|s| s.get(i).copied()) {
5764 crate::codegen::argument_defaults::emit_argument(
5765 emitter,
5766 ctx,
5767 &ctx.ta
5768 .try_expr(arg)
5769 .map_err(crate::codegen::arena_failure)?
5770 .ty,
5771 slot,
5772 defaults.get(i).and_then(Option::as_ref),
5773 )?;
5774 }
5775 }
5776 Ok(())
5777}
5778
5779fn wrapper_abi_for_call(
5794 ctx: &CodegenCtx,
5795 direct_key: &crate::MangledName,
5796 args: &[ExprId],
5797 generic_args: Option<&[crate::GenericArgument]>,
5798 return_cast: Option<&Type>,
5799 call_ret_ty: &Type,
5800) -> Result<MethodSlotAbi, crate::compiler_error::CompilerFailure> {
5801 if let Some(recorded) = ctx.symbols.iface_method_abi(direct_key) {
5802 return Ok(recorded.clone());
5803 }
5804 let erased = ctx.symbols.value_type(&Type::Unknown)?;
5805 let slot_is_erased =
5806 |i: usize| generic_args.is_some_and(|flags| flags.get(i).is_some_and(|g| g.is_generic));
5807 Ok(MethodSlotAbi {
5808 params: args
5809 .iter()
5810 .enumerate()
5811 .map(|(i, &a)| {
5812 Ok::<_, crate::compiler_error::CompilerFailure>(if slot_is_erased(i) {
5813 erased
5814 } else {
5815 ctx.symbols.value_type(
5816 &ctx.ta
5817 .try_expr(a)
5818 .map_err(crate::codegen::arena_failure)?
5819 .ty,
5820 )?
5821 })
5822 })
5823 .collect::<Result<_, _>>()?,
5824 ret: match return_cast {
5825 Some(_) => Some(erased),
5826 None => (!call_ret_ty.is_void())
5827 .then(|| ctx.symbols.value_type(call_ret_ty))
5828 .transpose()?,
5829 },
5830 })
5831}
5832
5833fn emit_slot_return_cast(
5836 emitter: &mut FunctionEmitter,
5837 ctx: &CodegenCtx,
5838 call_ret_ty: &Type,
5839 abi: Option<&MethodSlotAbi>,
5840) -> Result<(), crate::compiler_error::CompilerFailure> {
5841 if matches!(call_ret_ty.peel(), Type::Never) {
5842 emitter.instruction(Instruction::Unreachable);
5843 return Ok(());
5844 }
5845 if abi.is_some_and(|abi| abi.ret.is_none()) {
5848 emit_undefined(emitter, ctx)?;
5849 }
5850 if call_ret_ty.is_void() {
5851 return Ok(());
5852 }
5853 if matches!(call_ret_ty, Type::Unknown) {
5854 match abi.and_then(|abi| abi.ret) {
5855 Some(ValType::F64) => cast::emit_box(emitter, ctx, &Type::Number)?,
5856 Some(ValType::I32) => cast::emit_box(emitter, ctx, &Type::Boolean)?,
5857 _ => {}
5858 }
5859 return Ok(());
5860 }
5861 if abi.and_then(|a| a.ret) != Some(ctx.symbols.value_type(call_ret_ty)?) {
5862 crate::codegen::cast_check::emit_checked_cast_on_stack(
5863 emitter,
5864 ctx,
5865 &Type::Unknown,
5866 call_ret_ty,
5867 )?;
5868 }
5869 crate::codegen::field_guards::attach(emitter, ctx, call_ret_ty)?;
5870 Ok(())
5871}
5872
5873fn emit_class_vtable_dispatch(
5886 emitter: &mut FunctionEmitter,
5887 ctx: &CodegenCtx,
5888 class: &crate::MangledName,
5889 method: &str,
5890 args: &[ExprId],
5891 call_ret_ty: &Type,
5892) -> Result<(), crate::compiler_error::CompilerFailure> {
5893 let struct_idx = ctx
5894 .symbols
5895 .class_struct_type_idx(class)
5896 .ok_or_else(|| crate::codegen::internal_failure("class struct type recorded"))?;
5897 let vtable_idx = ctx
5898 .symbols
5899 .class_vtable_type_idx(class)
5900 .ok_or_else(|| crate::codegen::internal_failure("class vtable type recorded"))?;
5901 let slot = ctx
5902 .symbols
5903 .class_method_slot(class, method)
5904 .ok_or_else(|| crate::codegen::internal_failure("class method slot recorded"))?;
5905 let sig_idx = ctx
5906 .symbols
5907 .class_method_sig(class, method)
5908 .ok_or_else(|| crate::codegen::internal_failure("class method sig recorded"))?;
5909
5910 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5911 nullable: false,
5912 heap_type: HeapType::Concrete(struct_idx),
5913 }))?;
5914 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
5917 emitter.instruction(Instruction::LocalSet(rcv_local));
5918
5919 emitter.instruction(Instruction::LocalGet(rcv_local));
5921 emitter.instruction(Instruction::StructGet {
5922 struct_type_index: struct_idx,
5923 field_index: 0,
5924 });
5925 emitter.instruction(Instruction::StructGet {
5926 struct_type_index: vtable_idx,
5927 field_index: slot,
5928 });
5929 let fn_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5930 nullable: false,
5931 heap_type: HeapType::Concrete(sig_idx),
5932 }))?;
5933 emitter.instruction(Instruction::LocalSet(fn_local));
5934
5935 let abi = ctx.symbols.class_method_abi(class, method).cloned();
5937 emitter.instruction(Instruction::LocalGet(rcv_local));
5938 emit_args_into_slots(
5939 emitter,
5940 ctx,
5941 args,
5942 abi.as_ref().map(|a| a.params.as_slice()),
5943 )?;
5944 emitter.instruction(Instruction::LocalGet(fn_local));
5945 emitter.instruction(Instruction::CallRef(sig_idx));
5946 emit_slot_return_cast(emitter, ctx, call_ret_ty, abi.as_ref())?;
5947 Ok(())
5948}
5949
5950fn emit_intrinsic_call(
5951 emitter: &mut FunctionEmitter,
5952 ctx: &CodegenCtx,
5953 kind: Intrinsic,
5954 args: &[ExprId],
5955 result_ty: &Type,
5956) -> Result<(), crate::compiler_error::CompilerFailure> {
5957 let _: () = match kind {
5958 Intrinsic::Assert => {
5959 if args.len() != 2 {
5963 return Err(crate::codegen::internal_failure(
5964 "assert requires two arguments",
5965 ));
5966 }
5967 emit_expr(emitter, ctx, args[0])?;
5968 cast::emit_condition_to_i32(
5969 emitter,
5970 ctx,
5971 &ctx.ta
5972 .try_expr(args[0])
5973 .map_err(crate::codegen::arena_failure)?
5974 .ty,
5975 )?;
5976 emit_expr(emitter, ctx, args[1])?;
5977 let message_type = &ctx
5978 .ta
5979 .try_expr(args[1])
5980 .map_err(crate::codegen::arena_failure)?
5981 .ty;
5982 let msg_local = emitter.add_anonymous_local(ctx.symbols.value_type(message_type)?)?;
5983 emitter.instruction(Instruction::LocalSet(msg_local));
5984 emitter.instruction(Instruction::I32Eqz);
5985 emitter.emit_if(BlockType::Empty);
5986 emitter.instruction(Instruction::LocalGet(msg_local));
5987 cast::emit_coerce_to_slot(emitter, ctx, message_type, &Type::String)?;
5988 let ctor_idx = ctx
5989 .symbols
5990 .func_idx(&crate::mangle::prelude("Error#constructor"))
5991 .ok_or_else(|| {
5992 crate::codegen::internal_failure("Error#constructor exported from prelude")
5993 })?;
5994 emitter.instruction(Instruction::Call(ctor_idx));
5995 crate::codegen::throw::emit_error_throw(emitter, ctx);
5996 emitter.emit_end();
5997 emit_undefined(emitter, ctx)?;
5998 }
5999 Intrinsic::JsonStringify => {
6000 super::json::emit_stringify(emitter, ctx, args, result_ty)?;
6001 }
6002 Intrinsic::JsonParse => {
6003 super::json::emit_parse(emitter, ctx, args)?;
6004 }
6005 Intrinsic::ToString => {
6006 let [value] = args else {
6007 return Err(crate::codegen::internal_failure(
6008 "string conversion requires one argument",
6009 ));
6010 };
6011 emit_expr(emitter, ctx, *value)?;
6012 cast::emit_box(
6013 emitter,
6014 ctx,
6015 &ctx.ta
6016 .try_expr(*value)
6017 .map_err(crate::codegen::arena_failure)?
6018 .ty,
6019 )?;
6020 let function = ctx
6021 .symbols
6022 .prelude_func_idx("__value_to_string")
6023 .ok_or_else(|| {
6024 crate::codegen::internal_failure("dynamic string conversion collected")
6025 })?;
6026 emitter.instruction(Instruction::Call(function));
6027 }
6028 Intrinsic::BigIntFromString => {
6035 if args.len() != 1 {
6036 return Err(crate::codegen::internal_failure(
6037 "BigInt.fromString requires one argument",
6038 ));
6039 }
6040 emit_expr(emitter, ctx, args[0])?;
6041 let string_type_idx = ctx
6044 .symbols
6045 .string_type_idx()
6046 .ok_or_else(|| crate::codegen::internal_failure("$string registered"))?;
6047 emitter.instruction(Instruction::StructGet {
6048 struct_type_index: string_type_idx,
6049 field_index: 1,
6050 });
6051 let host_idx = ctx
6052 .symbols
6053 .func_idx(&crate::mangle::host(
6054 crate::runtime::BIGINT_MODULE_NAME,
6055 "fromString",
6056 ))
6057 .ok_or_else(|| {
6058 crate::codegen::internal_failure(
6059 "submilli:bigint.fromString import recorded by codegen bootstrap",
6060 )
6061 })?;
6062 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6063 }
6064 };
6065 Ok(())
6066}
6067
6068fn emit_vtable_equality(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: BinOp) {
6073 ctx.latch(emit_vtable_equality_checked(emitter, ctx, op));
6074}
6075
6076fn emit_vtable_equality_checked(
6077 emitter: &mut FunctionEmitter,
6078 ctx: &CodegenCtx,
6079 op: BinOp,
6080) -> Result<(), crate::compiler_error::CompilerFailure> {
6081 let intrinsics = ctx
6082 .symbols
6083 .intrinsic_type_indices()
6084 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6085 let operand_ref = ValType::Ref(RefType {
6088 nullable: true,
6089 heap_type: HeapType::Concrete(intrinsics.object),
6090 });
6091 let object_ref = ValType::Ref(RefType {
6092 nullable: false,
6093 heap_type: HeapType::Concrete(intrinsics.object),
6094 });
6095 let equals_fn_ref = ValType::Ref(RefType {
6096 nullable: false,
6097 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6098 });
6099
6100 let Some(lhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6102 return Ok(());
6103 };
6104 let Some(rhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6105 return Ok(());
6106 };
6107 let Some(lhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6108 return Ok(());
6109 };
6110 let Some(rhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6111 return Ok(());
6112 };
6113 let Some(eq_fn_local) = ctx.latch(emitter.add_anonymous_local(equals_fn_ref)) else {
6114 return Ok(());
6115 };
6116
6117 emitter.instruction(Instruction::LocalSet(rhs_local));
6118 emitter.instruction(Instruction::LocalSet(lhs_local));
6119
6120 emitter.instruction(Instruction::LocalGet(lhs_local));
6121 emitter.instruction(Instruction::RefAsNonNull);
6122 emitter.instruction(Instruction::LocalSet(lhs_object));
6123 emitter.instruction(Instruction::LocalGet(rhs_local));
6124 emitter.instruction(Instruction::RefAsNonNull);
6125 emitter.instruction(Instruction::LocalSet(rhs_object));
6126
6127 emitter.instruction(Instruction::LocalGet(lhs_object));
6128 emitter.instruction(Instruction::StructGet {
6129 struct_type_index: intrinsics.object,
6130 field_index: 0,
6131 });
6132 emitter.instruction(Instruction::StructGet {
6133 struct_type_index: intrinsics.vtable,
6134 field_index: 2,
6135 });
6136 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6137
6138 emitter.instruction(Instruction::LocalGet(lhs_object));
6139 emitter.instruction(Instruction::LocalGet(rhs_object));
6140 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6141 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6142
6143 if matches!(op, BinOp::NotEq) {
6144 emitter.instruction(Instruction::I32Eqz);
6145 }
6146
6147 Ok(())
6148}
6149
6150fn emit_boxed_eq(
6163 emitter: &mut FunctionEmitter,
6164 ctx: &CodegenCtx,
6165 lhs: ExprId,
6166 rhs: ExprId,
6167 op: BinOp,
6168) -> Result<(), crate::compiler_error::CompilerFailure> {
6169 let intrinsics = ctx
6170 .symbols
6171 .intrinsic_type_indices()
6172 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6173 let object_null_ref = ValType::Ref(RefType {
6174 nullable: true,
6175 heap_type: HeapType::Concrete(intrinsics.object),
6176 });
6177 let object_ref = ValType::Ref(RefType {
6178 nullable: false,
6179 heap_type: HeapType::Concrete(intrinsics.object),
6180 });
6181 let equals_fn_ref = ValType::Ref(RefType {
6182 nullable: false,
6183 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6184 });
6185
6186 let lhs_ty = ctx
6187 .ta
6188 .try_expr(lhs)
6189 .map_err(crate::codegen::arena_failure)?
6190 .ty
6191 .clone();
6192 let rhs_ty = ctx
6193 .ta
6194 .try_expr(rhs)
6195 .map_err(crate::codegen::arena_failure)?
6196 .ty
6197 .clone();
6198
6199 let lhs_local = emitter.add_anonymous_local(object_null_ref)?;
6204 let rhs_local = emitter.add_anonymous_local(object_null_ref)?;
6205 let lhs_nonnull_local = emitter.add_anonymous_local(object_ref)?;
6206 let eq_fn_local = emitter.add_anonymous_local(equals_fn_ref)?;
6207
6208 emit_expr(emitter, ctx, lhs)?;
6209 cast::emit_box(emitter, ctx, &lhs_ty)?;
6210 emitter.instruction(Instruction::LocalSet(lhs_local));
6211
6212 emit_expr(emitter, ctx, rhs)?;
6213 cast::emit_box(emitter, ctx, &rhs_ty)?;
6214 emitter.instruction(Instruction::LocalSet(rhs_local));
6215
6216 emitter.instruction(Instruction::LocalGet(lhs_local));
6220 emitter.instruction(Instruction::RefIsNull);
6221 emitter.emit_if(BlockType::Result(ValType::I32));
6222 emitter.instruction(Instruction::LocalGet(rhs_local));
6223 emitter.instruction(Instruction::RefIsNull);
6224 emitter.emit_else();
6225 emitter.instruction(Instruction::LocalGet(rhs_local));
6226 emitter.instruction(Instruction::RefIsNull);
6227 emitter.emit_if(BlockType::Result(ValType::I32));
6228 emitter.instruction(Instruction::I32Const(0));
6229 emitter.emit_else();
6230 emitter.instruction(Instruction::LocalGet(lhs_local));
6232 emitter.instruction(Instruction::RefAsNonNull);
6233 emitter.instruction(Instruction::LocalTee(lhs_nonnull_local));
6234 emitter.instruction(Instruction::StructGet {
6235 struct_type_index: intrinsics.object,
6236 field_index: 0,
6237 });
6238 emitter.instruction(Instruction::StructGet {
6239 struct_type_index: intrinsics.vtable,
6240 field_index: 2,
6241 });
6242 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6243 emitter.instruction(Instruction::LocalGet(lhs_nonnull_local));
6244 emitter.instruction(Instruction::LocalGet(rhs_local));
6245 emitter.instruction(Instruction::RefAsNonNull);
6246 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6247 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6248 emitter.emit_end();
6249 emitter.emit_end();
6250
6251 let _: () = if matches!(op, BinOp::NotEq) {
6252 emitter.instruction(Instruction::I32Eqz);
6253 };
6254 Ok(())
6255}
6256
6257fn emit_bigint_literal(
6263 emitter: &mut FunctionEmitter,
6264 ctx: &CodegenCtx,
6265 digits: &str,
6266) -> Result<(), crate::compiler_error::CompilerFailure> {
6267 use crate::codegen::internal_failure;
6268 if let Some(entry) = ctx.bigints.lookup(digits)? {
6269 let raw_bigint_idx = ctx
6274 .symbols
6275 .raw_bigint_type_idx()
6276 .ok_or_else(|| internal_failure("the $rawBigInt intrinsic is not registered"))?;
6277 let bigint_idx = ctx
6278 .symbols
6279 .bigint_type_idx()
6280 .ok_or_else(|| internal_failure("the $bigint intrinsic is not registered"))?;
6281 let vtable_global = ctx
6282 .symbols
6283 .prelude_global_idx("bigint_vtable")
6284 .ok_or_else(|| internal_failure("bigint_vtable is not imported from the prelude"))?;
6285 let data_idx = crate::codegen::wasm_u32(ctx.strings.strings.len())?
6286 .checked_add(entry.data_idx)
6287 .ok_or_else(|| internal_failure("the module has too many data segments"))?;
6288 emitter.instruction(Instruction::GlobalGet(vtable_global));
6289 emitter.instruction(Instruction::I32Const(i32::from(entry.sign)));
6290 emitter.instruction(Instruction::I32Const(0));
6291 emitter.instruction(Instruction::I32Const(entry.limb_count.cast_signed()));
6292 emitter.instruction(Instruction::ArrayNewData {
6293 array_type_index: raw_bigint_idx,
6294 array_data_index: data_idx,
6295 });
6296 emitter.instruction(Instruction::StructNew(bigint_idx));
6297 return Ok(());
6298 }
6299 let v: i64 = digits
6303 .parse()
6304 .map_err(|_| internal_failure("an unpooled bigint literal is not a safe integer"))?;
6305 let host_idx = ctx
6306 .symbols
6307 .func_idx(&crate::mangle::host(
6308 crate::runtime::BIGINT_MODULE_NAME,
6309 "fromNumber",
6310 ))
6311 .ok_or_else(|| internal_failure("submilli:bigint.fromNumber is not imported"))?;
6312 emitter.instruction(Instruction::F64Const(Ieee64::from(v as f64)));
6313 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6314 Ok(())
6315}
6316
6317fn emit_regex_literal(
6329 emitter: &mut FunctionEmitter,
6330 ctx: &CodegenCtx,
6331 source: &str,
6332 flags: &str,
6333) -> Result<(), crate::compiler_error::CompilerFailure> {
6334 super::emit_const_string_by_text(emitter, ctx, source)?;
6335 super::emit_const_string_by_text(emitter, ctx, flags)?;
6336 let ctor_idx = ctx
6337 .symbols
6338 .func_idx(&crate::mangle::prelude("RegExpConstructor#new"))
6339 .ok_or_else(|| crate::codegen::internal_failure("RegExpConstructor#new is not imported"))?;
6340 emitter.instruction(Instruction::Call(ctor_idx));
6341 Ok(())
6342}
6343
6344fn emit_bigint_extract_to_stack(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
6350 ctx.latch(emit_bigint_extract_to_stack_checked(emitter, ctx));
6351}
6352
6353fn emit_bigint_extract_to_stack_checked(
6354 emitter: &mut FunctionEmitter,
6355 ctx: &CodegenCtx,
6356) -> Result<(), crate::compiler_error::CompilerFailure> {
6357 let bigint_idx = ctx
6358 .symbols
6359 .bigint_type_idx()
6360 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6361 let Some(scratch) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6364 wasm_encoder::RefType {
6365 nullable: false,
6366 heap_type: wasm_encoder::HeapType::Concrete(bigint_idx),
6367 },
6368 ))) else {
6369 return Ok(());
6370 };
6371 emitter.instruction(Instruction::LocalSet(scratch));
6372 emitter.instruction(Instruction::LocalGet(scratch));
6373 emitter.instruction(Instruction::StructGet {
6374 struct_type_index: bigint_idx,
6375 field_index: 1, });
6377 emitter.instruction(Instruction::LocalGet(scratch));
6378 emitter.instruction(Instruction::StructGet {
6379 struct_type_index: bigint_idx,
6380 field_index: 2, });
6382
6383 Ok(())
6384}
6385
6386fn emit_bigint_wrap_host_result(
6391 emitter: &mut FunctionEmitter,
6392 ctx: &CodegenCtx,
6393 host_func_idx: u32,
6394) {
6395 ctx.latch(emit_bigint_wrap_host_result_checked(
6396 emitter,
6397 ctx,
6398 host_func_idx,
6399 ));
6400}
6401
6402fn emit_bigint_wrap_host_result_checked(
6403 emitter: &mut FunctionEmitter,
6404 ctx: &CodegenCtx,
6405 host_func_idx: u32,
6406) -> Result<(), crate::compiler_error::CompilerFailure> {
6407 emitter.instruction(Instruction::Call(host_func_idx));
6408 let bigint_idx = ctx
6409 .symbols
6410 .bigint_type_idx()
6411 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6412 let raw_bigint_idx = ctx
6413 .symbols
6414 .raw_bigint_type_idx()
6415 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6416 let vtable_global = ctx
6417 .symbols
6418 .prelude_global_idx("bigint_vtable")
6419 .ok_or_else(|| {
6420 crate::codegen::internal_failure("bigint_vtable imported from prelude bootstrap")
6421 })?;
6422 let Some(scratch_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6425 wasm_encoder::RefType {
6426 nullable: false,
6427 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6428 },
6429 ))) else {
6430 return Ok(());
6431 };
6432 let Some(scratch_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32))
6433 else {
6434 return Ok(());
6435 };
6436 emitter.instruction(Instruction::LocalSet(scratch_limbs));
6437 emitter.instruction(Instruction::LocalSet(scratch_sign));
6438 emitter.instruction(Instruction::GlobalGet(vtable_global));
6439 emitter.instruction(Instruction::LocalGet(scratch_sign));
6440 emitter.instruction(Instruction::LocalGet(scratch_limbs));
6441 emitter.instruction(Instruction::StructNew(bigint_idx));
6442
6443 Ok(())
6444}
6445
6446fn emit_bigint_binop_inline(
6450 emitter: &mut FunctionEmitter,
6451 ctx: &CodegenCtx,
6452 lhs: ExprId,
6453 rhs: ExprId,
6454 op_name: &str,
6455) -> Result<(), crate::compiler_error::CompilerFailure> {
6456 emit_expr(emitter, ctx, lhs)?;
6457 emit_bigint_extract_to_stack(emitter, ctx);
6458 let raw_bigint_idx = ctx
6459 .symbols
6460 .raw_bigint_type_idx()
6461 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6462 let lhs_limbs =
6463 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6464 nullable: false,
6465 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6466 }))?;
6467 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6468 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6469 emitter.instruction(Instruction::LocalSet(lhs_sign));
6470 emitter.instruction(Instruction::LocalGet(lhs_sign));
6471 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6472 emit_expr(emitter, ctx, rhs)?;
6473 emit_bigint_extract_to_stack(emitter, ctx);
6474 let host_idx = ctx
6475 .symbols
6476 .func_idx(&crate::mangle::host(
6477 crate::runtime::BIGINT_MODULE_NAME,
6478 op_name,
6479 ))
6480 .ok_or_else(|| {
6481 crate::codegen::internal_failure(format!(
6482 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6483 ))
6484 })?;
6485 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6486 Ok(())
6487}
6488
6489fn emit_bigint_pm_one(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: crate::PostfixOp) {
6496 ctx.latch(emit_bigint_pm_one_checked(emitter, ctx, op));
6497}
6498
6499fn emit_bigint_pm_one_checked(
6500 emitter: &mut FunctionEmitter,
6501 ctx: &CodegenCtx,
6502 op: crate::PostfixOp,
6503) -> Result<(), crate::compiler_error::CompilerFailure> {
6504 emit_bigint_extract_to_stack(emitter, ctx);
6505 let raw_bigint_idx = ctx
6506 .symbols
6507 .raw_bigint_type_idx()
6508 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6509 let Some(lhs_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6510 wasm_encoder::RefType {
6511 nullable: false,
6512 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6513 },
6514 ))) else {
6515 return Ok(());
6516 };
6517 let Some(lhs_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32)) else {
6518 return Ok(());
6519 };
6520 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6521 emitter.instruction(Instruction::LocalSet(lhs_sign));
6522 emitter.instruction(Instruction::LocalGet(lhs_sign));
6523 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6524 if ctx.latch(emit_bigint_literal(emitter, ctx, "1")).is_none() {
6525 return Ok(());
6526 }
6527 emit_bigint_extract_to_stack(emitter, ctx);
6528 let op_name = match op {
6529 crate::PostfixOp::Inc => "add",
6530 crate::PostfixOp::Dec => "sub",
6531 crate::PostfixOp::NonNullAssert => {
6532 return Err(crate::codegen::internal_failure(
6533 "non-null assertion is not PostfixUnary",
6534 ));
6535 }
6536 };
6537 let host_idx = ctx
6538 .symbols
6539 .func_idx(&crate::mangle::host(
6540 crate::runtime::BIGINT_MODULE_NAME,
6541 op_name,
6542 ))
6543 .ok_or_else(|| {
6544 crate::codegen::internal_failure(format!(
6545 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6546 ))
6547 })?;
6548 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6549
6550 Ok(())
6551}
6552
6553fn emit_bigint_cmp_inline(
6556 emitter: &mut FunctionEmitter,
6557 ctx: &CodegenCtx,
6558 lhs: ExprId,
6559 rhs: ExprId,
6560 op: BinOp,
6561) -> Result<(), crate::compiler_error::CompilerFailure> {
6562 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6563 emitter.instruction(Instruction::I32Const(0));
6564 let inst = match op {
6565 BinOp::Lt => Instruction::I32LtS,
6566 BinOp::Gt => Instruction::I32GtS,
6567 BinOp::Le => Instruction::I32LeS,
6568 BinOp::Ge => Instruction::I32GeS,
6569 _ => {
6570 return Err(crate::codegen::internal_failure(
6571 "emit_bigint_cmp_inline called with non-cmp op",
6572 ));
6573 }
6574 };
6575 emitter.instruction(inst);
6576 Ok(())
6577}
6578
6579fn emit_bigint_cmp_eq_inline(
6581 emitter: &mut FunctionEmitter,
6582 ctx: &CodegenCtx,
6583 lhs: ExprId,
6584 rhs: ExprId,
6585 op: BinOp,
6586) -> Result<(), crate::compiler_error::CompilerFailure> {
6587 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6588 emitter.instruction(Instruction::I32Eqz);
6589 let _: () = if matches!(op, BinOp::NotEq) {
6590 emitter.instruction(Instruction::I32Eqz);
6591 };
6592 Ok(())
6593}
6594
6595fn emit_bigint_cmp_call(
6598 emitter: &mut FunctionEmitter,
6599 ctx: &CodegenCtx,
6600 lhs: ExprId,
6601 rhs: ExprId,
6602) -> Result<(), crate::compiler_error::CompilerFailure> {
6603 emit_expr(emitter, ctx, lhs)?;
6604 emit_bigint_extract_to_stack(emitter, ctx);
6605 let raw_bigint_idx = ctx
6606 .symbols
6607 .raw_bigint_type_idx()
6608 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6609 let lhs_limbs =
6610 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6611 nullable: false,
6612 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6613 }))?;
6614 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6615 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6616 emitter.instruction(Instruction::LocalSet(lhs_sign));
6617 emitter.instruction(Instruction::LocalGet(lhs_sign));
6618 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6619 emit_expr(emitter, ctx, rhs)?;
6620 emit_bigint_extract_to_stack(emitter, ctx);
6621 let cmp_idx = ctx
6622 .symbols
6623 .func_idx(&crate::mangle::host(
6624 crate::runtime::BIGINT_MODULE_NAME,
6625 "cmp",
6626 ))
6627 .ok_or_else(|| {
6628 crate::codegen::internal_failure(
6629 "submilli:bigint.cmp import recorded by codegen bootstrap",
6630 )
6631 })?;
6632 emitter.instruction(Instruction::Call(cmp_idx));
6633 Ok(())
6634}
6635
6636fn evaluate_object_members(
6638 emitter: &mut FunctionEmitter<'_>,
6639 ctx: &CodegenCtx<'_>,
6640 members: &[TypedObjectMember],
6641) -> Result<HashMap<ExprId, u32>, crate::compiler_error::CompilerFailure> {
6642 let intrinsics = ctx
6643 .symbols
6644 .intrinsic_type_indices()
6645 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
6646 let mut values = HashMap::new();
6647 for member in members {
6648 let TypedObjectMember::Value(value_id) = *member else {
6649 return Err(crate::codegen::internal_failure(
6650 "spread field reached plain object lowering",
6651 ));
6652 };
6653 let value_ty = &ctx
6654 .ta
6655 .try_expr(value_id)
6656 .map_err(crate::codegen::arena_failure)?
6657 .ty;
6658 emit_expr(emitter, ctx, value_id)?;
6659 cast::emit_box(emitter, ctx, value_ty)?;
6660 let local = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
6661 emitter.instruction(Instruction::LocalSet(local));
6662 values.insert(value_id, local);
6663 }
6664 Ok(values)
6665}
6666
6667fn emit_field_holds_value_of(
6670 emitter: &mut FunctionEmitter<'_>,
6671 ctx: &CodegenCtx<'_>,
6672 object: u32,
6673 index: u32,
6674 value: u32,
6675 field_ty: &Type,
6676) -> Result<(), crate::compiler_error::CompilerFailure> {
6677 let intrinsics = ctx
6678 .symbols
6679 .intrinsic_type_indices()
6680 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6681 emitter.instruction(Instruction::LocalGet(object));
6682 emitter.instruction(Instruction::StructGet {
6683 struct_type_index: intrinsics.object_shape,
6684 field_index: 1,
6685 });
6686 emitter.instruction(Instruction::LocalGet(index));
6687 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
6688 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
6689 emitter.instruction(Instruction::LocalGet(value));
6690 emit_is_nullish(emitter, ctx)?;
6691 emitter.instruction(Instruction::I32Eqz);
6692 emitter.instruction(Instruction::I32Or);
6693 let _: () = if field_runtime_type_is_testable(field_ty) {
6694 emit_structural_test(emitter, ctx, value, field_ty, field_ty)?;
6695 emitter.instruction(Instruction::I32And);
6696 };
6697 Ok(())
6698}
6699
6700fn object_ref(object_type: u32) -> ValType {
6701 ValType::Ref(RefType {
6702 nullable: true,
6703 heap_type: HeapType::Concrete(object_type),
6704 })
6705}
6706
6707pub(super) fn emit_index_number(
6709 emitter: &mut FunctionEmitter,
6710 ctx: &CodegenCtx,
6711 ty: &Type,
6712) -> Result<(), crate::compiler_error::CompilerFailure> {
6713 if ty == &Type::Unknown {
6714 let function = ctx
6715 .symbols
6716 .prelude_func_idx("__value_to_index")
6717 .ok_or_else(|| crate::codegen::internal_failure("dynamic index helper collected"))?;
6718 emitter.instruction(Instruction::Call(function));
6719 } else {
6720 cast::emit_coerce_to_slot(emitter, ctx, ty, &Type::Number)?;
6721 };
6722 Ok(())
6723}
6724
6725fn emit_computed_object(
6726 emitter: &mut FunctionEmitter,
6727 ctx: &CodegenCtx,
6728 members: &[TypedObjectMember],
6729) -> Result<(), crate::compiler_error::CompilerFailure> {
6730 let Some(intr) = ctx.require(ctx.symbols.intrinsic_type_indices(), "intrinsics declared")
6731 else {
6732 return Ok(());
6733 };
6734 let Some(merge) = ctx.require(
6735 ctx.symbols.prelude_func_idx("ObjectConstructor##spread"),
6736 "spread helper imported",
6737 ) else {
6738 return Ok(());
6739 };
6740 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
6741 nullable: false,
6742 heap_type: HeapType::Concrete(intr.object_shape),
6743 }))?;
6744 for _ in 0..4 {
6745 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
6746 }
6747 emitter.instruction(Instruction::Call(merge));
6748 emitter.instruction(Instruction::LocalSet(object));
6749 for member in members {
6750 emitter.instruction(Instruction::LocalGet(object));
6751 match member {
6752 TypedObjectMember::Computed { key, value } => {
6753 emit_expr(emitter, ctx, *key)?;
6754 crate::codegen::cast_check::emit_operation_cast_on_stack(
6755 emitter,
6756 ctx,
6757 &ctx.ta
6758 .try_expr(*key)
6759 .map_err(crate::codegen::arena_failure)?
6760 .ty,
6761 &Type::String,
6762 )?;
6763 emit_expr(emitter, ctx, *value)?;
6764 cast::emit_box(
6765 emitter,
6766 ctx,
6767 &ctx.ta
6768 .try_expr(*value)
6769 .map_err(crate::codegen::arena_failure)?
6770 .ty,
6771 )?;
6772 let Some(symbol) = ctx.require(
6773 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
6774 "dynamic write imported",
6775 ) else {
6776 return Ok(());
6777 };
6778 emitter.instruction(Instruction::Call(symbol));
6779 }
6780 TypedObjectMember::Spread { source, .. } => {
6781 emit_expr(emitter, ctx, *source)?;
6782 for _ in 0..2 {
6783 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
6784 }
6785 emitter.instruction(Instruction::Call(merge));
6786 emitter.instruction(Instruction::LocalSet(object));
6787 }
6788 TypedObjectMember::Value(_) => {
6789 ctx.fail("computed literal contains an unlowered named field");
6790 return Ok(());
6791 }
6792 }
6793 }
6794 emitter.instruction(Instruction::LocalGet(object));
6795 Ok(())
6796}
6797
6798fn emit_object_index_postfix(
6799 emitter: &mut FunctionEmitter,
6800 ctx: &CodegenCtx,
6801 receiver: ExprId,
6802 index: ExprId,
6803 op: crate::PostfixOp,
6804 read_ty: &Type,
6805) -> Result<(), crate::compiler_error::CompilerFailure> {
6806 emit_expr(emitter, ctx, receiver)?;
6807 cast::emit_box(
6808 emitter,
6809 ctx,
6810 &ctx.ta
6811 .try_expr(receiver)
6812 .map_err(crate::codegen::arena_failure)?
6813 .ty,
6814 )?;
6815 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6816 emitter.instruction(Instruction::LocalSet(object));
6817 emit_expr(emitter, ctx, index)?;
6818 crate::codegen::cast_check::emit_operation_cast_on_stack(
6819 emitter,
6820 ctx,
6821 &ctx.ta
6822 .try_expr(index)
6823 .map_err(crate::codegen::arena_failure)?
6824 .ty,
6825 &Type::String,
6826 )?;
6827 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
6828 emitter.instruction(Instruction::LocalSet(key));
6829 emitter.instruction(Instruction::LocalGet(object));
6830 emitter.instruction(Instruction::LocalGet(key));
6831 let Some(symbol) = ctx.require(
6832 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
6833 "record read imported",
6834 ) else {
6835 return Ok(());
6836 };
6837 emitter.instruction(Instruction::Call(symbol));
6838 crate::codegen::cast_check::emit_operation_cast_on_stack(
6839 emitter,
6840 ctx,
6841 &Type::Unknown,
6842 read_ty,
6843 )?;
6844 cast::emit_box(emitter, ctx, read_ty)?;
6845 emit_postfix_numeric(emitter, ctx, &Type::Unknown);
6846 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6847 emitter.instruction(Instruction::LocalTee(old));
6848 emit_postfix_delta(emitter, ctx, op, &Type::Unknown);
6849 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6850 emitter.instruction(Instruction::LocalSet(updated));
6851 emitter.instruction(Instruction::LocalGet(object));
6852 emitter.instruction(Instruction::LocalGet(key));
6853 emitter.instruction(Instruction::LocalGet(updated));
6854 let Some(symbol) = ctx.require(
6855 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
6856 "record write imported",
6857 ) else {
6858 return Ok(());
6859 };
6860 emitter.instruction(Instruction::Call(symbol));
6861 emitter.instruction(Instruction::LocalGet(old));
6862 Ok(())
6863}
6864
6865#[cfg(test)]
6866mod tests {
6867 use super::*;
6868 use crate::codegen::invariant_tests::{assert_internal, with_context};
6869 use crate::{FileId, Span, TypedAst, TypedExpr};
6870
6871 #[test]
6872 fn missing_method_dispatch_is_a_compiler_error() {
6873 with_context(
6874 &TypedAst::new(),
6875 &crate::codegen::SymbolTable::default(),
6876 |ctx| {
6877 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
6878 let error = emit_method_call_with_receiver_on_stack(
6879 &mut emitter,
6880 ctx,
6881 &Type::Number,
6882 &crate::mangle::prelude("MissingInterface"),
6883 "missingMethod",
6884 &[],
6885 None,
6886 None,
6887 &Type::Number,
6888 )
6889 .unwrap_err();
6890 assert!(error.to_string().contains("MissingInterface.missingMethod"));
6891 assert_internal(error);
6892 },
6893 );
6894 }
6895
6896 #[test]
6897 fn call_and_chain_metadata_are_checked_before_operands() {
6898 with_context(
6899 &TypedAst::new(),
6900 &crate::codegen::SymbolTable::default(),
6901 |ctx| {
6902 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
6903 assert_internal(
6904 emit_intrinsic_call(&mut emitter, ctx, Intrinsic::Assert, &[], &Type::Void)
6905 .unwrap_err(),
6906 );
6907 assert_internal(
6908 emit_intrinsic_call(
6909 &mut emitter,
6910 ctx,
6911 Intrinsic::BigIntFromString,
6912 &[],
6913 &Type::BigInt,
6914 )
6915 .unwrap_err(),
6916 );
6917 assert_internal(
6918 emit_direct_call(
6919 &mut emitter,
6920 ctx,
6921 false,
6922 0,
6923 &[Type::Number],
6924 &Type::Void,
6925 &[],
6926 )
6927 .unwrap_err(),
6928 );
6929 assert_internal(
6930 emit_args_into_slots(&mut emitter, ctx, &[], Some(&[ValType::F64]))
6931 .unwrap_err(),
6932 );
6933 assert_internal(
6934 emit_chain_parts(
6935 &mut emitter,
6936 ctx,
6937 &[],
6938 1,
6939 &Type::Number,
6940 &Type::Number,
6941 None,
6942 )
6943 .unwrap_err(),
6944 );
6945 assert_internal(
6946 emit_chain_parts(
6947 &mut emitter,
6948 ctx,
6949 &[],
6950 0,
6951 &Type::Number,
6952 &Type::Number,
6953 Some(&[]),
6954 )
6955 .unwrap_err(),
6956 );
6957 },
6958 );
6959 }
6960
6961 #[test]
6962 fn uninterned_literal_fails_and_a_fresh_emitter_still_works() {
6963 let mut ta = TypedAst::new();
6964 let string = ta
6965 .try_push_expr(TypedExpr {
6966 kind: TypedExprKind::String("missing".into()),
6967 ty: Type::String,
6968 span: Span::at(FileId(0)),
6969 })
6970 .unwrap();
6971 let number = ta
6972 .try_push_expr(TypedExpr {
6973 kind: TypedExprKind::Number(3.0),
6974 ty: Type::Number,
6975 span: Span::at(FileId(0)),
6976 })
6977 .unwrap();
6978 with_context(&ta, &crate::codegen::SymbolTable::default(), |ctx| {
6979 let mut failed = FunctionEmitter::new(ctx, &[]).unwrap();
6980 assert_internal(emit_expr(&mut failed, ctx, string).unwrap_err());
6981 drop(failed);
6982 let mut healthy = FunctionEmitter::new(ctx, &[]).unwrap();
6983 emit_expr(&mut healthy, ctx, number).unwrap();
6984 healthy.instruction(Instruction::Drop);
6985 healthy.build().unwrap();
6986 });
6987 }
6988}