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, ctx, *receiver, iface, &name.name, args, None, None, &expr.ty,
271 )?;
272 }
273 TypedExprKind::GenericMethodCall {
274 receiver,
275 iface,
276 name,
277 args,
278 return_cast,
279 type_predicate: _,
280 } => {
281 let plain_args: Vec<ExprId> = args.iter().map(|a| a.expr).collect();
282 emit_method_call(
283 emitter,
284 ctx,
285 *receiver,
286 iface,
287 &name.name,
288 &plain_args,
289 Some(args),
290 return_cast.as_ref(),
291 &expr.ty,
292 )?;
293 }
294 TypedExprKind::ObjectLiteral { members, fields } => {
295 emit_object_literal(emitter, ctx, &expr.ty, members, fields)?;
296 }
297 TypedExprKind::FieldAccess { receiver, name } => {
298 emit_field_access(emitter, ctx, id, &expr.ty, receiver, name)?;
299 }
300 TypedExprKind::InterfacePropertyAccess {
301 receiver,
302 iface,
303 name,
304 } => emit_interface_property(emitter, ctx, receiver, iface, name)?,
305 TypedExprKind::ArrayLiteral { elements, .. } => {
306 emit_array_literal(emitter, ctx, elements)?;
307 }
308 TypedExprKind::TupleLiteral { elements, .. } => {
309 emit_tuple_literal(emitter, ctx, elements)?;
310 }
311 TypedExprKind::IndexAccess { receiver, index } => {
312 emit_index_access(emitter, ctx, id, receiver, index, &expr.ty)?;
313 }
314 TypedExprKind::Undefined => emit_undefined(emitter, ctx)?,
315 TypedExprKind::Null => {
316 let _ = ctx; emitter.instruction(Instruction::RefNull(HeapType::Abstract {
328 shared: false,
329 ty: wasm_encoder::AbstractHeapType::None,
330 }));
331 }
332 TypedExprKind::This => {
333 let slot = emitter.this_local().ok_or_else(|| {
334 crate::codegen::internal_failure("`this` has a local receiver or captured binding")
335 })?;
336 if emitter.dynamic_this {
337 emitter.instruction(Instruction::LocalGet(slot));
338 emitter.instruction(Instruction::RefIsNull);
339 emitter.emit_if(BlockType::Empty);
340 crate::codegen::throw::emit_type_error_throw(
341 emitter,
342 ctx,
343 "Unbound function has no this receiver",
344 );
345 emitter.emit_end();
346 }
347 emitter.instruction(Instruction::LocalGet(slot));
348 if emitter.dynamic_this {
349 crate::codegen::cast_check::emit_operation_cast_on_stack(
350 emitter,
351 ctx,
352 &Type::Unknown,
353 &expr.ty,
354 )?;
355 }
356 }
357 TypedExprKind::NumberEnumMember { value, .. } => {
358 let boxed_idx = ctx
365 .symbols
366 .boxed_number_type_idx()
367 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
368 let vtable_global = ctx
369 .symbols
370 .prelude_global_idx("boxed_number_vtable")
371 .ok_or_else(|| {
372 crate::codegen::internal_failure("boxed_number_vtable imported from prelude")
373 })?;
374 emitter.instruction(Instruction::GlobalGet(vtable_global));
375 emitter.instruction(Instruction::F64Const(Ieee64::from(*value)));
376 emitter.instruction(Instruction::StructNew(boxed_idx));
377 }
378 TypedExprKind::StringEnumMember { .. } => {
379 let pool_idx = ctx.strings.locations.get(&id).copied().ok_or_else(|| {
384 crate::codegen::internal_failure("a string enum value was not interned")
385 })?;
386 super::emit_pooled_string(emitter, ctx, pool_idx)?;
387 }
388 TypedExprKind::TypeofTag { value, tag } => {
389 emit_typeof_tag(emitter, ctx, *value, *tag)?;
390 }
391 TypedExprKind::InstanceOf { value, class } => match class.lowered() {
392 Type::ClassRef { mangled, .. } => {
393 let vtable_global =
394 ctx.symbols
395 .class_vtable_global_idx(mangled)
396 .ok_or_else(|| {
397 crate::codegen::internal_failure("class vtable global recorded")
398 })?;
399 emit_expr(emitter, ctx, *value)?;
400 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
401 cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
402 }
403 Type::Uint8Array => {
407 let idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
408 crate::codegen::internal_failure("$Uint8Array intrinsic registered")
409 })?;
410 emit_expr(emitter, ctx, *value)?;
411 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
412 }
413 _ => {
414 return Err(crate::codegen::internal_failure(format!(
415 "instanceof codegen with a non-class RHS: {class:?}"
416 )));
417 }
418 },
419 TypedExprKind::Narrowed {
420 path,
421 source,
422 binding,
423 cast_info,
424 inner,
425 } => {
426 emitter.push_scope();
430 if path.chain.is_empty() {
431 let shadow_val = ctx.symbols.value_type(&cast_info.to_ty)?;
432 let shadow = emitter.define_local(binding, shadow_val)?;
433 emit_expr(emitter, ctx, *source)?;
434 crate::codegen::function_emitter::cast::emit_narrowing_cast(
435 emitter, ctx, cast_info,
436 )?;
437 emitter.instruction(Instruction::LocalSet(shadow));
438 } else {
439 emitter.register_narrow_source(&binding.name, *source)?;
440 }
441 emit_expr(emitter, ctx, *inner)?;
442 emitter.pop_scope()?;
443 }
444 TypedExprKind::Closure {
445 params,
446 captured,
447 runtime_generics,
448 ..
449 } => {
450 emit_closure_value(
451 emitter,
452 ctx,
453 id,
454 &expr.ty,
455 params,
456 captured,
457 runtime_generics,
458 )?;
459 }
460 TypedExprKind::Ternary { cond, then_, else_ } => {
465 let result_ty = expr.ty.clone();
466 let block_ty = conditional_block_type(ctx, &result_ty)?;
467 let cond_ty = ctx
468 .ta
469 .try_expr(*cond)
470 .map_err(crate::codegen::arena_failure)?
471 .ty
472 .clone();
473 emit_expr(emitter, ctx, *cond)?;
474 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
475 emitter.emit_if(block_ty);
476 emit_conditional_operand(emitter, ctx, *then_, &result_ty)?;
477 emitter.emit_else();
478 emit_conditional_operand(emitter, ctx, *else_, &result_ty)?;
479 emitter.emit_end();
480 }
481 TypedExprKind::NullishCoalesce { lhs, rhs } => {
484 emit_nullish_coalesce(emitter, ctx, lhs, rhs, &expr.ty)?;
485 }
486 TypedExprKind::OptionalChain { base, parts } => {
494 let result_ty = expr.ty.clone();
495 let base_ty = ctx
496 .ta
497 .try_expr(*base)
498 .map_err(crate::codegen::arena_failure)?
499 .ty
500 .clone();
501 let receiver = if matches!(parts.first(), Some(crate::TypedChainPart::Call { .. })) {
502 emit_callee(emitter, ctx, *base)?
503 } else {
504 emit_expr(emitter, ctx, *base)?;
505 None
506 };
507 emitter.call_receiver = receiver;
508 emit_chain_parts(
509 emitter,
510 ctx,
511 parts,
512 0,
513 &base_ty,
514 &result_ty,
515 ctx.ta.runtime_chain_types.get(&id).map(Vec::as_slice),
516 )?;
517 }
518 TypedExprKind::PostfixUnary { op, target } => {
519 if let crate::PostfixTarget::Index {
520 receiver, index, ..
521 } = target
522 && ctx
523 .ta
524 .source_type(*receiver)
525 .map_err(crate::codegen::arena_failure)?
526 .is_structural_object()
527 {
528 emit_object_index_postfix(
529 emitter,
530 ctx,
531 *receiver,
532 *index,
533 *op,
534 ctx.ta
535 .source_type(id)
536 .map_err(crate::codegen::arena_failure)?,
537 )?;
538 cast::emit_coerce_to_slot(emitter, ctx, &Type::Unknown, &expr.ty)?;
539 } else {
540 emit_postfix_unary(emitter, ctx, *op, target, &expr.ty)?;
541 }
542 }
543 TypedExprKind::NonNullAssert { value } => {
544 crate::codegen::cast_check::emit_non_null_assert(emitter, ctx, *value, &expr.ty)?;
545 }
546 TypedExprKind::Cast {
547 value,
548 target_ty,
549 check,
550 } => {
551 crate::codegen::cast_check::emit_cast(
552 emitter,
553 ctx,
554 *value,
555 target_ty,
556 check.as_deref(),
557 expr.span,
558 )?;
559 }
560 };
561 if matches!(expr.ty, Type::Never) && !completes_when_never(&expr.kind) {
566 emitter.instruction(Instruction::Unreachable);
567 }
568 Ok(())
569}
570
571fn completes_when_never(kind: &TypedExprKind) -> bool {
577 matches!(
578 kind,
579 TypedExprKind::LocalNarrowRef { .. }
580 | TypedExprKind::FieldAccess { .. }
581 | TypedExprKind::IndexAccess { .. }
582 )
583}
584
585pub(crate) fn emit_undefined(
587 emitter: &mut FunctionEmitter<'_>,
588 ctx: &CodegenCtx<'_>,
589) -> Result<(), crate::compiler_error::CompilerFailure> {
590 let global = ctx
591 .symbols
592 .prelude_global_idx(crate::runtime::prelude::undefined::GLOBAL_NAME)
593 .ok_or_else(|| crate::codegen::internal_failure("undefined global was not imported"))?;
594 emitter.instruction(Instruction::GlobalGet(global));
595 Ok(())
596}
597
598pub(crate) fn emit_is_nullish(
600 emitter: &mut FunctionEmitter<'_>,
601 ctx: &CodegenCtx<'_>,
602) -> Result<(), crate::compiler_error::CompilerFailure> {
603 let intrinsic = ctx
604 .symbols
605 .intrinsic_type_indices()
606 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
607 let value = emitter.add_anonymous_local(ValType::Ref(RefType {
608 nullable: true,
609 heap_type: HeapType::Concrete(intrinsic.object),
610 }))?;
611 emitter.instruction(Instruction::LocalSet(value));
612 emitter.instructions(crate::codegen::nullish::is_nullish(
613 &[Instruction::LocalGet(value)],
614 intrinsic,
615 ));
616 Ok(())
617}
618
619pub(crate) fn emit_is_undefined(
620 emitter: &mut FunctionEmitter<'_>,
621 ctx: &CodegenCtx<'_>,
622) -> Result<(), crate::compiler_error::CompilerFailure> {
623 let intrinsic = ctx
624 .symbols
625 .intrinsic_type_indices()
626 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
627 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
628 intrinsic.undefined,
629 )));
630 Ok(())
631}
632
633fn emit_global_ref(
634 emitter: &mut FunctionEmitter,
635 ctx: &CodegenCtx,
636 mangled: &crate::MangledName,
637 result_ty: &Type,
638) -> Result<(), crate::compiler_error::CompilerFailure> {
639 if let Some(iface) = static_interface(ctx, result_ty) {
648 emit_static_value(emitter, ctx, iface)?;
649 if let ValType::Ref(RefType {
650 nullable: false, ..
651 }) = ctx.symbols.value_type(result_ty)?
652 {
653 emitter.instruction(Instruction::RefAsNonNull);
654 }
655 return Ok(());
656 }
657 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
658 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
659 crate::codegen::internal_failure("top-level let/const recorded during codegen")
660 })?;
661 emitter.instruction(Instruction::GlobalGet(idx));
662 if let ValType::Ref(RefType {
663 nullable: false, ..
664 }) = ctx.symbols.value_type(result_ty)?
665 {
666 emitter.instruction(Instruction::RefAsNonNull);
667 }
668
669 Ok(())
670}
671
672fn static_interface<'a>(ctx: &CodegenCtx, ty: &'a Type) -> Option<&'a crate::MangledName> {
674 match ty.lowered() {
675 Type::InterfaceRef { mangled, .. }
676 if ctx.symbols.iface_dispatch(mangled) == Some(crate::Dispatch::Static) =>
677 {
678 Some(mangled)
679 }
680 _ => None,
681 }
682}
683
684fn emit_static_value(
685 emitter: &mut FunctionEmitter,
686 ctx: &CodegenCtx,
687 iface: &crate::MangledName,
688) -> Result<(), crate::compiler_error::CompilerFailure> {
689 let tag = ctx.symbols.static_value_tag(iface).ok_or_else(|| {
690 crate::codegen::internal_failure("a static binding read as a value has a recorded tag")
691 })?;
692 super::emit_inline_string_literal(emitter, ctx, tag)?;
693 let helper = ctx
694 .symbols
695 .prelude_func_idx("ObjectConstructor##staticValue")
696 .ok_or_else(|| crate::codegen::internal_failure("the static value helper is imported"))?;
697 emitter.instruction(Instruction::Call(helper));
698 Ok(())
699}
700
701fn emit_function_ref(
702 emitter: &mut FunctionEmitter,
703 ctx: &CodegenCtx,
704 mangled: &crate::MangledName,
705 result_ty: &Type,
706) -> Result<(), crate::compiler_error::CompilerFailure> {
707 let closure_struct_idx = ctx
718 .symbols
719 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
720 .ok_or_else(|| {
721 crate::codegen::internal_failure(
722 "closure struct type registered for every function-as-value",
723 )
724 })?;
725 let adapter_idx = ctx.symbols.adapter_func_idx(mangled).ok_or_else(|| {
726 crate::codegen::internal_failure("adapter func recorded for every function-as-value")
727 })?;
728 let closure_global_idx = ctx
729 .symbols
730 .adapter_closure_global_idx(mangled)
731 .ok_or_else(|| {
732 crate::codegen::internal_failure("closure global recorded for every function-as-value")
733 })?;
734 let vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
735 crate::codegen::internal_failure(
736 "closure vtable global emitted whenever closures or adapters exist",
737 )
738 })?;
739 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
740 emitter.instruction(Instruction::RefIsNull);
741 emitter.emit_if(BlockType::Empty);
742 emitter.instruction(Instruction::GlobalGet(vtable_idx));
743 emitter.instruction(Instruction::RefFunc(adapter_idx));
744 emitter.instruction(Instruction::GlobalGet(vtable_idx));
745 if let Some(metadata) = ctx.symbols.function_argument_metadata.get(mangled) {
746 crate::codegen::call_arguments::wrap(emitter, ctx, metadata)?;
747 }
748 emitter.instruction(Instruction::I64Const(0));
749 emitter.instruction(Instruction::StructNew(closure_struct_idx));
750 emitter.instruction(Instruction::GlobalSet(closure_global_idx));
751 emitter.emit_end();
752 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
753 if ctx.symbols.is_shared_closure_global(mangled) {
754 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
755 closure_struct_idx,
756 )));
757 } else {
758 emitter.instruction(Instruction::RefAsNonNull);
759 }
760
761 Ok(())
762}
763
764fn emit_symbol_call(
765 emitter: &mut FunctionEmitter,
766 ctx: &CodegenCtx,
767 mangled: &crate::MangledName,
768 args: &[ExprId],
769 result_ty: &Type,
770) -> Result<(), crate::compiler_error::CompilerFailure> {
771 let target = ctx.symbols.top_level_fn(mangled).ok_or_else(|| {
784 crate::codegen::internal_failure(format!(
785 "Call references unknown top-level fn `{}`",
786 mangled.as_str()
787 ))
788 })?;
789 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
790 let Type::ClassRef { mangled: class, .. } = result_ty.lowered() else {
791 return Err(crate::codegen::internal_failure("constructor class"));
792 };
793 emit_args_into_slots(emitter, ctx, args, ctx.symbols.class_ctor_abi(class))?;
794 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
795 emitter.instruction(Instruction::Call(target.wasm_idx));
796 } else {
797 emit_direct_call(
798 emitter,
799 ctx,
800 target.is_host,
801 target.wasm_idx,
802 &target.params,
803 &target.ret,
804 args,
805 )?;
806 }
807 if result_ty.is_void() {
808 cast::emit_box(emitter, ctx, &target.ret)?;
809 }
810
811 Ok(())
812}
813
814fn emit_interface_property(
815 emitter: &mut FunctionEmitter,
816 ctx: &CodegenCtx,
817 receiver: &ExprId,
818 iface: &crate::MangledName,
819 name: &Ident,
820) -> Result<(), crate::compiler_error::CompilerFailure> {
821 let key = crate::mangle::extend(iface, &name.name);
827 if ctx.symbols.iface_dispatch(iface) == Some(crate::Dispatch::Static) {
830 let global_idx = ctx.symbols.global_idx(&key).ok_or_else(|| {
831 crate::codegen::internal_failure(
832 "static interface property recorded as a global import",
833 )
834 })?;
835 emitter.instruction(Instruction::GlobalGet(global_idx));
836 return Ok(());
837 }
838 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
839 emit_receiver(emitter, ctx, *receiver)?;
840 emit_inline_length(emitter, ctx, struct_idx);
841 return Ok(());
842 }
843 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
844 crate::codegen::internal_failure(
845 "interface property getter recorded during the dependency-import pass",
846 )
847 })?;
848 emit_receiver(emitter, ctx, *receiver)?;
849 if matches!(
862 ctx.ta
863 .source_type(*receiver)
864 .map_err(crate::codegen::arena_failure)?
865 .lowered(),
866 Type::InterfaceRef { .. }
867 ) {
868 emitter.instruction(Instruction::RefAsNonNull);
869 }
870 emitter.instruction(Instruction::Call(func_idx));
871
872 Ok(())
873}
874
875fn emit_index_access(
876 emitter: &mut FunctionEmitter,
877 ctx: &CodegenCtx,
878 id: ExprId,
879 receiver: &ExprId,
880 index: &ExprId,
881 result_ty: &Type,
882) -> Result<(), crate::compiler_error::CompilerFailure> {
883 let recv_ty = ctx
888 .ta
889 .source_type(*receiver)
890 .map_err(crate::codegen::arena_failure)?
891 .clone();
892 if recv_ty.is_structural_object() {
893 emit_expr(emitter, ctx, *receiver)?;
894 cast::emit_box(
895 emitter,
896 ctx,
897 &ctx.ta
898 .try_expr(*receiver)
899 .map_err(crate::codegen::arena_failure)?
900 .ty,
901 )?;
902 emit_expr(emitter, ctx, *index)?;
903 crate::codegen::cast_check::emit_operation_cast_on_stack(
904 emitter,
905 ctx,
906 &ctx.ta
907 .try_expr(*index)
908 .map_err(crate::codegen::arena_failure)?
909 .ty,
910 &Type::String,
911 )?;
912 let Some(symbol) = ctx.require(
913 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
914 "dynamic read imported",
915 ) else {
916 return Ok(());
917 };
918 emitter.instruction(Instruction::Call(symbol));
919 let checked_ty = ctx
920 .ta
921 .source_type(id)
922 .map_err(crate::codegen::arena_failure)?;
923 crate::codegen::cast_check::emit_checked_cast_on_stack(
924 emitter,
925 ctx,
926 &Type::Unknown,
927 checked_ty,
928 )?;
929 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
930 } else if recv_ty.lowered() == &Type::Uint8Array {
931 emit_expr(emitter, ctx, *receiver)?;
932 emit_uint8_index_with_receiver_on_stack(emitter, ctx, *index)?;
933 } else if recv_ty.is_string_shaped() {
934 emit_expr(emitter, ctx, *receiver)?;
935 emit_string_index_with_receiver_on_stack(emitter, ctx, *index)?;
936 } else {
937 emit_expr(emitter, ctx, *receiver)?;
938 emit_bounds_checked_index_with_receiver_on_stack(
939 emitter,
940 ctx,
941 *index,
942 result_ty,
943 is_optional_tuple_position(ctx, &recv_ty, *index)?,
944 )?;
945 }
946
947 Ok(())
948}
949
950fn emit_string_index_with_receiver_on_stack(
953 emitter: &mut FunctionEmitter,
954 ctx: &CodegenCtx,
955 index: ExprId,
956) -> Result<(), crate::compiler_error::CompilerFailure> {
957 let Some(helper) = ctx.require(
958 ctx.symbols.prelude_func_idx("string_index"),
959 "string index helper imported",
960 ) else {
961 return Ok(());
962 };
963 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
966 emitter.instruction(Instruction::LocalSet(receiver));
967 emit_expr(emitter, ctx, index)?;
968 emit_index_number(
969 emitter,
970 ctx,
971 &ctx.ta
972 .try_expr(index)
973 .map_err(crate::codegen::arena_failure)?
974 .ty,
975 )?;
976 let index_local = emitter.add_anonymous_local(ValType::F64)?;
977 emitter.instruction(Instruction::LocalSet(index_local));
978 emitter.instruction(Instruction::LocalGet(receiver));
979 crate::codegen::cast_check::emit_operation_cast_on_stack(
980 emitter,
981 ctx,
982 &Type::Unknown,
983 &Type::String,
984 )?;
985 emitter.instruction(Instruction::LocalGet(index_local));
986 emitter.instruction(Instruction::Call(helper));
987 Ok(())
988}
989
990fn emit_nullish_coalesce(
991 emitter: &mut FunctionEmitter,
992 ctx: &CodegenCtx,
993 lhs: &ExprId,
994 rhs: &ExprId,
995 result_ty: &Type,
996) -> Result<(), crate::compiler_error::CompilerFailure> {
997 let result_ty = result_ty.clone();
998
999 let result_val = ctx.symbols.value_type(&result_ty)?;
1000 let lhs_ty = ctx
1001 .ta
1002 .try_expr(*lhs)
1003 .map_err(crate::codegen::arena_failure)?
1004 .ty
1005 .clone();
1006 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
1007 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
1008 if lhs_is_ref {
1009 let tmp = emitter.add_anonymous_local(lhs_val)?;
1010 emit_expr(emitter, ctx, *lhs)?;
1011 emitter.instruction(Instruction::LocalTee(tmp));
1012 emit_is_nullish(emitter, ctx)?;
1013 emitter.emit_if(BlockType::Result(result_val));
1014 emit_conditional_operand(emitter, ctx, *rhs, &result_ty)?;
1015 emitter.emit_else();
1016 emitter.instruction(Instruction::LocalGet(tmp));
1017 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, &result_ty)?;
1027 emitter.emit_end();
1028 } else {
1029 emit_expr(emitter, ctx, *lhs)?;
1031 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
1032 emitter, ctx, &lhs_ty, &result_ty,
1033 )?;
1034 }
1035
1036 Ok(())
1037}
1038
1039fn conditional_block_type(
1042 ctx: &CodegenCtx,
1043 result_ty: &Type,
1044) -> Result<BlockType, crate::compiler_error::CompilerFailure> {
1045 Ok(BlockType::Result(ctx.symbols.value_type(result_ty)?))
1046}
1047
1048fn emit_conditional_operand(
1052 emitter: &mut FunctionEmitter,
1053 ctx: &CodegenCtx,
1054 operand: ExprId,
1055 result_ty: &Type,
1056) -> Result<(), crate::compiler_error::CompilerFailure> {
1057 let operand_ty = ctx
1058 .ta
1059 .try_expr(operand)
1060 .map_err(crate::codegen::arena_failure)?
1061 .ty
1062 .clone();
1063 emit_expr(emitter, ctx, operand)?;
1064 cast::emit_coerce_to_slot(emitter, ctx, &operand_ty, result_ty)
1065}
1066
1067fn emit_generic_call(
1074 emitter: &mut FunctionEmitter,
1075 ctx: &CodegenCtx,
1076 result_ty: &Type,
1077 mangled: &crate::MangledName,
1078 type_args: &[Type],
1079 args: &[crate::GenericArgument],
1080 return_cast: &Option<Type>,
1081) -> Result<(), crate::compiler_error::CompilerFailure> {
1082 let (wasm_idx, target_params, target_return) = match ctx.symbols.top_level_fn(mangled) {
1097 Some(target) => (target.wasm_idx, target.params.clone(), target.ret.clone()),
1098 None => {
1099 return Err(crate::codegen::internal_failure(
1100 "generic call target is not registered",
1101 ));
1102 }
1103 };
1104 for (i, arg) in args.iter().enumerate() {
1105 emit_expr(emitter, ctx, arg.expr)?;
1106 if arg.is_generic {
1107 let arg_ty = ctx
1108 .ta
1109 .try_expr(arg.expr)
1110 .map_err(crate::codegen::arena_failure)?
1111 .ty
1112 .clone();
1113 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &arg_ty)?;
1114 } else if let Some(param_ty) = target_params.get(i) {
1115 let arg_ty = ctx
1120 .ta
1121 .try_expr(arg.expr)
1122 .map_err(crate::codegen::arena_failure)?
1123 .ty
1124 .clone();
1125 cast::emit_coerce_to_slot(emitter, ctx, &arg_ty, param_ty)?;
1126 }
1127 }
1128 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
1129 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
1130 }
1131 if ctx.symbols.runtime_generic_functions.contains(mangled) {
1132 crate::codegen::runtime_descriptors::require_direct_schema(ctx, mangled, type_args)?;
1133 crate::codegen::runtime_descriptors::environment(emitter, ctx, type_args)?;
1134 }
1135 emitter.instruction(Instruction::Call(wasm_idx));
1136 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
1137 emit_undefined(emitter, ctx)?;
1138 }
1139 if let Some(ty) = return_cast {
1140 cast::emit_cast_to(emitter, ctx, ty)?;
1141 } else {
1142 cast::emit_coerce_to_slot(emitter, ctx, &target_return, result_ty)?;
1143 }
1144
1145 Ok(())
1146}
1147
1148fn emit_object_literal(
1149 emitter: &mut FunctionEmitter,
1150 ctx: &CodegenCtx,
1151 result_ty: &Type,
1152 members: &[TypedObjectMember],
1153 fields: &[crate::TypedObjectFieldOrigin],
1154) -> Result<(), crate::compiler_error::CompilerFailure> {
1155 if members
1156 .iter()
1157 .any(|member| matches!(member, TypedObjectMember::Computed { .. }))
1158 {
1159 emit_computed_object(emitter, ctx, members)?;
1160 return Ok(());
1161 }
1162 if !matches!(result_ty, Type::Object { .. } | Type::InterfaceRef { .. }) {
1163 return Err(crate::codegen::internal_failure(
1164 "object literal requires an object representation",
1165 ));
1166 }
1167 let structural_ty = crate::typed_ast::object_literal_layout(result_ty, fields);
1168 if members
1169 .iter()
1170 .any(|member| matches!(member, TypedObjectMember::Spread { .. }))
1171 {
1172 emit_object_spread(emitter, ctx, members, &structural_ty)?;
1173 return Ok(());
1174 }
1175 let vtable_idx = ctx
1176 .symbols
1177 .vtable_global_idx(&structural_ty)
1178 .ok_or_else(|| {
1179 crate::codegen::internal_failure("vtable global recorded during object-emission pass")
1180 })?;
1181 let object_shape_idx = ctx
1182 .symbols
1183 .object_subtype_idx(&structural_ty)
1184 .ok_or_else(|| {
1185 crate::codegen::internal_failure(
1186 "object shape type recorded during object-emission pass",
1187 )
1188 })?;
1189 let Type::Object {
1190 fields: declared_fields,
1191 ..
1192 } = &structural_ty
1193 else {
1194 return Err(crate::codegen::internal_failure(
1195 "object literal has no structural representation",
1196 ));
1197 };
1198 let shape_key: Vec<_> = declared_fields
1199 .iter()
1200 .map(|(name, field)| crate::codegen::field_names::FieldName {
1201 name: name.clone(),
1202 optional: field.optional,
1203 is_accessor: false,
1204 is_private: false,
1205 })
1206 .collect();
1207 let intrinsics = ctx
1208 .symbols
1209 .intrinsic_type_indices()
1210 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1211
1212 let evaluated = evaluate_object_members(emitter, ctx, members)?;
1214
1215 emitter.instruction(Instruction::GlobalGet(vtable_idx));
1216 crate::codegen::field_names::emit_instance_names(emitter, ctx, &shape_key, |name| {
1217 fields.iter().any(|field| {
1218 field.name.name == name && !matches!(field.source, TypedObjectFieldSource::Absent(_))
1219 })
1220 })?;
1221
1222 let by_name: std::collections::BTreeMap<&str, &crate::TypedObjectFieldOrigin> =
1227 fields.iter().map(|f| (f.name.name.as_str(), f)).collect();
1228 for (name, field) in declared_fields {
1229 if let Some(origin) = by_name.get(name.as_str()) {
1230 let (TypedObjectFieldSource::Literal(value) | TypedObjectFieldSource::Absent(value)) =
1231 &origin.source
1232 else {
1233 return Err(crate::codegen::internal_failure(
1234 "spread field reached plain object lowering",
1235 ));
1236 };
1237 emitter.instruction(Instruction::LocalGet(*evaluated.get(value).ok_or_else(
1238 || crate::codegen::internal_failure("object field value was not evaluated"),
1239 )?));
1240 } else {
1241 if !field.optional {
1242 return Err(crate::codegen::internal_failure(
1243 "required object field is missing from the literal",
1244 ));
1245 }
1246 emit_undefined(emitter, ctx)?;
1247 }
1248 }
1249 emitter.instruction(Instruction::ArrayNewFixed {
1250 array_type_index: intrinsics.object_fields,
1251 array_size: crate::codegen::wasm_u32(declared_fields.len())?,
1252 });
1253 emitter.instruction(Instruction::RefNull(HeapType::ANY));
1254 emitter.instruction(Instruction::StructNew(object_shape_idx));
1255
1256 Ok(())
1257}
1258
1259fn emit_field_access(
1260 emitter: &mut FunctionEmitter,
1261 ctx: &CodegenCtx,
1262 id: ExprId,
1263 result_ty: &Type,
1264 receiver: &ExprId,
1265 name: &Ident,
1266) -> Result<(), crate::compiler_error::CompilerFailure> {
1267 let receiver_ty = ctx
1268 .ta
1269 .source_type(*receiver)
1270 .map_err(crate::codegen::arena_failure)?
1271 .clone();
1272 if ctx.ta.has_string_index(&receiver_ty) {
1273 emit_expr(emitter, ctx, *receiver)?;
1274 cast::emit_box(
1275 emitter,
1276 ctx,
1277 &ctx.ta
1278 .try_expr(*receiver)
1279 .map_err(crate::codegen::arena_failure)?
1280 .ty,
1281 )?;
1282 let Some(symbol) = ctx.require(
1283 ctx.symbols.field_name_string_global_idx(&name.name),
1284 "property name collected",
1285 ) else {
1286 return Ok(());
1287 };
1288 emitter.instruction(Instruction::GlobalGet(symbol));
1289 let Some(symbol) = ctx.require(
1290 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
1291 "record read imported",
1292 ) else {
1293 return Ok(());
1294 };
1295 emitter.instruction(Instruction::Call(symbol));
1296 let checked_ty = ctx
1297 .ta
1298 .source_type(id)
1299 .map_err(crate::codegen::arena_failure)?;
1300 crate::codegen::cast_check::emit_checked_cast_on_stack(
1301 emitter,
1302 ctx,
1303 &Type::Unknown,
1304 checked_ty,
1305 )?;
1306 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
1307 return Ok(());
1308 }
1309 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
1311 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
1312 emit_receiver(emitter, ctx, *receiver)?;
1313 emit_class_method_value(
1314 emitter,
1315 ctx,
1316 &receiver_ty,
1317 &name.name,
1318 result_ty,
1319 ctx.ta
1320 .source_type(id)
1321 .map_err(crate::codegen::arena_failure)?,
1322 )?;
1323 return Ok(());
1324 }
1325 if ctx.symbols.class_field_slot(mangled, &name.name).is_none() {
1327 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
1328 emit_method_call(
1329 emitter,
1330 ctx,
1331 *receiver,
1332 mangled,
1333 &getter,
1334 &[],
1335 None,
1336 None,
1337 &ctx.ta
1338 .try_expr(id)
1339 .map_err(crate::codegen::arena_failure)?
1340 .ty,
1341 )?;
1342 return Ok(());
1343 }
1344 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
1345 crate::codegen::internal_failure("class struct type recorded in classes::emit")
1346 })?;
1347 let slot = ctx
1348 .symbols
1349 .class_field_slot(mangled, &name.name)
1350 .ok_or_else(|| {
1351 crate::codegen::internal_failure("class field slot recorded in classes::emit")
1352 })?;
1353 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1354 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1355 })?;
1356 emit_receiver(emitter, ctx, *receiver)?;
1357 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&receiver_ty)?)?;
1358 emitter.instruction(Instruction::LocalTee(object));
1359 emitter.instruction(Instruction::StructGet {
1360 struct_type_index: struct_idx,
1361 field_index: 2,
1362 });
1363 emitter.instruction(Instruction::I32Const(slot as i32));
1364 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
1365 if ctx
1366 .symbols
1367 .class_field_narrowing_check(mangled, &name.name)
1368 .is_some()
1369 {
1370 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
1371 }
1372 emit_class_field_slot_cast_as(
1373 emitter,
1374 ctx,
1375 mangled,
1376 &name.name,
1377 &ctx.ta
1378 .try_expr(id)
1379 .map_err(crate::codegen::arena_failure)?
1380 .ty,
1381 ctx.ta
1382 .source_type(id)
1383 .map_err(crate::codegen::arena_failure)?,
1384 )?;
1385 return Ok(());
1386 }
1387 let intrinsics = ctx
1388 .symbols
1389 .intrinsic_type_indices()
1390 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1391 let field_ty = ctx
1394 .ta
1395 .try_expr(id)
1396 .map_err(crate::codegen::arena_failure)?
1397 .ty
1398 .clone();
1399 emit_receiver(emitter, ctx, *receiver)?;
1402 let rcv_local = stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
1403 emit_object_property_read_as(
1404 emitter,
1405 ctx,
1406 rcv_local,
1407 &name.name,
1408 &field_ty,
1409 ctx.ta
1410 .source_type(id)
1411 .map_err(crate::codegen::arena_failure)?,
1412 )?;
1413
1414 Ok(())
1415}
1416
1417fn emit_closure_value(
1418 emitter: &mut FunctionEmitter,
1419 ctx: &CodegenCtx,
1420 id: ExprId,
1421 result_ty: &Type,
1422 params: &[crate::TypedParam],
1423 captured: &[crate::CapturedVar],
1424 runtime_generics: &[String],
1425) -> Result<(), crate::compiler_error::CompilerFailure> {
1426 let closure_struct_idx = ctx
1444 .symbols
1445 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
1446 .ok_or_else(|| {
1447 crate::codegen::internal_failure(
1448 "closure struct type registered for every closure-typed expression",
1449 )
1450 })?;
1451 let env_type_idx = ctx.symbols.env_type_idx(id).ok_or_else(|| {
1452 crate::codegen::internal_failure("env type registered for every closure expression")
1453 })?;
1454 let closure_func_idx = ctx
1455 .symbols
1456 .closure_func_idx(id)
1457 .ok_or_else(|| crate::codegen::internal_failure("closure body function index allocated"))?;
1458 let closure_vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
1459 crate::codegen::internal_failure("closure vtable global emitted whenever closures exist")
1460 })?;
1461
1462 emitter.instruction(Instruction::GlobalGet(closure_vtable_idx));
1464
1465 emitter.instruction(Instruction::RefFunc(closure_func_idx));
1467
1468 for c in captured {
1471 emit_captured_load(emitter, ctx, c)?;
1472 }
1473 if !runtime_generics.is_empty() {
1474 let types: Vec<_> = runtime_generics
1475 .iter()
1476 .cloned()
1477 .map(Type::TypeVar)
1478 .collect();
1479 crate::codegen::runtime_descriptors::environment(emitter, ctx, &types)?;
1480 }
1481 let self_environment = if ctx.ta.closure_names.contains_key(&id) {
1482 emitter.instruction(Instruction::RefNull(HeapType::Concrete(
1483 ctx.symbols
1484 .intrinsic_type_indices()
1485 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
1486 .object,
1487 )));
1488 Some(emitter.add_anonymous_local(ValType::Ref(RefType {
1489 nullable: false,
1490 heap_type: HeapType::Concrete(env_type_idx),
1491 }))?)
1492 } else {
1493 None
1494 };
1495 emitter.instruction(Instruction::StructNew(env_type_idx));
1496 if let Some(local) = self_environment {
1497 emitter.instruction(Instruction::LocalTee(local));
1498 }
1499 if let Some(metadata) = crate::codegen::call_arguments::typed_metadata(params) {
1500 crate::codegen::call_arguments::wrap(emitter, ctx, &metadata)?;
1501 }
1502
1503 if ctx.ta.closure_this.contains_key(&id) {
1504 crate::codegen::this_binding::wrap(emitter, ctx)?;
1505 }
1506 emitter.instruction(Instruction::I64Const(0));
1510 emitter.instruction(Instruction::StructNew(closure_struct_idx));
1511 if let Some(environment) = self_environment {
1512 let closure = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1513 emitter.instruction(Instruction::LocalSet(closure));
1514 emitter.instruction(Instruction::LocalGet(environment));
1515 emitter.instruction(Instruction::LocalGet(closure));
1516 emitter.instruction(Instruction::StructSet {
1517 struct_type_index: env_type_idx,
1518 field_index: crate::codegen::wasm_u32(
1519 captured
1520 .len()
1521 .checked_add(usize::from(!runtime_generics.is_empty()))
1522 .ok_or_else(|| {
1523 crate::codegen::internal_failure("closure capture count overflow")
1524 })?,
1525 )?,
1526 });
1527 emitter.instruction(Instruction::LocalGet(closure));
1528 }
1529
1530 Ok(())
1531}
1532
1533fn inferred_never(
1536 ctx: &CodegenCtx,
1537 id: ExprId,
1538) -> Result<bool, crate::compiler_error::CompilerFailure> {
1539 let ty = ctx
1540 .ta
1541 .source_type(id)
1542 .map_err(crate::codegen::arena_failure)?;
1543 Ok(matches!(ty, Type::Never))
1544}
1545
1546fn emit_local_narrow_ref(
1547 emitter: &mut FunctionEmitter<'_>,
1548 ctx: &CodegenCtx<'_>,
1549 binding: &Ident,
1550 path: &ReferencePath,
1551 narrowed_ty: &Type,
1552) -> Result<(), crate::compiler_error::CompilerFailure> {
1553 if !path.chain.is_empty()
1554 && let Some(source) = emitter.narrow_source(&binding.name)
1555 {
1556 emit_expr(emitter, ctx, source)?;
1559 crate::codegen::cast_check::emit_checked_cast_on_stack(
1560 emitter,
1561 ctx,
1562 &ctx.ta
1563 .try_expr(source)
1564 .map_err(crate::codegen::arena_failure)?
1565 .ty,
1566 narrowed_ty,
1567 )?;
1568 return Ok(());
1569 }
1570 let Some((slot, slot_ty)) = emitter.narrowed_read_slot(&binding.name) else {
1571 if let BindingId::Local { name, .. } = &path.root
1576 && let Some((slot, slot_ty)) = emitter.narrowed_read_slot(name)
1577 {
1578 emitter.instruction(Instruction::LocalGet(slot));
1579 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1580 if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1581 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1582 }
1583 return Ok(());
1584 }
1585 if let BindingId::Global(mangled) = &path.root {
1586 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1587 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1588 })?;
1589 emitter.instruction(Instruction::GlobalGet(idx));
1590 let source_ty = ctx.symbols.global_type(mangled).ok_or_else(|| {
1591 crate::codegen::internal_failure("language globals retain their declared type")
1592 })?;
1593 crate::codegen::cast_check::emit_checked_cast_on_stack(
1594 emitter,
1595 ctx,
1596 source_ty,
1597 narrowed_ty,
1598 )?;
1599 return Ok(());
1600 }
1601 if let Some(source) = emitter.narrow_source(&binding.name) {
1602 emit_expr(emitter, ctx, source)?;
1603 let source_ty = &ctx
1604 .ta
1605 .try_expr(source)
1606 .map_err(crate::codegen::arena_failure)?
1607 .ty;
1608 let cast_info = cast_info_for(source_ty.clone(), narrowed_ty.clone());
1609 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1610 return Ok(());
1611 }
1612 return Err(crate::codegen::internal_failure(format!(
1613 "narrow binding `{}` (path {path:?}) not registered in codegen scope — \
1614 the region's source names a shadow from a scope that already closed",
1615 binding.name,
1616 )));
1617 };
1618 emitter.instruction(Instruction::LocalGet(slot));
1619 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1620 let _: () = if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1621 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1622 };
1623 Ok(())
1624}
1625
1626fn unbox_if_boxed(
1634 emitter: &mut FunctionEmitter<'_>,
1635 ctx: &CodegenCtx<'_>,
1636 slot_ty: ValType,
1637) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1638 let ValType::Ref(RefType {
1639 heap_type: HeapType::Concrete(idx),
1640 ..
1641 }) = slot_ty
1642 else {
1643 return Ok(slot_ty);
1644 };
1645 let Some(payload) = ctx.symbols.box_payload_type(idx) else {
1646 return Ok(slot_ty);
1647 };
1648 let local = emitter.add_anonymous_local(slot_ty)?;
1649 emitter.instruction(Instruction::LocalSet(local));
1650 super::binding_cells::read(emitter, ctx, local, idx)?;
1651 Ok(payload)
1652}
1653
1654fn emit_array_literal(
1657 emitter: &mut FunctionEmitter,
1658 ctx: &CodegenCtx,
1659 elements: &[crate::TypedArrayElement],
1660) -> Result<(), crate::compiler_error::CompilerFailure> {
1661 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1682 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1683 })?;
1684 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1685 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1686 })?;
1687 let array_vtable_global = ctx
1688 .symbols
1689 .prelude_global_idx("array_vtable")
1690 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1691
1692 let has_spread = elements
1693 .iter()
1694 .any(|e| matches!(e, crate::TypedArrayElement::Spread(_)));
1695
1696 if has_spread {
1697 emit_spread_array_literal(emitter, ctx, elements)?;
1698 } else {
1699 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1700 for el in elements {
1701 let elem_id = el.expr_id();
1702 emit_expr(emitter, ctx, elem_id)?;
1703 let elem_ty = ctx
1704 .ta
1705 .try_expr(elem_id)
1706 .map_err(crate::codegen::arena_failure)?
1707 .ty
1708 .clone();
1709 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &elem_ty)?;
1710 }
1711 emitter.instruction(Instruction::ArrayNewFixed {
1712 array_type_index: raw_array_idx,
1713 array_size: crate::codegen::wasm_u32(elements.len())?,
1714 });
1715 emitter.instruction(Instruction::I32Const(
1716 i32::try_from(elements.len())
1717 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1718 ));
1719 emitter.instruction(Instruction::StructNew(array_idx));
1720 }
1721 Ok(())
1722}
1723
1724fn emit_tuple_literal(
1725 emitter: &mut FunctionEmitter,
1726 ctx: &CodegenCtx,
1727 elements: &[ExprId],
1728) -> Result<(), crate::compiler_error::CompilerFailure> {
1729 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1731 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1732 })?;
1733 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1734 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1735 })?;
1736 let array_vtable_global = ctx
1737 .symbols
1738 .prelude_global_idx("array_vtable")
1739 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1740 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1741 for &elem_id in elements {
1742 emit_expr(emitter, ctx, elem_id)?;
1743 crate::codegen::function_emitter::cast::emit_box(
1744 emitter,
1745 ctx,
1746 &ctx.ta
1747 .try_expr(elem_id)
1748 .map_err(crate::codegen::arena_failure)?
1749 .ty,
1750 )?;
1751 }
1752 emitter.instruction(Instruction::ArrayNewFixed {
1753 array_type_index: raw_array_idx,
1754 array_size: crate::codegen::wasm_u32(elements.len())?,
1755 });
1756 emitter.instruction(Instruction::I32Const(
1757 i32::try_from(elements.len())
1758 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1759 ));
1760 emitter.instruction(Instruction::StructNew(array_idx));
1761 Ok(())
1762}
1763
1764fn emit_spread_array_literal(
1765 emitter: &mut FunctionEmitter,
1766 ctx: &CodegenCtx,
1767 elements: &[crate::TypedArrayElement],
1768) -> Result<(), crate::compiler_error::CompilerFailure> {
1769 let array = ctx
1770 .symbols
1771 .array_type_idx()
1772 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?;
1773 let raw = ctx
1774 .symbols
1775 .raw_array_type_idx()
1776 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?;
1777 let raw_type = ValType::Ref(RefType {
1778 nullable: false,
1779 heap_type: HeapType::Concrete(raw),
1780 });
1781 let total = emitter.add_anonymous_local(ValType::I32)?;
1782 let offset = emitter.add_anonymous_local(ValType::I32)?;
1783 let destination = emitter.add_anonymous_local(raw_type)?;
1784 emitter.instruction(Instruction::I32Const(0));
1785 emitter.instruction(Instruction::LocalSet(total));
1786 let mut chunks = Vec::with_capacity(elements.len());
1789 for element in elements {
1790 emit_literal_chunk(emitter, ctx, element, array, raw)?;
1791 let chunk = emitter.add_anonymous_local(raw_type)?;
1792 emitter.instruction(Instruction::LocalTee(chunk));
1793 emitter.instruction(Instruction::ArrayLen);
1794 emitter.instruction(Instruction::LocalGet(total));
1795 emitter.instruction(Instruction::I32Add);
1796 emitter.instruction(Instruction::LocalSet(total));
1797 chunks.push(chunk);
1798 }
1799 emitter.instruction(Instruction::LocalGet(total));
1800 emitter.instruction(Instruction::ArrayNewDefault(raw));
1801 emitter.instruction(Instruction::LocalSet(destination));
1802 emitter.instruction(Instruction::I32Const(0));
1803 emitter.instruction(Instruction::LocalSet(offset));
1804 for chunk in chunks {
1805 emitter.instruction(Instruction::LocalGet(destination));
1806 emitter.instruction(Instruction::LocalGet(offset));
1807 emitter.instruction(Instruction::LocalGet(chunk));
1808 emitter.instruction(Instruction::I32Const(0));
1809 emitter.instruction(Instruction::LocalGet(chunk));
1810 emitter.instruction(Instruction::ArrayLen);
1811 emitter.instruction(Instruction::ArrayCopy {
1812 array_type_index_dst: raw,
1813 array_type_index_src: raw,
1814 });
1815 emitter.instruction(Instruction::LocalGet(offset));
1816 emitter.instruction(Instruction::LocalGet(chunk));
1817 emitter.instruction(Instruction::ArrayLen);
1818 emitter.instruction(Instruction::I32Add);
1819 emitter.instruction(Instruction::LocalSet(offset));
1820 }
1821 emitter.instruction(Instruction::GlobalGet(
1822 ctx.symbols
1823 .prelude_global_idx("array_vtable")
1824 .ok_or_else(|| crate::codegen::internal_failure("array vtable declared"))?,
1825 ));
1826 emitter.instruction(Instruction::LocalGet(destination));
1827 emitter.instruction(Instruction::LocalGet(total));
1828 emitter.instruction(Instruction::StructNew(array));
1829 Ok(())
1830}
1831
1832fn emit_literal_chunk(
1833 emitter: &mut FunctionEmitter,
1834 ctx: &CodegenCtx,
1835 element: &crate::TypedArrayElement,
1836 array: u32,
1837 raw: u32,
1838) -> Result<(), crate::compiler_error::CompilerFailure> {
1839 if let crate::TypedArrayElement::Value(value) = element {
1840 emit_expr(emitter, ctx, *value)?;
1841 cast::emit_box(
1842 emitter,
1843 ctx,
1844 &ctx.ta
1845 .try_expr(*value)
1846 .map_err(crate::codegen::arena_failure)?
1847 .ty,
1848 )?;
1849 emitter.instruction(Instruction::ArrayNewFixed {
1850 array_type_index: raw,
1851 array_size: 1,
1852 });
1853 return Ok(());
1854 }
1855 emit_receiver(emitter, ctx, element.expr_id())?;
1858 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(array)));
1859
1860 let raw_type = ValType::Ref(RefType {
1861 nullable: false,
1862 heap_type: HeapType::Concrete(raw),
1863 });
1864 let source = emitter.add_anonymous_local(raw_type)?;
1865 let snapshot = emitter.add_anonymous_local(raw_type)?;
1866 let length = stash_array_length(emitter, array)?;
1867 emitter.instruction(Instruction::StructGet {
1868 struct_type_index: array,
1869 field_index: 1,
1870 });
1871 emitter.instruction(Instruction::LocalSet(source));
1872 emitter.instruction(Instruction::LocalGet(length));
1873 emitter.instruction(Instruction::ArrayNewDefault(raw));
1874 emitter.instruction(Instruction::LocalTee(snapshot));
1875 emitter.instruction(Instruction::I32Const(0));
1876 emitter.instruction(Instruction::LocalGet(source));
1877 emitter.instruction(Instruction::I32Const(0));
1878 emitter.instruction(Instruction::LocalGet(length));
1879 emitter.instruction(Instruction::ArrayCopy {
1880 array_type_index_dst: raw,
1881 array_type_index_src: raw,
1882 });
1883 emitter.instruction(Instruction::LocalGet(snapshot));
1884 Ok(())
1885}
1886
1887fn emit_postfix_unary(
1891 emitter: &mut FunctionEmitter<'_>,
1892 ctx: &CodegenCtx<'_>,
1893 op: crate::PostfixOp,
1894 target: &crate::PostfixTarget,
1895 result_ty: &Type,
1896) -> Result<(), crate::compiler_error::CompilerFailure> {
1897 let _: () = match target {
1898 crate::PostfixTarget::Local {
1899 ident,
1900 boxed,
1901 target_ty,
1902 } => {
1903 let slot = emitter.write_slot(&ident.name)?;
1905 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1906 let box_idx = if *boxed {
1907 Some(ctx.symbols.box_type_idx(target_ty)?.ok_or_else(|| {
1908 crate::codegen::internal_failure("box type registered for postfix operand")
1909 })?)
1910 } else {
1911 None
1912 };
1913 if let Some(box_idx) = box_idx {
1914 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
1915 } else {
1916 emitter.instruction(Instruction::LocalGet(slot));
1917 }
1918 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1919 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1920 emit_postfix_numeric(emitter, ctx, result_ty);
1921 emitter.instruction(Instruction::LocalSet(old));
1922 if box_idx.is_some() {
1923 emitter.instruction(Instruction::LocalGet(slot));
1924 }
1925 emitter.instruction(Instruction::LocalGet(old));
1926 emit_postfix_delta(emitter, ctx, op, result_ty);
1927 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1928 if let Some(box_idx) = box_idx {
1929 emitter.instruction(Instruction::StructSet {
1930 struct_type_index: box_idx,
1931 field_index: 0,
1932 });
1933 } else {
1934 emitter.instruction(Instruction::LocalSet(slot));
1935 }
1936 emitter.instruction(Instruction::LocalGet(old));
1937 }
1938 crate::PostfixTarget::Global {
1939 mangled, target_ty, ..
1940 } => {
1941 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1942 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1943 })?;
1944 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1945 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
1946 emitter.instruction(Instruction::GlobalGet(idx));
1947 if let ValType::Ref(RefType {
1949 nullable: false, ..
1950 }) = ctx.symbols.value_type(target_ty)?
1951 {
1952 emitter.instruction(Instruction::RefAsNonNull);
1953 }
1954 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1955 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1956 emit_postfix_numeric(emitter, ctx, result_ty);
1957 emitter.instruction(Instruction::LocalSet(old));
1958 emitter.instruction(Instruction::LocalGet(old));
1959 emit_postfix_delta(emitter, ctx, op, result_ty);
1960 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1961 emitter.instruction(Instruction::GlobalSet(idx));
1962 emitter.instruction(Instruction::LocalGet(old));
1963 }
1964 crate::PostfixTarget::Field {
1965 receiver,
1966 name,
1967 target_ty,
1968 } => {
1969 let receiver_ty = ctx
1974 .ta
1975 .source_type(*receiver)
1976 .map_err(crate::codegen::arena_failure)?
1977 .clone();
1978 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
1979 emit_class_field_postfix(
1980 emitter, ctx, *receiver, mangled, &name.name, op, target_ty,
1981 )?;
1982 return Ok(());
1983 }
1984 if let Some((getter, setter)) = host_property_accessors(ctx, &receiver_ty, name)? {
1985 emit_host_property_postfix(
1986 emitter,
1987 ctx,
1988 *receiver,
1989 &receiver_ty,
1990 (getter, setter),
1991 op,
1992 target_ty,
1993 )?;
1994 return Ok(());
1995 }
1996 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1998 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1999 })?;
2000 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2001 emit_receiver(emitter, ctx, *receiver)?;
2002 let rcv_local =
2003 stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
2004 emit_object_property_read(emitter, ctx, rcv_local, &name.name, target_ty)?;
2005 emit_postfix_numeric(emitter, ctx, target_ty);
2006 emitter.instruction(Instruction::LocalSet(tmp));
2007 emitter.instruction(Instruction::LocalGet(tmp));
2008 emit_postfix_delta(emitter, ctx, op, target_ty);
2009 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2010 emitter.instruction(Instruction::LocalSet(updated));
2011 emit_object_property_write_value(
2012 emitter,
2013 ctx,
2014 rcv_local,
2015 name,
2016 &ShapeArgument::Local {
2017 slot: updated,
2018 ty: target_ty.clone(),
2019 },
2020 )?;
2021 emitter.instruction(Instruction::LocalGet(tmp));
2022 }
2023 crate::PostfixTarget::Index {
2024 receiver, index, ..
2025 } => {
2026 emit_index_postfix(emitter, ctx, *receiver, *index, op)?;
2027 }
2028 };
2029 Ok(())
2030}
2031
2032enum HostPropertyGetter {
2034 Func(u32),
2035 InlineLength(u32),
2037}
2038
2039fn host_property_accessors(
2042 ctx: &CodegenCtx,
2043 receiver_ty: &Type,
2044 name: &Ident,
2045) -> Result<Option<(HostPropertyGetter, u32)>, crate::compiler_error::CompilerFailure> {
2046 let Some(iface) = crate::codegen::host_property_iface(receiver_ty) else {
2047 return Ok(None);
2048 };
2049 let setter_key = crate::mangle::extend(&iface, &crate::mangle::property_setter(&name.name));
2050 let Some(setter) = ctx.symbols.func_idx(&setter_key) else {
2051 return Ok(None);
2052 };
2053 if let Some(struct_idx) = inline_length_struct_idx(ctx, &iface, &name.name)? {
2054 return Ok(Some((HostPropertyGetter::InlineLength(struct_idx), setter)));
2055 }
2056 let getter = ctx
2057 .symbols
2058 .func_idx(&crate::mangle::extend(&iface, &name.name))
2059 .ok_or_else(|| {
2060 crate::codegen::internal_failure("host property getter recorded beside its setter")
2061 })?;
2062 Ok(Some((HostPropertyGetter::Func(getter), setter)))
2063}
2064
2065fn emit_host_property_postfix(
2066 emitter: &mut FunctionEmitter,
2067 ctx: &CodegenCtx,
2068 receiver: ExprId,
2069 receiver_ty: &Type,
2070 (getter, setter): (HostPropertyGetter, u32),
2071 op: crate::PostfixOp,
2072 target_ty: &Type,
2073) -> Result<(), crate::compiler_error::CompilerFailure> {
2074 let recv = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2076 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Number)?)?;
2077 emit_receiver(emitter, ctx, receiver)?;
2078 emitter.instruction(Instruction::LocalSet(recv));
2079 emitter.instruction(Instruction::LocalGet(recv));
2081 emitter.instruction(Instruction::RefAsNonNull);
2082 emitter.instruction(Instruction::LocalGet(recv));
2083 emitter.instruction(Instruction::RefAsNonNull);
2084 match getter {
2085 HostPropertyGetter::Func(getter) => {
2086 emitter.instruction(Instruction::Call(getter));
2087 }
2088 HostPropertyGetter::InlineLength(struct_idx) => {
2089 emit_inline_length(emitter, ctx, struct_idx);
2090 }
2091 }
2092 emitter.instruction(Instruction::LocalTee(old));
2093 emit_postfix_delta(emitter, ctx, op, &Type::Number);
2094 emitter.instruction(Instruction::Call(setter));
2095 emitter.instruction(Instruction::LocalGet(old));
2096 if target_ty != &Type::Number {
2097 cast::emit_box(emitter, ctx, &Type::Number)?;
2098 }
2099 Ok(())
2100}
2101
2102fn emit_index_postfix(
2103 emitter: &mut FunctionEmitter,
2104 ctx: &CodegenCtx,
2105 receiver: ExprId,
2106 index: ExprId,
2107 op: crate::PostfixOp,
2108) -> Result<(), crate::compiler_error::CompilerFailure> {
2109 let is_uint8 = ctx
2110 .ta
2111 .source_type(receiver)
2112 .map_err(crate::codegen::arena_failure)?
2113 .lowered()
2114 == &Type::Uint8Array;
2115 let result_ty = if is_uint8 {
2116 Type::Number
2117 } else {
2118 Type::Unknown
2119 };
2120 emit_expr(emitter, ctx, receiver)?;
2121 cast::emit_box(
2122 emitter,
2123 ctx,
2124 &ctx.ta
2125 .try_expr(receiver)
2126 .map_err(crate::codegen::arena_failure)?
2127 .ty,
2128 )?;
2129 let receiver_local = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
2130 emitter.instruction(Instruction::LocalSet(receiver_local));
2131 emit_expr(emitter, ctx, index)?;
2132 let key = emitter.add_anonymous_local(
2133 ctx.symbols.value_type(
2134 &ctx.ta
2135 .try_expr(index)
2136 .map_err(crate::codegen::arena_failure)?
2137 .ty,
2138 )?,
2139 )?;
2140 emitter.instruction(Instruction::LocalSet(key));
2141 let (backing, position, raw_type) = emit_index_location(
2142 emitter,
2143 ctx,
2144 receiver_local,
2145 key,
2146 &ctx.ta
2147 .try_expr(index)
2148 .map_err(crate::codegen::arena_failure)?
2149 .ty,
2150 is_uint8,
2151 )?;
2152 emitter.instruction(Instruction::LocalGet(backing));
2153 emitter.instruction(Instruction::LocalGet(position));
2154 if is_uint8 {
2155 emitter.instruction(Instruction::ArrayGetU(raw_type));
2156 emitter.instruction(Instruction::F64ConvertI32U);
2157 } else {
2158 emitter.instruction(Instruction::ArrayGet(raw_type));
2159 }
2160 emit_postfix_numeric(emitter, ctx, &result_ty);
2161 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2162 emitter.instruction(Instruction::LocalTee(old));
2163 emit_postfix_delta(emitter, ctx, op, &result_ty);
2164 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2165 emitter.instruction(Instruction::LocalSet(updated));
2166 let (backing, position, _) = emit_index_location(
2169 emitter,
2170 ctx,
2171 receiver_local,
2172 key,
2173 &ctx.ta
2174 .try_expr(index)
2175 .map_err(crate::codegen::arena_failure)?
2176 .ty,
2177 is_uint8,
2178 )?;
2179 emitter.instruction(Instruction::LocalGet(backing));
2180 emitter.instruction(Instruction::LocalGet(position));
2181 emitter.instruction(Instruction::LocalGet(updated));
2182 if is_uint8 {
2183 emitter.instruction(Instruction::I32TruncSatF64S);
2184 }
2185 emitter.instruction(Instruction::ArraySet(raw_type));
2186 emitter.instruction(Instruction::LocalGet(old));
2187 Ok(())
2188}
2189
2190fn emit_index_location(
2191 emitter: &mut FunctionEmitter,
2192 ctx: &CodegenCtx,
2193 receiver: u32,
2194 key: u32,
2195 key_ty: &Type,
2196 is_uint8: bool,
2197) -> Result<(u32, u32, u32), crate::compiler_error::CompilerFailure> {
2198 let (container, raw, ty) = if is_uint8 {
2199 (
2200 ctx.symbols
2201 .uint8_array_type_idx()
2202 .ok_or_else(|| crate::codegen::internal_failure("Uint8Array declared"))?,
2203 ctx.symbols
2204 .raw_uint8_array_type_idx()
2205 .ok_or_else(|| crate::codegen::internal_failure("raw Uint8Array declared"))?,
2206 Type::Uint8Array,
2207 )
2208 } else {
2209 (
2210 ctx.symbols
2211 .array_type_idx()
2212 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?,
2213 ctx.symbols
2214 .raw_array_type_idx()
2215 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?,
2216 Type::Array(Box::new(Type::Unknown)),
2217 )
2218 };
2219 emitter.instruction(Instruction::LocalGet(receiver));
2220 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, &Type::Unknown, &ty)?;
2221 emitter.instruction(Instruction::LocalGet(key));
2222 emit_index_number(emitter, ctx, key_ty)?;
2223 let operand = stash_index_operand(emitter)?;
2224 let length = (!is_uint8)
2225 .then(|| stash_array_length(emitter, container))
2226 .transpose()?;
2227 emitter.instruction(Instruction::StructGet {
2228 struct_type_index: container,
2229 field_index: 1,
2230 });
2231 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
2232 nullable: false,
2233 heap_type: HeapType::Concrete(raw),
2234 }))?;
2235 emitter.instruction(Instruction::LocalSet(backing));
2236 let position = match length {
2237 Some(length) => emit_checked_index_with_length(emitter, ctx, length, operand)?,
2238 None => emit_checked_index(emitter, ctx, backing, operand)?,
2239 };
2240 Ok((backing, position, raw))
2241}
2242
2243fn emit_postfix_numeric(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, ty: &Type) {
2244 ctx.latch(emit_postfix_numeric_checked(emitter, ctx, ty));
2245}
2246
2247fn emit_postfix_numeric_checked(
2248 emitter: &mut FunctionEmitter,
2249 ctx: &CodegenCtx,
2250 ty: &Type,
2251) -> Result<(), crate::compiler_error::CompilerFailure> {
2252 if matches!(ty, Type::Unknown) {
2253 let function = ctx
2254 .symbols
2255 .prelude_func_idx("__value_numeric")
2256 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2257 emitter.instruction(Instruction::Call(function));
2258 }
2259
2260 Ok(())
2261}
2262
2263fn emit_postfix_delta(
2264 emitter: &mut FunctionEmitter<'_>,
2265 ctx: &CodegenCtx<'_>,
2266 op: crate::PostfixOp,
2267 ty: &Type,
2268) {
2269 ctx.latch(emit_postfix_delta_checked(emitter, ctx, op, ty));
2270}
2271
2272fn emit_postfix_delta_checked(
2273 emitter: &mut FunctionEmitter<'_>,
2274 ctx: &CodegenCtx<'_>,
2275 op: crate::PostfixOp,
2276 ty: &Type,
2277) -> Result<(), crate::compiler_error::CompilerFailure> {
2278 if matches!(ty, Type::Unknown) {
2279 let name = if matches!(op, crate::PostfixOp::Inc) {
2280 "inc"
2281 } else {
2282 "dec"
2283 };
2284 let function = ctx
2285 .symbols
2286 .prelude_func_idx(&format!("__value_{name}"))
2287 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2288 emitter.instruction(Instruction::Call(function));
2289 return Ok(());
2290 }
2291 if ty.is_bigint() {
2292 emit_bigint_pm_one(emitter, ctx, op);
2293 return Ok(());
2294 }
2295 emitter.instruction(Instruction::F64Const(Ieee64::from(1.0)));
2296 emitter.instruction(match op {
2297 crate::PostfixOp::Inc => Instruction::F64Add,
2298 crate::PostfixOp::Dec => Instruction::F64Sub,
2299 crate::PostfixOp::NonNullAssert => {
2300 return Err(crate::codegen::internal_failure(
2301 "non-null assertion is not PostfixUnary",
2302 ));
2303 }
2304 });
2305
2306 Ok(())
2307}
2308
2309#[allow(clippy::too_many_arguments)]
2314fn emit_class_field_postfix(
2315 emitter: &mut FunctionEmitter<'_>,
2316 ctx: &CodegenCtx<'_>,
2317 receiver: ExprId,
2318 mangled: &crate::MangledName,
2319 field: &str,
2320 op: crate::PostfixOp,
2321 target_ty: &Type,
2322) -> Result<(), crate::compiler_error::CompilerFailure> {
2323 let intrinsics = ctx
2324 .symbols
2325 .intrinsic_type_indices()
2326 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
2327 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
2328 crate::codegen::internal_failure("class struct type recorded in classes::emit")
2329 })?;
2330 let slot = ctx
2333 .symbols
2334 .class_field_slot(mangled, field)
2335 .ok_or_else(|| {
2336 crate::codegen::internal_failure(
2337 "data-field slot recorded in classes::emit — accessors are rejected in infer",
2338 )
2339 })?;
2340 let fields_local = emitter.add_anonymous_local(ValType::Ref(RefType {
2341 nullable: false,
2342 heap_type: HeapType::Concrete(intrinsics.object_fields),
2343 }))?;
2344 let old = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2345 let object = emitter.add_anonymous_local(
2346 ctx.symbols.value_type(
2347 ctx.ta
2348 .source_type(receiver)
2349 .map_err(crate::codegen::arena_failure)?,
2350 )?,
2351 )?;
2352 emit_receiver(emitter, ctx, receiver)?;
2353 emitter.instruction(Instruction::LocalTee(object));
2354 emitter.instruction(Instruction::StructGet {
2355 struct_type_index: struct_idx,
2356 field_index: 2,
2357 });
2358 emitter.instruction(Instruction::LocalSet(fields_local));
2359 emitter.instruction(Instruction::LocalGet(fields_local));
2360 emitter.instruction(Instruction::I32Const(slot as i32));
2361 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
2362 if ctx
2363 .symbols
2364 .class_field_narrowing_check(mangled, field)
2365 .is_some()
2366 {
2367 crate::codegen::field_guards::check(emitter, ctx, object, mangled, field)?;
2368 }
2369 emit_class_field_slot_cast(emitter, ctx, mangled, field, target_ty)?;
2370 emit_postfix_numeric(emitter, ctx, target_ty);
2371 emitter.instruction(Instruction::LocalSet(old));
2372 emitter.instruction(Instruction::LocalGet(fields_local));
2373 emitter.instruction(Instruction::I32Const(slot as i32));
2374 emitter.instruction(Instruction::LocalGet(old));
2375 emit_postfix_delta(emitter, ctx, op, target_ty);
2376 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, target_ty)?;
2377 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
2378 emitter.instruction(Instruction::LocalGet(old));
2379 Ok(())
2380}
2381
2382fn emit_chain_parts(
2390 emitter: &mut FunctionEmitter<'_>,
2391 ctx: &CodegenCtx<'_>,
2392 parts: &[crate::TypedChainPart],
2393 idx: usize,
2394 receiver_ty: &Type,
2395 result_ty: &Type,
2396 source_types: Option<&[Type]>,
2397) -> Result<(), crate::compiler_error::CompilerFailure> {
2398 if source_types.is_some_and(|types| parts.len().checked_add(1) != Some(types.len())) {
2399 return Err(crate::codegen::internal_failure(
2400 "optional-chain source type count mismatch",
2401 ));
2402 }
2403 if idx == parts.len() {
2404 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
2409 emitter,
2410 ctx,
2411 receiver_ty,
2412 result_ty,
2413 )?;
2414 return Ok(());
2415 }
2416 let part = parts.get(idx).ok_or_else(|| {
2417 crate::codegen::internal_failure("optional-chain part index is out of bounds")
2418 })?;
2419 let next = idx
2420 .checked_add(1)
2421 .ok_or_else(|| crate::codegen::internal_failure("optional-chain index overflow"))?;
2422 let source_ty = source_types.and_then(|types| types.get(idx));
2423 let check_ty = source_types.and_then(|types| types.get(next));
2424 let saved_receiver = if matches!(
2425 part,
2426 crate::TypedChainPart::Field { .. }
2427 | crate::TypedChainPart::Index { .. }
2428 | crate::TypedChainPart::InterfaceProperty { .. }
2429 ) {
2430 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2431 emitter.instruction(Instruction::LocalTee(slot));
2432 Some(slot)
2433 } else if matches!(part, crate::TypedChainPart::NonNull { .. }) {
2434 emitter.call_receiver
2435 } else {
2436 None
2437 };
2438 let part_result_ty = part.result_ty().clone();
2439 let recv_val = part
2442 .is_optional()
2443 .then(|| ctx.symbols.value_type(receiver_ty))
2444 .transpose()?;
2445 let _: () = if let Some(recv_val @ ValType::Ref(_)) = recv_val {
2451 let block_ty = BlockType::Result(ctx.symbols.value_type(result_ty)?);
2452 let tmp = emitter.add_anonymous_local(recv_val)?;
2453 emitter.instruction(Instruction::LocalTee(tmp));
2454 emit_is_nullish(emitter, ctx)?;
2455 emitter.emit_if(block_ty);
2456 emit_undefined_for_chain_result(emitter, ctx, result_ty)?;
2457 emitter.emit_else();
2458 emitter.instruction(Instruction::LocalGet(tmp));
2459 emitter.instruction(Instruction::RefAsNonNull);
2460 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2461 emitter.call_receiver = saved_receiver;
2462 emit_chain_parts(
2463 emitter,
2464 ctx,
2465 parts,
2466 next,
2467 &part_result_ty,
2468 result_ty,
2469 source_types,
2470 )?;
2471 emitter.emit_end();
2472 } else {
2473 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2474 emitter.call_receiver = saved_receiver;
2475 emit_chain_parts(
2476 emitter,
2477 ctx,
2478 parts,
2479 next,
2480 &part_result_ty,
2481 result_ty,
2482 source_types,
2483 )?;
2484 };
2485 Ok(())
2486}
2487
2488fn emit_chain_operation(
2489 emitter: &mut FunctionEmitter,
2490 ctx: &CodegenCtx,
2491 part: &crate::TypedChainPart,
2492 receiver_ty: &Type,
2493 source_ty: Option<&Type>,
2494 check_ty: Option<&Type>,
2495) -> Result<(), crate::compiler_error::CompilerFailure> {
2496 if let crate::TypedChainPart::MethodCall {
2497 iface,
2498 name,
2499 result_ty,
2500 span,
2501 ..
2502 } = part
2503 && !crate::codegen::is_host_package(iface.as_str())
2504 && let Some(args) = ctx.ta.authored_call_arguments(*span)
2505 {
2506 cast::emit_box(emitter, ctx, receiver_ty)?;
2507 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2508 cast::emit_cast_to(emitter, ctx, result_ty)?;
2509 return Ok(());
2510 }
2511 if receiver_ty == &Type::Unknown {
2512 match part {
2513 crate::TypedChainPart::MethodCall {
2514 iface,
2515 name,
2516 args,
2517 result_ty,
2518 span,
2519 ..
2520 } if crate::codegen::runtime_values::dynamic_member_interface(iface) => {
2521 let args = ctx.ta.authored_call_arguments(*span).unwrap_or(args);
2522 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2523 return Ok(());
2524 }
2525 crate::TypedChainPart::InterfaceProperty { iface, name, .. }
2526 if crate::codegen::runtime_values::dynamic_member_interface(iface) =>
2527 {
2528 emit_live_member_on_stack(emitter, ctx, iface, &name.name, None)?;
2529 return Ok(());
2530 }
2531 _ => {}
2532 }
2533 }
2534 let operation_ty =
2535 crate::typechecker::infer::narrowing::strip_nullish(source_ty.unwrap_or(receiver_ty));
2536 if !matches!(
2537 part,
2538 crate::TypedChainPart::Index { .. }
2539 | crate::TypedChainPart::Call { .. }
2540 | crate::TypedChainPart::NonNull { .. }
2541 ) {
2542 let receiver_slot = ctx.symbols.value_type(receiver_ty)?;
2543 if receiver_slot == ctx.symbols.value_type(&operation_ty)? {
2544 if matches!(receiver_slot, ValType::Ref(_)) {
2545 cast::emit_cast_to(emitter, ctx, &operation_ty)?;
2546 }
2547 } else {
2548 crate::codegen::cast_check::emit_operation_cast_on_stack(
2549 emitter,
2550 ctx,
2551 receiver_ty,
2552 &operation_ty,
2553 )?;
2554 }
2555 }
2556 emit_chain_access(
2557 emitter,
2558 ctx,
2559 part,
2560 &operation_ty,
2561 check_ty.unwrap_or(part.result_ty()),
2562 )?;
2563 Ok(())
2564}
2565
2566fn emit_live_member_on_stack(
2567 emitter: &mut FunctionEmitter,
2568 ctx: &CodegenCtx,
2569 iface: &crate::MangledName,
2570 name: &str,
2571 args: Option<&[ExprId]>,
2572) -> Result<(), crate::compiler_error::CompilerFailure> {
2573 super::emit_const_string_by_text(emitter, ctx, name)?;
2574 super::emit_const_string_by_text(emitter, ctx, iface.as_str())?;
2575 let helper = if args.is_some() {
2576 "__value_member"
2577 } else {
2578 "__value_property"
2579 };
2580 emitter.instruction(Instruction::Call(
2581 ctx.symbols
2582 .prelude_func_idx(helper)
2583 .ok_or_else(|| crate::codegen::internal_failure("live member helper collected"))?,
2584 ));
2585 let Some(args) = args else {
2586 return Ok(());
2587 };
2588 let intr = ctx
2589 .symbols
2590 .intrinsic_type_indices()
2591 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2592 emitter.instruction(Instruction::GlobalGet(
2593 ctx.symbols
2594 .prelude_global_idx("array_vtable")
2595 .ok_or_else(|| crate::codegen::internal_failure("array vtable"))?,
2596 ));
2597 for &arg in args {
2598 emit_expr(emitter, ctx, arg)?;
2599 cast::emit_box(
2600 emitter,
2601 ctx,
2602 &ctx.ta
2603 .try_expr(arg)
2604 .map_err(crate::codegen::arena_failure)?
2605 .ty,
2606 )?;
2607 }
2608 emitter.instruction(Instruction::ArrayNewFixed {
2609 array_type_index: intr.raw_array,
2610 array_size: crate::codegen::wasm_u32(args.len())?,
2611 });
2612 emitter.instruction(Instruction::I32Const(i32::try_from(args.len()).map_err(
2613 |_| crate::codegen::internal_failure("argument array too large"),
2614 )?));
2615 emitter.instruction(Instruction::StructNew(intr.array));
2616 emitter.instruction(Instruction::Call(
2617 ctx.symbols
2618 .prelude_func_idx("__value_invoke")
2619 .ok_or_else(|| crate::codegen::internal_failure("live invocation collected"))?,
2620 ));
2621 Ok(())
2622}
2623
2624fn inline_length_struct_idx(
2627 ctx: &CodegenCtx<'_>,
2628 iface: &crate::MangledName,
2629 prop: &str,
2630) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
2631 let key = crate::mangle::extend(iface, prop);
2632 if !ctx.symbols.is_intrinsic_member(&key) {
2633 return Ok(None);
2634 }
2635 let index = if *iface == crate::mangle::prelude("String") && prop == "length" {
2636 ctx.symbols.string_type_idx()
2637 } else if *iface == crate::mangle::prelude("Array") && prop == "length" {
2638 ctx.symbols.array_type_idx()
2639 } else {
2640 return Err(crate::codegen::internal_failure(format!(
2641 "no inline lowering for intrinsic member `{}`",
2642 key.as_str()
2643 )));
2644 };
2645 index
2646 .map(Some)
2647 .ok_or_else(|| crate::codegen::internal_failure("length receiver intrinsic missing"))
2648}
2649
2650fn emit_inline_length(emitter: &mut FunctionEmitter<'_>, ctx: &CodegenCtx<'_>, struct_idx: u32) {
2652 let is_array = ctx.symbols.array_type_idx() == Some(struct_idx);
2653 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
2654 emitter.instruction(Instruction::StructGet {
2655 struct_type_index: struct_idx,
2656 field_index: if is_array { 2 } else { 1 },
2657 });
2658 if !is_array {
2659 emitter.instruction(Instruction::ArrayLen);
2660 }
2661 emitter.instruction(Instruction::F64ConvertI32U);
2662}
2663
2664fn emit_undefined_for_chain_result(
2666 emitter: &mut FunctionEmitter<'_>,
2667 ctx: &CodegenCtx<'_>,
2668 result_ty: &Type,
2669) -> Result<(), crate::compiler_error::CompilerFailure> {
2670 emit_undefined(emitter, ctx)?;
2671 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)
2672}
2673
2674fn emit_class_field_slot_cast(
2678 emitter: &mut FunctionEmitter,
2679 ctx: &CodegenCtx,
2680 class: &crate::MangledName,
2681 field: &str,
2682 result_ty: &Type,
2683) -> Result<(), crate::compiler_error::CompilerFailure> {
2684 emit_class_field_slot_cast_as(emitter, ctx, class, field, result_ty, result_ty)?;
2685 Ok(())
2686}
2687
2688fn emit_class_field_slot_cast_as(
2689 emitter: &mut FunctionEmitter<'_>,
2690 ctx: &CodegenCtx<'_>,
2691 class: &crate::MangledName,
2692 field: &str,
2693 result_ty: &Type,
2694 check_ty: &Type,
2695) -> Result<(), crate::compiler_error::CompilerFailure> {
2696 let _: () = match ctx.symbols.class_field_narrowing_check(class, field) {
2697 Some(check) => {
2698 crate::codegen::cast_check::emit_narrowed_field_read_as(
2699 emitter, ctx, check, result_ty, check_ty,
2700 )?;
2701 }
2702 None => crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?,
2703 };
2704 Ok(())
2705}
2706
2707fn emit_object_spread(
2708 emitter: &mut FunctionEmitter,
2709 ctx: &CodegenCtx,
2710 sources: &[TypedObjectMember],
2711 shape: &Type,
2712) -> Result<(), crate::compiler_error::CompilerFailure> {
2713 let intrinsics = ctx
2714 .symbols
2715 .intrinsic_type_indices()
2716 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2717 let merge = ctx
2718 .symbols
2719 .prelude_func_idx("ObjectConstructor##spread")
2720 .ok_or_else(|| crate::codegen::internal_failure("spread helper collected"))?;
2721 let null_object = Instruction::RefNull(HeapType::Concrete(intrinsics.object));
2722 emitter.instruction(null_object.clone());
2723 for (index, source) in sources.iter().enumerate() {
2724 let final_shape = (index + 1 == sources.len()).then_some(shape);
2725 emit_receiver(emitter, ctx, source.expr_id())?;
2729 let narrowed_ty = ctx
2730 .ta
2731 .source_type(source.expr_id())
2732 .map_err(crate::codegen::arena_failure)?;
2733 let non_object_source = if may_hold_non_object(narrowed_ty) {
2738 Some(emit_object_source_test(
2739 emitter,
2740 ctx,
2741 narrowed_ty,
2742 intrinsics.object,
2743 intrinsics.object_shape,
2744 )?)
2745 } else {
2746 None
2747 };
2748 let source_local =
2749 stash_receiver_as_object_shape(emitter, narrowed_ty, intrinsics.object_shape)?;
2750 emitter.instruction(Instruction::LocalGet(source_local));
2751 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2752 if matches!(source, TypedObjectMember::Spread { by_name: true, .. }) {
2753 emit_spread_mask(emitter, ctx, source_local, source.expr_id(), shape)?;
2754 } else {
2755 emitter.instruction(null_object.clone());
2756 }
2757 emitter.instruction(Instruction::Call(merge));
2758 if let Some(NonObjectSource {
2759 accumulator,
2760 opaque,
2761 }) = non_object_source
2762 {
2763 emitter.emit_else();
2764 emitter.instruction(Instruction::LocalGet(accumulator));
2765 match opaque {
2768 Some(source) => emitter.instruction(Instruction::LocalGet(source)),
2769 None => emitter.instruction(null_object.clone()),
2770 }
2771 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2772 emitter.instruction(null_object.clone());
2773 emitter.instruction(Instruction::Call(merge));
2774 emitter.emit_end();
2775 }
2776 }
2777 Ok(())
2778}
2779
2780fn emit_spread_shape_argument(
2783 emitter: &mut FunctionEmitter,
2784 ctx: &CodegenCtx,
2785 final_shape: Option<&Type>,
2786 null_object: &Instruction<'static>,
2787) {
2788 match final_shape {
2789 Some(shape) => emit_spread_shape(emitter, ctx, shape),
2790 None => emitter.instruction(null_object.clone()),
2791 }
2792}
2793
2794fn may_hold_non_object(ty: &Type) -> bool {
2798 match ty.lowered() {
2799 Type::Union(members) => members.iter().any(may_hold_non_object),
2800 Type::Null
2801 | Type::Undefined
2802 | Type::Boolean
2803 | Type::BooleanLiteral(_)
2804 | Type::Number
2805 | Type::NumberLiteral(_)
2806 | Type::String
2807 | Type::StringLiteral(_)
2808 | Type::Unknown => true,
2810 _ => false,
2811 }
2812}
2813
2814struct NonObjectSource {
2816 accumulator: u32,
2817 opaque: Option<u32>,
2820}
2821
2822fn emit_object_source_test(
2827 emitter: &mut FunctionEmitter,
2828 ctx: &CodegenCtx,
2829 source_ty: &Type,
2830 object: u32,
2831 object_shape: u32,
2832) -> Result<NonObjectSource, crate::compiler_error::CompilerFailure> {
2833 let source_type = match ctx.symbols.value_type(source_ty)? {
2834 ValType::Ref(reference) => ValType::Ref(RefType {
2835 nullable: true,
2836 ..reference
2837 }),
2838 other => other,
2839 };
2840 let opaque = source_type == object_ref(object);
2841 let source = emitter.add_anonymous_local(source_type)?;
2842 emitter.instruction(Instruction::LocalSet(source));
2843 let accumulator = emitter.add_anonymous_local(object_ref(object))?;
2844 emitter.instruction(Instruction::LocalSet(accumulator));
2845 emitter.instruction(Instruction::LocalGet(source));
2846 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
2847 object_shape,
2848 )));
2849 emitter.emit_if(BlockType::Result(ValType::Ref(RefType {
2850 nullable: false,
2851 heap_type: HeapType::Concrete(object_shape),
2852 })));
2853 emitter.instruction(Instruction::LocalGet(accumulator));
2854 emitter.instruction(Instruction::LocalGet(source));
2855 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
2856 object_shape,
2857 )));
2858 Ok(NonObjectSource {
2859 accumulator,
2860 opaque: opaque.then_some(source),
2861 })
2862}
2863
2864fn emit_spread_mask(
2868 emitter: &mut FunctionEmitter,
2869 ctx: &CodegenCtx,
2870 source: u32,
2871 source_expr: crate::ExprId,
2872 shape: &Type,
2873) -> Result<(), crate::compiler_error::CompilerFailure> {
2874 let checked = ctx
2875 .ta
2876 .spread_mask_fields
2877 .get(&source_expr)
2878 .ok_or_else(|| crate::codegen::internal_failure("by-name spread fields recorded"))?;
2879 let omitted = ctx.ta.spread_omitted_fields.get(&source_expr);
2882 let fields: Vec<(&String, Option<&Type>)> = checked
2883 .iter()
2884 .map(|(name, ty)| (name, Some(ty)))
2885 .chain(omitted.into_iter().flatten().map(|name| (name, None)))
2886 .collect();
2887 let intrinsics = ctx
2888 .symbols
2889 .intrinsic_type_indices()
2890 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2891 let vtable = ctx
2892 .symbols
2893 .vtable_global_idx(shape)
2894 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2895 emitter.instruction(Instruction::GlobalGet(vtable));
2896 for (name, _) in &fields {
2897 let global = ctx
2898 .symbols
2899 .field_name_string_global_idx(name)
2900 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2901 emitter.instruction(Instruction::GlobalGet(global));
2902 }
2903 emitter.instruction(Instruction::ArrayNewFixed {
2904 array_type_index: intrinsics.field_names,
2905 array_size: crate::codegen::wasm_u32(fields.len())?,
2906 });
2907 for (name, ty) in fields.iter().copied() {
2908 let global = ctx
2909 .symbols
2910 .field_name_string_global_idx(name)
2911 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2912 let Some(ty) = ty else {
2913 emitter.instruction(Instruction::GlobalGet(global));
2914 continue;
2915 };
2916 let index = emitter.add_anonymous_local(ValType::I32)?;
2917 let value = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
2918 emit_object_field_index_by_name(emitter, ctx, source, global);
2919 emitter.instruction(Instruction::LocalTee(index));
2920 emitter.instruction(Instruction::I32Const(0));
2921 emitter.instruction(Instruction::I32GeS);
2922 emitter.emit_if(BlockType::Result(ValType::I32));
2923 emit_field_slot_get(emitter, intrinsics, source, index);
2924 emitter.instruction(Instruction::LocalSet(value));
2925 emit_field_holds_value_of(emitter, ctx, source, index, value, ty)?;
2926 emitter.emit_else();
2927 emitter.instruction(Instruction::I32Const(0));
2928 emitter.emit_end();
2929 emitter.emit_if(BlockType::Result(object_ref(intrinsics.object)));
2930 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intrinsics.object)));
2931 emitter.emit_else();
2932 emitter.instruction(Instruction::GlobalGet(global));
2933 emitter.emit_end();
2934 }
2935 emitter.instruction(Instruction::ArrayNewFixed {
2936 array_type_index: intrinsics.object_fields,
2937 array_size: crate::codegen::wasm_u32(fields.len())?,
2938 });
2939 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2940 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2941 Ok(())
2942}
2943
2944fn emit_spread_shape(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, shape: &Type) {
2947 ctx.latch(emit_spread_shape_checked(emitter, ctx, shape));
2948}
2949
2950fn emit_spread_shape_checked(
2951 emitter: &mut FunctionEmitter,
2952 ctx: &CodegenCtx,
2953 shape: &Type,
2954) -> Result<(), crate::compiler_error::CompilerFailure> {
2955 let Type::Object { fields, .. } = shape else {
2956 return Err(crate::codegen::internal_failure("structural spread shape"));
2957 };
2958 let intrinsics = ctx
2959 .symbols
2960 .intrinsic_type_indices()
2961 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2962 let names: Vec<_> = fields
2963 .iter()
2964 .map(|(name, field)| crate::codegen::field_names::FieldName {
2965 name: name.clone(),
2966 optional: field.optional,
2967 is_accessor: false,
2968 is_private: false,
2969 })
2970 .collect();
2971 let names_global = ctx
2972 .symbols
2973 .field_names_global_idx(&names)
2974 .ok_or_else(|| crate::codegen::internal_failure("spread shape names collected"))?;
2975 let vtable = ctx
2976 .symbols
2977 .vtable_global_idx(shape)
2978 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2979 emitter.instruction(Instruction::GlobalGet(vtable));
2980 emitter.instruction(Instruction::GlobalGet(names_global));
2981 emitter.instruction(Instruction::I32Const(
2982 crate::codegen::wasm_u32(fields.len())? as i32,
2983 ));
2984 emitter.instruction(Instruction::ArrayNewDefault(intrinsics.object_fields));
2985 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2986 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2987
2988 Ok(())
2989}
2990
2991pub(super) fn stash_receiver_as_object_shape(
3007 emitter: &mut FunctionEmitter<'_>,
3008 receiver_ty: &Type,
3009 object_shape: u32,
3010) -> Result<u32, crate::compiler_error::CompilerFailure> {
3011 coerce_field_receiver_to_object_shape(emitter, receiver_ty, object_shape);
3012 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
3013 nullable: false,
3014 heap_type: HeapType::Concrete(object_shape),
3015 }))?;
3016 emitter.instruction(Instruction::LocalSet(rcv_local));
3017 Ok(rcv_local)
3018}
3019
3020fn coerce_field_receiver_to_object_shape(
3021 emitter: &mut FunctionEmitter<'_>,
3022 receiver_ty: &Type,
3023 object_shape: u32,
3024) {
3025 if crate::codegen::may_hold_builtin_value(receiver_ty) {
3026 emitter.instruction(Instruction::RefAsNonNull);
3027 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
3028 object_shape,
3029 )));
3030 }
3031}
3032
3033fn emit_chain_access(
3039 emitter: &mut FunctionEmitter<'_>,
3040 ctx: &CodegenCtx<'_>,
3041 part: &crate::TypedChainPart,
3042 receiver_ty: &Type,
3043 check_ty: &Type,
3044) -> Result<(), crate::compiler_error::CompilerFailure> {
3045 let _: () = match part {
3046 crate::TypedChainPart::Field {
3047 name, result_ty, ..
3048 } => {
3049 if ctx.ta.has_string_index(receiver_ty) {
3050 let Some(symbol) = ctx.require(
3051 ctx.symbols.field_name_string_global_idx(&name.name),
3052 "property name collected",
3053 ) else {
3054 return Ok(());
3055 };
3056 emitter.instruction(Instruction::GlobalGet(symbol));
3057 let Some(symbol) = ctx.require(
3058 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3059 "record read imported",
3060 ) else {
3061 return Ok(());
3062 };
3063 emitter.instruction(Instruction::Call(symbol));
3064 crate::codegen::cast_check::emit_checked_cast_on_stack(
3065 emitter,
3066 ctx,
3067 &Type::Unknown,
3068 check_ty,
3069 )?;
3070 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3071 return Ok(());
3072 }
3073 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
3074 crate::codegen::internal_failure("intrinsics declared by codegen entry")
3075 })?;
3076 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
3080 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
3081 emit_class_method_value(
3082 emitter,
3083 ctx,
3084 receiver_ty,
3085 &name.name,
3086 result_ty,
3087 check_ty,
3088 )?;
3089 return Ok(());
3090 }
3091 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
3092 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
3093 emit_class_vtable_dispatch(emitter, ctx, mangled, &getter, &[], result_ty)?;
3094 return Ok(());
3095 };
3096 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3097 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3098 })?;
3099 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
3100 nullable: false,
3101 heap_type: HeapType::Concrete(struct_idx),
3102 }))?;
3103 emitter.instruction(Instruction::LocalTee(object));
3104 emitter.instruction(Instruction::StructGet {
3105 struct_type_index: struct_idx,
3106 field_index: 2,
3107 });
3108 emitter.instruction(Instruction::I32Const(slot as i32));
3109 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3110 if ctx
3111 .symbols
3112 .class_field_narrowing_check(mangled, &name.name)
3113 .is_some()
3114 {
3115 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
3116 }
3117 emit_class_field_slot_cast_as(
3118 emitter, ctx, mangled, &name.name, result_ty, check_ty,
3119 )?;
3120 return Ok(());
3121 }
3122 let rcv_local =
3123 stash_receiver_as_object_shape(emitter, receiver_ty, intrinsics.object_shape)?;
3124 emit_object_property_read_as(emitter, ctx, rcv_local, &name.name, result_ty, check_ty)?;
3125 }
3126 crate::TypedChainPart::Index { idx, result_ty, .. } => {
3127 if receiver_ty.is_structural_object() {
3128 emit_expr(emitter, ctx, *idx)?;
3129 crate::codegen::cast_check::emit_operation_cast_on_stack(
3130 emitter,
3131 ctx,
3132 &ctx.ta
3133 .try_expr(*idx)
3134 .map_err(crate::codegen::arena_failure)?
3135 .ty,
3136 &Type::String,
3137 )?;
3138 let Some(symbol) = ctx.require(
3139 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3140 "record read imported",
3141 ) else {
3142 return Ok(());
3143 };
3144 emitter.instruction(Instruction::Call(symbol));
3145 crate::codegen::cast_check::emit_checked_cast_on_stack(
3146 emitter,
3147 ctx,
3148 &Type::Unknown,
3149 check_ty,
3150 )?;
3151 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3152 } else if receiver_ty.lowered() == &Type::Uint8Array {
3153 emit_uint8_index_with_receiver_on_stack(emitter, ctx, *idx)?;
3154 } else if receiver_ty.is_string_shaped() {
3155 emit_string_index_with_receiver_on_stack(emitter, ctx, *idx)?;
3156 } else {
3157 let source_ty = match receiver_ty.lowered() {
3158 Type::Array(element) => element.as_ref(),
3159 Type::Tuple(elements) => match &ctx
3160 .ta
3161 .try_expr(*idx)
3162 .map_err(crate::codegen::arena_failure)?
3163 .kind
3164 {
3165 TypedExprKind::Number(index) => {
3166 elements.get(*index as usize).unwrap_or(result_ty)
3167 }
3168 _ => result_ty,
3169 },
3170 _ => result_ty,
3171 };
3172 emit_bounds_checked_index_with_receiver_on_stack(
3173 emitter,
3174 ctx,
3175 *idx,
3176 source_ty,
3177 is_optional_tuple_position(ctx, receiver_ty, *idx)?,
3178 )?;
3179 if source_ty != result_ty {
3180 crate::codegen::cast_check::emit_checked_cast_on_stack(
3181 emitter, ctx, source_ty, result_ty,
3182 )?;
3183 }
3184 }
3185 }
3186 crate::TypedChainPart::InterfaceProperty { iface, name, .. } => {
3187 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
3198 emit_inline_length(emitter, ctx, struct_idx);
3199 return Ok(());
3200 }
3201 let key = crate::mangle::extend(iface, &name.name);
3202 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
3203 crate::codegen::internal_failure(
3204 "interface property getter recorded during the dependency-import pass",
3205 )
3206 })?;
3207 if matches!(receiver_ty.lowered(), Type::InterfaceRef { .. }) {
3208 emitter.instruction(Instruction::RefAsNonNull);
3209 }
3210 emitter.instruction(Instruction::Call(func_idx));
3211 }
3212 crate::TypedChainPart::Call {
3213 args,
3214 union_signature,
3215 ..
3216 } => {
3217 if let Some(signature) = union_signature
3224 && crate::codegen::closure_coercions::is_union_call_cast(receiver_ty, signature)
3225 {
3226 crate::codegen::closure_coercions::emit_union_call_cast(emitter, ctx, signature)?;
3227 }
3228 emit_indirect_closure_call_with_receiver_on_stack(
3229 emitter,
3230 ctx,
3231 union_signature.as_ref().unwrap_or(receiver_ty),
3232 args,
3233 part.result_ty(),
3234 )?;
3235 }
3236 crate::TypedChainPart::MethodCall {
3237 iface,
3238 name,
3239 args,
3240 result_ty,
3241 ..
3242 } => {
3243 emit_method_call_with_receiver_on_stack(
3253 emitter,
3254 ctx,
3255 receiver_ty,
3256 iface,
3257 &name.name,
3258 args,
3259 None,
3260 None,
3261 result_ty,
3262 )?;
3263 }
3264 crate::TypedChainPart::NonNull { result_ty, .. } => {
3265 crate::codegen::cast_check::emit_non_null_assert_on_stack(
3266 emitter,
3267 ctx,
3268 receiver_ty,
3269 result_ty,
3270 )?;
3271 }
3272 };
3273 Ok(())
3274}
3275
3276fn emit_captured_load(
3287 emitter: &mut FunctionEmitter,
3288 _ctx: &CodegenCtx,
3289 c: &crate::CapturedVar,
3290) -> Result<(), crate::compiler_error::CompilerFailure> {
3291 let slot = emitter.local_slot(&c.name.name).ok_or_else(|| {
3292 crate::codegen::internal_failure("captured binding is missing from the outer scope")
3293 })?;
3294 emitter.instruction(Instruction::LocalGet(slot));
3295 Ok(())
3296}
3297
3298fn emit_binary(
3299 emitter: &mut FunctionEmitter,
3300 ctx: &CodegenCtx,
3301 op: BinOp,
3302 lhs: ExprId,
3303 rhs: ExprId,
3304 result_ty: &Type,
3305) -> Result<(), crate::compiler_error::CompilerFailure> {
3306 let admits_never_operand = matches!(
3307 op,
3308 BinOp::Add
3309 | BinOp::Sub
3310 | BinOp::Mul
3311 | BinOp::Div
3312 | BinOp::Rem
3313 | BinOp::Pow
3314 | BinOp::Lt
3315 | BinOp::Gt
3316 | BinOp::Le
3317 | BinOp::Ge
3318 );
3319 if admits_never_operand && try_emit_unreachable_for_never_operand(emitter, ctx, &[lhs, rhs])? {
3320 return Ok(());
3321 }
3322 let _: () = match op {
3323 BinOp::BitAnd
3328 | BinOp::BitOr
3329 | BinOp::BitXor
3330 | BinOp::Shl
3331 | BinOp::Shr
3332 | BinOp::UnsignedShr => {
3333 if has_numeric_operands(ctx, &[lhs, rhs], result_ty)? {
3334 emit_number_bitwise(emitter, ctx, op, lhs, rhs)?;
3335 } else {
3336 let name = op.bitwise_name().ok_or_else(|| {
3337 crate::codegen::internal_failure("missing bitwise host operation")
3338 })?;
3339 emit_bitwise_host(emitter, ctx, name, &[lhs, rhs], result_ty)?;
3340 }
3341 }
3342 BinOp::Add => match &result_ty.widen_literal() {
3345 Type::Number => {
3346 emit_primitive_operand(emitter, ctx, lhs)?;
3347 emit_primitive_operand(emitter, ctx, rhs)?;
3348 emitter.instruction(Instruction::F64Add);
3349 }
3350 Type::String => {
3351 emit_primitive_operand(emitter, ctx, lhs)?;
3352 emit_primitive_operand(emitter, ctx, rhs)?;
3353 let idx = ctx
3354 .symbols
3355 .prelude_func_idx("string_concat")
3356 .ok_or_else(|| {
3357 crate::codegen::internal_failure("string_concat imported from prelude")
3358 })?;
3359 emitter.instruction(Instruction::Call(idx));
3360 }
3361 Type::BigInt => emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "add")?,
3363 other => {
3364 return Err(crate::codegen::internal_failure(format!(
3365 "typechecker rejects `+` for {other:?}"
3366 )));
3367 }
3368 },
3369 BinOp::Sub | BinOp::Mul | BinOp::Div => {
3370 if result_ty.is_bigint() {
3372 let name = match op {
3373 BinOp::Sub => "sub",
3374 BinOp::Mul => "mul",
3375 BinOp::Div => "div",
3376 _ => {
3377 return Err(crate::codegen::internal_failure(
3378 "invalid compiler emission dispatch",
3379 ));
3380 }
3381 };
3382 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, name)?;
3383 return Ok(());
3384 }
3385 if !matches!(result_ty, Type::Number) {
3386 return Err(crate::codegen::internal_failure(
3387 "numeric operator has a non-number result type",
3388 ));
3389 }
3390 emit_primitive_operand(emitter, ctx, lhs)?;
3391 emit_primitive_operand(emitter, ctx, rhs)?;
3392 let inst = match op {
3393 BinOp::Sub => Instruction::F64Sub,
3394 BinOp::Mul => Instruction::F64Mul,
3395 BinOp::Div => Instruction::F64Div,
3396 _ => {
3397 return Err(crate::codegen::internal_failure(
3398 "invalid compiler emission dispatch",
3399 ));
3400 }
3401 };
3402 emitter.instruction(inst);
3403 }
3404 BinOp::Pow => {
3405 if result_ty.is_bigint() {
3410 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "pow")?;
3411 return Ok(());
3412 }
3413 if !matches!(result_ty, Type::Number) {
3414 return Err(crate::codegen::internal_failure(
3415 "numeric operator has a non-number result type",
3416 ));
3417 }
3418 emit_primitive_operand(emitter, ctx, lhs)?;
3419 emit_primitive_operand(emitter, ctx, rhs)?;
3420 let idx = ctx
3421 .symbols
3422 .func_idx(&crate::runtime::prelude::math::math_key("pow"))
3423 .ok_or_else(|| crate::codegen::internal_failure("Math#pow host import recorded"))?;
3424 emitter.instruction(Instruction::Call(idx));
3425 }
3426 BinOp::Rem => {
3427 if result_ty.is_bigint() {
3429 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "mod")?;
3430 return Ok(());
3431 }
3432 let a = emitter.add_anonymous_local(ValType::F64)?;
3436 let b = emitter.add_anonymous_local(ValType::F64)?;
3437 emit_primitive_operand(emitter, ctx, lhs)?;
3438 emitter.instruction(Instruction::LocalSet(a));
3439 emit_primitive_operand(emitter, ctx, rhs)?;
3440 emitter.instruction(Instruction::LocalSet(b));
3441 emitter.instruction(Instruction::LocalGet(a));
3443 emitter.instruction(Instruction::LocalGet(a));
3444 emitter.instruction(Instruction::LocalGet(b));
3445 emitter.instruction(Instruction::F64Div);
3446 emitter.instruction(Instruction::F64Trunc);
3447 emitter.instruction(Instruction::LocalGet(b));
3448 emitter.instruction(Instruction::F64Mul);
3449 emitter.instruction(Instruction::F64Sub);
3450 }
3451 BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
3452 let operand_ty = ctx
3455 .ta
3456 .try_expr(lhs)
3457 .map_err(crate::codegen::arena_failure)?
3458 .ty
3459 .primitive_behavior()
3460 .clone();
3461 if operand_ty.is_bigint() {
3462 emit_bigint_cmp_inline(emitter, ctx, lhs, rhs, op)?;
3463 return Ok(());
3464 }
3465 emit_primitive_operand(emitter, ctx, lhs)?;
3466 emit_primitive_operand(emitter, ctx, rhs)?;
3467 if matches!(operand_ty, Type::String | Type::StringLiteral(_)) {
3468 let idx = ctx.symbols.prelude_func_idx("string_cmp").ok_or_else(|| {
3469 crate::codegen::internal_failure("string_cmp imported from prelude")
3470 })?;
3471 emitter.instruction(Instruction::Call(idx));
3472 emitter.instruction(Instruction::I32Const(0));
3473 emitter.instruction(match op {
3474 BinOp::Lt => Instruction::I32LtS,
3475 BinOp::Gt => Instruction::I32GtS,
3476 BinOp::Le => Instruction::I32LeS,
3477 BinOp::Ge => Instruction::I32GeS,
3478 _ => {
3479 return Err(crate::codegen::internal_failure(
3480 "invalid compiler emission dispatch",
3481 ));
3482 }
3483 });
3484 } else {
3485 emitter.instruction(match op {
3486 BinOp::Lt => Instruction::F64Lt,
3487 BinOp::Gt => Instruction::F64Gt,
3488 BinOp::Le => Instruction::F64Le,
3489 BinOp::Ge => Instruction::F64Ge,
3490 _ => {
3491 return Err(crate::codegen::internal_failure(
3492 "invalid compiler emission dispatch",
3493 ));
3494 }
3495 });
3496 }
3497 }
3498 BinOp::Eq | BinOp::NotEq => emit_equality(emitter, ctx, op, lhs, rhs)?,
3499 BinOp::And | BinOp::Or => emit_logical(emitter, ctx, op, lhs, rhs, result_ty)?,
3500 BinOp::In => emit_in_operator(emitter, ctx, lhs, rhs)?,
3501 BinOp::NullishCoalesce => {
3505 return Err(crate::codegen::internal_failure(
3506 "BinOp::NullishCoalesce should be lifted into TypedExprKind::NullishCoalesce by infer",
3507 ));
3508 }
3509 };
3510 Ok(())
3511}
3512
3513fn emit_equality(
3516 emitter: &mut FunctionEmitter,
3517 ctx: &CodegenCtx,
3518 op: BinOp,
3519 lhs: ExprId,
3520 rhs: ExprId,
3521) -> Result<(), crate::compiler_error::CompilerFailure> {
3522 let lhs_is_null_lit = matches!(
3529 ctx.ta
3530 .try_expr(lhs)
3531 .map_err(crate::codegen::arena_failure)?
3532 .kind,
3533 TypedExprKind::Null
3534 );
3535 let rhs_is_null_lit = matches!(
3536 ctx.ta
3537 .try_expr(rhs)
3538 .map_err(crate::codegen::arena_failure)?
3539 .kind,
3540 TypedExprKind::Null
3541 );
3542 if lhs_is_null_lit && rhs_is_null_lit {
3543 let v: i32 = if matches!(op, BinOp::Eq) { 1 } else { 0 };
3544 emitter.instruction(Instruction::I32Const(v));
3545 return Ok(());
3546 }
3547 if lhs_is_null_lit || rhs_is_null_lit {
3548 let non_null_side = if lhs_is_null_lit { rhs } else { lhs };
3549 let non_null_ty = ctx
3550 .ta
3551 .try_expr(non_null_side)
3552 .map_err(crate::codegen::arena_failure)?
3553 .ty
3554 .clone();
3555 let non_null_val = ctx.symbols.value_type(&non_null_ty)?;
3556 if matches!(non_null_val, ValType::F64 | ValType::I32) {
3564 emit_expr(emitter, ctx, non_null_side)?;
3565 emitter.instruction(Instruction::Drop);
3566 let v: i32 = if matches!(op, BinOp::NotEq) { 1 } else { 0 };
3567 emitter.instruction(Instruction::I32Const(v));
3568 return Ok(());
3569 }
3570 emit_expr(emitter, ctx, non_null_side)?;
3571 emitter.instruction(Instruction::RefIsNull);
3572 if matches!(op, BinOp::NotEq) {
3573 emitter.instruction(Instruction::I32Eqz);
3574 }
3575 return Ok(());
3576 }
3577 let lhs_ty = ctx
3578 .ta
3579 .try_expr(lhs)
3580 .map_err(crate::codegen::arena_failure)?
3581 .ty
3582 .clone();
3583 let rhs_ty = ctx
3584 .ta
3585 .try_expr(rhs)
3586 .map_err(crate::codegen::arena_failure)?
3587 .ty
3588 .clone();
3589 let operand_val = ctx.symbols.value_type(lhs_ty.primitive_behavior())?;
3590 if may_hold_null(&lhs_ty)
3596 || may_hold_null(&rhs_ty)
3597 || operand_val != ctx.symbols.value_type(rhs_ty.primitive_behavior())?
3598 {
3599 emit_boxed_eq(emitter, ctx, lhs, rhs, op)?;
3600 return Ok(());
3601 }
3602 if lhs_ty.is_bigint() {
3606 emit_bigint_cmp_eq_inline(emitter, ctx, lhs, rhs, op)?;
3607 return Ok(());
3608 }
3609 let string_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
3610 crate::codegen::internal_failure("string type registered with intrinsics")
3611 })?;
3612 match operand_val {
3618 ValType::F64 => {
3619 emit_primitive_operand(emitter, ctx, lhs)?;
3620 emit_primitive_operand(emitter, ctx, rhs)?;
3621 emitter.instruction(if matches!(op, BinOp::Eq) {
3622 Instruction::F64Eq
3623 } else {
3624 Instruction::F64Ne
3625 });
3626 }
3627 ValType::I32 => {
3628 emit_expr(emitter, ctx, lhs)?;
3629 emit_expr(emitter, ctx, rhs)?;
3630 emitter.instruction(if matches!(op, BinOp::Eq) {
3631 Instruction::I32Eq
3632 } else {
3633 Instruction::I32Ne
3634 });
3635 }
3636 ValType::Ref(RefType {
3637 heap_type: HeapType::Concrete(idx),
3638 ..
3639 }) if idx == string_idx => {
3640 emit_expr(emitter, ctx, lhs)?;
3641 emit_expr(emitter, ctx, rhs)?;
3642 let func = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
3643 crate::codegen::internal_failure("string_eq imported from prelude")
3644 })?;
3645 emitter.instruction(Instruction::Call(func));
3646 if matches!(op, BinOp::NotEq) {
3647 emitter.instruction(Instruction::I32Eqz);
3648 }
3649 }
3650 ValType::Ref(_) => {
3651 emit_expr(emitter, ctx, lhs)?;
3662 cast::emit_box(emitter, ctx, &lhs_ty)?;
3663 emit_expr(emitter, ctx, rhs)?;
3664 cast::emit_box(emitter, ctx, &rhs_ty)?;
3665 emit_vtable_equality(emitter, ctx, op);
3666 }
3667 other => {
3668 return Err(crate::codegen::internal_failure(format!(
3669 "typechecker rejects equality on Wasm value-type `{other:?}`"
3670 )));
3671 }
3672 }
3673 Ok(())
3674}
3675
3676fn emit_logical(
3677 emitter: &mut FunctionEmitter,
3678 ctx: &CodegenCtx,
3679 op: BinOp,
3680 lhs: ExprId,
3681 rhs: ExprId,
3682 result_ty: &Type,
3683) -> Result<(), crate::compiler_error::CompilerFailure> {
3684 let result_val = ctx.symbols.value_type(result_ty)?;
3690 let lhs_ty = ctx
3691 .ta
3692 .try_expr(lhs)
3693 .map_err(crate::codegen::arena_failure)?
3694 .ty
3695 .clone();
3696 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
3697 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
3698 let tmp = emitter.add_anonymous_local(lhs_val)?;
3699 emit_expr(emitter, ctx, lhs)?;
3700 emitter.instruction(Instruction::LocalTee(tmp));
3701 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &lhs_ty)?;
3702 let emit_rhs_branch =
3703 |emitter: &mut FunctionEmitter| -> Result<(), crate::compiler_error::CompilerFailure> {
3704 let rhs_ty = ctx
3705 .ta
3706 .try_expr(rhs)
3707 .map_err(crate::codegen::arena_failure)?
3708 .ty
3709 .clone();
3710 emit_expr(emitter, ctx, rhs)?;
3711 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3712 emitter, ctx, &rhs_ty, result_ty,
3713 )?;
3714 Ok(())
3715 };
3716 let kept_lhs_ty = match op {
3720 BinOp::And => falsy_part(&lhs_ty),
3721 _ => truthy_part(&lhs_ty),
3722 };
3723 let lhs_is_never_kept = matches!(kept_lhs_ty.lowered(), Type::Never);
3724 let emit_kept_lhs_branch = |emitter: &mut FunctionEmitter| {
3725 if lhs_is_never_kept {
3726 emitter.instruction(Instruction::Unreachable);
3727 return Ok(());
3728 }
3729 emitter.instruction(Instruction::LocalGet(tmp));
3730 if lhs_is_ref {
3731 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?;
3735 } else {
3736 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3737 emitter, ctx, &lhs_ty, result_ty,
3738 )?;
3739 }
3740 Ok::<(), crate::compiler_error::CompilerFailure>(())
3741 };
3742 emitter.emit_if(BlockType::Result(result_val));
3743 match op {
3744 BinOp::And => emit_rhs_branch(emitter)?,
3745 _ => emit_kept_lhs_branch(emitter)?,
3746 }
3747 emitter.emit_else();
3748 match op {
3749 BinOp::And => emit_kept_lhs_branch(emitter)?,
3750 _ => emit_rhs_branch(emitter)?,
3751 }
3752 emitter.emit_end();
3753 Ok(())
3754}
3755
3756fn try_emit_unreachable_for_never_operand(
3761 emitter: &mut FunctionEmitter,
3762 ctx: &CodegenCtx,
3763 operands: &[ExprId],
3764) -> Result<bool, crate::compiler_error::CompilerFailure> {
3765 let mut has_never_operand = false;
3766 for &operand in operands {
3767 let ty = &ctx
3768 .ta
3769 .try_expr(operand)
3770 .map_err(crate::codegen::arena_failure)?
3771 .ty;
3772 has_never_operand |= matches!(ty.lowered(), Type::Never);
3773 }
3774 if !has_never_operand {
3775 return Ok(false);
3776 }
3777 for &operand in operands {
3778 emit_expr(emitter, ctx, operand)?;
3779 emitter.instruction(Instruction::Drop);
3780 }
3781 emitter.instruction(Instruction::Unreachable);
3782 Ok(true)
3783}
3784
3785fn emit_primitive_operand(
3786 emitter: &mut FunctionEmitter,
3787 ctx: &CodegenCtx,
3788 expr: ExprId,
3789) -> Result<(), crate::compiler_error::CompilerFailure> {
3790 emit_expr(emitter, ctx, expr)?;
3791 let ty = &ctx
3792 .ta
3793 .try_expr(expr)
3794 .map_err(crate::codegen::arena_failure)?
3795 .ty;
3796 let _: () = if matches!(ty.primitive_behavior(), Type::Number)
3797 && ctx.symbols.value_type(ty)? != ValType::F64
3798 {
3799 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
3800 };
3801 Ok(())
3802}
3803
3804fn emit_in_operator(
3812 emitter: &mut FunctionEmitter,
3813 ctx: &CodegenCtx,
3814 lhs: ExprId,
3815 rhs: ExprId,
3816) -> Result<(), crate::compiler_error::CompilerFailure> {
3817 emit_expr(emitter, ctx, lhs)?;
3818 crate::codegen::cast_check::emit_operation_cast_on_stack(
3819 emitter,
3820 ctx,
3821 &ctx.ta
3822 .try_expr(lhs)
3823 .map_err(crate::codegen::arena_failure)?
3824 .ty,
3825 &Type::String,
3826 )?;
3827 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
3828 emitter.instruction(Instruction::LocalSet(key));
3829 emit_expr(emitter, ctx, rhs)?;
3830 cast::emit_box(
3831 emitter,
3832 ctx,
3833 &ctx.ta
3834 .try_expr(rhs)
3835 .map_err(crate::codegen::arena_failure)?
3836 .ty,
3837 )?;
3838 emitter.instruction(Instruction::LocalGet(key));
3839 let Some(symbol) = ctx.require(
3840 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
3841 "presence helper imported",
3842 ) else {
3843 return Ok(());
3844 };
3845 emitter.instruction(Instruction::Call(symbol));
3846 Ok(())
3847}
3848
3849pub(crate) fn emit_class_field_setup(
3854 emitter: &mut FunctionEmitter,
3855 ctx: &CodegenCtx,
3856 mangled: &crate::MangledName,
3857) -> Result<(), crate::compiler_error::CompilerFailure> {
3858 emit_class_field_setup_phase(emitter, ctx, mangled, FieldSetupPhase::All)
3859}
3860
3861#[derive(Clone, Copy)]
3862pub(crate) enum FieldSetupPhase {
3863 All,
3864 Initializers,
3865 ParameterProperties,
3866}
3867
3868pub(crate) fn emit_class_field_setup_phase(
3869 emitter: &mut FunctionEmitter,
3870 ctx: &CodegenCtx,
3871 mangled: &crate::MangledName,
3872 phase: FieldSetupPhase,
3873) -> Result<(), crate::compiler_error::CompilerFailure> {
3874 use crate::codegen::symbol_table::FieldSetup;
3875 let setup: Vec<FieldSetup> = match ctx.symbols.class_field_setup(mangled) {
3876 Some(s) if !s.is_empty() => s.to_vec(),
3877 _ => return Ok(()),
3878 };
3879 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3880 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3881 })?;
3882 let intrinsics = ctx
3883 .symbols
3884 .intrinsic_type_indices()
3885 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3886 let this = emitter.this_local().ok_or_else(|| {
3887 crate::codegen::internal_failure("field setup runs inside a constructor init fn")
3888 })?;
3889 for step in &setup {
3890 let parameter_property = matches!(step, FieldSetup::ParamCopy { .. });
3891 if matches!(phase, FieldSetupPhase::Initializers) && parameter_property
3892 || matches!(phase, FieldSetupPhase::ParameterProperties) && !parameter_property
3893 {
3894 continue;
3895 }
3896 let field = match step {
3897 FieldSetup::Init { field, .. }
3898 | FieldSetup::ParamCopy { field, .. }
3899 | FieldSetup::Reset { field } => field,
3900 };
3901 let slot = ctx
3902 .symbols
3903 .class_field_slot(mangled, field)
3904 .ok_or_else(|| {
3905 crate::codegen::internal_failure("class field slot recorded in classes::emit")
3906 })?;
3907 match step {
3908 FieldSetup::Init { value, .. } => {
3909 let value_ty = ctx
3910 .ta
3911 .try_expr(*value)
3912 .map_err(crate::codegen::arena_failure)?
3913 .ty
3914 .clone();
3915 emit_expr(emitter, ctx, *value)?;
3916 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &value_ty)?;
3917 }
3918 FieldSetup::ParamCopy { param, .. } => {
3919 let local = emitter.require_local_slot(¶m.name.name)?;
3920 if param.boxed {
3921 let box_type = ctx.symbols.box_type_idx(¶m.ty)?.ok_or_else(|| {
3922 crate::codegen::internal_failure(
3923 "constructor parameter cell is not registered",
3924 )
3925 })?;
3926 super::binding_cells::read(emitter, ctx, local, box_type)?;
3927 cast::emit_unerase(emitter, ctx, ¶m.ty)?;
3928 } else {
3929 emitter.instruction(Instruction::LocalGet(local));
3930 }
3931 cast::emit_box(emitter, ctx, ¶m.ty)?;
3932 }
3933 FieldSetup::Reset { .. } => {
3934 emit_undefined(emitter, ctx)?;
3935 }
3936 }
3937 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
3938 emitter.instruction(Instruction::LocalSet(stored));
3939 emitter.instruction(Instruction::LocalGet(this));
3940 emitter.instruction(Instruction::StructGet {
3941 struct_type_index: struct_idx,
3942 field_index: 2,
3943 });
3944 emitter.instruction(Instruction::I32Const(slot as i32));
3945 emitter.instruction(Instruction::LocalGet(stored));
3946 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
3947 let index = emitter.add_anonymous_local(ValType::I32)?;
3948 emitter.instruction(Instruction::I32Const(slot as i32));
3949 emitter.instruction(Instruction::LocalSet(index));
3950 crate::codegen::field_names::emit_mark_present(emitter, ctx, this, index)?;
3951 }
3952 Ok(())
3953}
3954
3955pub(crate) fn emit_field_slot_get(
3959 emitter: &mut FunctionEmitter,
3960 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
3961 object_local: u32,
3962 index_local: u32,
3963) {
3964 emitter.instruction(Instruction::LocalGet(object_local));
3965 emitter.instruction(Instruction::StructGet {
3966 struct_type_index: intrinsics.object_shape,
3967 field_index: 2,
3968 });
3969 emitter.instruction(Instruction::LocalGet(index_local));
3970 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3971}
3972
3973pub(crate) fn emit_object_field_read_by_name(
3974 emitter: &mut FunctionEmitter,
3975 ctx: &CodegenCtx,
3976 object_local: u32,
3977 name_global: u32,
3978) {
3979 ctx.latch(emit_object_field_read_by_name_checked(
3980 emitter,
3981 ctx,
3982 object_local,
3983 name_global,
3984 ));
3985}
3986
3987fn emit_object_field_read_by_name_checked(
3988 emitter: &mut FunctionEmitter,
3989 ctx: &CodegenCtx,
3990 object_local: u32,
3991 name_global: u32,
3992) -> Result<(), crate::compiler_error::CompilerFailure> {
3993 let intrinsics = ctx
3994 .symbols
3995 .intrinsic_type_indices()
3996 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3997 let Some(index_local) = ctx.latch(emitter.add_anonymous_local(ValType::I32)) else {
3998 return Ok(());
3999 };
4000 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4001 emitter.instruction(Instruction::LocalSet(index_local));
4002 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
4003 nullable: true,
4004 heap_type: HeapType::Concrete(intrinsics.object),
4005 })));
4006 emitter.instruction(Instruction::LocalGet(index_local));
4007 emitter.instruction(Instruction::I32Const(0));
4008 emitter.instruction(Instruction::I32LtS);
4009 emitter.emit_if(BlockType::Empty);
4010 emit_undefined(emitter, ctx)?;
4011 emitter.instruction(Instruction::Br(1));
4012 emitter.emit_end();
4013 emitter.instruction(Instruction::LocalGet(object_local));
4014 emitter.instruction(Instruction::StructGet {
4015 struct_type_index: intrinsics.object_shape,
4016 field_index: 2,
4017 });
4018 emitter.instruction(Instruction::LocalGet(index_local));
4019 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
4020 emitter.emit_end();
4021
4022 Ok(())
4023}
4024
4025pub(crate) fn emit_object_field_write_by_name(
4026 emitter: &mut FunctionEmitter,
4027 ctx: &CodegenCtx,
4028 object_local: u32,
4029 name_global: u32,
4030) -> Result<(), crate::compiler_error::CompilerFailure> {
4031 let intrinsics = ctx
4032 .symbols
4033 .intrinsic_type_indices()
4034 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4035 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4036 nullable: true,
4037 heap_type: HeapType::Concrete(intrinsics.object),
4038 }))?;
4039 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4040 emitter.instruction(Instruction::LocalSet(value_local));
4041 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4042 emitter.instruction(Instruction::LocalTee(index_local));
4043 emitter.instruction(Instruction::I32Const(0));
4044 emitter.instruction(Instruction::I32GeS);
4045 emitter.emit_if(BlockType::Empty);
4046 emitter.instruction(Instruction::LocalGet(object_local));
4047 emitter.instruction(Instruction::StructGet {
4048 struct_type_index: intrinsics.object_shape,
4049 field_index: 2,
4050 });
4051 emitter.instruction(Instruction::LocalGet(index_local));
4052 emitter.instruction(Instruction::LocalGet(value_local));
4053 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
4054 crate::codegen::field_names::emit_mark_present(emitter, ctx, object_local, index_local)?;
4055 emitter.emit_else();
4056 emitter.instruction(Instruction::LocalGet(object_local));
4057 emitter.instruction(Instruction::GlobalGet(name_global));
4058 emitter.instruction(Instruction::LocalGet(value_local));
4059 let insert = ctx
4060 .symbols
4061 .prelude_func_idx("ObjectConstructor##insertField")
4062 .ok_or_else(|| {
4063 crate::codegen::internal_failure("field insertion helper is not imported")
4064 })?;
4065 emitter.instruction(Instruction::Call(insert));
4066 emitter.emit_end();
4067 Ok(())
4068}
4069
4070pub(crate) fn accessor_branch_emittable(ctx: &CodegenCtx, getter_name: &str) -> bool {
4077 ctx.symbols
4078 .field_name_string_global_idx(getter_name)
4079 .is_some()
4080}
4081
4082pub(crate) fn emit_is_accessor_backed(
4099 emitter: &mut FunctionEmitter,
4100 ctx: &CodegenCtx,
4101 object_local: u32,
4102 accessor_name: &str,
4103 kind: crate::AccessorKind,
4104) -> Result<u32, crate::compiler_error::CompilerFailure> {
4105 let intrinsics = ctx
4106 .symbols
4107 .intrinsic_type_indices()
4108 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4109 let accessor_global = ctx
4110 .symbols
4111 .field_name_string_global_idx(accessor_name)
4112 .ok_or_else(|| {
4113 crate::codegen::internal_failure("caller checked the accessor name has a string global")
4114 })?;
4115 let closure_struct_idx = ctx
4116 .symbols
4117 .closure_struct_type_idx(crate::codegen::classes::accessor_closure_sig(kind))
4118 .ok_or_else(|| {
4119 crate::codegen::internal_failure(
4120 "accessor closure struct collected by analysis::note_shaped_property_access",
4121 )
4122 })?;
4123 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4124 emit_field_index_by_name(emitter, ctx, object_local, accessor_global, true);
4125 emitter.instruction(Instruction::LocalTee(index_local));
4126 emitter.instruction(Instruction::I32Const(0));
4127 emitter.instruction(Instruction::I32GeS);
4128 emitter.instruction(Instruction::LocalGet(object_local));
4129 emitter.instruction(Instruction::StructGet {
4130 struct_type_index: intrinsics.object,
4131 field_index: 0,
4132 });
4133 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4134 intrinsics.class_vtable,
4135 )));
4136 emitter.instruction(Instruction::I32And);
4137 emitter.emit_if(BlockType::Result(ValType::I32));
4138 emitter.instruction(Instruction::LocalGet(object_local));
4139 emitter.instruction(Instruction::StructGet {
4140 struct_type_index: intrinsics.object_shape,
4141 field_index: 1,
4142 });
4143 emitter.instruction(Instruction::LocalGet(index_local));
4144 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
4145 crate::codegen::field_names::emit_name_is_accessor(emitter, ctx)?;
4146 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4147 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4148 closure_struct_idx,
4149 )));
4150 emitter.instruction(Instruction::I32And);
4151 emitter.emit_else();
4152 emitter.instruction(Instruction::I32Const(0));
4153 emitter.emit_end();
4154 Ok(index_local)
4155}
4156
4157pub(crate) fn emit_object_property_read(
4167 emitter: &mut FunctionEmitter,
4168 ctx: &CodegenCtx,
4169 object_local: u32,
4170 prop_name: &str,
4171 field_ty: &Type,
4172) -> Result<(), crate::compiler_error::CompilerFailure> {
4173 emit_object_property_read_as(emitter, ctx, object_local, prop_name, field_ty, field_ty)?;
4174 Ok(())
4175}
4176
4177fn emit_object_property_read_as(
4178 emitter: &mut FunctionEmitter,
4179 ctx: &CodegenCtx,
4180 object_local: u32,
4181 prop_name: &str,
4182 field_ty: &Type,
4183 check_ty: &Type,
4184) -> Result<(), crate::compiler_error::CompilerFailure> {
4185 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4186 let name_global = ctx
4187 .symbols
4188 .field_name_string_global_idx(prop_name)
4189 .ok_or_else(|| {
4190 crate::codegen::internal_failure(
4191 "per-name string global recorded during field-name-strings emission",
4192 )
4193 })?;
4194 if !accessor_branch_emittable(ctx, &getter) {
4195 emit_object_field_read_by_name(emitter, ctx, object_local, name_global);
4196 emit_dynamic_narrowed_property_read(
4197 emitter,
4198 ctx,
4199 object_local,
4200 prop_name,
4201 field_ty,
4202 check_ty,
4203 )?;
4204 return Ok(());
4205 }
4206 let intrinsics = ctx
4207 .symbols
4208 .intrinsic_type_indices()
4209 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4210 let result_vt = ctx.symbols.value_type(field_ty)?;
4211 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4212 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4213 emitter.instruction(Instruction::LocalSet(index_local));
4214 emitter.instruction(Instruction::LocalGet(index_local));
4215 emitter.instruction(Instruction::I32Const(0));
4216 emitter.instruction(Instruction::I32GeS);
4217 emitter.emit_if(BlockType::Result(result_vt));
4218 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4220 emit_dynamic_narrowed_property_read(emitter, ctx, object_local, prop_name, field_ty, check_ty)?;
4221 emitter.emit_else();
4222 let getter_slot_local = emit_is_accessor_backed(
4223 emitter,
4224 ctx,
4225 object_local,
4226 &getter,
4227 crate::AccessorKind::Get,
4228 )?;
4229 emitter.emit_if(BlockType::Result(result_vt));
4230 emitter.instruction(Instruction::LocalGet(object_local));
4232 emit_interface_method_via_shape_with_receiver_on_stack(
4233 emitter,
4234 ctx,
4235 &getter,
4236 &[],
4237 field_ty,
4238 Some(getter_slot_local),
4239 )?;
4240 emitter.emit_else();
4241 emit_undefined(emitter, ctx)?;
4247 cast::emit_cast_to(emitter, ctx, field_ty)?;
4248 emitter.emit_end();
4249 emitter.emit_end();
4250 Ok(())
4251}
4252
4253fn emit_dynamic_narrowed_property_read(
4254 emitter: &mut FunctionEmitter,
4255 ctx: &CodegenCtx,
4256 object_local: u32,
4257 prop_name: &str,
4258 field_ty: &Type,
4259 check_ty: &Type,
4260) -> Result<(), crate::compiler_error::CompilerFailure> {
4261 let checks: Vec<_> = ctx
4262 .symbols
4263 .class_field_narrowing_checks(prop_name)
4264 .filter_map(|(class, check)| {
4265 ctx.symbols
4266 .class_vtable_global_idx(class)
4267 .map(|vtable| (vtable, check))
4268 })
4269 .collect();
4270 if checks.is_empty() {
4271 cast::emit_cast_to(emitter, ctx, field_ty)?;
4272 return Ok(());
4273 }
4274
4275 let intrinsics = ctx
4276 .symbols
4277 .intrinsic_type_indices()
4278 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4279 let raw = emitter.add_anonymous_local(ValType::Ref(RefType {
4280 nullable: true,
4281 heap_type: HeapType::Concrete(intrinsics.object),
4282 }))?;
4283 emitter.instruction(Instruction::LocalSet(raw));
4284 emit_dynamic_narrowing_check(
4285 emitter,
4286 ctx,
4287 object_local,
4288 raw,
4289 field_ty,
4290 check_ty,
4291 &checks,
4292 0,
4293 )?;
4294 Ok(())
4295}
4296
4297#[allow(clippy::too_many_arguments)]
4298fn emit_dynamic_narrowing_check(
4299 emitter: &mut FunctionEmitter,
4300 ctx: &CodegenCtx,
4301 object_local: u32,
4302 raw: u32,
4303 field_ty: &Type,
4304 check_ty: &Type,
4305 checks: &[(u32, &crate::FieldNarrowingCheck)],
4306 index: usize,
4307) -> Result<(), crate::compiler_error::CompilerFailure> {
4308 let Some((vtable, check)) = checks.get(index) else {
4309 emitter.instruction(Instruction::LocalGet(raw));
4310 cast::emit_cast_to(emitter, ctx, field_ty)?;
4311 return Ok(());
4312 };
4313 emitter.instruction(Instruction::LocalGet(object_local));
4314 crate::codegen::function_emitter::cast::emit_nominal_instance_test(emitter, ctx, *vtable)?;
4315 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(field_ty)?));
4316 emitter.instruction(Instruction::LocalGet(raw));
4317 crate::codegen::cast_check::emit_narrowed_field_read_as(
4318 emitter, ctx, check, field_ty, check_ty,
4319 )?;
4320 emitter.emit_else();
4321 emit_dynamic_narrowing_check(
4322 emitter,
4323 ctx,
4324 object_local,
4325 raw,
4326 field_ty,
4327 check_ty,
4328 checks,
4329 index + 1,
4330 )?;
4331 emitter.emit_end();
4332 Ok(())
4333}
4334
4335pub(crate) fn emit_object_property_write(
4341 emitter: &mut FunctionEmitter,
4342 ctx: &CodegenCtx,
4343 object_local: u32,
4344 prop: &Ident,
4345 value: ExprId,
4346) -> Result<(), crate::compiler_error::CompilerFailure> {
4347 emit_object_property_write_value(
4348 emitter,
4349 ctx,
4350 object_local,
4351 prop,
4352 &ShapeArgument::Expression(value),
4353 )?;
4354 Ok(())
4355}
4356
4357fn emit_object_property_write_value(
4358 emitter: &mut FunctionEmitter,
4359 ctx: &CodegenCtx,
4360 object_local: u32,
4361 prop: &Ident,
4362 value: &ShapeArgument,
4363) -> Result<(), crate::compiler_error::CompilerFailure> {
4364 let prop_name = prop.name.as_str();
4365 let setter = crate::codegen::classes::accessor_setter_name(prop_name);
4366 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4367 let has_setter_name = ctx.symbols.field_name_string_global_idx(&setter).is_some();
4368 let has_getter_name = ctx.symbols.field_name_string_global_idx(&getter).is_some();
4369 let name_global = ctx
4370 .symbols
4371 .field_name_string_global_idx(prop_name)
4372 .ok_or_else(|| {
4373 crate::codegen::internal_failure(
4374 "per-name string global recorded during field-name-strings emission",
4375 )
4376 })?;
4377 if !has_setter_name && !has_getter_name {
4381 let value_ty = value.ty(ctx)?;
4382 value.emit(emitter, ctx)?;
4383 cast::emit_box(emitter, ctx, &value_ty)?;
4384 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4385 return Ok(());
4386 }
4387 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4388 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4389 emitter.instruction(Instruction::LocalSet(index_local));
4390 emitter.instruction(Instruction::LocalGet(index_local));
4391 emitter.instruction(Instruction::I32Const(0));
4392 emitter.instruction(Instruction::I32GeS);
4393 emitter.emit_if(BlockType::Empty);
4394 let value_ty = value.ty(ctx)?;
4397 value.emit(emitter, ctx)?;
4398 cast::emit_box(emitter, ctx, &value_ty)?;
4399 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4400 emitter.emit_else();
4401 let arms: Vec<(&str, AccessorWrite)> = [
4406 has_setter_name.then_some((setter.as_str(), AccessorWrite::Setter)),
4407 has_getter_name.then_some((getter.as_str(), AccessorWrite::ReadOnly)),
4408 ]
4409 .into_iter()
4410 .flatten()
4411 .collect();
4412 for (accessor, arm) in &arms {
4413 let slot_index_local =
4414 emit_is_accessor_backed(emitter, ctx, object_local, accessor, arm.scanned_kind())?;
4415 emitter.emit_if(BlockType::Empty);
4416 match arm {
4417 AccessorWrite::Setter => {
4418 emitter.instruction(Instruction::LocalGet(object_local));
4419 emit_shape_method_with_arguments(
4420 emitter,
4421 ctx,
4422 accessor,
4423 std::slice::from_ref(value),
4424 &Type::Void,
4425 Some(slot_index_local),
4426 )?;
4427 emitter.instruction(Instruction::Drop);
4428 }
4429 AccessorWrite::ReadOnly => {
4432 value.emit(emitter, ctx)?;
4433 emitter.instruction(Instruction::Drop);
4434 emitter.record_span(prop.span);
4437 crate::codegen::throw::emit_type_error_throw(
4438 emitter,
4439 ctx,
4440 crate::codegen::throw::READ_ONLY_PROPERTY_MESSAGE,
4441 );
4442 }
4443 }
4444 emitter.emit_else();
4445 }
4446 let value_ty = value.ty(ctx)?;
4447 value.emit(emitter, ctx)?;
4448 cast::emit_box(emitter, ctx, &value_ty)?;
4449 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4450 for _ in &arms {
4451 emitter.emit_end();
4452 }
4453 emitter.emit_end();
4454 Ok(())
4455}
4456
4457fn emit_bounds_checked_index_with_receiver_on_stack(
4471 emitter: &mut FunctionEmitter,
4472 ctx: &CodegenCtx,
4473 idx: ExprId,
4474 result_ty: &Type,
4475 allow_missing: bool,
4476) -> Result<(), crate::compiler_error::CompilerFailure> {
4477 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
4478 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4479 })?;
4480 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
4481 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4482 })?;
4483 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4484 nullable: false,
4485 heap_type: HeapType::Concrete(raw_array_idx),
4486 }))?;
4487 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4488 emitter.instruction(Instruction::LocalSet(receiver));
4489 emit_expr(emitter, ctx, idx)?;
4490 let key = emitter.add_anonymous_local(
4491 ctx.symbols.value_type(
4492 &ctx.ta
4493 .try_expr(idx)
4494 .map_err(crate::codegen::arena_failure)?
4495 .ty,
4496 )?,
4497 )?;
4498 emitter.instruction(Instruction::LocalSet(key));
4499 emitter.instruction(Instruction::LocalGet(receiver));
4500 crate::codegen::cast_check::emit_operation_cast_on_stack(
4501 emitter,
4502 ctx,
4503 &Type::Unknown,
4504 &Type::Array(Box::new(Type::Unknown)),
4505 )?;
4506 emitter.instruction(Instruction::LocalGet(key));
4507 emit_index_number(
4508 emitter,
4509 ctx,
4510 &ctx.ta
4511 .try_expr(idx)
4512 .map_err(crate::codegen::arena_failure)?
4513 .ty,
4514 )?;
4515 let idx_f64_local = stash_index_operand(emitter)?;
4516 let length = stash_array_length(emitter, array_idx)?;
4517 emitter.instruction(Instruction::StructGet {
4518 struct_type_index: array_idx,
4519 field_index: 1,
4520 });
4521 emitter.instruction(Instruction::LocalSet(raw_arr_local));
4522 if allow_missing {
4523 emitter.instruction(Instruction::LocalGet(idx_f64_local));
4524 emitter.instruction(Instruction::LocalGet(length));
4525 emitter.instruction(Instruction::F64ConvertI32U);
4526 emitter.instruction(Instruction::F64Ge);
4527 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
4528 emit_undefined(emitter, ctx)?;
4529 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
4530 emitter.emit_else();
4531 }
4532 let idx_local = emit_checked_index_with_length(emitter, ctx, length, idx_f64_local)?;
4533 emitter.instruction(Instruction::LocalGet(raw_arr_local));
4534 emitter.instruction(Instruction::LocalGet(idx_local));
4535 emitter.instruction(Instruction::ArrayGet(raw_array_idx));
4536 cast::emit_cast_to(emitter, ctx, result_ty)?;
4537 if allow_missing {
4538 emitter.emit_end();
4539 }
4540 Ok(())
4541}
4542
4543fn is_optional_tuple_position(
4544 ctx: &CodegenCtx<'_>,
4545 receiver: &Type,
4546 index: ExprId,
4547) -> Result<bool, crate::compiler_error::CompilerFailure> {
4548 let expression = ctx
4549 .ta
4550 .try_expr(index)
4551 .map_err(crate::codegen::arena_failure)?;
4552 let TypedExprKind::Number(index) = expression.kind else {
4553 return Ok(false);
4554 };
4555 Ok(index.fract() == 0.0 && tuple_permits_missing_index(receiver, index))
4556}
4557
4558fn tuple_permits_missing_index(receiver: &Type, index: f64) -> bool {
4562 match receiver.lowered() {
4563 Type::Tuple(elements) => index >= elements.required_len() as f64,
4564 Type::Union(members) => members
4565 .iter()
4566 .any(|member| tuple_permits_missing_index(member, index)),
4567 _ => false,
4568 }
4569}
4570
4571fn emit_uint8_index_with_receiver_on_stack(
4572 emitter: &mut FunctionEmitter,
4573 ctx: &CodegenCtx,
4574 index: ExprId,
4575) -> Result<(), crate::compiler_error::CompilerFailure> {
4576 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
4577 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4578 })?;
4579 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
4580 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4581 })?;
4582 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4583 nullable: false,
4584 heap_type: HeapType::Concrete(raw_uint8_idx),
4585 }))?;
4586 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4587 emitter.instruction(Instruction::LocalSet(receiver));
4588 emit_expr(emitter, ctx, index)?;
4589 let key = emitter.add_anonymous_local(
4590 ctx.symbols.value_type(
4591 &ctx.ta
4592 .try_expr(index)
4593 .map_err(crate::codegen::arena_failure)?
4594 .ty,
4595 )?,
4596 )?;
4597 emitter.instruction(Instruction::LocalSet(key));
4598 emitter.instruction(Instruction::LocalGet(receiver));
4599 crate::codegen::cast_check::emit_operation_cast_on_stack(
4600 emitter,
4601 ctx,
4602 &Type::Unknown,
4603 &Type::Uint8Array,
4604 )?;
4605 emitter.instruction(Instruction::LocalGet(key));
4606 emit_index_number(
4607 emitter,
4608 ctx,
4609 &ctx.ta
4610 .try_expr(index)
4611 .map_err(crate::codegen::arena_failure)?
4612 .ty,
4613 )?;
4614 let idx_f64_local = stash_index_operand(emitter)?;
4615 emitter.instruction(Instruction::StructGet {
4616 struct_type_index: uint8_idx,
4617 field_index: 1,
4618 });
4619 emitter.instruction(Instruction::LocalSet(raw_local));
4620 let idx_local = emit_checked_index(emitter, ctx, raw_local, idx_f64_local)?;
4621 emitter.instruction(Instruction::LocalGet(raw_local));
4622 emitter.instruction(Instruction::LocalGet(idx_local));
4623 emitter.instruction(Instruction::ArrayGetU(raw_uint8_idx));
4624 emitter.instruction(Instruction::F64ConvertI32U);
4625 Ok(())
4626}
4627
4628#[derive(Clone, Copy)]
4630enum AccessorWrite {
4631 Setter,
4632 ReadOnly,
4633}
4634
4635impl AccessorWrite {
4636 fn scanned_kind(self) -> crate::AccessorKind {
4639 match self {
4640 AccessorWrite::Setter => crate::AccessorKind::Set,
4641 AccessorWrite::ReadOnly => crate::AccessorKind::Get,
4642 }
4643 }
4644}
4645
4646pub(crate) fn emit_object_field_index_by_name(
4647 emitter: &mut FunctionEmitter,
4648 ctx: &CodegenCtx,
4649 object_local: u32,
4650 name_global: u32,
4651) {
4652 emit_field_index_by_name(emitter, ctx, object_local, name_global, false);
4653}
4654
4655fn emit_field_index_by_name(
4656 emitter: &mut FunctionEmitter,
4657 ctx: &CodegenCtx,
4658 object_local: u32,
4659 name_global: u32,
4660 accessor: bool,
4661) {
4662 ctx.latch(emit_field_index_by_name_checked(
4663 emitter,
4664 ctx,
4665 object_local,
4666 name_global,
4667 accessor,
4668 ));
4669}
4670
4671fn emit_field_index_by_name_checked(
4672 emitter: &mut FunctionEmitter,
4673 ctx: &CodegenCtx,
4674 object_local: u32,
4675 name_global: u32,
4676 accessor: bool,
4677) -> Result<(), crate::compiler_error::CompilerFailure> {
4678 emitter.instruction(Instruction::LocalGet(object_local));
4679 emitter.instruction(Instruction::GlobalGet(name_global));
4680 emitter.instruction(Instruction::I32Const(i32::from(accessor)));
4681 emitter.instruction(Instruction::Call(
4682 ctx.symbols
4683 .field_lookup_function
4684 .ok_or_else(|| crate::codegen::internal_failure("field lookup allocated"))?,
4685 ));
4686
4687 Ok(())
4688}
4689
4690fn emit_direct_call(
4696 emitter: &mut FunctionEmitter,
4697 ctx: &CodegenCtx,
4698 is_host: bool,
4699 wasm_idx: u32,
4700 params: &[Type],
4701 ret: &Type,
4702 args: &[ExprId],
4703) -> Result<(), crate::compiler_error::CompilerFailure> {
4704 if params.len() != args.len() {
4705 return Err(crate::codegen::internal_failure(
4706 "direct-call argument count mismatch",
4707 ));
4708 }
4709 let string_type_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
4710 crate::codegen::internal_failure("$string type registered before call codegen")
4711 })?;
4712 let raw_string_type_idx = ctx.symbols.raw_string_type_idx().ok_or_else(|| {
4713 crate::codegen::internal_failure("$rawString type registered before call codegen")
4714 })?;
4715 let mut evaluated = Vec::with_capacity(args.len());
4716 for &arg in args {
4717 emit_expr(emitter, ctx, arg)?;
4718 let slot = emitter.add_anonymous_local(
4719 ctx.symbols.value_type(
4720 &ctx.ta
4721 .try_expr(arg)
4722 .map_err(crate::codegen::arena_failure)?
4723 .ty,
4724 )?,
4725 )?;
4726 emitter.instruction(Instruction::LocalSet(slot));
4727 evaluated.push(slot);
4728 }
4729 for (i, (&arg_id, local)) in args.iter().zip(evaluated).enumerate() {
4730 emitter.instruction(Instruction::LocalGet(local));
4731 if let Some(param_ty) = params.get(i) {
4737 let arg_ty = ctx
4738 .ta
4739 .try_expr(arg_id)
4740 .map_err(crate::codegen::arena_failure)?
4741 .ty
4742 .clone();
4743 let default = ctx
4744 .symbols
4745 .host_call_defaults(wasm_idx, args.len())
4746 .get(i)
4747 .and_then(Option::as_ref);
4748 crate::codegen::argument_defaults::emit_argument(
4749 emitter,
4750 ctx,
4751 &arg_ty,
4752 ctx.symbols.value_type(param_ty)?,
4753 default,
4754 )?;
4755 }
4756 if is_host
4761 && params
4762 .get(i)
4763 .is_some_and(|p| matches!(p.lowered(), Type::String))
4764 {
4765 emitter.instruction(Instruction::StructGet {
4766 struct_type_index: string_type_idx,
4767 field_index: 1,
4768 });
4769 }
4770 }
4771 emitter.instruction(Instruction::Call(wasm_idx));
4772 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
4773 emit_undefined(emitter, ctx)?;
4774 }
4775 let _: () = if is_host && matches!(ret.lowered(), Type::String) {
4776 let scratch = emitter.add_anonymous_local(ValType::Ref(RefType {
4777 nullable: false,
4778 heap_type: HeapType::Concrete(raw_string_type_idx),
4779 }))?;
4780 emitter.instruction(Instruction::LocalSet(scratch));
4781 let vtable_global = ctx
4782 .symbols
4783 .prelude_global_idx("string_vtable")
4784 .ok_or_else(|| {
4785 crate::codegen::internal_failure("string_vtable imported from prelude")
4786 })?;
4787 emitter.instruction(Instruction::GlobalGet(vtable_global));
4788 emitter.instruction(Instruction::LocalGet(scratch));
4789 emitter.instruction(Instruction::I64Const(0));
4790 emitter.instruction(Instruction::StructNew(string_type_idx));
4791 };
4792 Ok(())
4793}
4794
4795fn emit_indirect_closure_call(
4804 emitter: &mut FunctionEmitter,
4805 ctx: &CodegenCtx,
4806 callee: ExprId,
4807 args: &[ExprId],
4808 result_ty: &Type,
4809) -> Result<(), crate::compiler_error::CompilerFailure> {
4810 let callee_ty = ctx
4811 .ta
4812 .source_type(callee)
4813 .map_err(crate::codegen::arena_failure)?
4814 .clone();
4815 let receiver = emit_callee(emitter, ctx, callee)?;
4816 emitter.call_receiver = receiver;
4817 emit_indirect_closure_call_with_receiver_on_stack(emitter, ctx, &callee_ty, args, result_ty)?;
4818 Ok(())
4819}
4820
4821fn emit_callee(
4823 emitter: &mut FunctionEmitter,
4824 ctx: &CodegenCtx,
4825 callee: ExprId,
4826) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
4827 let reference = reference_expression(ctx.ta, callee)?;
4828 let receiver = match &ctx
4829 .ta
4830 .try_expr(reference)
4831 .map_err(crate::codegen::arena_failure)?
4832 .kind
4833 {
4834 TypedExprKind::FieldAccess { receiver, .. }
4835 | TypedExprKind::IndexAccess { receiver, .. }
4836 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => Some(*receiver),
4837 _ => None,
4838 };
4839 let mark = emitter.single_evaluation_mark();
4840 let slot = receiver
4841 .map(|receiver| {
4842 emit_expr(emitter, ctx, receiver)?;
4843 let slot = emitter.add_anonymous_local(
4844 ctx.symbols.value_type(
4845 &ctx.ta
4846 .try_expr(receiver)
4847 .map_err(crate::codegen::arena_failure)?
4848 .ty,
4849 )?,
4850 )?;
4851 emitter.instruction(Instruction::LocalSet(slot));
4852 emitter.record_single_evaluation(receiver, slot)?;
4853 Ok::<_, crate::compiler_error::CompilerFailure>(slot)
4854 })
4855 .transpose()?;
4856 emit_expr(emitter, ctx, callee)?;
4857 emitter.end_single_evaluations(mark)?;
4858 Ok(
4859 if matches!(
4860 ctx.ta
4861 .try_expr(reference)
4862 .map_err(crate::codegen::arena_failure)?
4863 .kind,
4864 TypedExprKind::OptionalChain { .. }
4865 ) {
4866 emitter.call_receiver.take()
4867 } else {
4868 slot
4869 },
4870 )
4871}
4872
4873fn reference_expression(
4874 ast: &crate::TypedAst,
4875 mut id: ExprId,
4876) -> Result<ExprId, crate::compiler_error::CompilerFailure> {
4877 loop {
4878 match ast
4879 .try_expr(id)
4880 .map_err(crate::codegen::arena_failure)?
4881 .kind
4882 {
4883 TypedExprKind::Cast { value, .. } | TypedExprKind::NonNullAssert { value } => {
4884 id = value;
4885 }
4886 _ => return Ok(id),
4887 }
4888 }
4889}
4890
4891fn emit_indirect_closure_call_with_receiver_on_stack(
4897 emitter: &mut FunctionEmitter,
4898 ctx: &CodegenCtx,
4899 callee_ty: &Type,
4900 args: &[ExprId],
4901 result_ty: &Type,
4902) -> Result<(), crate::compiler_error::CompilerFailure> {
4903 let receiver = emitter.call_receiver.take();
4904 let callee = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4905 emitter.instruction(Instruction::LocalSet(callee));
4906 let mut arguments = Vec::with_capacity(args.len());
4907 for &arg in args {
4908 emit_expr(emitter, ctx, arg)?;
4909 cast::emit_box(
4910 emitter,
4911 ctx,
4912 &ctx.ta
4913 .try_expr(arg)
4914 .map_err(crate::codegen::arena_failure)?
4915 .ty,
4916 )?;
4917 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4918 emitter.instruction(Instruction::LocalSet(slot));
4919 arguments.push(slot);
4920 }
4921 emitter.instruction(Instruction::LocalGet(callee));
4922 crate::codegen::cast_check::emit_operation_cast_on_stack(
4923 emitter,
4924 ctx,
4925 &Type::Unknown,
4926 callee_ty,
4927 )?;
4928 let callee_ty = callee_ty.lowered();
4933 let closure_sig = crate::codegen::closures::classify(callee_ty)?;
4934 let closure_struct_idx = ctx
4935 .symbols
4936 .closure_struct_type_idx(closure_sig)
4937 .ok_or_else(|| {
4938 crate::codegen::internal_failure(
4939 "closure struct type registered for every closure-typed callee",
4940 )
4941 })?;
4942 let fn_type_idx = ctx
4943 .symbols
4944 .closure_func_type_idx(closure_sig)
4945 .ok_or_else(|| {
4946 crate::codegen::internal_failure(
4947 "closure funcref type registered for every closure-typed callee",
4948 )
4949 })?;
4950 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4951 nullable: false,
4952 heap_type: HeapType::Concrete(closure_struct_idx),
4953 }))?;
4954 emitter.instruction(Instruction::LocalTee(closure_local));
4955 emitter.instruction(Instruction::StructGet {
4956 struct_type_index: closure_struct_idx,
4957 field_index: 2,
4958 });
4959 if let Some(receiver) = receiver {
4960 crate::codegen::this_binding::bind(emitter, ctx, receiver)?;
4961 }
4962 for argument in arguments {
4970 emitter.instruction(Instruction::LocalGet(argument));
4971 }
4972 emitter.instruction(Instruction::LocalGet(closure_local));
4973 emitter.instruction(Instruction::StructGet {
4974 struct_type_index: closure_struct_idx,
4975 field_index: 1,
4976 });
4977 emitter.instruction(Instruction::CallRef(fn_type_idx));
4978 let Type::Function { ret, .. } = callee_ty.lowered() else {
4981 return Err(crate::codegen::internal_failure(
4982 "indirect call requires a function type",
4983 ));
4984 };
4985 let _: () = if !ret.is_void() {
4986 crate::codegen::cast_check::emit_checked_cast_on_stack(
4987 emitter,
4988 ctx,
4989 &Type::Unknown,
4990 result_ty,
4991 )?;
4992 };
4993 Ok(())
4994}
4995
4996fn emit_typeof_tag(
4999 emitter: &mut FunctionEmitter,
5000 ctx: &CodegenCtx,
5001 value: ExprId,
5002 tag: crate::TypeofTagKind,
5003) -> Result<(), crate::compiler_error::CompilerFailure> {
5004 let _: () = match tag {
5005 crate::TypeofTagKind::BigInt => {
5006 emit_expr(emitter, ctx, value)?;
5007 cast::emit_box(
5008 emitter,
5009 ctx,
5010 &ctx.ta
5011 .try_expr(value)
5012 .map_err(crate::codegen::arena_failure)?
5013 .ty,
5014 )?;
5015 let intrinsic = ctx
5016 .symbols
5017 .bigint_type_idx()
5018 .ok_or_else(|| crate::codegen::internal_failure("bigint intrinsic missing"))?;
5019 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(intrinsic)));
5020 }
5021 crate::TypeofTagKind::Undefined => {
5022 emit_expr(emitter, ctx, value)?;
5023 cast::emit_box(
5024 emitter,
5025 ctx,
5026 &ctx.ta
5027 .try_expr(value)
5028 .map_err(crate::codegen::arena_failure)?
5029 .ty,
5030 )?;
5031 emit_is_undefined(emitter, ctx)?;
5032 }
5033 crate::TypeofTagKind::Number => {
5034 let boxed = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
5040 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
5041 })?;
5042 emit_expr(emitter, ctx, value)?;
5043 cast::emit_box(
5044 emitter,
5045 ctx,
5046 &ctx.ta
5047 .try_expr(value)
5048 .map_err(crate::codegen::arena_failure)?
5049 .ty,
5050 )?;
5051 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5052 }
5053 crate::TypeofTagKind::String => {
5054 let string = ctx.symbols.string_type_idx().ok_or_else(|| {
5055 crate::codegen::internal_failure("$string intrinsic type registered")
5056 })?;
5057 emit_expr(emitter, ctx, value)?;
5058 cast::emit_box(
5059 emitter,
5060 ctx,
5061 &ctx.ta
5062 .try_expr(value)
5063 .map_err(crate::codegen::arena_failure)?
5064 .ty,
5065 )?;
5066 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
5067 }
5068 crate::TypeofTagKind::Boolean => {
5069 let boxed = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
5070 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
5071 })?;
5072 emit_expr(emitter, ctx, value)?;
5073 cast::emit_box(
5074 emitter,
5075 ctx,
5076 &ctx.ta
5077 .try_expr(value)
5078 .map_err(crate::codegen::arena_failure)?
5079 .ty,
5080 )?;
5081 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5082 }
5083 crate::TypeofTagKind::Function => {
5084 let closure_idx = ctx.symbols.closure_type_idx().ok_or_else(|| {
5085 crate::codegen::internal_failure("$Closure intrinsic type registered")
5086 })?;
5087 emit_expr(emitter, ctx, value)?;
5088 cast::emit_box(
5089 emitter,
5090 ctx,
5091 &ctx.ta
5092 .try_expr(value)
5093 .map_err(crate::codegen::arena_failure)?
5094 .ty,
5095 )?;
5096 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
5097 }
5098 crate::TypeofTagKind::Object => {
5099 let scratch_ty = ctx.symbols.value_type(&Type::Unknown)?;
5100 let nullable = matches!(scratch_ty, ValType::Ref(rt) if rt.nullable);
5101 let scratch = emitter.add_anonymous_local(scratch_ty)?;
5102 emit_expr(emitter, ctx, value)?;
5103 cast::emit_box(
5104 emitter,
5105 ctx,
5106 &ctx.ta
5107 .try_expr(value)
5108 .map_err(crate::codegen::arena_failure)?
5109 .ty,
5110 )?;
5111 emitter.instruction(Instruction::LocalSet(scratch));
5112 emit_is_non_primitive(emitter, ctx, scratch, Closures::AreNotObjects)?;
5113 if nullable {
5114 emitter.instruction(Instruction::LocalGet(scratch));
5119 emitter.instruction(Instruction::RefIsNull);
5120 emitter.instruction(Instruction::I32Or);
5121 }
5122 }
5123 };
5124 Ok(())
5125}
5126
5127#[derive(Clone, Copy, PartialEq, Eq)]
5130pub(crate) enum Closures {
5131 AreObjects,
5132 AreNotObjects,
5133}
5134
5135pub(crate) fn emit_is_non_primitive(
5144 emitter: &mut FunctionEmitter,
5145 ctx: &CodegenCtx,
5146 local: u32,
5147 closures: Closures,
5148) -> Result<(), crate::compiler_error::CompilerFailure> {
5149 let intrinsics = ctx
5150 .symbols
5151 .intrinsic_type_indices()
5152 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5153 emitter.instruction(Instruction::LocalGet(local));
5154 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5155 intrinsics.object,
5156 )));
5157 let mut excluded = vec![
5158 intrinsics.string,
5159 intrinsics.boxed_number,
5160 intrinsics.boxed_boolean,
5161 intrinsics.bigint,
5162 intrinsics.undefined,
5163 ];
5164 if closures == Closures::AreNotObjects {
5165 excluded.push(intrinsics.closure);
5166 }
5167 for primitive in excluded {
5168 emitter.instruction(Instruction::LocalGet(local));
5169 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(primitive)));
5170 emitter.instruction(Instruction::I32Eqz);
5171 emitter.instruction(Instruction::I32And);
5172 }
5173 Ok(())
5174}
5175
5176fn has_numeric_operands(
5179 ctx: &CodegenCtx,
5180 operands: &[ExprId],
5181 result_ty: &Type,
5182) -> Result<bool, crate::compiler_error::CompilerFailure> {
5183 if !matches!(result_ty.lowered(), Type::Number | Type::NumberLiteral(_)) {
5184 return Ok(false);
5185 }
5186 for &operand in operands {
5187 let ty = &ctx
5188 .ta
5189 .try_expr(operand)
5190 .map_err(crate::codegen::arena_failure)?
5191 .ty;
5192 if !matches!(
5193 ty.primitive_behavior(),
5194 Type::Number | Type::NumberLiteral(_)
5195 ) {
5196 return Ok(false);
5197 }
5198 }
5199 Ok(true)
5200}
5201
5202fn emit_number_bitwise(
5203 emitter: &mut FunctionEmitter,
5204 ctx: &CodegenCtx,
5205 op: BinOp,
5206 lhs: ExprId,
5207 rhs: ExprId,
5208) -> Result<(), crate::compiler_error::CompilerFailure> {
5209 emit_primitive_operand(emitter, ctx, lhs)?;
5210 emit_to_int32(emitter, ctx)?;
5211 emit_primitive_operand(emitter, ctx, rhs)?;
5212 emit_to_int32(emitter, ctx)?;
5213 let (instruction, unsigned) = match op {
5215 BinOp::BitAnd => (Instruction::I32And, false),
5216 BinOp::BitOr => (Instruction::I32Or, false),
5217 BinOp::BitXor => (Instruction::I32Xor, false),
5218 BinOp::Shl => (Instruction::I32Shl, false),
5219 BinOp::Shr => (Instruction::I32ShrS, false),
5220 BinOp::UnsignedShr => (Instruction::I32ShrU, true),
5221 _ => {
5222 return Err(crate::codegen::internal_failure(
5223 "a numeric bitwise operation has a bitwise operator",
5224 ));
5225 }
5226 };
5227 emitter.instruction(instruction);
5228 emitter.instruction(if unsigned {
5229 Instruction::F64ConvertI32U
5230 } else {
5231 Instruction::F64ConvertI32S
5232 });
5233 Ok(())
5234}
5235
5236fn emit_to_int32(
5240 emitter: &mut FunctionEmitter,
5241 ctx: &CodegenCtx,
5242) -> Result<(), crate::compiler_error::CompilerFailure> {
5243 let value = emitter.add_anonymous_local(ValType::F64)?;
5244 emitter.instruction(Instruction::LocalTee(value));
5245 emitter.instruction(Instruction::F64Abs);
5246 emitter.instruction(Instruction::F64Const(Ieee64::from(
5247 9_223_372_036_854_775_808.0,
5248 )));
5249 emitter.instruction(Instruction::F64Lt);
5250 emitter.emit_if(BlockType::Result(ValType::I32));
5251 emitter.instruction(Instruction::LocalGet(value));
5252 emitter.instruction(Instruction::I64TruncSatF64S);
5253 emitter.instruction(Instruction::I32WrapI64);
5254 emitter.emit_else();
5255 emitter.instruction(Instruction::LocalGet(value));
5256 cast::emit_box(emitter, ctx, &Type::Number)?;
5257 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
5258 cast::emit_box(emitter, ctx, &Type::Number)?;
5259 let host = ctx
5260 .symbols
5261 .func_idx(&crate::mangle::prelude("__value_bitor"))
5262 .ok_or_else(|| crate::codegen::internal_failure("missing bitwise host import: bitor"))?;
5263 emitter.instruction(Instruction::Call(host));
5264 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5265 emitter.instruction(Instruction::I32TruncF64S);
5266 emitter.emit_end();
5267 Ok(())
5268}
5269
5270fn emit_bitwise_host(
5271 emitter: &mut FunctionEmitter,
5272 ctx: &CodegenCtx,
5273 name: &str,
5274 operands: &[ExprId],
5275 result_ty: &Type,
5276) -> Result<(), crate::compiler_error::CompilerFailure> {
5277 for &operand in operands {
5278 emit_expr(emitter, ctx, operand)?;
5279 let ty = &ctx
5280 .ta
5281 .try_expr(operand)
5282 .map_err(crate::codegen::arena_failure)?
5283 .ty;
5284 cast::emit_box(emitter, ctx, ty)?;
5285 }
5286 let mangled = crate::mangle::prelude(&format!("__value_{name}"));
5287 let host = ctx.symbols.func_idx(&mangled).ok_or_else(|| {
5288 crate::codegen::internal_failure(format!("missing bitwise host import: {name}"))
5289 })?;
5290 emitter.instruction(Instruction::Call(host));
5291 cast::emit_cast_to(emitter, ctx, result_ty)
5292}
5293
5294fn emit_unary(
5295 emitter: &mut FunctionEmitter,
5296 ctx: &CodegenCtx,
5297 op: UnOp,
5298 operand: ExprId,
5299 result_ty: &Type,
5300) -> Result<(), crate::compiler_error::CompilerFailure> {
5301 if matches!(op, UnOp::Neg | UnOp::Pos)
5302 && try_emit_unreachable_for_never_operand(emitter, ctx, &[operand])?
5303 {
5304 return Ok(());
5305 }
5306 let _: () = match op {
5307 UnOp::BitNot if has_numeric_operands(ctx, &[operand], result_ty)? => {
5308 emit_primitive_operand(emitter, ctx, operand)?;
5309 emit_to_int32(emitter, ctx)?;
5310 emitter.instruction(Instruction::I32Const(-1));
5311 emitter.instruction(Instruction::I32Xor);
5312 emitter.instruction(Instruction::F64ConvertI32S);
5313 }
5314 UnOp::BitNot => emit_bitwise_host(emitter, ctx, "bitnot", &[operand], result_ty)?,
5315 UnOp::Neg => {
5316 if ctx
5318 .ta
5319 .try_expr(operand)
5320 .map_err(crate::codegen::arena_failure)?
5321 .ty
5322 .is_bigint()
5323 {
5324 emit_primitive_operand(emitter, ctx, operand)?;
5325 emit_bigint_extract_to_stack(emitter, ctx);
5326 let host_idx = ctx
5327 .symbols
5328 .func_idx(&crate::mangle::host(
5329 crate::runtime::BIGINT_MODULE_NAME,
5330 "neg",
5331 ))
5332 .ok_or_else(|| {
5333 crate::codegen::internal_failure(
5334 "submilli:bigint.neg import recorded by codegen bootstrap",
5335 )
5336 })?;
5337 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
5338 return Ok(());
5339 }
5340 emit_primitive_operand(emitter, ctx, operand)?;
5341 emitter.instruction(Instruction::F64Neg);
5342 }
5343 UnOp::Pos => {
5344 let operand_ty = ctx
5349 .ta
5350 .try_expr(operand)
5351 .map_err(crate::codegen::arena_failure)?
5352 .ty
5353 .clone();
5354 if operand_ty.is_string_shaped() {
5355 emit_primitive_operand(emitter, ctx, operand)?;
5356 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
5357 } else if matches!(operand_ty.primitive_behavior(), Type::Union(_)) {
5358 emit_expr(emitter, ctx, operand)?;
5359 cast::emit_box(emitter, ctx, &operand_ty)?;
5360 emit_union_to_number(emitter, ctx)?;
5361 } else {
5362 emit_primitive_operand(emitter, ctx, operand)?;
5363 }
5364 }
5365 UnOp::Not => {
5366 let operand_ty = ctx
5372 .ta
5373 .try_expr(operand)
5374 .map_err(crate::codegen::arena_failure)?
5375 .ty
5376 .clone();
5377 emit_expr(emitter, ctx, operand)?;
5378 crate::codegen::function_emitter::cast::emit_condition_to_i32(
5379 emitter,
5380 ctx,
5381 &operand_ty,
5382 )?;
5383 emitter.instruction(Instruction::I32Eqz);
5384 }
5385 };
5386 Ok(())
5387}
5388
5389fn to_number_host(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
5390 ctx.symbols
5391 .func_idx(&crate::mangle::host(
5392 crate::runtime::NUMBER_MODULE_NAME,
5393 "toNumber",
5394 ))
5395 .ok_or_else(|| {
5396 crate::codegen::internal_failure(
5397 "submilli:number.toNumber import recorded by codegen analysis",
5398 )
5399 })
5400}
5401
5402fn emit_union_to_number(
5405 emitter: &mut FunctionEmitter,
5406 ctx: &CodegenCtx,
5407) -> Result<(), crate::compiler_error::CompilerFailure> {
5408 let string = ctx
5409 .symbols
5410 .string_type_idx()
5411 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic type registered"))?;
5412 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5413 emitter.instruction(Instruction::LocalTee(tmp));
5414 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
5415 emitter.emit_if(BlockType::Result(ValType::F64));
5416 emitter.instruction(Instruction::LocalGet(tmp));
5417 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(string)));
5418 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
5419 emitter.emit_else();
5420 emitter.instruction(Instruction::LocalGet(tmp));
5421 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5422 emitter.emit_end();
5423 Ok(())
5424}
5425
5426#[allow(clippy::too_many_arguments)]
5430pub(crate) fn emit_method_call(
5431 emitter: &mut FunctionEmitter,
5432 ctx: &CodegenCtx,
5433 receiver: ExprId,
5434 iface: &crate::MangledName,
5435 method: &str,
5436 args: &[ExprId],
5437 generic_args: Option<&[crate::GenericArgument]>,
5438 return_cast: Option<&Type>,
5439 call_ret_ty: &Type,
5440) -> Result<(), crate::compiler_error::CompilerFailure> {
5441 let recv_ty = ctx
5442 .ta
5443 .source_type(receiver)
5444 .map_err(crate::codegen::arena_failure)?
5445 .clone();
5446 if !emit_dropped_static_receiver(emitter, ctx, receiver, iface)? {
5447 emit_receiver(emitter, ctx, receiver)?;
5448 }
5449 emit_method_call_with_receiver_on_stack(
5450 emitter,
5451 ctx,
5452 &recv_ty,
5453 iface,
5454 method,
5455 args,
5456 generic_args,
5457 return_cast,
5458 call_ret_ty,
5459 )?;
5460 Ok(())
5461}
5462
5463fn emit_dropped_static_receiver(
5467 emitter: &mut FunctionEmitter,
5468 ctx: &CodegenCtx,
5469 receiver: ExprId,
5470 iface: &crate::MangledName,
5471) -> Result<bool, crate::compiler_error::CompilerFailure> {
5472 let expr = ctx
5473 .ta
5474 .try_expr(receiver)
5475 .map_err(crate::codegen::arena_failure)?;
5476 if !matches!(expr.kind, TypedExprKind::GlobalRef { .. })
5477 || static_interface(ctx, &expr.ty) != Some(iface)
5478 {
5479 return Ok(false);
5480 }
5481 match ctx.symbols.value_type(&expr.ty)? {
5482 ValType::Ref(RefType { heap_type, .. }) => {
5483 emitter.instruction(Instruction::RefNull(heap_type));
5484 }
5485 other => {
5486 return Err(crate::codegen::internal_failure(format!(
5487 "static-interface binding lowered to {other:?}"
5488 )));
5489 }
5490 }
5491 Ok(true)
5492}
5493
5494#[allow(clippy::too_many_arguments)]
5501fn emit_method_call_with_receiver_on_stack(
5502 emitter: &mut FunctionEmitter,
5503 ctx: &CodegenCtx,
5504 recv_ty: &Type,
5505 iface: &crate::MangledName,
5506 method: &str,
5507 args: &[ExprId],
5508 generic_args: Option<&[crate::GenericArgument]>,
5509 return_cast: Option<&Type>,
5510 call_ret_ty: &Type,
5511) -> Result<(), crate::compiler_error::CompilerFailure> {
5512 let concrete_receiver = crate::typechecker::infer::narrowing::strip_nullish(recv_ty);
5513 if concrete_receiver.primitive_union_members().is_some() {
5514 cast::emit_box(emitter, ctx, recv_ty)?;
5515 emit_live_member_on_stack(emitter, ctx, iface, method, Some(args))?;
5516 return cast::emit_cast_to(emitter, ctx, call_ret_ty);
5517 }
5518 crate::codegen::field_guards::attach(emitter, ctx, &concrete_receiver)?;
5519 if recv_ty.is_regexp_match_array() && *iface == crate::mangle::prelude("Array") {
5520 cast::emit_cast_to(emitter, ctx, &Type::Array(Box::new(Type::regexp_element())))?;
5521 }
5522 let direct_key = crate::mangle::extend(iface, method);
5523 if let Some(func_idx) = ctx.symbols.func_idx(&direct_key) {
5524 if matches!(recv_ty.primitive_behavior(), Type::Number)
5525 && ctx.symbols.value_type(recv_ty)? != ValType::F64
5526 {
5527 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5528 }
5529 match ctx.symbols.iface_dispatch(iface) {
5538 Some(crate::Dispatch::Direct) => {
5539 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
5551 emitter.instruction(Instruction::RefAsNonNull);
5552 }
5553 }
5554 Some(crate::Dispatch::Static) => {
5555 emitter.instruction(Instruction::Drop);
5556 }
5557 None | Some(crate::Dispatch::VTable) => {}
5562 }
5563 let abi = wrapper_abi_for_call(
5573 ctx,
5574 &direct_key,
5575 args,
5576 generic_args,
5577 return_cast,
5578 call_ret_ty,
5579 )?;
5580 let defaults = ctx.symbols.host_call_defaults(func_idx, args.len());
5581 emit_args_into_slots_with_defaults(emitter, ctx, args, Some(&abi.params), defaults)?;
5582 emitter.instruction(Instruction::Call(func_idx));
5583 emit_slot_return_cast(emitter, ctx, call_ret_ty, Some(&abi))?;
5584 return Ok(());
5585 }
5586 if ctx.symbols.is_rewritable_class_method(iface, method) {
5589 emit_interface_method_via_shape_with_receiver_on_stack(
5590 emitter,
5591 ctx,
5592 method,
5593 args,
5594 call_ret_ty,
5595 None,
5596 )?;
5597 return Ok(());
5598 }
5599 if ctx.symbols.class_method_slot(iface, method).is_some() {
5604 emit_class_vtable_dispatch(emitter, ctx, iface, method, args, call_ret_ty)?;
5605 return Ok(());
5606 }
5607 if let Some(slot) = vtable_slot_for_method(method) {
5608 emit_vtable_dispatch_on_object_stack(emitter, ctx, slot);
5609 return Ok(());
5610 }
5611 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
5620 emit_interface_method_via_shape_with_receiver_on_stack(
5621 emitter,
5622 ctx,
5623 method,
5624 args,
5625 call_ret_ty,
5626 None,
5627 )?;
5628 return Ok(());
5629 }
5630 Err(crate::codegen::internal_failure(format!(
5631 "method dispatch was not registered for `{iface}.{method}`"
5632 )))
5633}
5634
5635fn emit_class_method_value(
5636 emitter: &mut FunctionEmitter,
5637 ctx: &CodegenCtx,
5638 receiver_ty: &Type,
5639 method: &str,
5640 result_ty: &Type,
5641 check_ty: &Type,
5642) -> Result<(), crate::compiler_error::CompilerFailure> {
5643 let object_shape = ctx
5644 .symbols
5645 .intrinsic_type_indices()
5646 .ok_or_else(|| {
5647 crate::codegen::internal_failure("class method value requires object shape intrinsic")
5648 })?
5649 .object_shape;
5650 let receiver = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
5651 emit_object_property_read_as(emitter, ctx, receiver, method, result_ty, check_ty)
5652}
5653
5654pub(in crate::codegen) fn emit_interface_method_via_shape_with_receiver_on_stack(
5670 emitter: &mut FunctionEmitter,
5671 ctx: &CodegenCtx,
5672 method: &str,
5673 args: &[ExprId],
5674 call_ret_ty: &Type,
5675 slot_index_local: Option<u32>,
5676) -> Result<(), crate::compiler_error::CompilerFailure> {
5677 let args: Vec<_> = args
5678 .iter()
5679 .copied()
5680 .map(ShapeArgument::Expression)
5681 .collect();
5682 emit_shape_method_with_arguments(emitter, ctx, method, &args, call_ret_ty, slot_index_local)?;
5683 Ok(())
5684}
5685
5686enum ShapeArgument {
5687 Expression(ExprId),
5688 Local { slot: u32, ty: Type },
5689}
5690
5691impl ShapeArgument {
5692 fn ty(&self, ctx: &CodegenCtx) -> Result<Type, crate::compiler_error::CompilerFailure> {
5693 Ok(match self {
5694 Self::Expression(id) => ctx
5695 .ta
5696 .try_expr(*id)
5697 .map_err(crate::codegen::arena_failure)?
5698 .ty
5699 .clone(),
5700 Self::Local { ty, .. } => ty.clone(),
5701 })
5702 }
5703
5704 fn emit(
5705 &self,
5706 emitter: &mut FunctionEmitter,
5707 ctx: &CodegenCtx,
5708 ) -> Result<(), crate::compiler_error::CompilerFailure> {
5709 let _: () = match self {
5710 Self::Expression(id) => emit_expr(emitter, ctx, *id)?,
5711 Self::Local { slot, .. } => {
5712 emitter.instruction(Instruction::LocalGet(*slot));
5713 }
5714 };
5715 Ok(())
5716 }
5717}
5718
5719fn emit_shape_method_with_arguments(
5720 emitter: &mut FunctionEmitter,
5721 ctx: &CodegenCtx,
5722 method: &str,
5723 args: &[ShapeArgument],
5724 call_ret_ty: &Type,
5725 slot_index_local: Option<u32>,
5726) -> Result<(), crate::compiler_error::CompilerFailure> {
5727 let intrinsics = ctx
5728 .symbols
5729 .intrinsic_type_indices()
5730 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5731
5732 let arg_tys: Vec<Type> = args.iter().map(|a| a.ty(ctx)).collect::<Result<_, _>>()?;
5735 let fn_ty = Type::Function {
5736 params: arg_tys,
5737 ret: Box::new(call_ret_ty.clone()),
5738 predicate: None,
5739 has_rest: false,
5743 optional: 0,
5744 };
5745 let closure_sig = crate::codegen::closures::classify(&fn_ty)?;
5746 let closure_struct_idx = ctx
5747 .symbols
5748 .closure_struct_type_idx(closure_sig)
5749 .ok_or_else(|| {
5750 crate::codegen::internal_failure(
5751 "closure struct registered for the method's signature (SUB-166)",
5752 )
5753 })?;
5754 let fn_type_idx = ctx
5755 .symbols
5756 .closure_func_type_idx(closure_sig)
5757 .ok_or_else(|| {
5758 crate::codegen::internal_failure(
5759 "closure funcref type registered for the method's signature",
5760 )
5761 })?;
5762
5763 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5764 nullable: false,
5765 heap_type: HeapType::Concrete(intrinsics.object_shape),
5766 }))?;
5767 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5768 nullable: false,
5769 heap_type: HeapType::Concrete(closure_struct_idx),
5770 }))?;
5771
5772 emitter.instruction(Instruction::RefAsNonNull);
5776 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
5777 intrinsics.object_shape,
5778 )));
5779 emitter.instruction(Instruction::LocalSet(rcv_local));
5780
5781 if let Some(index_local) = slot_index_local {
5784 emit_field_slot_get(emitter, intrinsics, rcv_local, index_local);
5785 } else {
5786 let name_global = ctx
5787 .symbols
5788 .field_name_string_global_idx(method)
5789 .ok_or_else(|| {
5790 crate::codegen::internal_failure(
5791 ("per-name string global recorded during field-name-strings emission \
5792 for interface method names")
5793 .to_string(),
5794 )
5795 })?;
5796 emit_object_field_read_by_name(emitter, ctx, rcv_local, name_global);
5797 }
5798
5799 let method_value = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5800 emitter.instruction(Instruction::LocalSet(method_value));
5801 let mut evaluated_args = Vec::with_capacity(args.len());
5802 for arg in args {
5803 arg.emit(emitter, ctx)?;
5804 cast::emit_box(emitter, ctx, &arg.ty(ctx)?)?;
5805 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5806 emitter.instruction(Instruction::LocalSet(slot));
5807 evaluated_args.push(slot);
5808 }
5809 emitter.instruction(Instruction::LocalGet(method_value));
5810 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5811 intrinsics.closure,
5812 )));
5813 emitter.instruction(Instruction::I32Eqz);
5814 emitter.emit_if(BlockType::Empty);
5815 crate::codegen::throw::emit_type_error_throw(emitter, ctx, "Value is not callable");
5816 emitter.emit_end();
5817 emitter.instruction(Instruction::LocalGet(method_value));
5818
5819 crate::codegen::closure_coercions::emit_erased_cast(
5823 emitter,
5824 ctx,
5825 crate::codegen::closures::ClosureSig::of(args.len(), call_ret_ty)?,
5826 )?;
5827 emitter.instruction(Instruction::LocalTee(closure_local));
5828
5829 emitter.instruction(Instruction::StructGet {
5831 struct_type_index: closure_struct_idx,
5832 field_index: 2,
5833 });
5834
5835 crate::codegen::this_binding::bind(emitter, ctx, rcv_local)?;
5836 for slot in evaluated_args {
5839 emitter.instruction(Instruction::LocalGet(slot));
5840 }
5841
5842 emitter.instruction(Instruction::LocalGet(closure_local));
5844 emitter.instruction(Instruction::StructGet {
5845 struct_type_index: closure_struct_idx,
5846 field_index: 1,
5847 });
5848 emitter.instruction(Instruction::CallRef(fn_type_idx));
5849
5850 let _: () = if !call_ret_ty.is_void() {
5853 crate::codegen::cast_check::emit_checked_cast_on_stack(
5854 emitter,
5855 ctx,
5856 &Type::Unknown,
5857 call_ret_ty,
5858 )?;
5859 };
5860 Ok(())
5861}
5862
5863fn vtable_slot_for_method(method: &str) -> Option<u32> {
5874 match method {
5875 "toString" => Some(0),
5876 "toJson" => Some(1),
5877 "equals" => Some(2),
5878 "hash" => Some(3),
5879 _ => None,
5880 }
5881}
5882
5883pub(super) fn emit_vtable_dispatch_on_object_stack(
5894 emitter: &mut FunctionEmitter,
5895 ctx: &CodegenCtx,
5896 slot: u32,
5897) {
5898 let Some(intrinsics) = ctx.require(
5899 ctx.symbols.intrinsic_type_indices(),
5900 "intrinsics declared by codegen entry",
5901 ) else {
5902 return;
5903 };
5904 let fn_type_idx = match slot {
5909 0 => intrinsics.to_string_fn,
5910 1 => intrinsics.to_json_fn,
5911 _ => {
5912 ctx.fail("vtable string dispatch requires toString or toJson slot");
5913 return;
5914 }
5915 };
5916 let Some(obj_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5917 nullable: false,
5918 heap_type: HeapType::Concrete(intrinsics.object),
5919 }))) else {
5920 return;
5921 };
5922 let Some(fn_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5923 nullable: false,
5924 heap_type: HeapType::Concrete(fn_type_idx),
5925 }))) else {
5926 return;
5927 };
5928 emitter.instruction(Instruction::RefAsNonNull);
5934 emitter.instruction(Instruction::LocalTee(obj_local));
5935 emitter.instruction(Instruction::StructGet {
5936 struct_type_index: intrinsics.object,
5937 field_index: 0,
5938 });
5939 emitter.instruction(Instruction::StructGet {
5940 struct_type_index: intrinsics.vtable,
5941 field_index: slot,
5942 });
5943 emitter.instruction(Instruction::LocalSet(fn_local));
5944 emitter.instruction(Instruction::LocalGet(obj_local));
5945 emitter.instruction(Instruction::LocalGet(fn_local));
5946 emitter.instruction(Instruction::CallRef(fn_type_idx));
5947}
5948
5949fn emit_args_into_slots(
5953 emitter: &mut FunctionEmitter,
5954 ctx: &CodegenCtx,
5955 args: &[ExprId],
5956 slots: Option<&[ValType]>,
5957) -> Result<(), crate::compiler_error::CompilerFailure> {
5958 emit_args_into_slots_with_defaults(emitter, ctx, args, slots, &[])
5959}
5960
5961fn emit_args_into_slots_with_defaults(
5962 emitter: &mut FunctionEmitter,
5963 ctx: &CodegenCtx,
5964 args: &[ExprId],
5965 slots: Option<&[ValType]>,
5966 defaults: &[Option<crate::DefaultValue>],
5967) -> Result<(), crate::compiler_error::CompilerFailure> {
5968 if slots.is_some_and(|slots| slots.len() != args.len()) {
5969 return Err(crate::codegen::internal_failure(
5970 "call ABI argument count mismatch",
5971 ));
5972 }
5973 let locals: Vec<_> = args
5974 .iter()
5975 .map(|&arg| {
5976 emit_expr(emitter, ctx, arg)?;
5977 let local = emitter.add_anonymous_local(
5978 ctx.symbols.value_type(
5979 &ctx.ta
5980 .try_expr(arg)
5981 .map_err(crate::codegen::arena_failure)?
5982 .ty,
5983 )?,
5984 )?;
5985 emitter.instruction(Instruction::LocalSet(local));
5986 Ok::<_, crate::compiler_error::CompilerFailure>(local)
5987 })
5988 .collect::<Result<_, _>>()?;
5989 for (i, (&arg, local)) in args.iter().zip(locals).enumerate() {
5990 emitter.instruction(Instruction::LocalGet(local));
5991 if let Some(slot) = slots.and_then(|s| s.get(i).copied()) {
5992 crate::codegen::argument_defaults::emit_argument(
5993 emitter,
5994 ctx,
5995 &ctx.ta
5996 .try_expr(arg)
5997 .map_err(crate::codegen::arena_failure)?
5998 .ty,
5999 slot,
6000 defaults.get(i).and_then(Option::as_ref),
6001 )?;
6002 }
6003 }
6004 Ok(())
6005}
6006
6007fn wrapper_abi_for_call(
6022 ctx: &CodegenCtx,
6023 direct_key: &crate::MangledName,
6024 args: &[ExprId],
6025 generic_args: Option<&[crate::GenericArgument]>,
6026 return_cast: Option<&Type>,
6027 call_ret_ty: &Type,
6028) -> Result<MethodSlotAbi, crate::compiler_error::CompilerFailure> {
6029 if let Some(recorded) = ctx.symbols.iface_method_abi(direct_key) {
6030 return Ok(recorded.clone());
6031 }
6032 let erased = ctx.symbols.value_type(&Type::Unknown)?;
6033 let slot_is_erased =
6034 |i: usize| generic_args.is_some_and(|flags| flags.get(i).is_some_and(|g| g.is_generic));
6035 Ok(MethodSlotAbi {
6036 params: args
6037 .iter()
6038 .enumerate()
6039 .map(|(i, &a)| {
6040 Ok::<_, crate::compiler_error::CompilerFailure>(if slot_is_erased(i) {
6041 erased
6042 } else {
6043 ctx.symbols.value_type(
6044 &ctx.ta
6045 .try_expr(a)
6046 .map_err(crate::codegen::arena_failure)?
6047 .ty,
6048 )?
6049 })
6050 })
6051 .collect::<Result<_, _>>()?,
6052 ret: match return_cast {
6053 Some(_) => Some(erased),
6054 None => (!call_ret_ty.is_void())
6055 .then(|| ctx.symbols.value_type(call_ret_ty))
6056 .transpose()?,
6057 },
6058 })
6059}
6060
6061fn emit_slot_return_cast(
6064 emitter: &mut FunctionEmitter,
6065 ctx: &CodegenCtx,
6066 call_ret_ty: &Type,
6067 abi: Option<&MethodSlotAbi>,
6068) -> Result<(), crate::compiler_error::CompilerFailure> {
6069 if matches!(call_ret_ty.lowered(), Type::Never) {
6070 emitter.instruction(Instruction::Unreachable);
6071 return Ok(());
6072 }
6073 if abi.is_some_and(|abi| abi.ret.is_none()) {
6076 emit_undefined(emitter, ctx)?;
6077 }
6078 if call_ret_ty.is_void() {
6079 return Ok(());
6080 }
6081 if matches!(call_ret_ty, Type::Unknown) {
6082 match abi.and_then(|abi| abi.ret) {
6083 Some(ValType::F64) => cast::emit_box(emitter, ctx, &Type::Number)?,
6084 Some(ValType::I32) => cast::emit_box(emitter, ctx, &Type::Boolean)?,
6085 _ => {}
6086 }
6087 return Ok(());
6088 }
6089 if abi.and_then(|a| a.ret) != Some(ctx.symbols.value_type(call_ret_ty)?) {
6090 crate::codegen::cast_check::emit_checked_cast_on_stack(
6091 emitter,
6092 ctx,
6093 &Type::Unknown,
6094 call_ret_ty,
6095 )?;
6096 }
6097 crate::codegen::field_guards::attach(emitter, ctx, call_ret_ty)?;
6098 Ok(())
6099}
6100
6101fn emit_class_vtable_dispatch(
6114 emitter: &mut FunctionEmitter,
6115 ctx: &CodegenCtx,
6116 class: &crate::MangledName,
6117 method: &str,
6118 args: &[ExprId],
6119 call_ret_ty: &Type,
6120) -> Result<(), crate::compiler_error::CompilerFailure> {
6121 let struct_idx = ctx
6122 .symbols
6123 .class_struct_type_idx(class)
6124 .ok_or_else(|| crate::codegen::internal_failure("class struct type recorded"))?;
6125 let vtable_idx = ctx
6126 .symbols
6127 .class_vtable_type_idx(class)
6128 .ok_or_else(|| crate::codegen::internal_failure("class vtable type recorded"))?;
6129 let slot = ctx
6130 .symbols
6131 .class_method_slot(class, method)
6132 .ok_or_else(|| crate::codegen::internal_failure("class method slot recorded"))?;
6133 let sig_idx = ctx
6134 .symbols
6135 .class_method_sig(class, method)
6136 .ok_or_else(|| crate::codegen::internal_failure("class method sig recorded"))?;
6137
6138 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6139 nullable: false,
6140 heap_type: HeapType::Concrete(struct_idx),
6141 }))?;
6142 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
6145 emitter.instruction(Instruction::LocalSet(rcv_local));
6146
6147 emitter.instruction(Instruction::LocalGet(rcv_local));
6149 emitter.instruction(Instruction::StructGet {
6150 struct_type_index: struct_idx,
6151 field_index: 0,
6152 });
6153 emitter.instruction(Instruction::StructGet {
6154 struct_type_index: vtable_idx,
6155 field_index: slot,
6156 });
6157 let fn_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6158 nullable: false,
6159 heap_type: HeapType::Concrete(sig_idx),
6160 }))?;
6161 emitter.instruction(Instruction::LocalSet(fn_local));
6162
6163 let abi = ctx.symbols.class_method_abi(class, method).cloned();
6165 emitter.instruction(Instruction::LocalGet(rcv_local));
6166 emit_args_into_slots(
6167 emitter,
6168 ctx,
6169 args,
6170 abi.as_ref().map(|a| a.params.as_slice()),
6171 )?;
6172 emitter.instruction(Instruction::LocalGet(fn_local));
6173 emitter.instruction(Instruction::CallRef(sig_idx));
6174 emit_slot_return_cast(emitter, ctx, call_ret_ty, abi.as_ref())?;
6175 Ok(())
6176}
6177
6178fn emit_intrinsic_call(
6179 emitter: &mut FunctionEmitter,
6180 ctx: &CodegenCtx,
6181 kind: Intrinsic,
6182 args: &[ExprId],
6183 result_ty: &Type,
6184) -> Result<(), crate::compiler_error::CompilerFailure> {
6185 let _: () = match kind {
6186 Intrinsic::Assert => {
6187 if args.len() != 2 {
6191 return Err(crate::codegen::internal_failure(
6192 "assert requires two arguments",
6193 ));
6194 }
6195 emit_expr(emitter, ctx, args[0])?;
6196 cast::emit_condition_to_i32(
6197 emitter,
6198 ctx,
6199 &ctx.ta
6200 .try_expr(args[0])
6201 .map_err(crate::codegen::arena_failure)?
6202 .ty,
6203 )?;
6204 emit_expr(emitter, ctx, args[1])?;
6205 let message_type = &ctx
6206 .ta
6207 .try_expr(args[1])
6208 .map_err(crate::codegen::arena_failure)?
6209 .ty;
6210 let msg_local = emitter.add_anonymous_local(ctx.symbols.value_type(message_type)?)?;
6211 emitter.instruction(Instruction::LocalSet(msg_local));
6212 emitter.instruction(Instruction::I32Eqz);
6213 emitter.emit_if(BlockType::Empty);
6214 emitter.instruction(Instruction::LocalGet(msg_local));
6215 cast::emit_coerce_to_slot(emitter, ctx, message_type, &Type::String)?;
6216 let ctor_idx = ctx
6217 .symbols
6218 .func_idx(&crate::mangle::prelude("Error#constructor"))
6219 .ok_or_else(|| {
6220 crate::codegen::internal_failure("Error#constructor exported from prelude")
6221 })?;
6222 crate::codegen::throw::emit_no_error_options(emitter);
6223 emitter.instruction(Instruction::Call(ctor_idx));
6224 crate::codegen::throw::emit_error_throw(emitter, ctx);
6225 emitter.emit_end();
6226 emit_undefined(emitter, ctx)?;
6227 }
6228 Intrinsic::JsonStringify => {
6229 super::json::emit_stringify(emitter, ctx, args, result_ty)?;
6230 }
6231 Intrinsic::JsonParse => {
6232 super::json::emit_parse(emitter, ctx, args)?;
6233 }
6234 Intrinsic::ToString => {
6235 let [value] = args else {
6236 return Err(crate::codegen::internal_failure(
6237 "string conversion requires one argument",
6238 ));
6239 };
6240 emit_expr(emitter, ctx, *value)?;
6241 cast::emit_box(
6242 emitter,
6243 ctx,
6244 &ctx.ta
6245 .try_expr(*value)
6246 .map_err(crate::codegen::arena_failure)?
6247 .ty,
6248 )?;
6249 let function = ctx
6250 .symbols
6251 .prelude_func_idx("__value_to_string")
6252 .ok_or_else(|| {
6253 crate::codegen::internal_failure("dynamic string conversion collected")
6254 })?;
6255 emitter.instruction(Instruction::Call(function));
6256 }
6257 Intrinsic::ToObject => {
6258 let [value] = args else {
6259 return Err(crate::codegen::internal_failure(
6260 "object conversion requires one argument",
6261 ));
6262 };
6263 emit_expr(emitter, ctx, *value)?;
6264 let function = ctx
6265 .symbols
6266 .prelude_func_idx("ObjectConstructor##toObject")
6267 .ok_or_else(|| {
6268 crate::codegen::internal_failure("object conversion helper collected")
6269 })?;
6270 emitter.instruction(Instruction::Call(function));
6271 }
6272 Intrinsic::BigIntFromString => {
6279 if args.len() != 1 {
6280 return Err(crate::codegen::internal_failure(
6281 "BigInt.fromString requires one argument",
6282 ));
6283 }
6284 emit_expr(emitter, ctx, args[0])?;
6285 let string_type_idx = ctx
6288 .symbols
6289 .string_type_idx()
6290 .ok_or_else(|| crate::codegen::internal_failure("$string registered"))?;
6291 emitter.instruction(Instruction::StructGet {
6292 struct_type_index: string_type_idx,
6293 field_index: 1,
6294 });
6295 let host_idx = ctx
6296 .symbols
6297 .func_idx(&crate::mangle::host(
6298 crate::runtime::BIGINT_MODULE_NAME,
6299 "fromString",
6300 ))
6301 .ok_or_else(|| {
6302 crate::codegen::internal_failure(
6303 "submilli:bigint.fromString import recorded by codegen bootstrap",
6304 )
6305 })?;
6306 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6307 }
6308 };
6309 Ok(())
6310}
6311
6312fn emit_vtable_equality(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: BinOp) {
6317 ctx.latch(emit_vtable_equality_checked(emitter, ctx, op));
6318}
6319
6320fn emit_vtable_equality_checked(
6321 emitter: &mut FunctionEmitter,
6322 ctx: &CodegenCtx,
6323 op: BinOp,
6324) -> Result<(), crate::compiler_error::CompilerFailure> {
6325 let intrinsics = ctx
6326 .symbols
6327 .intrinsic_type_indices()
6328 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6329 let operand_ref = ValType::Ref(RefType {
6332 nullable: true,
6333 heap_type: HeapType::Concrete(intrinsics.object),
6334 });
6335 let object_ref = ValType::Ref(RefType {
6336 nullable: false,
6337 heap_type: HeapType::Concrete(intrinsics.object),
6338 });
6339 let equals_fn_ref = ValType::Ref(RefType {
6340 nullable: false,
6341 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6342 });
6343
6344 let Some(lhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6346 return Ok(());
6347 };
6348 let Some(rhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6349 return Ok(());
6350 };
6351 let Some(lhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6352 return Ok(());
6353 };
6354 let Some(rhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6355 return Ok(());
6356 };
6357 let Some(eq_fn_local) = ctx.latch(emitter.add_anonymous_local(equals_fn_ref)) else {
6358 return Ok(());
6359 };
6360
6361 emitter.instruction(Instruction::LocalSet(rhs_local));
6362 emitter.instruction(Instruction::LocalSet(lhs_local));
6363
6364 emitter.instruction(Instruction::LocalGet(lhs_local));
6365 emitter.instruction(Instruction::RefAsNonNull);
6366 emitter.instruction(Instruction::LocalSet(lhs_object));
6367 emitter.instruction(Instruction::LocalGet(rhs_local));
6368 emitter.instruction(Instruction::RefAsNonNull);
6369 emitter.instruction(Instruction::LocalSet(rhs_object));
6370
6371 emitter.instruction(Instruction::LocalGet(lhs_object));
6372 emitter.instruction(Instruction::StructGet {
6373 struct_type_index: intrinsics.object,
6374 field_index: 0,
6375 });
6376 emitter.instruction(Instruction::StructGet {
6377 struct_type_index: intrinsics.vtable,
6378 field_index: 2,
6379 });
6380 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6381
6382 emitter.instruction(Instruction::LocalGet(lhs_object));
6383 emitter.instruction(Instruction::LocalGet(rhs_object));
6384 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6385 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6386
6387 if matches!(op, BinOp::NotEq) {
6388 emitter.instruction(Instruction::I32Eqz);
6389 }
6390
6391 Ok(())
6392}
6393
6394fn emit_boxed_eq(
6407 emitter: &mut FunctionEmitter,
6408 ctx: &CodegenCtx,
6409 lhs: ExprId,
6410 rhs: ExprId,
6411 op: BinOp,
6412) -> Result<(), crate::compiler_error::CompilerFailure> {
6413 let intrinsics = ctx
6414 .symbols
6415 .intrinsic_type_indices()
6416 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6417 let object_null_ref = ValType::Ref(RefType {
6418 nullable: true,
6419 heap_type: HeapType::Concrete(intrinsics.object),
6420 });
6421 let object_ref = ValType::Ref(RefType {
6422 nullable: false,
6423 heap_type: HeapType::Concrete(intrinsics.object),
6424 });
6425 let equals_fn_ref = ValType::Ref(RefType {
6426 nullable: false,
6427 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6428 });
6429
6430 let lhs_ty = ctx
6431 .ta
6432 .try_expr(lhs)
6433 .map_err(crate::codegen::arena_failure)?
6434 .ty
6435 .clone();
6436 let rhs_ty = ctx
6437 .ta
6438 .try_expr(rhs)
6439 .map_err(crate::codegen::arena_failure)?
6440 .ty
6441 .clone();
6442
6443 let lhs_local = emitter.add_anonymous_local(object_null_ref)?;
6448 let rhs_local = emitter.add_anonymous_local(object_null_ref)?;
6449 let lhs_nonnull_local = emitter.add_anonymous_local(object_ref)?;
6450 let eq_fn_local = emitter.add_anonymous_local(equals_fn_ref)?;
6451
6452 emit_expr(emitter, ctx, lhs)?;
6453 cast::emit_box(emitter, ctx, &lhs_ty)?;
6454 emitter.instruction(Instruction::LocalSet(lhs_local));
6455
6456 emit_expr(emitter, ctx, rhs)?;
6457 cast::emit_box(emitter, ctx, &rhs_ty)?;
6458 emitter.instruction(Instruction::LocalSet(rhs_local));
6459
6460 emitter.instruction(Instruction::LocalGet(lhs_local));
6464 emitter.instruction(Instruction::RefIsNull);
6465 emitter.emit_if(BlockType::Result(ValType::I32));
6466 emitter.instruction(Instruction::LocalGet(rhs_local));
6467 emitter.instruction(Instruction::RefIsNull);
6468 emitter.emit_else();
6469 emitter.instruction(Instruction::LocalGet(rhs_local));
6470 emitter.instruction(Instruction::RefIsNull);
6471 emitter.emit_if(BlockType::Result(ValType::I32));
6472 emitter.instruction(Instruction::I32Const(0));
6473 emitter.emit_else();
6474 emitter.instruction(Instruction::LocalGet(lhs_local));
6476 emitter.instruction(Instruction::RefAsNonNull);
6477 emitter.instruction(Instruction::LocalTee(lhs_nonnull_local));
6478 emitter.instruction(Instruction::StructGet {
6479 struct_type_index: intrinsics.object,
6480 field_index: 0,
6481 });
6482 emitter.instruction(Instruction::StructGet {
6483 struct_type_index: intrinsics.vtable,
6484 field_index: 2,
6485 });
6486 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6487 emitter.instruction(Instruction::LocalGet(lhs_nonnull_local));
6488 emitter.instruction(Instruction::LocalGet(rhs_local));
6489 emitter.instruction(Instruction::RefAsNonNull);
6490 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6491 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6492 emitter.emit_end();
6493 emitter.emit_end();
6494
6495 let _: () = if matches!(op, BinOp::NotEq) {
6496 emitter.instruction(Instruction::I32Eqz);
6497 };
6498 Ok(())
6499}
6500
6501fn emit_bigint_literal(
6507 emitter: &mut FunctionEmitter,
6508 ctx: &CodegenCtx,
6509 digits: &str,
6510) -> Result<(), crate::compiler_error::CompilerFailure> {
6511 use crate::codegen::internal_failure;
6512 if let Some(entry) = ctx.bigints.lookup(digits)? {
6513 let raw_bigint_idx = ctx
6518 .symbols
6519 .raw_bigint_type_idx()
6520 .ok_or_else(|| internal_failure("the $rawBigInt intrinsic is not registered"))?;
6521 let bigint_idx = ctx
6522 .symbols
6523 .bigint_type_idx()
6524 .ok_or_else(|| internal_failure("the $bigint intrinsic is not registered"))?;
6525 let vtable_global = ctx
6526 .symbols
6527 .prelude_global_idx("bigint_vtable")
6528 .ok_or_else(|| internal_failure("bigint_vtable is not imported from the prelude"))?;
6529 let data_idx = crate::codegen::wasm_u32(ctx.strings.strings.len())?
6530 .checked_add(entry.data_idx)
6531 .ok_or_else(|| internal_failure("the module has too many data segments"))?;
6532 emitter.instruction(Instruction::GlobalGet(vtable_global));
6533 emitter.instruction(Instruction::I32Const(i32::from(entry.sign)));
6534 emitter.instruction(Instruction::I32Const(0));
6535 emitter.instruction(Instruction::I32Const(entry.limb_count.cast_signed()));
6536 emitter.instruction(Instruction::ArrayNewData {
6537 array_type_index: raw_bigint_idx,
6538 array_data_index: data_idx,
6539 });
6540 emitter.instruction(Instruction::StructNew(bigint_idx));
6541 return Ok(());
6542 }
6543 let v: i64 = digits
6547 .parse()
6548 .map_err(|_| internal_failure("an unpooled bigint literal is not a safe integer"))?;
6549 let host_idx = ctx
6550 .symbols
6551 .func_idx(&crate::mangle::host(
6552 crate::runtime::BIGINT_MODULE_NAME,
6553 "fromNumber",
6554 ))
6555 .ok_or_else(|| internal_failure("submilli:bigint.fromNumber is not imported"))?;
6556 emitter.instruction(Instruction::F64Const(Ieee64::from(v as f64)));
6557 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6558 Ok(())
6559}
6560
6561fn emit_regex_literal(
6573 emitter: &mut FunctionEmitter,
6574 ctx: &CodegenCtx,
6575 source: &str,
6576 flags: &str,
6577) -> Result<(), crate::compiler_error::CompilerFailure> {
6578 super::emit_const_string_by_text(emitter, ctx, source)?;
6579 super::emit_const_string_by_text(emitter, ctx, flags)?;
6580 let ctor_idx = ctx
6581 .symbols
6582 .func_idx(&crate::mangle::prelude("RegExpConstructor#new"))
6583 .ok_or_else(|| crate::codegen::internal_failure("RegExpConstructor#new is not imported"))?;
6584 emitter.instruction(Instruction::Call(ctor_idx));
6585 Ok(())
6586}
6587
6588fn emit_bigint_extract_to_stack(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
6594 ctx.latch(emit_bigint_extract_to_stack_checked(emitter, ctx));
6595}
6596
6597fn emit_bigint_extract_to_stack_checked(
6598 emitter: &mut FunctionEmitter,
6599 ctx: &CodegenCtx,
6600) -> Result<(), crate::compiler_error::CompilerFailure> {
6601 let bigint_idx = ctx
6602 .symbols
6603 .bigint_type_idx()
6604 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6605 let Some(scratch) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6608 wasm_encoder::RefType {
6609 nullable: false,
6610 heap_type: wasm_encoder::HeapType::Concrete(bigint_idx),
6611 },
6612 ))) else {
6613 return Ok(());
6614 };
6615 emitter.instruction(Instruction::LocalSet(scratch));
6616 emitter.instruction(Instruction::LocalGet(scratch));
6617 emitter.instruction(Instruction::StructGet {
6618 struct_type_index: bigint_idx,
6619 field_index: 1, });
6621 emitter.instruction(Instruction::LocalGet(scratch));
6622 emitter.instruction(Instruction::StructGet {
6623 struct_type_index: bigint_idx,
6624 field_index: 2, });
6626
6627 Ok(())
6628}
6629
6630fn emit_bigint_wrap_host_result(
6635 emitter: &mut FunctionEmitter,
6636 ctx: &CodegenCtx,
6637 host_func_idx: u32,
6638) {
6639 ctx.latch(emit_bigint_wrap_host_result_checked(
6640 emitter,
6641 ctx,
6642 host_func_idx,
6643 ));
6644}
6645
6646fn emit_bigint_wrap_host_result_checked(
6647 emitter: &mut FunctionEmitter,
6648 ctx: &CodegenCtx,
6649 host_func_idx: u32,
6650) -> Result<(), crate::compiler_error::CompilerFailure> {
6651 emitter.instruction(Instruction::Call(host_func_idx));
6652 let bigint_idx = ctx
6653 .symbols
6654 .bigint_type_idx()
6655 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6656 let raw_bigint_idx = ctx
6657 .symbols
6658 .raw_bigint_type_idx()
6659 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6660 let vtable_global = ctx
6661 .symbols
6662 .prelude_global_idx("bigint_vtable")
6663 .ok_or_else(|| {
6664 crate::codegen::internal_failure("bigint_vtable imported from prelude bootstrap")
6665 })?;
6666 let Some(scratch_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6669 wasm_encoder::RefType {
6670 nullable: false,
6671 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6672 },
6673 ))) else {
6674 return Ok(());
6675 };
6676 let Some(scratch_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32))
6677 else {
6678 return Ok(());
6679 };
6680 emitter.instruction(Instruction::LocalSet(scratch_limbs));
6681 emitter.instruction(Instruction::LocalSet(scratch_sign));
6682 emitter.instruction(Instruction::GlobalGet(vtable_global));
6683 emitter.instruction(Instruction::LocalGet(scratch_sign));
6684 emitter.instruction(Instruction::LocalGet(scratch_limbs));
6685 emitter.instruction(Instruction::StructNew(bigint_idx));
6686
6687 Ok(())
6688}
6689
6690fn emit_bigint_binop_inline(
6694 emitter: &mut FunctionEmitter,
6695 ctx: &CodegenCtx,
6696 lhs: ExprId,
6697 rhs: ExprId,
6698 op_name: &str,
6699) -> Result<(), crate::compiler_error::CompilerFailure> {
6700 emit_expr(emitter, ctx, lhs)?;
6701 emit_bigint_extract_to_stack(emitter, ctx);
6702 let raw_bigint_idx = ctx
6703 .symbols
6704 .raw_bigint_type_idx()
6705 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6706 let lhs_limbs =
6707 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6708 nullable: false,
6709 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6710 }))?;
6711 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6712 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6713 emitter.instruction(Instruction::LocalSet(lhs_sign));
6714 emitter.instruction(Instruction::LocalGet(lhs_sign));
6715 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6716 emit_expr(emitter, ctx, rhs)?;
6717 emit_bigint_extract_to_stack(emitter, ctx);
6718 let host_idx = ctx
6719 .symbols
6720 .func_idx(&crate::mangle::host(
6721 crate::runtime::BIGINT_MODULE_NAME,
6722 op_name,
6723 ))
6724 .ok_or_else(|| {
6725 crate::codegen::internal_failure(format!(
6726 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6727 ))
6728 })?;
6729 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6730 Ok(())
6731}
6732
6733fn emit_bigint_pm_one(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: crate::PostfixOp) {
6740 ctx.latch(emit_bigint_pm_one_checked(emitter, ctx, op));
6741}
6742
6743fn emit_bigint_pm_one_checked(
6744 emitter: &mut FunctionEmitter,
6745 ctx: &CodegenCtx,
6746 op: crate::PostfixOp,
6747) -> Result<(), crate::compiler_error::CompilerFailure> {
6748 emit_bigint_extract_to_stack(emitter, ctx);
6749 let raw_bigint_idx = ctx
6750 .symbols
6751 .raw_bigint_type_idx()
6752 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6753 let Some(lhs_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6754 wasm_encoder::RefType {
6755 nullable: false,
6756 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6757 },
6758 ))) else {
6759 return Ok(());
6760 };
6761 let Some(lhs_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32)) else {
6762 return Ok(());
6763 };
6764 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6765 emitter.instruction(Instruction::LocalSet(lhs_sign));
6766 emitter.instruction(Instruction::LocalGet(lhs_sign));
6767 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6768 if ctx.latch(emit_bigint_literal(emitter, ctx, "1")).is_none() {
6769 return Ok(());
6770 }
6771 emit_bigint_extract_to_stack(emitter, ctx);
6772 let op_name = match op {
6773 crate::PostfixOp::Inc => "add",
6774 crate::PostfixOp::Dec => "sub",
6775 crate::PostfixOp::NonNullAssert => {
6776 return Err(crate::codegen::internal_failure(
6777 "non-null assertion is not PostfixUnary",
6778 ));
6779 }
6780 };
6781 let host_idx = ctx
6782 .symbols
6783 .func_idx(&crate::mangle::host(
6784 crate::runtime::BIGINT_MODULE_NAME,
6785 op_name,
6786 ))
6787 .ok_or_else(|| {
6788 crate::codegen::internal_failure(format!(
6789 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6790 ))
6791 })?;
6792 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6793
6794 Ok(())
6795}
6796
6797fn emit_bigint_cmp_inline(
6800 emitter: &mut FunctionEmitter,
6801 ctx: &CodegenCtx,
6802 lhs: ExprId,
6803 rhs: ExprId,
6804 op: BinOp,
6805) -> Result<(), crate::compiler_error::CompilerFailure> {
6806 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6807 emitter.instruction(Instruction::I32Const(0));
6808 let inst = match op {
6809 BinOp::Lt => Instruction::I32LtS,
6810 BinOp::Gt => Instruction::I32GtS,
6811 BinOp::Le => Instruction::I32LeS,
6812 BinOp::Ge => Instruction::I32GeS,
6813 _ => {
6814 return Err(crate::codegen::internal_failure(
6815 "emit_bigint_cmp_inline called with non-cmp op",
6816 ));
6817 }
6818 };
6819 emitter.instruction(inst);
6820 Ok(())
6821}
6822
6823fn emit_bigint_cmp_eq_inline(
6825 emitter: &mut FunctionEmitter,
6826 ctx: &CodegenCtx,
6827 lhs: ExprId,
6828 rhs: ExprId,
6829 op: BinOp,
6830) -> Result<(), crate::compiler_error::CompilerFailure> {
6831 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6832 emitter.instruction(Instruction::I32Eqz);
6833 let _: () = if matches!(op, BinOp::NotEq) {
6834 emitter.instruction(Instruction::I32Eqz);
6835 };
6836 Ok(())
6837}
6838
6839fn emit_bigint_cmp_call(
6842 emitter: &mut FunctionEmitter,
6843 ctx: &CodegenCtx,
6844 lhs: ExprId,
6845 rhs: ExprId,
6846) -> Result<(), crate::compiler_error::CompilerFailure> {
6847 emit_expr(emitter, ctx, lhs)?;
6848 emit_bigint_extract_to_stack(emitter, ctx);
6849 let raw_bigint_idx = ctx
6850 .symbols
6851 .raw_bigint_type_idx()
6852 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6853 let lhs_limbs =
6854 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6855 nullable: false,
6856 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6857 }))?;
6858 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6859 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6860 emitter.instruction(Instruction::LocalSet(lhs_sign));
6861 emitter.instruction(Instruction::LocalGet(lhs_sign));
6862 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6863 emit_expr(emitter, ctx, rhs)?;
6864 emit_bigint_extract_to_stack(emitter, ctx);
6865 let cmp_idx = ctx
6866 .symbols
6867 .func_idx(&crate::mangle::host(
6868 crate::runtime::BIGINT_MODULE_NAME,
6869 "cmp",
6870 ))
6871 .ok_or_else(|| {
6872 crate::codegen::internal_failure(
6873 "submilli:bigint.cmp import recorded by codegen bootstrap",
6874 )
6875 })?;
6876 emitter.instruction(Instruction::Call(cmp_idx));
6877 Ok(())
6878}
6879
6880fn evaluate_object_members(
6882 emitter: &mut FunctionEmitter<'_>,
6883 ctx: &CodegenCtx<'_>,
6884 members: &[TypedObjectMember],
6885) -> Result<HashMap<ExprId, u32>, crate::compiler_error::CompilerFailure> {
6886 let intrinsics = ctx
6887 .symbols
6888 .intrinsic_type_indices()
6889 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
6890 let mut values = HashMap::new();
6891 for member in members {
6892 let TypedObjectMember::Value(value_id) = *member else {
6893 return Err(crate::codegen::internal_failure(
6894 "spread field reached plain object lowering",
6895 ));
6896 };
6897 let value_ty = &ctx
6898 .ta
6899 .try_expr(value_id)
6900 .map_err(crate::codegen::arena_failure)?
6901 .ty;
6902 emit_expr(emitter, ctx, value_id)?;
6903 cast::emit_box(emitter, ctx, value_ty)?;
6904 let local = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
6905 emitter.instruction(Instruction::LocalSet(local));
6906 values.insert(value_id, local);
6907 }
6908 Ok(values)
6909}
6910
6911fn emit_field_holds_value_of(
6914 emitter: &mut FunctionEmitter<'_>,
6915 ctx: &CodegenCtx<'_>,
6916 object: u32,
6917 index: u32,
6918 value: u32,
6919 field_ty: &Type,
6920) -> Result<(), crate::compiler_error::CompilerFailure> {
6921 let intrinsics = ctx
6922 .symbols
6923 .intrinsic_type_indices()
6924 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6925 emitter.instruction(Instruction::LocalGet(object));
6926 emitter.instruction(Instruction::StructGet {
6927 struct_type_index: intrinsics.object_shape,
6928 field_index: 1,
6929 });
6930 emitter.instruction(Instruction::LocalGet(index));
6931 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
6932 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
6933 emitter.instruction(Instruction::LocalGet(value));
6934 emit_is_nullish(emitter, ctx)?;
6935 emitter.instruction(Instruction::I32Eqz);
6936 emitter.instruction(Instruction::I32Or);
6937 let _: () = if field_runtime_type_is_testable(field_ty) {
6938 emit_structural_test(emitter, ctx, value, field_ty, field_ty)?;
6939 emitter.instruction(Instruction::I32And);
6940 };
6941 Ok(())
6942}
6943
6944fn object_ref(object_type: u32) -> ValType {
6945 ValType::Ref(RefType {
6946 nullable: true,
6947 heap_type: HeapType::Concrete(object_type),
6948 })
6949}
6950
6951pub(super) fn emit_index_number(
6953 emitter: &mut FunctionEmitter,
6954 ctx: &CodegenCtx,
6955 ty: &Type,
6956) -> Result<(), crate::compiler_error::CompilerFailure> {
6957 if ty == &Type::Unknown {
6958 let function = ctx
6959 .symbols
6960 .prelude_func_idx("__value_to_index")
6961 .ok_or_else(|| crate::codegen::internal_failure("dynamic index helper collected"))?;
6962 emitter.instruction(Instruction::Call(function));
6963 } else {
6964 cast::emit_coerce_to_slot(emitter, ctx, ty, &Type::Number)?;
6965 };
6966 Ok(())
6967}
6968
6969fn emit_computed_object(
6970 emitter: &mut FunctionEmitter,
6971 ctx: &CodegenCtx,
6972 members: &[TypedObjectMember],
6973) -> Result<(), crate::compiler_error::CompilerFailure> {
6974 let Some(intr) = ctx.require(ctx.symbols.intrinsic_type_indices(), "intrinsics declared")
6975 else {
6976 return Ok(());
6977 };
6978 let Some(merge) = ctx.require(
6979 ctx.symbols.prelude_func_idx("ObjectConstructor##spread"),
6980 "spread helper imported",
6981 ) else {
6982 return Ok(());
6983 };
6984 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
6985 nullable: false,
6986 heap_type: HeapType::Concrete(intr.object_shape),
6987 }))?;
6988 for _ in 0..4 {
6989 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
6990 }
6991 emitter.instruction(Instruction::Call(merge));
6992 emitter.instruction(Instruction::LocalSet(object));
6993 for member in members {
6994 emitter.instruction(Instruction::LocalGet(object));
6995 match member {
6996 TypedObjectMember::Computed { key, value } => {
6997 emit_expr(emitter, ctx, *key)?;
6998 crate::codegen::cast_check::emit_operation_cast_on_stack(
6999 emitter,
7000 ctx,
7001 &ctx.ta
7002 .try_expr(*key)
7003 .map_err(crate::codegen::arena_failure)?
7004 .ty,
7005 &Type::String,
7006 )?;
7007 emit_expr(emitter, ctx, *value)?;
7008 cast::emit_box(
7009 emitter,
7010 ctx,
7011 &ctx.ta
7012 .try_expr(*value)
7013 .map_err(crate::codegen::arena_failure)?
7014 .ty,
7015 )?;
7016 let Some(symbol) = ctx.require(
7017 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7018 "dynamic write imported",
7019 ) else {
7020 return Ok(());
7021 };
7022 emitter.instruction(Instruction::Call(symbol));
7023 }
7024 TypedObjectMember::Spread { source, .. } => {
7025 emit_expr(emitter, ctx, *source)?;
7026 for _ in 0..2 {
7027 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
7028 }
7029 emitter.instruction(Instruction::Call(merge));
7030 emitter.instruction(Instruction::LocalSet(object));
7031 }
7032 TypedObjectMember::Value(_) => {
7033 ctx.fail("computed literal contains an unlowered named field");
7034 return Ok(());
7035 }
7036 }
7037 }
7038 emitter.instruction(Instruction::LocalGet(object));
7039 Ok(())
7040}
7041
7042fn emit_object_index_postfix(
7043 emitter: &mut FunctionEmitter,
7044 ctx: &CodegenCtx,
7045 receiver: ExprId,
7046 index: ExprId,
7047 op: crate::PostfixOp,
7048 read_ty: &Type,
7049) -> Result<(), crate::compiler_error::CompilerFailure> {
7050 emit_expr(emitter, ctx, receiver)?;
7051 cast::emit_box(
7052 emitter,
7053 ctx,
7054 &ctx.ta
7055 .try_expr(receiver)
7056 .map_err(crate::codegen::arena_failure)?
7057 .ty,
7058 )?;
7059 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7060 emitter.instruction(Instruction::LocalSet(object));
7061 emit_expr(emitter, ctx, index)?;
7062 crate::codegen::cast_check::emit_operation_cast_on_stack(
7063 emitter,
7064 ctx,
7065 &ctx.ta
7066 .try_expr(index)
7067 .map_err(crate::codegen::arena_failure)?
7068 .ty,
7069 &Type::String,
7070 )?;
7071 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
7072 emitter.instruction(Instruction::LocalSet(key));
7073 emitter.instruction(Instruction::LocalGet(object));
7074 emitter.instruction(Instruction::LocalGet(key));
7075 let Some(symbol) = ctx.require(
7076 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
7077 "record read imported",
7078 ) else {
7079 return Ok(());
7080 };
7081 emitter.instruction(Instruction::Call(symbol));
7082 crate::codegen::cast_check::emit_operation_cast_on_stack(
7083 emitter,
7084 ctx,
7085 &Type::Unknown,
7086 read_ty,
7087 )?;
7088 cast::emit_box(emitter, ctx, read_ty)?;
7089 emit_postfix_numeric(emitter, ctx, &Type::Unknown);
7090 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7091 emitter.instruction(Instruction::LocalTee(old));
7092 emit_postfix_delta(emitter, ctx, op, &Type::Unknown);
7093 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7094 emitter.instruction(Instruction::LocalSet(updated));
7095 emitter.instruction(Instruction::LocalGet(object));
7096 emitter.instruction(Instruction::LocalGet(key));
7097 emitter.instruction(Instruction::LocalGet(updated));
7098 let Some(symbol) = ctx.require(
7099 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7100 "record write imported",
7101 ) else {
7102 return Ok(());
7103 };
7104 emitter.instruction(Instruction::Call(symbol));
7105 emitter.instruction(Instruction::LocalGet(old));
7106 Ok(())
7107}
7108
7109#[cfg(test)]
7110mod tests {
7111 use super::*;
7112 use crate::codegen::invariant_tests::{assert_internal, with_context};
7113 use crate::{FileId, Span, TypedAst, TypedExpr};
7114
7115 #[test]
7116 fn missing_method_dispatch_is_a_compiler_error() {
7117 with_context(
7118 &TypedAst::new(),
7119 &crate::codegen::SymbolTable::default(),
7120 |ctx| {
7121 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7122 let error = emit_method_call_with_receiver_on_stack(
7123 &mut emitter,
7124 ctx,
7125 &Type::Number,
7126 &crate::mangle::prelude("MissingInterface"),
7127 "missingMethod",
7128 &[],
7129 None,
7130 None,
7131 &Type::Number,
7132 )
7133 .unwrap_err();
7134 assert!(error.to_string().contains("MissingInterface.missingMethod"));
7135 assert_internal(error);
7136 },
7137 );
7138 }
7139
7140 #[test]
7141 fn call_and_chain_metadata_are_checked_before_operands() {
7142 with_context(
7143 &TypedAst::new(),
7144 &crate::codegen::SymbolTable::default(),
7145 |ctx| {
7146 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7147 assert_internal(
7148 emit_intrinsic_call(&mut emitter, ctx, Intrinsic::Assert, &[], &Type::Void)
7149 .unwrap_err(),
7150 );
7151 assert_internal(
7152 emit_intrinsic_call(
7153 &mut emitter,
7154 ctx,
7155 Intrinsic::BigIntFromString,
7156 &[],
7157 &Type::BigInt,
7158 )
7159 .unwrap_err(),
7160 );
7161 assert_internal(
7162 emit_direct_call(
7163 &mut emitter,
7164 ctx,
7165 false,
7166 0,
7167 &[Type::Number],
7168 &Type::Void,
7169 &[],
7170 )
7171 .unwrap_err(),
7172 );
7173 assert_internal(
7174 emit_args_into_slots(&mut emitter, ctx, &[], Some(&[ValType::F64]))
7175 .unwrap_err(),
7176 );
7177 assert_internal(
7178 emit_chain_parts(
7179 &mut emitter,
7180 ctx,
7181 &[],
7182 1,
7183 &Type::Number,
7184 &Type::Number,
7185 None,
7186 )
7187 .unwrap_err(),
7188 );
7189 assert_internal(
7190 emit_chain_parts(
7191 &mut emitter,
7192 ctx,
7193 &[],
7194 0,
7195 &Type::Number,
7196 &Type::Number,
7197 Some(&[]),
7198 )
7199 .unwrap_err(),
7200 );
7201 },
7202 );
7203 }
7204
7205 #[test]
7206 fn uninterned_literal_fails_and_a_fresh_emitter_still_works() {
7207 let mut ta = TypedAst::new();
7208 let string = ta
7209 .try_push_expr(TypedExpr {
7210 kind: TypedExprKind::String("missing".into()),
7211 ty: Type::String,
7212 span: Span::at(FileId(0)),
7213 })
7214 .unwrap();
7215 let number = ta
7216 .try_push_expr(TypedExpr {
7217 kind: TypedExprKind::Number(3.0),
7218 ty: Type::Number,
7219 span: Span::at(FileId(0)),
7220 })
7221 .unwrap();
7222 with_context(&ta, &crate::codegen::SymbolTable::default(), |ctx| {
7223 let mut failed = FunctionEmitter::new(ctx, &[]).unwrap();
7224 assert_internal(emit_expr(&mut failed, ctx, string).unwrap_err());
7225 drop(failed);
7226 let mut healthy = FunctionEmitter::new(ctx, &[]).unwrap();
7227 emit_expr(&mut healthy, ctx, number).unwrap();
7228 healthy.instruction(Instruction::Drop);
7229 healthy.build().unwrap();
7230 });
7231 }
7232}