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 {
3059 args,
3060 union_signature,
3061 ..
3062 } => {
3063 emit_indirect_closure_call_with_receiver_on_stack(
3070 emitter,
3071 ctx,
3072 union_signature.as_ref().unwrap_or(receiver_ty),
3073 args,
3074 part.result_ty(),
3075 )?;
3076 }
3077 crate::TypedChainPart::MethodCall {
3078 iface,
3079 name,
3080 args,
3081 result_ty,
3082 ..
3083 } => {
3084 emit_method_call_with_receiver_on_stack(
3094 emitter,
3095 ctx,
3096 receiver_ty,
3097 iface,
3098 &name.name,
3099 args,
3100 None,
3101 None,
3102 result_ty,
3103 )?;
3104 }
3105 crate::TypedChainPart::NonNull { result_ty, .. } => {
3106 crate::codegen::cast_check::emit_non_null_assert_on_stack(
3107 emitter,
3108 ctx,
3109 receiver_ty,
3110 result_ty,
3111 )?;
3112 }
3113 };
3114 Ok(())
3115}
3116
3117fn emit_captured_load(
3128 emitter: &mut FunctionEmitter,
3129 _ctx: &CodegenCtx,
3130 c: &crate::CapturedVar,
3131) -> Result<(), crate::compiler_error::CompilerFailure> {
3132 let slot = emitter.local_slot(&c.name.name).ok_or_else(|| {
3133 crate::codegen::internal_failure("captured binding is missing from the outer scope")
3134 })?;
3135 emitter.instruction(Instruction::LocalGet(slot));
3136 Ok(())
3137}
3138
3139fn emit_binary(
3140 emitter: &mut FunctionEmitter,
3141 ctx: &CodegenCtx,
3142 op: BinOp,
3143 lhs: ExprId,
3144 rhs: ExprId,
3145 result_ty: &Type,
3146) -> Result<(), crate::compiler_error::CompilerFailure> {
3147 let admits_never_operand = matches!(
3148 op,
3149 BinOp::Add
3150 | BinOp::Sub
3151 | BinOp::Mul
3152 | BinOp::Div
3153 | BinOp::Rem
3154 | BinOp::Pow
3155 | BinOp::Lt
3156 | BinOp::Gt
3157 | BinOp::Le
3158 | BinOp::Ge
3159 );
3160 if admits_never_operand && try_emit_unreachable_for_never_operand(emitter, ctx, &[lhs, rhs])? {
3161 return Ok(());
3162 }
3163 let _: () = match op {
3164 BinOp::BitAnd
3169 | BinOp::BitOr
3170 | BinOp::BitXor
3171 | BinOp::Shl
3172 | BinOp::Shr
3173 | BinOp::UnsignedShr => {
3174 if has_numeric_operands(ctx, &[lhs, rhs], result_ty)? {
3175 emit_number_bitwise(emitter, ctx, op, lhs, rhs)?;
3176 } else {
3177 let name = op.bitwise_name().ok_or_else(|| {
3178 crate::codegen::internal_failure("missing bitwise host operation")
3179 })?;
3180 emit_bitwise_host(emitter, ctx, name, &[lhs, rhs], result_ty)?;
3181 }
3182 }
3183 BinOp::Add => match &result_ty.widen_literal() {
3186 Type::Number => {
3187 emit_primitive_operand(emitter, ctx, lhs)?;
3188 emit_primitive_operand(emitter, ctx, rhs)?;
3189 emitter.instruction(Instruction::F64Add);
3190 }
3191 Type::String => {
3192 emit_primitive_operand(emitter, ctx, lhs)?;
3193 emit_primitive_operand(emitter, ctx, rhs)?;
3194 let idx = ctx
3195 .symbols
3196 .prelude_func_idx("string_concat")
3197 .ok_or_else(|| {
3198 crate::codegen::internal_failure("string_concat imported from prelude")
3199 })?;
3200 emitter.instruction(Instruction::Call(idx));
3201 }
3202 Type::BigInt => emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "add")?,
3204 other => {
3205 return Err(crate::codegen::internal_failure(format!(
3206 "typechecker rejects `+` for {other:?}"
3207 )));
3208 }
3209 },
3210 BinOp::Sub | BinOp::Mul | BinOp::Div => {
3211 if result_ty.is_bigint() {
3213 let name = match op {
3214 BinOp::Sub => "sub",
3215 BinOp::Mul => "mul",
3216 BinOp::Div => "div",
3217 _ => {
3218 return Err(crate::codegen::internal_failure(
3219 "invalid compiler emission dispatch",
3220 ));
3221 }
3222 };
3223 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, name)?;
3224 return Ok(());
3225 }
3226 if !matches!(result_ty, Type::Number) {
3227 return Err(crate::codegen::internal_failure(
3228 "numeric operator has a non-number result type",
3229 ));
3230 }
3231 emit_primitive_operand(emitter, ctx, lhs)?;
3232 emit_primitive_operand(emitter, ctx, rhs)?;
3233 let inst = match op {
3234 BinOp::Sub => Instruction::F64Sub,
3235 BinOp::Mul => Instruction::F64Mul,
3236 BinOp::Div => Instruction::F64Div,
3237 _ => {
3238 return Err(crate::codegen::internal_failure(
3239 "invalid compiler emission dispatch",
3240 ));
3241 }
3242 };
3243 emitter.instruction(inst);
3244 }
3245 BinOp::Pow => {
3246 if result_ty.is_bigint() {
3251 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "pow")?;
3252 return Ok(());
3253 }
3254 if !matches!(result_ty, Type::Number) {
3255 return Err(crate::codegen::internal_failure(
3256 "numeric operator has a non-number result type",
3257 ));
3258 }
3259 emit_primitive_operand(emitter, ctx, lhs)?;
3260 emit_primitive_operand(emitter, ctx, rhs)?;
3261 let idx = ctx
3262 .symbols
3263 .func_idx(&crate::runtime::prelude::math::math_key("pow"))
3264 .ok_or_else(|| crate::codegen::internal_failure("Math#pow host import recorded"))?;
3265 emitter.instruction(Instruction::Call(idx));
3266 }
3267 BinOp::Rem => {
3268 if result_ty.is_bigint() {
3270 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "mod")?;
3271 return Ok(());
3272 }
3273 let a = emitter.add_anonymous_local(ValType::F64)?;
3277 let b = emitter.add_anonymous_local(ValType::F64)?;
3278 emit_primitive_operand(emitter, ctx, lhs)?;
3279 emitter.instruction(Instruction::LocalSet(a));
3280 emit_primitive_operand(emitter, ctx, rhs)?;
3281 emitter.instruction(Instruction::LocalSet(b));
3282 emitter.instruction(Instruction::LocalGet(a));
3284 emitter.instruction(Instruction::LocalGet(a));
3285 emitter.instruction(Instruction::LocalGet(b));
3286 emitter.instruction(Instruction::F64Div);
3287 emitter.instruction(Instruction::F64Trunc);
3288 emitter.instruction(Instruction::LocalGet(b));
3289 emitter.instruction(Instruction::F64Mul);
3290 emitter.instruction(Instruction::F64Sub);
3291 }
3292 BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
3293 let operand_ty = ctx
3296 .ta
3297 .try_expr(lhs)
3298 .map_err(crate::codegen::arena_failure)?
3299 .ty
3300 .primitive_behavior()
3301 .clone();
3302 if operand_ty.is_bigint() {
3303 emit_bigint_cmp_inline(emitter, ctx, lhs, rhs, op)?;
3304 return Ok(());
3305 }
3306 emit_primitive_operand(emitter, ctx, lhs)?;
3307 emit_primitive_operand(emitter, ctx, rhs)?;
3308 if matches!(operand_ty, Type::String | Type::StringLiteral(_)) {
3309 let idx = ctx.symbols.prelude_func_idx("string_cmp").ok_or_else(|| {
3310 crate::codegen::internal_failure("string_cmp imported from prelude")
3311 })?;
3312 emitter.instruction(Instruction::Call(idx));
3313 emitter.instruction(Instruction::I32Const(0));
3314 emitter.instruction(match op {
3315 BinOp::Lt => Instruction::I32LtS,
3316 BinOp::Gt => Instruction::I32GtS,
3317 BinOp::Le => Instruction::I32LeS,
3318 BinOp::Ge => Instruction::I32GeS,
3319 _ => {
3320 return Err(crate::codegen::internal_failure(
3321 "invalid compiler emission dispatch",
3322 ));
3323 }
3324 });
3325 } else {
3326 emitter.instruction(match op {
3327 BinOp::Lt => Instruction::F64Lt,
3328 BinOp::Gt => Instruction::F64Gt,
3329 BinOp::Le => Instruction::F64Le,
3330 BinOp::Ge => Instruction::F64Ge,
3331 _ => {
3332 return Err(crate::codegen::internal_failure(
3333 "invalid compiler emission dispatch",
3334 ));
3335 }
3336 });
3337 }
3338 }
3339 BinOp::Eq | BinOp::NotEq => emit_equality(emitter, ctx, op, lhs, rhs)?,
3340 BinOp::And | BinOp::Or => emit_logical(emitter, ctx, op, lhs, rhs, result_ty)?,
3341 BinOp::In => emit_in_operator(emitter, ctx, lhs, rhs)?,
3342 BinOp::NullishCoalesce => {
3346 return Err(crate::codegen::internal_failure(
3347 "BinOp::NullishCoalesce should be lifted into TypedExprKind::NullishCoalesce by infer",
3348 ));
3349 }
3350 };
3351 Ok(())
3352}
3353
3354fn emit_equality(
3357 emitter: &mut FunctionEmitter,
3358 ctx: &CodegenCtx,
3359 op: BinOp,
3360 lhs: ExprId,
3361 rhs: ExprId,
3362) -> Result<(), crate::compiler_error::CompilerFailure> {
3363 let lhs_is_null_lit = matches!(
3370 ctx.ta
3371 .try_expr(lhs)
3372 .map_err(crate::codegen::arena_failure)?
3373 .kind,
3374 TypedExprKind::Null
3375 );
3376 let rhs_is_null_lit = matches!(
3377 ctx.ta
3378 .try_expr(rhs)
3379 .map_err(crate::codegen::arena_failure)?
3380 .kind,
3381 TypedExprKind::Null
3382 );
3383 if lhs_is_null_lit && rhs_is_null_lit {
3384 let v: i32 = if matches!(op, BinOp::Eq) { 1 } else { 0 };
3385 emitter.instruction(Instruction::I32Const(v));
3386 return Ok(());
3387 }
3388 if lhs_is_null_lit || rhs_is_null_lit {
3389 let non_null_side = if lhs_is_null_lit { rhs } else { lhs };
3390 let non_null_ty = ctx
3391 .ta
3392 .try_expr(non_null_side)
3393 .map_err(crate::codegen::arena_failure)?
3394 .ty
3395 .clone();
3396 let non_null_val = ctx.symbols.value_type(&non_null_ty)?;
3397 if matches!(non_null_val, ValType::F64 | ValType::I32) {
3405 emit_expr(emitter, ctx, non_null_side)?;
3406 emitter.instruction(Instruction::Drop);
3407 let v: i32 = if matches!(op, BinOp::NotEq) { 1 } else { 0 };
3408 emitter.instruction(Instruction::I32Const(v));
3409 return Ok(());
3410 }
3411 emit_expr(emitter, ctx, non_null_side)?;
3412 emitter.instruction(Instruction::RefIsNull);
3413 if matches!(op, BinOp::NotEq) {
3414 emitter.instruction(Instruction::I32Eqz);
3415 }
3416 return Ok(());
3417 }
3418 let lhs_ty = ctx
3419 .ta
3420 .try_expr(lhs)
3421 .map_err(crate::codegen::arena_failure)?
3422 .ty
3423 .clone();
3424 let rhs_ty = ctx
3425 .ta
3426 .try_expr(rhs)
3427 .map_err(crate::codegen::arena_failure)?
3428 .ty
3429 .clone();
3430 let operand_val = ctx.symbols.value_type(lhs_ty.primitive_behavior())?;
3431 if may_hold_null(&lhs_ty)
3437 || may_hold_null(&rhs_ty)
3438 || operand_val != ctx.symbols.value_type(rhs_ty.primitive_behavior())?
3439 {
3440 emit_boxed_eq(emitter, ctx, lhs, rhs, op)?;
3441 return Ok(());
3442 }
3443 if lhs_ty.is_bigint() {
3447 emit_bigint_cmp_eq_inline(emitter, ctx, lhs, rhs, op)?;
3448 return Ok(());
3449 }
3450 let string_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
3451 crate::codegen::internal_failure("string type registered with intrinsics")
3452 })?;
3453 match operand_val {
3459 ValType::F64 => {
3460 emit_primitive_operand(emitter, ctx, lhs)?;
3461 emit_primitive_operand(emitter, ctx, rhs)?;
3462 emitter.instruction(if matches!(op, BinOp::Eq) {
3463 Instruction::F64Eq
3464 } else {
3465 Instruction::F64Ne
3466 });
3467 }
3468 ValType::I32 => {
3469 emit_expr(emitter, ctx, lhs)?;
3470 emit_expr(emitter, ctx, rhs)?;
3471 emitter.instruction(if matches!(op, BinOp::Eq) {
3472 Instruction::I32Eq
3473 } else {
3474 Instruction::I32Ne
3475 });
3476 }
3477 ValType::Ref(RefType {
3478 heap_type: HeapType::Concrete(idx),
3479 ..
3480 }) if idx == string_idx => {
3481 emit_expr(emitter, ctx, lhs)?;
3482 emit_expr(emitter, ctx, rhs)?;
3483 let func = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
3484 crate::codegen::internal_failure("string_eq imported from prelude")
3485 })?;
3486 emitter.instruction(Instruction::Call(func));
3487 if matches!(op, BinOp::NotEq) {
3488 emitter.instruction(Instruction::I32Eqz);
3489 }
3490 }
3491 ValType::Ref(_) => {
3492 emit_expr(emitter, ctx, lhs)?;
3503 cast::emit_box(emitter, ctx, &lhs_ty)?;
3504 emit_expr(emitter, ctx, rhs)?;
3505 cast::emit_box(emitter, ctx, &rhs_ty)?;
3506 emit_vtable_equality(emitter, ctx, op);
3507 }
3508 other => {
3509 return Err(crate::codegen::internal_failure(format!(
3510 "typechecker rejects equality on Wasm value-type `{other:?}`"
3511 )));
3512 }
3513 }
3514 Ok(())
3515}
3516
3517fn emit_logical(
3518 emitter: &mut FunctionEmitter,
3519 ctx: &CodegenCtx,
3520 op: BinOp,
3521 lhs: ExprId,
3522 rhs: ExprId,
3523 result_ty: &Type,
3524) -> Result<(), crate::compiler_error::CompilerFailure> {
3525 let result_val = ctx.symbols.value_type(result_ty)?;
3531 let lhs_ty = ctx
3532 .ta
3533 .try_expr(lhs)
3534 .map_err(crate::codegen::arena_failure)?
3535 .ty
3536 .clone();
3537 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
3538 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
3539 let tmp = emitter.add_anonymous_local(lhs_val)?;
3540 emit_expr(emitter, ctx, lhs)?;
3541 emitter.instruction(Instruction::LocalTee(tmp));
3542 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &lhs_ty)?;
3543 let emit_rhs_branch =
3544 |emitter: &mut FunctionEmitter| -> Result<(), crate::compiler_error::CompilerFailure> {
3545 let rhs_ty = ctx
3546 .ta
3547 .try_expr(rhs)
3548 .map_err(crate::codegen::arena_failure)?
3549 .ty
3550 .clone();
3551 emit_expr(emitter, ctx, rhs)?;
3552 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3553 emitter, ctx, &rhs_ty, result_ty,
3554 )?;
3555 Ok(())
3556 };
3557 let kept_lhs_ty = match op {
3561 BinOp::And => falsy_part(&lhs_ty),
3562 _ => truthy_part(&lhs_ty),
3563 };
3564 let lhs_is_never_kept = matches!(kept_lhs_ty.peel(), Type::Never);
3565 let emit_kept_lhs_branch = |emitter: &mut FunctionEmitter| {
3566 if lhs_is_never_kept {
3567 emitter.instruction(Instruction::Unreachable);
3568 return Ok(());
3569 }
3570 emitter.instruction(Instruction::LocalGet(tmp));
3571 if lhs_is_ref {
3572 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?;
3576 } else {
3577 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3578 emitter, ctx, &lhs_ty, result_ty,
3579 )?;
3580 }
3581 Ok::<(), crate::compiler_error::CompilerFailure>(())
3582 };
3583 emitter.emit_if(BlockType::Result(result_val));
3584 match op {
3585 BinOp::And => emit_rhs_branch(emitter)?,
3586 _ => emit_kept_lhs_branch(emitter)?,
3587 }
3588 emitter.emit_else();
3589 match op {
3590 BinOp::And => emit_kept_lhs_branch(emitter)?,
3591 _ => emit_rhs_branch(emitter)?,
3592 }
3593 emitter.emit_end();
3594 Ok(())
3595}
3596
3597fn try_emit_unreachable_for_never_operand(
3602 emitter: &mut FunctionEmitter,
3603 ctx: &CodegenCtx,
3604 operands: &[ExprId],
3605) -> Result<bool, crate::compiler_error::CompilerFailure> {
3606 let mut has_never_operand = false;
3607 for &operand in operands {
3608 let ty = &ctx
3609 .ta
3610 .try_expr(operand)
3611 .map_err(crate::codegen::arena_failure)?
3612 .ty;
3613 has_never_operand |= matches!(ty.peel(), Type::Never);
3614 }
3615 if !has_never_operand {
3616 return Ok(false);
3617 }
3618 for &operand in operands {
3619 emit_expr(emitter, ctx, operand)?;
3620 emitter.instruction(Instruction::Drop);
3621 }
3622 emitter.instruction(Instruction::Unreachable);
3623 Ok(true)
3624}
3625
3626fn emit_primitive_operand(
3627 emitter: &mut FunctionEmitter,
3628 ctx: &CodegenCtx,
3629 expr: ExprId,
3630) -> Result<(), crate::compiler_error::CompilerFailure> {
3631 emit_expr(emitter, ctx, expr)?;
3632 let ty = &ctx
3633 .ta
3634 .try_expr(expr)
3635 .map_err(crate::codegen::arena_failure)?
3636 .ty;
3637 let _: () = if matches!(ty.primitive_behavior(), Type::Number)
3638 && ctx.symbols.value_type(ty)? != ValType::F64
3639 {
3640 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
3641 };
3642 Ok(())
3643}
3644
3645fn emit_in_operator(
3653 emitter: &mut FunctionEmitter,
3654 ctx: &CodegenCtx,
3655 lhs: ExprId,
3656 rhs: ExprId,
3657) -> Result<(), crate::compiler_error::CompilerFailure> {
3658 emit_expr(emitter, ctx, lhs)?;
3659 crate::codegen::cast_check::emit_operation_cast_on_stack(
3660 emitter,
3661 ctx,
3662 &ctx.ta
3663 .try_expr(lhs)
3664 .map_err(crate::codegen::arena_failure)?
3665 .ty,
3666 &Type::String,
3667 )?;
3668 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
3669 emitter.instruction(Instruction::LocalSet(key));
3670 emit_expr(emitter, ctx, rhs)?;
3671 cast::emit_box(
3672 emitter,
3673 ctx,
3674 &ctx.ta
3675 .try_expr(rhs)
3676 .map_err(crate::codegen::arena_failure)?
3677 .ty,
3678 )?;
3679 emitter.instruction(Instruction::LocalGet(key));
3680 let Some(symbol) = ctx.require(
3681 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
3682 "presence helper imported",
3683 ) else {
3684 return Ok(());
3685 };
3686 emitter.instruction(Instruction::Call(symbol));
3687 Ok(())
3688}
3689
3690pub(crate) fn emit_class_field_setup(
3695 emitter: &mut FunctionEmitter,
3696 ctx: &CodegenCtx,
3697 mangled: &crate::MangledName,
3698) -> Result<(), crate::compiler_error::CompilerFailure> {
3699 emit_class_field_setup_phase(emitter, ctx, mangled, FieldSetupPhase::All)
3700}
3701
3702#[derive(Clone, Copy)]
3703pub(crate) enum FieldSetupPhase {
3704 All,
3705 Initializers,
3706 ParameterProperties,
3707}
3708
3709pub(crate) fn emit_class_field_setup_phase(
3710 emitter: &mut FunctionEmitter,
3711 ctx: &CodegenCtx,
3712 mangled: &crate::MangledName,
3713 phase: FieldSetupPhase,
3714) -> Result<(), crate::compiler_error::CompilerFailure> {
3715 use crate::codegen::symbol_table::FieldSetup;
3716 let setup: Vec<FieldSetup> = match ctx.symbols.class_field_setup(mangled) {
3717 Some(s) if !s.is_empty() => s.to_vec(),
3718 _ => return Ok(()),
3719 };
3720 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3721 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3722 })?;
3723 let intrinsics = ctx
3724 .symbols
3725 .intrinsic_type_indices()
3726 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3727 let this = emitter.this_local().ok_or_else(|| {
3728 crate::codegen::internal_failure("field setup runs inside a constructor init fn")
3729 })?;
3730 for step in &setup {
3731 let parameter_property = matches!(step, FieldSetup::ParamCopy { .. });
3732 if matches!(phase, FieldSetupPhase::Initializers) && parameter_property
3733 || matches!(phase, FieldSetupPhase::ParameterProperties) && !parameter_property
3734 {
3735 continue;
3736 }
3737 let field = match step {
3738 FieldSetup::Init { field, .. }
3739 | FieldSetup::ParamCopy { field, .. }
3740 | FieldSetup::Reset { field } => field,
3741 };
3742 let slot = ctx
3743 .symbols
3744 .class_field_slot(mangled, field)
3745 .ok_or_else(|| {
3746 crate::codegen::internal_failure("class field slot recorded in classes::emit")
3747 })?;
3748 match step {
3749 FieldSetup::Init { value, .. } => {
3750 let value_ty = ctx
3751 .ta
3752 .try_expr(*value)
3753 .map_err(crate::codegen::arena_failure)?
3754 .ty
3755 .clone();
3756 emit_expr(emitter, ctx, *value)?;
3757 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &value_ty)?;
3758 }
3759 FieldSetup::ParamCopy { param, .. } => {
3760 let local = emitter.require_local_slot(¶m.name.name)?;
3761 if param.boxed {
3762 let box_type = ctx.symbols.box_type_idx(¶m.ty)?.ok_or_else(|| {
3763 crate::codegen::internal_failure(
3764 "constructor parameter cell is not registered",
3765 )
3766 })?;
3767 super::binding_cells::read(emitter, ctx, local, box_type)?;
3768 cast::emit_unerase(emitter, ctx, ¶m.ty)?;
3769 } else {
3770 emitter.instruction(Instruction::LocalGet(local));
3771 }
3772 cast::emit_box(emitter, ctx, ¶m.ty)?;
3773 }
3774 FieldSetup::Reset { .. } => {
3775 emit_undefined(emitter, ctx)?;
3776 }
3777 }
3778 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
3779 emitter.instruction(Instruction::LocalSet(stored));
3780 emitter.instruction(Instruction::LocalGet(this));
3781 emitter.instruction(Instruction::StructGet {
3782 struct_type_index: struct_idx,
3783 field_index: 2,
3784 });
3785 emitter.instruction(Instruction::I32Const(slot as i32));
3786 emitter.instruction(Instruction::LocalGet(stored));
3787 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
3788 let index = emitter.add_anonymous_local(ValType::I32)?;
3789 emitter.instruction(Instruction::I32Const(slot as i32));
3790 emitter.instruction(Instruction::LocalSet(index));
3791 crate::codegen::field_names::emit_mark_present(emitter, ctx, this, index)?;
3792 }
3793 Ok(())
3794}
3795
3796pub(crate) fn emit_field_slot_get(
3800 emitter: &mut FunctionEmitter,
3801 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
3802 object_local: u32,
3803 index_local: u32,
3804) {
3805 emitter.instruction(Instruction::LocalGet(object_local));
3806 emitter.instruction(Instruction::StructGet {
3807 struct_type_index: intrinsics.object_shape,
3808 field_index: 2,
3809 });
3810 emitter.instruction(Instruction::LocalGet(index_local));
3811 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3812}
3813
3814pub(crate) fn emit_object_field_read_by_name(
3815 emitter: &mut FunctionEmitter,
3816 ctx: &CodegenCtx,
3817 object_local: u32,
3818 name_global: u32,
3819) {
3820 ctx.latch(emit_object_field_read_by_name_checked(
3821 emitter,
3822 ctx,
3823 object_local,
3824 name_global,
3825 ));
3826}
3827
3828fn emit_object_field_read_by_name_checked(
3829 emitter: &mut FunctionEmitter,
3830 ctx: &CodegenCtx,
3831 object_local: u32,
3832 name_global: u32,
3833) -> Result<(), crate::compiler_error::CompilerFailure> {
3834 let intrinsics = ctx
3835 .symbols
3836 .intrinsic_type_indices()
3837 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3838 let Some(index_local) = ctx.latch(emitter.add_anonymous_local(ValType::I32)) else {
3839 return Ok(());
3840 };
3841 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
3842 emitter.instruction(Instruction::LocalSet(index_local));
3843 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
3844 nullable: true,
3845 heap_type: HeapType::Concrete(intrinsics.object),
3846 })));
3847 emitter.instruction(Instruction::LocalGet(index_local));
3848 emitter.instruction(Instruction::I32Const(0));
3849 emitter.instruction(Instruction::I32LtS);
3850 emitter.emit_if(BlockType::Empty);
3851 emit_undefined(emitter, ctx)?;
3852 emitter.instruction(Instruction::Br(1));
3853 emitter.emit_end();
3854 emitter.instruction(Instruction::LocalGet(object_local));
3855 emitter.instruction(Instruction::StructGet {
3856 struct_type_index: intrinsics.object_shape,
3857 field_index: 2,
3858 });
3859 emitter.instruction(Instruction::LocalGet(index_local));
3860 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3861 emitter.emit_end();
3862
3863 Ok(())
3864}
3865
3866pub(crate) fn emit_object_field_write_by_name(
3867 emitter: &mut FunctionEmitter,
3868 ctx: &CodegenCtx,
3869 object_local: u32,
3870 name_global: u32,
3871) -> Result<(), crate::compiler_error::CompilerFailure> {
3872 let intrinsics = ctx
3873 .symbols
3874 .intrinsic_type_indices()
3875 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3876 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
3877 nullable: true,
3878 heap_type: HeapType::Concrete(intrinsics.object),
3879 }))?;
3880 let index_local = emitter.add_anonymous_local(ValType::I32)?;
3881 emitter.instruction(Instruction::LocalSet(value_local));
3882 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
3883 emitter.instruction(Instruction::LocalTee(index_local));
3884 emitter.instruction(Instruction::I32Const(0));
3885 emitter.instruction(Instruction::I32GeS);
3886 emitter.emit_if(BlockType::Empty);
3887 emitter.instruction(Instruction::LocalGet(object_local));
3888 emitter.instruction(Instruction::StructGet {
3889 struct_type_index: intrinsics.object_shape,
3890 field_index: 2,
3891 });
3892 emitter.instruction(Instruction::LocalGet(index_local));
3893 emitter.instruction(Instruction::LocalGet(value_local));
3894 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
3895 crate::codegen::field_names::emit_mark_present(emitter, ctx, object_local, index_local)?;
3896 emitter.emit_else();
3897 emitter.instruction(Instruction::LocalGet(object_local));
3898 emitter.instruction(Instruction::GlobalGet(name_global));
3899 emitter.instruction(Instruction::LocalGet(value_local));
3900 let insert = ctx
3901 .symbols
3902 .prelude_func_idx("ObjectConstructor##insertField")
3903 .ok_or_else(|| {
3904 crate::codegen::internal_failure("field insertion helper is not imported")
3905 })?;
3906 emitter.instruction(Instruction::Call(insert));
3907 emitter.emit_end();
3908 Ok(())
3909}
3910
3911pub(crate) fn accessor_branch_emittable(ctx: &CodegenCtx, getter_name: &str) -> bool {
3918 ctx.symbols
3919 .field_name_string_global_idx(getter_name)
3920 .is_some()
3921}
3922
3923pub(crate) fn emit_is_accessor_backed(
3940 emitter: &mut FunctionEmitter,
3941 ctx: &CodegenCtx,
3942 object_local: u32,
3943 accessor_name: &str,
3944 kind: crate::AccessorKind,
3945) -> Result<u32, crate::compiler_error::CompilerFailure> {
3946 let intrinsics = ctx
3947 .symbols
3948 .intrinsic_type_indices()
3949 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3950 let accessor_global = ctx
3951 .symbols
3952 .field_name_string_global_idx(accessor_name)
3953 .ok_or_else(|| {
3954 crate::codegen::internal_failure("caller checked the accessor name has a string global")
3955 })?;
3956 let closure_struct_idx = ctx
3957 .symbols
3958 .closure_struct_type_idx(crate::codegen::classes::accessor_closure_sig(kind))
3959 .ok_or_else(|| {
3960 crate::codegen::internal_failure(
3961 "accessor closure struct collected by analysis::note_shaped_property_access",
3962 )
3963 })?;
3964 let index_local = emitter.add_anonymous_local(ValType::I32)?;
3965 emit_field_index_by_name(emitter, ctx, object_local, accessor_global, true);
3966 emitter.instruction(Instruction::LocalTee(index_local));
3967 emitter.instruction(Instruction::I32Const(0));
3968 emitter.instruction(Instruction::I32GeS);
3969 emitter.instruction(Instruction::LocalGet(object_local));
3970 emitter.instruction(Instruction::StructGet {
3971 struct_type_index: intrinsics.object,
3972 field_index: 0,
3973 });
3974 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
3975 intrinsics.class_vtable,
3976 )));
3977 emitter.instruction(Instruction::I32And);
3978 emitter.emit_if(BlockType::Result(ValType::I32));
3979 emitter.instruction(Instruction::LocalGet(object_local));
3980 emitter.instruction(Instruction::StructGet {
3981 struct_type_index: intrinsics.object_shape,
3982 field_index: 1,
3983 });
3984 emitter.instruction(Instruction::LocalGet(index_local));
3985 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
3986 crate::codegen::field_names::emit_name_is_accessor(emitter, ctx)?;
3987 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
3988 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
3989 closure_struct_idx,
3990 )));
3991 emitter.instruction(Instruction::I32And);
3992 emitter.emit_else();
3993 emitter.instruction(Instruction::I32Const(0));
3994 emitter.emit_end();
3995 Ok(index_local)
3996}
3997
3998pub(crate) fn emit_object_property_read(
4008 emitter: &mut FunctionEmitter,
4009 ctx: &CodegenCtx,
4010 object_local: u32,
4011 prop_name: &str,
4012 field_ty: &Type,
4013) -> Result<(), crate::compiler_error::CompilerFailure> {
4014 emit_object_property_read_as(emitter, ctx, object_local, prop_name, field_ty, field_ty)?;
4015 Ok(())
4016}
4017
4018fn emit_object_property_read_as(
4019 emitter: &mut FunctionEmitter,
4020 ctx: &CodegenCtx,
4021 object_local: u32,
4022 prop_name: &str,
4023 field_ty: &Type,
4024 check_ty: &Type,
4025) -> Result<(), crate::compiler_error::CompilerFailure> {
4026 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4027 let name_global = ctx
4028 .symbols
4029 .field_name_string_global_idx(prop_name)
4030 .ok_or_else(|| {
4031 crate::codegen::internal_failure(
4032 "per-name string global recorded during field-name-strings emission",
4033 )
4034 })?;
4035 if !accessor_branch_emittable(ctx, &getter) {
4036 emit_object_field_read_by_name(emitter, ctx, object_local, name_global);
4037 emit_dynamic_narrowed_property_read(
4038 emitter,
4039 ctx,
4040 object_local,
4041 prop_name,
4042 field_ty,
4043 check_ty,
4044 )?;
4045 return Ok(());
4046 }
4047 let intrinsics = ctx
4048 .symbols
4049 .intrinsic_type_indices()
4050 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4051 let result_vt = ctx.symbols.value_type(field_ty)?;
4052 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4053 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4054 emitter.instruction(Instruction::LocalSet(index_local));
4055 emitter.instruction(Instruction::LocalGet(index_local));
4056 emitter.instruction(Instruction::I32Const(0));
4057 emitter.instruction(Instruction::I32GeS);
4058 emitter.emit_if(BlockType::Result(result_vt));
4059 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4061 emit_dynamic_narrowed_property_read(emitter, ctx, object_local, prop_name, field_ty, check_ty)?;
4062 emitter.emit_else();
4063 let getter_slot_local = emit_is_accessor_backed(
4064 emitter,
4065 ctx,
4066 object_local,
4067 &getter,
4068 crate::AccessorKind::Get,
4069 )?;
4070 emitter.emit_if(BlockType::Result(result_vt));
4071 emitter.instruction(Instruction::LocalGet(object_local));
4073 emit_interface_method_via_shape_with_receiver_on_stack(
4074 emitter,
4075 ctx,
4076 &getter,
4077 &[],
4078 field_ty,
4079 Some(getter_slot_local),
4080 )?;
4081 emitter.emit_else();
4082 emit_undefined(emitter, ctx)?;
4088 cast::emit_cast_to(emitter, ctx, field_ty)?;
4089 emitter.emit_end();
4090 emitter.emit_end();
4091 Ok(())
4092}
4093
4094fn emit_dynamic_narrowed_property_read(
4095 emitter: &mut FunctionEmitter,
4096 ctx: &CodegenCtx,
4097 object_local: u32,
4098 prop_name: &str,
4099 field_ty: &Type,
4100 check_ty: &Type,
4101) -> Result<(), crate::compiler_error::CompilerFailure> {
4102 let checks: Vec<_> = ctx
4103 .symbols
4104 .class_field_narrowing_checks(prop_name)
4105 .filter_map(|(class, check)| {
4106 ctx.symbols
4107 .class_vtable_global_idx(class)
4108 .map(|vtable| (vtable, check))
4109 })
4110 .collect();
4111 if checks.is_empty() {
4112 cast::emit_cast_to(emitter, ctx, field_ty)?;
4113 return Ok(());
4114 }
4115
4116 let intrinsics = ctx
4117 .symbols
4118 .intrinsic_type_indices()
4119 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4120 let raw = emitter.add_anonymous_local(ValType::Ref(RefType {
4121 nullable: true,
4122 heap_type: HeapType::Concrete(intrinsics.object),
4123 }))?;
4124 emitter.instruction(Instruction::LocalSet(raw));
4125 emit_dynamic_narrowing_check(
4126 emitter,
4127 ctx,
4128 object_local,
4129 raw,
4130 field_ty,
4131 check_ty,
4132 &checks,
4133 0,
4134 )?;
4135 Ok(())
4136}
4137
4138#[allow(clippy::too_many_arguments)]
4139fn emit_dynamic_narrowing_check(
4140 emitter: &mut FunctionEmitter,
4141 ctx: &CodegenCtx,
4142 object_local: u32,
4143 raw: u32,
4144 field_ty: &Type,
4145 check_ty: &Type,
4146 checks: &[(u32, &crate::FieldNarrowingCheck)],
4147 index: usize,
4148) -> Result<(), crate::compiler_error::CompilerFailure> {
4149 let Some((vtable, check)) = checks.get(index) else {
4150 emitter.instruction(Instruction::LocalGet(raw));
4151 cast::emit_cast_to(emitter, ctx, field_ty)?;
4152 return Ok(());
4153 };
4154 emitter.instruction(Instruction::LocalGet(object_local));
4155 crate::codegen::function_emitter::cast::emit_nominal_instance_test(emitter, ctx, *vtable)?;
4156 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(field_ty)?));
4157 emitter.instruction(Instruction::LocalGet(raw));
4158 crate::codegen::cast_check::emit_narrowed_field_read_as(
4159 emitter, ctx, check, field_ty, check_ty,
4160 )?;
4161 emitter.emit_else();
4162 emit_dynamic_narrowing_check(
4163 emitter,
4164 ctx,
4165 object_local,
4166 raw,
4167 field_ty,
4168 check_ty,
4169 checks,
4170 index + 1,
4171 )?;
4172 emitter.emit_end();
4173 Ok(())
4174}
4175
4176pub(crate) fn emit_object_property_write(
4182 emitter: &mut FunctionEmitter,
4183 ctx: &CodegenCtx,
4184 object_local: u32,
4185 prop: &Ident,
4186 value: ExprId,
4187) -> Result<(), crate::compiler_error::CompilerFailure> {
4188 emit_object_property_write_value(
4189 emitter,
4190 ctx,
4191 object_local,
4192 prop,
4193 &ShapeArgument::Expression(value),
4194 )?;
4195 Ok(())
4196}
4197
4198fn emit_object_property_write_value(
4199 emitter: &mut FunctionEmitter,
4200 ctx: &CodegenCtx,
4201 object_local: u32,
4202 prop: &Ident,
4203 value: &ShapeArgument,
4204) -> Result<(), crate::compiler_error::CompilerFailure> {
4205 let prop_name = prop.name.as_str();
4206 let setter = crate::codegen::classes::accessor_setter_name(prop_name);
4207 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4208 let has_setter_name = ctx.symbols.field_name_string_global_idx(&setter).is_some();
4209 let has_getter_name = ctx.symbols.field_name_string_global_idx(&getter).is_some();
4210 let name_global = ctx
4211 .symbols
4212 .field_name_string_global_idx(prop_name)
4213 .ok_or_else(|| {
4214 crate::codegen::internal_failure(
4215 "per-name string global recorded during field-name-strings emission",
4216 )
4217 })?;
4218 if !has_setter_name && !has_getter_name {
4222 let value_ty = value.ty(ctx)?;
4223 value.emit(emitter, ctx)?;
4224 cast::emit_box(emitter, ctx, &value_ty)?;
4225 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4226 return Ok(());
4227 }
4228 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4229 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4230 emitter.instruction(Instruction::LocalSet(index_local));
4231 emitter.instruction(Instruction::LocalGet(index_local));
4232 emitter.instruction(Instruction::I32Const(0));
4233 emitter.instruction(Instruction::I32GeS);
4234 emitter.emit_if(BlockType::Empty);
4235 let value_ty = value.ty(ctx)?;
4238 value.emit(emitter, ctx)?;
4239 cast::emit_box(emitter, ctx, &value_ty)?;
4240 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4241 emitter.emit_else();
4242 let arms: Vec<(&str, AccessorWrite)> = [
4247 has_setter_name.then_some((setter.as_str(), AccessorWrite::Setter)),
4248 has_getter_name.then_some((getter.as_str(), AccessorWrite::ReadOnly)),
4249 ]
4250 .into_iter()
4251 .flatten()
4252 .collect();
4253 for (accessor, arm) in &arms {
4254 let slot_index_local =
4255 emit_is_accessor_backed(emitter, ctx, object_local, accessor, arm.scanned_kind())?;
4256 emitter.emit_if(BlockType::Empty);
4257 match arm {
4258 AccessorWrite::Setter => {
4259 emitter.instruction(Instruction::LocalGet(object_local));
4260 emit_shape_method_with_arguments(
4261 emitter,
4262 ctx,
4263 accessor,
4264 std::slice::from_ref(value),
4265 &Type::Void,
4266 Some(slot_index_local),
4267 )?;
4268 emitter.instruction(Instruction::Drop);
4269 }
4270 AccessorWrite::ReadOnly => {
4273 value.emit(emitter, ctx)?;
4274 emitter.instruction(Instruction::Drop);
4275 emitter.record_span(prop.span);
4278 crate::codegen::throw::emit_type_error_throw(
4279 emitter,
4280 ctx,
4281 crate::codegen::throw::READ_ONLY_PROPERTY_MESSAGE,
4282 );
4283 }
4284 }
4285 emitter.emit_else();
4286 }
4287 let value_ty = value.ty(ctx)?;
4288 value.emit(emitter, ctx)?;
4289 cast::emit_box(emitter, ctx, &value_ty)?;
4290 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4291 for _ in &arms {
4292 emitter.emit_end();
4293 }
4294 emitter.emit_end();
4295 Ok(())
4296}
4297
4298fn emit_bounds_checked_index_with_receiver_on_stack(
4312 emitter: &mut FunctionEmitter,
4313 ctx: &CodegenCtx,
4314 idx: ExprId,
4315 result_ty: &Type,
4316 allow_missing: bool,
4317) -> Result<(), crate::compiler_error::CompilerFailure> {
4318 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
4319 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4320 })?;
4321 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
4322 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4323 })?;
4324 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4325 nullable: false,
4326 heap_type: HeapType::Concrete(raw_array_idx),
4327 }))?;
4328 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4329 emitter.instruction(Instruction::LocalSet(receiver));
4330 emit_expr(emitter, ctx, idx)?;
4331 let key = emitter.add_anonymous_local(
4332 ctx.symbols.value_type(
4333 &ctx.ta
4334 .try_expr(idx)
4335 .map_err(crate::codegen::arena_failure)?
4336 .ty,
4337 )?,
4338 )?;
4339 emitter.instruction(Instruction::LocalSet(key));
4340 emitter.instruction(Instruction::LocalGet(receiver));
4341 crate::codegen::cast_check::emit_operation_cast_on_stack(
4342 emitter,
4343 ctx,
4344 &Type::Unknown,
4345 &Type::Array(Box::new(Type::Unknown)),
4346 )?;
4347 emitter.instruction(Instruction::LocalGet(key));
4348 emit_index_number(
4349 emitter,
4350 ctx,
4351 &ctx.ta
4352 .try_expr(idx)
4353 .map_err(crate::codegen::arena_failure)?
4354 .ty,
4355 )?;
4356 let idx_f64_local = stash_index_operand(emitter)?;
4357 let length = stash_array_length(emitter, array_idx)?;
4358 emitter.instruction(Instruction::StructGet {
4359 struct_type_index: array_idx,
4360 field_index: 1,
4361 });
4362 emitter.instruction(Instruction::LocalSet(raw_arr_local));
4363 if allow_missing {
4364 emitter.instruction(Instruction::LocalGet(idx_f64_local));
4365 emitter.instruction(Instruction::LocalGet(length));
4366 emitter.instruction(Instruction::F64ConvertI32U);
4367 emitter.instruction(Instruction::F64Ge);
4368 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
4369 emit_undefined(emitter, ctx)?;
4370 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
4371 emitter.emit_else();
4372 }
4373 let idx_local = emit_checked_index_with_length(emitter, ctx, length, idx_f64_local)?;
4374 emitter.instruction(Instruction::LocalGet(raw_arr_local));
4375 emitter.instruction(Instruction::LocalGet(idx_local));
4376 emitter.instruction(Instruction::ArrayGet(raw_array_idx));
4377 cast::emit_cast_to(emitter, ctx, result_ty)?;
4378 if allow_missing {
4379 emitter.emit_end();
4380 }
4381 Ok(())
4382}
4383
4384fn is_optional_tuple_position(
4385 ctx: &CodegenCtx<'_>,
4386 receiver: &Type,
4387 index: ExprId,
4388) -> Result<bool, crate::compiler_error::CompilerFailure> {
4389 let expression = ctx
4390 .ta
4391 .try_expr(index)
4392 .map_err(crate::codegen::arena_failure)?;
4393 let TypedExprKind::Number(index) = expression.kind else {
4394 return Ok(false);
4395 };
4396 Ok(index.fract() == 0.0 && tuple_permits_missing_index(receiver, index))
4397}
4398
4399fn tuple_permits_missing_index(receiver: &Type, index: f64) -> bool {
4400 match receiver.peel() {
4401 Type::Tuple(elements) => {
4402 index >= elements.required_len() as f64 && index < elements.len() as f64
4403 }
4404 Type::Union(members) => members
4405 .iter()
4406 .any(|member| tuple_permits_missing_index(member, index)),
4407 _ => false,
4408 }
4409}
4410
4411fn emit_uint8_index_with_receiver_on_stack(
4412 emitter: &mut FunctionEmitter,
4413 ctx: &CodegenCtx,
4414 index: ExprId,
4415) -> Result<(), crate::compiler_error::CompilerFailure> {
4416 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
4417 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4418 })?;
4419 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
4420 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4421 })?;
4422 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4423 nullable: false,
4424 heap_type: HeapType::Concrete(raw_uint8_idx),
4425 }))?;
4426 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4427 emitter.instruction(Instruction::LocalSet(receiver));
4428 emit_expr(emitter, ctx, index)?;
4429 let key = emitter.add_anonymous_local(
4430 ctx.symbols.value_type(
4431 &ctx.ta
4432 .try_expr(index)
4433 .map_err(crate::codegen::arena_failure)?
4434 .ty,
4435 )?,
4436 )?;
4437 emitter.instruction(Instruction::LocalSet(key));
4438 emitter.instruction(Instruction::LocalGet(receiver));
4439 crate::codegen::cast_check::emit_operation_cast_on_stack(
4440 emitter,
4441 ctx,
4442 &Type::Unknown,
4443 &Type::Uint8Array,
4444 )?;
4445 emitter.instruction(Instruction::LocalGet(key));
4446 emit_index_number(
4447 emitter,
4448 ctx,
4449 &ctx.ta
4450 .try_expr(index)
4451 .map_err(crate::codegen::arena_failure)?
4452 .ty,
4453 )?;
4454 let idx_f64_local = stash_index_operand(emitter)?;
4455 emitter.instruction(Instruction::StructGet {
4456 struct_type_index: uint8_idx,
4457 field_index: 1,
4458 });
4459 emitter.instruction(Instruction::LocalSet(raw_local));
4460 let idx_local = emit_checked_index(emitter, ctx, raw_local, idx_f64_local)?;
4461 emitter.instruction(Instruction::LocalGet(raw_local));
4462 emitter.instruction(Instruction::LocalGet(idx_local));
4463 emitter.instruction(Instruction::ArrayGetU(raw_uint8_idx));
4464 emitter.instruction(Instruction::F64ConvertI32U);
4465 Ok(())
4466}
4467
4468#[derive(Clone, Copy)]
4470enum AccessorWrite {
4471 Setter,
4472 ReadOnly,
4473}
4474
4475impl AccessorWrite {
4476 fn scanned_kind(self) -> crate::AccessorKind {
4479 match self {
4480 AccessorWrite::Setter => crate::AccessorKind::Set,
4481 AccessorWrite::ReadOnly => crate::AccessorKind::Get,
4482 }
4483 }
4484}
4485
4486pub(crate) fn emit_object_field_index_by_name(
4487 emitter: &mut FunctionEmitter,
4488 ctx: &CodegenCtx,
4489 object_local: u32,
4490 name_global: u32,
4491) {
4492 emit_field_index_by_name(emitter, ctx, object_local, name_global, false);
4493}
4494
4495fn emit_field_index_by_name(
4496 emitter: &mut FunctionEmitter,
4497 ctx: &CodegenCtx,
4498 object_local: u32,
4499 name_global: u32,
4500 accessor: bool,
4501) {
4502 ctx.latch(emit_field_index_by_name_checked(
4503 emitter,
4504 ctx,
4505 object_local,
4506 name_global,
4507 accessor,
4508 ));
4509}
4510
4511fn emit_field_index_by_name_checked(
4512 emitter: &mut FunctionEmitter,
4513 ctx: &CodegenCtx,
4514 object_local: u32,
4515 name_global: u32,
4516 accessor: bool,
4517) -> Result<(), crate::compiler_error::CompilerFailure> {
4518 emitter.instruction(Instruction::LocalGet(object_local));
4519 emitter.instruction(Instruction::GlobalGet(name_global));
4520 emitter.instruction(Instruction::I32Const(i32::from(accessor)));
4521 emitter.instruction(Instruction::Call(
4522 ctx.symbols
4523 .field_lookup_function
4524 .ok_or_else(|| crate::codegen::internal_failure("field lookup allocated"))?,
4525 ));
4526
4527 Ok(())
4528}
4529
4530fn emit_direct_call(
4536 emitter: &mut FunctionEmitter,
4537 ctx: &CodegenCtx,
4538 is_host: bool,
4539 wasm_idx: u32,
4540 params: &[Type],
4541 ret: &Type,
4542 args: &[ExprId],
4543) -> Result<(), crate::compiler_error::CompilerFailure> {
4544 if params.len() != args.len() {
4545 return Err(crate::codegen::internal_failure(
4546 "direct-call argument count mismatch",
4547 ));
4548 }
4549 let string_type_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
4550 crate::codegen::internal_failure("$string type registered before call codegen")
4551 })?;
4552 let raw_string_type_idx = ctx.symbols.raw_string_type_idx().ok_or_else(|| {
4553 crate::codegen::internal_failure("$rawString type registered before call codegen")
4554 })?;
4555 let mut evaluated = Vec::with_capacity(args.len());
4556 for &arg in args {
4557 emit_expr(emitter, ctx, arg)?;
4558 let slot = emitter.add_anonymous_local(
4559 ctx.symbols.value_type(
4560 &ctx.ta
4561 .try_expr(arg)
4562 .map_err(crate::codegen::arena_failure)?
4563 .ty,
4564 )?,
4565 )?;
4566 emitter.instruction(Instruction::LocalSet(slot));
4567 evaluated.push(slot);
4568 }
4569 for (i, (&arg_id, local)) in args.iter().zip(evaluated).enumerate() {
4570 emitter.instruction(Instruction::LocalGet(local));
4571 if let Some(param_ty) = params.get(i) {
4577 let arg_ty = ctx
4578 .ta
4579 .try_expr(arg_id)
4580 .map_err(crate::codegen::arena_failure)?
4581 .ty
4582 .clone();
4583 let default = ctx
4584 .symbols
4585 .host_call_defaults(wasm_idx, args.len())
4586 .get(i)
4587 .and_then(Option::as_ref);
4588 crate::codegen::argument_defaults::emit_argument(
4589 emitter,
4590 ctx,
4591 &arg_ty,
4592 ctx.symbols.value_type(param_ty)?,
4593 default,
4594 )?;
4595 }
4596 if is_host
4601 && params
4602 .get(i)
4603 .is_some_and(|p| matches!(p.peel(), Type::String))
4604 {
4605 emitter.instruction(Instruction::StructGet {
4606 struct_type_index: string_type_idx,
4607 field_index: 1,
4608 });
4609 }
4610 }
4611 emitter.instruction(Instruction::Call(wasm_idx));
4612 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
4613 emit_undefined(emitter, ctx)?;
4614 }
4615 let _: () = if is_host && matches!(ret.peel(), Type::String) {
4616 let scratch = emitter.add_anonymous_local(ValType::Ref(RefType {
4617 nullable: false,
4618 heap_type: HeapType::Concrete(raw_string_type_idx),
4619 }))?;
4620 emitter.instruction(Instruction::LocalSet(scratch));
4621 let vtable_global = ctx
4622 .symbols
4623 .prelude_global_idx("string_vtable")
4624 .ok_or_else(|| {
4625 crate::codegen::internal_failure("string_vtable imported from prelude")
4626 })?;
4627 emitter.instruction(Instruction::GlobalGet(vtable_global));
4628 emitter.instruction(Instruction::LocalGet(scratch));
4629 emitter.instruction(Instruction::I64Const(0));
4630 emitter.instruction(Instruction::StructNew(string_type_idx));
4631 };
4632 Ok(())
4633}
4634
4635fn emit_indirect_closure_call(
4644 emitter: &mut FunctionEmitter,
4645 ctx: &CodegenCtx,
4646 callee: ExprId,
4647 args: &[ExprId],
4648 result_ty: &Type,
4649) -> Result<(), crate::compiler_error::CompilerFailure> {
4650 let callee_ty = ctx
4651 .ta
4652 .source_type(callee)
4653 .map_err(crate::codegen::arena_failure)?
4654 .clone();
4655 let receiver = emit_callee(emitter, ctx, callee)?;
4656 emitter.call_receiver = receiver;
4657 emit_indirect_closure_call_with_receiver_on_stack(emitter, ctx, &callee_ty, args, result_ty)?;
4658 Ok(())
4659}
4660
4661fn emit_callee(
4663 emitter: &mut FunctionEmitter,
4664 ctx: &CodegenCtx,
4665 callee: ExprId,
4666) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
4667 let reference = reference_expression(ctx.ta, callee)?;
4668 let receiver = match &ctx
4669 .ta
4670 .try_expr(reference)
4671 .map_err(crate::codegen::arena_failure)?
4672 .kind
4673 {
4674 TypedExprKind::FieldAccess { receiver, .. }
4675 | TypedExprKind::IndexAccess { receiver, .. }
4676 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => Some(*receiver),
4677 _ => None,
4678 };
4679 let mark = emitter.single_evaluation_mark();
4680 let slot = receiver
4681 .map(|receiver| {
4682 emit_expr(emitter, ctx, receiver)?;
4683 let slot = emitter.add_anonymous_local(
4684 ctx.symbols.value_type(
4685 &ctx.ta
4686 .try_expr(receiver)
4687 .map_err(crate::codegen::arena_failure)?
4688 .ty,
4689 )?,
4690 )?;
4691 emitter.instruction(Instruction::LocalSet(slot));
4692 emitter.record_single_evaluation(receiver, slot)?;
4693 Ok::<_, crate::compiler_error::CompilerFailure>(slot)
4694 })
4695 .transpose()?;
4696 emit_expr(emitter, ctx, callee)?;
4697 emitter.end_single_evaluations(mark)?;
4698 Ok(
4699 if matches!(
4700 ctx.ta
4701 .try_expr(reference)
4702 .map_err(crate::codegen::arena_failure)?
4703 .kind,
4704 TypedExprKind::OptionalChain { .. }
4705 ) {
4706 emitter.call_receiver.take()
4707 } else {
4708 slot
4709 },
4710 )
4711}
4712
4713fn reference_expression(
4714 ast: &crate::TypedAst,
4715 mut id: ExprId,
4716) -> Result<ExprId, crate::compiler_error::CompilerFailure> {
4717 loop {
4718 match ast
4719 .try_expr(id)
4720 .map_err(crate::codegen::arena_failure)?
4721 .kind
4722 {
4723 TypedExprKind::Cast { value, .. } | TypedExprKind::NonNullAssert { value } => {
4724 id = value;
4725 }
4726 _ => return Ok(id),
4727 }
4728 }
4729}
4730
4731fn emit_indirect_closure_call_with_receiver_on_stack(
4737 emitter: &mut FunctionEmitter,
4738 ctx: &CodegenCtx,
4739 callee_ty: &Type,
4740 args: &[ExprId],
4741 result_ty: &Type,
4742) -> Result<(), crate::compiler_error::CompilerFailure> {
4743 let receiver = emitter.call_receiver.take();
4744 let callee = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4745 emitter.instruction(Instruction::LocalSet(callee));
4746 let mut arguments = Vec::with_capacity(args.len());
4747 for &arg in args {
4748 emit_expr(emitter, ctx, arg)?;
4749 cast::emit_box(
4750 emitter,
4751 ctx,
4752 &ctx.ta
4753 .try_expr(arg)
4754 .map_err(crate::codegen::arena_failure)?
4755 .ty,
4756 )?;
4757 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4758 emitter.instruction(Instruction::LocalSet(slot));
4759 arguments.push(slot);
4760 }
4761 emitter.instruction(Instruction::LocalGet(callee));
4762 crate::codegen::cast_check::emit_operation_cast_on_stack(
4763 emitter,
4764 ctx,
4765 &Type::Unknown,
4766 callee_ty,
4767 )?;
4768 let callee_ty = callee_ty.peel();
4773 let closure_sig = crate::codegen::closures::classify(callee_ty)?;
4774 let closure_struct_idx = ctx
4775 .symbols
4776 .closure_struct_type_idx(closure_sig)
4777 .ok_or_else(|| {
4778 crate::codegen::internal_failure(
4779 "closure struct type registered for every closure-typed callee",
4780 )
4781 })?;
4782 let fn_type_idx = ctx
4783 .symbols
4784 .closure_func_type_idx(closure_sig)
4785 .ok_or_else(|| {
4786 crate::codegen::internal_failure(
4787 "closure funcref type registered for every closure-typed callee",
4788 )
4789 })?;
4790 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4791 nullable: false,
4792 heap_type: HeapType::Concrete(closure_struct_idx),
4793 }))?;
4794 emitter.instruction(Instruction::LocalTee(closure_local));
4795 emitter.instruction(Instruction::StructGet {
4796 struct_type_index: closure_struct_idx,
4797 field_index: 2,
4798 });
4799 if let Some(receiver) = receiver {
4800 crate::codegen::this_binding::bind(emitter, ctx, receiver)?;
4801 }
4802 for argument in arguments {
4810 emitter.instruction(Instruction::LocalGet(argument));
4811 }
4812 emitter.instruction(Instruction::LocalGet(closure_local));
4813 emitter.instruction(Instruction::StructGet {
4814 struct_type_index: closure_struct_idx,
4815 field_index: 1,
4816 });
4817 emitter.instruction(Instruction::CallRef(fn_type_idx));
4818 let Type::Function { ret, .. } = callee_ty.peel() else {
4821 return Err(crate::codegen::internal_failure(
4822 "indirect call requires a function type",
4823 ));
4824 };
4825 let _: () = if !ret.is_void() {
4826 crate::codegen::cast_check::emit_checked_cast_on_stack(
4827 emitter,
4828 ctx,
4829 &Type::Unknown,
4830 result_ty,
4831 )?;
4832 };
4833 Ok(())
4834}
4835
4836fn emit_typeof_tag(
4839 emitter: &mut FunctionEmitter,
4840 ctx: &CodegenCtx,
4841 value: ExprId,
4842 tag: crate::TypeofTagKind,
4843) -> Result<(), crate::compiler_error::CompilerFailure> {
4844 let _: () = match tag {
4845 crate::TypeofTagKind::BigInt => {
4846 emit_expr(emitter, ctx, value)?;
4847 cast::emit_box(
4848 emitter,
4849 ctx,
4850 &ctx.ta
4851 .try_expr(value)
4852 .map_err(crate::codegen::arena_failure)?
4853 .ty,
4854 )?;
4855 let intrinsic = ctx
4856 .symbols
4857 .bigint_type_idx()
4858 .ok_or_else(|| crate::codegen::internal_failure("bigint intrinsic missing"))?;
4859 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(intrinsic)));
4860 }
4861 crate::TypeofTagKind::Undefined => {
4862 emit_expr(emitter, ctx, value)?;
4863 cast::emit_box(
4864 emitter,
4865 ctx,
4866 &ctx.ta
4867 .try_expr(value)
4868 .map_err(crate::codegen::arena_failure)?
4869 .ty,
4870 )?;
4871 emit_is_undefined(emitter, ctx)?;
4872 }
4873 crate::TypeofTagKind::Number => {
4874 let boxed = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
4880 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
4881 })?;
4882 emit_expr(emitter, ctx, value)?;
4883 cast::emit_box(
4884 emitter,
4885 ctx,
4886 &ctx.ta
4887 .try_expr(value)
4888 .map_err(crate::codegen::arena_failure)?
4889 .ty,
4890 )?;
4891 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
4892 }
4893 crate::TypeofTagKind::String => {
4894 let string = ctx.symbols.string_type_idx().ok_or_else(|| {
4895 crate::codegen::internal_failure("$string intrinsic type registered")
4896 })?;
4897 emit_expr(emitter, ctx, value)?;
4898 cast::emit_box(
4899 emitter,
4900 ctx,
4901 &ctx.ta
4902 .try_expr(value)
4903 .map_err(crate::codegen::arena_failure)?
4904 .ty,
4905 )?;
4906 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
4907 }
4908 crate::TypeofTagKind::Boolean => {
4909 let boxed = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
4910 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
4911 })?;
4912 emit_expr(emitter, ctx, value)?;
4913 cast::emit_box(
4914 emitter,
4915 ctx,
4916 &ctx.ta
4917 .try_expr(value)
4918 .map_err(crate::codegen::arena_failure)?
4919 .ty,
4920 )?;
4921 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
4922 }
4923 crate::TypeofTagKind::Function => {
4924 let closure_idx = ctx.symbols.closure_type_idx().ok_or_else(|| {
4925 crate::codegen::internal_failure("$Closure intrinsic type registered")
4926 })?;
4927 emit_expr(emitter, ctx, value)?;
4928 cast::emit_box(
4929 emitter,
4930 ctx,
4931 &ctx.ta
4932 .try_expr(value)
4933 .map_err(crate::codegen::arena_failure)?
4934 .ty,
4935 )?;
4936 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
4937 }
4938 crate::TypeofTagKind::Object => {
4939 let scratch_ty = ctx.symbols.value_type(&Type::Unknown)?;
4940 let nullable = matches!(scratch_ty, ValType::Ref(rt) if rt.nullable);
4941 let scratch = emitter.add_anonymous_local(scratch_ty)?;
4942 emit_expr(emitter, ctx, value)?;
4943 cast::emit_box(
4944 emitter,
4945 ctx,
4946 &ctx.ta
4947 .try_expr(value)
4948 .map_err(crate::codegen::arena_failure)?
4949 .ty,
4950 )?;
4951 emitter.instruction(Instruction::LocalSet(scratch));
4952
4953 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
4960 crate::codegen::internal_failure("intrinsics declared by codegen entry")
4961 })?;
4962 emitter.instruction(Instruction::LocalGet(scratch));
4963 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4964 intrinsics.object,
4965 )));
4966 for primitive in [
4967 intrinsics.string,
4968 intrinsics.boxed_number,
4969 intrinsics.boxed_boolean,
4970 intrinsics.bigint,
4971 intrinsics.closure,
4972 intrinsics.undefined,
4973 ] {
4974 emitter.instruction(Instruction::LocalGet(scratch));
4975 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(primitive)));
4976 emitter.instruction(Instruction::I32Eqz);
4977 emitter.instruction(Instruction::I32And);
4978 }
4979 if nullable {
4980 emitter.instruction(Instruction::LocalGet(scratch));
4985 emitter.instruction(Instruction::RefIsNull);
4986 emitter.instruction(Instruction::I32Or);
4987 }
4988 }
4989 };
4990 Ok(())
4991}
4992
4993fn has_numeric_operands(
4996 ctx: &CodegenCtx,
4997 operands: &[ExprId],
4998 result_ty: &Type,
4999) -> Result<bool, crate::compiler_error::CompilerFailure> {
5000 if !matches!(result_ty.peel(), Type::Number | Type::NumberLiteral(_)) {
5001 return Ok(false);
5002 }
5003 for &operand in operands {
5004 let ty = &ctx
5005 .ta
5006 .try_expr(operand)
5007 .map_err(crate::codegen::arena_failure)?
5008 .ty;
5009 if !matches!(
5010 ty.primitive_behavior(),
5011 Type::Number | Type::NumberLiteral(_)
5012 ) {
5013 return Ok(false);
5014 }
5015 }
5016 Ok(true)
5017}
5018
5019fn emit_number_bitwise(
5020 emitter: &mut FunctionEmitter,
5021 ctx: &CodegenCtx,
5022 op: BinOp,
5023 lhs: ExprId,
5024 rhs: ExprId,
5025) -> Result<(), crate::compiler_error::CompilerFailure> {
5026 emit_primitive_operand(emitter, ctx, lhs)?;
5027 emit_to_int32(emitter, ctx)?;
5028 emit_primitive_operand(emitter, ctx, rhs)?;
5029 emit_to_int32(emitter, ctx)?;
5030 let (instruction, unsigned) = match op {
5032 BinOp::BitAnd => (Instruction::I32And, false),
5033 BinOp::BitOr => (Instruction::I32Or, false),
5034 BinOp::BitXor => (Instruction::I32Xor, false),
5035 BinOp::Shl => (Instruction::I32Shl, false),
5036 BinOp::Shr => (Instruction::I32ShrS, false),
5037 BinOp::UnsignedShr => (Instruction::I32ShrU, true),
5038 _ => {
5039 return Err(crate::codegen::internal_failure(
5040 "a numeric bitwise operation has a bitwise operator",
5041 ));
5042 }
5043 };
5044 emitter.instruction(instruction);
5045 emitter.instruction(if unsigned {
5046 Instruction::F64ConvertI32U
5047 } else {
5048 Instruction::F64ConvertI32S
5049 });
5050 Ok(())
5051}
5052
5053fn emit_to_int32(
5057 emitter: &mut FunctionEmitter,
5058 ctx: &CodegenCtx,
5059) -> Result<(), crate::compiler_error::CompilerFailure> {
5060 let value = emitter.add_anonymous_local(ValType::F64)?;
5061 emitter.instruction(Instruction::LocalTee(value));
5062 emitter.instruction(Instruction::F64Abs);
5063 emitter.instruction(Instruction::F64Const(Ieee64::from(
5064 9_223_372_036_854_775_808.0,
5065 )));
5066 emitter.instruction(Instruction::F64Lt);
5067 emitter.emit_if(BlockType::Result(ValType::I32));
5068 emitter.instruction(Instruction::LocalGet(value));
5069 emitter.instruction(Instruction::I64TruncSatF64S);
5070 emitter.instruction(Instruction::I32WrapI64);
5071 emitter.emit_else();
5072 emitter.instruction(Instruction::LocalGet(value));
5073 cast::emit_box(emitter, ctx, &Type::Number)?;
5074 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
5075 cast::emit_box(emitter, ctx, &Type::Number)?;
5076 let host = ctx
5077 .symbols
5078 .func_idx(&crate::mangle::prelude("__value_bitor"))
5079 .ok_or_else(|| crate::codegen::internal_failure("missing bitwise host import: bitor"))?;
5080 emitter.instruction(Instruction::Call(host));
5081 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5082 emitter.instruction(Instruction::I32TruncF64S);
5083 emitter.emit_end();
5084 Ok(())
5085}
5086
5087fn emit_bitwise_host(
5088 emitter: &mut FunctionEmitter,
5089 ctx: &CodegenCtx,
5090 name: &str,
5091 operands: &[ExprId],
5092 result_ty: &Type,
5093) -> Result<(), crate::compiler_error::CompilerFailure> {
5094 for &operand in operands {
5095 emit_expr(emitter, ctx, operand)?;
5096 let ty = &ctx
5097 .ta
5098 .try_expr(operand)
5099 .map_err(crate::codegen::arena_failure)?
5100 .ty;
5101 cast::emit_box(emitter, ctx, ty)?;
5102 }
5103 let mangled = crate::mangle::prelude(&format!("__value_{name}"));
5104 let host = ctx.symbols.func_idx(&mangled).ok_or_else(|| {
5105 crate::codegen::internal_failure(format!("missing bitwise host import: {name}"))
5106 })?;
5107 emitter.instruction(Instruction::Call(host));
5108 cast::emit_cast_to(emitter, ctx, result_ty)
5109}
5110
5111fn emit_unary(
5112 emitter: &mut FunctionEmitter,
5113 ctx: &CodegenCtx,
5114 op: UnOp,
5115 operand: ExprId,
5116 result_ty: &Type,
5117) -> Result<(), crate::compiler_error::CompilerFailure> {
5118 if matches!(op, UnOp::Neg | UnOp::Pos)
5119 && try_emit_unreachable_for_never_operand(emitter, ctx, &[operand])?
5120 {
5121 return Ok(());
5122 }
5123 let _: () = match op {
5124 UnOp::BitNot if has_numeric_operands(ctx, &[operand], result_ty)? => {
5125 emit_primitive_operand(emitter, ctx, operand)?;
5126 emit_to_int32(emitter, ctx)?;
5127 emitter.instruction(Instruction::I32Const(-1));
5128 emitter.instruction(Instruction::I32Xor);
5129 emitter.instruction(Instruction::F64ConvertI32S);
5130 }
5131 UnOp::BitNot => emit_bitwise_host(emitter, ctx, "bitnot", &[operand], result_ty)?,
5132 UnOp::Neg => {
5133 if ctx
5135 .ta
5136 .try_expr(operand)
5137 .map_err(crate::codegen::arena_failure)?
5138 .ty
5139 .is_bigint()
5140 {
5141 emit_primitive_operand(emitter, ctx, operand)?;
5142 emit_bigint_extract_to_stack(emitter, ctx);
5143 let host_idx = ctx
5144 .symbols
5145 .func_idx(&crate::mangle::host(
5146 crate::runtime::BIGINT_MODULE_NAME,
5147 "neg",
5148 ))
5149 .ok_or_else(|| {
5150 crate::codegen::internal_failure(
5151 "submilli:bigint.neg import recorded by codegen bootstrap",
5152 )
5153 })?;
5154 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
5155 return Ok(());
5156 }
5157 emit_primitive_operand(emitter, ctx, operand)?;
5158 emitter.instruction(Instruction::F64Neg);
5159 }
5160 UnOp::Pos => {
5161 emit_primitive_operand(emitter, ctx, operand)?;
5165 if ctx
5166 .ta
5167 .try_expr(operand)
5168 .map_err(crate::codegen::arena_failure)?
5169 .ty
5170 .is_string_shaped()
5171 {
5172 let host_idx = ctx
5173 .symbols
5174 .func_idx(&crate::mangle::host(
5175 crate::runtime::NUMBER_MODULE_NAME,
5176 "toNumber",
5177 ))
5178 .ok_or_else(|| {
5179 crate::codegen::internal_failure(
5180 "submilli:number.toNumber import recorded by codegen analysis",
5181 )
5182 })?;
5183 emitter.instruction(Instruction::Call(host_idx));
5184 }
5185 }
5186 UnOp::Not => {
5187 let operand_ty = ctx
5193 .ta
5194 .try_expr(operand)
5195 .map_err(crate::codegen::arena_failure)?
5196 .ty
5197 .clone();
5198 emit_expr(emitter, ctx, operand)?;
5199 crate::codegen::function_emitter::cast::emit_condition_to_i32(
5200 emitter,
5201 ctx,
5202 &operand_ty,
5203 )?;
5204 emitter.instruction(Instruction::I32Eqz);
5205 }
5206 };
5207 Ok(())
5208}
5209
5210#[allow(clippy::too_many_arguments)]
5214pub(crate) fn emit_method_call(
5215 emitter: &mut FunctionEmitter,
5216 ctx: &CodegenCtx,
5217 receiver: ExprId,
5218 iface: &crate::MangledName,
5219 method: &str,
5220 args: &[ExprId],
5221 generic_args: Option<&[crate::GenericArgument]>,
5222 return_cast: Option<&Type>,
5223 call_ret_ty: &Type,
5224) -> Result<(), crate::compiler_error::CompilerFailure> {
5225 let recv_ty = ctx
5226 .ta
5227 .source_type(receiver)
5228 .map_err(crate::codegen::arena_failure)?
5229 .clone();
5230 if !emit_dropped_static_receiver(emitter, ctx, receiver, iface)? {
5231 emit_receiver(emitter, ctx, receiver)?;
5232 }
5233 emit_method_call_with_receiver_on_stack(
5234 emitter,
5235 ctx,
5236 &recv_ty,
5237 iface,
5238 method,
5239 args,
5240 generic_args,
5241 return_cast,
5242 call_ret_ty,
5243 )?;
5244 Ok(())
5245}
5246
5247fn emit_dropped_static_receiver(
5251 emitter: &mut FunctionEmitter,
5252 ctx: &CodegenCtx,
5253 receiver: ExprId,
5254 iface: &crate::MangledName,
5255) -> Result<bool, crate::compiler_error::CompilerFailure> {
5256 let expr = ctx
5257 .ta
5258 .try_expr(receiver)
5259 .map_err(crate::codegen::arena_failure)?;
5260 if !matches!(expr.kind, TypedExprKind::GlobalRef { .. })
5261 || static_interface(ctx, &expr.ty) != Some(iface)
5262 {
5263 return Ok(false);
5264 }
5265 match ctx.symbols.value_type(&expr.ty)? {
5266 ValType::Ref(RefType { heap_type, .. }) => {
5267 emitter.instruction(Instruction::RefNull(heap_type));
5268 }
5269 other => {
5270 return Err(crate::codegen::internal_failure(format!(
5271 "static-interface binding lowered to {other:?}"
5272 )));
5273 }
5274 }
5275 Ok(true)
5276}
5277
5278#[allow(clippy::too_many_arguments)]
5285fn emit_method_call_with_receiver_on_stack(
5286 emitter: &mut FunctionEmitter,
5287 ctx: &CodegenCtx,
5288 recv_ty: &Type,
5289 iface: &crate::MangledName,
5290 method: &str,
5291 args: &[ExprId],
5292 generic_args: Option<&[crate::GenericArgument]>,
5293 return_cast: Option<&Type>,
5294 call_ret_ty: &Type,
5295) -> Result<(), crate::compiler_error::CompilerFailure> {
5296 let concrete_receiver = crate::typechecker::infer::narrowing::strip_nullish(recv_ty);
5297 crate::codegen::field_guards::attach(emitter, ctx, &concrete_receiver)?;
5298 let direct_key = crate::mangle::extend(iface, method);
5299 if let Some(func_idx) = ctx.symbols.func_idx(&direct_key) {
5300 if matches!(recv_ty.primitive_behavior(), Type::Number)
5301 && ctx.symbols.value_type(recv_ty)? != ValType::F64
5302 {
5303 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5304 }
5305 match ctx.symbols.iface_dispatch(iface) {
5314 Some(crate::Dispatch::Direct) => {
5315 if matches!(recv_ty.peel(), Type::InterfaceRef { .. }) {
5327 emitter.instruction(Instruction::RefAsNonNull);
5328 }
5329 }
5330 Some(crate::Dispatch::Static) => {
5331 emitter.instruction(Instruction::Drop);
5332 }
5333 None | Some(crate::Dispatch::VTable) => {}
5338 }
5339 let abi = wrapper_abi_for_call(
5349 ctx,
5350 &direct_key,
5351 args,
5352 generic_args,
5353 return_cast,
5354 call_ret_ty,
5355 )?;
5356 let defaults = ctx.symbols.host_call_defaults(func_idx, args.len());
5357 emit_args_into_slots_with_defaults(emitter, ctx, args, Some(&abi.params), defaults)?;
5358 emitter.instruction(Instruction::Call(func_idx));
5359 emit_slot_return_cast(emitter, ctx, call_ret_ty, Some(&abi))?;
5360 return Ok(());
5361 }
5362 if ctx.symbols.is_rewritable_class_method(iface, method) {
5365 emit_interface_method_via_shape_with_receiver_on_stack(
5366 emitter,
5367 ctx,
5368 method,
5369 args,
5370 call_ret_ty,
5371 None,
5372 )?;
5373 return Ok(());
5374 }
5375 if ctx.symbols.class_method_slot(iface, method).is_some() {
5380 emit_class_vtable_dispatch(emitter, ctx, iface, method, args, call_ret_ty)?;
5381 return Ok(());
5382 }
5383 if let Some(slot) = vtable_slot_for_method(method) {
5384 emit_vtable_dispatch_on_object_stack(emitter, ctx, slot);
5385 return Ok(());
5386 }
5387 if matches!(recv_ty.peel(), Type::InterfaceRef { .. }) {
5396 emit_interface_method_via_shape_with_receiver_on_stack(
5397 emitter,
5398 ctx,
5399 method,
5400 args,
5401 call_ret_ty,
5402 None,
5403 )?;
5404 return Ok(());
5405 }
5406 Err(crate::codegen::internal_failure(format!(
5407 "method dispatch was not registered for `{iface}.{method}`"
5408 )))
5409}
5410
5411fn emit_class_method_value(
5412 emitter: &mut FunctionEmitter,
5413 ctx: &CodegenCtx,
5414 receiver_ty: &Type,
5415 method: &str,
5416 result_ty: &Type,
5417 check_ty: &Type,
5418) -> Result<(), crate::compiler_error::CompilerFailure> {
5419 let object_shape = ctx
5420 .symbols
5421 .intrinsic_type_indices()
5422 .ok_or_else(|| {
5423 crate::codegen::internal_failure("class method value requires object shape intrinsic")
5424 })?
5425 .object_shape;
5426 let receiver = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
5427 emit_object_property_read_as(emitter, ctx, receiver, method, result_ty, check_ty)
5428}
5429
5430pub(in crate::codegen) fn emit_interface_method_via_shape_with_receiver_on_stack(
5446 emitter: &mut FunctionEmitter,
5447 ctx: &CodegenCtx,
5448 method: &str,
5449 args: &[ExprId],
5450 call_ret_ty: &Type,
5451 slot_index_local: Option<u32>,
5452) -> Result<(), crate::compiler_error::CompilerFailure> {
5453 let args: Vec<_> = args
5454 .iter()
5455 .copied()
5456 .map(ShapeArgument::Expression)
5457 .collect();
5458 emit_shape_method_with_arguments(emitter, ctx, method, &args, call_ret_ty, slot_index_local)?;
5459 Ok(())
5460}
5461
5462enum ShapeArgument {
5463 Expression(ExprId),
5464 Local { slot: u32, ty: Type },
5465}
5466
5467impl ShapeArgument {
5468 fn ty(&self, ctx: &CodegenCtx) -> Result<Type, crate::compiler_error::CompilerFailure> {
5469 Ok(match self {
5470 Self::Expression(id) => ctx
5471 .ta
5472 .try_expr(*id)
5473 .map_err(crate::codegen::arena_failure)?
5474 .ty
5475 .clone(),
5476 Self::Local { ty, .. } => ty.clone(),
5477 })
5478 }
5479
5480 fn emit(
5481 &self,
5482 emitter: &mut FunctionEmitter,
5483 ctx: &CodegenCtx,
5484 ) -> Result<(), crate::compiler_error::CompilerFailure> {
5485 let _: () = match self {
5486 Self::Expression(id) => emit_expr(emitter, ctx, *id)?,
5487 Self::Local { slot, .. } => {
5488 emitter.instruction(Instruction::LocalGet(*slot));
5489 }
5490 };
5491 Ok(())
5492 }
5493}
5494
5495fn emit_shape_method_with_arguments(
5496 emitter: &mut FunctionEmitter,
5497 ctx: &CodegenCtx,
5498 method: &str,
5499 args: &[ShapeArgument],
5500 call_ret_ty: &Type,
5501 slot_index_local: Option<u32>,
5502) -> Result<(), crate::compiler_error::CompilerFailure> {
5503 let intrinsics = ctx
5504 .symbols
5505 .intrinsic_type_indices()
5506 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5507
5508 let arg_tys: Vec<Type> = args.iter().map(|a| a.ty(ctx)).collect::<Result<_, _>>()?;
5511 let fn_ty = Type::Function {
5512 params: arg_tys,
5513 ret: Box::new(call_ret_ty.clone()),
5514 predicate: None,
5515 has_rest: false,
5519 optional: 0,
5520 };
5521 let closure_sig = crate::codegen::closures::classify(&fn_ty)?;
5522 let closure_struct_idx = ctx
5523 .symbols
5524 .closure_struct_type_idx(closure_sig)
5525 .ok_or_else(|| {
5526 crate::codegen::internal_failure(
5527 "closure struct registered for the method's signature (SUB-166)",
5528 )
5529 })?;
5530 let fn_type_idx = ctx
5531 .symbols
5532 .closure_func_type_idx(closure_sig)
5533 .ok_or_else(|| {
5534 crate::codegen::internal_failure(
5535 "closure funcref type registered for the method's signature",
5536 )
5537 })?;
5538
5539 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5540 nullable: false,
5541 heap_type: HeapType::Concrete(intrinsics.object_shape),
5542 }))?;
5543 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5544 nullable: false,
5545 heap_type: HeapType::Concrete(closure_struct_idx),
5546 }))?;
5547
5548 emitter.instruction(Instruction::RefAsNonNull);
5552 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
5553 intrinsics.object_shape,
5554 )));
5555 emitter.instruction(Instruction::LocalSet(rcv_local));
5556
5557 if let Some(index_local) = slot_index_local {
5560 emit_field_slot_get(emitter, intrinsics, rcv_local, index_local);
5561 } else {
5562 let name_global = ctx
5563 .symbols
5564 .field_name_string_global_idx(method)
5565 .ok_or_else(|| {
5566 crate::codegen::internal_failure(
5567 ("per-name string global recorded during field-name-strings emission \
5568 for interface method names")
5569 .to_string(),
5570 )
5571 })?;
5572 emit_object_field_read_by_name(emitter, ctx, rcv_local, name_global);
5573 }
5574
5575 let method_value = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5576 emitter.instruction(Instruction::LocalSet(method_value));
5577 let mut evaluated_args = Vec::with_capacity(args.len());
5578 for arg in args {
5579 arg.emit(emitter, ctx)?;
5580 cast::emit_box(emitter, ctx, &arg.ty(ctx)?)?;
5581 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5582 emitter.instruction(Instruction::LocalSet(slot));
5583 evaluated_args.push(slot);
5584 }
5585 emitter.instruction(Instruction::LocalGet(method_value));
5586 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5587 intrinsics.closure,
5588 )));
5589 emitter.instruction(Instruction::I32Eqz);
5590 emitter.emit_if(BlockType::Empty);
5591 crate::codegen::throw::emit_type_error_throw(emitter, ctx, "Value is not callable");
5592 emitter.emit_end();
5593 emitter.instruction(Instruction::LocalGet(method_value));
5594
5595 crate::codegen::closure_coercions::emit_erased_cast(
5599 emitter,
5600 ctx,
5601 crate::codegen::closures::ClosureSig::of(args.len(), call_ret_ty)?,
5602 )?;
5603 emitter.instruction(Instruction::LocalTee(closure_local));
5604
5605 emitter.instruction(Instruction::StructGet {
5607 struct_type_index: closure_struct_idx,
5608 field_index: 2,
5609 });
5610
5611 crate::codegen::this_binding::bind(emitter, ctx, rcv_local)?;
5612 for slot in evaluated_args {
5615 emitter.instruction(Instruction::LocalGet(slot));
5616 }
5617
5618 emitter.instruction(Instruction::LocalGet(closure_local));
5620 emitter.instruction(Instruction::StructGet {
5621 struct_type_index: closure_struct_idx,
5622 field_index: 1,
5623 });
5624 emitter.instruction(Instruction::CallRef(fn_type_idx));
5625
5626 let _: () = if !call_ret_ty.is_void() {
5629 crate::codegen::cast_check::emit_checked_cast_on_stack(
5630 emitter,
5631 ctx,
5632 &Type::Unknown,
5633 call_ret_ty,
5634 )?;
5635 };
5636 Ok(())
5637}
5638
5639fn vtable_slot_for_method(method: &str) -> Option<u32> {
5650 match method {
5651 "toString" => Some(0),
5652 "toJson" => Some(1),
5653 "equals" => Some(2),
5654 "hash" => Some(3),
5655 _ => None,
5656 }
5657}
5658
5659pub(super) fn emit_vtable_dispatch_on_object_stack(
5670 emitter: &mut FunctionEmitter,
5671 ctx: &CodegenCtx,
5672 slot: u32,
5673) {
5674 let Some(intrinsics) = ctx.require(
5675 ctx.symbols.intrinsic_type_indices(),
5676 "intrinsics declared by codegen entry",
5677 ) else {
5678 return;
5679 };
5680 let fn_type_idx = match slot {
5685 0 => intrinsics.to_string_fn,
5686 1 => intrinsics.to_json_fn,
5687 _ => {
5688 ctx.fail("vtable string dispatch requires toString or toJson slot");
5689 return;
5690 }
5691 };
5692 let Some(obj_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5693 nullable: false,
5694 heap_type: HeapType::Concrete(intrinsics.object),
5695 }))) else {
5696 return;
5697 };
5698 let Some(fn_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5699 nullable: false,
5700 heap_type: HeapType::Concrete(fn_type_idx),
5701 }))) else {
5702 return;
5703 };
5704 emitter.instruction(Instruction::RefAsNonNull);
5710 emitter.instruction(Instruction::LocalTee(obj_local));
5711 emitter.instruction(Instruction::StructGet {
5712 struct_type_index: intrinsics.object,
5713 field_index: 0,
5714 });
5715 emitter.instruction(Instruction::StructGet {
5716 struct_type_index: intrinsics.vtable,
5717 field_index: slot,
5718 });
5719 emitter.instruction(Instruction::LocalSet(fn_local));
5720 emitter.instruction(Instruction::LocalGet(obj_local));
5721 emitter.instruction(Instruction::LocalGet(fn_local));
5722 emitter.instruction(Instruction::CallRef(fn_type_idx));
5723}
5724
5725fn emit_args_into_slots(
5729 emitter: &mut FunctionEmitter,
5730 ctx: &CodegenCtx,
5731 args: &[ExprId],
5732 slots: Option<&[ValType]>,
5733) -> Result<(), crate::compiler_error::CompilerFailure> {
5734 emit_args_into_slots_with_defaults(emitter, ctx, args, slots, &[])
5735}
5736
5737fn emit_args_into_slots_with_defaults(
5738 emitter: &mut FunctionEmitter,
5739 ctx: &CodegenCtx,
5740 args: &[ExprId],
5741 slots: Option<&[ValType]>,
5742 defaults: &[Option<crate::DefaultValue>],
5743) -> Result<(), crate::compiler_error::CompilerFailure> {
5744 if slots.is_some_and(|slots| slots.len() != args.len()) {
5745 return Err(crate::codegen::internal_failure(
5746 "call ABI argument count mismatch",
5747 ));
5748 }
5749 let locals: Vec<_> = args
5750 .iter()
5751 .map(|&arg| {
5752 emit_expr(emitter, ctx, arg)?;
5753 let local = emitter.add_anonymous_local(
5754 ctx.symbols.value_type(
5755 &ctx.ta
5756 .try_expr(arg)
5757 .map_err(crate::codegen::arena_failure)?
5758 .ty,
5759 )?,
5760 )?;
5761 emitter.instruction(Instruction::LocalSet(local));
5762 Ok::<_, crate::compiler_error::CompilerFailure>(local)
5763 })
5764 .collect::<Result<_, _>>()?;
5765 for (i, (&arg, local)) in args.iter().zip(locals).enumerate() {
5766 emitter.instruction(Instruction::LocalGet(local));
5767 if let Some(slot) = slots.and_then(|s| s.get(i).copied()) {
5768 crate::codegen::argument_defaults::emit_argument(
5769 emitter,
5770 ctx,
5771 &ctx.ta
5772 .try_expr(arg)
5773 .map_err(crate::codegen::arena_failure)?
5774 .ty,
5775 slot,
5776 defaults.get(i).and_then(Option::as_ref),
5777 )?;
5778 }
5779 }
5780 Ok(())
5781}
5782
5783fn wrapper_abi_for_call(
5798 ctx: &CodegenCtx,
5799 direct_key: &crate::MangledName,
5800 args: &[ExprId],
5801 generic_args: Option<&[crate::GenericArgument]>,
5802 return_cast: Option<&Type>,
5803 call_ret_ty: &Type,
5804) -> Result<MethodSlotAbi, crate::compiler_error::CompilerFailure> {
5805 if let Some(recorded) = ctx.symbols.iface_method_abi(direct_key) {
5806 return Ok(recorded.clone());
5807 }
5808 let erased = ctx.symbols.value_type(&Type::Unknown)?;
5809 let slot_is_erased =
5810 |i: usize| generic_args.is_some_and(|flags| flags.get(i).is_some_and(|g| g.is_generic));
5811 Ok(MethodSlotAbi {
5812 params: args
5813 .iter()
5814 .enumerate()
5815 .map(|(i, &a)| {
5816 Ok::<_, crate::compiler_error::CompilerFailure>(if slot_is_erased(i) {
5817 erased
5818 } else {
5819 ctx.symbols.value_type(
5820 &ctx.ta
5821 .try_expr(a)
5822 .map_err(crate::codegen::arena_failure)?
5823 .ty,
5824 )?
5825 })
5826 })
5827 .collect::<Result<_, _>>()?,
5828 ret: match return_cast {
5829 Some(_) => Some(erased),
5830 None => (!call_ret_ty.is_void())
5831 .then(|| ctx.symbols.value_type(call_ret_ty))
5832 .transpose()?,
5833 },
5834 })
5835}
5836
5837fn emit_slot_return_cast(
5840 emitter: &mut FunctionEmitter,
5841 ctx: &CodegenCtx,
5842 call_ret_ty: &Type,
5843 abi: Option<&MethodSlotAbi>,
5844) -> Result<(), crate::compiler_error::CompilerFailure> {
5845 if matches!(call_ret_ty.peel(), Type::Never) {
5846 emitter.instruction(Instruction::Unreachable);
5847 return Ok(());
5848 }
5849 if abi.is_some_and(|abi| abi.ret.is_none()) {
5852 emit_undefined(emitter, ctx)?;
5853 }
5854 if call_ret_ty.is_void() {
5855 return Ok(());
5856 }
5857 if matches!(call_ret_ty, Type::Unknown) {
5858 match abi.and_then(|abi| abi.ret) {
5859 Some(ValType::F64) => cast::emit_box(emitter, ctx, &Type::Number)?,
5860 Some(ValType::I32) => cast::emit_box(emitter, ctx, &Type::Boolean)?,
5861 _ => {}
5862 }
5863 return Ok(());
5864 }
5865 if abi.and_then(|a| a.ret) != Some(ctx.symbols.value_type(call_ret_ty)?) {
5866 crate::codegen::cast_check::emit_checked_cast_on_stack(
5867 emitter,
5868 ctx,
5869 &Type::Unknown,
5870 call_ret_ty,
5871 )?;
5872 }
5873 crate::codegen::field_guards::attach(emitter, ctx, call_ret_ty)?;
5874 Ok(())
5875}
5876
5877fn emit_class_vtable_dispatch(
5890 emitter: &mut FunctionEmitter,
5891 ctx: &CodegenCtx,
5892 class: &crate::MangledName,
5893 method: &str,
5894 args: &[ExprId],
5895 call_ret_ty: &Type,
5896) -> Result<(), crate::compiler_error::CompilerFailure> {
5897 let struct_idx = ctx
5898 .symbols
5899 .class_struct_type_idx(class)
5900 .ok_or_else(|| crate::codegen::internal_failure("class struct type recorded"))?;
5901 let vtable_idx = ctx
5902 .symbols
5903 .class_vtable_type_idx(class)
5904 .ok_or_else(|| crate::codegen::internal_failure("class vtable type recorded"))?;
5905 let slot = ctx
5906 .symbols
5907 .class_method_slot(class, method)
5908 .ok_or_else(|| crate::codegen::internal_failure("class method slot recorded"))?;
5909 let sig_idx = ctx
5910 .symbols
5911 .class_method_sig(class, method)
5912 .ok_or_else(|| crate::codegen::internal_failure("class method sig recorded"))?;
5913
5914 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5915 nullable: false,
5916 heap_type: HeapType::Concrete(struct_idx),
5917 }))?;
5918 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
5921 emitter.instruction(Instruction::LocalSet(rcv_local));
5922
5923 emitter.instruction(Instruction::LocalGet(rcv_local));
5925 emitter.instruction(Instruction::StructGet {
5926 struct_type_index: struct_idx,
5927 field_index: 0,
5928 });
5929 emitter.instruction(Instruction::StructGet {
5930 struct_type_index: vtable_idx,
5931 field_index: slot,
5932 });
5933 let fn_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5934 nullable: false,
5935 heap_type: HeapType::Concrete(sig_idx),
5936 }))?;
5937 emitter.instruction(Instruction::LocalSet(fn_local));
5938
5939 let abi = ctx.symbols.class_method_abi(class, method).cloned();
5941 emitter.instruction(Instruction::LocalGet(rcv_local));
5942 emit_args_into_slots(
5943 emitter,
5944 ctx,
5945 args,
5946 abi.as_ref().map(|a| a.params.as_slice()),
5947 )?;
5948 emitter.instruction(Instruction::LocalGet(fn_local));
5949 emitter.instruction(Instruction::CallRef(sig_idx));
5950 emit_slot_return_cast(emitter, ctx, call_ret_ty, abi.as_ref())?;
5951 Ok(())
5952}
5953
5954fn emit_intrinsic_call(
5955 emitter: &mut FunctionEmitter,
5956 ctx: &CodegenCtx,
5957 kind: Intrinsic,
5958 args: &[ExprId],
5959 result_ty: &Type,
5960) -> Result<(), crate::compiler_error::CompilerFailure> {
5961 let _: () = match kind {
5962 Intrinsic::Assert => {
5963 if args.len() != 2 {
5967 return Err(crate::codegen::internal_failure(
5968 "assert requires two arguments",
5969 ));
5970 }
5971 emit_expr(emitter, ctx, args[0])?;
5972 cast::emit_condition_to_i32(
5973 emitter,
5974 ctx,
5975 &ctx.ta
5976 .try_expr(args[0])
5977 .map_err(crate::codegen::arena_failure)?
5978 .ty,
5979 )?;
5980 emit_expr(emitter, ctx, args[1])?;
5981 let message_type = &ctx
5982 .ta
5983 .try_expr(args[1])
5984 .map_err(crate::codegen::arena_failure)?
5985 .ty;
5986 let msg_local = emitter.add_anonymous_local(ctx.symbols.value_type(message_type)?)?;
5987 emitter.instruction(Instruction::LocalSet(msg_local));
5988 emitter.instruction(Instruction::I32Eqz);
5989 emitter.emit_if(BlockType::Empty);
5990 emitter.instruction(Instruction::LocalGet(msg_local));
5991 cast::emit_coerce_to_slot(emitter, ctx, message_type, &Type::String)?;
5992 let ctor_idx = ctx
5993 .symbols
5994 .func_idx(&crate::mangle::prelude("Error#constructor"))
5995 .ok_or_else(|| {
5996 crate::codegen::internal_failure("Error#constructor exported from prelude")
5997 })?;
5998 emitter.instruction(Instruction::Call(ctor_idx));
5999 crate::codegen::throw::emit_error_throw(emitter, ctx);
6000 emitter.emit_end();
6001 emit_undefined(emitter, ctx)?;
6002 }
6003 Intrinsic::JsonStringify => {
6004 super::json::emit_stringify(emitter, ctx, args, result_ty)?;
6005 }
6006 Intrinsic::JsonParse => {
6007 super::json::emit_parse(emitter, ctx, args)?;
6008 }
6009 Intrinsic::ToString => {
6010 let [value] = args else {
6011 return Err(crate::codegen::internal_failure(
6012 "string conversion requires one argument",
6013 ));
6014 };
6015 emit_expr(emitter, ctx, *value)?;
6016 cast::emit_box(
6017 emitter,
6018 ctx,
6019 &ctx.ta
6020 .try_expr(*value)
6021 .map_err(crate::codegen::arena_failure)?
6022 .ty,
6023 )?;
6024 let function = ctx
6025 .symbols
6026 .prelude_func_idx("__value_to_string")
6027 .ok_or_else(|| {
6028 crate::codegen::internal_failure("dynamic string conversion collected")
6029 })?;
6030 emitter.instruction(Instruction::Call(function));
6031 }
6032 Intrinsic::BigIntFromString => {
6039 if args.len() != 1 {
6040 return Err(crate::codegen::internal_failure(
6041 "BigInt.fromString requires one argument",
6042 ));
6043 }
6044 emit_expr(emitter, ctx, args[0])?;
6045 let string_type_idx = ctx
6048 .symbols
6049 .string_type_idx()
6050 .ok_or_else(|| crate::codegen::internal_failure("$string registered"))?;
6051 emitter.instruction(Instruction::StructGet {
6052 struct_type_index: string_type_idx,
6053 field_index: 1,
6054 });
6055 let host_idx = ctx
6056 .symbols
6057 .func_idx(&crate::mangle::host(
6058 crate::runtime::BIGINT_MODULE_NAME,
6059 "fromString",
6060 ))
6061 .ok_or_else(|| {
6062 crate::codegen::internal_failure(
6063 "submilli:bigint.fromString import recorded by codegen bootstrap",
6064 )
6065 })?;
6066 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6067 }
6068 };
6069 Ok(())
6070}
6071
6072fn emit_vtable_equality(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: BinOp) {
6077 ctx.latch(emit_vtable_equality_checked(emitter, ctx, op));
6078}
6079
6080fn emit_vtable_equality_checked(
6081 emitter: &mut FunctionEmitter,
6082 ctx: &CodegenCtx,
6083 op: BinOp,
6084) -> Result<(), crate::compiler_error::CompilerFailure> {
6085 let intrinsics = ctx
6086 .symbols
6087 .intrinsic_type_indices()
6088 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6089 let operand_ref = ValType::Ref(RefType {
6092 nullable: true,
6093 heap_type: HeapType::Concrete(intrinsics.object),
6094 });
6095 let object_ref = ValType::Ref(RefType {
6096 nullable: false,
6097 heap_type: HeapType::Concrete(intrinsics.object),
6098 });
6099 let equals_fn_ref = ValType::Ref(RefType {
6100 nullable: false,
6101 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6102 });
6103
6104 let Some(lhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6106 return Ok(());
6107 };
6108 let Some(rhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6109 return Ok(());
6110 };
6111 let Some(lhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6112 return Ok(());
6113 };
6114 let Some(rhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6115 return Ok(());
6116 };
6117 let Some(eq_fn_local) = ctx.latch(emitter.add_anonymous_local(equals_fn_ref)) else {
6118 return Ok(());
6119 };
6120
6121 emitter.instruction(Instruction::LocalSet(rhs_local));
6122 emitter.instruction(Instruction::LocalSet(lhs_local));
6123
6124 emitter.instruction(Instruction::LocalGet(lhs_local));
6125 emitter.instruction(Instruction::RefAsNonNull);
6126 emitter.instruction(Instruction::LocalSet(lhs_object));
6127 emitter.instruction(Instruction::LocalGet(rhs_local));
6128 emitter.instruction(Instruction::RefAsNonNull);
6129 emitter.instruction(Instruction::LocalSet(rhs_object));
6130
6131 emitter.instruction(Instruction::LocalGet(lhs_object));
6132 emitter.instruction(Instruction::StructGet {
6133 struct_type_index: intrinsics.object,
6134 field_index: 0,
6135 });
6136 emitter.instruction(Instruction::StructGet {
6137 struct_type_index: intrinsics.vtable,
6138 field_index: 2,
6139 });
6140 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6141
6142 emitter.instruction(Instruction::LocalGet(lhs_object));
6143 emitter.instruction(Instruction::LocalGet(rhs_object));
6144 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6145 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6146
6147 if matches!(op, BinOp::NotEq) {
6148 emitter.instruction(Instruction::I32Eqz);
6149 }
6150
6151 Ok(())
6152}
6153
6154fn emit_boxed_eq(
6167 emitter: &mut FunctionEmitter,
6168 ctx: &CodegenCtx,
6169 lhs: ExprId,
6170 rhs: ExprId,
6171 op: BinOp,
6172) -> Result<(), crate::compiler_error::CompilerFailure> {
6173 let intrinsics = ctx
6174 .symbols
6175 .intrinsic_type_indices()
6176 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6177 let object_null_ref = ValType::Ref(RefType {
6178 nullable: true,
6179 heap_type: HeapType::Concrete(intrinsics.object),
6180 });
6181 let object_ref = ValType::Ref(RefType {
6182 nullable: false,
6183 heap_type: HeapType::Concrete(intrinsics.object),
6184 });
6185 let equals_fn_ref = ValType::Ref(RefType {
6186 nullable: false,
6187 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6188 });
6189
6190 let lhs_ty = ctx
6191 .ta
6192 .try_expr(lhs)
6193 .map_err(crate::codegen::arena_failure)?
6194 .ty
6195 .clone();
6196 let rhs_ty = ctx
6197 .ta
6198 .try_expr(rhs)
6199 .map_err(crate::codegen::arena_failure)?
6200 .ty
6201 .clone();
6202
6203 let lhs_local = emitter.add_anonymous_local(object_null_ref)?;
6208 let rhs_local = emitter.add_anonymous_local(object_null_ref)?;
6209 let lhs_nonnull_local = emitter.add_anonymous_local(object_ref)?;
6210 let eq_fn_local = emitter.add_anonymous_local(equals_fn_ref)?;
6211
6212 emit_expr(emitter, ctx, lhs)?;
6213 cast::emit_box(emitter, ctx, &lhs_ty)?;
6214 emitter.instruction(Instruction::LocalSet(lhs_local));
6215
6216 emit_expr(emitter, ctx, rhs)?;
6217 cast::emit_box(emitter, ctx, &rhs_ty)?;
6218 emitter.instruction(Instruction::LocalSet(rhs_local));
6219
6220 emitter.instruction(Instruction::LocalGet(lhs_local));
6224 emitter.instruction(Instruction::RefIsNull);
6225 emitter.emit_if(BlockType::Result(ValType::I32));
6226 emitter.instruction(Instruction::LocalGet(rhs_local));
6227 emitter.instruction(Instruction::RefIsNull);
6228 emitter.emit_else();
6229 emitter.instruction(Instruction::LocalGet(rhs_local));
6230 emitter.instruction(Instruction::RefIsNull);
6231 emitter.emit_if(BlockType::Result(ValType::I32));
6232 emitter.instruction(Instruction::I32Const(0));
6233 emitter.emit_else();
6234 emitter.instruction(Instruction::LocalGet(lhs_local));
6236 emitter.instruction(Instruction::RefAsNonNull);
6237 emitter.instruction(Instruction::LocalTee(lhs_nonnull_local));
6238 emitter.instruction(Instruction::StructGet {
6239 struct_type_index: intrinsics.object,
6240 field_index: 0,
6241 });
6242 emitter.instruction(Instruction::StructGet {
6243 struct_type_index: intrinsics.vtable,
6244 field_index: 2,
6245 });
6246 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6247 emitter.instruction(Instruction::LocalGet(lhs_nonnull_local));
6248 emitter.instruction(Instruction::LocalGet(rhs_local));
6249 emitter.instruction(Instruction::RefAsNonNull);
6250 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6251 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6252 emitter.emit_end();
6253 emitter.emit_end();
6254
6255 let _: () = if matches!(op, BinOp::NotEq) {
6256 emitter.instruction(Instruction::I32Eqz);
6257 };
6258 Ok(())
6259}
6260
6261fn emit_bigint_literal(
6267 emitter: &mut FunctionEmitter,
6268 ctx: &CodegenCtx,
6269 digits: &str,
6270) -> Result<(), crate::compiler_error::CompilerFailure> {
6271 use crate::codegen::internal_failure;
6272 if let Some(entry) = ctx.bigints.lookup(digits)? {
6273 let raw_bigint_idx = ctx
6278 .symbols
6279 .raw_bigint_type_idx()
6280 .ok_or_else(|| internal_failure("the $rawBigInt intrinsic is not registered"))?;
6281 let bigint_idx = ctx
6282 .symbols
6283 .bigint_type_idx()
6284 .ok_or_else(|| internal_failure("the $bigint intrinsic is not registered"))?;
6285 let vtable_global = ctx
6286 .symbols
6287 .prelude_global_idx("bigint_vtable")
6288 .ok_or_else(|| internal_failure("bigint_vtable is not imported from the prelude"))?;
6289 let data_idx = crate::codegen::wasm_u32(ctx.strings.strings.len())?
6290 .checked_add(entry.data_idx)
6291 .ok_or_else(|| internal_failure("the module has too many data segments"))?;
6292 emitter.instruction(Instruction::GlobalGet(vtable_global));
6293 emitter.instruction(Instruction::I32Const(i32::from(entry.sign)));
6294 emitter.instruction(Instruction::I32Const(0));
6295 emitter.instruction(Instruction::I32Const(entry.limb_count.cast_signed()));
6296 emitter.instruction(Instruction::ArrayNewData {
6297 array_type_index: raw_bigint_idx,
6298 array_data_index: data_idx,
6299 });
6300 emitter.instruction(Instruction::StructNew(bigint_idx));
6301 return Ok(());
6302 }
6303 let v: i64 = digits
6307 .parse()
6308 .map_err(|_| internal_failure("an unpooled bigint literal is not a safe integer"))?;
6309 let host_idx = ctx
6310 .symbols
6311 .func_idx(&crate::mangle::host(
6312 crate::runtime::BIGINT_MODULE_NAME,
6313 "fromNumber",
6314 ))
6315 .ok_or_else(|| internal_failure("submilli:bigint.fromNumber is not imported"))?;
6316 emitter.instruction(Instruction::F64Const(Ieee64::from(v as f64)));
6317 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6318 Ok(())
6319}
6320
6321fn emit_regex_literal(
6333 emitter: &mut FunctionEmitter,
6334 ctx: &CodegenCtx,
6335 source: &str,
6336 flags: &str,
6337) -> Result<(), crate::compiler_error::CompilerFailure> {
6338 super::emit_const_string_by_text(emitter, ctx, source)?;
6339 super::emit_const_string_by_text(emitter, ctx, flags)?;
6340 let ctor_idx = ctx
6341 .symbols
6342 .func_idx(&crate::mangle::prelude("RegExpConstructor#new"))
6343 .ok_or_else(|| crate::codegen::internal_failure("RegExpConstructor#new is not imported"))?;
6344 emitter.instruction(Instruction::Call(ctor_idx));
6345 Ok(())
6346}
6347
6348fn emit_bigint_extract_to_stack(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
6354 ctx.latch(emit_bigint_extract_to_stack_checked(emitter, ctx));
6355}
6356
6357fn emit_bigint_extract_to_stack_checked(
6358 emitter: &mut FunctionEmitter,
6359 ctx: &CodegenCtx,
6360) -> Result<(), crate::compiler_error::CompilerFailure> {
6361 let bigint_idx = ctx
6362 .symbols
6363 .bigint_type_idx()
6364 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6365 let Some(scratch) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6368 wasm_encoder::RefType {
6369 nullable: false,
6370 heap_type: wasm_encoder::HeapType::Concrete(bigint_idx),
6371 },
6372 ))) else {
6373 return Ok(());
6374 };
6375 emitter.instruction(Instruction::LocalSet(scratch));
6376 emitter.instruction(Instruction::LocalGet(scratch));
6377 emitter.instruction(Instruction::StructGet {
6378 struct_type_index: bigint_idx,
6379 field_index: 1, });
6381 emitter.instruction(Instruction::LocalGet(scratch));
6382 emitter.instruction(Instruction::StructGet {
6383 struct_type_index: bigint_idx,
6384 field_index: 2, });
6386
6387 Ok(())
6388}
6389
6390fn emit_bigint_wrap_host_result(
6395 emitter: &mut FunctionEmitter,
6396 ctx: &CodegenCtx,
6397 host_func_idx: u32,
6398) {
6399 ctx.latch(emit_bigint_wrap_host_result_checked(
6400 emitter,
6401 ctx,
6402 host_func_idx,
6403 ));
6404}
6405
6406fn emit_bigint_wrap_host_result_checked(
6407 emitter: &mut FunctionEmitter,
6408 ctx: &CodegenCtx,
6409 host_func_idx: u32,
6410) -> Result<(), crate::compiler_error::CompilerFailure> {
6411 emitter.instruction(Instruction::Call(host_func_idx));
6412 let bigint_idx = ctx
6413 .symbols
6414 .bigint_type_idx()
6415 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6416 let raw_bigint_idx = ctx
6417 .symbols
6418 .raw_bigint_type_idx()
6419 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6420 let vtable_global = ctx
6421 .symbols
6422 .prelude_global_idx("bigint_vtable")
6423 .ok_or_else(|| {
6424 crate::codegen::internal_failure("bigint_vtable imported from prelude bootstrap")
6425 })?;
6426 let Some(scratch_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6429 wasm_encoder::RefType {
6430 nullable: false,
6431 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6432 },
6433 ))) else {
6434 return Ok(());
6435 };
6436 let Some(scratch_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32))
6437 else {
6438 return Ok(());
6439 };
6440 emitter.instruction(Instruction::LocalSet(scratch_limbs));
6441 emitter.instruction(Instruction::LocalSet(scratch_sign));
6442 emitter.instruction(Instruction::GlobalGet(vtable_global));
6443 emitter.instruction(Instruction::LocalGet(scratch_sign));
6444 emitter.instruction(Instruction::LocalGet(scratch_limbs));
6445 emitter.instruction(Instruction::StructNew(bigint_idx));
6446
6447 Ok(())
6448}
6449
6450fn emit_bigint_binop_inline(
6454 emitter: &mut FunctionEmitter,
6455 ctx: &CodegenCtx,
6456 lhs: ExprId,
6457 rhs: ExprId,
6458 op_name: &str,
6459) -> Result<(), crate::compiler_error::CompilerFailure> {
6460 emit_expr(emitter, ctx, lhs)?;
6461 emit_bigint_extract_to_stack(emitter, ctx);
6462 let raw_bigint_idx = ctx
6463 .symbols
6464 .raw_bigint_type_idx()
6465 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6466 let lhs_limbs =
6467 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6468 nullable: false,
6469 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6470 }))?;
6471 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6472 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6473 emitter.instruction(Instruction::LocalSet(lhs_sign));
6474 emitter.instruction(Instruction::LocalGet(lhs_sign));
6475 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6476 emit_expr(emitter, ctx, rhs)?;
6477 emit_bigint_extract_to_stack(emitter, ctx);
6478 let host_idx = ctx
6479 .symbols
6480 .func_idx(&crate::mangle::host(
6481 crate::runtime::BIGINT_MODULE_NAME,
6482 op_name,
6483 ))
6484 .ok_or_else(|| {
6485 crate::codegen::internal_failure(format!(
6486 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6487 ))
6488 })?;
6489 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6490 Ok(())
6491}
6492
6493fn emit_bigint_pm_one(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: crate::PostfixOp) {
6500 ctx.latch(emit_bigint_pm_one_checked(emitter, ctx, op));
6501}
6502
6503fn emit_bigint_pm_one_checked(
6504 emitter: &mut FunctionEmitter,
6505 ctx: &CodegenCtx,
6506 op: crate::PostfixOp,
6507) -> Result<(), crate::compiler_error::CompilerFailure> {
6508 emit_bigint_extract_to_stack(emitter, ctx);
6509 let raw_bigint_idx = ctx
6510 .symbols
6511 .raw_bigint_type_idx()
6512 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6513 let Some(lhs_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6514 wasm_encoder::RefType {
6515 nullable: false,
6516 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6517 },
6518 ))) else {
6519 return Ok(());
6520 };
6521 let Some(lhs_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32)) else {
6522 return Ok(());
6523 };
6524 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6525 emitter.instruction(Instruction::LocalSet(lhs_sign));
6526 emitter.instruction(Instruction::LocalGet(lhs_sign));
6527 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6528 if ctx.latch(emit_bigint_literal(emitter, ctx, "1")).is_none() {
6529 return Ok(());
6530 }
6531 emit_bigint_extract_to_stack(emitter, ctx);
6532 let op_name = match op {
6533 crate::PostfixOp::Inc => "add",
6534 crate::PostfixOp::Dec => "sub",
6535 crate::PostfixOp::NonNullAssert => {
6536 return Err(crate::codegen::internal_failure(
6537 "non-null assertion is not PostfixUnary",
6538 ));
6539 }
6540 };
6541 let host_idx = ctx
6542 .symbols
6543 .func_idx(&crate::mangle::host(
6544 crate::runtime::BIGINT_MODULE_NAME,
6545 op_name,
6546 ))
6547 .ok_or_else(|| {
6548 crate::codegen::internal_failure(format!(
6549 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6550 ))
6551 })?;
6552 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6553
6554 Ok(())
6555}
6556
6557fn emit_bigint_cmp_inline(
6560 emitter: &mut FunctionEmitter,
6561 ctx: &CodegenCtx,
6562 lhs: ExprId,
6563 rhs: ExprId,
6564 op: BinOp,
6565) -> Result<(), crate::compiler_error::CompilerFailure> {
6566 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6567 emitter.instruction(Instruction::I32Const(0));
6568 let inst = match op {
6569 BinOp::Lt => Instruction::I32LtS,
6570 BinOp::Gt => Instruction::I32GtS,
6571 BinOp::Le => Instruction::I32LeS,
6572 BinOp::Ge => Instruction::I32GeS,
6573 _ => {
6574 return Err(crate::codegen::internal_failure(
6575 "emit_bigint_cmp_inline called with non-cmp op",
6576 ));
6577 }
6578 };
6579 emitter.instruction(inst);
6580 Ok(())
6581}
6582
6583fn emit_bigint_cmp_eq_inline(
6585 emitter: &mut FunctionEmitter,
6586 ctx: &CodegenCtx,
6587 lhs: ExprId,
6588 rhs: ExprId,
6589 op: BinOp,
6590) -> Result<(), crate::compiler_error::CompilerFailure> {
6591 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6592 emitter.instruction(Instruction::I32Eqz);
6593 let _: () = if matches!(op, BinOp::NotEq) {
6594 emitter.instruction(Instruction::I32Eqz);
6595 };
6596 Ok(())
6597}
6598
6599fn emit_bigint_cmp_call(
6602 emitter: &mut FunctionEmitter,
6603 ctx: &CodegenCtx,
6604 lhs: ExprId,
6605 rhs: ExprId,
6606) -> Result<(), crate::compiler_error::CompilerFailure> {
6607 emit_expr(emitter, ctx, lhs)?;
6608 emit_bigint_extract_to_stack(emitter, ctx);
6609 let raw_bigint_idx = ctx
6610 .symbols
6611 .raw_bigint_type_idx()
6612 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6613 let lhs_limbs =
6614 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6615 nullable: false,
6616 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6617 }))?;
6618 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6619 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6620 emitter.instruction(Instruction::LocalSet(lhs_sign));
6621 emitter.instruction(Instruction::LocalGet(lhs_sign));
6622 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6623 emit_expr(emitter, ctx, rhs)?;
6624 emit_bigint_extract_to_stack(emitter, ctx);
6625 let cmp_idx = ctx
6626 .symbols
6627 .func_idx(&crate::mangle::host(
6628 crate::runtime::BIGINT_MODULE_NAME,
6629 "cmp",
6630 ))
6631 .ok_or_else(|| {
6632 crate::codegen::internal_failure(
6633 "submilli:bigint.cmp import recorded by codegen bootstrap",
6634 )
6635 })?;
6636 emitter.instruction(Instruction::Call(cmp_idx));
6637 Ok(())
6638}
6639
6640fn evaluate_object_members(
6642 emitter: &mut FunctionEmitter<'_>,
6643 ctx: &CodegenCtx<'_>,
6644 members: &[TypedObjectMember],
6645) -> Result<HashMap<ExprId, u32>, crate::compiler_error::CompilerFailure> {
6646 let intrinsics = ctx
6647 .symbols
6648 .intrinsic_type_indices()
6649 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
6650 let mut values = HashMap::new();
6651 for member in members {
6652 let TypedObjectMember::Value(value_id) = *member else {
6653 return Err(crate::codegen::internal_failure(
6654 "spread field reached plain object lowering",
6655 ));
6656 };
6657 let value_ty = &ctx
6658 .ta
6659 .try_expr(value_id)
6660 .map_err(crate::codegen::arena_failure)?
6661 .ty;
6662 emit_expr(emitter, ctx, value_id)?;
6663 cast::emit_box(emitter, ctx, value_ty)?;
6664 let local = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
6665 emitter.instruction(Instruction::LocalSet(local));
6666 values.insert(value_id, local);
6667 }
6668 Ok(values)
6669}
6670
6671fn emit_field_holds_value_of(
6674 emitter: &mut FunctionEmitter<'_>,
6675 ctx: &CodegenCtx<'_>,
6676 object: u32,
6677 index: u32,
6678 value: u32,
6679 field_ty: &Type,
6680) -> Result<(), crate::compiler_error::CompilerFailure> {
6681 let intrinsics = ctx
6682 .symbols
6683 .intrinsic_type_indices()
6684 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6685 emitter.instruction(Instruction::LocalGet(object));
6686 emitter.instruction(Instruction::StructGet {
6687 struct_type_index: intrinsics.object_shape,
6688 field_index: 1,
6689 });
6690 emitter.instruction(Instruction::LocalGet(index));
6691 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
6692 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
6693 emitter.instruction(Instruction::LocalGet(value));
6694 emit_is_nullish(emitter, ctx)?;
6695 emitter.instruction(Instruction::I32Eqz);
6696 emitter.instruction(Instruction::I32Or);
6697 let _: () = if field_runtime_type_is_testable(field_ty) {
6698 emit_structural_test(emitter, ctx, value, field_ty, field_ty)?;
6699 emitter.instruction(Instruction::I32And);
6700 };
6701 Ok(())
6702}
6703
6704fn object_ref(object_type: u32) -> ValType {
6705 ValType::Ref(RefType {
6706 nullable: true,
6707 heap_type: HeapType::Concrete(object_type),
6708 })
6709}
6710
6711pub(super) fn emit_index_number(
6713 emitter: &mut FunctionEmitter,
6714 ctx: &CodegenCtx,
6715 ty: &Type,
6716) -> Result<(), crate::compiler_error::CompilerFailure> {
6717 if ty == &Type::Unknown {
6718 let function = ctx
6719 .symbols
6720 .prelude_func_idx("__value_to_index")
6721 .ok_or_else(|| crate::codegen::internal_failure("dynamic index helper collected"))?;
6722 emitter.instruction(Instruction::Call(function));
6723 } else {
6724 cast::emit_coerce_to_slot(emitter, ctx, ty, &Type::Number)?;
6725 };
6726 Ok(())
6727}
6728
6729fn emit_computed_object(
6730 emitter: &mut FunctionEmitter,
6731 ctx: &CodegenCtx,
6732 members: &[TypedObjectMember],
6733) -> Result<(), crate::compiler_error::CompilerFailure> {
6734 let Some(intr) = ctx.require(ctx.symbols.intrinsic_type_indices(), "intrinsics declared")
6735 else {
6736 return Ok(());
6737 };
6738 let Some(merge) = ctx.require(
6739 ctx.symbols.prelude_func_idx("ObjectConstructor##spread"),
6740 "spread helper imported",
6741 ) else {
6742 return Ok(());
6743 };
6744 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
6745 nullable: false,
6746 heap_type: HeapType::Concrete(intr.object_shape),
6747 }))?;
6748 for _ in 0..4 {
6749 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
6750 }
6751 emitter.instruction(Instruction::Call(merge));
6752 emitter.instruction(Instruction::LocalSet(object));
6753 for member in members {
6754 emitter.instruction(Instruction::LocalGet(object));
6755 match member {
6756 TypedObjectMember::Computed { key, value } => {
6757 emit_expr(emitter, ctx, *key)?;
6758 crate::codegen::cast_check::emit_operation_cast_on_stack(
6759 emitter,
6760 ctx,
6761 &ctx.ta
6762 .try_expr(*key)
6763 .map_err(crate::codegen::arena_failure)?
6764 .ty,
6765 &Type::String,
6766 )?;
6767 emit_expr(emitter, ctx, *value)?;
6768 cast::emit_box(
6769 emitter,
6770 ctx,
6771 &ctx.ta
6772 .try_expr(*value)
6773 .map_err(crate::codegen::arena_failure)?
6774 .ty,
6775 )?;
6776 let Some(symbol) = ctx.require(
6777 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
6778 "dynamic write imported",
6779 ) else {
6780 return Ok(());
6781 };
6782 emitter.instruction(Instruction::Call(symbol));
6783 }
6784 TypedObjectMember::Spread { source, .. } => {
6785 emit_expr(emitter, ctx, *source)?;
6786 for _ in 0..2 {
6787 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
6788 }
6789 emitter.instruction(Instruction::Call(merge));
6790 emitter.instruction(Instruction::LocalSet(object));
6791 }
6792 TypedObjectMember::Value(_) => {
6793 ctx.fail("computed literal contains an unlowered named field");
6794 return Ok(());
6795 }
6796 }
6797 }
6798 emitter.instruction(Instruction::LocalGet(object));
6799 Ok(())
6800}
6801
6802fn emit_object_index_postfix(
6803 emitter: &mut FunctionEmitter,
6804 ctx: &CodegenCtx,
6805 receiver: ExprId,
6806 index: ExprId,
6807 op: crate::PostfixOp,
6808 read_ty: &Type,
6809) -> Result<(), crate::compiler_error::CompilerFailure> {
6810 emit_expr(emitter, ctx, receiver)?;
6811 cast::emit_box(
6812 emitter,
6813 ctx,
6814 &ctx.ta
6815 .try_expr(receiver)
6816 .map_err(crate::codegen::arena_failure)?
6817 .ty,
6818 )?;
6819 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6820 emitter.instruction(Instruction::LocalSet(object));
6821 emit_expr(emitter, ctx, index)?;
6822 crate::codegen::cast_check::emit_operation_cast_on_stack(
6823 emitter,
6824 ctx,
6825 &ctx.ta
6826 .try_expr(index)
6827 .map_err(crate::codegen::arena_failure)?
6828 .ty,
6829 &Type::String,
6830 )?;
6831 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
6832 emitter.instruction(Instruction::LocalSet(key));
6833 emitter.instruction(Instruction::LocalGet(object));
6834 emitter.instruction(Instruction::LocalGet(key));
6835 let Some(symbol) = ctx.require(
6836 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
6837 "record read imported",
6838 ) else {
6839 return Ok(());
6840 };
6841 emitter.instruction(Instruction::Call(symbol));
6842 crate::codegen::cast_check::emit_operation_cast_on_stack(
6843 emitter,
6844 ctx,
6845 &Type::Unknown,
6846 read_ty,
6847 )?;
6848 cast::emit_box(emitter, ctx, read_ty)?;
6849 emit_postfix_numeric(emitter, ctx, &Type::Unknown);
6850 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6851 emitter.instruction(Instruction::LocalTee(old));
6852 emit_postfix_delta(emitter, ctx, op, &Type::Unknown);
6853 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6854 emitter.instruction(Instruction::LocalSet(updated));
6855 emitter.instruction(Instruction::LocalGet(object));
6856 emitter.instruction(Instruction::LocalGet(key));
6857 emitter.instruction(Instruction::LocalGet(updated));
6858 let Some(symbol) = ctx.require(
6859 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
6860 "record write imported",
6861 ) else {
6862 return Ok(());
6863 };
6864 emitter.instruction(Instruction::Call(symbol));
6865 emitter.instruction(Instruction::LocalGet(old));
6866 Ok(())
6867}
6868
6869#[cfg(test)]
6870mod tests {
6871 use super::*;
6872 use crate::codegen::invariant_tests::{assert_internal, with_context};
6873 use crate::{FileId, Span, TypedAst, TypedExpr};
6874
6875 #[test]
6876 fn missing_method_dispatch_is_a_compiler_error() {
6877 with_context(
6878 &TypedAst::new(),
6879 &crate::codegen::SymbolTable::default(),
6880 |ctx| {
6881 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
6882 let error = emit_method_call_with_receiver_on_stack(
6883 &mut emitter,
6884 ctx,
6885 &Type::Number,
6886 &crate::mangle::prelude("MissingInterface"),
6887 "missingMethod",
6888 &[],
6889 None,
6890 None,
6891 &Type::Number,
6892 )
6893 .unwrap_err();
6894 assert!(error.to_string().contains("MissingInterface.missingMethod"));
6895 assert_internal(error);
6896 },
6897 );
6898 }
6899
6900 #[test]
6901 fn call_and_chain_metadata_are_checked_before_operands() {
6902 with_context(
6903 &TypedAst::new(),
6904 &crate::codegen::SymbolTable::default(),
6905 |ctx| {
6906 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
6907 assert_internal(
6908 emit_intrinsic_call(&mut emitter, ctx, Intrinsic::Assert, &[], &Type::Void)
6909 .unwrap_err(),
6910 );
6911 assert_internal(
6912 emit_intrinsic_call(
6913 &mut emitter,
6914 ctx,
6915 Intrinsic::BigIntFromString,
6916 &[],
6917 &Type::BigInt,
6918 )
6919 .unwrap_err(),
6920 );
6921 assert_internal(
6922 emit_direct_call(
6923 &mut emitter,
6924 ctx,
6925 false,
6926 0,
6927 &[Type::Number],
6928 &Type::Void,
6929 &[],
6930 )
6931 .unwrap_err(),
6932 );
6933 assert_internal(
6934 emit_args_into_slots(&mut emitter, ctx, &[], Some(&[ValType::F64]))
6935 .unwrap_err(),
6936 );
6937 assert_internal(
6938 emit_chain_parts(
6939 &mut emitter,
6940 ctx,
6941 &[],
6942 1,
6943 &Type::Number,
6944 &Type::Number,
6945 None,
6946 )
6947 .unwrap_err(),
6948 );
6949 assert_internal(
6950 emit_chain_parts(
6951 &mut emitter,
6952 ctx,
6953 &[],
6954 0,
6955 &Type::Number,
6956 &Type::Number,
6957 Some(&[]),
6958 )
6959 .unwrap_err(),
6960 );
6961 },
6962 );
6963 }
6964
6965 #[test]
6966 fn uninterned_literal_fails_and_a_fresh_emitter_still_works() {
6967 let mut ta = TypedAst::new();
6968 let string = ta
6969 .try_push_expr(TypedExpr {
6970 kind: TypedExprKind::String("missing".into()),
6971 ty: Type::String,
6972 span: Span::at(FileId(0)),
6973 })
6974 .unwrap();
6975 let number = ta
6976 .try_push_expr(TypedExpr {
6977 kind: TypedExprKind::Number(3.0),
6978 ty: Type::Number,
6979 span: Span::at(FileId(0)),
6980 })
6981 .unwrap();
6982 with_context(&ta, &crate::codegen::SymbolTable::default(), |ctx| {
6983 let mut failed = FunctionEmitter::new(ctx, &[]).unwrap();
6984 assert_internal(emit_expr(&mut failed, ctx, string).unwrap_err());
6985 drop(failed);
6986 let mut healthy = FunctionEmitter::new(ctx, &[]).unwrap();
6987 emit_expr(&mut healthy, ctx, number).unwrap();
6988 healthy.instruction(Instruction::Drop);
6989 healthy.build().unwrap();
6990 });
6991 }
6992}