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