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 if let Some(class) = ctx.ta.inherited_static_receivers.get(&id) {
41 crate::codegen::init_guard::emit_check(emitter, ctx, class);
42 }
43 emit_expr_value(emitter, ctx, id)?;
44 crate::codegen::field_guards::attach(
47 emitter,
48 ctx,
49 &ctx.ta
50 .try_expr(id)
51 .map_err(crate::codegen::arena_failure)?
52 .ty,
53 )?;
54 Ok(())
55}
56
57fn emit_receiver(
58 emitter: &mut FunctionEmitter,
59 ctx: &CodegenCtx,
60 id: ExprId,
61) -> Result<(), crate::compiler_error::CompilerFailure> {
62 emit_expr(emitter, ctx, id)?;
63 let source = &ctx
64 .ta
65 .try_expr(id)
66 .map_err(crate::codegen::arena_failure)?
67 .ty;
68 let target = ctx
69 .ta
70 .source_type(id)
71 .map_err(crate::codegen::arena_failure)?;
72 let _: () = if source != target {
73 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, source, target)?;
74 };
75 Ok(())
76}
77
78fn emit_expr_value(
79 emitter: &mut FunctionEmitter,
80 ctx: &CodegenCtx,
81 id: ExprId,
82) -> Result<(), crate::compiler_error::CompilerFailure> {
83 let expr = ctx.ta.try_expr(id).map_err(crate::codegen::arena_failure)?;
84 emitter.record_span(expr.span);
85 if let Some(slot) = emitter.evaluated_slot(id) {
88 emitter.instruction(Instruction::LocalGet(slot));
89 return Ok(());
90 }
91 let _: () = match &expr.kind {
92 TypedExprKind::Number(v) => {
93 emitter.instruction(Instruction::F64Const(Ieee64::from(*v)));
94 }
95 TypedExprKind::BigInt(digits) => emit_bigint_literal(emitter, ctx, digits)?,
96 TypedExprKind::Boolean(b) => {
97 emitter.instruction(Instruction::I32Const(if *b { 1 } else { 0 }));
98 }
99 TypedExprKind::String(_) => {
103 let pool_idx = ctx.strings.locations.get(&id).copied().ok_or_else(|| {
104 crate::codegen::internal_failure("a string literal was not interned")
105 })?;
106 super::emit_pooled_string(emitter, ctx, pool_idx)?;
107 }
108 TypedExprKind::Regex { source, flags } => {
109 emit_regex_literal(emitter, ctx, source, flags)?;
110 }
111 TypedExprKind::Sequence { stmts, result } => {
112 for &stmt in stmts {
113 super::stmt::emit_statement(emitter, ctx, stmt)?;
114 }
115 emit_expr(emitter, ctx, *result)?;
116 }
117 TypedExprKind::EffectThen { effect, result } => {
118 emit_expr(emitter, ctx, *effect)?;
119 emitter.instruction(Instruction::Drop);
120 emit_expr(emitter, ctx, *result)?;
121 }
122 TypedExprKind::Binary { op, lhs, rhs } => {
123 emit_binary(emitter, ctx, *op, *lhs, *rhs, &expr.ty)?;
124 }
125 TypedExprKind::Unary { op, operand } => {
126 emit_unary(emitter, ctx, *op, *operand, &expr.ty)?;
127 }
128 TypedExprKind::LocalRef { ident, boxed } => {
129 if ctx.ta.parameter_inputs.contains(&id) {
130 let (slot, source) = emitter
131 .parameter_input_slots
132 .get(&ident.name)
133 .cloned()
134 .ok_or_else(|| {
135 crate::codegen::internal_failure("parameter input slot is missing")
136 })?;
137 emitter.instruction(Instruction::LocalGet(slot));
138 cast::emit_coerce_to_slot(emitter, ctx, &source, &expr.ty)?;
139 return Ok(());
140 }
141 let slot = emitter.require_local_slot(&ident.name)?;
149 if *boxed {
150 let box_idx = ctx.symbols.box_type_idx(&expr.ty)?.ok_or_else(|| {
151 crate::codegen::internal_failure("box type registered for every boxed LocalRef")
152 })?;
153 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
154 cast::emit_unerase(emitter, ctx, &expr.ty)?;
155 } else {
156 emitter.instruction(Instruction::LocalGet(slot));
157 }
158 }
159 TypedExprKind::LocalNarrowRef { .. } if inferred_never(ctx, id)? => {
162 emitter.instruction(Instruction::Unreachable);
163 }
164 TypedExprKind::LocalNarrowRef { binding, path, .. } => {
165 emit_local_narrow_ref(emitter, ctx, binding, path, &expr.ty)?;
166 }
167 TypedExprKind::GlobalRef { mangled, .. } => {
168 emit_global_ref(emitter, ctx, mangled, &expr.ty)?;
169 }
170 TypedExprKind::FunctionRef { mangled, .. } => {
171 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
172 emit_function_ref(emitter, ctx, mangled, &expr.ty)?;
173 }
174 TypedExprKind::Call { mangled, args, .. } => {
175 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
176 emit_symbol_call(emitter, ctx, mangled, args, &expr.ty)?;
177 }
178 TypedExprKind::SuperCtorCall { parent, args } => {
179 let this = emitter.this_local().ok_or_else(|| {
183 crate::codegen::internal_failure("super(...) only inside a constructor body")
184 })?;
185 emitter.instruction(Instruction::LocalGet(this));
186 let ctor_abi = ctx.symbols.class_ctor_abi(parent).map(<[ValType]>::to_vec);
187 let init = ctx
188 .symbols
189 .class_ctor_init_func_idx(parent)
190 .ok_or_else(|| crate::codegen::internal_failure("parent ctor init fn allocated"))?;
191 let defaults = ctx.symbols.host_call_defaults(init, args.len()).to_vec();
193 emit_args_into_slots_with_defaults(emitter, ctx, args, ctor_abi.as_deref(), &defaults)?;
194 emitter.instruction(Instruction::Call(init));
195 if let Some(mangled) = emitter.ctor_class().cloned() {
198 emit_class_field_setup(emitter, ctx, &mangled)?;
199 }
200 emit_undefined(emitter, ctx)?;
201 }
202 TypedExprKind::SuperMethodCall { owner, name, args } => {
203 let this = emitter.this_local().ok_or_else(|| {
208 crate::codegen::internal_failure("super.method() only inside a method body")
209 })?;
210 emitter.instruction(Instruction::LocalGet(this));
211 let abi = ctx.symbols.class_method_abi(owner, &name.name).cloned();
212 emit_args_into_slots(
213 emitter,
214 ctx,
215 args,
216 abi.as_ref().map(|a| a.params.as_slice()),
217 )?;
218 let func = ctx
219 .symbols
220 .class_method_func_idx(owner, &name.name)
221 .ok_or_else(|| {
222 crate::codegen::internal_failure("parent method body fn allocated")
223 })?;
224 emitter.instruction(Instruction::Call(func));
225 emit_slot_return_cast(emitter, ctx, &expr.ty.clone(), abi.as_ref())?;
226 }
227 TypedExprKind::VirtualCall { target, tool, args } => {
228 super::virtual_call::emit_virtual_call(emitter, ctx, target, tool, args)?;
232 }
233 TypedExprKind::CallClosure { callee, args } => {
234 emit_indirect_closure_call(emitter, ctx, *callee, args, &expr.ty)?;
240 }
241 TypedExprKind::GenericCall {
242 mangled,
243 type_args,
244 args,
245 return_cast,
246 type_predicate: _,
247 } => {
248 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
249 emit_generic_call(
250 emitter,
251 ctx,
252 &expr.ty,
253 mangled,
254 type_args,
255 args,
256 return_cast,
257 )?;
258 }
259 TypedExprKind::IntrinsicCall { kind, args } => {
260 emit_intrinsic_call(emitter, ctx, *kind, args, &expr.ty)?;
261 }
262 TypedExprKind::MethodCall {
263 receiver,
264 iface,
265 name,
266 args,
267 type_predicate: _,
268 } => {
269 emit_method_call(
270 emitter,
271 ctx,
272 *receiver,
273 iface,
274 &name.name,
275 args,
276 None,
277 &[],
278 None,
279 &expr.ty,
280 )?;
281 }
282 TypedExprKind::GenericMethodCall {
283 receiver,
284 iface,
285 name,
286 type_args,
287 args,
288 return_cast,
289 type_predicate: _,
290 } => {
291 let plain_args: Vec<ExprId> = args.iter().map(|a| a.expr).collect();
292 emit_method_call(
293 emitter,
294 ctx,
295 *receiver,
296 iface,
297 &name.name,
298 &plain_args,
299 Some(args),
300 type_args,
301 return_cast.as_ref(),
302 &expr.ty,
303 )?;
304 }
305 TypedExprKind::ObjectLiteral { members, fields } => {
306 emit_object_literal(emitter, ctx, &expr.ty, members, fields)?;
307 }
308 TypedExprKind::FieldAccess { receiver, name } => {
309 emit_field_access(emitter, ctx, id, &expr.ty, receiver, name)?;
310 }
311 TypedExprKind::InterfacePropertyAccess {
312 receiver,
313 iface,
314 name,
315 } => emit_interface_property(emitter, ctx, receiver, iface, name)?,
316 TypedExprKind::ArrayLiteral { elements, .. } => {
317 emit_array_literal(emitter, ctx, elements)?;
318 }
319 TypedExprKind::TupleLiteral { elements, .. } => {
320 emit_tuple_literal(emitter, ctx, elements)?;
321 }
322 TypedExprKind::IndexAccess { receiver, index } => {
323 emit_index_access(emitter, ctx, id, receiver, index, &expr.ty)?;
324 }
325 TypedExprKind::Undefined => emit_undefined(emitter, ctx)?,
326 TypedExprKind::Null => {
327 let _ = ctx; emitter.instruction(Instruction::RefNull(HeapType::Abstract {
339 shared: false,
340 ty: wasm_encoder::AbstractHeapType::None,
341 }));
342 }
343 TypedExprKind::This => {
344 let slot = emitter.this_local().ok_or_else(|| {
345 crate::codegen::internal_failure("`this` has a local receiver or captured binding")
346 })?;
347 if emitter.dynamic_this {
348 emitter.instruction(Instruction::LocalGet(slot));
349 emitter.instruction(Instruction::RefIsNull);
350 emitter.emit_if(BlockType::Empty);
351 crate::codegen::throw::emit_type_error_throw(
352 emitter,
353 ctx,
354 "Unbound function has no this receiver",
355 );
356 emitter.emit_end();
357 }
358 emitter.instruction(Instruction::LocalGet(slot));
359 if emitter.dynamic_this {
360 crate::codegen::cast_check::emit_operation_cast_on_stack(
361 emitter,
362 ctx,
363 &Type::Unknown,
364 &expr.ty,
365 )?;
366 }
367 }
368 TypedExprKind::NumberEnumMember { value, .. } => {
369 let boxed_idx = ctx
376 .symbols
377 .boxed_number_type_idx()
378 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
379 let vtable_global = ctx
380 .symbols
381 .prelude_global_idx("boxed_number_vtable")
382 .ok_or_else(|| {
383 crate::codegen::internal_failure("boxed_number_vtable imported from prelude")
384 })?;
385 emitter.instruction(Instruction::GlobalGet(vtable_global));
386 emitter.instruction(Instruction::F64Const(Ieee64::from(*value)));
387 emitter.instruction(Instruction::StructNew(boxed_idx));
388 }
389 TypedExprKind::StringEnumMember { .. } => {
390 let pool_idx = ctx.strings.locations.get(&id).copied().ok_or_else(|| {
395 crate::codegen::internal_failure("a string enum value was not interned")
396 })?;
397 super::emit_pooled_string(emitter, ctx, pool_idx)?;
398 }
399 TypedExprKind::TypeofTag { value, tag } => {
400 emit_typeof_tag(emitter, ctx, *value, *tag)?;
401 }
402 TypedExprKind::InstanceOf { value, class } => match class.lowered() {
403 Type::ClassRef { mangled, .. } => {
404 let vtable_global =
405 ctx.symbols
406 .class_vtable_global_idx(mangled)
407 .ok_or_else(|| {
408 crate::codegen::internal_failure("class vtable global recorded")
409 })?;
410 emit_expr(emitter, ctx, *value)?;
411 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
412 cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
413 }
414 Type::Uint8Array => {
418 let idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
419 crate::codegen::internal_failure("$Uint8Array intrinsic registered")
420 })?;
421 emit_expr(emitter, ctx, *value)?;
422 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
423 }
424 _ => {
425 return Err(crate::codegen::internal_failure(format!(
426 "instanceof codegen with a non-class RHS: {class:?}"
427 )));
428 }
429 },
430 TypedExprKind::Narrowed {
431 path,
432 source,
433 binding,
434 cast_info,
435 inner,
436 } => {
437 emitter.push_scope();
441 if path.chain.is_empty() {
442 let shadow_val = ctx.symbols.value_type(&cast_info.to_ty)?;
443 let shadow = emitter.define_local(binding, shadow_val)?;
444 emit_expr(emitter, ctx, *source)?;
445 crate::codegen::function_emitter::cast::emit_narrowing_cast(
446 emitter, ctx, cast_info,
447 )?;
448 emitter.instruction(Instruction::LocalSet(shadow));
449 } else {
450 emitter.register_narrow_source(&binding.name, *source)?;
451 }
452 emit_expr(emitter, ctx, *inner)?;
453 emitter.pop_scope()?;
454 }
455 TypedExprKind::Closure {
456 params,
457 captured,
458 runtime_generics,
459 ..
460 } => {
461 emit_closure_value(
462 emitter,
463 ctx,
464 id,
465 &expr.ty,
466 params,
467 captured,
468 runtime_generics,
469 )?;
470 }
471 TypedExprKind::Ternary { cond, then_, else_ } => {
476 let result_ty = expr.ty.clone();
477 let block_ty = conditional_block_type(ctx, &result_ty)?;
478 let cond_ty = ctx
479 .ta
480 .try_expr(*cond)
481 .map_err(crate::codegen::arena_failure)?
482 .ty
483 .clone();
484 emit_expr(emitter, ctx, *cond)?;
485 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
486 emitter.emit_if(block_ty);
487 emit_conditional_operand(emitter, ctx, *then_, &result_ty)?;
488 emitter.emit_else();
489 emit_conditional_operand(emitter, ctx, *else_, &result_ty)?;
490 emitter.emit_end();
491 }
492 TypedExprKind::NullishCoalesce { lhs, rhs } => {
495 emit_nullish_coalesce(emitter, ctx, lhs, rhs, &expr.ty)?;
496 }
497 TypedExprKind::OptionalChain { base, parts } => {
505 let result_ty = expr.ty.clone();
506 let base_ty = ctx
507 .ta
508 .try_expr(*base)
509 .map_err(crate::codegen::arena_failure)?
510 .ty
511 .clone();
512 let receiver = if matches!(parts.first(), Some(crate::TypedChainPart::Call { .. })) {
513 emit_callee(emitter, ctx, *base)?
514 } else {
515 emit_expr(emitter, ctx, *base)?;
516 None
517 };
518 emitter.call_receiver = receiver;
519 emit_chain_parts(
520 emitter,
521 ctx,
522 parts,
523 0,
524 &base_ty,
525 &result_ty,
526 ctx.ta.runtime_chain_types.get(&id).map(Vec::as_slice),
527 )?;
528 }
529 TypedExprKind::PostfixUnary { op, target } => {
530 if let crate::PostfixTarget::Index {
531 receiver, index, ..
532 } = target
533 && ctx
534 .ta
535 .source_type(*receiver)
536 .map_err(crate::codegen::arena_failure)?
537 .is_structural_object()
538 {
539 emit_object_index_postfix(
540 emitter,
541 ctx,
542 *receiver,
543 *index,
544 *op,
545 ctx.ta
546 .source_type(id)
547 .map_err(crate::codegen::arena_failure)?,
548 )?;
549 cast::emit_coerce_to_slot(emitter, ctx, &Type::Unknown, &expr.ty)?;
550 } else {
551 emit_postfix_unary(emitter, ctx, *op, target, &expr.ty)?;
552 }
553 }
554 TypedExprKind::NonNullAssert { value } => {
555 crate::codegen::cast_check::emit_non_null_assert(emitter, ctx, *value, &expr.ty)?;
556 }
557 TypedExprKind::Cast {
558 value,
559 target_ty,
560 check,
561 } => {
562 crate::codegen::cast_check::emit_cast(
563 emitter,
564 ctx,
565 *value,
566 target_ty,
567 check.as_deref(),
568 expr.span,
569 )?;
570 }
571 };
572 if matches!(expr.ty, Type::Never) && !completes_when_never(&expr.kind) {
577 emitter.instruction(Instruction::Unreachable);
578 }
579 Ok(())
580}
581
582fn completes_when_never(kind: &TypedExprKind) -> bool {
588 matches!(
589 kind,
590 TypedExprKind::LocalNarrowRef { .. }
591 | TypedExprKind::FieldAccess { .. }
592 | TypedExprKind::IndexAccess { .. }
593 )
594}
595
596pub(crate) fn emit_undefined(
598 emitter: &mut FunctionEmitter<'_>,
599 ctx: &CodegenCtx<'_>,
600) -> Result<(), crate::compiler_error::CompilerFailure> {
601 let global = ctx
602 .symbols
603 .prelude_global_idx(crate::runtime::prelude::undefined::GLOBAL_NAME)
604 .ok_or_else(|| crate::codegen::internal_failure("undefined global was not imported"))?;
605 emitter.instruction(Instruction::GlobalGet(global));
606 Ok(())
607}
608
609pub(crate) fn emit_is_nullish(
611 emitter: &mut FunctionEmitter<'_>,
612 ctx: &CodegenCtx<'_>,
613) -> Result<(), crate::compiler_error::CompilerFailure> {
614 let intrinsic = ctx
615 .symbols
616 .intrinsic_type_indices()
617 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
618 let value = emitter.add_anonymous_local(ValType::Ref(RefType {
619 nullable: true,
620 heap_type: HeapType::Concrete(intrinsic.object),
621 }))?;
622 emitter.instruction(Instruction::LocalSet(value));
623 emitter.instructions(crate::codegen::nullish::is_nullish(
624 &[Instruction::LocalGet(value)],
625 intrinsic,
626 ));
627 Ok(())
628}
629
630pub(crate) fn emit_is_undefined(
631 emitter: &mut FunctionEmitter<'_>,
632 ctx: &CodegenCtx<'_>,
633) -> Result<(), crate::compiler_error::CompilerFailure> {
634 let intrinsic = ctx
635 .symbols
636 .intrinsic_type_indices()
637 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
638 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
639 intrinsic.undefined,
640 )));
641 Ok(())
642}
643
644fn emit_global_ref(
645 emitter: &mut FunctionEmitter,
646 ctx: &CodegenCtx,
647 mangled: &crate::MangledName,
648 result_ty: &Type,
649) -> Result<(), crate::compiler_error::CompilerFailure> {
650 if let Some(iface) = static_interface(ctx, result_ty) {
659 emit_static_value(emitter, ctx, iface)?;
660 if let ValType::Ref(RefType {
661 nullable: false, ..
662 }) = ctx.symbols.value_type(result_ty)?
663 {
664 emitter.instruction(Instruction::RefAsNonNull);
665 }
666 return Ok(());
667 }
668 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
669 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
670 crate::codegen::internal_failure("top-level let/const recorded during codegen")
671 })?;
672 emitter.instruction(Instruction::GlobalGet(idx));
673 if let ValType::Ref(RefType {
674 nullable: false, ..
675 }) = ctx.symbols.value_type(result_ty)?
676 {
677 emitter.instruction(Instruction::RefAsNonNull);
678 }
679
680 Ok(())
681}
682
683fn static_interface<'a>(ctx: &CodegenCtx, ty: &'a Type) -> Option<&'a crate::MangledName> {
685 match ty.lowered() {
686 Type::InterfaceRef { mangled, .. }
687 if ctx.symbols.iface_dispatch(mangled) == Some(crate::Dispatch::Static) =>
688 {
689 Some(mangled)
690 }
691 _ => None,
692 }
693}
694
695fn emit_static_value(
696 emitter: &mut FunctionEmitter,
697 ctx: &CodegenCtx,
698 iface: &crate::MangledName,
699) -> Result<(), crate::compiler_error::CompilerFailure> {
700 let tag = ctx.symbols.static_value_tag(iface).ok_or_else(|| {
701 crate::codegen::internal_failure("a static binding read as a value has a recorded tag")
702 })?;
703 super::emit_inline_string_literal(emitter, ctx, tag)?;
704 let helper = ctx
705 .symbols
706 .prelude_func_idx("ObjectConstructor##staticValue")
707 .ok_or_else(|| crate::codegen::internal_failure("the static value helper is imported"))?;
708 emitter.instruction(Instruction::Call(helper));
709 Ok(())
710}
711
712fn emit_function_ref(
713 emitter: &mut FunctionEmitter,
714 ctx: &CodegenCtx,
715 mangled: &crate::MangledName,
716 result_ty: &Type,
717) -> Result<(), crate::compiler_error::CompilerFailure> {
718 let closure_struct_idx = ctx
729 .symbols
730 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
731 .ok_or_else(|| {
732 crate::codegen::internal_failure(
733 "closure struct type registered for every function-as-value",
734 )
735 })?;
736 let adapter_idx = ctx.symbols.adapter_func_idx(mangled).ok_or_else(|| {
737 crate::codegen::internal_failure("adapter func recorded for every function-as-value")
738 })?;
739 let closure_global_idx = ctx
740 .symbols
741 .adapter_closure_global_idx(mangled)
742 .ok_or_else(|| {
743 crate::codegen::internal_failure("closure global recorded for every function-as-value")
744 })?;
745 let vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
746 crate::codegen::internal_failure(
747 "closure vtable global emitted whenever closures or adapters exist",
748 )
749 })?;
750 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
751 emitter.instruction(Instruction::RefIsNull);
752 emitter.emit_if(BlockType::Empty);
753 emitter.instruction(Instruction::GlobalGet(vtable_idx));
754 emitter.instruction(Instruction::RefFunc(adapter_idx));
755 emitter.instruction(Instruction::GlobalGet(vtable_idx));
756 if let Some(metadata) = ctx.symbols.function_argument_metadata.get(mangled) {
757 crate::codegen::call_arguments::wrap(emitter, ctx, metadata)?;
758 }
759 emitter.instruction(Instruction::I64Const(0));
760 emitter.instruction(Instruction::StructNew(closure_struct_idx));
761 emitter.instruction(Instruction::GlobalSet(closure_global_idx));
762 emitter.emit_end();
763 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
764 if ctx.symbols.is_shared_closure_global(mangled) {
765 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
766 closure_struct_idx,
767 )));
768 } else {
769 emitter.instruction(Instruction::RefAsNonNull);
770 }
771
772 Ok(())
773}
774
775fn emit_symbol_call(
776 emitter: &mut FunctionEmitter,
777 ctx: &CodegenCtx,
778 mangled: &crate::MangledName,
779 args: &[ExprId],
780 result_ty: &Type,
781) -> Result<(), crate::compiler_error::CompilerFailure> {
782 let target = ctx.symbols.top_level_fn(mangled).ok_or_else(|| {
795 crate::codegen::internal_failure(format!(
796 "Call references unknown top-level fn `{}`",
797 mangled.as_str()
798 ))
799 })?;
800 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
801 let Type::ClassRef { mangled: class, .. } = result_ty.lowered() else {
802 return Err(crate::codegen::internal_failure("constructor class"));
803 };
804 emit_args_into_slots(emitter, ctx, args, ctx.symbols.class_ctor_abi(class))?;
805 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
806 emitter.instruction(Instruction::Call(target.wasm_idx));
807 } else {
808 emit_direct_call(
809 emitter,
810 ctx,
811 target.is_host,
812 target.wasm_idx,
813 &target.params,
814 &target.ret,
815 args,
816 )?;
817 }
818 if result_ty.is_void() {
819 cast::emit_box(emitter, ctx, &target.ret)?;
820 }
821
822 Ok(())
823}
824
825fn emit_interface_property(
826 emitter: &mut FunctionEmitter,
827 ctx: &CodegenCtx,
828 receiver: &ExprId,
829 iface: &crate::MangledName,
830 name: &Ident,
831) -> Result<(), crate::compiler_error::CompilerFailure> {
832 let key = crate::mangle::extend(iface, &name.name);
838 if ctx.symbols.iface_dispatch(iface) == Some(crate::Dispatch::Static) {
841 let global_idx = ctx.symbols.global_idx(&key).ok_or_else(|| {
842 crate::codegen::internal_failure(
843 "static interface property recorded as a global import",
844 )
845 })?;
846 emitter.instruction(Instruction::GlobalGet(global_idx));
847 return Ok(());
848 }
849 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
850 emit_receiver(emitter, ctx, *receiver)?;
851 emit_inline_length(emitter, ctx, struct_idx);
852 return Ok(());
853 }
854 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
855 crate::codegen::internal_failure(
856 "interface property getter recorded during the dependency-import pass",
857 )
858 })?;
859 emit_receiver(emitter, ctx, *receiver)?;
860 if matches!(
873 ctx.ta
874 .source_type(*receiver)
875 .map_err(crate::codegen::arena_failure)?
876 .lowered(),
877 Type::InterfaceRef { .. }
878 ) {
879 emitter.instruction(Instruction::RefAsNonNull);
880 }
881 emitter.instruction(Instruction::Call(func_idx));
882
883 Ok(())
884}
885
886fn emit_index_access(
887 emitter: &mut FunctionEmitter,
888 ctx: &CodegenCtx,
889 id: ExprId,
890 receiver: &ExprId,
891 index: &ExprId,
892 result_ty: &Type,
893) -> Result<(), crate::compiler_error::CompilerFailure> {
894 let recv_ty = ctx
899 .ta
900 .source_type(*receiver)
901 .map_err(crate::codegen::arena_failure)?
902 .clone();
903 if recv_ty.is_structural_object() {
904 emit_expr(emitter, ctx, *receiver)?;
905 cast::emit_box(
906 emitter,
907 ctx,
908 &ctx.ta
909 .try_expr(*receiver)
910 .map_err(crate::codegen::arena_failure)?
911 .ty,
912 )?;
913 emit_expr(emitter, ctx, *index)?;
914 crate::codegen::cast_check::emit_operation_cast_on_stack(
915 emitter,
916 ctx,
917 &ctx.ta
918 .try_expr(*index)
919 .map_err(crate::codegen::arena_failure)?
920 .ty,
921 &Type::String,
922 )?;
923 let Some(symbol) = ctx.require(
924 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
925 "dynamic read imported",
926 ) else {
927 return Ok(());
928 };
929 emitter.instruction(Instruction::Call(symbol));
930 let checked_ty = ctx
931 .ta
932 .source_type(id)
933 .map_err(crate::codegen::arena_failure)?;
934 crate::codegen::cast_check::emit_checked_cast_on_stack(
935 emitter,
936 ctx,
937 &Type::Unknown,
938 checked_ty,
939 )?;
940 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
941 } else if recv_ty.lowered() == &Type::Uint8Array {
942 emit_expr(emitter, ctx, *receiver)?;
943 emit_uint8_index_with_receiver_on_stack(emitter, ctx, *index)?;
944 } else if recv_ty.is_string_shaped() {
945 emit_expr(emitter, ctx, *receiver)?;
946 emit_string_index_with_receiver_on_stack(emitter, ctx, *index)?;
947 } else {
948 emit_expr(emitter, ctx, *receiver)?;
949 emit_bounds_checked_index_with_receiver_on_stack(
950 emitter,
951 ctx,
952 *index,
953 result_ty,
954 is_optional_tuple_position(ctx, &recv_ty, *index)?,
955 )?;
956 }
957
958 Ok(())
959}
960
961fn emit_string_index_with_receiver_on_stack(
964 emitter: &mut FunctionEmitter,
965 ctx: &CodegenCtx,
966 index: ExprId,
967) -> Result<(), crate::compiler_error::CompilerFailure> {
968 let Some(helper) = ctx.require(
969 ctx.symbols.prelude_func_idx("string_index"),
970 "string index helper imported",
971 ) else {
972 return Ok(());
973 };
974 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
977 emitter.instruction(Instruction::LocalSet(receiver));
978 emit_expr(emitter, ctx, index)?;
979 emit_index_number(
980 emitter,
981 ctx,
982 &ctx.ta
983 .try_expr(index)
984 .map_err(crate::codegen::arena_failure)?
985 .ty,
986 )?;
987 let index_local = emitter.add_anonymous_local(ValType::F64)?;
988 emitter.instruction(Instruction::LocalSet(index_local));
989 emitter.instruction(Instruction::LocalGet(receiver));
990 crate::codegen::cast_check::emit_operation_cast_on_stack(
991 emitter,
992 ctx,
993 &Type::Unknown,
994 &Type::String,
995 )?;
996 emitter.instruction(Instruction::LocalGet(index_local));
997 emitter.instruction(Instruction::Call(helper));
998 Ok(())
999}
1000
1001fn emit_nullish_coalesce(
1002 emitter: &mut FunctionEmitter,
1003 ctx: &CodegenCtx,
1004 lhs: &ExprId,
1005 rhs: &ExprId,
1006 result_ty: &Type,
1007) -> Result<(), crate::compiler_error::CompilerFailure> {
1008 let result_ty = result_ty.clone();
1009
1010 let result_val = ctx.symbols.value_type(&result_ty)?;
1011 let lhs_ty = ctx
1012 .ta
1013 .try_expr(*lhs)
1014 .map_err(crate::codegen::arena_failure)?
1015 .ty
1016 .clone();
1017 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
1018 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
1019 if lhs_is_ref {
1020 let tmp = emitter.add_anonymous_local(lhs_val)?;
1021 emit_expr(emitter, ctx, *lhs)?;
1022 emitter.instruction(Instruction::LocalTee(tmp));
1023 emit_is_nullish(emitter, ctx)?;
1024 emitter.emit_if(BlockType::Result(result_val));
1025 emit_conditional_operand(emitter, ctx, *rhs, &result_ty)?;
1026 emitter.emit_else();
1027 emitter.instruction(Instruction::LocalGet(tmp));
1028 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, &result_ty)?;
1038 emitter.emit_end();
1039 } else {
1040 emit_expr(emitter, ctx, *lhs)?;
1042 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
1043 emitter, ctx, &lhs_ty, &result_ty,
1044 )?;
1045 }
1046
1047 Ok(())
1048}
1049
1050fn conditional_block_type(
1053 ctx: &CodegenCtx,
1054 result_ty: &Type,
1055) -> Result<BlockType, crate::compiler_error::CompilerFailure> {
1056 Ok(BlockType::Result(ctx.symbols.value_type(result_ty)?))
1057}
1058
1059fn emit_conditional_operand(
1063 emitter: &mut FunctionEmitter,
1064 ctx: &CodegenCtx,
1065 operand: ExprId,
1066 result_ty: &Type,
1067) -> Result<(), crate::compiler_error::CompilerFailure> {
1068 let operand_ty = ctx
1069 .ta
1070 .try_expr(operand)
1071 .map_err(crate::codegen::arena_failure)?
1072 .ty
1073 .clone();
1074 emit_expr(emitter, ctx, operand)?;
1075 cast::emit_coerce_to_slot(emitter, ctx, &operand_ty, result_ty)
1076}
1077
1078fn emit_generic_call(
1085 emitter: &mut FunctionEmitter,
1086 ctx: &CodegenCtx,
1087 result_ty: &Type,
1088 mangled: &crate::MangledName,
1089 type_args: &[Type],
1090 args: &[crate::GenericArgument],
1091 return_cast: &Option<Type>,
1092) -> Result<(), crate::compiler_error::CompilerFailure> {
1093 let (wasm_idx, target_params, target_return) = match ctx.symbols.top_level_fn(mangled) {
1108 Some(target) => (target.wasm_idx, target.params.clone(), target.ret.clone()),
1109 None => {
1110 return Err(crate::codegen::internal_failure(
1111 "generic call target is not registered",
1112 ));
1113 }
1114 };
1115 for (i, arg) in args.iter().enumerate() {
1116 emit_expr(emitter, ctx, arg.expr)?;
1117 if arg.is_generic {
1118 let arg_ty = ctx
1119 .ta
1120 .try_expr(arg.expr)
1121 .map_err(crate::codegen::arena_failure)?
1122 .ty
1123 .clone();
1124 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &arg_ty)?;
1125 } else if let Some(param_ty) = target_params.get(i) {
1126 let arg_ty = ctx
1131 .ta
1132 .try_expr(arg.expr)
1133 .map_err(crate::codegen::arena_failure)?
1134 .ty
1135 .clone();
1136 cast::emit_coerce_to_slot(emitter, ctx, &arg_ty, param_ty)?;
1137 }
1138 }
1139 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
1140 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
1141 }
1142 if ctx.symbols.runtime_generic_functions.contains(mangled) {
1143 crate::codegen::runtime_descriptors::require_direct_schema(ctx, mangled, type_args)?;
1144 crate::codegen::runtime_descriptors::environment(emitter, ctx, type_args)?;
1145 }
1146 emitter.instruction(Instruction::Call(wasm_idx));
1147 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
1148 emit_undefined(emitter, ctx)?;
1149 }
1150 if let Some(ty) = return_cast {
1151 cast::emit_cast_to(emitter, ctx, ty)?;
1152 } else {
1153 cast::emit_coerce_to_slot(emitter, ctx, &target_return, result_ty)?;
1154 }
1155
1156 Ok(())
1157}
1158
1159fn emit_object_literal(
1160 emitter: &mut FunctionEmitter,
1161 ctx: &CodegenCtx,
1162 result_ty: &Type,
1163 members: &[TypedObjectMember],
1164 fields: &[crate::TypedObjectFieldOrigin],
1165) -> Result<(), crate::compiler_error::CompilerFailure> {
1166 if members
1167 .iter()
1168 .any(|member| matches!(member, TypedObjectMember::Computed { .. }))
1169 {
1170 emit_computed_object(emitter, ctx, members)?;
1171 return Ok(());
1172 }
1173 if !matches!(result_ty, Type::Object { .. } | Type::InterfaceRef { .. }) {
1174 return Err(crate::codegen::internal_failure(
1175 "object literal requires an object representation",
1176 ));
1177 }
1178 let structural_ty = crate::typed_ast::object_literal_layout(result_ty, fields);
1179 if members
1180 .iter()
1181 .any(|member| matches!(member, TypedObjectMember::Spread { .. }))
1182 {
1183 emit_object_spread(emitter, ctx, members, &structural_ty)?;
1184 return Ok(());
1185 }
1186 let vtable_idx = ctx
1187 .symbols
1188 .vtable_global_idx(&structural_ty)
1189 .ok_or_else(|| {
1190 crate::codegen::internal_failure("vtable global recorded during object-emission pass")
1191 })?;
1192 let object_shape_idx = ctx
1193 .symbols
1194 .object_subtype_idx(&structural_ty)
1195 .ok_or_else(|| {
1196 crate::codegen::internal_failure(
1197 "object shape type recorded during object-emission pass",
1198 )
1199 })?;
1200 let Type::Object {
1201 fields: declared_fields,
1202 ..
1203 } = &structural_ty
1204 else {
1205 return Err(crate::codegen::internal_failure(
1206 "object literal has no structural representation",
1207 ));
1208 };
1209 let shape_key: Vec<_> = declared_fields
1210 .iter()
1211 .map(|(name, field)| crate::codegen::field_names::FieldName {
1212 name: name.clone(),
1213 optional: field.optional,
1214 is_accessor: false,
1215 is_private: false,
1216 })
1217 .collect();
1218 let intrinsics = ctx
1219 .symbols
1220 .intrinsic_type_indices()
1221 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1222
1223 let evaluated = evaluate_object_members(emitter, ctx, members)?;
1225
1226 emitter.instruction(Instruction::GlobalGet(vtable_idx));
1227 crate::codegen::field_names::emit_instance_names(emitter, ctx, &shape_key, |name| {
1228 fields.iter().any(|field| {
1229 field.name.name == name && !matches!(field.source, TypedObjectFieldSource::Absent(_))
1230 })
1231 })?;
1232
1233 let by_name: std::collections::BTreeMap<&str, &crate::TypedObjectFieldOrigin> =
1238 fields.iter().map(|f| (f.name.name.as_str(), f)).collect();
1239 for (name, field) in declared_fields {
1240 if let Some(origin) = by_name.get(name.as_str()) {
1241 let (TypedObjectFieldSource::Literal(value) | TypedObjectFieldSource::Absent(value)) =
1242 &origin.source
1243 else {
1244 return Err(crate::codegen::internal_failure(
1245 "spread field reached plain object lowering",
1246 ));
1247 };
1248 emitter.instruction(Instruction::LocalGet(*evaluated.get(value).ok_or_else(
1249 || crate::codegen::internal_failure("object field value was not evaluated"),
1250 )?));
1251 } else {
1252 if !field.optional {
1253 return Err(crate::codegen::internal_failure(
1254 "required object field is missing from the literal",
1255 ));
1256 }
1257 emit_undefined(emitter, ctx)?;
1258 }
1259 }
1260 emitter.instruction(Instruction::ArrayNewFixed {
1261 array_type_index: intrinsics.object_fields,
1262 array_size: crate::codegen::wasm_u32(declared_fields.len())?,
1263 });
1264 emitter.instruction(Instruction::RefNull(HeapType::ANY));
1265 emitter.instruction(Instruction::StructNew(object_shape_idx));
1266
1267 Ok(())
1268}
1269
1270fn emit_field_access(
1271 emitter: &mut FunctionEmitter,
1272 ctx: &CodegenCtx,
1273 id: ExprId,
1274 result_ty: &Type,
1275 receiver: &ExprId,
1276 name: &Ident,
1277) -> Result<(), crate::compiler_error::CompilerFailure> {
1278 let receiver_ty = ctx
1279 .ta
1280 .source_type(*receiver)
1281 .map_err(crate::codegen::arena_failure)?
1282 .clone();
1283 if ctx.ta.has_string_index(&receiver_ty) {
1284 emit_expr(emitter, ctx, *receiver)?;
1285 cast::emit_box(
1286 emitter,
1287 ctx,
1288 &ctx.ta
1289 .try_expr(*receiver)
1290 .map_err(crate::codegen::arena_failure)?
1291 .ty,
1292 )?;
1293 let Some(symbol) = ctx.require(
1294 ctx.symbols.field_name_string_global_idx(&name.name),
1295 "property name collected",
1296 ) else {
1297 return Ok(());
1298 };
1299 emitter.instruction(Instruction::GlobalGet(symbol));
1300 let Some(symbol) = ctx.require(
1301 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
1302 "record read imported",
1303 ) else {
1304 return Ok(());
1305 };
1306 emitter.instruction(Instruction::Call(symbol));
1307 let checked_ty = ctx
1308 .ta
1309 .source_type(id)
1310 .map_err(crate::codegen::arena_failure)?;
1311 crate::codegen::cast_check::emit_checked_cast_on_stack(
1312 emitter,
1313 ctx,
1314 &Type::Unknown,
1315 checked_ty,
1316 )?;
1317 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
1318 return Ok(());
1319 }
1320 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
1322 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
1323 emit_receiver(emitter, ctx, *receiver)?;
1324 emit_class_method_value(
1325 emitter,
1326 ctx,
1327 &receiver_ty,
1328 &name.name,
1329 result_ty,
1330 ctx.ta
1331 .source_type(id)
1332 .map_err(crate::codegen::arena_failure)?,
1333 )?;
1334 return Ok(());
1335 }
1336 if ctx.symbols.class_field_slot(mangled, &name.name).is_none() {
1338 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
1339 emit_method_call(
1340 emitter,
1341 ctx,
1342 *receiver,
1343 mangled,
1344 &getter,
1345 &[],
1346 None,
1347 &[],
1348 None,
1349 &ctx.ta
1350 .try_expr(id)
1351 .map_err(crate::codegen::arena_failure)?
1352 .ty,
1353 )?;
1354 return Ok(());
1355 }
1356 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
1357 crate::codegen::internal_failure("class struct type recorded in classes::emit")
1358 })?;
1359 let slot = ctx
1360 .symbols
1361 .class_field_slot(mangled, &name.name)
1362 .ok_or_else(|| {
1363 crate::codegen::internal_failure("class field slot recorded in classes::emit")
1364 })?;
1365 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1366 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1367 })?;
1368 emit_receiver(emitter, ctx, *receiver)?;
1369 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&receiver_ty)?)?;
1370 emitter.instruction(Instruction::LocalTee(object));
1371 emitter.instruction(Instruction::StructGet {
1372 struct_type_index: struct_idx,
1373 field_index: 2,
1374 });
1375 emitter.instruction(Instruction::I32Const(slot as i32));
1376 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
1377 if ctx
1378 .symbols
1379 .class_field_narrowing_check(mangled, &name.name)
1380 .is_some()
1381 {
1382 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
1383 }
1384 emit_class_field_slot_cast_as(
1385 emitter,
1386 ctx,
1387 mangled,
1388 &name.name,
1389 &ctx.ta
1390 .try_expr(id)
1391 .map_err(crate::codegen::arena_failure)?
1392 .ty,
1393 ctx.ta
1394 .source_type(id)
1395 .map_err(crate::codegen::arena_failure)?,
1396 )?;
1397 return Ok(());
1398 }
1399 let intrinsics = ctx
1400 .symbols
1401 .intrinsic_type_indices()
1402 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1403 let field_ty = ctx
1406 .ta
1407 .try_expr(id)
1408 .map_err(crate::codegen::arena_failure)?
1409 .ty
1410 .clone();
1411 emit_receiver(emitter, ctx, *receiver)?;
1414 let rcv_local = stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
1415 emit_object_property_read_as(
1416 emitter,
1417 ctx,
1418 rcv_local,
1419 &name.name,
1420 &field_ty,
1421 ctx.ta
1422 .source_type(id)
1423 .map_err(crate::codegen::arena_failure)?,
1424 )?;
1425
1426 Ok(())
1427}
1428
1429fn emit_closure_value(
1430 emitter: &mut FunctionEmitter,
1431 ctx: &CodegenCtx,
1432 id: ExprId,
1433 result_ty: &Type,
1434 params: &[crate::TypedParam],
1435 captured: &[crate::CapturedVar],
1436 runtime_generics: &[String],
1437) -> Result<(), crate::compiler_error::CompilerFailure> {
1438 let closure_struct_idx = ctx
1456 .symbols
1457 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
1458 .ok_or_else(|| {
1459 crate::codegen::internal_failure(
1460 "closure struct type registered for every closure-typed expression",
1461 )
1462 })?;
1463 let env_type_idx = ctx.symbols.env_type_idx(id).ok_or_else(|| {
1464 crate::codegen::internal_failure("env type registered for every closure expression")
1465 })?;
1466 let closure_func_idx = ctx
1467 .symbols
1468 .closure_func_idx(id)
1469 .ok_or_else(|| crate::codegen::internal_failure("closure body function index allocated"))?;
1470 let closure_vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
1471 crate::codegen::internal_failure("closure vtable global emitted whenever closures exist")
1472 })?;
1473
1474 emitter.instruction(Instruction::GlobalGet(closure_vtable_idx));
1476
1477 emitter.instruction(Instruction::RefFunc(closure_func_idx));
1479
1480 for c in captured {
1483 emit_captured_load(emitter, ctx, c)?;
1484 }
1485 if !runtime_generics.is_empty() {
1486 let types: Vec<_> = runtime_generics
1487 .iter()
1488 .cloned()
1489 .map(Type::TypeVar)
1490 .collect();
1491 crate::codegen::runtime_descriptors::environment(emitter, ctx, &types)?;
1492 }
1493 let self_environment = if ctx.ta.closure_names.contains_key(&id) {
1494 emitter.instruction(Instruction::RefNull(HeapType::Concrete(
1495 ctx.symbols
1496 .intrinsic_type_indices()
1497 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
1498 .object,
1499 )));
1500 Some(emitter.add_anonymous_local(ValType::Ref(RefType {
1501 nullable: false,
1502 heap_type: HeapType::Concrete(env_type_idx),
1503 }))?)
1504 } else {
1505 None
1506 };
1507 emitter.instruction(Instruction::StructNew(env_type_idx));
1508 if let Some(local) = self_environment {
1509 emitter.instruction(Instruction::LocalTee(local));
1510 }
1511 if let Some(metadata) = crate::codegen::call_arguments::typed_metadata(params) {
1512 crate::codegen::call_arguments::wrap(emitter, ctx, &metadata)?;
1513 }
1514
1515 if ctx.ta.closure_this.contains_key(&id) {
1516 crate::codegen::this_binding::wrap(emitter, ctx)?;
1517 }
1518 emitter.instruction(Instruction::I64Const(0));
1522 emitter.instruction(Instruction::StructNew(closure_struct_idx));
1523 if let Some(environment) = self_environment {
1524 let closure = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1525 emitter.instruction(Instruction::LocalSet(closure));
1526 emitter.instruction(Instruction::LocalGet(environment));
1527 emitter.instruction(Instruction::LocalGet(closure));
1528 emitter.instruction(Instruction::StructSet {
1529 struct_type_index: env_type_idx,
1530 field_index: crate::codegen::wasm_u32(
1531 captured
1532 .len()
1533 .checked_add(usize::from(!runtime_generics.is_empty()))
1534 .ok_or_else(|| {
1535 crate::codegen::internal_failure("closure capture count overflow")
1536 })?,
1537 )?,
1538 });
1539 emitter.instruction(Instruction::LocalGet(closure));
1540 }
1541
1542 Ok(())
1543}
1544
1545fn inferred_never(
1548 ctx: &CodegenCtx,
1549 id: ExprId,
1550) -> Result<bool, crate::compiler_error::CompilerFailure> {
1551 let ty = ctx
1552 .ta
1553 .source_type(id)
1554 .map_err(crate::codegen::arena_failure)?;
1555 Ok(matches!(ty, Type::Never))
1556}
1557
1558fn emit_local_narrow_ref(
1559 emitter: &mut FunctionEmitter<'_>,
1560 ctx: &CodegenCtx<'_>,
1561 binding: &Ident,
1562 path: &ReferencePath,
1563 narrowed_ty: &Type,
1564) -> Result<(), crate::compiler_error::CompilerFailure> {
1565 if !path.chain.is_empty()
1566 && let Some(source) = emitter.narrow_source(&binding.name)
1567 {
1568 emit_expr(emitter, ctx, source)?;
1571 crate::codegen::cast_check::emit_checked_cast_on_stack(
1572 emitter,
1573 ctx,
1574 &ctx.ta
1575 .try_expr(source)
1576 .map_err(crate::codegen::arena_failure)?
1577 .ty,
1578 narrowed_ty,
1579 )?;
1580 return Ok(());
1581 }
1582 let Some((slot, slot_ty)) = emitter.narrowed_read_slot(&binding.name) else {
1583 if let BindingId::Local { name, .. } = &path.root
1588 && let Some((slot, slot_ty)) = emitter.narrowed_read_slot(name)
1589 {
1590 emitter.instruction(Instruction::LocalGet(slot));
1591 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1592 if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1593 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1594 }
1595 return Ok(());
1596 }
1597 if let BindingId::Global(mangled) = &path.root {
1598 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1599 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1600 })?;
1601 emitter.instruction(Instruction::GlobalGet(idx));
1602 let source_ty = ctx.symbols.global_type(mangled).ok_or_else(|| {
1603 crate::codegen::internal_failure("language globals retain their declared type")
1604 })?;
1605 crate::codegen::cast_check::emit_checked_cast_on_stack(
1606 emitter,
1607 ctx,
1608 source_ty,
1609 narrowed_ty,
1610 )?;
1611 return Ok(());
1612 }
1613 if let Some(source) = emitter.narrow_source(&binding.name) {
1614 emit_expr(emitter, ctx, source)?;
1615 let source_ty = &ctx
1616 .ta
1617 .try_expr(source)
1618 .map_err(crate::codegen::arena_failure)?
1619 .ty;
1620 let cast_info = cast_info_for(source_ty.clone(), narrowed_ty.clone());
1621 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1622 return Ok(());
1623 }
1624 return Err(crate::codegen::internal_failure(format!(
1625 "narrow binding `{}` (path {path:?}) not registered in codegen scope — \
1626 the region's source names a shadow from a scope that already closed",
1627 binding.name,
1628 )));
1629 };
1630 emitter.instruction(Instruction::LocalGet(slot));
1631 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1632 let _: () = if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1633 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1634 };
1635 Ok(())
1636}
1637
1638fn unbox_if_boxed(
1646 emitter: &mut FunctionEmitter<'_>,
1647 ctx: &CodegenCtx<'_>,
1648 slot_ty: ValType,
1649) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1650 let ValType::Ref(RefType {
1651 heap_type: HeapType::Concrete(idx),
1652 ..
1653 }) = slot_ty
1654 else {
1655 return Ok(slot_ty);
1656 };
1657 let Some(payload) = ctx.symbols.box_payload_type(idx) else {
1658 return Ok(slot_ty);
1659 };
1660 let local = emitter.add_anonymous_local(slot_ty)?;
1661 emitter.instruction(Instruction::LocalSet(local));
1662 super::binding_cells::read(emitter, ctx, local, idx)?;
1663 Ok(payload)
1664}
1665
1666fn emit_array_literal(
1669 emitter: &mut FunctionEmitter,
1670 ctx: &CodegenCtx,
1671 elements: &[crate::TypedArrayElement],
1672) -> Result<(), crate::compiler_error::CompilerFailure> {
1673 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1694 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1695 })?;
1696 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1697 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1698 })?;
1699 let array_vtable_global = ctx
1700 .symbols
1701 .prelude_global_idx("array_vtable")
1702 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1703
1704 let has_spread = elements
1705 .iter()
1706 .any(|e| matches!(e, crate::TypedArrayElement::Spread(_)));
1707
1708 if has_spread {
1709 emit_spread_array_literal(emitter, ctx, elements)?;
1710 } else {
1711 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1712 for el in elements {
1713 let elem_id = el.expr_id();
1714 emit_expr(emitter, ctx, elem_id)?;
1715 let elem_ty = ctx
1716 .ta
1717 .try_expr(elem_id)
1718 .map_err(crate::codegen::arena_failure)?
1719 .ty
1720 .clone();
1721 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &elem_ty)?;
1722 }
1723 emitter.instruction(Instruction::ArrayNewFixed {
1724 array_type_index: raw_array_idx,
1725 array_size: crate::codegen::wasm_u32(elements.len())?,
1726 });
1727 emitter.instruction(Instruction::I32Const(
1728 i32::try_from(elements.len())
1729 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1730 ));
1731 emitter.instruction(Instruction::StructNew(array_idx));
1732 }
1733 Ok(())
1734}
1735
1736fn emit_tuple_literal(
1737 emitter: &mut FunctionEmitter,
1738 ctx: &CodegenCtx,
1739 elements: &[ExprId],
1740) -> Result<(), crate::compiler_error::CompilerFailure> {
1741 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1743 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1744 })?;
1745 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1746 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1747 })?;
1748 let array_vtable_global = ctx
1749 .symbols
1750 .prelude_global_idx("array_vtable")
1751 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1752 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1753 for &elem_id in elements {
1754 emit_expr(emitter, ctx, elem_id)?;
1755 crate::codegen::function_emitter::cast::emit_box(
1756 emitter,
1757 ctx,
1758 &ctx.ta
1759 .try_expr(elem_id)
1760 .map_err(crate::codegen::arena_failure)?
1761 .ty,
1762 )?;
1763 }
1764 emitter.instruction(Instruction::ArrayNewFixed {
1765 array_type_index: raw_array_idx,
1766 array_size: crate::codegen::wasm_u32(elements.len())?,
1767 });
1768 emitter.instruction(Instruction::I32Const(
1769 i32::try_from(elements.len())
1770 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1771 ));
1772 emitter.instruction(Instruction::StructNew(array_idx));
1773 Ok(())
1774}
1775
1776fn emit_spread_array_literal(
1777 emitter: &mut FunctionEmitter,
1778 ctx: &CodegenCtx,
1779 elements: &[crate::TypedArrayElement],
1780) -> Result<(), crate::compiler_error::CompilerFailure> {
1781 let array = ctx
1782 .symbols
1783 .array_type_idx()
1784 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?;
1785 let raw = ctx
1786 .symbols
1787 .raw_array_type_idx()
1788 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?;
1789 let raw_type = ValType::Ref(RefType {
1790 nullable: false,
1791 heap_type: HeapType::Concrete(raw),
1792 });
1793 let total = emitter.add_anonymous_local(ValType::I32)?;
1794 let offset = emitter.add_anonymous_local(ValType::I32)?;
1795 let destination = emitter.add_anonymous_local(raw_type)?;
1796 emitter.instruction(Instruction::I32Const(0));
1797 emitter.instruction(Instruction::LocalSet(total));
1798 let mut chunks = Vec::with_capacity(elements.len());
1801 for element in elements {
1802 emit_literal_chunk(emitter, ctx, element, array, raw)?;
1803 let chunk = emitter.add_anonymous_local(raw_type)?;
1804 emitter.instruction(Instruction::LocalTee(chunk));
1805 emitter.instruction(Instruction::ArrayLen);
1806 emitter.instruction(Instruction::LocalGet(total));
1807 emitter.instruction(Instruction::I32Add);
1808 emitter.instruction(Instruction::LocalSet(total));
1809 chunks.push(chunk);
1810 }
1811 emitter.instruction(Instruction::LocalGet(total));
1812 emitter.instruction(Instruction::ArrayNewDefault(raw));
1813 emitter.instruction(Instruction::LocalSet(destination));
1814 emitter.instruction(Instruction::I32Const(0));
1815 emitter.instruction(Instruction::LocalSet(offset));
1816 for chunk in chunks {
1817 emitter.instruction(Instruction::LocalGet(destination));
1818 emitter.instruction(Instruction::LocalGet(offset));
1819 emitter.instruction(Instruction::LocalGet(chunk));
1820 emitter.instruction(Instruction::I32Const(0));
1821 emitter.instruction(Instruction::LocalGet(chunk));
1822 emitter.instruction(Instruction::ArrayLen);
1823 emitter.instruction(Instruction::ArrayCopy {
1824 array_type_index_dst: raw,
1825 array_type_index_src: raw,
1826 });
1827 emitter.instruction(Instruction::LocalGet(offset));
1828 emitter.instruction(Instruction::LocalGet(chunk));
1829 emitter.instruction(Instruction::ArrayLen);
1830 emitter.instruction(Instruction::I32Add);
1831 emitter.instruction(Instruction::LocalSet(offset));
1832 }
1833 emitter.instruction(Instruction::GlobalGet(
1834 ctx.symbols
1835 .prelude_global_idx("array_vtable")
1836 .ok_or_else(|| crate::codegen::internal_failure("array vtable declared"))?,
1837 ));
1838 emitter.instruction(Instruction::LocalGet(destination));
1839 emitter.instruction(Instruction::LocalGet(total));
1840 emitter.instruction(Instruction::StructNew(array));
1841 Ok(())
1842}
1843
1844fn emit_literal_chunk(
1845 emitter: &mut FunctionEmitter,
1846 ctx: &CodegenCtx,
1847 element: &crate::TypedArrayElement,
1848 array: u32,
1849 raw: u32,
1850) -> Result<(), crate::compiler_error::CompilerFailure> {
1851 if let crate::TypedArrayElement::Value(value) = element {
1852 emit_expr(emitter, ctx, *value)?;
1853 cast::emit_box(
1854 emitter,
1855 ctx,
1856 &ctx.ta
1857 .try_expr(*value)
1858 .map_err(crate::codegen::arena_failure)?
1859 .ty,
1860 )?;
1861 emitter.instruction(Instruction::ArrayNewFixed {
1862 array_type_index: raw,
1863 array_size: 1,
1864 });
1865 return Ok(());
1866 }
1867 emit_receiver(emitter, ctx, element.expr_id())?;
1870 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(array)));
1871
1872 let raw_type = ValType::Ref(RefType {
1873 nullable: false,
1874 heap_type: HeapType::Concrete(raw),
1875 });
1876 let source = emitter.add_anonymous_local(raw_type)?;
1877 let snapshot = emitter.add_anonymous_local(raw_type)?;
1878 let length = stash_array_length(emitter, array)?;
1879 emitter.instruction(Instruction::StructGet {
1880 struct_type_index: array,
1881 field_index: 1,
1882 });
1883 emitter.instruction(Instruction::LocalSet(source));
1884 emitter.instruction(Instruction::LocalGet(length));
1885 emitter.instruction(Instruction::ArrayNewDefault(raw));
1886 emitter.instruction(Instruction::LocalTee(snapshot));
1887 emitter.instruction(Instruction::I32Const(0));
1888 emitter.instruction(Instruction::LocalGet(source));
1889 emitter.instruction(Instruction::I32Const(0));
1890 emitter.instruction(Instruction::LocalGet(length));
1891 emitter.instruction(Instruction::ArrayCopy {
1892 array_type_index_dst: raw,
1893 array_type_index_src: raw,
1894 });
1895 emitter.instruction(Instruction::LocalGet(snapshot));
1896 Ok(())
1897}
1898
1899fn emit_postfix_unary(
1903 emitter: &mut FunctionEmitter<'_>,
1904 ctx: &CodegenCtx<'_>,
1905 op: crate::PostfixOp,
1906 target: &crate::PostfixTarget,
1907 result_ty: &Type,
1908) -> Result<(), crate::compiler_error::CompilerFailure> {
1909 let _: () = match target {
1910 crate::PostfixTarget::Local {
1911 ident,
1912 boxed,
1913 target_ty,
1914 } => {
1915 let slot = emitter.write_slot(&ident.name)?;
1917 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1918 let box_idx = if *boxed {
1919 Some(ctx.symbols.box_type_idx(target_ty)?.ok_or_else(|| {
1920 crate::codegen::internal_failure("box type registered for postfix operand")
1921 })?)
1922 } else {
1923 None
1924 };
1925 if let Some(box_idx) = box_idx {
1926 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
1927 } else {
1928 emitter.instruction(Instruction::LocalGet(slot));
1929 }
1930 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1931 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1932 emit_postfix_numeric(emitter, ctx, result_ty);
1933 emitter.instruction(Instruction::LocalSet(old));
1934 if box_idx.is_some() {
1935 emitter.instruction(Instruction::LocalGet(slot));
1936 }
1937 emitter.instruction(Instruction::LocalGet(old));
1938 emit_postfix_delta(emitter, ctx, op, result_ty);
1939 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1940 if let Some(box_idx) = box_idx {
1941 emitter.instruction(Instruction::StructSet {
1942 struct_type_index: box_idx,
1943 field_index: 0,
1944 });
1945 } else {
1946 emitter.instruction(Instruction::LocalSet(slot));
1947 }
1948 emitter.instruction(Instruction::LocalGet(old));
1949 }
1950 crate::PostfixTarget::Global {
1951 mangled, target_ty, ..
1952 } => {
1953 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1954 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1955 })?;
1956 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1957 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
1958 emitter.instruction(Instruction::GlobalGet(idx));
1959 if let ValType::Ref(RefType {
1961 nullable: false, ..
1962 }) = ctx.symbols.value_type(target_ty)?
1963 {
1964 emitter.instruction(Instruction::RefAsNonNull);
1965 }
1966 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1967 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1968 emit_postfix_numeric(emitter, ctx, result_ty);
1969 emitter.instruction(Instruction::LocalSet(old));
1970 emitter.instruction(Instruction::LocalGet(old));
1971 emit_postfix_delta(emitter, ctx, op, result_ty);
1972 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1973 emitter.instruction(Instruction::GlobalSet(idx));
1974 emitter.instruction(Instruction::LocalGet(old));
1975 }
1976 crate::PostfixTarget::Field {
1977 receiver,
1978 name,
1979 target_ty,
1980 } => {
1981 let receiver_ty = ctx
1986 .ta
1987 .source_type(*receiver)
1988 .map_err(crate::codegen::arena_failure)?
1989 .clone();
1990 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
1991 emit_class_field_postfix(
1992 emitter, ctx, *receiver, mangled, &name.name, op, target_ty,
1993 )?;
1994 return Ok(());
1995 }
1996 if let Some((getter, setter)) = host_property_accessors(ctx, &receiver_ty, name)? {
1997 emit_host_property_postfix(
1998 emitter,
1999 ctx,
2000 *receiver,
2001 &receiver_ty,
2002 (getter, setter),
2003 op,
2004 target_ty,
2005 )?;
2006 return Ok(());
2007 }
2008 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
2010 crate::codegen::internal_failure("intrinsics declared by codegen entry")
2011 })?;
2012 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2013 emit_receiver(emitter, ctx, *receiver)?;
2014 let rcv_local =
2015 stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
2016 emit_object_property_read(emitter, ctx, rcv_local, &name.name, target_ty)?;
2017 emit_postfix_numeric(emitter, ctx, target_ty);
2018 emitter.instruction(Instruction::LocalSet(tmp));
2019 emitter.instruction(Instruction::LocalGet(tmp));
2020 emit_postfix_delta(emitter, ctx, op, target_ty);
2021 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2022 emitter.instruction(Instruction::LocalSet(updated));
2023 emit_object_property_write_value(
2024 emitter,
2025 ctx,
2026 rcv_local,
2027 name,
2028 &ShapeArgument::Local {
2029 slot: updated,
2030 ty: target_ty.clone(),
2031 },
2032 )?;
2033 emitter.instruction(Instruction::LocalGet(tmp));
2034 }
2035 crate::PostfixTarget::Index {
2036 receiver, index, ..
2037 } => {
2038 emit_index_postfix(emitter, ctx, *receiver, *index, op)?;
2039 }
2040 };
2041 Ok(())
2042}
2043
2044enum HostPropertyGetter {
2046 Func(u32),
2047 InlineLength(u32),
2049}
2050
2051fn host_property_accessors(
2054 ctx: &CodegenCtx,
2055 receiver_ty: &Type,
2056 name: &Ident,
2057) -> Result<Option<(HostPropertyGetter, u32)>, crate::compiler_error::CompilerFailure> {
2058 let Some(iface) = crate::codegen::host_property_iface(receiver_ty) else {
2059 return Ok(None);
2060 };
2061 let setter_key = crate::mangle::extend(&iface, &crate::mangle::property_setter(&name.name));
2062 let Some(setter) = ctx.symbols.func_idx(&setter_key) else {
2063 return Ok(None);
2064 };
2065 if let Some(struct_idx) = inline_length_struct_idx(ctx, &iface, &name.name)? {
2066 return Ok(Some((HostPropertyGetter::InlineLength(struct_idx), setter)));
2067 }
2068 let getter = ctx
2069 .symbols
2070 .func_idx(&crate::mangle::extend(&iface, &name.name))
2071 .ok_or_else(|| {
2072 crate::codegen::internal_failure("host property getter recorded beside its setter")
2073 })?;
2074 Ok(Some((HostPropertyGetter::Func(getter), setter)))
2075}
2076
2077fn emit_host_property_postfix(
2078 emitter: &mut FunctionEmitter,
2079 ctx: &CodegenCtx,
2080 receiver: ExprId,
2081 receiver_ty: &Type,
2082 (getter, setter): (HostPropertyGetter, u32),
2083 op: crate::PostfixOp,
2084 target_ty: &Type,
2085) -> Result<(), crate::compiler_error::CompilerFailure> {
2086 let recv = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2088 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Number)?)?;
2089 emit_receiver(emitter, ctx, receiver)?;
2090 emitter.instruction(Instruction::LocalSet(recv));
2091 emitter.instruction(Instruction::LocalGet(recv));
2093 emitter.instruction(Instruction::RefAsNonNull);
2094 emitter.instruction(Instruction::LocalGet(recv));
2095 emitter.instruction(Instruction::RefAsNonNull);
2096 match getter {
2097 HostPropertyGetter::Func(getter) => {
2098 emitter.instruction(Instruction::Call(getter));
2099 }
2100 HostPropertyGetter::InlineLength(struct_idx) => {
2101 emit_inline_length(emitter, ctx, struct_idx);
2102 }
2103 }
2104 emitter.instruction(Instruction::LocalTee(old));
2105 emit_postfix_delta(emitter, ctx, op, &Type::Number);
2106 emitter.instruction(Instruction::Call(setter));
2107 emitter.instruction(Instruction::LocalGet(old));
2108 if target_ty != &Type::Number {
2109 cast::emit_box(emitter, ctx, &Type::Number)?;
2110 }
2111 Ok(())
2112}
2113
2114fn emit_index_postfix(
2115 emitter: &mut FunctionEmitter,
2116 ctx: &CodegenCtx,
2117 receiver: ExprId,
2118 index: ExprId,
2119 op: crate::PostfixOp,
2120) -> Result<(), crate::compiler_error::CompilerFailure> {
2121 let is_uint8 = ctx
2122 .ta
2123 .source_type(receiver)
2124 .map_err(crate::codegen::arena_failure)?
2125 .lowered()
2126 == &Type::Uint8Array;
2127 let result_ty = if is_uint8 {
2128 Type::Number
2129 } else {
2130 Type::Unknown
2131 };
2132 emit_expr(emitter, ctx, receiver)?;
2133 cast::emit_box(
2134 emitter,
2135 ctx,
2136 &ctx.ta
2137 .try_expr(receiver)
2138 .map_err(crate::codegen::arena_failure)?
2139 .ty,
2140 )?;
2141 let receiver_local = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
2142 emitter.instruction(Instruction::LocalSet(receiver_local));
2143 emit_expr(emitter, ctx, index)?;
2144 let key = emitter.add_anonymous_local(
2145 ctx.symbols.value_type(
2146 &ctx.ta
2147 .try_expr(index)
2148 .map_err(crate::codegen::arena_failure)?
2149 .ty,
2150 )?,
2151 )?;
2152 emitter.instruction(Instruction::LocalSet(key));
2153 let (backing, position, raw_type) = emit_index_location(
2154 emitter,
2155 ctx,
2156 receiver_local,
2157 key,
2158 &ctx.ta
2159 .try_expr(index)
2160 .map_err(crate::codegen::arena_failure)?
2161 .ty,
2162 is_uint8,
2163 )?;
2164 emitter.instruction(Instruction::LocalGet(backing));
2165 emitter.instruction(Instruction::LocalGet(position));
2166 if is_uint8 {
2167 emitter.instruction(Instruction::ArrayGetU(raw_type));
2168 emitter.instruction(Instruction::F64ConvertI32U);
2169 } else {
2170 emitter.instruction(Instruction::ArrayGet(raw_type));
2171 }
2172 emit_postfix_numeric(emitter, ctx, &result_ty);
2173 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2174 emitter.instruction(Instruction::LocalTee(old));
2175 emit_postfix_delta(emitter, ctx, op, &result_ty);
2176 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2177 emitter.instruction(Instruction::LocalSet(updated));
2178 let (backing, position, _) = emit_index_location(
2181 emitter,
2182 ctx,
2183 receiver_local,
2184 key,
2185 &ctx.ta
2186 .try_expr(index)
2187 .map_err(crate::codegen::arena_failure)?
2188 .ty,
2189 is_uint8,
2190 )?;
2191 emitter.instruction(Instruction::LocalGet(backing));
2192 emitter.instruction(Instruction::LocalGet(position));
2193 emitter.instruction(Instruction::LocalGet(updated));
2194 if is_uint8 {
2195 emitter.instruction(Instruction::I32TruncSatF64S);
2196 }
2197 emitter.instruction(Instruction::ArraySet(raw_type));
2198 emitter.instruction(Instruction::LocalGet(old));
2199 Ok(())
2200}
2201
2202fn emit_index_location(
2203 emitter: &mut FunctionEmitter,
2204 ctx: &CodegenCtx,
2205 receiver: u32,
2206 key: u32,
2207 key_ty: &Type,
2208 is_uint8: bool,
2209) -> Result<(u32, u32, u32), crate::compiler_error::CompilerFailure> {
2210 let (container, raw, ty) = if is_uint8 {
2211 (
2212 ctx.symbols
2213 .uint8_array_type_idx()
2214 .ok_or_else(|| crate::codegen::internal_failure("Uint8Array declared"))?,
2215 ctx.symbols
2216 .raw_uint8_array_type_idx()
2217 .ok_or_else(|| crate::codegen::internal_failure("raw Uint8Array declared"))?,
2218 Type::Uint8Array,
2219 )
2220 } else {
2221 (
2222 ctx.symbols
2223 .array_type_idx()
2224 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?,
2225 ctx.symbols
2226 .raw_array_type_idx()
2227 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?,
2228 Type::Array(Box::new(Type::Unknown)),
2229 )
2230 };
2231 emitter.instruction(Instruction::LocalGet(receiver));
2232 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, &Type::Unknown, &ty)?;
2233 emitter.instruction(Instruction::LocalGet(key));
2234 emit_index_number(emitter, ctx, key_ty)?;
2235 let operand = stash_index_operand(emitter)?;
2236 let length = (!is_uint8)
2237 .then(|| stash_array_length(emitter, container))
2238 .transpose()?;
2239 emitter.instruction(Instruction::StructGet {
2240 struct_type_index: container,
2241 field_index: 1,
2242 });
2243 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
2244 nullable: false,
2245 heap_type: HeapType::Concrete(raw),
2246 }))?;
2247 emitter.instruction(Instruction::LocalSet(backing));
2248 let position = match length {
2249 Some(length) => emit_checked_index_with_length(emitter, ctx, length, operand)?,
2250 None => emit_checked_index(emitter, ctx, backing, operand)?,
2251 };
2252 Ok((backing, position, raw))
2253}
2254
2255fn emit_postfix_numeric(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, ty: &Type) {
2256 ctx.latch(emit_postfix_numeric_checked(emitter, ctx, ty));
2257}
2258
2259fn emit_postfix_numeric_checked(
2260 emitter: &mut FunctionEmitter,
2261 ctx: &CodegenCtx,
2262 ty: &Type,
2263) -> Result<(), crate::compiler_error::CompilerFailure> {
2264 if matches!(ty, Type::Unknown) {
2265 let function = ctx
2266 .symbols
2267 .prelude_func_idx("__value_numeric")
2268 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2269 emitter.instruction(Instruction::Call(function));
2270 }
2271
2272 Ok(())
2273}
2274
2275fn emit_postfix_delta(
2276 emitter: &mut FunctionEmitter<'_>,
2277 ctx: &CodegenCtx<'_>,
2278 op: crate::PostfixOp,
2279 ty: &Type,
2280) {
2281 ctx.latch(emit_postfix_delta_checked(emitter, ctx, op, ty));
2282}
2283
2284fn emit_postfix_delta_checked(
2285 emitter: &mut FunctionEmitter<'_>,
2286 ctx: &CodegenCtx<'_>,
2287 op: crate::PostfixOp,
2288 ty: &Type,
2289) -> Result<(), crate::compiler_error::CompilerFailure> {
2290 if matches!(ty, Type::Unknown) {
2291 let name = if matches!(op, crate::PostfixOp::Inc) {
2292 "inc"
2293 } else {
2294 "dec"
2295 };
2296 let function = ctx
2297 .symbols
2298 .prelude_func_idx(&format!("__value_{name}"))
2299 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2300 emitter.instruction(Instruction::Call(function));
2301 return Ok(());
2302 }
2303 if ty.is_bigint() {
2304 emit_bigint_pm_one(emitter, ctx, op);
2305 return Ok(());
2306 }
2307 emitter.instruction(Instruction::F64Const(Ieee64::from(1.0)));
2308 emitter.instruction(match op {
2309 crate::PostfixOp::Inc => Instruction::F64Add,
2310 crate::PostfixOp::Dec => Instruction::F64Sub,
2311 crate::PostfixOp::NonNullAssert => {
2312 return Err(crate::codegen::internal_failure(
2313 "non-null assertion is not PostfixUnary",
2314 ));
2315 }
2316 });
2317
2318 Ok(())
2319}
2320
2321#[allow(clippy::too_many_arguments)]
2326fn emit_class_field_postfix(
2327 emitter: &mut FunctionEmitter<'_>,
2328 ctx: &CodegenCtx<'_>,
2329 receiver: ExprId,
2330 mangled: &crate::MangledName,
2331 field: &str,
2332 op: crate::PostfixOp,
2333 target_ty: &Type,
2334) -> Result<(), crate::compiler_error::CompilerFailure> {
2335 let intrinsics = ctx
2336 .symbols
2337 .intrinsic_type_indices()
2338 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
2339 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
2340 crate::codegen::internal_failure("class struct type recorded in classes::emit")
2341 })?;
2342 let slot = ctx
2345 .symbols
2346 .class_field_slot(mangled, field)
2347 .ok_or_else(|| {
2348 crate::codegen::internal_failure(
2349 "data-field slot recorded in classes::emit — accessors are rejected in infer",
2350 )
2351 })?;
2352 let fields_local = emitter.add_anonymous_local(ValType::Ref(RefType {
2353 nullable: false,
2354 heap_type: HeapType::Concrete(intrinsics.object_fields),
2355 }))?;
2356 let old = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2357 let object = emitter.add_anonymous_local(
2358 ctx.symbols.value_type(
2359 ctx.ta
2360 .source_type(receiver)
2361 .map_err(crate::codegen::arena_failure)?,
2362 )?,
2363 )?;
2364 emit_receiver(emitter, ctx, receiver)?;
2365 emitter.instruction(Instruction::LocalTee(object));
2366 emitter.instruction(Instruction::StructGet {
2367 struct_type_index: struct_idx,
2368 field_index: 2,
2369 });
2370 emitter.instruction(Instruction::LocalSet(fields_local));
2371 emitter.instruction(Instruction::LocalGet(fields_local));
2372 emitter.instruction(Instruction::I32Const(slot as i32));
2373 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
2374 if ctx
2375 .symbols
2376 .class_field_narrowing_check(mangled, field)
2377 .is_some()
2378 {
2379 crate::codegen::field_guards::check(emitter, ctx, object, mangled, field)?;
2380 }
2381 emit_class_field_slot_cast(emitter, ctx, mangled, field, target_ty)?;
2382 emit_postfix_numeric(emitter, ctx, target_ty);
2383 emitter.instruction(Instruction::LocalSet(old));
2384 emitter.instruction(Instruction::LocalGet(fields_local));
2385 emitter.instruction(Instruction::I32Const(slot as i32));
2386 emitter.instruction(Instruction::LocalGet(old));
2387 emit_postfix_delta(emitter, ctx, op, target_ty);
2388 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, target_ty)?;
2389 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
2390 emitter.instruction(Instruction::LocalGet(old));
2391 Ok(())
2392}
2393
2394fn emit_chain_parts(
2402 emitter: &mut FunctionEmitter<'_>,
2403 ctx: &CodegenCtx<'_>,
2404 parts: &[crate::TypedChainPart],
2405 idx: usize,
2406 receiver_ty: &Type,
2407 result_ty: &Type,
2408 source_types: Option<&[Type]>,
2409) -> Result<(), crate::compiler_error::CompilerFailure> {
2410 if source_types.is_some_and(|types| parts.len().checked_add(1) != Some(types.len())) {
2411 return Err(crate::codegen::internal_failure(
2412 "optional-chain source type count mismatch",
2413 ));
2414 }
2415 if idx == parts.len() {
2416 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
2421 emitter,
2422 ctx,
2423 receiver_ty,
2424 result_ty,
2425 )?;
2426 return Ok(());
2427 }
2428 let part = parts.get(idx).ok_or_else(|| {
2429 crate::codegen::internal_failure("optional-chain part index is out of bounds")
2430 })?;
2431 let next = idx
2432 .checked_add(1)
2433 .ok_or_else(|| crate::codegen::internal_failure("optional-chain index overflow"))?;
2434 let source_ty = source_types.and_then(|types| types.get(idx));
2435 let check_ty = source_types.and_then(|types| types.get(next));
2436 let saved_receiver = if matches!(
2437 part,
2438 crate::TypedChainPart::Field { .. }
2439 | crate::TypedChainPart::Index { .. }
2440 | crate::TypedChainPart::InterfaceProperty { .. }
2441 ) {
2442 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2443 emitter.instruction(Instruction::LocalTee(slot));
2444 Some(slot)
2445 } else if matches!(part, crate::TypedChainPart::NonNull { .. }) {
2446 emitter.call_receiver
2447 } else {
2448 None
2449 };
2450 let part_result_ty = part.result_ty().clone();
2451 let recv_val = part
2454 .is_optional()
2455 .then(|| ctx.symbols.value_type(receiver_ty))
2456 .transpose()?;
2457 let _: () = if let Some(recv_val @ ValType::Ref(_)) = recv_val {
2463 let block_ty = BlockType::Result(ctx.symbols.value_type(result_ty)?);
2464 let tmp = emitter.add_anonymous_local(recv_val)?;
2465 emitter.instruction(Instruction::LocalTee(tmp));
2466 emit_is_nullish(emitter, ctx)?;
2467 emitter.emit_if(block_ty);
2468 emit_undefined_for_chain_result(emitter, ctx, result_ty)?;
2469 emitter.emit_else();
2470 emitter.instruction(Instruction::LocalGet(tmp));
2471 emitter.instruction(Instruction::RefAsNonNull);
2472 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2473 emitter.call_receiver = saved_receiver;
2474 emit_chain_parts(
2475 emitter,
2476 ctx,
2477 parts,
2478 next,
2479 &part_result_ty,
2480 result_ty,
2481 source_types,
2482 )?;
2483 emitter.emit_end();
2484 } else {
2485 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2486 emitter.call_receiver = saved_receiver;
2487 emit_chain_parts(
2488 emitter,
2489 ctx,
2490 parts,
2491 next,
2492 &part_result_ty,
2493 result_ty,
2494 source_types,
2495 )?;
2496 };
2497 Ok(())
2498}
2499
2500fn emit_chain_operation(
2501 emitter: &mut FunctionEmitter,
2502 ctx: &CodegenCtx,
2503 part: &crate::TypedChainPart,
2504 receiver_ty: &Type,
2505 source_ty: Option<&Type>,
2506 check_ty: Option<&Type>,
2507) -> Result<(), crate::compiler_error::CompilerFailure> {
2508 if let crate::TypedChainPart::MethodCall {
2509 iface,
2510 name,
2511 result_ty,
2512 span,
2513 ..
2514 } = part
2515 && !crate::codegen::is_host_package(iface.as_str())
2516 && let Some(args) = ctx.ta.authored_call_arguments(*span)
2517 {
2518 cast::emit_box(emitter, ctx, receiver_ty)?;
2519 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2520 cast::emit_cast_to(emitter, ctx, result_ty)?;
2521 return Ok(());
2522 }
2523 if receiver_ty == &Type::Unknown {
2524 match part {
2525 crate::TypedChainPart::MethodCall {
2526 iface,
2527 name,
2528 args,
2529 result_ty,
2530 span,
2531 ..
2532 } if crate::codegen::runtime_values::dynamic_member_interface(iface) => {
2533 let args = ctx.ta.authored_call_arguments(*span).unwrap_or(args);
2534 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2535 return Ok(());
2536 }
2537 crate::TypedChainPart::InterfaceProperty { iface, name, .. }
2538 if crate::codegen::runtime_values::dynamic_member_interface(iface) =>
2539 {
2540 emit_live_member_on_stack(emitter, ctx, iface, &name.name, None)?;
2541 return Ok(());
2542 }
2543 _ => {}
2544 }
2545 }
2546 let operation_ty =
2547 crate::typechecker::infer::narrowing::strip_nullish(source_ty.unwrap_or(receiver_ty));
2548 if !matches!(
2549 part,
2550 crate::TypedChainPart::Index { .. }
2551 | crate::TypedChainPart::Call { .. }
2552 | crate::TypedChainPart::NonNull { .. }
2553 ) {
2554 let receiver_slot = ctx.symbols.value_type(receiver_ty)?;
2555 if receiver_slot == ctx.symbols.value_type(&operation_ty)? {
2556 if matches!(receiver_slot, ValType::Ref(_)) {
2557 cast::emit_cast_to(emitter, ctx, &operation_ty)?;
2558 }
2559 } else {
2560 crate::codegen::cast_check::emit_operation_cast_on_stack(
2561 emitter,
2562 ctx,
2563 receiver_ty,
2564 &operation_ty,
2565 )?;
2566 }
2567 }
2568 emit_chain_access(
2569 emitter,
2570 ctx,
2571 part,
2572 &operation_ty,
2573 check_ty.unwrap_or(part.result_ty()),
2574 )?;
2575 Ok(())
2576}
2577
2578fn emit_live_member_on_stack(
2579 emitter: &mut FunctionEmitter,
2580 ctx: &CodegenCtx,
2581 iface: &crate::MangledName,
2582 name: &str,
2583 args: Option<&[ExprId]>,
2584) -> Result<(), crate::compiler_error::CompilerFailure> {
2585 super::emit_const_string_by_text(emitter, ctx, name)?;
2586 super::emit_const_string_by_text(emitter, ctx, iface.as_str())?;
2587 let helper = if args.is_some() {
2588 "__value_member"
2589 } else {
2590 "__value_property"
2591 };
2592 emitter.instruction(Instruction::Call(
2593 ctx.symbols
2594 .prelude_func_idx(helper)
2595 .ok_or_else(|| crate::codegen::internal_failure("live member helper collected"))?,
2596 ));
2597 let Some(args) = args else {
2598 return Ok(());
2599 };
2600 let intr = ctx
2601 .symbols
2602 .intrinsic_type_indices()
2603 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2604 emitter.instruction(Instruction::GlobalGet(
2605 ctx.symbols
2606 .prelude_global_idx("array_vtable")
2607 .ok_or_else(|| crate::codegen::internal_failure("array vtable"))?,
2608 ));
2609 for &arg in args {
2610 emit_expr(emitter, ctx, arg)?;
2611 cast::emit_box(
2612 emitter,
2613 ctx,
2614 &ctx.ta
2615 .try_expr(arg)
2616 .map_err(crate::codegen::arena_failure)?
2617 .ty,
2618 )?;
2619 }
2620 emitter.instruction(Instruction::ArrayNewFixed {
2621 array_type_index: intr.raw_array,
2622 array_size: crate::codegen::wasm_u32(args.len())?,
2623 });
2624 emitter.instruction(Instruction::I32Const(i32::try_from(args.len()).map_err(
2625 |_| crate::codegen::internal_failure("argument array too large"),
2626 )?));
2627 emitter.instruction(Instruction::StructNew(intr.array));
2628 emitter.instruction(Instruction::Call(
2629 ctx.symbols
2630 .prelude_func_idx("__value_invoke")
2631 .ok_or_else(|| crate::codegen::internal_failure("live invocation collected"))?,
2632 ));
2633 Ok(())
2634}
2635
2636fn inline_length_struct_idx(
2639 ctx: &CodegenCtx<'_>,
2640 iface: &crate::MangledName,
2641 prop: &str,
2642) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
2643 let key = crate::mangle::extend(iface, prop);
2644 if !ctx.symbols.is_intrinsic_member(&key) {
2645 return Ok(None);
2646 }
2647 let index = if *iface == crate::mangle::prelude("String") && prop == "length" {
2648 ctx.symbols.string_type_idx()
2649 } else if *iface == crate::mangle::prelude("Array") && prop == "length" {
2650 ctx.symbols.array_type_idx()
2651 } else {
2652 return Err(crate::codegen::internal_failure(format!(
2653 "no inline lowering for intrinsic member `{}`",
2654 key.as_str()
2655 )));
2656 };
2657 index
2658 .map(Some)
2659 .ok_or_else(|| crate::codegen::internal_failure("length receiver intrinsic missing"))
2660}
2661
2662fn emit_inline_length(emitter: &mut FunctionEmitter<'_>, ctx: &CodegenCtx<'_>, struct_idx: u32) {
2664 let is_array = ctx.symbols.array_type_idx() == Some(struct_idx);
2665 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
2666 emitter.instruction(Instruction::StructGet {
2667 struct_type_index: struct_idx,
2668 field_index: if is_array { 2 } else { 1 },
2669 });
2670 if !is_array {
2671 emitter.instruction(Instruction::ArrayLen);
2672 }
2673 emitter.instruction(Instruction::F64ConvertI32U);
2674}
2675
2676fn emit_undefined_for_chain_result(
2678 emitter: &mut FunctionEmitter<'_>,
2679 ctx: &CodegenCtx<'_>,
2680 result_ty: &Type,
2681) -> Result<(), crate::compiler_error::CompilerFailure> {
2682 emit_undefined(emitter, ctx)?;
2683 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)
2684}
2685
2686fn emit_class_field_slot_cast(
2690 emitter: &mut FunctionEmitter,
2691 ctx: &CodegenCtx,
2692 class: &crate::MangledName,
2693 field: &str,
2694 result_ty: &Type,
2695) -> Result<(), crate::compiler_error::CompilerFailure> {
2696 emit_class_field_slot_cast_as(emitter, ctx, class, field, result_ty, result_ty)?;
2697 Ok(())
2698}
2699
2700fn emit_class_field_slot_cast_as(
2701 emitter: &mut FunctionEmitter<'_>,
2702 ctx: &CodegenCtx<'_>,
2703 class: &crate::MangledName,
2704 field: &str,
2705 result_ty: &Type,
2706 check_ty: &Type,
2707) -> Result<(), crate::compiler_error::CompilerFailure> {
2708 let _: () = match ctx.symbols.class_field_narrowing_check(class, field) {
2709 Some(check) => {
2710 crate::codegen::cast_check::emit_narrowed_field_read_as(
2711 emitter, ctx, check, result_ty, check_ty,
2712 )?;
2713 }
2714 None => crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?,
2715 };
2716 Ok(())
2717}
2718
2719fn emit_object_spread(
2720 emitter: &mut FunctionEmitter,
2721 ctx: &CodegenCtx,
2722 sources: &[TypedObjectMember],
2723 shape: &Type,
2724) -> Result<(), crate::compiler_error::CompilerFailure> {
2725 let intrinsics = ctx
2726 .symbols
2727 .intrinsic_type_indices()
2728 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2729 let merge = ctx
2730 .symbols
2731 .prelude_func_idx("ObjectConstructor##spread")
2732 .ok_or_else(|| crate::codegen::internal_failure("spread helper collected"))?;
2733 let null_object = Instruction::RefNull(HeapType::Concrete(intrinsics.object));
2734 emitter.instruction(null_object.clone());
2735 for (index, source) in sources.iter().enumerate() {
2736 let final_shape = (index + 1 == sources.len()).then_some(shape);
2737 emit_receiver(emitter, ctx, source.expr_id())?;
2741 let narrowed_ty = ctx
2742 .ta
2743 .source_type(source.expr_id())
2744 .map_err(crate::codegen::arena_failure)?;
2745 let non_object_source = if may_hold_non_object(narrowed_ty) {
2750 Some(emit_object_source_test(
2751 emitter,
2752 ctx,
2753 narrowed_ty,
2754 intrinsics.object,
2755 intrinsics.object_shape,
2756 )?)
2757 } else {
2758 None
2759 };
2760 let source_local =
2761 stash_receiver_as_object_shape(emitter, narrowed_ty, intrinsics.object_shape)?;
2762 emitter.instruction(Instruction::LocalGet(source_local));
2763 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2764 if matches!(source, TypedObjectMember::Spread { by_name: true, .. }) {
2765 emit_spread_mask(emitter, ctx, source_local, source.expr_id(), shape)?;
2766 } else {
2767 emitter.instruction(null_object.clone());
2768 }
2769 emitter.instruction(Instruction::Call(merge));
2770 if let Some(NonObjectSource {
2771 accumulator,
2772 opaque,
2773 }) = non_object_source
2774 {
2775 emitter.emit_else();
2776 emitter.instruction(Instruction::LocalGet(accumulator));
2777 match opaque {
2780 Some(source) => emitter.instruction(Instruction::LocalGet(source)),
2781 None => emitter.instruction(null_object.clone()),
2782 }
2783 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2784 emitter.instruction(null_object.clone());
2785 emitter.instruction(Instruction::Call(merge));
2786 emitter.emit_end();
2787 }
2788 }
2789 Ok(())
2790}
2791
2792fn emit_spread_shape_argument(
2795 emitter: &mut FunctionEmitter,
2796 ctx: &CodegenCtx,
2797 final_shape: Option<&Type>,
2798 null_object: &Instruction<'static>,
2799) {
2800 match final_shape {
2801 Some(shape) => emit_spread_shape(emitter, ctx, shape),
2802 None => emitter.instruction(null_object.clone()),
2803 }
2804}
2805
2806fn may_hold_non_object(ty: &Type) -> bool {
2810 match ty.lowered() {
2811 Type::Union(members) => members.iter().any(may_hold_non_object),
2812 Type::Null
2813 | Type::Undefined
2814 | Type::Boolean
2815 | Type::BooleanLiteral(_)
2816 | Type::Number
2817 | Type::NumberLiteral(_)
2818 | Type::String
2819 | Type::StringLiteral(_)
2820 | Type::Unknown => true,
2822 _ => false,
2823 }
2824}
2825
2826struct NonObjectSource {
2828 accumulator: u32,
2829 opaque: Option<u32>,
2832}
2833
2834fn emit_object_source_test(
2839 emitter: &mut FunctionEmitter,
2840 ctx: &CodegenCtx,
2841 source_ty: &Type,
2842 object: u32,
2843 object_shape: u32,
2844) -> Result<NonObjectSource, crate::compiler_error::CompilerFailure> {
2845 let source_type = match ctx.symbols.value_type(source_ty)? {
2846 ValType::Ref(reference) => ValType::Ref(RefType {
2847 nullable: true,
2848 ..reference
2849 }),
2850 other => other,
2851 };
2852 let opaque = source_type == object_ref(object);
2853 let source = emitter.add_anonymous_local(source_type)?;
2854 emitter.instruction(Instruction::LocalSet(source));
2855 let accumulator = emitter.add_anonymous_local(object_ref(object))?;
2856 emitter.instruction(Instruction::LocalSet(accumulator));
2857 emitter.instruction(Instruction::LocalGet(source));
2858 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
2859 object_shape,
2860 )));
2861 emitter.emit_if(BlockType::Result(ValType::Ref(RefType {
2862 nullable: false,
2863 heap_type: HeapType::Concrete(object_shape),
2864 })));
2865 emitter.instruction(Instruction::LocalGet(accumulator));
2866 emitter.instruction(Instruction::LocalGet(source));
2867 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
2868 object_shape,
2869 )));
2870 Ok(NonObjectSource {
2871 accumulator,
2872 opaque: opaque.then_some(source),
2873 })
2874}
2875
2876fn emit_spread_mask(
2880 emitter: &mut FunctionEmitter,
2881 ctx: &CodegenCtx,
2882 source: u32,
2883 source_expr: crate::ExprId,
2884 shape: &Type,
2885) -> Result<(), crate::compiler_error::CompilerFailure> {
2886 let checked = ctx
2887 .ta
2888 .spread_mask_fields
2889 .get(&source_expr)
2890 .ok_or_else(|| crate::codegen::internal_failure("by-name spread fields recorded"))?;
2891 let omitted = ctx.ta.spread_omitted_fields.get(&source_expr);
2894 let fields: Vec<(&String, Option<&Type>)> = checked
2895 .iter()
2896 .map(|(name, ty)| (name, Some(ty)))
2897 .chain(omitted.into_iter().flatten().map(|name| (name, None)))
2898 .collect();
2899 let intrinsics = ctx
2900 .symbols
2901 .intrinsic_type_indices()
2902 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2903 let vtable = ctx
2904 .symbols
2905 .vtable_global_idx(shape)
2906 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2907 emitter.instruction(Instruction::GlobalGet(vtable));
2908 for (name, _) in &fields {
2909 let global = ctx
2910 .symbols
2911 .field_name_string_global_idx(name)
2912 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2913 emitter.instruction(Instruction::GlobalGet(global));
2914 }
2915 emitter.instruction(Instruction::ArrayNewFixed {
2916 array_type_index: intrinsics.field_names,
2917 array_size: crate::codegen::wasm_u32(fields.len())?,
2918 });
2919 for (name, ty) in fields.iter().copied() {
2920 let global = ctx
2921 .symbols
2922 .field_name_string_global_idx(name)
2923 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2924 let Some(ty) = ty else {
2925 emitter.instruction(Instruction::GlobalGet(global));
2926 continue;
2927 };
2928 let index = emitter.add_anonymous_local(ValType::I32)?;
2929 let value = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
2930 emit_object_field_index_by_name(emitter, ctx, source, global);
2931 emitter.instruction(Instruction::LocalTee(index));
2932 emitter.instruction(Instruction::I32Const(0));
2933 emitter.instruction(Instruction::I32GeS);
2934 emitter.emit_if(BlockType::Result(ValType::I32));
2935 emit_field_slot_get(emitter, intrinsics, source, index);
2936 emitter.instruction(Instruction::LocalSet(value));
2937 emit_field_holds_value_of(emitter, ctx, source, index, value, ty)?;
2938 emitter.emit_else();
2939 emitter.instruction(Instruction::I32Const(0));
2940 emitter.emit_end();
2941 emitter.emit_if(BlockType::Result(object_ref(intrinsics.object)));
2942 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intrinsics.object)));
2943 emitter.emit_else();
2944 emitter.instruction(Instruction::GlobalGet(global));
2945 emitter.emit_end();
2946 }
2947 emitter.instruction(Instruction::ArrayNewFixed {
2948 array_type_index: intrinsics.object_fields,
2949 array_size: crate::codegen::wasm_u32(fields.len())?,
2950 });
2951 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2952 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2953 Ok(())
2954}
2955
2956fn emit_spread_shape(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, shape: &Type) {
2959 ctx.latch(emit_spread_shape_checked(emitter, ctx, shape));
2960}
2961
2962fn emit_spread_shape_checked(
2963 emitter: &mut FunctionEmitter,
2964 ctx: &CodegenCtx,
2965 shape: &Type,
2966) -> Result<(), crate::compiler_error::CompilerFailure> {
2967 let Type::Object { fields, .. } = shape else {
2968 return Err(crate::codegen::internal_failure("structural spread shape"));
2969 };
2970 let intrinsics = ctx
2971 .symbols
2972 .intrinsic_type_indices()
2973 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2974 let names: Vec<_> = fields
2975 .iter()
2976 .map(|(name, field)| crate::codegen::field_names::FieldName {
2977 name: name.clone(),
2978 optional: field.optional,
2979 is_accessor: false,
2980 is_private: false,
2981 })
2982 .collect();
2983 let names_global = ctx
2984 .symbols
2985 .field_names_global_idx(&names)
2986 .ok_or_else(|| crate::codegen::internal_failure("spread shape names collected"))?;
2987 let vtable = ctx
2988 .symbols
2989 .vtable_global_idx(shape)
2990 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2991 emitter.instruction(Instruction::GlobalGet(vtable));
2992 emitter.instruction(Instruction::GlobalGet(names_global));
2993 emitter.instruction(Instruction::I32Const(
2994 crate::codegen::wasm_u32(fields.len())? as i32,
2995 ));
2996 emitter.instruction(Instruction::ArrayNewDefault(intrinsics.object_fields));
2997 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2998 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2999
3000 Ok(())
3001}
3002
3003pub(super) fn stash_receiver_as_object_shape(
3019 emitter: &mut FunctionEmitter<'_>,
3020 receiver_ty: &Type,
3021 object_shape: u32,
3022) -> Result<u32, crate::compiler_error::CompilerFailure> {
3023 coerce_field_receiver_to_object_shape(emitter, receiver_ty, object_shape);
3024 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
3025 nullable: false,
3026 heap_type: HeapType::Concrete(object_shape),
3027 }))?;
3028 emitter.instruction(Instruction::LocalSet(rcv_local));
3029 Ok(rcv_local)
3030}
3031
3032fn coerce_field_receiver_to_object_shape(
3033 emitter: &mut FunctionEmitter<'_>,
3034 receiver_ty: &Type,
3035 object_shape: u32,
3036) {
3037 if crate::codegen::may_hold_builtin_value(receiver_ty) {
3038 emitter.instruction(Instruction::RefAsNonNull);
3039 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
3040 object_shape,
3041 )));
3042 }
3043}
3044
3045fn emit_chain_access(
3051 emitter: &mut FunctionEmitter<'_>,
3052 ctx: &CodegenCtx<'_>,
3053 part: &crate::TypedChainPart,
3054 receiver_ty: &Type,
3055 check_ty: &Type,
3056) -> Result<(), crate::compiler_error::CompilerFailure> {
3057 let _: () = match part {
3058 crate::TypedChainPart::Field {
3059 name, result_ty, ..
3060 } => {
3061 if ctx.ta.has_string_index(receiver_ty) {
3062 let Some(symbol) = ctx.require(
3063 ctx.symbols.field_name_string_global_idx(&name.name),
3064 "property name collected",
3065 ) else {
3066 return Ok(());
3067 };
3068 emitter.instruction(Instruction::GlobalGet(symbol));
3069 let Some(symbol) = ctx.require(
3070 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3071 "record read imported",
3072 ) else {
3073 return Ok(());
3074 };
3075 emitter.instruction(Instruction::Call(symbol));
3076 crate::codegen::cast_check::emit_checked_cast_on_stack(
3077 emitter,
3078 ctx,
3079 &Type::Unknown,
3080 check_ty,
3081 )?;
3082 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3083 return Ok(());
3084 }
3085 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
3086 crate::codegen::internal_failure("intrinsics declared by codegen entry")
3087 })?;
3088 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
3092 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
3093 emit_class_method_value(
3094 emitter,
3095 ctx,
3096 receiver_ty,
3097 &name.name,
3098 result_ty,
3099 check_ty,
3100 )?;
3101 return Ok(());
3102 }
3103 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
3104 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
3105 emit_class_vtable_dispatch(emitter, ctx, mangled, &getter, &[], result_ty)?;
3106 return Ok(());
3107 };
3108 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3109 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3110 })?;
3111 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
3112 nullable: false,
3113 heap_type: HeapType::Concrete(struct_idx),
3114 }))?;
3115 emitter.instruction(Instruction::LocalTee(object));
3116 emitter.instruction(Instruction::StructGet {
3117 struct_type_index: struct_idx,
3118 field_index: 2,
3119 });
3120 emitter.instruction(Instruction::I32Const(slot as i32));
3121 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3122 if ctx
3123 .symbols
3124 .class_field_narrowing_check(mangled, &name.name)
3125 .is_some()
3126 {
3127 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
3128 }
3129 emit_class_field_slot_cast_as(
3130 emitter, ctx, mangled, &name.name, result_ty, check_ty,
3131 )?;
3132 return Ok(());
3133 }
3134 let rcv_local =
3135 stash_receiver_as_object_shape(emitter, receiver_ty, intrinsics.object_shape)?;
3136 emit_object_property_read_as(emitter, ctx, rcv_local, &name.name, result_ty, check_ty)?;
3137 }
3138 crate::TypedChainPart::Index { idx, result_ty, .. } => {
3139 if receiver_ty.is_structural_object() {
3140 emit_expr(emitter, ctx, *idx)?;
3141 crate::codegen::cast_check::emit_operation_cast_on_stack(
3142 emitter,
3143 ctx,
3144 &ctx.ta
3145 .try_expr(*idx)
3146 .map_err(crate::codegen::arena_failure)?
3147 .ty,
3148 &Type::String,
3149 )?;
3150 let Some(symbol) = ctx.require(
3151 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3152 "record read imported",
3153 ) else {
3154 return Ok(());
3155 };
3156 emitter.instruction(Instruction::Call(symbol));
3157 crate::codegen::cast_check::emit_checked_cast_on_stack(
3158 emitter,
3159 ctx,
3160 &Type::Unknown,
3161 check_ty,
3162 )?;
3163 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3164 } else if receiver_ty.lowered() == &Type::Uint8Array {
3165 emit_uint8_index_with_receiver_on_stack(emitter, ctx, *idx)?;
3166 } else if receiver_ty.is_string_shaped() {
3167 emit_string_index_with_receiver_on_stack(emitter, ctx, *idx)?;
3168 } else {
3169 let source_ty = match receiver_ty.lowered() {
3170 Type::Array(element) => element.as_ref(),
3171 Type::Tuple(elements) => match &ctx
3172 .ta
3173 .try_expr(*idx)
3174 .map_err(crate::codegen::arena_failure)?
3175 .kind
3176 {
3177 TypedExprKind::Number(index) => {
3178 elements.get(*index as usize).unwrap_or(result_ty)
3179 }
3180 _ => result_ty,
3181 },
3182 _ => result_ty,
3183 };
3184 emit_bounds_checked_index_with_receiver_on_stack(
3185 emitter,
3186 ctx,
3187 *idx,
3188 source_ty,
3189 is_optional_tuple_position(ctx, receiver_ty, *idx)?,
3190 )?;
3191 if source_ty != result_ty {
3192 crate::codegen::cast_check::emit_checked_cast_on_stack(
3193 emitter, ctx, source_ty, result_ty,
3194 )?;
3195 }
3196 }
3197 }
3198 crate::TypedChainPart::InterfaceProperty { iface, name, .. } => {
3199 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
3210 emit_inline_length(emitter, ctx, struct_idx);
3211 return Ok(());
3212 }
3213 let key = crate::mangle::extend(iface, &name.name);
3214 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
3215 crate::codegen::internal_failure(
3216 "interface property getter recorded during the dependency-import pass",
3217 )
3218 })?;
3219 if matches!(receiver_ty.lowered(), Type::InterfaceRef { .. }) {
3220 emitter.instruction(Instruction::RefAsNonNull);
3221 }
3222 emitter.instruction(Instruction::Call(func_idx));
3223 }
3224 crate::TypedChainPart::Call {
3225 args,
3226 union_signature,
3227 ..
3228 } => {
3229 if let Some(signature) = union_signature
3236 && crate::codegen::closure_coercions::is_union_call_cast(receiver_ty, signature)
3237 {
3238 crate::codegen::closure_coercions::emit_union_call_cast(emitter, ctx, signature)?;
3239 }
3240 emit_indirect_closure_call_with_receiver_on_stack(
3241 emitter,
3242 ctx,
3243 union_signature.as_ref().unwrap_or(receiver_ty),
3244 args,
3245 part.result_ty(),
3246 )?;
3247 }
3248 crate::TypedChainPart::MethodCall {
3249 iface,
3250 name,
3251 args,
3252 result_ty,
3253 ..
3254 } => {
3255 emit_method_call_with_receiver_on_stack(
3265 emitter,
3266 ctx,
3267 receiver_ty,
3268 iface,
3269 &name.name,
3270 args,
3271 None,
3272 &[],
3273 None,
3274 result_ty,
3275 )?;
3276 }
3277 crate::TypedChainPart::NonNull { result_ty, .. } => {
3278 crate::codegen::cast_check::emit_non_null_assert_on_stack(
3279 emitter,
3280 ctx,
3281 receiver_ty,
3282 result_ty,
3283 )?;
3284 }
3285 };
3286 Ok(())
3287}
3288
3289fn emit_captured_load(
3300 emitter: &mut FunctionEmitter,
3301 _ctx: &CodegenCtx,
3302 c: &crate::CapturedVar,
3303) -> Result<(), crate::compiler_error::CompilerFailure> {
3304 let slot = emitter.local_slot(&c.name.name).ok_or_else(|| {
3305 crate::codegen::internal_failure("captured binding is missing from the outer scope")
3306 })?;
3307 emitter.instruction(Instruction::LocalGet(slot));
3308 Ok(())
3309}
3310
3311fn emit_binary(
3312 emitter: &mut FunctionEmitter,
3313 ctx: &CodegenCtx,
3314 op: BinOp,
3315 lhs: ExprId,
3316 rhs: ExprId,
3317 result_ty: &Type,
3318) -> Result<(), crate::compiler_error::CompilerFailure> {
3319 let admits_never_operand = matches!(
3320 op,
3321 BinOp::Add
3322 | BinOp::Sub
3323 | BinOp::Mul
3324 | BinOp::Div
3325 | BinOp::Rem
3326 | BinOp::Pow
3327 | BinOp::Lt
3328 | BinOp::Gt
3329 | BinOp::Le
3330 | BinOp::Ge
3331 );
3332 if admits_never_operand && try_emit_unreachable_for_never_operand(emitter, ctx, &[lhs, rhs])? {
3333 return Ok(());
3334 }
3335 let _: () = match op {
3336 BinOp::BitAnd
3341 | BinOp::BitOr
3342 | BinOp::BitXor
3343 | BinOp::Shl
3344 | BinOp::Shr
3345 | BinOp::UnsignedShr => {
3346 if has_numeric_operands(ctx, &[lhs, rhs], result_ty)? {
3347 emit_number_bitwise(emitter, ctx, op, lhs, rhs)?;
3348 } else {
3349 let name = op.bitwise_name().ok_or_else(|| {
3350 crate::codegen::internal_failure("missing bitwise host operation")
3351 })?;
3352 emit_bitwise_host(emitter, ctx, name, &[lhs, rhs], result_ty)?;
3353 }
3354 }
3355 BinOp::Add => match &result_ty.widen_literal() {
3358 Type::Number => {
3359 emit_primitive_operand(emitter, ctx, lhs)?;
3360 emit_primitive_operand(emitter, ctx, rhs)?;
3361 emitter.instruction(Instruction::F64Add);
3362 }
3363 Type::String => {
3364 emit_primitive_operand(emitter, ctx, lhs)?;
3365 emit_primitive_operand(emitter, ctx, rhs)?;
3366 let idx = ctx
3367 .symbols
3368 .prelude_func_idx("string_concat")
3369 .ok_or_else(|| {
3370 crate::codegen::internal_failure("string_concat imported from prelude")
3371 })?;
3372 emitter.instruction(Instruction::Call(idx));
3373 }
3374 Type::BigInt => emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "add")?,
3376 other => {
3377 return Err(crate::codegen::internal_failure(format!(
3378 "typechecker rejects `+` for {other:?}"
3379 )));
3380 }
3381 },
3382 BinOp::Sub | BinOp::Mul | BinOp::Div => {
3383 if result_ty.is_bigint() {
3385 let name = match op {
3386 BinOp::Sub => "sub",
3387 BinOp::Mul => "mul",
3388 BinOp::Div => "div",
3389 _ => {
3390 return Err(crate::codegen::internal_failure(
3391 "invalid compiler emission dispatch",
3392 ));
3393 }
3394 };
3395 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, name)?;
3396 return Ok(());
3397 }
3398 if !matches!(result_ty, Type::Number) {
3399 return Err(crate::codegen::internal_failure(
3400 "numeric operator has a non-number result type",
3401 ));
3402 }
3403 emit_primitive_operand(emitter, ctx, lhs)?;
3404 emit_primitive_operand(emitter, ctx, rhs)?;
3405 let inst = match op {
3406 BinOp::Sub => Instruction::F64Sub,
3407 BinOp::Mul => Instruction::F64Mul,
3408 BinOp::Div => Instruction::F64Div,
3409 _ => {
3410 return Err(crate::codegen::internal_failure(
3411 "invalid compiler emission dispatch",
3412 ));
3413 }
3414 };
3415 emitter.instruction(inst);
3416 }
3417 BinOp::Pow => {
3418 if result_ty.is_bigint() {
3423 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "pow")?;
3424 return Ok(());
3425 }
3426 if !matches!(result_ty, Type::Number) {
3427 return Err(crate::codegen::internal_failure(
3428 "numeric operator has a non-number result type",
3429 ));
3430 }
3431 emit_primitive_operand(emitter, ctx, lhs)?;
3432 emit_primitive_operand(emitter, ctx, rhs)?;
3433 let idx = ctx
3434 .symbols
3435 .func_idx(&crate::runtime::prelude::math::math_key("pow"))
3436 .ok_or_else(|| crate::codegen::internal_failure("Math#pow host import recorded"))?;
3437 emitter.instruction(Instruction::Call(idx));
3438 }
3439 BinOp::Rem => {
3440 if result_ty.is_bigint() {
3442 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "mod")?;
3443 return Ok(());
3444 }
3445 let a = emitter.add_anonymous_local(ValType::F64)?;
3449 let b = emitter.add_anonymous_local(ValType::F64)?;
3450 emit_primitive_operand(emitter, ctx, lhs)?;
3451 emitter.instruction(Instruction::LocalSet(a));
3452 emit_primitive_operand(emitter, ctx, rhs)?;
3453 emitter.instruction(Instruction::LocalSet(b));
3454 emitter.instruction(Instruction::LocalGet(a));
3456 emitter.instruction(Instruction::LocalGet(a));
3457 emitter.instruction(Instruction::LocalGet(b));
3458 emitter.instruction(Instruction::F64Div);
3459 emitter.instruction(Instruction::F64Trunc);
3460 emitter.instruction(Instruction::LocalGet(b));
3461 emitter.instruction(Instruction::F64Mul);
3462 emitter.instruction(Instruction::F64Sub);
3463 }
3464 BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
3465 let operand_ty = ctx
3468 .ta
3469 .try_expr(lhs)
3470 .map_err(crate::codegen::arena_failure)?
3471 .ty
3472 .primitive_behavior()
3473 .clone();
3474 if operand_ty.is_bigint() {
3475 emit_bigint_cmp_inline(emitter, ctx, lhs, rhs, op)?;
3476 return Ok(());
3477 }
3478 emit_primitive_operand(emitter, ctx, lhs)?;
3479 emit_primitive_operand(emitter, ctx, rhs)?;
3480 if matches!(operand_ty, Type::String | Type::StringLiteral(_)) {
3481 let idx = ctx.symbols.prelude_func_idx("string_cmp").ok_or_else(|| {
3482 crate::codegen::internal_failure("string_cmp imported from prelude")
3483 })?;
3484 emitter.instruction(Instruction::Call(idx));
3485 emitter.instruction(Instruction::I32Const(0));
3486 emitter.instruction(match op {
3487 BinOp::Lt => Instruction::I32LtS,
3488 BinOp::Gt => Instruction::I32GtS,
3489 BinOp::Le => Instruction::I32LeS,
3490 BinOp::Ge => Instruction::I32GeS,
3491 _ => {
3492 return Err(crate::codegen::internal_failure(
3493 "invalid compiler emission dispatch",
3494 ));
3495 }
3496 });
3497 } else {
3498 emitter.instruction(match op {
3499 BinOp::Lt => Instruction::F64Lt,
3500 BinOp::Gt => Instruction::F64Gt,
3501 BinOp::Le => Instruction::F64Le,
3502 BinOp::Ge => Instruction::F64Ge,
3503 _ => {
3504 return Err(crate::codegen::internal_failure(
3505 "invalid compiler emission dispatch",
3506 ));
3507 }
3508 });
3509 }
3510 }
3511 BinOp::Eq | BinOp::NotEq => emit_equality(emitter, ctx, op, lhs, rhs)?,
3512 BinOp::And | BinOp::Or => emit_logical(emitter, ctx, op, lhs, rhs, result_ty)?,
3513 BinOp::In => emit_in_operator(emitter, ctx, lhs, rhs)?,
3514 BinOp::NullishCoalesce => {
3518 return Err(crate::codegen::internal_failure(
3519 "BinOp::NullishCoalesce should be lifted into TypedExprKind::NullishCoalesce by infer",
3520 ));
3521 }
3522 };
3523 Ok(())
3524}
3525
3526fn emit_equality(
3529 emitter: &mut FunctionEmitter,
3530 ctx: &CodegenCtx,
3531 op: BinOp,
3532 lhs: ExprId,
3533 rhs: ExprId,
3534) -> Result<(), crate::compiler_error::CompilerFailure> {
3535 let lhs_is_null_lit = matches!(
3542 ctx.ta
3543 .try_expr(lhs)
3544 .map_err(crate::codegen::arena_failure)?
3545 .kind,
3546 TypedExprKind::Null
3547 );
3548 let rhs_is_null_lit = matches!(
3549 ctx.ta
3550 .try_expr(rhs)
3551 .map_err(crate::codegen::arena_failure)?
3552 .kind,
3553 TypedExprKind::Null
3554 );
3555 if lhs_is_null_lit && rhs_is_null_lit {
3556 let v: i32 = if matches!(op, BinOp::Eq) { 1 } else { 0 };
3557 emitter.instruction(Instruction::I32Const(v));
3558 return Ok(());
3559 }
3560 if lhs_is_null_lit || rhs_is_null_lit {
3561 let non_null_side = if lhs_is_null_lit { rhs } else { lhs };
3562 let non_null_ty = ctx
3563 .ta
3564 .try_expr(non_null_side)
3565 .map_err(crate::codegen::arena_failure)?
3566 .ty
3567 .clone();
3568 let non_null_val = ctx.symbols.value_type(&non_null_ty)?;
3569 if matches!(non_null_val, ValType::F64 | ValType::I32) {
3577 emit_expr(emitter, ctx, non_null_side)?;
3578 emitter.instruction(Instruction::Drop);
3579 let v: i32 = if matches!(op, BinOp::NotEq) { 1 } else { 0 };
3580 emitter.instruction(Instruction::I32Const(v));
3581 return Ok(());
3582 }
3583 emit_expr(emitter, ctx, non_null_side)?;
3584 emitter.instruction(Instruction::RefIsNull);
3585 if matches!(op, BinOp::NotEq) {
3586 emitter.instruction(Instruction::I32Eqz);
3587 }
3588 return Ok(());
3589 }
3590 let lhs_ty = ctx
3591 .ta
3592 .try_expr(lhs)
3593 .map_err(crate::codegen::arena_failure)?
3594 .ty
3595 .clone();
3596 let rhs_ty = ctx
3597 .ta
3598 .try_expr(rhs)
3599 .map_err(crate::codegen::arena_failure)?
3600 .ty
3601 .clone();
3602 let operand_val = ctx.symbols.value_type(lhs_ty.primitive_behavior())?;
3603 if may_hold_null(&lhs_ty)
3609 || may_hold_null(&rhs_ty)
3610 || operand_val != ctx.symbols.value_type(rhs_ty.primitive_behavior())?
3611 {
3612 emit_boxed_eq(emitter, ctx, lhs, rhs, op)?;
3613 return Ok(());
3614 }
3615 if lhs_ty.is_bigint() {
3619 emit_bigint_cmp_eq_inline(emitter, ctx, lhs, rhs, op)?;
3620 return Ok(());
3621 }
3622 let string_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
3623 crate::codegen::internal_failure("string type registered with intrinsics")
3624 })?;
3625 match operand_val {
3631 ValType::F64 => {
3632 emit_primitive_operand(emitter, ctx, lhs)?;
3633 emit_primitive_operand(emitter, ctx, rhs)?;
3634 emitter.instruction(if matches!(op, BinOp::Eq) {
3635 Instruction::F64Eq
3636 } else {
3637 Instruction::F64Ne
3638 });
3639 }
3640 ValType::I32 => {
3641 emit_expr(emitter, ctx, lhs)?;
3642 emit_expr(emitter, ctx, rhs)?;
3643 emitter.instruction(if matches!(op, BinOp::Eq) {
3644 Instruction::I32Eq
3645 } else {
3646 Instruction::I32Ne
3647 });
3648 }
3649 ValType::Ref(RefType {
3650 heap_type: HeapType::Concrete(idx),
3651 ..
3652 }) if idx == string_idx => {
3653 emit_expr(emitter, ctx, lhs)?;
3654 emit_expr(emitter, ctx, rhs)?;
3655 let func = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
3656 crate::codegen::internal_failure("string_eq imported from prelude")
3657 })?;
3658 emitter.instruction(Instruction::Call(func));
3659 if matches!(op, BinOp::NotEq) {
3660 emitter.instruction(Instruction::I32Eqz);
3661 }
3662 }
3663 ValType::Ref(_) => {
3664 emit_expr(emitter, ctx, lhs)?;
3675 cast::emit_box(emitter, ctx, &lhs_ty)?;
3676 emit_expr(emitter, ctx, rhs)?;
3677 cast::emit_box(emitter, ctx, &rhs_ty)?;
3678 emit_vtable_equality(emitter, ctx, op);
3679 }
3680 other => {
3681 return Err(crate::codegen::internal_failure(format!(
3682 "typechecker rejects equality on Wasm value-type `{other:?}`"
3683 )));
3684 }
3685 }
3686 Ok(())
3687}
3688
3689fn emit_logical(
3690 emitter: &mut FunctionEmitter,
3691 ctx: &CodegenCtx,
3692 op: BinOp,
3693 lhs: ExprId,
3694 rhs: ExprId,
3695 result_ty: &Type,
3696) -> Result<(), crate::compiler_error::CompilerFailure> {
3697 let result_val = ctx.symbols.value_type(result_ty)?;
3703 let lhs_ty = ctx
3704 .ta
3705 .try_expr(lhs)
3706 .map_err(crate::codegen::arena_failure)?
3707 .ty
3708 .clone();
3709 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
3710 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
3711 let tmp = emitter.add_anonymous_local(lhs_val)?;
3712 emit_expr(emitter, ctx, lhs)?;
3713 emitter.instruction(Instruction::LocalTee(tmp));
3714 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &lhs_ty)?;
3715 let emit_rhs_branch =
3716 |emitter: &mut FunctionEmitter| -> Result<(), crate::compiler_error::CompilerFailure> {
3717 let rhs_ty = ctx
3718 .ta
3719 .try_expr(rhs)
3720 .map_err(crate::codegen::arena_failure)?
3721 .ty
3722 .clone();
3723 emit_expr(emitter, ctx, rhs)?;
3724 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3725 emitter, ctx, &rhs_ty, result_ty,
3726 )?;
3727 Ok(())
3728 };
3729 let kept_lhs_ty = match op {
3733 BinOp::And => falsy_part(&lhs_ty),
3734 _ => truthy_part(&lhs_ty),
3735 };
3736 let lhs_is_never_kept = matches!(kept_lhs_ty.lowered(), Type::Never);
3737 let emit_kept_lhs_branch = |emitter: &mut FunctionEmitter| {
3738 if lhs_is_never_kept {
3739 emitter.instruction(Instruction::Unreachable);
3740 return Ok(());
3741 }
3742 emitter.instruction(Instruction::LocalGet(tmp));
3743 if lhs_is_ref {
3744 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?;
3748 } else {
3749 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3750 emitter, ctx, &lhs_ty, result_ty,
3751 )?;
3752 }
3753 Ok::<(), crate::compiler_error::CompilerFailure>(())
3754 };
3755 emitter.emit_if(BlockType::Result(result_val));
3756 match op {
3757 BinOp::And => emit_rhs_branch(emitter)?,
3758 _ => emit_kept_lhs_branch(emitter)?,
3759 }
3760 emitter.emit_else();
3761 match op {
3762 BinOp::And => emit_kept_lhs_branch(emitter)?,
3763 _ => emit_rhs_branch(emitter)?,
3764 }
3765 emitter.emit_end();
3766 Ok(())
3767}
3768
3769fn try_emit_unreachable_for_never_operand(
3774 emitter: &mut FunctionEmitter,
3775 ctx: &CodegenCtx,
3776 operands: &[ExprId],
3777) -> Result<bool, crate::compiler_error::CompilerFailure> {
3778 let mut has_never_operand = false;
3779 for &operand in operands {
3780 let ty = &ctx
3781 .ta
3782 .try_expr(operand)
3783 .map_err(crate::codegen::arena_failure)?
3784 .ty;
3785 has_never_operand |= matches!(ty.lowered(), Type::Never);
3786 }
3787 if !has_never_operand {
3788 return Ok(false);
3789 }
3790 for &operand in operands {
3791 emit_expr(emitter, ctx, operand)?;
3792 emitter.instruction(Instruction::Drop);
3793 }
3794 emitter.instruction(Instruction::Unreachable);
3795 Ok(true)
3796}
3797
3798fn emit_primitive_operand(
3799 emitter: &mut FunctionEmitter,
3800 ctx: &CodegenCtx,
3801 expr: ExprId,
3802) -> Result<(), crate::compiler_error::CompilerFailure> {
3803 emit_expr(emitter, ctx, expr)?;
3804 let ty = &ctx
3805 .ta
3806 .try_expr(expr)
3807 .map_err(crate::codegen::arena_failure)?
3808 .ty;
3809 let _: () = if matches!(ty.primitive_behavior(), Type::Number)
3810 && ctx.symbols.value_type(ty)? != ValType::F64
3811 {
3812 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
3813 };
3814 Ok(())
3815}
3816
3817fn emit_in_operator(
3825 emitter: &mut FunctionEmitter,
3826 ctx: &CodegenCtx,
3827 lhs: ExprId,
3828 rhs: ExprId,
3829) -> Result<(), crate::compiler_error::CompilerFailure> {
3830 emit_expr(emitter, ctx, lhs)?;
3831 crate::codegen::cast_check::emit_operation_cast_on_stack(
3832 emitter,
3833 ctx,
3834 &ctx.ta
3835 .try_expr(lhs)
3836 .map_err(crate::codegen::arena_failure)?
3837 .ty,
3838 &Type::String,
3839 )?;
3840 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
3841 emitter.instruction(Instruction::LocalSet(key));
3842 emit_expr(emitter, ctx, rhs)?;
3843 cast::emit_box(
3844 emitter,
3845 ctx,
3846 &ctx.ta
3847 .try_expr(rhs)
3848 .map_err(crate::codegen::arena_failure)?
3849 .ty,
3850 )?;
3851 emitter.instruction(Instruction::LocalGet(key));
3852 let Some(symbol) = ctx.require(
3853 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
3854 "presence helper imported",
3855 ) else {
3856 return Ok(());
3857 };
3858 emitter.instruction(Instruction::Call(symbol));
3859 Ok(())
3860}
3861
3862pub(crate) fn emit_class_field_setup(
3867 emitter: &mut FunctionEmitter,
3868 ctx: &CodegenCtx,
3869 mangled: &crate::MangledName,
3870) -> Result<(), crate::compiler_error::CompilerFailure> {
3871 emit_class_field_setup_phase(emitter, ctx, mangled, FieldSetupPhase::All)
3872}
3873
3874#[derive(Clone, Copy)]
3875pub(crate) enum FieldSetupPhase {
3876 All,
3877 Initializers,
3878 ParameterProperties,
3879}
3880
3881pub(crate) fn emit_class_field_setup_phase(
3882 emitter: &mut FunctionEmitter,
3883 ctx: &CodegenCtx,
3884 mangled: &crate::MangledName,
3885 phase: FieldSetupPhase,
3886) -> Result<(), crate::compiler_error::CompilerFailure> {
3887 use crate::codegen::symbol_table::FieldSetup;
3888 let setup: Vec<FieldSetup> = match ctx.symbols.class_field_setup(mangled) {
3889 Some(s) if !s.is_empty() => s.to_vec(),
3890 _ => return Ok(()),
3891 };
3892 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3893 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3894 })?;
3895 let intrinsics = ctx
3896 .symbols
3897 .intrinsic_type_indices()
3898 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3899 let this = emitter.this_local().ok_or_else(|| {
3900 crate::codegen::internal_failure("field setup runs inside a constructor init fn")
3901 })?;
3902 for step in &setup {
3903 let parameter_property = matches!(step, FieldSetup::ParamCopy { .. });
3904 if matches!(phase, FieldSetupPhase::Initializers) && parameter_property
3905 || matches!(phase, FieldSetupPhase::ParameterProperties) && !parameter_property
3906 {
3907 continue;
3908 }
3909 let field = match step {
3910 FieldSetup::Init { field, .. }
3911 | FieldSetup::ParamCopy { field, .. }
3912 | FieldSetup::Reset { field } => field,
3913 };
3914 let slot = ctx
3915 .symbols
3916 .class_field_slot(mangled, field)
3917 .ok_or_else(|| {
3918 crate::codegen::internal_failure("class field slot recorded in classes::emit")
3919 })?;
3920 match step {
3921 FieldSetup::Init { value, .. } => {
3922 let value_ty = ctx
3923 .ta
3924 .try_expr(*value)
3925 .map_err(crate::codegen::arena_failure)?
3926 .ty
3927 .clone();
3928 emit_expr(emitter, ctx, *value)?;
3929 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &value_ty)?;
3930 }
3931 FieldSetup::ParamCopy { param, .. } => {
3932 let local = emitter.require_local_slot(¶m.name.name)?;
3933 if param.boxed {
3934 let box_type = ctx.symbols.box_type_idx(¶m.ty)?.ok_or_else(|| {
3935 crate::codegen::internal_failure(
3936 "constructor parameter cell is not registered",
3937 )
3938 })?;
3939 super::binding_cells::read(emitter, ctx, local, box_type)?;
3940 cast::emit_unerase(emitter, ctx, ¶m.ty)?;
3941 } else {
3942 emitter.instruction(Instruction::LocalGet(local));
3943 }
3944 cast::emit_box(emitter, ctx, ¶m.ty)?;
3945 }
3946 FieldSetup::Reset { .. } => {
3947 emit_undefined(emitter, ctx)?;
3948 }
3949 }
3950 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
3951 emitter.instruction(Instruction::LocalSet(stored));
3952 emitter.instruction(Instruction::LocalGet(this));
3953 emitter.instruction(Instruction::StructGet {
3954 struct_type_index: struct_idx,
3955 field_index: 2,
3956 });
3957 emitter.instruction(Instruction::I32Const(slot as i32));
3958 emitter.instruction(Instruction::LocalGet(stored));
3959 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
3960 let index = emitter.add_anonymous_local(ValType::I32)?;
3961 emitter.instruction(Instruction::I32Const(slot as i32));
3962 emitter.instruction(Instruction::LocalSet(index));
3963 crate::codegen::field_names::emit_mark_present(emitter, ctx, this, index)?;
3964 }
3965 Ok(())
3966}
3967
3968pub(crate) fn emit_field_slot_get(
3972 emitter: &mut FunctionEmitter,
3973 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
3974 object_local: u32,
3975 index_local: u32,
3976) {
3977 emitter.instruction(Instruction::LocalGet(object_local));
3978 emitter.instruction(Instruction::StructGet {
3979 struct_type_index: intrinsics.object_shape,
3980 field_index: 2,
3981 });
3982 emitter.instruction(Instruction::LocalGet(index_local));
3983 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3984}
3985
3986pub(crate) fn emit_object_field_read_by_name(
3987 emitter: &mut FunctionEmitter,
3988 ctx: &CodegenCtx,
3989 object_local: u32,
3990 name_global: u32,
3991) {
3992 ctx.latch(emit_object_field_read_by_name_checked(
3993 emitter,
3994 ctx,
3995 object_local,
3996 name_global,
3997 ));
3998}
3999
4000fn emit_object_field_read_by_name_checked(
4001 emitter: &mut FunctionEmitter,
4002 ctx: &CodegenCtx,
4003 object_local: u32,
4004 name_global: u32,
4005) -> Result<(), crate::compiler_error::CompilerFailure> {
4006 let intrinsics = ctx
4007 .symbols
4008 .intrinsic_type_indices()
4009 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4010 let Some(index_local) = ctx.latch(emitter.add_anonymous_local(ValType::I32)) else {
4011 return Ok(());
4012 };
4013 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4014 emitter.instruction(Instruction::LocalSet(index_local));
4015 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
4016 nullable: true,
4017 heap_type: HeapType::Concrete(intrinsics.object),
4018 })));
4019 emitter.instruction(Instruction::LocalGet(index_local));
4020 emitter.instruction(Instruction::I32Const(0));
4021 emitter.instruction(Instruction::I32LtS);
4022 emitter.emit_if(BlockType::Empty);
4023 emit_undefined(emitter, ctx)?;
4024 emitter.instruction(Instruction::Br(1));
4025 emitter.emit_end();
4026 emitter.instruction(Instruction::LocalGet(object_local));
4027 emitter.instruction(Instruction::StructGet {
4028 struct_type_index: intrinsics.object_shape,
4029 field_index: 2,
4030 });
4031 emitter.instruction(Instruction::LocalGet(index_local));
4032 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
4033 emitter.emit_end();
4034
4035 Ok(())
4036}
4037
4038pub(crate) fn emit_object_field_write_by_name(
4039 emitter: &mut FunctionEmitter,
4040 ctx: &CodegenCtx,
4041 object_local: u32,
4042 name_global: u32,
4043) -> Result<(), crate::compiler_error::CompilerFailure> {
4044 let intrinsics = ctx
4045 .symbols
4046 .intrinsic_type_indices()
4047 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4048 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4049 nullable: true,
4050 heap_type: HeapType::Concrete(intrinsics.object),
4051 }))?;
4052 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4053 emitter.instruction(Instruction::LocalSet(value_local));
4054 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4055 emitter.instruction(Instruction::LocalTee(index_local));
4056 emitter.instruction(Instruction::I32Const(0));
4057 emitter.instruction(Instruction::I32GeS);
4058 emitter.emit_if(BlockType::Empty);
4059 emitter.instruction(Instruction::LocalGet(object_local));
4060 emitter.instruction(Instruction::StructGet {
4061 struct_type_index: intrinsics.object_shape,
4062 field_index: 2,
4063 });
4064 emitter.instruction(Instruction::LocalGet(index_local));
4065 emitter.instruction(Instruction::LocalGet(value_local));
4066 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
4067 crate::codegen::field_names::emit_mark_present(emitter, ctx, object_local, index_local)?;
4068 emitter.emit_else();
4069 emitter.instruction(Instruction::LocalGet(object_local));
4070 emitter.instruction(Instruction::GlobalGet(name_global));
4071 emitter.instruction(Instruction::LocalGet(value_local));
4072 let insert = ctx
4073 .symbols
4074 .prelude_func_idx("ObjectConstructor##insertField")
4075 .ok_or_else(|| {
4076 crate::codegen::internal_failure("field insertion helper is not imported")
4077 })?;
4078 emitter.instruction(Instruction::Call(insert));
4079 emitter.emit_end();
4080 Ok(())
4081}
4082
4083pub(crate) fn accessor_branch_emittable(ctx: &CodegenCtx, getter_name: &str) -> bool {
4090 ctx.symbols
4091 .field_name_string_global_idx(getter_name)
4092 .is_some()
4093}
4094
4095pub(crate) fn emit_is_accessor_backed(
4112 emitter: &mut FunctionEmitter,
4113 ctx: &CodegenCtx,
4114 object_local: u32,
4115 accessor_name: &str,
4116 kind: crate::AccessorKind,
4117) -> Result<u32, crate::compiler_error::CompilerFailure> {
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 accessor_global = ctx
4123 .symbols
4124 .field_name_string_global_idx(accessor_name)
4125 .ok_or_else(|| {
4126 crate::codegen::internal_failure("caller checked the accessor name has a string global")
4127 })?;
4128 let closure_struct_idx = ctx
4129 .symbols
4130 .closure_struct_type_idx(crate::codegen::classes::accessor_closure_sig(kind))
4131 .ok_or_else(|| {
4132 crate::codegen::internal_failure(
4133 "accessor closure struct collected by analysis::note_shaped_property_access",
4134 )
4135 })?;
4136 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4137 emit_field_index_by_name(emitter, ctx, object_local, accessor_global, true);
4138 emitter.instruction(Instruction::LocalTee(index_local));
4139 emitter.instruction(Instruction::I32Const(0));
4140 emitter.instruction(Instruction::I32GeS);
4141 emitter.instruction(Instruction::LocalGet(object_local));
4142 emitter.instruction(Instruction::StructGet {
4143 struct_type_index: intrinsics.object,
4144 field_index: 0,
4145 });
4146 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4147 intrinsics.class_vtable,
4148 )));
4149 emitter.instruction(Instruction::I32And);
4150 emitter.emit_if(BlockType::Result(ValType::I32));
4151 emitter.instruction(Instruction::LocalGet(object_local));
4152 emitter.instruction(Instruction::StructGet {
4153 struct_type_index: intrinsics.object_shape,
4154 field_index: 1,
4155 });
4156 emitter.instruction(Instruction::LocalGet(index_local));
4157 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
4158 crate::codegen::field_names::emit_name_is_accessor(emitter, ctx)?;
4159 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4160 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4161 closure_struct_idx,
4162 )));
4163 emitter.instruction(Instruction::I32And);
4164 emitter.emit_else();
4165 emitter.instruction(Instruction::I32Const(0));
4166 emitter.emit_end();
4167 Ok(index_local)
4168}
4169
4170pub(crate) fn emit_object_property_read(
4180 emitter: &mut FunctionEmitter,
4181 ctx: &CodegenCtx,
4182 object_local: u32,
4183 prop_name: &str,
4184 field_ty: &Type,
4185) -> Result<(), crate::compiler_error::CompilerFailure> {
4186 emit_object_property_read_as(emitter, ctx, object_local, prop_name, field_ty, field_ty)?;
4187 Ok(())
4188}
4189
4190fn emit_object_property_read_as(
4191 emitter: &mut FunctionEmitter,
4192 ctx: &CodegenCtx,
4193 object_local: u32,
4194 prop_name: &str,
4195 field_ty: &Type,
4196 check_ty: &Type,
4197) -> Result<(), crate::compiler_error::CompilerFailure> {
4198 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4199 let name_global = ctx
4200 .symbols
4201 .field_name_string_global_idx(prop_name)
4202 .ok_or_else(|| {
4203 crate::codegen::internal_failure(
4204 "per-name string global recorded during field-name-strings emission",
4205 )
4206 })?;
4207 if !accessor_branch_emittable(ctx, &getter) {
4208 emit_object_field_read_by_name(emitter, ctx, object_local, name_global);
4209 emit_dynamic_narrowed_property_read(
4210 emitter,
4211 ctx,
4212 object_local,
4213 prop_name,
4214 field_ty,
4215 check_ty,
4216 )?;
4217 return Ok(());
4218 }
4219 let intrinsics = ctx
4220 .symbols
4221 .intrinsic_type_indices()
4222 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4223 let result_vt = ctx.symbols.value_type(field_ty)?;
4224 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4225 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4226 emitter.instruction(Instruction::LocalSet(index_local));
4227 emitter.instruction(Instruction::LocalGet(index_local));
4228 emitter.instruction(Instruction::I32Const(0));
4229 emitter.instruction(Instruction::I32GeS);
4230 emitter.emit_if(BlockType::Result(result_vt));
4231 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4233 emit_dynamic_narrowed_property_read(emitter, ctx, object_local, prop_name, field_ty, check_ty)?;
4234 emitter.emit_else();
4235 let getter_slot_local = emit_is_accessor_backed(
4236 emitter,
4237 ctx,
4238 object_local,
4239 &getter,
4240 crate::AccessorKind::Get,
4241 )?;
4242 emitter.emit_if(BlockType::Result(result_vt));
4243 emitter.instruction(Instruction::LocalGet(object_local));
4245 emit_interface_method_via_shape_with_receiver_on_stack(
4246 emitter,
4247 ctx,
4248 &getter,
4249 &[],
4250 field_ty,
4251 Some(getter_slot_local),
4252 )?;
4253 emitter.emit_else();
4254 emit_undefined(emitter, ctx)?;
4260 cast::emit_cast_to(emitter, ctx, field_ty)?;
4261 emitter.emit_end();
4262 emitter.emit_end();
4263 Ok(())
4264}
4265
4266fn emit_dynamic_narrowed_property_read(
4267 emitter: &mut FunctionEmitter,
4268 ctx: &CodegenCtx,
4269 object_local: u32,
4270 prop_name: &str,
4271 field_ty: &Type,
4272 check_ty: &Type,
4273) -> Result<(), crate::compiler_error::CompilerFailure> {
4274 let checks: Vec<_> = ctx
4275 .symbols
4276 .class_field_narrowing_checks(prop_name)
4277 .filter_map(|(class, check)| {
4278 ctx.symbols
4279 .class_vtable_global_idx(class)
4280 .map(|vtable| (vtable, check))
4281 })
4282 .collect();
4283 if checks.is_empty() {
4284 cast::emit_cast_to(emitter, ctx, field_ty)?;
4285 return Ok(());
4286 }
4287
4288 let intrinsics = ctx
4289 .symbols
4290 .intrinsic_type_indices()
4291 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4292 let raw = emitter.add_anonymous_local(ValType::Ref(RefType {
4293 nullable: true,
4294 heap_type: HeapType::Concrete(intrinsics.object),
4295 }))?;
4296 emitter.instruction(Instruction::LocalSet(raw));
4297 emit_dynamic_narrowing_check(
4298 emitter,
4299 ctx,
4300 object_local,
4301 raw,
4302 field_ty,
4303 check_ty,
4304 &checks,
4305 0,
4306 )?;
4307 Ok(())
4308}
4309
4310#[allow(clippy::too_many_arguments)]
4311fn emit_dynamic_narrowing_check(
4312 emitter: &mut FunctionEmitter,
4313 ctx: &CodegenCtx,
4314 object_local: u32,
4315 raw: u32,
4316 field_ty: &Type,
4317 check_ty: &Type,
4318 checks: &[(u32, &crate::FieldNarrowingCheck)],
4319 index: usize,
4320) -> Result<(), crate::compiler_error::CompilerFailure> {
4321 let Some((vtable, check)) = checks.get(index) else {
4322 emitter.instruction(Instruction::LocalGet(raw));
4323 cast::emit_cast_to(emitter, ctx, field_ty)?;
4324 return Ok(());
4325 };
4326 emitter.instruction(Instruction::LocalGet(object_local));
4327 crate::codegen::function_emitter::cast::emit_nominal_instance_test(emitter, ctx, *vtable)?;
4328 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(field_ty)?));
4329 emitter.instruction(Instruction::LocalGet(raw));
4330 crate::codegen::cast_check::emit_narrowed_field_read_as(
4331 emitter, ctx, check, field_ty, check_ty,
4332 )?;
4333 emitter.emit_else();
4334 emit_dynamic_narrowing_check(
4335 emitter,
4336 ctx,
4337 object_local,
4338 raw,
4339 field_ty,
4340 check_ty,
4341 checks,
4342 index + 1,
4343 )?;
4344 emitter.emit_end();
4345 Ok(())
4346}
4347
4348pub(crate) fn emit_object_property_write(
4354 emitter: &mut FunctionEmitter,
4355 ctx: &CodegenCtx,
4356 object_local: u32,
4357 prop: &Ident,
4358 value: ExprId,
4359) -> Result<(), crate::compiler_error::CompilerFailure> {
4360 emit_object_property_write_value(
4361 emitter,
4362 ctx,
4363 object_local,
4364 prop,
4365 &ShapeArgument::Expression(value),
4366 )?;
4367 Ok(())
4368}
4369
4370fn emit_object_property_write_value(
4371 emitter: &mut FunctionEmitter,
4372 ctx: &CodegenCtx,
4373 object_local: u32,
4374 prop: &Ident,
4375 value: &ShapeArgument,
4376) -> Result<(), crate::compiler_error::CompilerFailure> {
4377 let prop_name = prop.name.as_str();
4378 let setter = crate::codegen::classes::accessor_setter_name(prop_name);
4379 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4380 let has_setter_name = ctx.symbols.field_name_string_global_idx(&setter).is_some();
4381 let has_getter_name = ctx.symbols.field_name_string_global_idx(&getter).is_some();
4382 let name_global = ctx
4383 .symbols
4384 .field_name_string_global_idx(prop_name)
4385 .ok_or_else(|| {
4386 crate::codegen::internal_failure(
4387 "per-name string global recorded during field-name-strings emission",
4388 )
4389 })?;
4390 if !has_setter_name && !has_getter_name {
4394 let value_ty = value.ty(ctx)?;
4395 value.emit(emitter, ctx)?;
4396 cast::emit_box(emitter, ctx, &value_ty)?;
4397 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4398 return Ok(());
4399 }
4400 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4401 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4402 emitter.instruction(Instruction::LocalSet(index_local));
4403 emitter.instruction(Instruction::LocalGet(index_local));
4404 emitter.instruction(Instruction::I32Const(0));
4405 emitter.instruction(Instruction::I32GeS);
4406 emitter.emit_if(BlockType::Empty);
4407 let value_ty = value.ty(ctx)?;
4410 value.emit(emitter, ctx)?;
4411 cast::emit_box(emitter, ctx, &value_ty)?;
4412 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4413 emitter.emit_else();
4414 let arms: Vec<(&str, AccessorWrite)> = [
4419 has_setter_name.then_some((setter.as_str(), AccessorWrite::Setter)),
4420 has_getter_name.then_some((getter.as_str(), AccessorWrite::ReadOnly)),
4421 ]
4422 .into_iter()
4423 .flatten()
4424 .collect();
4425 for (accessor, arm) in &arms {
4426 let slot_index_local =
4427 emit_is_accessor_backed(emitter, ctx, object_local, accessor, arm.scanned_kind())?;
4428 emitter.emit_if(BlockType::Empty);
4429 match arm {
4430 AccessorWrite::Setter => {
4431 emitter.instruction(Instruction::LocalGet(object_local));
4432 emit_shape_method_with_arguments(
4433 emitter,
4434 ctx,
4435 accessor,
4436 std::slice::from_ref(value),
4437 &Type::Void,
4438 Some(slot_index_local),
4439 )?;
4440 emitter.instruction(Instruction::Drop);
4441 }
4442 AccessorWrite::ReadOnly => {
4445 value.emit(emitter, ctx)?;
4446 emitter.instruction(Instruction::Drop);
4447 emitter.record_span(prop.span);
4450 crate::codegen::throw::emit_type_error_throw(
4451 emitter,
4452 ctx,
4453 crate::codegen::throw::READ_ONLY_PROPERTY_MESSAGE,
4454 );
4455 }
4456 }
4457 emitter.emit_else();
4458 }
4459 let value_ty = value.ty(ctx)?;
4460 value.emit(emitter, ctx)?;
4461 cast::emit_box(emitter, ctx, &value_ty)?;
4462 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4463 for _ in &arms {
4464 emitter.emit_end();
4465 }
4466 emitter.emit_end();
4467 Ok(())
4468}
4469
4470fn emit_bounds_checked_index_with_receiver_on_stack(
4484 emitter: &mut FunctionEmitter,
4485 ctx: &CodegenCtx,
4486 idx: ExprId,
4487 result_ty: &Type,
4488 allow_missing: bool,
4489) -> Result<(), crate::compiler_error::CompilerFailure> {
4490 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
4491 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4492 })?;
4493 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
4494 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4495 })?;
4496 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4497 nullable: false,
4498 heap_type: HeapType::Concrete(raw_array_idx),
4499 }))?;
4500 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4501 emitter.instruction(Instruction::LocalSet(receiver));
4502 emit_expr(emitter, ctx, idx)?;
4503 let key = emitter.add_anonymous_local(
4504 ctx.symbols.value_type(
4505 &ctx.ta
4506 .try_expr(idx)
4507 .map_err(crate::codegen::arena_failure)?
4508 .ty,
4509 )?,
4510 )?;
4511 emitter.instruction(Instruction::LocalSet(key));
4512 emitter.instruction(Instruction::LocalGet(receiver));
4513 crate::codegen::cast_check::emit_operation_cast_on_stack(
4514 emitter,
4515 ctx,
4516 &Type::Unknown,
4517 &Type::Array(Box::new(Type::Unknown)),
4518 )?;
4519 emitter.instruction(Instruction::LocalGet(key));
4520 emit_index_number(
4521 emitter,
4522 ctx,
4523 &ctx.ta
4524 .try_expr(idx)
4525 .map_err(crate::codegen::arena_failure)?
4526 .ty,
4527 )?;
4528 let idx_f64_local = stash_index_operand(emitter)?;
4529 let length = stash_array_length(emitter, array_idx)?;
4530 emitter.instruction(Instruction::StructGet {
4531 struct_type_index: array_idx,
4532 field_index: 1,
4533 });
4534 emitter.instruction(Instruction::LocalSet(raw_arr_local));
4535 if allow_missing {
4536 emitter.instruction(Instruction::LocalGet(idx_f64_local));
4537 emitter.instruction(Instruction::LocalGet(length));
4538 emitter.instruction(Instruction::F64ConvertI32U);
4539 emitter.instruction(Instruction::F64Ge);
4540 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
4541 emit_undefined(emitter, ctx)?;
4542 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
4543 emitter.emit_else();
4544 }
4545 let idx_local = emit_checked_index_with_length(emitter, ctx, length, idx_f64_local)?;
4546 emitter.instruction(Instruction::LocalGet(raw_arr_local));
4547 emitter.instruction(Instruction::LocalGet(idx_local));
4548 emitter.instruction(Instruction::ArrayGet(raw_array_idx));
4549 cast::emit_cast_to(emitter, ctx, result_ty)?;
4550 if allow_missing {
4551 emitter.emit_end();
4552 }
4553 Ok(())
4554}
4555
4556fn is_optional_tuple_position(
4557 ctx: &CodegenCtx<'_>,
4558 receiver: &Type,
4559 index: ExprId,
4560) -> Result<bool, crate::compiler_error::CompilerFailure> {
4561 let expression = ctx
4562 .ta
4563 .try_expr(index)
4564 .map_err(crate::codegen::arena_failure)?;
4565 let TypedExprKind::Number(index) = expression.kind else {
4566 return Ok(false);
4567 };
4568 Ok(index.fract() == 0.0 && tuple_permits_missing_index(receiver, index))
4569}
4570
4571fn tuple_permits_missing_index(receiver: &Type, index: f64) -> bool {
4575 match receiver.lowered() {
4576 Type::Tuple(elements) => index >= elements.required_len() as f64,
4577 Type::Union(members) => members
4578 .iter()
4579 .any(|member| tuple_permits_missing_index(member, index)),
4580 _ => false,
4581 }
4582}
4583
4584fn emit_uint8_index_with_receiver_on_stack(
4585 emitter: &mut FunctionEmitter,
4586 ctx: &CodegenCtx,
4587 index: ExprId,
4588) -> Result<(), crate::compiler_error::CompilerFailure> {
4589 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
4590 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4591 })?;
4592 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
4593 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4594 })?;
4595 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4596 nullable: false,
4597 heap_type: HeapType::Concrete(raw_uint8_idx),
4598 }))?;
4599 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4600 emitter.instruction(Instruction::LocalSet(receiver));
4601 emit_expr(emitter, ctx, index)?;
4602 let key = emitter.add_anonymous_local(
4603 ctx.symbols.value_type(
4604 &ctx.ta
4605 .try_expr(index)
4606 .map_err(crate::codegen::arena_failure)?
4607 .ty,
4608 )?,
4609 )?;
4610 emitter.instruction(Instruction::LocalSet(key));
4611 emitter.instruction(Instruction::LocalGet(receiver));
4612 crate::codegen::cast_check::emit_operation_cast_on_stack(
4613 emitter,
4614 ctx,
4615 &Type::Unknown,
4616 &Type::Uint8Array,
4617 )?;
4618 emitter.instruction(Instruction::LocalGet(key));
4619 emit_index_number(
4620 emitter,
4621 ctx,
4622 &ctx.ta
4623 .try_expr(index)
4624 .map_err(crate::codegen::arena_failure)?
4625 .ty,
4626 )?;
4627 let idx_f64_local = stash_index_operand(emitter)?;
4628 emitter.instruction(Instruction::StructGet {
4629 struct_type_index: uint8_idx,
4630 field_index: 1,
4631 });
4632 emitter.instruction(Instruction::LocalSet(raw_local));
4633 let idx_local = emit_checked_index(emitter, ctx, raw_local, idx_f64_local)?;
4634 emitter.instruction(Instruction::LocalGet(raw_local));
4635 emitter.instruction(Instruction::LocalGet(idx_local));
4636 emitter.instruction(Instruction::ArrayGetU(raw_uint8_idx));
4637 emitter.instruction(Instruction::F64ConvertI32U);
4638 Ok(())
4639}
4640
4641#[derive(Clone, Copy)]
4643enum AccessorWrite {
4644 Setter,
4645 ReadOnly,
4646}
4647
4648impl AccessorWrite {
4649 fn scanned_kind(self) -> crate::AccessorKind {
4652 match self {
4653 AccessorWrite::Setter => crate::AccessorKind::Set,
4654 AccessorWrite::ReadOnly => crate::AccessorKind::Get,
4655 }
4656 }
4657}
4658
4659pub(crate) fn emit_object_field_index_by_name(
4660 emitter: &mut FunctionEmitter,
4661 ctx: &CodegenCtx,
4662 object_local: u32,
4663 name_global: u32,
4664) {
4665 emit_field_index_by_name(emitter, ctx, object_local, name_global, false);
4666}
4667
4668fn emit_field_index_by_name(
4669 emitter: &mut FunctionEmitter,
4670 ctx: &CodegenCtx,
4671 object_local: u32,
4672 name_global: u32,
4673 accessor: bool,
4674) {
4675 ctx.latch(emit_field_index_by_name_checked(
4676 emitter,
4677 ctx,
4678 object_local,
4679 name_global,
4680 accessor,
4681 ));
4682}
4683
4684fn emit_field_index_by_name_checked(
4685 emitter: &mut FunctionEmitter,
4686 ctx: &CodegenCtx,
4687 object_local: u32,
4688 name_global: u32,
4689 accessor: bool,
4690) -> Result<(), crate::compiler_error::CompilerFailure> {
4691 emitter.instruction(Instruction::LocalGet(object_local));
4692 emitter.instruction(Instruction::GlobalGet(name_global));
4693 emitter.instruction(Instruction::I32Const(i32::from(accessor)));
4694 emitter.instruction(Instruction::Call(
4695 ctx.symbols
4696 .field_lookup_function
4697 .ok_or_else(|| crate::codegen::internal_failure("field lookup allocated"))?,
4698 ));
4699
4700 Ok(())
4701}
4702
4703fn emit_direct_call(
4709 emitter: &mut FunctionEmitter,
4710 ctx: &CodegenCtx,
4711 is_host: bool,
4712 wasm_idx: u32,
4713 params: &[Type],
4714 ret: &Type,
4715 args: &[ExprId],
4716) -> Result<(), crate::compiler_error::CompilerFailure> {
4717 if params.len() != args.len() {
4718 return Err(crate::codegen::internal_failure(
4719 "direct-call argument count mismatch",
4720 ));
4721 }
4722 let string_type_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
4723 crate::codegen::internal_failure("$string type registered before call codegen")
4724 })?;
4725 let raw_string_type_idx = ctx.symbols.raw_string_type_idx().ok_or_else(|| {
4726 crate::codegen::internal_failure("$rawString type registered before call codegen")
4727 })?;
4728 let mut evaluated = Vec::with_capacity(args.len());
4729 for &arg in args {
4730 emit_expr(emitter, ctx, arg)?;
4731 let slot = emitter.add_anonymous_local(
4732 ctx.symbols.value_type(
4733 &ctx.ta
4734 .try_expr(arg)
4735 .map_err(crate::codegen::arena_failure)?
4736 .ty,
4737 )?,
4738 )?;
4739 emitter.instruction(Instruction::LocalSet(slot));
4740 evaluated.push(slot);
4741 }
4742 for (i, (&arg_id, local)) in args.iter().zip(evaluated).enumerate() {
4743 emitter.instruction(Instruction::LocalGet(local));
4744 if let Some(param_ty) = params.get(i) {
4750 let arg_ty = ctx
4751 .ta
4752 .try_expr(arg_id)
4753 .map_err(crate::codegen::arena_failure)?
4754 .ty
4755 .clone();
4756 let default = ctx
4757 .symbols
4758 .host_call_defaults(wasm_idx, args.len())
4759 .get(i)
4760 .and_then(Option::as_ref);
4761 crate::codegen::argument_defaults::emit_argument(
4762 emitter,
4763 ctx,
4764 &arg_ty,
4765 ctx.symbols.value_type(param_ty)?,
4766 default,
4767 )?;
4768 }
4769 if is_host
4774 && params
4775 .get(i)
4776 .is_some_and(|p| matches!(p.lowered(), Type::String))
4777 {
4778 emitter.instruction(Instruction::StructGet {
4779 struct_type_index: string_type_idx,
4780 field_index: 1,
4781 });
4782 }
4783 }
4784 emitter.instruction(Instruction::Call(wasm_idx));
4785 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
4786 emit_undefined(emitter, ctx)?;
4787 }
4788 let _: () = if is_host && matches!(ret.lowered(), Type::String) {
4789 let scratch = emitter.add_anonymous_local(ValType::Ref(RefType {
4790 nullable: false,
4791 heap_type: HeapType::Concrete(raw_string_type_idx),
4792 }))?;
4793 emitter.instruction(Instruction::LocalSet(scratch));
4794 let vtable_global = ctx
4795 .symbols
4796 .prelude_global_idx("string_vtable")
4797 .ok_or_else(|| {
4798 crate::codegen::internal_failure("string_vtable imported from prelude")
4799 })?;
4800 emitter.instruction(Instruction::GlobalGet(vtable_global));
4801 emitter.instruction(Instruction::LocalGet(scratch));
4802 emitter.instruction(Instruction::I64Const(0));
4803 emitter.instruction(Instruction::StructNew(string_type_idx));
4804 };
4805 Ok(())
4806}
4807
4808fn emit_indirect_closure_call(
4817 emitter: &mut FunctionEmitter,
4818 ctx: &CodegenCtx,
4819 callee: ExprId,
4820 args: &[ExprId],
4821 result_ty: &Type,
4822) -> Result<(), crate::compiler_error::CompilerFailure> {
4823 let callee_ty = ctx
4824 .ta
4825 .source_type(callee)
4826 .map_err(crate::codegen::arena_failure)?
4827 .clone();
4828 let receiver = emit_callee(emitter, ctx, callee)?;
4829 emitter.call_receiver = receiver;
4830 emit_indirect_closure_call_with_receiver_on_stack(emitter, ctx, &callee_ty, args, result_ty)?;
4831 Ok(())
4832}
4833
4834fn emit_callee(
4836 emitter: &mut FunctionEmitter,
4837 ctx: &CodegenCtx,
4838 callee: ExprId,
4839) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
4840 let reference = reference_expression(ctx.ta, callee)?;
4841 let receiver = match &ctx
4842 .ta
4843 .try_expr(reference)
4844 .map_err(crate::codegen::arena_failure)?
4845 .kind
4846 {
4847 TypedExprKind::FieldAccess { receiver, .. }
4848 | TypedExprKind::IndexAccess { receiver, .. }
4849 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => Some(*receiver),
4850 _ => None,
4851 };
4852 let mark = emitter.single_evaluation_mark();
4853 let slot = receiver
4854 .map(|receiver| {
4855 emit_expr(emitter, ctx, receiver)?;
4856 let slot = emitter.add_anonymous_local(
4857 ctx.symbols.value_type(
4858 &ctx.ta
4859 .try_expr(receiver)
4860 .map_err(crate::codegen::arena_failure)?
4861 .ty,
4862 )?,
4863 )?;
4864 emitter.instruction(Instruction::LocalSet(slot));
4865 emitter.record_single_evaluation(receiver, slot)?;
4866 Ok::<_, crate::compiler_error::CompilerFailure>(slot)
4867 })
4868 .transpose()?;
4869 emit_expr(emitter, ctx, callee)?;
4870 emitter.end_single_evaluations(mark)?;
4871 Ok(
4872 if matches!(
4873 ctx.ta
4874 .try_expr(reference)
4875 .map_err(crate::codegen::arena_failure)?
4876 .kind,
4877 TypedExprKind::OptionalChain { .. }
4878 ) {
4879 emitter.call_receiver.take()
4880 } else {
4881 slot
4882 },
4883 )
4884}
4885
4886fn reference_expression(
4887 ast: &crate::TypedAst,
4888 mut id: ExprId,
4889) -> Result<ExprId, crate::compiler_error::CompilerFailure> {
4890 loop {
4891 match ast
4892 .try_expr(id)
4893 .map_err(crate::codegen::arena_failure)?
4894 .kind
4895 {
4896 TypedExprKind::Cast { value, .. } | TypedExprKind::NonNullAssert { value } => {
4897 id = value;
4898 }
4899 _ => return Ok(id),
4900 }
4901 }
4902}
4903
4904fn emit_indirect_closure_call_with_receiver_on_stack(
4910 emitter: &mut FunctionEmitter,
4911 ctx: &CodegenCtx,
4912 callee_ty: &Type,
4913 args: &[ExprId],
4914 result_ty: &Type,
4915) -> Result<(), crate::compiler_error::CompilerFailure> {
4916 let receiver = emitter.call_receiver.take();
4917 let callee = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4918 emitter.instruction(Instruction::LocalSet(callee));
4919 let mut arguments = Vec::with_capacity(args.len());
4920 for &arg in args {
4921 emit_expr(emitter, ctx, arg)?;
4922 cast::emit_box(
4923 emitter,
4924 ctx,
4925 &ctx.ta
4926 .try_expr(arg)
4927 .map_err(crate::codegen::arena_failure)?
4928 .ty,
4929 )?;
4930 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4931 emitter.instruction(Instruction::LocalSet(slot));
4932 arguments.push(slot);
4933 }
4934 emitter.instruction(Instruction::LocalGet(callee));
4935 crate::codegen::cast_check::emit_operation_cast_on_stack(
4936 emitter,
4937 ctx,
4938 &Type::Unknown,
4939 callee_ty,
4940 )?;
4941 let callee_ty = callee_ty.lowered();
4946 let closure_sig = crate::codegen::closures::classify(callee_ty)?;
4947 let closure_struct_idx = ctx
4948 .symbols
4949 .closure_struct_type_idx(closure_sig)
4950 .ok_or_else(|| {
4951 crate::codegen::internal_failure(
4952 "closure struct type registered for every closure-typed callee",
4953 )
4954 })?;
4955 let fn_type_idx = ctx
4956 .symbols
4957 .closure_func_type_idx(closure_sig)
4958 .ok_or_else(|| {
4959 crate::codegen::internal_failure(
4960 "closure funcref type registered for every closure-typed callee",
4961 )
4962 })?;
4963 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4964 nullable: false,
4965 heap_type: HeapType::Concrete(closure_struct_idx),
4966 }))?;
4967 emitter.instruction(Instruction::LocalTee(closure_local));
4968 emitter.instruction(Instruction::StructGet {
4969 struct_type_index: closure_struct_idx,
4970 field_index: 2,
4971 });
4972 if let Some(receiver) = receiver {
4973 crate::codegen::this_binding::bind(emitter, ctx, receiver)?;
4974 }
4975 for argument in arguments {
4983 emitter.instruction(Instruction::LocalGet(argument));
4984 }
4985 emitter.instruction(Instruction::LocalGet(closure_local));
4986 emitter.instruction(Instruction::StructGet {
4987 struct_type_index: closure_struct_idx,
4988 field_index: 1,
4989 });
4990 emitter.instruction(Instruction::CallRef(fn_type_idx));
4991 let Type::Function { ret, .. } = callee_ty.lowered() else {
4994 return Err(crate::codegen::internal_failure(
4995 "indirect call requires a function type",
4996 ));
4997 };
4998 let _: () = if !ret.is_void() {
4999 crate::codegen::cast_check::emit_checked_cast_on_stack(
5000 emitter,
5001 ctx,
5002 &Type::Unknown,
5003 result_ty,
5004 )?;
5005 };
5006 Ok(())
5007}
5008
5009fn emit_typeof_tag(
5012 emitter: &mut FunctionEmitter,
5013 ctx: &CodegenCtx,
5014 value: ExprId,
5015 tag: crate::TypeofTagKind,
5016) -> Result<(), crate::compiler_error::CompilerFailure> {
5017 let _: () = match tag {
5018 crate::TypeofTagKind::BigInt => {
5019 emit_expr(emitter, ctx, value)?;
5020 cast::emit_box(
5021 emitter,
5022 ctx,
5023 &ctx.ta
5024 .try_expr(value)
5025 .map_err(crate::codegen::arena_failure)?
5026 .ty,
5027 )?;
5028 let intrinsic = ctx
5029 .symbols
5030 .bigint_type_idx()
5031 .ok_or_else(|| crate::codegen::internal_failure("bigint intrinsic missing"))?;
5032 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(intrinsic)));
5033 }
5034 crate::TypeofTagKind::Undefined => {
5035 emit_expr(emitter, ctx, value)?;
5036 cast::emit_box(
5037 emitter,
5038 ctx,
5039 &ctx.ta
5040 .try_expr(value)
5041 .map_err(crate::codegen::arena_failure)?
5042 .ty,
5043 )?;
5044 emit_is_undefined(emitter, ctx)?;
5045 }
5046 crate::TypeofTagKind::Number => {
5047 let boxed = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
5053 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
5054 })?;
5055 emit_expr(emitter, ctx, value)?;
5056 cast::emit_box(
5057 emitter,
5058 ctx,
5059 &ctx.ta
5060 .try_expr(value)
5061 .map_err(crate::codegen::arena_failure)?
5062 .ty,
5063 )?;
5064 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5065 }
5066 crate::TypeofTagKind::String => {
5067 let string = ctx.symbols.string_type_idx().ok_or_else(|| {
5068 crate::codegen::internal_failure("$string intrinsic type registered")
5069 })?;
5070 emit_expr(emitter, ctx, value)?;
5071 cast::emit_box(
5072 emitter,
5073 ctx,
5074 &ctx.ta
5075 .try_expr(value)
5076 .map_err(crate::codegen::arena_failure)?
5077 .ty,
5078 )?;
5079 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
5080 }
5081 crate::TypeofTagKind::Boolean => {
5082 let boxed = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
5083 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
5084 })?;
5085 emit_expr(emitter, ctx, value)?;
5086 cast::emit_box(
5087 emitter,
5088 ctx,
5089 &ctx.ta
5090 .try_expr(value)
5091 .map_err(crate::codegen::arena_failure)?
5092 .ty,
5093 )?;
5094 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5095 }
5096 crate::TypeofTagKind::Function => {
5097 let closure_idx = ctx.symbols.closure_type_idx().ok_or_else(|| {
5098 crate::codegen::internal_failure("$Closure intrinsic type registered")
5099 })?;
5100 emit_expr(emitter, ctx, value)?;
5101 cast::emit_box(
5102 emitter,
5103 ctx,
5104 &ctx.ta
5105 .try_expr(value)
5106 .map_err(crate::codegen::arena_failure)?
5107 .ty,
5108 )?;
5109 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
5110 }
5111 crate::TypeofTagKind::Object => {
5112 let scratch_ty = ctx.symbols.value_type(&Type::Unknown)?;
5113 let nullable = matches!(scratch_ty, ValType::Ref(rt) if rt.nullable);
5114 let scratch = emitter.add_anonymous_local(scratch_ty)?;
5115 emit_expr(emitter, ctx, value)?;
5116 cast::emit_box(
5117 emitter,
5118 ctx,
5119 &ctx.ta
5120 .try_expr(value)
5121 .map_err(crate::codegen::arena_failure)?
5122 .ty,
5123 )?;
5124 emitter.instruction(Instruction::LocalSet(scratch));
5125 emit_is_non_primitive(emitter, ctx, scratch, Closures::AreNotObjects)?;
5126 if nullable {
5127 emitter.instruction(Instruction::LocalGet(scratch));
5132 emitter.instruction(Instruction::RefIsNull);
5133 emitter.instruction(Instruction::I32Or);
5134 }
5135 }
5136 };
5137 Ok(())
5138}
5139
5140#[derive(Clone, Copy, PartialEq, Eq)]
5143pub(crate) enum Closures {
5144 AreObjects,
5145 AreNotObjects,
5146}
5147
5148pub(crate) fn emit_is_non_primitive(
5157 emitter: &mut FunctionEmitter,
5158 ctx: &CodegenCtx,
5159 local: u32,
5160 closures: Closures,
5161) -> Result<(), crate::compiler_error::CompilerFailure> {
5162 let intrinsics = ctx
5163 .symbols
5164 .intrinsic_type_indices()
5165 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5166 emitter.instruction(Instruction::LocalGet(local));
5167 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5168 intrinsics.object,
5169 )));
5170 let mut excluded = vec![
5171 intrinsics.string,
5172 intrinsics.boxed_number,
5173 intrinsics.boxed_boolean,
5174 intrinsics.bigint,
5175 intrinsics.undefined,
5176 ];
5177 if closures == Closures::AreNotObjects {
5178 excluded.push(intrinsics.closure);
5179 }
5180 for primitive in excluded {
5181 emitter.instruction(Instruction::LocalGet(local));
5182 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(primitive)));
5183 emitter.instruction(Instruction::I32Eqz);
5184 emitter.instruction(Instruction::I32And);
5185 }
5186 Ok(())
5187}
5188
5189fn has_numeric_operands(
5192 ctx: &CodegenCtx,
5193 operands: &[ExprId],
5194 result_ty: &Type,
5195) -> Result<bool, crate::compiler_error::CompilerFailure> {
5196 if !matches!(result_ty.lowered(), Type::Number | Type::NumberLiteral(_)) {
5197 return Ok(false);
5198 }
5199 for &operand in operands {
5200 let ty = &ctx
5201 .ta
5202 .try_expr(operand)
5203 .map_err(crate::codegen::arena_failure)?
5204 .ty;
5205 if !matches!(
5206 ty.primitive_behavior(),
5207 Type::Number | Type::NumberLiteral(_)
5208 ) {
5209 return Ok(false);
5210 }
5211 }
5212 Ok(true)
5213}
5214
5215fn emit_number_bitwise(
5216 emitter: &mut FunctionEmitter,
5217 ctx: &CodegenCtx,
5218 op: BinOp,
5219 lhs: ExprId,
5220 rhs: ExprId,
5221) -> Result<(), crate::compiler_error::CompilerFailure> {
5222 emit_primitive_operand(emitter, ctx, lhs)?;
5223 emit_to_int32(emitter, ctx)?;
5224 emit_primitive_operand(emitter, ctx, rhs)?;
5225 emit_to_int32(emitter, ctx)?;
5226 let (instruction, unsigned) = match op {
5228 BinOp::BitAnd => (Instruction::I32And, false),
5229 BinOp::BitOr => (Instruction::I32Or, false),
5230 BinOp::BitXor => (Instruction::I32Xor, false),
5231 BinOp::Shl => (Instruction::I32Shl, false),
5232 BinOp::Shr => (Instruction::I32ShrS, false),
5233 BinOp::UnsignedShr => (Instruction::I32ShrU, true),
5234 _ => {
5235 return Err(crate::codegen::internal_failure(
5236 "a numeric bitwise operation has a bitwise operator",
5237 ));
5238 }
5239 };
5240 emitter.instruction(instruction);
5241 emitter.instruction(if unsigned {
5242 Instruction::F64ConvertI32U
5243 } else {
5244 Instruction::F64ConvertI32S
5245 });
5246 Ok(())
5247}
5248
5249fn emit_to_int32(
5253 emitter: &mut FunctionEmitter,
5254 ctx: &CodegenCtx,
5255) -> Result<(), crate::compiler_error::CompilerFailure> {
5256 let value = emitter.add_anonymous_local(ValType::F64)?;
5257 emitter.instruction(Instruction::LocalTee(value));
5258 emitter.instruction(Instruction::F64Abs);
5259 emitter.instruction(Instruction::F64Const(Ieee64::from(
5260 9_223_372_036_854_775_808.0,
5261 )));
5262 emitter.instruction(Instruction::F64Lt);
5263 emitter.emit_if(BlockType::Result(ValType::I32));
5264 emitter.instruction(Instruction::LocalGet(value));
5265 emitter.instruction(Instruction::I64TruncSatF64S);
5266 emitter.instruction(Instruction::I32WrapI64);
5267 emitter.emit_else();
5268 emitter.instruction(Instruction::LocalGet(value));
5269 cast::emit_box(emitter, ctx, &Type::Number)?;
5270 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
5271 cast::emit_box(emitter, ctx, &Type::Number)?;
5272 let host = ctx
5273 .symbols
5274 .func_idx(&crate::mangle::prelude("__value_bitor"))
5275 .ok_or_else(|| crate::codegen::internal_failure("missing bitwise host import: bitor"))?;
5276 emitter.instruction(Instruction::Call(host));
5277 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5278 emitter.instruction(Instruction::I32TruncF64S);
5279 emitter.emit_end();
5280 Ok(())
5281}
5282
5283fn emit_bitwise_host(
5284 emitter: &mut FunctionEmitter,
5285 ctx: &CodegenCtx,
5286 name: &str,
5287 operands: &[ExprId],
5288 result_ty: &Type,
5289) -> Result<(), crate::compiler_error::CompilerFailure> {
5290 for &operand in operands {
5291 emit_expr(emitter, ctx, operand)?;
5292 let ty = &ctx
5293 .ta
5294 .try_expr(operand)
5295 .map_err(crate::codegen::arena_failure)?
5296 .ty;
5297 cast::emit_box(emitter, ctx, ty)?;
5298 }
5299 let mangled = crate::mangle::prelude(&format!("__value_{name}"));
5300 let host = ctx.symbols.func_idx(&mangled).ok_or_else(|| {
5301 crate::codegen::internal_failure(format!("missing bitwise host import: {name}"))
5302 })?;
5303 emitter.instruction(Instruction::Call(host));
5304 cast::emit_cast_to(emitter, ctx, result_ty)
5305}
5306
5307fn emit_unary(
5308 emitter: &mut FunctionEmitter,
5309 ctx: &CodegenCtx,
5310 op: UnOp,
5311 operand: ExprId,
5312 result_ty: &Type,
5313) -> Result<(), crate::compiler_error::CompilerFailure> {
5314 if matches!(op, UnOp::Neg | UnOp::Pos)
5315 && try_emit_unreachable_for_never_operand(emitter, ctx, &[operand])?
5316 {
5317 return Ok(());
5318 }
5319 let _: () = match op {
5320 UnOp::BitNot if has_numeric_operands(ctx, &[operand], result_ty)? => {
5321 emit_primitive_operand(emitter, ctx, operand)?;
5322 emit_to_int32(emitter, ctx)?;
5323 emitter.instruction(Instruction::I32Const(-1));
5324 emitter.instruction(Instruction::I32Xor);
5325 emitter.instruction(Instruction::F64ConvertI32S);
5326 }
5327 UnOp::BitNot => emit_bitwise_host(emitter, ctx, "bitnot", &[operand], result_ty)?,
5328 UnOp::Neg => {
5329 if ctx
5331 .ta
5332 .try_expr(operand)
5333 .map_err(crate::codegen::arena_failure)?
5334 .ty
5335 .is_bigint()
5336 {
5337 emit_primitive_operand(emitter, ctx, operand)?;
5338 emit_bigint_extract_to_stack(emitter, ctx);
5339 let host_idx = ctx
5340 .symbols
5341 .func_idx(&crate::mangle::host(
5342 crate::runtime::BIGINT_MODULE_NAME,
5343 "neg",
5344 ))
5345 .ok_or_else(|| {
5346 crate::codegen::internal_failure(
5347 "submilli:bigint.neg import recorded by codegen bootstrap",
5348 )
5349 })?;
5350 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
5351 return Ok(());
5352 }
5353 emit_primitive_operand(emitter, ctx, operand)?;
5354 emitter.instruction(Instruction::F64Neg);
5355 }
5356 UnOp::Pos => {
5357 let operand_ty = ctx
5362 .ta
5363 .try_expr(operand)
5364 .map_err(crate::codegen::arena_failure)?
5365 .ty
5366 .clone();
5367 if operand_ty.is_string_shaped() {
5368 emit_primitive_operand(emitter, ctx, operand)?;
5369 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
5370 } else if matches!(operand_ty.primitive_behavior(), Type::Union(_)) {
5371 emit_expr(emitter, ctx, operand)?;
5372 cast::emit_box(emitter, ctx, &operand_ty)?;
5373 emit_union_to_number(emitter, ctx)?;
5374 } else {
5375 emit_primitive_operand(emitter, ctx, operand)?;
5376 }
5377 }
5378 UnOp::Not => {
5379 let operand_ty = ctx
5385 .ta
5386 .try_expr(operand)
5387 .map_err(crate::codegen::arena_failure)?
5388 .ty
5389 .clone();
5390 emit_expr(emitter, ctx, operand)?;
5391 crate::codegen::function_emitter::cast::emit_condition_to_i32(
5392 emitter,
5393 ctx,
5394 &operand_ty,
5395 )?;
5396 emitter.instruction(Instruction::I32Eqz);
5397 }
5398 };
5399 Ok(())
5400}
5401
5402fn to_number_host(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
5403 ctx.symbols
5404 .func_idx(&crate::mangle::host(
5405 crate::runtime::NUMBER_MODULE_NAME,
5406 "toNumber",
5407 ))
5408 .ok_or_else(|| {
5409 crate::codegen::internal_failure(
5410 "submilli:number.toNumber import recorded by codegen analysis",
5411 )
5412 })
5413}
5414
5415fn emit_union_to_number(
5418 emitter: &mut FunctionEmitter,
5419 ctx: &CodegenCtx,
5420) -> Result<(), crate::compiler_error::CompilerFailure> {
5421 let string = ctx
5422 .symbols
5423 .string_type_idx()
5424 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic type registered"))?;
5425 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5426 emitter.instruction(Instruction::LocalTee(tmp));
5427 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
5428 emitter.emit_if(BlockType::Result(ValType::F64));
5429 emitter.instruction(Instruction::LocalGet(tmp));
5430 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(string)));
5431 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
5432 emitter.emit_else();
5433 emitter.instruction(Instruction::LocalGet(tmp));
5434 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5435 emitter.emit_end();
5436 Ok(())
5437}
5438
5439#[allow(clippy::too_many_arguments)]
5443pub(crate) fn emit_method_call(
5444 emitter: &mut FunctionEmitter,
5445 ctx: &CodegenCtx,
5446 receiver: ExprId,
5447 iface: &crate::MangledName,
5448 method: &str,
5449 args: &[ExprId],
5450 generic_args: Option<&[crate::GenericArgument]>,
5451 type_args: &[Type],
5452 return_cast: Option<&Type>,
5453 call_ret_ty: &Type,
5454) -> Result<(), crate::compiler_error::CompilerFailure> {
5455 let recv_ty = ctx
5456 .ta
5457 .source_type(receiver)
5458 .map_err(crate::codegen::arena_failure)?
5459 .clone();
5460 if !emit_dropped_static_receiver(emitter, ctx, receiver, iface)? {
5461 emit_receiver(emitter, ctx, receiver)?;
5462 }
5463 emit_method_call_with_receiver_on_stack(
5464 emitter,
5465 ctx,
5466 &recv_ty,
5467 iface,
5468 method,
5469 args,
5470 generic_args,
5471 type_args,
5472 return_cast,
5473 call_ret_ty,
5474 )?;
5475 Ok(())
5476}
5477
5478fn emit_dropped_static_receiver(
5482 emitter: &mut FunctionEmitter,
5483 ctx: &CodegenCtx,
5484 receiver: ExprId,
5485 iface: &crate::MangledName,
5486) -> Result<bool, crate::compiler_error::CompilerFailure> {
5487 let expr = ctx
5488 .ta
5489 .try_expr(receiver)
5490 .map_err(crate::codegen::arena_failure)?;
5491 if !matches!(expr.kind, TypedExprKind::GlobalRef { .. })
5492 || static_interface(ctx, &expr.ty) != Some(iface)
5493 {
5494 return Ok(false);
5495 }
5496 match ctx.symbols.value_type(&expr.ty)? {
5497 ValType::Ref(RefType { heap_type, .. }) => {
5498 emitter.instruction(Instruction::RefNull(heap_type));
5499 }
5500 other => {
5501 return Err(crate::codegen::internal_failure(format!(
5502 "static-interface binding lowered to {other:?}"
5503 )));
5504 }
5505 }
5506 Ok(true)
5507}
5508
5509#[allow(clippy::too_many_arguments)]
5516fn emit_method_call_with_receiver_on_stack(
5517 emitter: &mut FunctionEmitter,
5518 ctx: &CodegenCtx,
5519 recv_ty: &Type,
5520 iface: &crate::MangledName,
5521 method: &str,
5522 args: &[ExprId],
5523 generic_args: Option<&[crate::GenericArgument]>,
5524 type_args: &[Type],
5525 return_cast: Option<&Type>,
5526 call_ret_ty: &Type,
5527) -> Result<(), crate::compiler_error::CompilerFailure> {
5528 let concrete_receiver = crate::typechecker::infer::narrowing::strip_nullish(recv_ty);
5529 if concrete_receiver.primitive_union_members().is_some() {
5530 cast::emit_box(emitter, ctx, recv_ty)?;
5531 emit_live_member_on_stack(emitter, ctx, iface, method, Some(args))?;
5532 return cast::emit_cast_to(emitter, ctx, call_ret_ty);
5533 }
5534 crate::codegen::field_guards::attach(emitter, ctx, &concrete_receiver)?;
5535 if recv_ty.is_regexp_match_array() && *iface == crate::mangle::prelude("Array") {
5536 cast::emit_cast_to(emitter, ctx, &Type::Array(Box::new(Type::regexp_element())))?;
5537 }
5538 let direct_key = crate::mangle::extend(iface, method);
5539 if let Some(func_idx) = ctx.symbols.func_idx(&direct_key) {
5540 if matches!(recv_ty.primitive_behavior(), Type::Number)
5541 && ctx.symbols.value_type(recv_ty)? != ValType::F64
5542 {
5543 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5544 }
5545 match ctx.symbols.iface_dispatch(iface) {
5554 Some(crate::Dispatch::Direct) => {
5555 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
5567 emitter.instruction(Instruction::RefAsNonNull);
5568 }
5569 }
5570 Some(crate::Dispatch::Static) => {
5571 emitter.instruction(Instruction::Drop);
5572 }
5573 None | Some(crate::Dispatch::VTable) => {}
5578 }
5579 let abi = wrapper_abi_for_call(
5589 ctx,
5590 &direct_key,
5591 args,
5592 generic_args,
5593 return_cast,
5594 call_ret_ty,
5595 )?;
5596 let defaults = ctx.symbols.host_call_defaults(func_idx, args.len());
5597 emit_args_into_slots_with_defaults(emitter, ctx, args, Some(&abi.params), defaults)?;
5598 if ctx.symbols.runtime_generic_functions.contains(&direct_key) {
5602 crate::codegen::runtime_descriptors::environment(emitter, ctx, type_args)?;
5603 }
5604 emitter.instruction(Instruction::Call(func_idx));
5605 emit_slot_return_cast(emitter, ctx, call_ret_ty, Some(&abi))?;
5606 return Ok(());
5607 }
5608 if ctx.symbols.is_rewritable_class_method(iface, method) {
5611 emit_interface_method_via_shape_with_receiver_on_stack(
5612 emitter,
5613 ctx,
5614 method,
5615 args,
5616 call_ret_ty,
5617 None,
5618 )?;
5619 return Ok(());
5620 }
5621 if ctx.symbols.class_method_slot(iface, method).is_some() {
5626 emit_class_vtable_dispatch(emitter, ctx, iface, method, args, call_ret_ty)?;
5627 return Ok(());
5628 }
5629 if let Some(slot) = vtable_slot_for_method(method) {
5630 emit_vtable_dispatch_on_object_stack(emitter, ctx, slot);
5631 return Ok(());
5632 }
5633 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
5642 emit_interface_method_via_shape_with_receiver_on_stack(
5643 emitter,
5644 ctx,
5645 method,
5646 args,
5647 call_ret_ty,
5648 None,
5649 )?;
5650 return Ok(());
5651 }
5652 Err(crate::codegen::internal_failure(format!(
5653 "method dispatch was not registered for `{iface}.{method}`"
5654 )))
5655}
5656
5657fn emit_class_method_value(
5658 emitter: &mut FunctionEmitter,
5659 ctx: &CodegenCtx,
5660 receiver_ty: &Type,
5661 method: &str,
5662 result_ty: &Type,
5663 check_ty: &Type,
5664) -> Result<(), crate::compiler_error::CompilerFailure> {
5665 let object_shape = ctx
5666 .symbols
5667 .intrinsic_type_indices()
5668 .ok_or_else(|| {
5669 crate::codegen::internal_failure("class method value requires object shape intrinsic")
5670 })?
5671 .object_shape;
5672 let receiver = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
5673 emit_object_property_read_as(emitter, ctx, receiver, method, result_ty, check_ty)
5674}
5675
5676pub(in crate::codegen) fn emit_interface_method_via_shape_with_receiver_on_stack(
5692 emitter: &mut FunctionEmitter,
5693 ctx: &CodegenCtx,
5694 method: &str,
5695 args: &[ExprId],
5696 call_ret_ty: &Type,
5697 slot_index_local: Option<u32>,
5698) -> Result<(), crate::compiler_error::CompilerFailure> {
5699 let args: Vec<_> = args
5700 .iter()
5701 .copied()
5702 .map(ShapeArgument::Expression)
5703 .collect();
5704 emit_shape_method_with_arguments(emitter, ctx, method, &args, call_ret_ty, slot_index_local)?;
5705 Ok(())
5706}
5707
5708enum ShapeArgument {
5709 Expression(ExprId),
5710 Local { slot: u32, ty: Type },
5711}
5712
5713impl ShapeArgument {
5714 fn ty(&self, ctx: &CodegenCtx) -> Result<Type, crate::compiler_error::CompilerFailure> {
5715 Ok(match self {
5716 Self::Expression(id) => ctx
5717 .ta
5718 .try_expr(*id)
5719 .map_err(crate::codegen::arena_failure)?
5720 .ty
5721 .clone(),
5722 Self::Local { ty, .. } => ty.clone(),
5723 })
5724 }
5725
5726 fn emit(
5727 &self,
5728 emitter: &mut FunctionEmitter,
5729 ctx: &CodegenCtx,
5730 ) -> Result<(), crate::compiler_error::CompilerFailure> {
5731 let _: () = match self {
5732 Self::Expression(id) => emit_expr(emitter, ctx, *id)?,
5733 Self::Local { slot, .. } => {
5734 emitter.instruction(Instruction::LocalGet(*slot));
5735 }
5736 };
5737 Ok(())
5738 }
5739}
5740
5741fn emit_shape_method_with_arguments(
5742 emitter: &mut FunctionEmitter,
5743 ctx: &CodegenCtx,
5744 method: &str,
5745 args: &[ShapeArgument],
5746 call_ret_ty: &Type,
5747 slot_index_local: Option<u32>,
5748) -> Result<(), crate::compiler_error::CompilerFailure> {
5749 let intrinsics = ctx
5750 .symbols
5751 .intrinsic_type_indices()
5752 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5753
5754 let arg_tys: Vec<Type> = args.iter().map(|a| a.ty(ctx)).collect::<Result<_, _>>()?;
5757 let fn_ty = Type::Function {
5758 params: arg_tys,
5759 ret: Box::new(call_ret_ty.clone()),
5760 predicate: None,
5761 has_rest: false,
5765 optional: 0,
5766 };
5767 let closure_sig = crate::codegen::closures::classify(&fn_ty)?;
5768 let closure_struct_idx = ctx
5769 .symbols
5770 .closure_struct_type_idx(closure_sig)
5771 .ok_or_else(|| {
5772 crate::codegen::internal_failure(
5773 "closure struct registered for the method's signature (SUB-166)",
5774 )
5775 })?;
5776 let fn_type_idx = ctx
5777 .symbols
5778 .closure_func_type_idx(closure_sig)
5779 .ok_or_else(|| {
5780 crate::codegen::internal_failure(
5781 "closure funcref type registered for the method's signature",
5782 )
5783 })?;
5784
5785 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5786 nullable: false,
5787 heap_type: HeapType::Concrete(intrinsics.object_shape),
5788 }))?;
5789 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5790 nullable: false,
5791 heap_type: HeapType::Concrete(closure_struct_idx),
5792 }))?;
5793
5794 emitter.instruction(Instruction::RefAsNonNull);
5798 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
5799 intrinsics.object_shape,
5800 )));
5801 emitter.instruction(Instruction::LocalSet(rcv_local));
5802
5803 if let Some(index_local) = slot_index_local {
5806 emit_field_slot_get(emitter, intrinsics, rcv_local, index_local);
5807 } else {
5808 let name_global = ctx
5809 .symbols
5810 .field_name_string_global_idx(method)
5811 .ok_or_else(|| {
5812 crate::codegen::internal_failure(
5813 ("per-name string global recorded during field-name-strings emission \
5814 for interface method names")
5815 .to_string(),
5816 )
5817 })?;
5818 emit_object_field_read_by_name(emitter, ctx, rcv_local, name_global);
5819 }
5820
5821 let method_value = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5822 emitter.instruction(Instruction::LocalSet(method_value));
5823 let mut evaluated_args = Vec::with_capacity(args.len());
5824 for arg in args {
5825 arg.emit(emitter, ctx)?;
5826 cast::emit_box(emitter, ctx, &arg.ty(ctx)?)?;
5827 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5828 emitter.instruction(Instruction::LocalSet(slot));
5829 evaluated_args.push(slot);
5830 }
5831 emitter.instruction(Instruction::LocalGet(method_value));
5832 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5833 intrinsics.closure,
5834 )));
5835 emitter.instruction(Instruction::I32Eqz);
5836 emitter.emit_if(BlockType::Empty);
5837 crate::codegen::throw::emit_type_error_throw(emitter, ctx, "Value is not callable");
5838 emitter.emit_end();
5839 emitter.instruction(Instruction::LocalGet(method_value));
5840
5841 crate::codegen::closure_coercions::emit_erased_cast(
5845 emitter,
5846 ctx,
5847 crate::codegen::closures::ClosureSig::of(args.len(), call_ret_ty)?,
5848 )?;
5849 emitter.instruction(Instruction::LocalTee(closure_local));
5850
5851 emitter.instruction(Instruction::StructGet {
5853 struct_type_index: closure_struct_idx,
5854 field_index: 2,
5855 });
5856
5857 crate::codegen::this_binding::bind(emitter, ctx, rcv_local)?;
5858 for slot in evaluated_args {
5861 emitter.instruction(Instruction::LocalGet(slot));
5862 }
5863
5864 emitter.instruction(Instruction::LocalGet(closure_local));
5866 emitter.instruction(Instruction::StructGet {
5867 struct_type_index: closure_struct_idx,
5868 field_index: 1,
5869 });
5870 emitter.instruction(Instruction::CallRef(fn_type_idx));
5871
5872 let _: () = if !call_ret_ty.is_void() {
5875 crate::codegen::cast_check::emit_checked_cast_on_stack(
5876 emitter,
5877 ctx,
5878 &Type::Unknown,
5879 call_ret_ty,
5880 )?;
5881 };
5882 Ok(())
5883}
5884
5885fn vtable_slot_for_method(method: &str) -> Option<u32> {
5896 match method {
5897 "toString" => Some(0),
5898 "toJson" => Some(1),
5899 "equals" => Some(2),
5900 "hash" => Some(3),
5901 _ => None,
5902 }
5903}
5904
5905pub(super) fn emit_vtable_dispatch_on_object_stack(
5916 emitter: &mut FunctionEmitter,
5917 ctx: &CodegenCtx,
5918 slot: u32,
5919) {
5920 let Some(intrinsics) = ctx.require(
5921 ctx.symbols.intrinsic_type_indices(),
5922 "intrinsics declared by codegen entry",
5923 ) else {
5924 return;
5925 };
5926 let fn_type_idx = match slot {
5931 0 => intrinsics.to_string_fn,
5932 1 => intrinsics.to_json_fn,
5933 _ => {
5934 ctx.fail("vtable string dispatch requires toString or toJson slot");
5935 return;
5936 }
5937 };
5938 let Some(obj_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5939 nullable: false,
5940 heap_type: HeapType::Concrete(intrinsics.object),
5941 }))) else {
5942 return;
5943 };
5944 let Some(fn_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5945 nullable: false,
5946 heap_type: HeapType::Concrete(fn_type_idx),
5947 }))) else {
5948 return;
5949 };
5950 emitter.instruction(Instruction::RefAsNonNull);
5956 emitter.instruction(Instruction::LocalTee(obj_local));
5957 emitter.instruction(Instruction::StructGet {
5958 struct_type_index: intrinsics.object,
5959 field_index: 0,
5960 });
5961 emitter.instruction(Instruction::StructGet {
5962 struct_type_index: intrinsics.vtable,
5963 field_index: slot,
5964 });
5965 emitter.instruction(Instruction::LocalSet(fn_local));
5966 emitter.instruction(Instruction::LocalGet(obj_local));
5967 emitter.instruction(Instruction::LocalGet(fn_local));
5968 emitter.instruction(Instruction::CallRef(fn_type_idx));
5969}
5970
5971fn emit_args_into_slots(
5975 emitter: &mut FunctionEmitter,
5976 ctx: &CodegenCtx,
5977 args: &[ExprId],
5978 slots: Option<&[ValType]>,
5979) -> Result<(), crate::compiler_error::CompilerFailure> {
5980 emit_args_into_slots_with_defaults(emitter, ctx, args, slots, &[])
5981}
5982
5983fn emit_args_into_slots_with_defaults(
5984 emitter: &mut FunctionEmitter,
5985 ctx: &CodegenCtx,
5986 args: &[ExprId],
5987 slots: Option<&[ValType]>,
5988 defaults: &[Option<crate::DefaultValue>],
5989) -> Result<(), crate::compiler_error::CompilerFailure> {
5990 if slots.is_some_and(|slots| slots.len() != args.len()) {
5991 return Err(crate::codegen::internal_failure(
5992 "call ABI argument count mismatch",
5993 ));
5994 }
5995 let locals: Vec<_> = args
5996 .iter()
5997 .map(|&arg| {
5998 emit_expr(emitter, ctx, arg)?;
5999 let local = emitter.add_anonymous_local(
6000 ctx.symbols.value_type(
6001 &ctx.ta
6002 .try_expr(arg)
6003 .map_err(crate::codegen::arena_failure)?
6004 .ty,
6005 )?,
6006 )?;
6007 emitter.instruction(Instruction::LocalSet(local));
6008 Ok::<_, crate::compiler_error::CompilerFailure>(local)
6009 })
6010 .collect::<Result<_, _>>()?;
6011 for (i, (&arg, local)) in args.iter().zip(locals).enumerate() {
6012 emitter.instruction(Instruction::LocalGet(local));
6013 if let Some(slot) = slots.and_then(|s| s.get(i).copied()) {
6014 crate::codegen::argument_defaults::emit_argument(
6015 emitter,
6016 ctx,
6017 &ctx.ta
6018 .try_expr(arg)
6019 .map_err(crate::codegen::arena_failure)?
6020 .ty,
6021 slot,
6022 defaults.get(i).and_then(Option::as_ref),
6023 )?;
6024 }
6025 }
6026 Ok(())
6027}
6028
6029fn wrapper_abi_for_call(
6044 ctx: &CodegenCtx,
6045 direct_key: &crate::MangledName,
6046 args: &[ExprId],
6047 generic_args: Option<&[crate::GenericArgument]>,
6048 return_cast: Option<&Type>,
6049 call_ret_ty: &Type,
6050) -> Result<MethodSlotAbi, crate::compiler_error::CompilerFailure> {
6051 if let Some(recorded) = ctx.symbols.iface_method_abi(direct_key) {
6052 return Ok(recorded.clone());
6053 }
6054 let erased = ctx.symbols.value_type(&Type::Unknown)?;
6055 let slot_is_erased =
6056 |i: usize| generic_args.is_some_and(|flags| flags.get(i).is_some_and(|g| g.is_generic));
6057 Ok(MethodSlotAbi {
6058 params: args
6059 .iter()
6060 .enumerate()
6061 .map(|(i, &a)| {
6062 Ok::<_, crate::compiler_error::CompilerFailure>(if slot_is_erased(i) {
6063 erased
6064 } else {
6065 ctx.symbols.value_type(
6066 &ctx.ta
6067 .try_expr(a)
6068 .map_err(crate::codegen::arena_failure)?
6069 .ty,
6070 )?
6071 })
6072 })
6073 .collect::<Result<_, _>>()?,
6074 ret: match return_cast {
6075 Some(_) => Some(erased),
6076 None => (!call_ret_ty.is_void())
6077 .then(|| ctx.symbols.value_type(call_ret_ty))
6078 .transpose()?,
6079 },
6080 })
6081}
6082
6083fn emit_slot_return_cast(
6086 emitter: &mut FunctionEmitter,
6087 ctx: &CodegenCtx,
6088 call_ret_ty: &Type,
6089 abi: Option<&MethodSlotAbi>,
6090) -> Result<(), crate::compiler_error::CompilerFailure> {
6091 if matches!(call_ret_ty.lowered(), Type::Never) {
6092 emitter.instruction(Instruction::Unreachable);
6093 return Ok(());
6094 }
6095 if abi.is_some_and(|abi| abi.ret.is_none()) {
6098 emit_undefined(emitter, ctx)?;
6099 }
6100 if call_ret_ty.is_void() {
6101 return Ok(());
6102 }
6103 if matches!(call_ret_ty, Type::Unknown) {
6104 match abi.and_then(|abi| abi.ret) {
6105 Some(ValType::F64) => cast::emit_box(emitter, ctx, &Type::Number)?,
6106 Some(ValType::I32) => cast::emit_box(emitter, ctx, &Type::Boolean)?,
6107 _ => {}
6108 }
6109 return Ok(());
6110 }
6111 if abi.and_then(|a| a.ret) != Some(ctx.symbols.value_type(call_ret_ty)?) {
6112 crate::codegen::cast_check::emit_checked_cast_on_stack(
6113 emitter,
6114 ctx,
6115 &Type::Unknown,
6116 call_ret_ty,
6117 )?;
6118 }
6119 crate::codegen::field_guards::attach(emitter, ctx, call_ret_ty)?;
6120 Ok(())
6121}
6122
6123fn emit_class_vtable_dispatch(
6136 emitter: &mut FunctionEmitter,
6137 ctx: &CodegenCtx,
6138 class: &crate::MangledName,
6139 method: &str,
6140 args: &[ExprId],
6141 call_ret_ty: &Type,
6142) -> Result<(), crate::compiler_error::CompilerFailure> {
6143 let struct_idx = ctx
6144 .symbols
6145 .class_struct_type_idx(class)
6146 .ok_or_else(|| crate::codegen::internal_failure("class struct type recorded"))?;
6147 let vtable_idx = ctx
6148 .symbols
6149 .class_vtable_type_idx(class)
6150 .ok_or_else(|| crate::codegen::internal_failure("class vtable type recorded"))?;
6151 let slot = ctx
6152 .symbols
6153 .class_method_slot(class, method)
6154 .ok_or_else(|| crate::codegen::internal_failure("class method slot recorded"))?;
6155 let sig_idx = ctx
6156 .symbols
6157 .class_method_sig(class, method)
6158 .ok_or_else(|| crate::codegen::internal_failure("class method sig recorded"))?;
6159
6160 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6161 nullable: false,
6162 heap_type: HeapType::Concrete(struct_idx),
6163 }))?;
6164 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
6167 emitter.instruction(Instruction::LocalSet(rcv_local));
6168
6169 emitter.instruction(Instruction::LocalGet(rcv_local));
6171 emitter.instruction(Instruction::StructGet {
6172 struct_type_index: struct_idx,
6173 field_index: 0,
6174 });
6175 emitter.instruction(Instruction::StructGet {
6176 struct_type_index: vtable_idx,
6177 field_index: slot,
6178 });
6179 let fn_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6180 nullable: false,
6181 heap_type: HeapType::Concrete(sig_idx),
6182 }))?;
6183 emitter.instruction(Instruction::LocalSet(fn_local));
6184
6185 let abi = ctx.symbols.class_method_abi(class, method).cloned();
6187 emitter.instruction(Instruction::LocalGet(rcv_local));
6188 emit_args_into_slots(
6189 emitter,
6190 ctx,
6191 args,
6192 abi.as_ref().map(|a| a.params.as_slice()),
6193 )?;
6194 emitter.instruction(Instruction::LocalGet(fn_local));
6195 emitter.instruction(Instruction::CallRef(sig_idx));
6196 emit_slot_return_cast(emitter, ctx, call_ret_ty, abi.as_ref())?;
6197 Ok(())
6198}
6199
6200fn emit_intrinsic_call(
6201 emitter: &mut FunctionEmitter,
6202 ctx: &CodegenCtx,
6203 kind: Intrinsic,
6204 args: &[ExprId],
6205 result_ty: &Type,
6206) -> Result<(), crate::compiler_error::CompilerFailure> {
6207 let _: () = match kind {
6208 Intrinsic::Assert => {
6209 if args.len() != 2 {
6213 return Err(crate::codegen::internal_failure(
6214 "assert requires two arguments",
6215 ));
6216 }
6217 emit_expr(emitter, ctx, args[0])?;
6218 cast::emit_condition_to_i32(
6219 emitter,
6220 ctx,
6221 &ctx.ta
6222 .try_expr(args[0])
6223 .map_err(crate::codegen::arena_failure)?
6224 .ty,
6225 )?;
6226 emit_expr(emitter, ctx, args[1])?;
6227 let message_type = &ctx
6228 .ta
6229 .try_expr(args[1])
6230 .map_err(crate::codegen::arena_failure)?
6231 .ty;
6232 let msg_local = emitter.add_anonymous_local(ctx.symbols.value_type(message_type)?)?;
6233 emitter.instruction(Instruction::LocalSet(msg_local));
6234 emitter.instruction(Instruction::I32Eqz);
6235 emitter.emit_if(BlockType::Empty);
6236 emitter.instruction(Instruction::LocalGet(msg_local));
6237 cast::emit_coerce_to_slot(emitter, ctx, message_type, &Type::String)?;
6238 let ctor_idx = ctx
6239 .symbols
6240 .func_idx(&crate::mangle::prelude("Error#constructor"))
6241 .ok_or_else(|| {
6242 crate::codegen::internal_failure("Error#constructor exported from prelude")
6243 })?;
6244 crate::codegen::throw::emit_no_error_options(emitter);
6245 emitter.instruction(Instruction::Call(ctor_idx));
6246 crate::codegen::throw::emit_error_throw(emitter, ctx);
6247 emitter.emit_end();
6248 emit_undefined(emitter, ctx)?;
6249 }
6250 Intrinsic::JsonStringify => {
6251 super::json::emit_stringify(emitter, ctx, args, result_ty)?;
6252 }
6253 Intrinsic::JsonParse => {
6254 super::json::emit_parse(emitter, ctx, args)?;
6255 }
6256 Intrinsic::ToString => {
6257 let [value] = args else {
6258 return Err(crate::codegen::internal_failure(
6259 "string conversion requires one argument",
6260 ));
6261 };
6262 emit_expr(emitter, ctx, *value)?;
6263 cast::emit_box(
6264 emitter,
6265 ctx,
6266 &ctx.ta
6267 .try_expr(*value)
6268 .map_err(crate::codegen::arena_failure)?
6269 .ty,
6270 )?;
6271 let function = ctx
6272 .symbols
6273 .prelude_func_idx("__value_to_string")
6274 .ok_or_else(|| {
6275 crate::codegen::internal_failure("dynamic string conversion collected")
6276 })?;
6277 emitter.instruction(Instruction::Call(function));
6278 }
6279 Intrinsic::ToObject => {
6280 let [value] = args else {
6281 return Err(crate::codegen::internal_failure(
6282 "object conversion requires one argument",
6283 ));
6284 };
6285 emit_expr(emitter, ctx, *value)?;
6286 let function = ctx
6287 .symbols
6288 .prelude_func_idx("ObjectConstructor##toObject")
6289 .ok_or_else(|| {
6290 crate::codegen::internal_failure("object conversion helper collected")
6291 })?;
6292 emitter.instruction(Instruction::Call(function));
6293 }
6294 Intrinsic::BigIntFromString => {
6301 if args.len() != 1 {
6302 return Err(crate::codegen::internal_failure(
6303 "BigInt.fromString requires one argument",
6304 ));
6305 }
6306 emit_expr(emitter, ctx, args[0])?;
6307 let string_type_idx = ctx
6310 .symbols
6311 .string_type_idx()
6312 .ok_or_else(|| crate::codegen::internal_failure("$string registered"))?;
6313 emitter.instruction(Instruction::StructGet {
6314 struct_type_index: string_type_idx,
6315 field_index: 1,
6316 });
6317 let host_idx = ctx
6318 .symbols
6319 .func_idx(&crate::mangle::host(
6320 crate::runtime::BIGINT_MODULE_NAME,
6321 "fromString",
6322 ))
6323 .ok_or_else(|| {
6324 crate::codegen::internal_failure(
6325 "submilli:bigint.fromString import recorded by codegen bootstrap",
6326 )
6327 })?;
6328 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6329 }
6330 };
6331 Ok(())
6332}
6333
6334fn emit_vtable_equality(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: BinOp) {
6339 ctx.latch(emit_vtable_equality_checked(emitter, ctx, op));
6340}
6341
6342fn emit_vtable_equality_checked(
6343 emitter: &mut FunctionEmitter,
6344 ctx: &CodegenCtx,
6345 op: BinOp,
6346) -> Result<(), crate::compiler_error::CompilerFailure> {
6347 let intrinsics = ctx
6348 .symbols
6349 .intrinsic_type_indices()
6350 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6351 let operand_ref = ValType::Ref(RefType {
6354 nullable: true,
6355 heap_type: HeapType::Concrete(intrinsics.object),
6356 });
6357 let object_ref = ValType::Ref(RefType {
6358 nullable: false,
6359 heap_type: HeapType::Concrete(intrinsics.object),
6360 });
6361 let equals_fn_ref = ValType::Ref(RefType {
6362 nullable: false,
6363 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6364 });
6365
6366 let Some(lhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6368 return Ok(());
6369 };
6370 let Some(rhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6371 return Ok(());
6372 };
6373 let Some(lhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6374 return Ok(());
6375 };
6376 let Some(rhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6377 return Ok(());
6378 };
6379 let Some(eq_fn_local) = ctx.latch(emitter.add_anonymous_local(equals_fn_ref)) else {
6380 return Ok(());
6381 };
6382
6383 emitter.instruction(Instruction::LocalSet(rhs_local));
6384 emitter.instruction(Instruction::LocalSet(lhs_local));
6385
6386 emitter.instruction(Instruction::LocalGet(lhs_local));
6387 emitter.instruction(Instruction::RefAsNonNull);
6388 emitter.instruction(Instruction::LocalSet(lhs_object));
6389 emitter.instruction(Instruction::LocalGet(rhs_local));
6390 emitter.instruction(Instruction::RefAsNonNull);
6391 emitter.instruction(Instruction::LocalSet(rhs_object));
6392
6393 emitter.instruction(Instruction::LocalGet(lhs_object));
6394 emitter.instruction(Instruction::StructGet {
6395 struct_type_index: intrinsics.object,
6396 field_index: 0,
6397 });
6398 emitter.instruction(Instruction::StructGet {
6399 struct_type_index: intrinsics.vtable,
6400 field_index: 2,
6401 });
6402 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6403
6404 emitter.instruction(Instruction::LocalGet(lhs_object));
6405 emitter.instruction(Instruction::LocalGet(rhs_object));
6406 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6407 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6408
6409 if matches!(op, BinOp::NotEq) {
6410 emitter.instruction(Instruction::I32Eqz);
6411 }
6412
6413 Ok(())
6414}
6415
6416fn emit_boxed_eq(
6429 emitter: &mut FunctionEmitter,
6430 ctx: &CodegenCtx,
6431 lhs: ExprId,
6432 rhs: ExprId,
6433 op: BinOp,
6434) -> Result<(), crate::compiler_error::CompilerFailure> {
6435 let intrinsics = ctx
6436 .symbols
6437 .intrinsic_type_indices()
6438 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6439 let object_null_ref = ValType::Ref(RefType {
6440 nullable: true,
6441 heap_type: HeapType::Concrete(intrinsics.object),
6442 });
6443 let object_ref = ValType::Ref(RefType {
6444 nullable: false,
6445 heap_type: HeapType::Concrete(intrinsics.object),
6446 });
6447 let equals_fn_ref = ValType::Ref(RefType {
6448 nullable: false,
6449 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6450 });
6451
6452 let lhs_ty = ctx
6453 .ta
6454 .try_expr(lhs)
6455 .map_err(crate::codegen::arena_failure)?
6456 .ty
6457 .clone();
6458 let rhs_ty = ctx
6459 .ta
6460 .try_expr(rhs)
6461 .map_err(crate::codegen::arena_failure)?
6462 .ty
6463 .clone();
6464
6465 let lhs_local = emitter.add_anonymous_local(object_null_ref)?;
6470 let rhs_local = emitter.add_anonymous_local(object_null_ref)?;
6471 let lhs_nonnull_local = emitter.add_anonymous_local(object_ref)?;
6472 let eq_fn_local = emitter.add_anonymous_local(equals_fn_ref)?;
6473
6474 emit_expr(emitter, ctx, lhs)?;
6475 cast::emit_box(emitter, ctx, &lhs_ty)?;
6476 emitter.instruction(Instruction::LocalSet(lhs_local));
6477
6478 emit_expr(emitter, ctx, rhs)?;
6479 cast::emit_box(emitter, ctx, &rhs_ty)?;
6480 emitter.instruction(Instruction::LocalSet(rhs_local));
6481
6482 emitter.instruction(Instruction::LocalGet(lhs_local));
6486 emitter.instruction(Instruction::RefIsNull);
6487 emitter.emit_if(BlockType::Result(ValType::I32));
6488 emitter.instruction(Instruction::LocalGet(rhs_local));
6489 emitter.instruction(Instruction::RefIsNull);
6490 emitter.emit_else();
6491 emitter.instruction(Instruction::LocalGet(rhs_local));
6492 emitter.instruction(Instruction::RefIsNull);
6493 emitter.emit_if(BlockType::Result(ValType::I32));
6494 emitter.instruction(Instruction::I32Const(0));
6495 emitter.emit_else();
6496 emitter.instruction(Instruction::LocalGet(lhs_local));
6498 emitter.instruction(Instruction::RefAsNonNull);
6499 emitter.instruction(Instruction::LocalTee(lhs_nonnull_local));
6500 emitter.instruction(Instruction::StructGet {
6501 struct_type_index: intrinsics.object,
6502 field_index: 0,
6503 });
6504 emitter.instruction(Instruction::StructGet {
6505 struct_type_index: intrinsics.vtable,
6506 field_index: 2,
6507 });
6508 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6509 emitter.instruction(Instruction::LocalGet(lhs_nonnull_local));
6510 emitter.instruction(Instruction::LocalGet(rhs_local));
6511 emitter.instruction(Instruction::RefAsNonNull);
6512 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6513 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6514 emitter.emit_end();
6515 emitter.emit_end();
6516
6517 let _: () = if matches!(op, BinOp::NotEq) {
6518 emitter.instruction(Instruction::I32Eqz);
6519 };
6520 Ok(())
6521}
6522
6523fn emit_bigint_literal(
6529 emitter: &mut FunctionEmitter,
6530 ctx: &CodegenCtx,
6531 digits: &str,
6532) -> Result<(), crate::compiler_error::CompilerFailure> {
6533 use crate::codegen::internal_failure;
6534 if let Some(entry) = ctx.bigints.lookup(digits)? {
6535 let raw_bigint_idx = ctx
6540 .symbols
6541 .raw_bigint_type_idx()
6542 .ok_or_else(|| internal_failure("the $rawBigInt intrinsic is not registered"))?;
6543 let bigint_idx = ctx
6544 .symbols
6545 .bigint_type_idx()
6546 .ok_or_else(|| internal_failure("the $bigint intrinsic is not registered"))?;
6547 let vtable_global = ctx
6548 .symbols
6549 .prelude_global_idx("bigint_vtable")
6550 .ok_or_else(|| internal_failure("bigint_vtable is not imported from the prelude"))?;
6551 let data_idx = crate::codegen::wasm_u32(ctx.strings.strings.len())?
6552 .checked_add(entry.data_idx)
6553 .ok_or_else(|| internal_failure("the module has too many data segments"))?;
6554 emitter.instruction(Instruction::GlobalGet(vtable_global));
6555 emitter.instruction(Instruction::I32Const(i32::from(entry.sign)));
6556 emitter.instruction(Instruction::I32Const(0));
6557 emitter.instruction(Instruction::I32Const(entry.limb_count.cast_signed()));
6558 emitter.instruction(Instruction::ArrayNewData {
6559 array_type_index: raw_bigint_idx,
6560 array_data_index: data_idx,
6561 });
6562 emitter.instruction(Instruction::StructNew(bigint_idx));
6563 return Ok(());
6564 }
6565 let v: i64 = digits
6569 .parse()
6570 .map_err(|_| internal_failure("an unpooled bigint literal is not a safe integer"))?;
6571 let host_idx = ctx
6572 .symbols
6573 .func_idx(&crate::mangle::host(
6574 crate::runtime::BIGINT_MODULE_NAME,
6575 "fromNumber",
6576 ))
6577 .ok_or_else(|| internal_failure("submilli:bigint.fromNumber is not imported"))?;
6578 emitter.instruction(Instruction::F64Const(Ieee64::from(v as f64)));
6579 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6580 Ok(())
6581}
6582
6583fn emit_regex_literal(
6595 emitter: &mut FunctionEmitter,
6596 ctx: &CodegenCtx,
6597 source: &str,
6598 flags: &str,
6599) -> Result<(), crate::compiler_error::CompilerFailure> {
6600 super::emit_const_string_by_text(emitter, ctx, source)?;
6601 super::emit_const_string_by_text(emitter, ctx, flags)?;
6602 let ctor_idx = ctx
6603 .symbols
6604 .func_idx(&crate::mangle::prelude("RegExpConstructor#new"))
6605 .ok_or_else(|| crate::codegen::internal_failure("RegExpConstructor#new is not imported"))?;
6606 emitter.instruction(Instruction::Call(ctor_idx));
6607 Ok(())
6608}
6609
6610fn emit_bigint_extract_to_stack(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
6616 ctx.latch(emit_bigint_extract_to_stack_checked(emitter, ctx));
6617}
6618
6619fn emit_bigint_extract_to_stack_checked(
6620 emitter: &mut FunctionEmitter,
6621 ctx: &CodegenCtx,
6622) -> Result<(), crate::compiler_error::CompilerFailure> {
6623 let bigint_idx = ctx
6624 .symbols
6625 .bigint_type_idx()
6626 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6627 let Some(scratch) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6630 wasm_encoder::RefType {
6631 nullable: false,
6632 heap_type: wasm_encoder::HeapType::Concrete(bigint_idx),
6633 },
6634 ))) else {
6635 return Ok(());
6636 };
6637 emitter.instruction(Instruction::LocalSet(scratch));
6638 emitter.instruction(Instruction::LocalGet(scratch));
6639 emitter.instruction(Instruction::StructGet {
6640 struct_type_index: bigint_idx,
6641 field_index: 1, });
6643 emitter.instruction(Instruction::LocalGet(scratch));
6644 emitter.instruction(Instruction::StructGet {
6645 struct_type_index: bigint_idx,
6646 field_index: 2, });
6648
6649 Ok(())
6650}
6651
6652fn emit_bigint_wrap_host_result(
6657 emitter: &mut FunctionEmitter,
6658 ctx: &CodegenCtx,
6659 host_func_idx: u32,
6660) {
6661 ctx.latch(emit_bigint_wrap_host_result_checked(
6662 emitter,
6663 ctx,
6664 host_func_idx,
6665 ));
6666}
6667
6668fn emit_bigint_wrap_host_result_checked(
6669 emitter: &mut FunctionEmitter,
6670 ctx: &CodegenCtx,
6671 host_func_idx: u32,
6672) -> Result<(), crate::compiler_error::CompilerFailure> {
6673 emitter.instruction(Instruction::Call(host_func_idx));
6674 let bigint_idx = ctx
6675 .symbols
6676 .bigint_type_idx()
6677 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6678 let raw_bigint_idx = ctx
6679 .symbols
6680 .raw_bigint_type_idx()
6681 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6682 let vtable_global = ctx
6683 .symbols
6684 .prelude_global_idx("bigint_vtable")
6685 .ok_or_else(|| {
6686 crate::codegen::internal_failure("bigint_vtable imported from prelude bootstrap")
6687 })?;
6688 let Some(scratch_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6691 wasm_encoder::RefType {
6692 nullable: false,
6693 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6694 },
6695 ))) else {
6696 return Ok(());
6697 };
6698 let Some(scratch_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32))
6699 else {
6700 return Ok(());
6701 };
6702 emitter.instruction(Instruction::LocalSet(scratch_limbs));
6703 emitter.instruction(Instruction::LocalSet(scratch_sign));
6704 emitter.instruction(Instruction::GlobalGet(vtable_global));
6705 emitter.instruction(Instruction::LocalGet(scratch_sign));
6706 emitter.instruction(Instruction::LocalGet(scratch_limbs));
6707 emitter.instruction(Instruction::StructNew(bigint_idx));
6708
6709 Ok(())
6710}
6711
6712fn emit_bigint_binop_inline(
6716 emitter: &mut FunctionEmitter,
6717 ctx: &CodegenCtx,
6718 lhs: ExprId,
6719 rhs: ExprId,
6720 op_name: &str,
6721) -> Result<(), crate::compiler_error::CompilerFailure> {
6722 emit_expr(emitter, ctx, lhs)?;
6723 emit_bigint_extract_to_stack(emitter, ctx);
6724 let raw_bigint_idx = ctx
6725 .symbols
6726 .raw_bigint_type_idx()
6727 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6728 let lhs_limbs =
6729 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6730 nullable: false,
6731 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6732 }))?;
6733 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6734 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6735 emitter.instruction(Instruction::LocalSet(lhs_sign));
6736 emitter.instruction(Instruction::LocalGet(lhs_sign));
6737 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6738 emit_expr(emitter, ctx, rhs)?;
6739 emit_bigint_extract_to_stack(emitter, ctx);
6740 let host_idx = ctx
6741 .symbols
6742 .func_idx(&crate::mangle::host(
6743 crate::runtime::BIGINT_MODULE_NAME,
6744 op_name,
6745 ))
6746 .ok_or_else(|| {
6747 crate::codegen::internal_failure(format!(
6748 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6749 ))
6750 })?;
6751 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6752 Ok(())
6753}
6754
6755fn emit_bigint_pm_one(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: crate::PostfixOp) {
6762 ctx.latch(emit_bigint_pm_one_checked(emitter, ctx, op));
6763}
6764
6765fn emit_bigint_pm_one_checked(
6766 emitter: &mut FunctionEmitter,
6767 ctx: &CodegenCtx,
6768 op: crate::PostfixOp,
6769) -> Result<(), crate::compiler_error::CompilerFailure> {
6770 emit_bigint_extract_to_stack(emitter, ctx);
6771 let raw_bigint_idx = ctx
6772 .symbols
6773 .raw_bigint_type_idx()
6774 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6775 let Some(lhs_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6776 wasm_encoder::RefType {
6777 nullable: false,
6778 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6779 },
6780 ))) else {
6781 return Ok(());
6782 };
6783 let Some(lhs_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32)) else {
6784 return Ok(());
6785 };
6786 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6787 emitter.instruction(Instruction::LocalSet(lhs_sign));
6788 emitter.instruction(Instruction::LocalGet(lhs_sign));
6789 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6790 if ctx.latch(emit_bigint_literal(emitter, ctx, "1")).is_none() {
6791 return Ok(());
6792 }
6793 emit_bigint_extract_to_stack(emitter, ctx);
6794 let op_name = match op {
6795 crate::PostfixOp::Inc => "add",
6796 crate::PostfixOp::Dec => "sub",
6797 crate::PostfixOp::NonNullAssert => {
6798 return Err(crate::codegen::internal_failure(
6799 "non-null assertion is not PostfixUnary",
6800 ));
6801 }
6802 };
6803 let host_idx = ctx
6804 .symbols
6805 .func_idx(&crate::mangle::host(
6806 crate::runtime::BIGINT_MODULE_NAME,
6807 op_name,
6808 ))
6809 .ok_or_else(|| {
6810 crate::codegen::internal_failure(format!(
6811 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6812 ))
6813 })?;
6814 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6815
6816 Ok(())
6817}
6818
6819fn emit_bigint_cmp_inline(
6822 emitter: &mut FunctionEmitter,
6823 ctx: &CodegenCtx,
6824 lhs: ExprId,
6825 rhs: ExprId,
6826 op: BinOp,
6827) -> Result<(), crate::compiler_error::CompilerFailure> {
6828 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6829 emitter.instruction(Instruction::I32Const(0));
6830 let inst = match op {
6831 BinOp::Lt => Instruction::I32LtS,
6832 BinOp::Gt => Instruction::I32GtS,
6833 BinOp::Le => Instruction::I32LeS,
6834 BinOp::Ge => Instruction::I32GeS,
6835 _ => {
6836 return Err(crate::codegen::internal_failure(
6837 "emit_bigint_cmp_inline called with non-cmp op",
6838 ));
6839 }
6840 };
6841 emitter.instruction(inst);
6842 Ok(())
6843}
6844
6845fn emit_bigint_cmp_eq_inline(
6847 emitter: &mut FunctionEmitter,
6848 ctx: &CodegenCtx,
6849 lhs: ExprId,
6850 rhs: ExprId,
6851 op: BinOp,
6852) -> Result<(), crate::compiler_error::CompilerFailure> {
6853 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6854 emitter.instruction(Instruction::I32Eqz);
6855 let _: () = if matches!(op, BinOp::NotEq) {
6856 emitter.instruction(Instruction::I32Eqz);
6857 };
6858 Ok(())
6859}
6860
6861fn emit_bigint_cmp_call(
6864 emitter: &mut FunctionEmitter,
6865 ctx: &CodegenCtx,
6866 lhs: ExprId,
6867 rhs: ExprId,
6868) -> Result<(), crate::compiler_error::CompilerFailure> {
6869 emit_expr(emitter, ctx, lhs)?;
6870 emit_bigint_extract_to_stack(emitter, ctx);
6871 let raw_bigint_idx = ctx
6872 .symbols
6873 .raw_bigint_type_idx()
6874 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6875 let lhs_limbs =
6876 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6877 nullable: false,
6878 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6879 }))?;
6880 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6881 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6882 emitter.instruction(Instruction::LocalSet(lhs_sign));
6883 emitter.instruction(Instruction::LocalGet(lhs_sign));
6884 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6885 emit_expr(emitter, ctx, rhs)?;
6886 emit_bigint_extract_to_stack(emitter, ctx);
6887 let cmp_idx = ctx
6888 .symbols
6889 .func_idx(&crate::mangle::host(
6890 crate::runtime::BIGINT_MODULE_NAME,
6891 "cmp",
6892 ))
6893 .ok_or_else(|| {
6894 crate::codegen::internal_failure(
6895 "submilli:bigint.cmp import recorded by codegen bootstrap",
6896 )
6897 })?;
6898 emitter.instruction(Instruction::Call(cmp_idx));
6899 Ok(())
6900}
6901
6902fn evaluate_object_members(
6904 emitter: &mut FunctionEmitter<'_>,
6905 ctx: &CodegenCtx<'_>,
6906 members: &[TypedObjectMember],
6907) -> Result<HashMap<ExprId, u32>, crate::compiler_error::CompilerFailure> {
6908 let intrinsics = ctx
6909 .symbols
6910 .intrinsic_type_indices()
6911 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
6912 let mut values = HashMap::new();
6913 for member in members {
6914 let TypedObjectMember::Value(value_id) = *member else {
6915 return Err(crate::codegen::internal_failure(
6916 "spread field reached plain object lowering",
6917 ));
6918 };
6919 let value_ty = &ctx
6920 .ta
6921 .try_expr(value_id)
6922 .map_err(crate::codegen::arena_failure)?
6923 .ty;
6924 emit_expr(emitter, ctx, value_id)?;
6925 cast::emit_box(emitter, ctx, value_ty)?;
6926 let local = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
6927 emitter.instruction(Instruction::LocalSet(local));
6928 values.insert(value_id, local);
6929 }
6930 Ok(values)
6931}
6932
6933fn emit_field_holds_value_of(
6936 emitter: &mut FunctionEmitter<'_>,
6937 ctx: &CodegenCtx<'_>,
6938 object: u32,
6939 index: u32,
6940 value: u32,
6941 field_ty: &Type,
6942) -> Result<(), crate::compiler_error::CompilerFailure> {
6943 let intrinsics = ctx
6944 .symbols
6945 .intrinsic_type_indices()
6946 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6947 emitter.instruction(Instruction::LocalGet(object));
6948 emitter.instruction(Instruction::StructGet {
6949 struct_type_index: intrinsics.object_shape,
6950 field_index: 1,
6951 });
6952 emitter.instruction(Instruction::LocalGet(index));
6953 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
6954 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
6955 emitter.instruction(Instruction::LocalGet(value));
6956 emit_is_nullish(emitter, ctx)?;
6957 emitter.instruction(Instruction::I32Eqz);
6958 emitter.instruction(Instruction::I32Or);
6959 let _: () = if field_runtime_type_is_testable(field_ty) {
6960 emit_structural_test(emitter, ctx, value, field_ty, field_ty)?;
6961 emitter.instruction(Instruction::I32And);
6962 };
6963 Ok(())
6964}
6965
6966fn object_ref(object_type: u32) -> ValType {
6967 ValType::Ref(RefType {
6968 nullable: true,
6969 heap_type: HeapType::Concrete(object_type),
6970 })
6971}
6972
6973pub(super) fn emit_index_number(
6975 emitter: &mut FunctionEmitter,
6976 ctx: &CodegenCtx,
6977 ty: &Type,
6978) -> Result<(), crate::compiler_error::CompilerFailure> {
6979 if ty == &Type::Unknown {
6980 let function = ctx
6981 .symbols
6982 .prelude_func_idx("__value_to_index")
6983 .ok_or_else(|| crate::codegen::internal_failure("dynamic index helper collected"))?;
6984 emitter.instruction(Instruction::Call(function));
6985 } else {
6986 cast::emit_coerce_to_slot(emitter, ctx, ty, &Type::Number)?;
6987 };
6988 Ok(())
6989}
6990
6991fn emit_computed_object(
6992 emitter: &mut FunctionEmitter,
6993 ctx: &CodegenCtx,
6994 members: &[TypedObjectMember],
6995) -> Result<(), crate::compiler_error::CompilerFailure> {
6996 let Some(intr) = ctx.require(ctx.symbols.intrinsic_type_indices(), "intrinsics declared")
6997 else {
6998 return Ok(());
6999 };
7000 let Some(merge) = ctx.require(
7001 ctx.symbols.prelude_func_idx("ObjectConstructor##spread"),
7002 "spread helper imported",
7003 ) else {
7004 return Ok(());
7005 };
7006 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
7007 nullable: false,
7008 heap_type: HeapType::Concrete(intr.object_shape),
7009 }))?;
7010 for _ in 0..4 {
7011 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
7012 }
7013 emitter.instruction(Instruction::Call(merge));
7014 emitter.instruction(Instruction::LocalSet(object));
7015 for member in members {
7016 emitter.instruction(Instruction::LocalGet(object));
7017 match member {
7018 TypedObjectMember::Computed { key, value } => {
7019 emit_expr(emitter, ctx, *key)?;
7020 crate::codegen::cast_check::emit_operation_cast_on_stack(
7021 emitter,
7022 ctx,
7023 &ctx.ta
7024 .try_expr(*key)
7025 .map_err(crate::codegen::arena_failure)?
7026 .ty,
7027 &Type::String,
7028 )?;
7029 emit_expr(emitter, ctx, *value)?;
7030 cast::emit_box(
7031 emitter,
7032 ctx,
7033 &ctx.ta
7034 .try_expr(*value)
7035 .map_err(crate::codegen::arena_failure)?
7036 .ty,
7037 )?;
7038 let Some(symbol) = ctx.require(
7039 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7040 "dynamic write imported",
7041 ) else {
7042 return Ok(());
7043 };
7044 emitter.instruction(Instruction::Call(symbol));
7045 }
7046 TypedObjectMember::Spread { source, .. } => {
7047 emit_expr(emitter, ctx, *source)?;
7048 for _ in 0..2 {
7049 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
7050 }
7051 emitter.instruction(Instruction::Call(merge));
7052 emitter.instruction(Instruction::LocalSet(object));
7053 }
7054 TypedObjectMember::Value(_) => {
7055 ctx.fail("computed literal contains an unlowered named field");
7056 return Ok(());
7057 }
7058 }
7059 }
7060 emitter.instruction(Instruction::LocalGet(object));
7061 Ok(())
7062}
7063
7064fn emit_object_index_postfix(
7065 emitter: &mut FunctionEmitter,
7066 ctx: &CodegenCtx,
7067 receiver: ExprId,
7068 index: ExprId,
7069 op: crate::PostfixOp,
7070 read_ty: &Type,
7071) -> Result<(), crate::compiler_error::CompilerFailure> {
7072 emit_expr(emitter, ctx, receiver)?;
7073 cast::emit_box(
7074 emitter,
7075 ctx,
7076 &ctx.ta
7077 .try_expr(receiver)
7078 .map_err(crate::codegen::arena_failure)?
7079 .ty,
7080 )?;
7081 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7082 emitter.instruction(Instruction::LocalSet(object));
7083 emit_expr(emitter, ctx, index)?;
7084 crate::codegen::cast_check::emit_operation_cast_on_stack(
7085 emitter,
7086 ctx,
7087 &ctx.ta
7088 .try_expr(index)
7089 .map_err(crate::codegen::arena_failure)?
7090 .ty,
7091 &Type::String,
7092 )?;
7093 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
7094 emitter.instruction(Instruction::LocalSet(key));
7095 emitter.instruction(Instruction::LocalGet(object));
7096 emitter.instruction(Instruction::LocalGet(key));
7097 let Some(symbol) = ctx.require(
7098 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
7099 "record read imported",
7100 ) else {
7101 return Ok(());
7102 };
7103 emitter.instruction(Instruction::Call(symbol));
7104 crate::codegen::cast_check::emit_operation_cast_on_stack(
7105 emitter,
7106 ctx,
7107 &Type::Unknown,
7108 read_ty,
7109 )?;
7110 cast::emit_box(emitter, ctx, read_ty)?;
7111 emit_postfix_numeric(emitter, ctx, &Type::Unknown);
7112 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7113 emitter.instruction(Instruction::LocalTee(old));
7114 emit_postfix_delta(emitter, ctx, op, &Type::Unknown);
7115 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7116 emitter.instruction(Instruction::LocalSet(updated));
7117 emitter.instruction(Instruction::LocalGet(object));
7118 emitter.instruction(Instruction::LocalGet(key));
7119 emitter.instruction(Instruction::LocalGet(updated));
7120 let Some(symbol) = ctx.require(
7121 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7122 "record write imported",
7123 ) else {
7124 return Ok(());
7125 };
7126 emitter.instruction(Instruction::Call(symbol));
7127 emitter.instruction(Instruction::LocalGet(old));
7128 Ok(())
7129}
7130
7131#[cfg(test)]
7132mod tests {
7133 use super::*;
7134 use crate::codegen::invariant_tests::{assert_internal, with_context};
7135 use crate::{FileId, Span, TypedAst, TypedExpr};
7136
7137 #[test]
7138 fn missing_method_dispatch_is_a_compiler_error() {
7139 with_context(
7140 &TypedAst::new(),
7141 &crate::codegen::SymbolTable::default(),
7142 |ctx| {
7143 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7144 let error = emit_method_call_with_receiver_on_stack(
7145 &mut emitter,
7146 ctx,
7147 &Type::Number,
7148 &crate::mangle::prelude("MissingInterface"),
7149 "missingMethod",
7150 &[],
7151 None,
7152 &[],
7153 None,
7154 &Type::Number,
7155 )
7156 .unwrap_err();
7157 assert!(error.to_string().contains("MissingInterface.missingMethod"));
7158 assert_internal(error);
7159 },
7160 );
7161 }
7162
7163 #[test]
7164 fn call_and_chain_metadata_are_checked_before_operands() {
7165 with_context(
7166 &TypedAst::new(),
7167 &crate::codegen::SymbolTable::default(),
7168 |ctx| {
7169 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7170 assert_internal(
7171 emit_intrinsic_call(&mut emitter, ctx, Intrinsic::Assert, &[], &Type::Void)
7172 .unwrap_err(),
7173 );
7174 assert_internal(
7175 emit_intrinsic_call(
7176 &mut emitter,
7177 ctx,
7178 Intrinsic::BigIntFromString,
7179 &[],
7180 &Type::BigInt,
7181 )
7182 .unwrap_err(),
7183 );
7184 assert_internal(
7185 emit_direct_call(
7186 &mut emitter,
7187 ctx,
7188 false,
7189 0,
7190 &[Type::Number],
7191 &Type::Void,
7192 &[],
7193 )
7194 .unwrap_err(),
7195 );
7196 assert_internal(
7197 emit_args_into_slots(&mut emitter, ctx, &[], Some(&[ValType::F64]))
7198 .unwrap_err(),
7199 );
7200 assert_internal(
7201 emit_chain_parts(
7202 &mut emitter,
7203 ctx,
7204 &[],
7205 1,
7206 &Type::Number,
7207 &Type::Number,
7208 None,
7209 )
7210 .unwrap_err(),
7211 );
7212 assert_internal(
7213 emit_chain_parts(
7214 &mut emitter,
7215 ctx,
7216 &[],
7217 0,
7218 &Type::Number,
7219 &Type::Number,
7220 Some(&[]),
7221 )
7222 .unwrap_err(),
7223 );
7224 },
7225 );
7226 }
7227
7228 #[test]
7229 fn uninterned_literal_fails_and_a_fresh_emitter_still_works() {
7230 let mut ta = TypedAst::new();
7231 let string = ta
7232 .try_push_expr(TypedExpr {
7233 kind: TypedExprKind::String("missing".into()),
7234 ty: Type::String,
7235 span: Span::at(FileId(0)),
7236 })
7237 .unwrap();
7238 let number = ta
7239 .try_push_expr(TypedExpr {
7240 kind: TypedExprKind::Number(3.0),
7241 ty: Type::Number,
7242 span: Span::at(FileId(0)),
7243 })
7244 .unwrap();
7245 with_context(&ta, &crate::codegen::SymbolTable::default(), |ctx| {
7246 let mut failed = FunctionEmitter::new(ctx, &[]).unwrap();
7247 assert_internal(emit_expr(&mut failed, ctx, string).unwrap_err());
7248 drop(failed);
7249 let mut healthy = FunctionEmitter::new(ctx, &[]).unwrap();
7250 emit_expr(&mut healthy, ctx, number).unwrap();
7251 healthy.instruction(Instruction::Drop);
7252 healthy.build().unwrap();
7253 });
7254 }
7255}