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::environment(emitter, ctx, type_args)?;
1133 }
1134 emitter.instruction(Instruction::Call(wasm_idx));
1135 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
1136 emit_undefined(emitter, ctx)?;
1137 }
1138 if let Some(ty) = return_cast {
1139 cast::emit_cast_to(emitter, ctx, ty)?;
1140 } else {
1141 cast::emit_coerce_to_slot(emitter, ctx, &target_return, result_ty)?;
1142 }
1143
1144 Ok(())
1145}
1146
1147fn emit_object_literal(
1148 emitter: &mut FunctionEmitter,
1149 ctx: &CodegenCtx,
1150 result_ty: &Type,
1151 members: &[TypedObjectMember],
1152 fields: &[crate::TypedObjectFieldOrigin],
1153) -> Result<(), crate::compiler_error::CompilerFailure> {
1154 if members
1155 .iter()
1156 .any(|member| matches!(member, TypedObjectMember::Computed { .. }))
1157 {
1158 emit_computed_object(emitter, ctx, members)?;
1159 return Ok(());
1160 }
1161 if !matches!(result_ty, Type::Object { .. } | Type::InterfaceRef { .. }) {
1162 return Err(crate::codegen::internal_failure(
1163 "object literal requires an object representation",
1164 ));
1165 }
1166 let structural_ty = crate::typed_ast::object_literal_layout(result_ty, fields);
1167 if members
1168 .iter()
1169 .any(|member| matches!(member, TypedObjectMember::Spread { .. }))
1170 {
1171 emit_object_spread(emitter, ctx, members, &structural_ty)?;
1172 return Ok(());
1173 }
1174 let vtable_idx = ctx
1175 .symbols
1176 .vtable_global_idx(&structural_ty)
1177 .ok_or_else(|| {
1178 crate::codegen::internal_failure("vtable global recorded during object-emission pass")
1179 })?;
1180 let object_shape_idx = ctx
1181 .symbols
1182 .object_subtype_idx(&structural_ty)
1183 .ok_or_else(|| {
1184 crate::codegen::internal_failure(
1185 "object shape type recorded during object-emission pass",
1186 )
1187 })?;
1188 let Type::Object {
1189 fields: declared_fields,
1190 ..
1191 } = &structural_ty
1192 else {
1193 return Err(crate::codegen::internal_failure(
1194 "object literal has no structural representation",
1195 ));
1196 };
1197 let shape_key: Vec<_> = declared_fields
1198 .iter()
1199 .map(|(name, field)| crate::codegen::field_names::FieldName {
1200 name: name.clone(),
1201 optional: field.optional,
1202 is_accessor: false,
1203 is_private: false,
1204 })
1205 .collect();
1206 let intrinsics = ctx
1207 .symbols
1208 .intrinsic_type_indices()
1209 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1210
1211 let evaluated = evaluate_object_members(emitter, ctx, members)?;
1213
1214 emitter.instruction(Instruction::GlobalGet(vtable_idx));
1215 crate::codegen::field_names::emit_instance_names(emitter, ctx, &shape_key, |name| {
1216 fields.iter().any(|field| {
1217 field.name.name == name && !matches!(field.source, TypedObjectFieldSource::Absent(_))
1218 })
1219 })?;
1220
1221 let by_name: std::collections::BTreeMap<&str, &crate::TypedObjectFieldOrigin> =
1226 fields.iter().map(|f| (f.name.name.as_str(), f)).collect();
1227 for (name, field) in declared_fields {
1228 if let Some(origin) = by_name.get(name.as_str()) {
1229 let (TypedObjectFieldSource::Literal(value) | TypedObjectFieldSource::Absent(value)) =
1230 &origin.source
1231 else {
1232 return Err(crate::codegen::internal_failure(
1233 "spread field reached plain object lowering",
1234 ));
1235 };
1236 emitter.instruction(Instruction::LocalGet(*evaluated.get(value).ok_or_else(
1237 || crate::codegen::internal_failure("object field value was not evaluated"),
1238 )?));
1239 } else {
1240 if !field.optional {
1241 return Err(crate::codegen::internal_failure(
1242 "required object field is missing from the literal",
1243 ));
1244 }
1245 emit_undefined(emitter, ctx)?;
1246 }
1247 }
1248 emitter.instruction(Instruction::ArrayNewFixed {
1249 array_type_index: intrinsics.object_fields,
1250 array_size: crate::codegen::wasm_u32(declared_fields.len())?,
1251 });
1252 emitter.instruction(Instruction::RefNull(HeapType::ANY));
1253 emitter.instruction(Instruction::StructNew(object_shape_idx));
1254
1255 Ok(())
1256}
1257
1258fn emit_field_access(
1259 emitter: &mut FunctionEmitter,
1260 ctx: &CodegenCtx,
1261 id: ExprId,
1262 result_ty: &Type,
1263 receiver: &ExprId,
1264 name: &Ident,
1265) -> Result<(), crate::compiler_error::CompilerFailure> {
1266 let receiver_ty = ctx
1267 .ta
1268 .source_type(*receiver)
1269 .map_err(crate::codegen::arena_failure)?
1270 .clone();
1271 if ctx.ta.has_string_index(&receiver_ty) {
1272 emit_expr(emitter, ctx, *receiver)?;
1273 cast::emit_box(
1274 emitter,
1275 ctx,
1276 &ctx.ta
1277 .try_expr(*receiver)
1278 .map_err(crate::codegen::arena_failure)?
1279 .ty,
1280 )?;
1281 let Some(symbol) = ctx.require(
1282 ctx.symbols.field_name_string_global_idx(&name.name),
1283 "property name collected",
1284 ) else {
1285 return Ok(());
1286 };
1287 emitter.instruction(Instruction::GlobalGet(symbol));
1288 let Some(symbol) = ctx.require(
1289 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
1290 "record read imported",
1291 ) else {
1292 return Ok(());
1293 };
1294 emitter.instruction(Instruction::Call(symbol));
1295 let checked_ty = ctx
1296 .ta
1297 .source_type(id)
1298 .map_err(crate::codegen::arena_failure)?;
1299 crate::codegen::cast_check::emit_checked_cast_on_stack(
1300 emitter,
1301 ctx,
1302 &Type::Unknown,
1303 checked_ty,
1304 )?;
1305 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
1306 return Ok(());
1307 }
1308 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
1310 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
1311 emit_receiver(emitter, ctx, *receiver)?;
1312 emit_class_method_value(
1313 emitter,
1314 ctx,
1315 &receiver_ty,
1316 &name.name,
1317 result_ty,
1318 ctx.ta
1319 .source_type(id)
1320 .map_err(crate::codegen::arena_failure)?,
1321 )?;
1322 return Ok(());
1323 }
1324 if ctx.symbols.class_field_slot(mangled, &name.name).is_none() {
1326 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
1327 emit_method_call(
1328 emitter,
1329 ctx,
1330 *receiver,
1331 mangled,
1332 &getter,
1333 &[],
1334 None,
1335 None,
1336 &ctx.ta
1337 .try_expr(id)
1338 .map_err(crate::codegen::arena_failure)?
1339 .ty,
1340 )?;
1341 return Ok(());
1342 }
1343 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
1344 crate::codegen::internal_failure("class struct type recorded in classes::emit")
1345 })?;
1346 let slot = ctx
1347 .symbols
1348 .class_field_slot(mangled, &name.name)
1349 .ok_or_else(|| {
1350 crate::codegen::internal_failure("class field slot recorded in classes::emit")
1351 })?;
1352 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1353 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1354 })?;
1355 emit_receiver(emitter, ctx, *receiver)?;
1356 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&receiver_ty)?)?;
1357 emitter.instruction(Instruction::LocalTee(object));
1358 emitter.instruction(Instruction::StructGet {
1359 struct_type_index: struct_idx,
1360 field_index: 2,
1361 });
1362 emitter.instruction(Instruction::I32Const(slot as i32));
1363 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
1364 if ctx
1365 .symbols
1366 .class_field_narrowing_check(mangled, &name.name)
1367 .is_some()
1368 {
1369 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
1370 }
1371 emit_class_field_slot_cast_as(
1372 emitter,
1373 ctx,
1374 mangled,
1375 &name.name,
1376 &ctx.ta
1377 .try_expr(id)
1378 .map_err(crate::codegen::arena_failure)?
1379 .ty,
1380 ctx.ta
1381 .source_type(id)
1382 .map_err(crate::codegen::arena_failure)?,
1383 )?;
1384 return Ok(());
1385 }
1386 let intrinsics = ctx
1387 .symbols
1388 .intrinsic_type_indices()
1389 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1390 let field_ty = ctx
1393 .ta
1394 .try_expr(id)
1395 .map_err(crate::codegen::arena_failure)?
1396 .ty
1397 .clone();
1398 emit_receiver(emitter, ctx, *receiver)?;
1401 let rcv_local = stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
1402 emit_object_property_read_as(
1403 emitter,
1404 ctx,
1405 rcv_local,
1406 &name.name,
1407 &field_ty,
1408 ctx.ta
1409 .source_type(id)
1410 .map_err(crate::codegen::arena_failure)?,
1411 )?;
1412
1413 Ok(())
1414}
1415
1416fn emit_closure_value(
1417 emitter: &mut FunctionEmitter,
1418 ctx: &CodegenCtx,
1419 id: ExprId,
1420 result_ty: &Type,
1421 params: &[crate::TypedParam],
1422 captured: &[crate::CapturedVar],
1423 runtime_generics: &[String],
1424) -> Result<(), crate::compiler_error::CompilerFailure> {
1425 let closure_struct_idx = ctx
1443 .symbols
1444 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
1445 .ok_or_else(|| {
1446 crate::codegen::internal_failure(
1447 "closure struct type registered for every closure-typed expression",
1448 )
1449 })?;
1450 let env_type_idx = ctx.symbols.env_type_idx(id).ok_or_else(|| {
1451 crate::codegen::internal_failure("env type registered for every closure expression")
1452 })?;
1453 let closure_func_idx = ctx
1454 .symbols
1455 .closure_func_idx(id)
1456 .ok_or_else(|| crate::codegen::internal_failure("closure body function index allocated"))?;
1457 let closure_vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
1458 crate::codegen::internal_failure("closure vtable global emitted whenever closures exist")
1459 })?;
1460
1461 emitter.instruction(Instruction::GlobalGet(closure_vtable_idx));
1463
1464 emitter.instruction(Instruction::RefFunc(closure_func_idx));
1466
1467 for c in captured {
1470 emit_captured_load(emitter, ctx, c)?;
1471 }
1472 if !runtime_generics.is_empty() {
1473 let types: Vec<_> = runtime_generics
1474 .iter()
1475 .cloned()
1476 .map(Type::TypeVar)
1477 .collect();
1478 crate::codegen::runtime_descriptors::environment(emitter, ctx, &types)?;
1479 }
1480 let self_environment = if ctx.ta.closure_names.contains_key(&id) {
1481 emitter.instruction(Instruction::RefNull(HeapType::Concrete(
1482 ctx.symbols
1483 .intrinsic_type_indices()
1484 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
1485 .object,
1486 )));
1487 Some(emitter.add_anonymous_local(ValType::Ref(RefType {
1488 nullable: false,
1489 heap_type: HeapType::Concrete(env_type_idx),
1490 }))?)
1491 } else {
1492 None
1493 };
1494 emitter.instruction(Instruction::StructNew(env_type_idx));
1495 if let Some(local) = self_environment {
1496 emitter.instruction(Instruction::LocalTee(local));
1497 }
1498 if let Some(metadata) = crate::codegen::call_arguments::typed_metadata(params) {
1499 crate::codegen::call_arguments::wrap(emitter, ctx, &metadata)?;
1500 }
1501
1502 if ctx.ta.closure_this.contains_key(&id) {
1503 crate::codegen::this_binding::wrap(emitter, ctx)?;
1504 }
1505 emitter.instruction(Instruction::I64Const(0));
1509 emitter.instruction(Instruction::StructNew(closure_struct_idx));
1510 if let Some(environment) = self_environment {
1511 let closure = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1512 emitter.instruction(Instruction::LocalSet(closure));
1513 emitter.instruction(Instruction::LocalGet(environment));
1514 emitter.instruction(Instruction::LocalGet(closure));
1515 emitter.instruction(Instruction::StructSet {
1516 struct_type_index: env_type_idx,
1517 field_index: crate::codegen::wasm_u32(
1518 captured
1519 .len()
1520 .checked_add(usize::from(!runtime_generics.is_empty()))
1521 .ok_or_else(|| {
1522 crate::codegen::internal_failure("closure capture count overflow")
1523 })?,
1524 )?,
1525 });
1526 emitter.instruction(Instruction::LocalGet(closure));
1527 }
1528
1529 Ok(())
1530}
1531
1532fn inferred_never(
1535 ctx: &CodegenCtx,
1536 id: ExprId,
1537) -> Result<bool, crate::compiler_error::CompilerFailure> {
1538 let ty = ctx
1539 .ta
1540 .source_type(id)
1541 .map_err(crate::codegen::arena_failure)?;
1542 Ok(matches!(ty, Type::Never))
1543}
1544
1545fn emit_local_narrow_ref(
1546 emitter: &mut FunctionEmitter<'_>,
1547 ctx: &CodegenCtx<'_>,
1548 binding: &Ident,
1549 path: &ReferencePath,
1550 narrowed_ty: &Type,
1551) -> Result<(), crate::compiler_error::CompilerFailure> {
1552 if !path.chain.is_empty()
1553 && let Some(source) = emitter.narrow_source(&binding.name)
1554 {
1555 emit_expr(emitter, ctx, source)?;
1558 crate::codegen::cast_check::emit_checked_cast_on_stack(
1559 emitter,
1560 ctx,
1561 &ctx.ta
1562 .try_expr(source)
1563 .map_err(crate::codegen::arena_failure)?
1564 .ty,
1565 narrowed_ty,
1566 )?;
1567 return Ok(());
1568 }
1569 let Some((slot, slot_ty)) = emitter.narrowed_read_slot(&binding.name) else {
1570 if let BindingId::Local { name, .. } = &path.root
1575 && let Some((slot, slot_ty)) = emitter.narrowed_read_slot(name)
1576 {
1577 emitter.instruction(Instruction::LocalGet(slot));
1578 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1579 if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1580 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1581 }
1582 return Ok(());
1583 }
1584 if let BindingId::Global(mangled) = &path.root {
1585 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1586 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1587 })?;
1588 emitter.instruction(Instruction::GlobalGet(idx));
1589 let source_ty = ctx.symbols.global_type(mangled).ok_or_else(|| {
1590 crate::codegen::internal_failure("language globals retain their declared type")
1591 })?;
1592 crate::codegen::cast_check::emit_checked_cast_on_stack(
1593 emitter,
1594 ctx,
1595 source_ty,
1596 narrowed_ty,
1597 )?;
1598 return Ok(());
1599 }
1600 if let Some(source) = emitter.narrow_source(&binding.name) {
1601 emit_expr(emitter, ctx, source)?;
1602 let source_ty = &ctx
1603 .ta
1604 .try_expr(source)
1605 .map_err(crate::codegen::arena_failure)?
1606 .ty;
1607 let cast_info = cast_info_for(source_ty.clone(), narrowed_ty.clone());
1608 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1609 return Ok(());
1610 }
1611 return Err(crate::codegen::internal_failure(format!(
1612 "narrow binding `{}` (path {path:?}) not registered in codegen scope — \
1613 the region's source names a shadow from a scope that already closed",
1614 binding.name,
1615 )));
1616 };
1617 emitter.instruction(Instruction::LocalGet(slot));
1618 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
1619 let _: () = if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
1620 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
1621 };
1622 Ok(())
1623}
1624
1625fn unbox_if_boxed(
1633 emitter: &mut FunctionEmitter<'_>,
1634 ctx: &CodegenCtx<'_>,
1635 slot_ty: ValType,
1636) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1637 let ValType::Ref(RefType {
1638 heap_type: HeapType::Concrete(idx),
1639 ..
1640 }) = slot_ty
1641 else {
1642 return Ok(slot_ty);
1643 };
1644 let Some(payload) = ctx.symbols.box_payload_type(idx) else {
1645 return Ok(slot_ty);
1646 };
1647 let local = emitter.add_anonymous_local(slot_ty)?;
1648 emitter.instruction(Instruction::LocalSet(local));
1649 super::binding_cells::read(emitter, ctx, local, idx)?;
1650 Ok(payload)
1651}
1652
1653fn emit_array_literal(
1656 emitter: &mut FunctionEmitter,
1657 ctx: &CodegenCtx,
1658 elements: &[crate::TypedArrayElement],
1659) -> Result<(), crate::compiler_error::CompilerFailure> {
1660 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1681 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1682 })?;
1683 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1684 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1685 })?;
1686 let array_vtable_global = ctx
1687 .symbols
1688 .prelude_global_idx("array_vtable")
1689 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1690
1691 let has_spread = elements
1692 .iter()
1693 .any(|e| matches!(e, crate::TypedArrayElement::Spread(_)));
1694
1695 if has_spread {
1696 emit_spread_array_literal(emitter, ctx, elements)?;
1697 } else {
1698 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1699 for el in elements {
1700 let elem_id = el.expr_id();
1701 emit_expr(emitter, ctx, elem_id)?;
1702 let elem_ty = ctx
1703 .ta
1704 .try_expr(elem_id)
1705 .map_err(crate::codegen::arena_failure)?
1706 .ty
1707 .clone();
1708 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &elem_ty)?;
1709 }
1710 emitter.instruction(Instruction::ArrayNewFixed {
1711 array_type_index: raw_array_idx,
1712 array_size: crate::codegen::wasm_u32(elements.len())?,
1713 });
1714 emitter.instruction(Instruction::I32Const(
1715 i32::try_from(elements.len())
1716 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1717 ));
1718 emitter.instruction(Instruction::StructNew(array_idx));
1719 }
1720 Ok(())
1721}
1722
1723fn emit_tuple_literal(
1724 emitter: &mut FunctionEmitter,
1725 ctx: &CodegenCtx,
1726 elements: &[ExprId],
1727) -> Result<(), crate::compiler_error::CompilerFailure> {
1728 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
1730 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1731 })?;
1732 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
1733 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
1734 })?;
1735 let array_vtable_global = ctx
1736 .symbols
1737 .prelude_global_idx("array_vtable")
1738 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
1739 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
1740 for &elem_id in elements {
1741 emit_expr(emitter, ctx, elem_id)?;
1742 crate::codegen::function_emitter::cast::emit_box(
1743 emitter,
1744 ctx,
1745 &ctx.ta
1746 .try_expr(elem_id)
1747 .map_err(crate::codegen::arena_failure)?
1748 .ty,
1749 )?;
1750 }
1751 emitter.instruction(Instruction::ArrayNewFixed {
1752 array_type_index: raw_array_idx,
1753 array_size: crate::codegen::wasm_u32(elements.len())?,
1754 });
1755 emitter.instruction(Instruction::I32Const(
1756 i32::try_from(elements.len())
1757 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
1758 ));
1759 emitter.instruction(Instruction::StructNew(array_idx));
1760 Ok(())
1761}
1762
1763fn emit_spread_array_literal(
1764 emitter: &mut FunctionEmitter,
1765 ctx: &CodegenCtx,
1766 elements: &[crate::TypedArrayElement],
1767) -> Result<(), crate::compiler_error::CompilerFailure> {
1768 let array = ctx
1769 .symbols
1770 .array_type_idx()
1771 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?;
1772 let raw = ctx
1773 .symbols
1774 .raw_array_type_idx()
1775 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?;
1776 let raw_type = ValType::Ref(RefType {
1777 nullable: false,
1778 heap_type: HeapType::Concrete(raw),
1779 });
1780 let total = emitter.add_anonymous_local(ValType::I32)?;
1781 let offset = emitter.add_anonymous_local(ValType::I32)?;
1782 let destination = emitter.add_anonymous_local(raw_type)?;
1783 emitter.instruction(Instruction::I32Const(0));
1784 emitter.instruction(Instruction::LocalSet(total));
1785 let mut chunks = Vec::with_capacity(elements.len());
1788 for element in elements {
1789 emit_literal_chunk(emitter, ctx, element, array, raw)?;
1790 let chunk = emitter.add_anonymous_local(raw_type)?;
1791 emitter.instruction(Instruction::LocalTee(chunk));
1792 emitter.instruction(Instruction::ArrayLen);
1793 emitter.instruction(Instruction::LocalGet(total));
1794 emitter.instruction(Instruction::I32Add);
1795 emitter.instruction(Instruction::LocalSet(total));
1796 chunks.push(chunk);
1797 }
1798 emitter.instruction(Instruction::LocalGet(total));
1799 emitter.instruction(Instruction::ArrayNewDefault(raw));
1800 emitter.instruction(Instruction::LocalSet(destination));
1801 emitter.instruction(Instruction::I32Const(0));
1802 emitter.instruction(Instruction::LocalSet(offset));
1803 for chunk in chunks {
1804 emitter.instruction(Instruction::LocalGet(destination));
1805 emitter.instruction(Instruction::LocalGet(offset));
1806 emitter.instruction(Instruction::LocalGet(chunk));
1807 emitter.instruction(Instruction::I32Const(0));
1808 emitter.instruction(Instruction::LocalGet(chunk));
1809 emitter.instruction(Instruction::ArrayLen);
1810 emitter.instruction(Instruction::ArrayCopy {
1811 array_type_index_dst: raw,
1812 array_type_index_src: raw,
1813 });
1814 emitter.instruction(Instruction::LocalGet(offset));
1815 emitter.instruction(Instruction::LocalGet(chunk));
1816 emitter.instruction(Instruction::ArrayLen);
1817 emitter.instruction(Instruction::I32Add);
1818 emitter.instruction(Instruction::LocalSet(offset));
1819 }
1820 emitter.instruction(Instruction::GlobalGet(
1821 ctx.symbols
1822 .prelude_global_idx("array_vtable")
1823 .ok_or_else(|| crate::codegen::internal_failure("array vtable declared"))?,
1824 ));
1825 emitter.instruction(Instruction::LocalGet(destination));
1826 emitter.instruction(Instruction::LocalGet(total));
1827 emitter.instruction(Instruction::StructNew(array));
1828 Ok(())
1829}
1830
1831fn emit_literal_chunk(
1832 emitter: &mut FunctionEmitter,
1833 ctx: &CodegenCtx,
1834 element: &crate::TypedArrayElement,
1835 array: u32,
1836 raw: u32,
1837) -> Result<(), crate::compiler_error::CompilerFailure> {
1838 if let crate::TypedArrayElement::Value(value) = element {
1839 emit_expr(emitter, ctx, *value)?;
1840 cast::emit_box(
1841 emitter,
1842 ctx,
1843 &ctx.ta
1844 .try_expr(*value)
1845 .map_err(crate::codegen::arena_failure)?
1846 .ty,
1847 )?;
1848 emitter.instruction(Instruction::ArrayNewFixed {
1849 array_type_index: raw,
1850 array_size: 1,
1851 });
1852 return Ok(());
1853 }
1854 emit_receiver(emitter, ctx, element.expr_id())?;
1857 let raw_type = ValType::Ref(RefType {
1858 nullable: false,
1859 heap_type: HeapType::Concrete(raw),
1860 });
1861 let source = emitter.add_anonymous_local(raw_type)?;
1862 let snapshot = emitter.add_anonymous_local(raw_type)?;
1863 let length = stash_array_length(emitter, array)?;
1864 emitter.instruction(Instruction::StructGet {
1865 struct_type_index: array,
1866 field_index: 1,
1867 });
1868 emitter.instruction(Instruction::LocalSet(source));
1869 emitter.instruction(Instruction::LocalGet(length));
1870 emitter.instruction(Instruction::ArrayNewDefault(raw));
1871 emitter.instruction(Instruction::LocalTee(snapshot));
1872 emitter.instruction(Instruction::I32Const(0));
1873 emitter.instruction(Instruction::LocalGet(source));
1874 emitter.instruction(Instruction::I32Const(0));
1875 emitter.instruction(Instruction::LocalGet(length));
1876 emitter.instruction(Instruction::ArrayCopy {
1877 array_type_index_dst: raw,
1878 array_type_index_src: raw,
1879 });
1880 emitter.instruction(Instruction::LocalGet(snapshot));
1881 Ok(())
1882}
1883
1884fn emit_postfix_unary(
1888 emitter: &mut FunctionEmitter<'_>,
1889 ctx: &CodegenCtx<'_>,
1890 op: crate::PostfixOp,
1891 target: &crate::PostfixTarget,
1892 result_ty: &Type,
1893) -> Result<(), crate::compiler_error::CompilerFailure> {
1894 let _: () = match target {
1895 crate::PostfixTarget::Local {
1896 ident,
1897 boxed,
1898 target_ty,
1899 } => {
1900 let slot = emitter.write_slot(&ident.name)?;
1902 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1903 let box_idx = if *boxed {
1904 Some(ctx.symbols.box_type_idx(target_ty)?.ok_or_else(|| {
1905 crate::codegen::internal_failure("box type registered for postfix operand")
1906 })?)
1907 } else {
1908 None
1909 };
1910 if let Some(box_idx) = box_idx {
1911 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
1912 } else {
1913 emitter.instruction(Instruction::LocalGet(slot));
1914 }
1915 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1916 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1917 emit_postfix_numeric(emitter, ctx, result_ty);
1918 emitter.instruction(Instruction::LocalSet(old));
1919 if box_idx.is_some() {
1920 emitter.instruction(Instruction::LocalGet(slot));
1921 }
1922 emitter.instruction(Instruction::LocalGet(old));
1923 emit_postfix_delta(emitter, ctx, op, result_ty);
1924 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1925 if let Some(box_idx) = box_idx {
1926 emitter.instruction(Instruction::StructSet {
1927 struct_type_index: box_idx,
1928 field_index: 0,
1929 });
1930 } else {
1931 emitter.instruction(Instruction::LocalSet(slot));
1932 }
1933 emitter.instruction(Instruction::LocalGet(old));
1934 }
1935 crate::PostfixTarget::Global {
1936 mangled, target_ty, ..
1937 } => {
1938 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
1939 crate::codegen::internal_failure("Inferer guarantees the binding exists")
1940 })?;
1941 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1942 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
1943 emitter.instruction(Instruction::GlobalGet(idx));
1944 if let ValType::Ref(RefType {
1946 nullable: false, ..
1947 }) = ctx.symbols.value_type(target_ty)?
1948 {
1949 emitter.instruction(Instruction::RefAsNonNull);
1950 }
1951 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
1952 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
1953 emit_postfix_numeric(emitter, ctx, result_ty);
1954 emitter.instruction(Instruction::LocalSet(old));
1955 emitter.instruction(Instruction::LocalGet(old));
1956 emit_postfix_delta(emitter, ctx, op, result_ty);
1957 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
1958 emitter.instruction(Instruction::GlobalSet(idx));
1959 emitter.instruction(Instruction::LocalGet(old));
1960 }
1961 crate::PostfixTarget::Field {
1962 receiver,
1963 name,
1964 target_ty,
1965 } => {
1966 let receiver_ty = ctx
1971 .ta
1972 .source_type(*receiver)
1973 .map_err(crate::codegen::arena_failure)?
1974 .clone();
1975 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
1976 emit_class_field_postfix(
1977 emitter, ctx, *receiver, mangled, &name.name, op, target_ty,
1978 )?;
1979 return Ok(());
1980 }
1981 if let Some((getter, setter)) = host_property_accessors(ctx, &receiver_ty, name)? {
1982 emit_host_property_postfix(
1983 emitter,
1984 ctx,
1985 *receiver,
1986 &receiver_ty,
1987 (getter, setter),
1988 op,
1989 target_ty,
1990 )?;
1991 return Ok(());
1992 }
1993 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1995 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1996 })?;
1997 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
1998 emit_receiver(emitter, ctx, *receiver)?;
1999 let rcv_local =
2000 stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
2001 emit_object_property_read(emitter, ctx, rcv_local, &name.name, target_ty)?;
2002 emit_postfix_numeric(emitter, ctx, target_ty);
2003 emitter.instruction(Instruction::LocalSet(tmp));
2004 emitter.instruction(Instruction::LocalGet(tmp));
2005 emit_postfix_delta(emitter, ctx, op, target_ty);
2006 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2007 emitter.instruction(Instruction::LocalSet(updated));
2008 emit_object_property_write_value(
2009 emitter,
2010 ctx,
2011 rcv_local,
2012 name,
2013 &ShapeArgument::Local {
2014 slot: updated,
2015 ty: target_ty.clone(),
2016 },
2017 )?;
2018 emitter.instruction(Instruction::LocalGet(tmp));
2019 }
2020 crate::PostfixTarget::Index {
2021 receiver, index, ..
2022 } => {
2023 emit_index_postfix(emitter, ctx, *receiver, *index, op)?;
2024 }
2025 };
2026 Ok(())
2027}
2028
2029enum HostPropertyGetter {
2031 Func(u32),
2032 InlineLength(u32),
2034}
2035
2036fn host_property_accessors(
2039 ctx: &CodegenCtx,
2040 receiver_ty: &Type,
2041 name: &Ident,
2042) -> Result<Option<(HostPropertyGetter, u32)>, crate::compiler_error::CompilerFailure> {
2043 let Some(iface) = crate::codegen::host_property_iface(receiver_ty) else {
2044 return Ok(None);
2045 };
2046 let setter_key = crate::mangle::extend(&iface, &crate::mangle::property_setter(&name.name));
2047 let Some(setter) = ctx.symbols.func_idx(&setter_key) else {
2048 return Ok(None);
2049 };
2050 if let Some(struct_idx) = inline_length_struct_idx(ctx, &iface, &name.name)? {
2051 return Ok(Some((HostPropertyGetter::InlineLength(struct_idx), setter)));
2052 }
2053 let getter = ctx
2054 .symbols
2055 .func_idx(&crate::mangle::extend(&iface, &name.name))
2056 .ok_or_else(|| {
2057 crate::codegen::internal_failure("host property getter recorded beside its setter")
2058 })?;
2059 Ok(Some((HostPropertyGetter::Func(getter), setter)))
2060}
2061
2062fn emit_host_property_postfix(
2063 emitter: &mut FunctionEmitter,
2064 ctx: &CodegenCtx,
2065 receiver: ExprId,
2066 receiver_ty: &Type,
2067 (getter, setter): (HostPropertyGetter, u32),
2068 op: crate::PostfixOp,
2069 target_ty: &Type,
2070) -> Result<(), crate::compiler_error::CompilerFailure> {
2071 let recv = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2073 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Number)?)?;
2074 emit_receiver(emitter, ctx, receiver)?;
2075 emitter.instruction(Instruction::LocalSet(recv));
2076 emitter.instruction(Instruction::LocalGet(recv));
2078 emitter.instruction(Instruction::RefAsNonNull);
2079 emitter.instruction(Instruction::LocalGet(recv));
2080 emitter.instruction(Instruction::RefAsNonNull);
2081 match getter {
2082 HostPropertyGetter::Func(getter) => {
2083 emitter.instruction(Instruction::Call(getter));
2084 }
2085 HostPropertyGetter::InlineLength(struct_idx) => {
2086 emit_inline_length(emitter, ctx, struct_idx);
2087 }
2088 }
2089 emitter.instruction(Instruction::LocalTee(old));
2090 emit_postfix_delta(emitter, ctx, op, &Type::Number);
2091 emitter.instruction(Instruction::Call(setter));
2092 emitter.instruction(Instruction::LocalGet(old));
2093 if target_ty != &Type::Number {
2094 cast::emit_box(emitter, ctx, &Type::Number)?;
2095 }
2096 Ok(())
2097}
2098
2099fn emit_index_postfix(
2100 emitter: &mut FunctionEmitter,
2101 ctx: &CodegenCtx,
2102 receiver: ExprId,
2103 index: ExprId,
2104 op: crate::PostfixOp,
2105) -> Result<(), crate::compiler_error::CompilerFailure> {
2106 let is_uint8 = ctx
2107 .ta
2108 .source_type(receiver)
2109 .map_err(crate::codegen::arena_failure)?
2110 .lowered()
2111 == &Type::Uint8Array;
2112 let result_ty = if is_uint8 {
2113 Type::Number
2114 } else {
2115 Type::Unknown
2116 };
2117 emit_expr(emitter, ctx, receiver)?;
2118 cast::emit_box(
2119 emitter,
2120 ctx,
2121 &ctx.ta
2122 .try_expr(receiver)
2123 .map_err(crate::codegen::arena_failure)?
2124 .ty,
2125 )?;
2126 let receiver_local = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
2127 emitter.instruction(Instruction::LocalSet(receiver_local));
2128 emit_expr(emitter, ctx, index)?;
2129 let key = emitter.add_anonymous_local(
2130 ctx.symbols.value_type(
2131 &ctx.ta
2132 .try_expr(index)
2133 .map_err(crate::codegen::arena_failure)?
2134 .ty,
2135 )?,
2136 )?;
2137 emitter.instruction(Instruction::LocalSet(key));
2138 let (backing, position, raw_type) = emit_index_location(
2139 emitter,
2140 ctx,
2141 receiver_local,
2142 key,
2143 &ctx.ta
2144 .try_expr(index)
2145 .map_err(crate::codegen::arena_failure)?
2146 .ty,
2147 is_uint8,
2148 )?;
2149 emitter.instruction(Instruction::LocalGet(backing));
2150 emitter.instruction(Instruction::LocalGet(position));
2151 if is_uint8 {
2152 emitter.instruction(Instruction::ArrayGetU(raw_type));
2153 emitter.instruction(Instruction::F64ConvertI32U);
2154 } else {
2155 emitter.instruction(Instruction::ArrayGet(raw_type));
2156 }
2157 emit_postfix_numeric(emitter, ctx, &result_ty);
2158 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2159 emitter.instruction(Instruction::LocalTee(old));
2160 emit_postfix_delta(emitter, ctx, op, &result_ty);
2161 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2162 emitter.instruction(Instruction::LocalSet(updated));
2163 let (backing, position, _) = emit_index_location(
2166 emitter,
2167 ctx,
2168 receiver_local,
2169 key,
2170 &ctx.ta
2171 .try_expr(index)
2172 .map_err(crate::codegen::arena_failure)?
2173 .ty,
2174 is_uint8,
2175 )?;
2176 emitter.instruction(Instruction::LocalGet(backing));
2177 emitter.instruction(Instruction::LocalGet(position));
2178 emitter.instruction(Instruction::LocalGet(updated));
2179 if is_uint8 {
2180 emitter.instruction(Instruction::I32TruncSatF64S);
2181 }
2182 emitter.instruction(Instruction::ArraySet(raw_type));
2183 emitter.instruction(Instruction::LocalGet(old));
2184 Ok(())
2185}
2186
2187fn emit_index_location(
2188 emitter: &mut FunctionEmitter,
2189 ctx: &CodegenCtx,
2190 receiver: u32,
2191 key: u32,
2192 key_ty: &Type,
2193 is_uint8: bool,
2194) -> Result<(u32, u32, u32), crate::compiler_error::CompilerFailure> {
2195 let (container, raw, ty) = if is_uint8 {
2196 (
2197 ctx.symbols
2198 .uint8_array_type_idx()
2199 .ok_or_else(|| crate::codegen::internal_failure("Uint8Array declared"))?,
2200 ctx.symbols
2201 .raw_uint8_array_type_idx()
2202 .ok_or_else(|| crate::codegen::internal_failure("raw Uint8Array declared"))?,
2203 Type::Uint8Array,
2204 )
2205 } else {
2206 (
2207 ctx.symbols
2208 .array_type_idx()
2209 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?,
2210 ctx.symbols
2211 .raw_array_type_idx()
2212 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?,
2213 Type::Array(Box::new(Type::Unknown)),
2214 )
2215 };
2216 emitter.instruction(Instruction::LocalGet(receiver));
2217 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, &Type::Unknown, &ty)?;
2218 emitter.instruction(Instruction::LocalGet(key));
2219 emit_index_number(emitter, ctx, key_ty)?;
2220 let operand = stash_index_operand(emitter)?;
2221 let length = (!is_uint8)
2222 .then(|| stash_array_length(emitter, container))
2223 .transpose()?;
2224 emitter.instruction(Instruction::StructGet {
2225 struct_type_index: container,
2226 field_index: 1,
2227 });
2228 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
2229 nullable: false,
2230 heap_type: HeapType::Concrete(raw),
2231 }))?;
2232 emitter.instruction(Instruction::LocalSet(backing));
2233 let position = match length {
2234 Some(length) => emit_checked_index_with_length(emitter, ctx, length, operand)?,
2235 None => emit_checked_index(emitter, ctx, backing, operand)?,
2236 };
2237 Ok((backing, position, raw))
2238}
2239
2240fn emit_postfix_numeric(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, ty: &Type) {
2241 ctx.latch(emit_postfix_numeric_checked(emitter, ctx, ty));
2242}
2243
2244fn emit_postfix_numeric_checked(
2245 emitter: &mut FunctionEmitter,
2246 ctx: &CodegenCtx,
2247 ty: &Type,
2248) -> Result<(), crate::compiler_error::CompilerFailure> {
2249 if matches!(ty, Type::Unknown) {
2250 let function = ctx
2251 .symbols
2252 .prelude_func_idx("__value_numeric")
2253 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2254 emitter.instruction(Instruction::Call(function));
2255 }
2256
2257 Ok(())
2258}
2259
2260fn emit_postfix_delta(
2261 emitter: &mut FunctionEmitter<'_>,
2262 ctx: &CodegenCtx<'_>,
2263 op: crate::PostfixOp,
2264 ty: &Type,
2265) {
2266 ctx.latch(emit_postfix_delta_checked(emitter, ctx, op, ty));
2267}
2268
2269fn emit_postfix_delta_checked(
2270 emitter: &mut FunctionEmitter<'_>,
2271 ctx: &CodegenCtx<'_>,
2272 op: crate::PostfixOp,
2273 ty: &Type,
2274) -> Result<(), crate::compiler_error::CompilerFailure> {
2275 if matches!(ty, Type::Unknown) {
2276 let name = if matches!(op, crate::PostfixOp::Inc) {
2277 "inc"
2278 } else {
2279 "dec"
2280 };
2281 let function = ctx
2282 .symbols
2283 .prelude_func_idx(&format!("__value_{name}"))
2284 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2285 emitter.instruction(Instruction::Call(function));
2286 return Ok(());
2287 }
2288 if ty.is_bigint() {
2289 emit_bigint_pm_one(emitter, ctx, op);
2290 return Ok(());
2291 }
2292 emitter.instruction(Instruction::F64Const(Ieee64::from(1.0)));
2293 emitter.instruction(match op {
2294 crate::PostfixOp::Inc => Instruction::F64Add,
2295 crate::PostfixOp::Dec => Instruction::F64Sub,
2296 crate::PostfixOp::NonNullAssert => {
2297 return Err(crate::codegen::internal_failure(
2298 "non-null assertion is not PostfixUnary",
2299 ));
2300 }
2301 });
2302
2303 Ok(())
2304}
2305
2306#[allow(clippy::too_many_arguments)]
2311fn emit_class_field_postfix(
2312 emitter: &mut FunctionEmitter<'_>,
2313 ctx: &CodegenCtx<'_>,
2314 receiver: ExprId,
2315 mangled: &crate::MangledName,
2316 field: &str,
2317 op: crate::PostfixOp,
2318 target_ty: &Type,
2319) -> Result<(), crate::compiler_error::CompilerFailure> {
2320 let intrinsics = ctx
2321 .symbols
2322 .intrinsic_type_indices()
2323 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
2324 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
2325 crate::codegen::internal_failure("class struct type recorded in classes::emit")
2326 })?;
2327 let slot = ctx
2330 .symbols
2331 .class_field_slot(mangled, field)
2332 .ok_or_else(|| {
2333 crate::codegen::internal_failure(
2334 "data-field slot recorded in classes::emit — accessors are rejected in infer",
2335 )
2336 })?;
2337 let fields_local = emitter.add_anonymous_local(ValType::Ref(RefType {
2338 nullable: false,
2339 heap_type: HeapType::Concrete(intrinsics.object_fields),
2340 }))?;
2341 let old = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2342 let object = emitter.add_anonymous_local(
2343 ctx.symbols.value_type(
2344 ctx.ta
2345 .source_type(receiver)
2346 .map_err(crate::codegen::arena_failure)?,
2347 )?,
2348 )?;
2349 emit_receiver(emitter, ctx, receiver)?;
2350 emitter.instruction(Instruction::LocalTee(object));
2351 emitter.instruction(Instruction::StructGet {
2352 struct_type_index: struct_idx,
2353 field_index: 2,
2354 });
2355 emitter.instruction(Instruction::LocalSet(fields_local));
2356 emitter.instruction(Instruction::LocalGet(fields_local));
2357 emitter.instruction(Instruction::I32Const(slot as i32));
2358 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
2359 if ctx
2360 .symbols
2361 .class_field_narrowing_check(mangled, field)
2362 .is_some()
2363 {
2364 crate::codegen::field_guards::check(emitter, ctx, object, mangled, field)?;
2365 }
2366 emit_class_field_slot_cast(emitter, ctx, mangled, field, target_ty)?;
2367 emit_postfix_numeric(emitter, ctx, target_ty);
2368 emitter.instruction(Instruction::LocalSet(old));
2369 emitter.instruction(Instruction::LocalGet(fields_local));
2370 emitter.instruction(Instruction::I32Const(slot as i32));
2371 emitter.instruction(Instruction::LocalGet(old));
2372 emit_postfix_delta(emitter, ctx, op, target_ty);
2373 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, target_ty)?;
2374 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
2375 emitter.instruction(Instruction::LocalGet(old));
2376 Ok(())
2377}
2378
2379fn emit_chain_parts(
2387 emitter: &mut FunctionEmitter<'_>,
2388 ctx: &CodegenCtx<'_>,
2389 parts: &[crate::TypedChainPart],
2390 idx: usize,
2391 receiver_ty: &Type,
2392 result_ty: &Type,
2393 source_types: Option<&[Type]>,
2394) -> Result<(), crate::compiler_error::CompilerFailure> {
2395 if source_types.is_some_and(|types| parts.len().checked_add(1) != Some(types.len())) {
2396 return Err(crate::codegen::internal_failure(
2397 "optional-chain source type count mismatch",
2398 ));
2399 }
2400 if idx == parts.len() {
2401 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
2406 emitter,
2407 ctx,
2408 receiver_ty,
2409 result_ty,
2410 )?;
2411 return Ok(());
2412 }
2413 let part = parts.get(idx).ok_or_else(|| {
2414 crate::codegen::internal_failure("optional-chain part index is out of bounds")
2415 })?;
2416 let next = idx
2417 .checked_add(1)
2418 .ok_or_else(|| crate::codegen::internal_failure("optional-chain index overflow"))?;
2419 let source_ty = source_types.and_then(|types| types.get(idx));
2420 let check_ty = source_types.and_then(|types| types.get(next));
2421 let saved_receiver = if matches!(
2422 part,
2423 crate::TypedChainPart::Field { .. }
2424 | crate::TypedChainPart::Index { .. }
2425 | crate::TypedChainPart::InterfaceProperty { .. }
2426 ) {
2427 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2428 emitter.instruction(Instruction::LocalTee(slot));
2429 Some(slot)
2430 } else if matches!(part, crate::TypedChainPart::NonNull { .. }) {
2431 emitter.call_receiver
2432 } else {
2433 None
2434 };
2435 let part_result_ty = part.result_ty().clone();
2436 let recv_val = part
2439 .is_optional()
2440 .then(|| ctx.symbols.value_type(receiver_ty))
2441 .transpose()?;
2442 let _: () = if let Some(recv_val @ ValType::Ref(_)) = recv_val {
2448 let block_ty = BlockType::Result(ctx.symbols.value_type(result_ty)?);
2449 let tmp = emitter.add_anonymous_local(recv_val)?;
2450 emitter.instruction(Instruction::LocalTee(tmp));
2451 emit_is_nullish(emitter, ctx)?;
2452 emitter.emit_if(block_ty);
2453 emit_undefined_for_chain_result(emitter, ctx, result_ty)?;
2454 emitter.emit_else();
2455 emitter.instruction(Instruction::LocalGet(tmp));
2456 emitter.instruction(Instruction::RefAsNonNull);
2457 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2458 emitter.call_receiver = saved_receiver;
2459 emit_chain_parts(
2460 emitter,
2461 ctx,
2462 parts,
2463 next,
2464 &part_result_ty,
2465 result_ty,
2466 source_types,
2467 )?;
2468 emitter.emit_end();
2469 } else {
2470 emit_chain_operation(emitter, ctx, part, receiver_ty, source_ty, check_ty)?;
2471 emitter.call_receiver = saved_receiver;
2472 emit_chain_parts(
2473 emitter,
2474 ctx,
2475 parts,
2476 next,
2477 &part_result_ty,
2478 result_ty,
2479 source_types,
2480 )?;
2481 };
2482 Ok(())
2483}
2484
2485fn emit_chain_operation(
2486 emitter: &mut FunctionEmitter,
2487 ctx: &CodegenCtx,
2488 part: &crate::TypedChainPart,
2489 receiver_ty: &Type,
2490 source_ty: Option<&Type>,
2491 check_ty: Option<&Type>,
2492) -> Result<(), crate::compiler_error::CompilerFailure> {
2493 if let crate::TypedChainPart::MethodCall {
2494 iface,
2495 name,
2496 result_ty,
2497 span,
2498 ..
2499 } = part
2500 && !crate::codegen::is_host_package(iface.as_str())
2501 && let Some(args) = ctx.ta.authored_call_arguments(*span)
2502 {
2503 cast::emit_box(emitter, ctx, receiver_ty)?;
2504 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2505 cast::emit_cast_to(emitter, ctx, result_ty)?;
2506 return Ok(());
2507 }
2508 if receiver_ty == &Type::Unknown {
2509 match part {
2510 crate::TypedChainPart::MethodCall {
2511 iface,
2512 name,
2513 args,
2514 result_ty,
2515 span,
2516 ..
2517 } if crate::codegen::runtime_values::dynamic_member_interface(iface) => {
2518 let args = ctx.ta.authored_call_arguments(*span).unwrap_or(args);
2519 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
2520 return Ok(());
2521 }
2522 crate::TypedChainPart::InterfaceProperty { iface, name, .. }
2523 if crate::codegen::runtime_values::dynamic_member_interface(iface) =>
2524 {
2525 emit_live_member_on_stack(emitter, ctx, iface, &name.name, None)?;
2526 return Ok(());
2527 }
2528 _ => {}
2529 }
2530 }
2531 let operation_ty =
2532 crate::typechecker::infer::narrowing::strip_nullish(source_ty.unwrap_or(receiver_ty));
2533 if !matches!(
2534 part,
2535 crate::TypedChainPart::Index { .. }
2536 | crate::TypedChainPart::Call { .. }
2537 | crate::TypedChainPart::NonNull { .. }
2538 ) {
2539 let receiver_slot = ctx.symbols.value_type(receiver_ty)?;
2540 if receiver_slot == ctx.symbols.value_type(&operation_ty)? {
2541 if matches!(receiver_slot, ValType::Ref(_)) {
2542 cast::emit_cast_to(emitter, ctx, &operation_ty)?;
2543 }
2544 } else {
2545 crate::codegen::cast_check::emit_operation_cast_on_stack(
2546 emitter,
2547 ctx,
2548 receiver_ty,
2549 &operation_ty,
2550 )?;
2551 }
2552 }
2553 emit_chain_access(
2554 emitter,
2555 ctx,
2556 part,
2557 &operation_ty,
2558 check_ty.unwrap_or(part.result_ty()),
2559 )?;
2560 Ok(())
2561}
2562
2563fn emit_live_member_on_stack(
2564 emitter: &mut FunctionEmitter,
2565 ctx: &CodegenCtx,
2566 iface: &crate::MangledName,
2567 name: &str,
2568 args: Option<&[ExprId]>,
2569) -> Result<(), crate::compiler_error::CompilerFailure> {
2570 super::emit_const_string_by_text(emitter, ctx, name)?;
2571 super::emit_const_string_by_text(emitter, ctx, iface.as_str())?;
2572 let helper = if args.is_some() {
2573 "__value_member"
2574 } else {
2575 "__value_property"
2576 };
2577 emitter.instruction(Instruction::Call(
2578 ctx.symbols
2579 .prelude_func_idx(helper)
2580 .ok_or_else(|| crate::codegen::internal_failure("live member helper collected"))?,
2581 ));
2582 let Some(args) = args else {
2583 return Ok(());
2584 };
2585 let intr = ctx
2586 .symbols
2587 .intrinsic_type_indices()
2588 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2589 emitter.instruction(Instruction::GlobalGet(
2590 ctx.symbols
2591 .prelude_global_idx("array_vtable")
2592 .ok_or_else(|| crate::codegen::internal_failure("array vtable"))?,
2593 ));
2594 for &arg in args {
2595 emit_expr(emitter, ctx, arg)?;
2596 cast::emit_box(
2597 emitter,
2598 ctx,
2599 &ctx.ta
2600 .try_expr(arg)
2601 .map_err(crate::codegen::arena_failure)?
2602 .ty,
2603 )?;
2604 }
2605 emitter.instruction(Instruction::ArrayNewFixed {
2606 array_type_index: intr.raw_array,
2607 array_size: crate::codegen::wasm_u32(args.len())?,
2608 });
2609 emitter.instruction(Instruction::I32Const(i32::try_from(args.len()).map_err(
2610 |_| crate::codegen::internal_failure("argument array too large"),
2611 )?));
2612 emitter.instruction(Instruction::StructNew(intr.array));
2613 emitter.instruction(Instruction::Call(
2614 ctx.symbols
2615 .prelude_func_idx("__value_invoke")
2616 .ok_or_else(|| crate::codegen::internal_failure("live invocation collected"))?,
2617 ));
2618 Ok(())
2619}
2620
2621fn inline_length_struct_idx(
2624 ctx: &CodegenCtx<'_>,
2625 iface: &crate::MangledName,
2626 prop: &str,
2627) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
2628 let key = crate::mangle::extend(iface, prop);
2629 if !ctx.symbols.is_intrinsic_member(&key) {
2630 return Ok(None);
2631 }
2632 let index = if *iface == crate::mangle::prelude("String") && prop == "length" {
2633 ctx.symbols.string_type_idx()
2634 } else if *iface == crate::mangle::prelude("Array") && prop == "length" {
2635 ctx.symbols.array_type_idx()
2636 } else {
2637 return Err(crate::codegen::internal_failure(format!(
2638 "no inline lowering for intrinsic member `{}`",
2639 key.as_str()
2640 )));
2641 };
2642 index
2643 .map(Some)
2644 .ok_or_else(|| crate::codegen::internal_failure("length receiver intrinsic missing"))
2645}
2646
2647fn emit_inline_length(emitter: &mut FunctionEmitter<'_>, ctx: &CodegenCtx<'_>, struct_idx: u32) {
2649 let is_array = ctx.symbols.array_type_idx() == Some(struct_idx);
2650 emitter.instruction(Instruction::StructGet {
2651 struct_type_index: struct_idx,
2652 field_index: if is_array { 2 } else { 1 },
2653 });
2654 if !is_array {
2655 emitter.instruction(Instruction::ArrayLen);
2656 }
2657 emitter.instruction(Instruction::F64ConvertI32U);
2658}
2659
2660fn emit_undefined_for_chain_result(
2662 emitter: &mut FunctionEmitter<'_>,
2663 ctx: &CodegenCtx<'_>,
2664 result_ty: &Type,
2665) -> Result<(), crate::compiler_error::CompilerFailure> {
2666 emit_undefined(emitter, ctx)?;
2667 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)
2668}
2669
2670fn emit_class_field_slot_cast(
2674 emitter: &mut FunctionEmitter,
2675 ctx: &CodegenCtx,
2676 class: &crate::MangledName,
2677 field: &str,
2678 result_ty: &Type,
2679) -> Result<(), crate::compiler_error::CompilerFailure> {
2680 emit_class_field_slot_cast_as(emitter, ctx, class, field, result_ty, result_ty)?;
2681 Ok(())
2682}
2683
2684fn emit_class_field_slot_cast_as(
2685 emitter: &mut FunctionEmitter<'_>,
2686 ctx: &CodegenCtx<'_>,
2687 class: &crate::MangledName,
2688 field: &str,
2689 result_ty: &Type,
2690 check_ty: &Type,
2691) -> Result<(), crate::compiler_error::CompilerFailure> {
2692 let _: () = match ctx.symbols.class_field_narrowing_check(class, field) {
2693 Some(check) => {
2694 crate::codegen::cast_check::emit_narrowed_field_read_as(
2695 emitter, ctx, check, result_ty, check_ty,
2696 )?;
2697 }
2698 None => crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?,
2699 };
2700 Ok(())
2701}
2702
2703fn emit_object_spread(
2704 emitter: &mut FunctionEmitter,
2705 ctx: &CodegenCtx,
2706 sources: &[TypedObjectMember],
2707 shape: &Type,
2708) -> Result<(), crate::compiler_error::CompilerFailure> {
2709 let intrinsics = ctx
2710 .symbols
2711 .intrinsic_type_indices()
2712 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2713 let merge = ctx
2714 .symbols
2715 .prelude_func_idx("ObjectConstructor##spread")
2716 .ok_or_else(|| crate::codegen::internal_failure("spread helper collected"))?;
2717 let null_object = Instruction::RefNull(HeapType::Concrete(intrinsics.object));
2718 emitter.instruction(null_object.clone());
2719 for (index, source) in sources.iter().enumerate() {
2720 let final_shape = (index + 1 == sources.len()).then_some(shape);
2721 emit_receiver(emitter, ctx, source.expr_id())?;
2725 let narrowed_ty = ctx
2726 .ta
2727 .source_type(source.expr_id())
2728 .map_err(crate::codegen::arena_failure)?;
2729 let non_object_source = if may_hold_non_object(narrowed_ty) {
2734 Some(emit_object_source_test(
2735 emitter,
2736 ctx,
2737 narrowed_ty,
2738 intrinsics.object,
2739 intrinsics.object_shape,
2740 )?)
2741 } else {
2742 None
2743 };
2744 let source_local =
2745 stash_receiver_as_object_shape(emitter, narrowed_ty, intrinsics.object_shape)?;
2746 emitter.instruction(Instruction::LocalGet(source_local));
2747 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2748 if matches!(source, TypedObjectMember::Spread { by_name: true, .. }) {
2749 emit_spread_mask(emitter, ctx, source_local, source.expr_id(), shape)?;
2750 } else {
2751 emitter.instruction(null_object.clone());
2752 }
2753 emitter.instruction(Instruction::Call(merge));
2754 if let Some(NonObjectSource {
2755 accumulator,
2756 opaque,
2757 }) = non_object_source
2758 {
2759 emitter.emit_else();
2760 emitter.instruction(Instruction::LocalGet(accumulator));
2761 match opaque {
2764 Some(source) => emitter.instruction(Instruction::LocalGet(source)),
2765 None => emitter.instruction(null_object.clone()),
2766 }
2767 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
2768 emitter.instruction(null_object.clone());
2769 emitter.instruction(Instruction::Call(merge));
2770 emitter.emit_end();
2771 }
2772 }
2773 Ok(())
2774}
2775
2776fn emit_spread_shape_argument(
2779 emitter: &mut FunctionEmitter,
2780 ctx: &CodegenCtx,
2781 final_shape: Option<&Type>,
2782 null_object: &Instruction<'static>,
2783) {
2784 match final_shape {
2785 Some(shape) => emit_spread_shape(emitter, ctx, shape),
2786 None => emitter.instruction(null_object.clone()),
2787 }
2788}
2789
2790fn may_hold_non_object(ty: &Type) -> bool {
2794 match ty.lowered() {
2795 Type::Union(members) => members.iter().any(may_hold_non_object),
2796 Type::Null
2797 | Type::Undefined
2798 | Type::Boolean
2799 | Type::BooleanLiteral(_)
2800 | Type::Number
2801 | Type::NumberLiteral(_)
2802 | Type::String
2803 | Type::StringLiteral(_)
2804 | Type::Unknown => true,
2806 _ => false,
2807 }
2808}
2809
2810struct NonObjectSource {
2812 accumulator: u32,
2813 opaque: Option<u32>,
2816}
2817
2818fn emit_object_source_test(
2823 emitter: &mut FunctionEmitter,
2824 ctx: &CodegenCtx,
2825 source_ty: &Type,
2826 object: u32,
2827 object_shape: u32,
2828) -> Result<NonObjectSource, crate::compiler_error::CompilerFailure> {
2829 let source_type = match ctx.symbols.value_type(source_ty)? {
2830 ValType::Ref(reference) => ValType::Ref(RefType {
2831 nullable: true,
2832 ..reference
2833 }),
2834 other => other,
2835 };
2836 let opaque = source_type == object_ref(object);
2837 let source = emitter.add_anonymous_local(source_type)?;
2838 emitter.instruction(Instruction::LocalSet(source));
2839 let accumulator = emitter.add_anonymous_local(object_ref(object))?;
2840 emitter.instruction(Instruction::LocalSet(accumulator));
2841 emitter.instruction(Instruction::LocalGet(source));
2842 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
2843 object_shape,
2844 )));
2845 emitter.emit_if(BlockType::Result(ValType::Ref(RefType {
2846 nullable: false,
2847 heap_type: HeapType::Concrete(object_shape),
2848 })));
2849 emitter.instruction(Instruction::LocalGet(accumulator));
2850 emitter.instruction(Instruction::LocalGet(source));
2851 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
2852 object_shape,
2853 )));
2854 Ok(NonObjectSource {
2855 accumulator,
2856 opaque: opaque.then_some(source),
2857 })
2858}
2859
2860fn emit_spread_mask(
2864 emitter: &mut FunctionEmitter,
2865 ctx: &CodegenCtx,
2866 source: u32,
2867 source_expr: crate::ExprId,
2868 shape: &Type,
2869) -> Result<(), crate::compiler_error::CompilerFailure> {
2870 let checked = ctx
2871 .ta
2872 .spread_mask_fields
2873 .get(&source_expr)
2874 .ok_or_else(|| crate::codegen::internal_failure("by-name spread fields recorded"))?;
2875 let omitted = ctx.ta.spread_omitted_fields.get(&source_expr);
2878 let fields: Vec<(&String, Option<&Type>)> = checked
2879 .iter()
2880 .map(|(name, ty)| (name, Some(ty)))
2881 .chain(omitted.into_iter().flatten().map(|name| (name, None)))
2882 .collect();
2883 let intrinsics = ctx
2884 .symbols
2885 .intrinsic_type_indices()
2886 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2887 let vtable = ctx
2888 .symbols
2889 .vtable_global_idx(shape)
2890 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2891 emitter.instruction(Instruction::GlobalGet(vtable));
2892 for (name, _) in &fields {
2893 let global = ctx
2894 .symbols
2895 .field_name_string_global_idx(name)
2896 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2897 emitter.instruction(Instruction::GlobalGet(global));
2898 }
2899 emitter.instruction(Instruction::ArrayNewFixed {
2900 array_type_index: intrinsics.field_names,
2901 array_size: crate::codegen::wasm_u32(fields.len())?,
2902 });
2903 for (name, ty) in fields.iter().copied() {
2904 let global = ctx
2905 .symbols
2906 .field_name_string_global_idx(name)
2907 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
2908 let Some(ty) = ty else {
2909 emitter.instruction(Instruction::GlobalGet(global));
2910 continue;
2911 };
2912 let index = emitter.add_anonymous_local(ValType::I32)?;
2913 let value = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
2914 emit_object_field_index_by_name(emitter, ctx, source, global);
2915 emitter.instruction(Instruction::LocalTee(index));
2916 emitter.instruction(Instruction::I32Const(0));
2917 emitter.instruction(Instruction::I32GeS);
2918 emitter.emit_if(BlockType::Result(ValType::I32));
2919 emit_field_slot_get(emitter, intrinsics, source, index);
2920 emitter.instruction(Instruction::LocalSet(value));
2921 emit_field_holds_value_of(emitter, ctx, source, index, value, ty)?;
2922 emitter.emit_else();
2923 emitter.instruction(Instruction::I32Const(0));
2924 emitter.emit_end();
2925 emitter.emit_if(BlockType::Result(object_ref(intrinsics.object)));
2926 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intrinsics.object)));
2927 emitter.emit_else();
2928 emitter.instruction(Instruction::GlobalGet(global));
2929 emitter.emit_end();
2930 }
2931 emitter.instruction(Instruction::ArrayNewFixed {
2932 array_type_index: intrinsics.object_fields,
2933 array_size: crate::codegen::wasm_u32(fields.len())?,
2934 });
2935 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2936 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2937 Ok(())
2938}
2939
2940fn emit_spread_shape(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, shape: &Type) {
2943 ctx.latch(emit_spread_shape_checked(emitter, ctx, shape));
2944}
2945
2946fn emit_spread_shape_checked(
2947 emitter: &mut FunctionEmitter,
2948 ctx: &CodegenCtx,
2949 shape: &Type,
2950) -> Result<(), crate::compiler_error::CompilerFailure> {
2951 let Type::Object { fields, .. } = shape else {
2952 return Err(crate::codegen::internal_failure("structural spread shape"));
2953 };
2954 let intrinsics = ctx
2955 .symbols
2956 .intrinsic_type_indices()
2957 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
2958 let names: Vec<_> = fields
2959 .iter()
2960 .map(|(name, field)| crate::codegen::field_names::FieldName {
2961 name: name.clone(),
2962 optional: field.optional,
2963 is_accessor: false,
2964 is_private: false,
2965 })
2966 .collect();
2967 let names_global = ctx
2968 .symbols
2969 .field_names_global_idx(&names)
2970 .ok_or_else(|| crate::codegen::internal_failure("spread shape names collected"))?;
2971 let vtable = ctx
2972 .symbols
2973 .vtable_global_idx(shape)
2974 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
2975 emitter.instruction(Instruction::GlobalGet(vtable));
2976 emitter.instruction(Instruction::GlobalGet(names_global));
2977 emitter.instruction(Instruction::I32Const(
2978 crate::codegen::wasm_u32(fields.len())? as i32,
2979 ));
2980 emitter.instruction(Instruction::ArrayNewDefault(intrinsics.object_fields));
2981 emitter.instruction(Instruction::RefNull(HeapType::ANY));
2982 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
2983
2984 Ok(())
2985}
2986
2987pub(super) fn stash_receiver_as_object_shape(
3003 emitter: &mut FunctionEmitter<'_>,
3004 receiver_ty: &Type,
3005 object_shape: u32,
3006) -> Result<u32, crate::compiler_error::CompilerFailure> {
3007 coerce_field_receiver_to_object_shape(emitter, receiver_ty, object_shape);
3008 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
3009 nullable: false,
3010 heap_type: HeapType::Concrete(object_shape),
3011 }))?;
3012 emitter.instruction(Instruction::LocalSet(rcv_local));
3013 Ok(rcv_local)
3014}
3015
3016fn coerce_field_receiver_to_object_shape(
3017 emitter: &mut FunctionEmitter<'_>,
3018 receiver_ty: &Type,
3019 object_shape: u32,
3020) {
3021 if crate::codegen::may_hold_builtin_value(receiver_ty) {
3022 emitter.instruction(Instruction::RefAsNonNull);
3023 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
3024 object_shape,
3025 )));
3026 }
3027}
3028
3029fn emit_chain_access(
3035 emitter: &mut FunctionEmitter<'_>,
3036 ctx: &CodegenCtx<'_>,
3037 part: &crate::TypedChainPart,
3038 receiver_ty: &Type,
3039 check_ty: &Type,
3040) -> Result<(), crate::compiler_error::CompilerFailure> {
3041 let _: () = match part {
3042 crate::TypedChainPart::Field {
3043 name, result_ty, ..
3044 } => {
3045 if ctx.ta.has_string_index(receiver_ty) {
3046 let Some(symbol) = ctx.require(
3047 ctx.symbols.field_name_string_global_idx(&name.name),
3048 "property name collected",
3049 ) else {
3050 return Ok(());
3051 };
3052 emitter.instruction(Instruction::GlobalGet(symbol));
3053 let Some(symbol) = ctx.require(
3054 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3055 "record read imported",
3056 ) else {
3057 return Ok(());
3058 };
3059 emitter.instruction(Instruction::Call(symbol));
3060 crate::codegen::cast_check::emit_checked_cast_on_stack(
3061 emitter,
3062 ctx,
3063 &Type::Unknown,
3064 check_ty,
3065 )?;
3066 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3067 return Ok(());
3068 }
3069 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
3070 crate::codegen::internal_failure("intrinsics declared by codegen entry")
3071 })?;
3072 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
3076 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
3077 emit_class_method_value(
3078 emitter,
3079 ctx,
3080 receiver_ty,
3081 &name.name,
3082 result_ty,
3083 check_ty,
3084 )?;
3085 return Ok(());
3086 }
3087 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
3088 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
3089 emit_class_vtable_dispatch(emitter, ctx, mangled, &getter, &[], result_ty)?;
3090 return Ok(());
3091 };
3092 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3093 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3094 })?;
3095 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
3096 nullable: false,
3097 heap_type: HeapType::Concrete(struct_idx),
3098 }))?;
3099 emitter.instruction(Instruction::LocalTee(object));
3100 emitter.instruction(Instruction::StructGet {
3101 struct_type_index: struct_idx,
3102 field_index: 2,
3103 });
3104 emitter.instruction(Instruction::I32Const(slot as i32));
3105 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3106 if ctx
3107 .symbols
3108 .class_field_narrowing_check(mangled, &name.name)
3109 .is_some()
3110 {
3111 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
3112 }
3113 emit_class_field_slot_cast_as(
3114 emitter, ctx, mangled, &name.name, result_ty, check_ty,
3115 )?;
3116 return Ok(());
3117 }
3118 let rcv_local =
3119 stash_receiver_as_object_shape(emitter, receiver_ty, intrinsics.object_shape)?;
3120 emit_object_property_read_as(emitter, ctx, rcv_local, &name.name, result_ty, check_ty)?;
3121 }
3122 crate::TypedChainPart::Index { idx, result_ty, .. } => {
3123 if receiver_ty.is_structural_object() {
3124 emit_expr(emitter, ctx, *idx)?;
3125 crate::codegen::cast_check::emit_operation_cast_on_stack(
3126 emitter,
3127 ctx,
3128 &ctx.ta
3129 .try_expr(*idx)
3130 .map_err(crate::codegen::arena_failure)?
3131 .ty,
3132 &Type::String,
3133 )?;
3134 let Some(symbol) = ctx.require(
3135 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3136 "record read imported",
3137 ) else {
3138 return Ok(());
3139 };
3140 emitter.instruction(Instruction::Call(symbol));
3141 crate::codegen::cast_check::emit_checked_cast_on_stack(
3142 emitter,
3143 ctx,
3144 &Type::Unknown,
3145 check_ty,
3146 )?;
3147 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3148 } else if receiver_ty.lowered() == &Type::Uint8Array {
3149 emit_uint8_index_with_receiver_on_stack(emitter, ctx, *idx)?;
3150 } else if receiver_ty.is_string_shaped() {
3151 emit_string_index_with_receiver_on_stack(emitter, ctx, *idx)?;
3152 } else {
3153 let source_ty = match receiver_ty.lowered() {
3154 Type::Array(element) => element.as_ref(),
3155 Type::Tuple(elements) => match &ctx
3156 .ta
3157 .try_expr(*idx)
3158 .map_err(crate::codegen::arena_failure)?
3159 .kind
3160 {
3161 TypedExprKind::Number(index) => {
3162 elements.get(*index as usize).unwrap_or(result_ty)
3163 }
3164 _ => result_ty,
3165 },
3166 _ => result_ty,
3167 };
3168 emit_bounds_checked_index_with_receiver_on_stack(
3169 emitter,
3170 ctx,
3171 *idx,
3172 source_ty,
3173 is_optional_tuple_position(ctx, receiver_ty, *idx)?,
3174 )?;
3175 if source_ty != result_ty {
3176 crate::codegen::cast_check::emit_checked_cast_on_stack(
3177 emitter, ctx, source_ty, result_ty,
3178 )?;
3179 }
3180 }
3181 }
3182 crate::TypedChainPart::InterfaceProperty { iface, name, .. } => {
3183 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
3194 emit_inline_length(emitter, ctx, struct_idx);
3195 return Ok(());
3196 }
3197 let key = crate::mangle::extend(iface, &name.name);
3198 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
3199 crate::codegen::internal_failure(
3200 "interface property getter recorded during the dependency-import pass",
3201 )
3202 })?;
3203 if matches!(receiver_ty.lowered(), Type::InterfaceRef { .. }) {
3204 emitter.instruction(Instruction::RefAsNonNull);
3205 }
3206 emitter.instruction(Instruction::Call(func_idx));
3207 }
3208 crate::TypedChainPart::Call {
3209 args,
3210 union_signature,
3211 ..
3212 } => {
3213 if let Some(signature) = union_signature
3220 && crate::codegen::closure_coercions::is_union_call_cast(receiver_ty, signature)
3221 {
3222 crate::codegen::closure_coercions::emit_union_call_cast(emitter, ctx, signature)?;
3223 }
3224 emit_indirect_closure_call_with_receiver_on_stack(
3225 emitter,
3226 ctx,
3227 union_signature.as_ref().unwrap_or(receiver_ty),
3228 args,
3229 part.result_ty(),
3230 )?;
3231 }
3232 crate::TypedChainPart::MethodCall {
3233 iface,
3234 name,
3235 args,
3236 result_ty,
3237 ..
3238 } => {
3239 emit_method_call_with_receiver_on_stack(
3249 emitter,
3250 ctx,
3251 receiver_ty,
3252 iface,
3253 &name.name,
3254 args,
3255 None,
3256 None,
3257 result_ty,
3258 )?;
3259 }
3260 crate::TypedChainPart::NonNull { result_ty, .. } => {
3261 crate::codegen::cast_check::emit_non_null_assert_on_stack(
3262 emitter,
3263 ctx,
3264 receiver_ty,
3265 result_ty,
3266 )?;
3267 }
3268 };
3269 Ok(())
3270}
3271
3272fn emit_captured_load(
3283 emitter: &mut FunctionEmitter,
3284 _ctx: &CodegenCtx,
3285 c: &crate::CapturedVar,
3286) -> Result<(), crate::compiler_error::CompilerFailure> {
3287 let slot = emitter.local_slot(&c.name.name).ok_or_else(|| {
3288 crate::codegen::internal_failure("captured binding is missing from the outer scope")
3289 })?;
3290 emitter.instruction(Instruction::LocalGet(slot));
3291 Ok(())
3292}
3293
3294fn emit_binary(
3295 emitter: &mut FunctionEmitter,
3296 ctx: &CodegenCtx,
3297 op: BinOp,
3298 lhs: ExprId,
3299 rhs: ExprId,
3300 result_ty: &Type,
3301) -> Result<(), crate::compiler_error::CompilerFailure> {
3302 let admits_never_operand = matches!(
3303 op,
3304 BinOp::Add
3305 | BinOp::Sub
3306 | BinOp::Mul
3307 | BinOp::Div
3308 | BinOp::Rem
3309 | BinOp::Pow
3310 | BinOp::Lt
3311 | BinOp::Gt
3312 | BinOp::Le
3313 | BinOp::Ge
3314 );
3315 if admits_never_operand && try_emit_unreachable_for_never_operand(emitter, ctx, &[lhs, rhs])? {
3316 return Ok(());
3317 }
3318 let _: () = match op {
3319 BinOp::BitAnd
3324 | BinOp::BitOr
3325 | BinOp::BitXor
3326 | BinOp::Shl
3327 | BinOp::Shr
3328 | BinOp::UnsignedShr => {
3329 if has_numeric_operands(ctx, &[lhs, rhs], result_ty)? {
3330 emit_number_bitwise(emitter, ctx, op, lhs, rhs)?;
3331 } else {
3332 let name = op.bitwise_name().ok_or_else(|| {
3333 crate::codegen::internal_failure("missing bitwise host operation")
3334 })?;
3335 emit_bitwise_host(emitter, ctx, name, &[lhs, rhs], result_ty)?;
3336 }
3337 }
3338 BinOp::Add => match &result_ty.widen_literal() {
3341 Type::Number => {
3342 emit_primitive_operand(emitter, ctx, lhs)?;
3343 emit_primitive_operand(emitter, ctx, rhs)?;
3344 emitter.instruction(Instruction::F64Add);
3345 }
3346 Type::String => {
3347 emit_primitive_operand(emitter, ctx, lhs)?;
3348 emit_primitive_operand(emitter, ctx, rhs)?;
3349 let idx = ctx
3350 .symbols
3351 .prelude_func_idx("string_concat")
3352 .ok_or_else(|| {
3353 crate::codegen::internal_failure("string_concat imported from prelude")
3354 })?;
3355 emitter.instruction(Instruction::Call(idx));
3356 }
3357 Type::BigInt => emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "add")?,
3359 other => {
3360 return Err(crate::codegen::internal_failure(format!(
3361 "typechecker rejects `+` for {other:?}"
3362 )));
3363 }
3364 },
3365 BinOp::Sub | BinOp::Mul | BinOp::Div => {
3366 if result_ty.is_bigint() {
3368 let name = match op {
3369 BinOp::Sub => "sub",
3370 BinOp::Mul => "mul",
3371 BinOp::Div => "div",
3372 _ => {
3373 return Err(crate::codegen::internal_failure(
3374 "invalid compiler emission dispatch",
3375 ));
3376 }
3377 };
3378 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, name)?;
3379 return Ok(());
3380 }
3381 if !matches!(result_ty, Type::Number) {
3382 return Err(crate::codegen::internal_failure(
3383 "numeric operator has a non-number result type",
3384 ));
3385 }
3386 emit_primitive_operand(emitter, ctx, lhs)?;
3387 emit_primitive_operand(emitter, ctx, rhs)?;
3388 let inst = match op {
3389 BinOp::Sub => Instruction::F64Sub,
3390 BinOp::Mul => Instruction::F64Mul,
3391 BinOp::Div => Instruction::F64Div,
3392 _ => {
3393 return Err(crate::codegen::internal_failure(
3394 "invalid compiler emission dispatch",
3395 ));
3396 }
3397 };
3398 emitter.instruction(inst);
3399 }
3400 BinOp::Pow => {
3401 if result_ty.is_bigint() {
3406 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "pow")?;
3407 return Ok(());
3408 }
3409 if !matches!(result_ty, Type::Number) {
3410 return Err(crate::codegen::internal_failure(
3411 "numeric operator has a non-number result type",
3412 ));
3413 }
3414 emit_primitive_operand(emitter, ctx, lhs)?;
3415 emit_primitive_operand(emitter, ctx, rhs)?;
3416 let idx = ctx
3417 .symbols
3418 .func_idx(&crate::runtime::prelude::math::math_key("pow"))
3419 .ok_or_else(|| crate::codegen::internal_failure("Math#pow host import recorded"))?;
3420 emitter.instruction(Instruction::Call(idx));
3421 }
3422 BinOp::Rem => {
3423 if result_ty.is_bigint() {
3425 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "mod")?;
3426 return Ok(());
3427 }
3428 let a = emitter.add_anonymous_local(ValType::F64)?;
3432 let b = emitter.add_anonymous_local(ValType::F64)?;
3433 emit_primitive_operand(emitter, ctx, lhs)?;
3434 emitter.instruction(Instruction::LocalSet(a));
3435 emit_primitive_operand(emitter, ctx, rhs)?;
3436 emitter.instruction(Instruction::LocalSet(b));
3437 emitter.instruction(Instruction::LocalGet(a));
3439 emitter.instruction(Instruction::LocalGet(a));
3440 emitter.instruction(Instruction::LocalGet(b));
3441 emitter.instruction(Instruction::F64Div);
3442 emitter.instruction(Instruction::F64Trunc);
3443 emitter.instruction(Instruction::LocalGet(b));
3444 emitter.instruction(Instruction::F64Mul);
3445 emitter.instruction(Instruction::F64Sub);
3446 }
3447 BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
3448 let operand_ty = ctx
3451 .ta
3452 .try_expr(lhs)
3453 .map_err(crate::codegen::arena_failure)?
3454 .ty
3455 .primitive_behavior()
3456 .clone();
3457 if operand_ty.is_bigint() {
3458 emit_bigint_cmp_inline(emitter, ctx, lhs, rhs, op)?;
3459 return Ok(());
3460 }
3461 emit_primitive_operand(emitter, ctx, lhs)?;
3462 emit_primitive_operand(emitter, ctx, rhs)?;
3463 if matches!(operand_ty, Type::String | Type::StringLiteral(_)) {
3464 let idx = ctx.symbols.prelude_func_idx("string_cmp").ok_or_else(|| {
3465 crate::codegen::internal_failure("string_cmp imported from prelude")
3466 })?;
3467 emitter.instruction(Instruction::Call(idx));
3468 emitter.instruction(Instruction::I32Const(0));
3469 emitter.instruction(match op {
3470 BinOp::Lt => Instruction::I32LtS,
3471 BinOp::Gt => Instruction::I32GtS,
3472 BinOp::Le => Instruction::I32LeS,
3473 BinOp::Ge => Instruction::I32GeS,
3474 _ => {
3475 return Err(crate::codegen::internal_failure(
3476 "invalid compiler emission dispatch",
3477 ));
3478 }
3479 });
3480 } else {
3481 emitter.instruction(match op {
3482 BinOp::Lt => Instruction::F64Lt,
3483 BinOp::Gt => Instruction::F64Gt,
3484 BinOp::Le => Instruction::F64Le,
3485 BinOp::Ge => Instruction::F64Ge,
3486 _ => {
3487 return Err(crate::codegen::internal_failure(
3488 "invalid compiler emission dispatch",
3489 ));
3490 }
3491 });
3492 }
3493 }
3494 BinOp::Eq | BinOp::NotEq => emit_equality(emitter, ctx, op, lhs, rhs)?,
3495 BinOp::And | BinOp::Or => emit_logical(emitter, ctx, op, lhs, rhs, result_ty)?,
3496 BinOp::In => emit_in_operator(emitter, ctx, lhs, rhs)?,
3497 BinOp::NullishCoalesce => {
3501 return Err(crate::codegen::internal_failure(
3502 "BinOp::NullishCoalesce should be lifted into TypedExprKind::NullishCoalesce by infer",
3503 ));
3504 }
3505 };
3506 Ok(())
3507}
3508
3509fn emit_equality(
3512 emitter: &mut FunctionEmitter,
3513 ctx: &CodegenCtx,
3514 op: BinOp,
3515 lhs: ExprId,
3516 rhs: ExprId,
3517) -> Result<(), crate::compiler_error::CompilerFailure> {
3518 let lhs_is_null_lit = matches!(
3525 ctx.ta
3526 .try_expr(lhs)
3527 .map_err(crate::codegen::arena_failure)?
3528 .kind,
3529 TypedExprKind::Null
3530 );
3531 let rhs_is_null_lit = matches!(
3532 ctx.ta
3533 .try_expr(rhs)
3534 .map_err(crate::codegen::arena_failure)?
3535 .kind,
3536 TypedExprKind::Null
3537 );
3538 if lhs_is_null_lit && rhs_is_null_lit {
3539 let v: i32 = if matches!(op, BinOp::Eq) { 1 } else { 0 };
3540 emitter.instruction(Instruction::I32Const(v));
3541 return Ok(());
3542 }
3543 if lhs_is_null_lit || rhs_is_null_lit {
3544 let non_null_side = if lhs_is_null_lit { rhs } else { lhs };
3545 let non_null_ty = ctx
3546 .ta
3547 .try_expr(non_null_side)
3548 .map_err(crate::codegen::arena_failure)?
3549 .ty
3550 .clone();
3551 let non_null_val = ctx.symbols.value_type(&non_null_ty)?;
3552 if matches!(non_null_val, ValType::F64 | ValType::I32) {
3560 emit_expr(emitter, ctx, non_null_side)?;
3561 emitter.instruction(Instruction::Drop);
3562 let v: i32 = if matches!(op, BinOp::NotEq) { 1 } else { 0 };
3563 emitter.instruction(Instruction::I32Const(v));
3564 return Ok(());
3565 }
3566 emit_expr(emitter, ctx, non_null_side)?;
3567 emitter.instruction(Instruction::RefIsNull);
3568 if matches!(op, BinOp::NotEq) {
3569 emitter.instruction(Instruction::I32Eqz);
3570 }
3571 return Ok(());
3572 }
3573 let lhs_ty = ctx
3574 .ta
3575 .try_expr(lhs)
3576 .map_err(crate::codegen::arena_failure)?
3577 .ty
3578 .clone();
3579 let rhs_ty = ctx
3580 .ta
3581 .try_expr(rhs)
3582 .map_err(crate::codegen::arena_failure)?
3583 .ty
3584 .clone();
3585 let operand_val = ctx.symbols.value_type(lhs_ty.primitive_behavior())?;
3586 if may_hold_null(&lhs_ty)
3592 || may_hold_null(&rhs_ty)
3593 || operand_val != ctx.symbols.value_type(rhs_ty.primitive_behavior())?
3594 {
3595 emit_boxed_eq(emitter, ctx, lhs, rhs, op)?;
3596 return Ok(());
3597 }
3598 if lhs_ty.is_bigint() {
3602 emit_bigint_cmp_eq_inline(emitter, ctx, lhs, rhs, op)?;
3603 return Ok(());
3604 }
3605 let string_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
3606 crate::codegen::internal_failure("string type registered with intrinsics")
3607 })?;
3608 match operand_val {
3614 ValType::F64 => {
3615 emit_primitive_operand(emitter, ctx, lhs)?;
3616 emit_primitive_operand(emitter, ctx, rhs)?;
3617 emitter.instruction(if matches!(op, BinOp::Eq) {
3618 Instruction::F64Eq
3619 } else {
3620 Instruction::F64Ne
3621 });
3622 }
3623 ValType::I32 => {
3624 emit_expr(emitter, ctx, lhs)?;
3625 emit_expr(emitter, ctx, rhs)?;
3626 emitter.instruction(if matches!(op, BinOp::Eq) {
3627 Instruction::I32Eq
3628 } else {
3629 Instruction::I32Ne
3630 });
3631 }
3632 ValType::Ref(RefType {
3633 heap_type: HeapType::Concrete(idx),
3634 ..
3635 }) if idx == string_idx => {
3636 emit_expr(emitter, ctx, lhs)?;
3637 emit_expr(emitter, ctx, rhs)?;
3638 let func = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
3639 crate::codegen::internal_failure("string_eq imported from prelude")
3640 })?;
3641 emitter.instruction(Instruction::Call(func));
3642 if matches!(op, BinOp::NotEq) {
3643 emitter.instruction(Instruction::I32Eqz);
3644 }
3645 }
3646 ValType::Ref(_) => {
3647 emit_expr(emitter, ctx, lhs)?;
3658 cast::emit_box(emitter, ctx, &lhs_ty)?;
3659 emit_expr(emitter, ctx, rhs)?;
3660 cast::emit_box(emitter, ctx, &rhs_ty)?;
3661 emit_vtable_equality(emitter, ctx, op);
3662 }
3663 other => {
3664 return Err(crate::codegen::internal_failure(format!(
3665 "typechecker rejects equality on Wasm value-type `{other:?}`"
3666 )));
3667 }
3668 }
3669 Ok(())
3670}
3671
3672fn emit_logical(
3673 emitter: &mut FunctionEmitter,
3674 ctx: &CodegenCtx,
3675 op: BinOp,
3676 lhs: ExprId,
3677 rhs: ExprId,
3678 result_ty: &Type,
3679) -> Result<(), crate::compiler_error::CompilerFailure> {
3680 let result_val = ctx.symbols.value_type(result_ty)?;
3686 let lhs_ty = ctx
3687 .ta
3688 .try_expr(lhs)
3689 .map_err(crate::codegen::arena_failure)?
3690 .ty
3691 .clone();
3692 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
3693 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
3694 let tmp = emitter.add_anonymous_local(lhs_val)?;
3695 emit_expr(emitter, ctx, lhs)?;
3696 emitter.instruction(Instruction::LocalTee(tmp));
3697 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &lhs_ty)?;
3698 let emit_rhs_branch =
3699 |emitter: &mut FunctionEmitter| -> Result<(), crate::compiler_error::CompilerFailure> {
3700 let rhs_ty = ctx
3701 .ta
3702 .try_expr(rhs)
3703 .map_err(crate::codegen::arena_failure)?
3704 .ty
3705 .clone();
3706 emit_expr(emitter, ctx, rhs)?;
3707 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3708 emitter, ctx, &rhs_ty, result_ty,
3709 )?;
3710 Ok(())
3711 };
3712 let kept_lhs_ty = match op {
3716 BinOp::And => falsy_part(&lhs_ty),
3717 _ => truthy_part(&lhs_ty),
3718 };
3719 let lhs_is_never_kept = matches!(kept_lhs_ty.lowered(), Type::Never);
3720 let emit_kept_lhs_branch = |emitter: &mut FunctionEmitter| {
3721 if lhs_is_never_kept {
3722 emitter.instruction(Instruction::Unreachable);
3723 return Ok(());
3724 }
3725 emitter.instruction(Instruction::LocalGet(tmp));
3726 if lhs_is_ref {
3727 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?;
3731 } else {
3732 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
3733 emitter, ctx, &lhs_ty, result_ty,
3734 )?;
3735 }
3736 Ok::<(), crate::compiler_error::CompilerFailure>(())
3737 };
3738 emitter.emit_if(BlockType::Result(result_val));
3739 match op {
3740 BinOp::And => emit_rhs_branch(emitter)?,
3741 _ => emit_kept_lhs_branch(emitter)?,
3742 }
3743 emitter.emit_else();
3744 match op {
3745 BinOp::And => emit_kept_lhs_branch(emitter)?,
3746 _ => emit_rhs_branch(emitter)?,
3747 }
3748 emitter.emit_end();
3749 Ok(())
3750}
3751
3752fn try_emit_unreachable_for_never_operand(
3757 emitter: &mut FunctionEmitter,
3758 ctx: &CodegenCtx,
3759 operands: &[ExprId],
3760) -> Result<bool, crate::compiler_error::CompilerFailure> {
3761 let mut has_never_operand = false;
3762 for &operand in operands {
3763 let ty = &ctx
3764 .ta
3765 .try_expr(operand)
3766 .map_err(crate::codegen::arena_failure)?
3767 .ty;
3768 has_never_operand |= matches!(ty.lowered(), Type::Never);
3769 }
3770 if !has_never_operand {
3771 return Ok(false);
3772 }
3773 for &operand in operands {
3774 emit_expr(emitter, ctx, operand)?;
3775 emitter.instruction(Instruction::Drop);
3776 }
3777 emitter.instruction(Instruction::Unreachable);
3778 Ok(true)
3779}
3780
3781fn emit_primitive_operand(
3782 emitter: &mut FunctionEmitter,
3783 ctx: &CodegenCtx,
3784 expr: ExprId,
3785) -> Result<(), crate::compiler_error::CompilerFailure> {
3786 emit_expr(emitter, ctx, expr)?;
3787 let ty = &ctx
3788 .ta
3789 .try_expr(expr)
3790 .map_err(crate::codegen::arena_failure)?
3791 .ty;
3792 let _: () = if matches!(ty.primitive_behavior(), Type::Number)
3793 && ctx.symbols.value_type(ty)? != ValType::F64
3794 {
3795 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
3796 };
3797 Ok(())
3798}
3799
3800fn emit_in_operator(
3808 emitter: &mut FunctionEmitter,
3809 ctx: &CodegenCtx,
3810 lhs: ExprId,
3811 rhs: ExprId,
3812) -> Result<(), crate::compiler_error::CompilerFailure> {
3813 emit_expr(emitter, ctx, lhs)?;
3814 crate::codegen::cast_check::emit_operation_cast_on_stack(
3815 emitter,
3816 ctx,
3817 &ctx.ta
3818 .try_expr(lhs)
3819 .map_err(crate::codegen::arena_failure)?
3820 .ty,
3821 &Type::String,
3822 )?;
3823 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
3824 emitter.instruction(Instruction::LocalSet(key));
3825 emit_expr(emitter, ctx, rhs)?;
3826 cast::emit_box(
3827 emitter,
3828 ctx,
3829 &ctx.ta
3830 .try_expr(rhs)
3831 .map_err(crate::codegen::arena_failure)?
3832 .ty,
3833 )?;
3834 emitter.instruction(Instruction::LocalGet(key));
3835 let Some(symbol) = ctx.require(
3836 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
3837 "presence helper imported",
3838 ) else {
3839 return Ok(());
3840 };
3841 emitter.instruction(Instruction::Call(symbol));
3842 Ok(())
3843}
3844
3845pub(crate) fn emit_class_field_setup(
3850 emitter: &mut FunctionEmitter,
3851 ctx: &CodegenCtx,
3852 mangled: &crate::MangledName,
3853) -> Result<(), crate::compiler_error::CompilerFailure> {
3854 emit_class_field_setup_phase(emitter, ctx, mangled, FieldSetupPhase::All)
3855}
3856
3857#[derive(Clone, Copy)]
3858pub(crate) enum FieldSetupPhase {
3859 All,
3860 Initializers,
3861 ParameterProperties,
3862}
3863
3864pub(crate) fn emit_class_field_setup_phase(
3865 emitter: &mut FunctionEmitter,
3866 ctx: &CodegenCtx,
3867 mangled: &crate::MangledName,
3868 phase: FieldSetupPhase,
3869) -> Result<(), crate::compiler_error::CompilerFailure> {
3870 use crate::codegen::symbol_table::FieldSetup;
3871 let setup: Vec<FieldSetup> = match ctx.symbols.class_field_setup(mangled) {
3872 Some(s) if !s.is_empty() => s.to_vec(),
3873 _ => return Ok(()),
3874 };
3875 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3876 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3877 })?;
3878 let intrinsics = ctx
3879 .symbols
3880 .intrinsic_type_indices()
3881 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3882 let this = emitter.this_local().ok_or_else(|| {
3883 crate::codegen::internal_failure("field setup runs inside a constructor init fn")
3884 })?;
3885 for step in &setup {
3886 let parameter_property = matches!(step, FieldSetup::ParamCopy { .. });
3887 if matches!(phase, FieldSetupPhase::Initializers) && parameter_property
3888 || matches!(phase, FieldSetupPhase::ParameterProperties) && !parameter_property
3889 {
3890 continue;
3891 }
3892 let field = match step {
3893 FieldSetup::Init { field, .. }
3894 | FieldSetup::ParamCopy { field, .. }
3895 | FieldSetup::Reset { field } => field,
3896 };
3897 let slot = ctx
3898 .symbols
3899 .class_field_slot(mangled, field)
3900 .ok_or_else(|| {
3901 crate::codegen::internal_failure("class field slot recorded in classes::emit")
3902 })?;
3903 match step {
3904 FieldSetup::Init { value, .. } => {
3905 let value_ty = ctx
3906 .ta
3907 .try_expr(*value)
3908 .map_err(crate::codegen::arena_failure)?
3909 .ty
3910 .clone();
3911 emit_expr(emitter, ctx, *value)?;
3912 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &value_ty)?;
3913 }
3914 FieldSetup::ParamCopy { param, .. } => {
3915 let local = emitter.require_local_slot(¶m.name.name)?;
3916 if param.boxed {
3917 let box_type = ctx.symbols.box_type_idx(¶m.ty)?.ok_or_else(|| {
3918 crate::codegen::internal_failure(
3919 "constructor parameter cell is not registered",
3920 )
3921 })?;
3922 super::binding_cells::read(emitter, ctx, local, box_type)?;
3923 cast::emit_unerase(emitter, ctx, ¶m.ty)?;
3924 } else {
3925 emitter.instruction(Instruction::LocalGet(local));
3926 }
3927 cast::emit_box(emitter, ctx, ¶m.ty)?;
3928 }
3929 FieldSetup::Reset { .. } => {
3930 emit_undefined(emitter, ctx)?;
3931 }
3932 }
3933 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
3934 emitter.instruction(Instruction::LocalSet(stored));
3935 emitter.instruction(Instruction::LocalGet(this));
3936 emitter.instruction(Instruction::StructGet {
3937 struct_type_index: struct_idx,
3938 field_index: 2,
3939 });
3940 emitter.instruction(Instruction::I32Const(slot as i32));
3941 emitter.instruction(Instruction::LocalGet(stored));
3942 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
3943 let index = emitter.add_anonymous_local(ValType::I32)?;
3944 emitter.instruction(Instruction::I32Const(slot as i32));
3945 emitter.instruction(Instruction::LocalSet(index));
3946 crate::codegen::field_names::emit_mark_present(emitter, ctx, this, index)?;
3947 }
3948 Ok(())
3949}
3950
3951pub(crate) fn emit_field_slot_get(
3955 emitter: &mut FunctionEmitter,
3956 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
3957 object_local: u32,
3958 index_local: u32,
3959) {
3960 emitter.instruction(Instruction::LocalGet(object_local));
3961 emitter.instruction(Instruction::StructGet {
3962 struct_type_index: intrinsics.object_shape,
3963 field_index: 2,
3964 });
3965 emitter.instruction(Instruction::LocalGet(index_local));
3966 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3967}
3968
3969pub(crate) fn emit_object_field_read_by_name(
3970 emitter: &mut FunctionEmitter,
3971 ctx: &CodegenCtx,
3972 object_local: u32,
3973 name_global: u32,
3974) {
3975 ctx.latch(emit_object_field_read_by_name_checked(
3976 emitter,
3977 ctx,
3978 object_local,
3979 name_global,
3980 ));
3981}
3982
3983fn emit_object_field_read_by_name_checked(
3984 emitter: &mut FunctionEmitter,
3985 ctx: &CodegenCtx,
3986 object_local: u32,
3987 name_global: u32,
3988) -> Result<(), crate::compiler_error::CompilerFailure> {
3989 let intrinsics = ctx
3990 .symbols
3991 .intrinsic_type_indices()
3992 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
3993 let Some(index_local) = ctx.latch(emitter.add_anonymous_local(ValType::I32)) else {
3994 return Ok(());
3995 };
3996 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
3997 emitter.instruction(Instruction::LocalSet(index_local));
3998 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
3999 nullable: true,
4000 heap_type: HeapType::Concrete(intrinsics.object),
4001 })));
4002 emitter.instruction(Instruction::LocalGet(index_local));
4003 emitter.instruction(Instruction::I32Const(0));
4004 emitter.instruction(Instruction::I32LtS);
4005 emitter.emit_if(BlockType::Empty);
4006 emit_undefined(emitter, ctx)?;
4007 emitter.instruction(Instruction::Br(1));
4008 emitter.emit_end();
4009 emitter.instruction(Instruction::LocalGet(object_local));
4010 emitter.instruction(Instruction::StructGet {
4011 struct_type_index: intrinsics.object_shape,
4012 field_index: 2,
4013 });
4014 emitter.instruction(Instruction::LocalGet(index_local));
4015 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
4016 emitter.emit_end();
4017
4018 Ok(())
4019}
4020
4021pub(crate) fn emit_object_field_write_by_name(
4022 emitter: &mut FunctionEmitter,
4023 ctx: &CodegenCtx,
4024 object_local: u32,
4025 name_global: u32,
4026) -> Result<(), crate::compiler_error::CompilerFailure> {
4027 let intrinsics = ctx
4028 .symbols
4029 .intrinsic_type_indices()
4030 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4031 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4032 nullable: true,
4033 heap_type: HeapType::Concrete(intrinsics.object),
4034 }))?;
4035 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4036 emitter.instruction(Instruction::LocalSet(value_local));
4037 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4038 emitter.instruction(Instruction::LocalTee(index_local));
4039 emitter.instruction(Instruction::I32Const(0));
4040 emitter.instruction(Instruction::I32GeS);
4041 emitter.emit_if(BlockType::Empty);
4042 emitter.instruction(Instruction::LocalGet(object_local));
4043 emitter.instruction(Instruction::StructGet {
4044 struct_type_index: intrinsics.object_shape,
4045 field_index: 2,
4046 });
4047 emitter.instruction(Instruction::LocalGet(index_local));
4048 emitter.instruction(Instruction::LocalGet(value_local));
4049 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
4050 crate::codegen::field_names::emit_mark_present(emitter, ctx, object_local, index_local)?;
4051 emitter.emit_else();
4052 emitter.instruction(Instruction::LocalGet(object_local));
4053 emitter.instruction(Instruction::GlobalGet(name_global));
4054 emitter.instruction(Instruction::LocalGet(value_local));
4055 let insert = ctx
4056 .symbols
4057 .prelude_func_idx("ObjectConstructor##insertField")
4058 .ok_or_else(|| {
4059 crate::codegen::internal_failure("field insertion helper is not imported")
4060 })?;
4061 emitter.instruction(Instruction::Call(insert));
4062 emitter.emit_end();
4063 Ok(())
4064}
4065
4066pub(crate) fn accessor_branch_emittable(ctx: &CodegenCtx, getter_name: &str) -> bool {
4073 ctx.symbols
4074 .field_name_string_global_idx(getter_name)
4075 .is_some()
4076}
4077
4078pub(crate) fn emit_is_accessor_backed(
4095 emitter: &mut FunctionEmitter,
4096 ctx: &CodegenCtx,
4097 object_local: u32,
4098 accessor_name: &str,
4099 kind: crate::AccessorKind,
4100) -> Result<u32, crate::compiler_error::CompilerFailure> {
4101 let intrinsics = ctx
4102 .symbols
4103 .intrinsic_type_indices()
4104 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4105 let accessor_global = ctx
4106 .symbols
4107 .field_name_string_global_idx(accessor_name)
4108 .ok_or_else(|| {
4109 crate::codegen::internal_failure("caller checked the accessor name has a string global")
4110 })?;
4111 let closure_struct_idx = ctx
4112 .symbols
4113 .closure_struct_type_idx(crate::codegen::classes::accessor_closure_sig(kind))
4114 .ok_or_else(|| {
4115 crate::codegen::internal_failure(
4116 "accessor closure struct collected by analysis::note_shaped_property_access",
4117 )
4118 })?;
4119 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4120 emit_field_index_by_name(emitter, ctx, object_local, accessor_global, true);
4121 emitter.instruction(Instruction::LocalTee(index_local));
4122 emitter.instruction(Instruction::I32Const(0));
4123 emitter.instruction(Instruction::I32GeS);
4124 emitter.instruction(Instruction::LocalGet(object_local));
4125 emitter.instruction(Instruction::StructGet {
4126 struct_type_index: intrinsics.object,
4127 field_index: 0,
4128 });
4129 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4130 intrinsics.class_vtable,
4131 )));
4132 emitter.instruction(Instruction::I32And);
4133 emitter.emit_if(BlockType::Result(ValType::I32));
4134 emitter.instruction(Instruction::LocalGet(object_local));
4135 emitter.instruction(Instruction::StructGet {
4136 struct_type_index: intrinsics.object_shape,
4137 field_index: 1,
4138 });
4139 emitter.instruction(Instruction::LocalGet(index_local));
4140 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
4141 crate::codegen::field_names::emit_name_is_accessor(emitter, ctx)?;
4142 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4143 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4144 closure_struct_idx,
4145 )));
4146 emitter.instruction(Instruction::I32And);
4147 emitter.emit_else();
4148 emitter.instruction(Instruction::I32Const(0));
4149 emitter.emit_end();
4150 Ok(index_local)
4151}
4152
4153pub(crate) fn emit_object_property_read(
4163 emitter: &mut FunctionEmitter,
4164 ctx: &CodegenCtx,
4165 object_local: u32,
4166 prop_name: &str,
4167 field_ty: &Type,
4168) -> Result<(), crate::compiler_error::CompilerFailure> {
4169 emit_object_property_read_as(emitter, ctx, object_local, prop_name, field_ty, field_ty)?;
4170 Ok(())
4171}
4172
4173fn emit_object_property_read_as(
4174 emitter: &mut FunctionEmitter,
4175 ctx: &CodegenCtx,
4176 object_local: u32,
4177 prop_name: &str,
4178 field_ty: &Type,
4179 check_ty: &Type,
4180) -> Result<(), crate::compiler_error::CompilerFailure> {
4181 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4182 let name_global = ctx
4183 .symbols
4184 .field_name_string_global_idx(prop_name)
4185 .ok_or_else(|| {
4186 crate::codegen::internal_failure(
4187 "per-name string global recorded during field-name-strings emission",
4188 )
4189 })?;
4190 if !accessor_branch_emittable(ctx, &getter) {
4191 emit_object_field_read_by_name(emitter, ctx, object_local, name_global);
4192 emit_dynamic_narrowed_property_read(
4193 emitter,
4194 ctx,
4195 object_local,
4196 prop_name,
4197 field_ty,
4198 check_ty,
4199 )?;
4200 return Ok(());
4201 }
4202 let intrinsics = ctx
4203 .symbols
4204 .intrinsic_type_indices()
4205 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4206 let result_vt = ctx.symbols.value_type(field_ty)?;
4207 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4208 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4209 emitter.instruction(Instruction::LocalSet(index_local));
4210 emitter.instruction(Instruction::LocalGet(index_local));
4211 emitter.instruction(Instruction::I32Const(0));
4212 emitter.instruction(Instruction::I32GeS);
4213 emitter.emit_if(BlockType::Result(result_vt));
4214 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4216 emit_dynamic_narrowed_property_read(emitter, ctx, object_local, prop_name, field_ty, check_ty)?;
4217 emitter.emit_else();
4218 let getter_slot_local = emit_is_accessor_backed(
4219 emitter,
4220 ctx,
4221 object_local,
4222 &getter,
4223 crate::AccessorKind::Get,
4224 )?;
4225 emitter.emit_if(BlockType::Result(result_vt));
4226 emitter.instruction(Instruction::LocalGet(object_local));
4228 emit_interface_method_via_shape_with_receiver_on_stack(
4229 emitter,
4230 ctx,
4231 &getter,
4232 &[],
4233 field_ty,
4234 Some(getter_slot_local),
4235 )?;
4236 emitter.emit_else();
4237 emit_undefined(emitter, ctx)?;
4243 cast::emit_cast_to(emitter, ctx, field_ty)?;
4244 emitter.emit_end();
4245 emitter.emit_end();
4246 Ok(())
4247}
4248
4249fn emit_dynamic_narrowed_property_read(
4250 emitter: &mut FunctionEmitter,
4251 ctx: &CodegenCtx,
4252 object_local: u32,
4253 prop_name: &str,
4254 field_ty: &Type,
4255 check_ty: &Type,
4256) -> Result<(), crate::compiler_error::CompilerFailure> {
4257 let checks: Vec<_> = ctx
4258 .symbols
4259 .class_field_narrowing_checks(prop_name)
4260 .filter_map(|(class, check)| {
4261 ctx.symbols
4262 .class_vtable_global_idx(class)
4263 .map(|vtable| (vtable, check))
4264 })
4265 .collect();
4266 if checks.is_empty() {
4267 cast::emit_cast_to(emitter, ctx, field_ty)?;
4268 return Ok(());
4269 }
4270
4271 let intrinsics = ctx
4272 .symbols
4273 .intrinsic_type_indices()
4274 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4275 let raw = emitter.add_anonymous_local(ValType::Ref(RefType {
4276 nullable: true,
4277 heap_type: HeapType::Concrete(intrinsics.object),
4278 }))?;
4279 emitter.instruction(Instruction::LocalSet(raw));
4280 emit_dynamic_narrowing_check(
4281 emitter,
4282 ctx,
4283 object_local,
4284 raw,
4285 field_ty,
4286 check_ty,
4287 &checks,
4288 0,
4289 )?;
4290 Ok(())
4291}
4292
4293#[allow(clippy::too_many_arguments)]
4294fn emit_dynamic_narrowing_check(
4295 emitter: &mut FunctionEmitter,
4296 ctx: &CodegenCtx,
4297 object_local: u32,
4298 raw: u32,
4299 field_ty: &Type,
4300 check_ty: &Type,
4301 checks: &[(u32, &crate::FieldNarrowingCheck)],
4302 index: usize,
4303) -> Result<(), crate::compiler_error::CompilerFailure> {
4304 let Some((vtable, check)) = checks.get(index) else {
4305 emitter.instruction(Instruction::LocalGet(raw));
4306 cast::emit_cast_to(emitter, ctx, field_ty)?;
4307 return Ok(());
4308 };
4309 emitter.instruction(Instruction::LocalGet(object_local));
4310 crate::codegen::function_emitter::cast::emit_nominal_instance_test(emitter, ctx, *vtable)?;
4311 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(field_ty)?));
4312 emitter.instruction(Instruction::LocalGet(raw));
4313 crate::codegen::cast_check::emit_narrowed_field_read_as(
4314 emitter, ctx, check, field_ty, check_ty,
4315 )?;
4316 emitter.emit_else();
4317 emit_dynamic_narrowing_check(
4318 emitter,
4319 ctx,
4320 object_local,
4321 raw,
4322 field_ty,
4323 check_ty,
4324 checks,
4325 index + 1,
4326 )?;
4327 emitter.emit_end();
4328 Ok(())
4329}
4330
4331pub(crate) fn emit_object_property_write(
4337 emitter: &mut FunctionEmitter,
4338 ctx: &CodegenCtx,
4339 object_local: u32,
4340 prop: &Ident,
4341 value: ExprId,
4342) -> Result<(), crate::compiler_error::CompilerFailure> {
4343 emit_object_property_write_value(
4344 emitter,
4345 ctx,
4346 object_local,
4347 prop,
4348 &ShapeArgument::Expression(value),
4349 )?;
4350 Ok(())
4351}
4352
4353fn emit_object_property_write_value(
4354 emitter: &mut FunctionEmitter,
4355 ctx: &CodegenCtx,
4356 object_local: u32,
4357 prop: &Ident,
4358 value: &ShapeArgument,
4359) -> Result<(), crate::compiler_error::CompilerFailure> {
4360 let prop_name = prop.name.as_str();
4361 let setter = crate::codegen::classes::accessor_setter_name(prop_name);
4362 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4363 let has_setter_name = ctx.symbols.field_name_string_global_idx(&setter).is_some();
4364 let has_getter_name = ctx.symbols.field_name_string_global_idx(&getter).is_some();
4365 let name_global = ctx
4366 .symbols
4367 .field_name_string_global_idx(prop_name)
4368 .ok_or_else(|| {
4369 crate::codegen::internal_failure(
4370 "per-name string global recorded during field-name-strings emission",
4371 )
4372 })?;
4373 if !has_setter_name && !has_getter_name {
4377 let value_ty = value.ty(ctx)?;
4378 value.emit(emitter, ctx)?;
4379 cast::emit_box(emitter, ctx, &value_ty)?;
4380 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4381 return Ok(());
4382 }
4383 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4384 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4385 emitter.instruction(Instruction::LocalSet(index_local));
4386 emitter.instruction(Instruction::LocalGet(index_local));
4387 emitter.instruction(Instruction::I32Const(0));
4388 emitter.instruction(Instruction::I32GeS);
4389 emitter.emit_if(BlockType::Empty);
4390 let value_ty = value.ty(ctx)?;
4393 value.emit(emitter, ctx)?;
4394 cast::emit_box(emitter, ctx, &value_ty)?;
4395 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4396 emitter.emit_else();
4397 let arms: Vec<(&str, AccessorWrite)> = [
4402 has_setter_name.then_some((setter.as_str(), AccessorWrite::Setter)),
4403 has_getter_name.then_some((getter.as_str(), AccessorWrite::ReadOnly)),
4404 ]
4405 .into_iter()
4406 .flatten()
4407 .collect();
4408 for (accessor, arm) in &arms {
4409 let slot_index_local =
4410 emit_is_accessor_backed(emitter, ctx, object_local, accessor, arm.scanned_kind())?;
4411 emitter.emit_if(BlockType::Empty);
4412 match arm {
4413 AccessorWrite::Setter => {
4414 emitter.instruction(Instruction::LocalGet(object_local));
4415 emit_shape_method_with_arguments(
4416 emitter,
4417 ctx,
4418 accessor,
4419 std::slice::from_ref(value),
4420 &Type::Void,
4421 Some(slot_index_local),
4422 )?;
4423 emitter.instruction(Instruction::Drop);
4424 }
4425 AccessorWrite::ReadOnly => {
4428 value.emit(emitter, ctx)?;
4429 emitter.instruction(Instruction::Drop);
4430 emitter.record_span(prop.span);
4433 crate::codegen::throw::emit_type_error_throw(
4434 emitter,
4435 ctx,
4436 crate::codegen::throw::READ_ONLY_PROPERTY_MESSAGE,
4437 );
4438 }
4439 }
4440 emitter.emit_else();
4441 }
4442 let value_ty = value.ty(ctx)?;
4443 value.emit(emitter, ctx)?;
4444 cast::emit_box(emitter, ctx, &value_ty)?;
4445 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
4446 for _ in &arms {
4447 emitter.emit_end();
4448 }
4449 emitter.emit_end();
4450 Ok(())
4451}
4452
4453fn emit_bounds_checked_index_with_receiver_on_stack(
4467 emitter: &mut FunctionEmitter,
4468 ctx: &CodegenCtx,
4469 idx: ExprId,
4470 result_ty: &Type,
4471 allow_missing: bool,
4472) -> Result<(), crate::compiler_error::CompilerFailure> {
4473 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
4474 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4475 })?;
4476 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
4477 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
4478 })?;
4479 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4480 nullable: false,
4481 heap_type: HeapType::Concrete(raw_array_idx),
4482 }))?;
4483 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4484 emitter.instruction(Instruction::LocalSet(receiver));
4485 emit_expr(emitter, ctx, idx)?;
4486 let key = emitter.add_anonymous_local(
4487 ctx.symbols.value_type(
4488 &ctx.ta
4489 .try_expr(idx)
4490 .map_err(crate::codegen::arena_failure)?
4491 .ty,
4492 )?,
4493 )?;
4494 emitter.instruction(Instruction::LocalSet(key));
4495 emitter.instruction(Instruction::LocalGet(receiver));
4496 crate::codegen::cast_check::emit_operation_cast_on_stack(
4497 emitter,
4498 ctx,
4499 &Type::Unknown,
4500 &Type::Array(Box::new(Type::Unknown)),
4501 )?;
4502 emitter.instruction(Instruction::LocalGet(key));
4503 emit_index_number(
4504 emitter,
4505 ctx,
4506 &ctx.ta
4507 .try_expr(idx)
4508 .map_err(crate::codegen::arena_failure)?
4509 .ty,
4510 )?;
4511 let idx_f64_local = stash_index_operand(emitter)?;
4512 let length = stash_array_length(emitter, array_idx)?;
4513 emitter.instruction(Instruction::StructGet {
4514 struct_type_index: array_idx,
4515 field_index: 1,
4516 });
4517 emitter.instruction(Instruction::LocalSet(raw_arr_local));
4518 if allow_missing {
4519 emitter.instruction(Instruction::LocalGet(idx_f64_local));
4520 emitter.instruction(Instruction::LocalGet(length));
4521 emitter.instruction(Instruction::F64ConvertI32U);
4522 emitter.instruction(Instruction::F64Ge);
4523 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
4524 emit_undefined(emitter, ctx)?;
4525 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
4526 emitter.emit_else();
4527 }
4528 let idx_local = emit_checked_index_with_length(emitter, ctx, length, idx_f64_local)?;
4529 emitter.instruction(Instruction::LocalGet(raw_arr_local));
4530 emitter.instruction(Instruction::LocalGet(idx_local));
4531 emitter.instruction(Instruction::ArrayGet(raw_array_idx));
4532 cast::emit_cast_to(emitter, ctx, result_ty)?;
4533 if allow_missing {
4534 emitter.emit_end();
4535 }
4536 Ok(())
4537}
4538
4539fn is_optional_tuple_position(
4540 ctx: &CodegenCtx<'_>,
4541 receiver: &Type,
4542 index: ExprId,
4543) -> Result<bool, crate::compiler_error::CompilerFailure> {
4544 let expression = ctx
4545 .ta
4546 .try_expr(index)
4547 .map_err(crate::codegen::arena_failure)?;
4548 let TypedExprKind::Number(index) = expression.kind else {
4549 return Ok(false);
4550 };
4551 Ok(index.fract() == 0.0 && tuple_permits_missing_index(receiver, index))
4552}
4553
4554fn tuple_permits_missing_index(receiver: &Type, index: f64) -> bool {
4558 match receiver.lowered() {
4559 Type::Tuple(elements) => index >= elements.required_len() as f64,
4560 Type::Union(members) => members
4561 .iter()
4562 .any(|member| tuple_permits_missing_index(member, index)),
4563 _ => false,
4564 }
4565}
4566
4567fn emit_uint8_index_with_receiver_on_stack(
4568 emitter: &mut FunctionEmitter,
4569 ctx: &CodegenCtx,
4570 index: ExprId,
4571) -> Result<(), crate::compiler_error::CompilerFailure> {
4572 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
4573 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4574 })?;
4575 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
4576 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
4577 })?;
4578 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4579 nullable: false,
4580 heap_type: HeapType::Concrete(raw_uint8_idx),
4581 }))?;
4582 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4583 emitter.instruction(Instruction::LocalSet(receiver));
4584 emit_expr(emitter, ctx, index)?;
4585 let key = emitter.add_anonymous_local(
4586 ctx.symbols.value_type(
4587 &ctx.ta
4588 .try_expr(index)
4589 .map_err(crate::codegen::arena_failure)?
4590 .ty,
4591 )?,
4592 )?;
4593 emitter.instruction(Instruction::LocalSet(key));
4594 emitter.instruction(Instruction::LocalGet(receiver));
4595 crate::codegen::cast_check::emit_operation_cast_on_stack(
4596 emitter,
4597 ctx,
4598 &Type::Unknown,
4599 &Type::Uint8Array,
4600 )?;
4601 emitter.instruction(Instruction::LocalGet(key));
4602 emit_index_number(
4603 emitter,
4604 ctx,
4605 &ctx.ta
4606 .try_expr(index)
4607 .map_err(crate::codegen::arena_failure)?
4608 .ty,
4609 )?;
4610 let idx_f64_local = stash_index_operand(emitter)?;
4611 emitter.instruction(Instruction::StructGet {
4612 struct_type_index: uint8_idx,
4613 field_index: 1,
4614 });
4615 emitter.instruction(Instruction::LocalSet(raw_local));
4616 let idx_local = emit_checked_index(emitter, ctx, raw_local, idx_f64_local)?;
4617 emitter.instruction(Instruction::LocalGet(raw_local));
4618 emitter.instruction(Instruction::LocalGet(idx_local));
4619 emitter.instruction(Instruction::ArrayGetU(raw_uint8_idx));
4620 emitter.instruction(Instruction::F64ConvertI32U);
4621 Ok(())
4622}
4623
4624#[derive(Clone, Copy)]
4626enum AccessorWrite {
4627 Setter,
4628 ReadOnly,
4629}
4630
4631impl AccessorWrite {
4632 fn scanned_kind(self) -> crate::AccessorKind {
4635 match self {
4636 AccessorWrite::Setter => crate::AccessorKind::Set,
4637 AccessorWrite::ReadOnly => crate::AccessorKind::Get,
4638 }
4639 }
4640}
4641
4642pub(crate) fn emit_object_field_index_by_name(
4643 emitter: &mut FunctionEmitter,
4644 ctx: &CodegenCtx,
4645 object_local: u32,
4646 name_global: u32,
4647) {
4648 emit_field_index_by_name(emitter, ctx, object_local, name_global, false);
4649}
4650
4651fn emit_field_index_by_name(
4652 emitter: &mut FunctionEmitter,
4653 ctx: &CodegenCtx,
4654 object_local: u32,
4655 name_global: u32,
4656 accessor: bool,
4657) {
4658 ctx.latch(emit_field_index_by_name_checked(
4659 emitter,
4660 ctx,
4661 object_local,
4662 name_global,
4663 accessor,
4664 ));
4665}
4666
4667fn emit_field_index_by_name_checked(
4668 emitter: &mut FunctionEmitter,
4669 ctx: &CodegenCtx,
4670 object_local: u32,
4671 name_global: u32,
4672 accessor: bool,
4673) -> Result<(), crate::compiler_error::CompilerFailure> {
4674 emitter.instruction(Instruction::LocalGet(object_local));
4675 emitter.instruction(Instruction::GlobalGet(name_global));
4676 emitter.instruction(Instruction::I32Const(i32::from(accessor)));
4677 emitter.instruction(Instruction::Call(
4678 ctx.symbols
4679 .field_lookup_function
4680 .ok_or_else(|| crate::codegen::internal_failure("field lookup allocated"))?,
4681 ));
4682
4683 Ok(())
4684}
4685
4686fn emit_direct_call(
4692 emitter: &mut FunctionEmitter,
4693 ctx: &CodegenCtx,
4694 is_host: bool,
4695 wasm_idx: u32,
4696 params: &[Type],
4697 ret: &Type,
4698 args: &[ExprId],
4699) -> Result<(), crate::compiler_error::CompilerFailure> {
4700 if params.len() != args.len() {
4701 return Err(crate::codegen::internal_failure(
4702 "direct-call argument count mismatch",
4703 ));
4704 }
4705 let string_type_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
4706 crate::codegen::internal_failure("$string type registered before call codegen")
4707 })?;
4708 let raw_string_type_idx = ctx.symbols.raw_string_type_idx().ok_or_else(|| {
4709 crate::codegen::internal_failure("$rawString type registered before call codegen")
4710 })?;
4711 let mut evaluated = Vec::with_capacity(args.len());
4712 for &arg in args {
4713 emit_expr(emitter, ctx, arg)?;
4714 let slot = emitter.add_anonymous_local(
4715 ctx.symbols.value_type(
4716 &ctx.ta
4717 .try_expr(arg)
4718 .map_err(crate::codegen::arena_failure)?
4719 .ty,
4720 )?,
4721 )?;
4722 emitter.instruction(Instruction::LocalSet(slot));
4723 evaluated.push(slot);
4724 }
4725 for (i, (&arg_id, local)) in args.iter().zip(evaluated).enumerate() {
4726 emitter.instruction(Instruction::LocalGet(local));
4727 if let Some(param_ty) = params.get(i) {
4733 let arg_ty = ctx
4734 .ta
4735 .try_expr(arg_id)
4736 .map_err(crate::codegen::arena_failure)?
4737 .ty
4738 .clone();
4739 let default = ctx
4740 .symbols
4741 .host_call_defaults(wasm_idx, args.len())
4742 .get(i)
4743 .and_then(Option::as_ref);
4744 crate::codegen::argument_defaults::emit_argument(
4745 emitter,
4746 ctx,
4747 &arg_ty,
4748 ctx.symbols.value_type(param_ty)?,
4749 default,
4750 )?;
4751 }
4752 if is_host
4757 && params
4758 .get(i)
4759 .is_some_and(|p| matches!(p.lowered(), Type::String))
4760 {
4761 emitter.instruction(Instruction::StructGet {
4762 struct_type_index: string_type_idx,
4763 field_index: 1,
4764 });
4765 }
4766 }
4767 emitter.instruction(Instruction::Call(wasm_idx));
4768 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
4769 emit_undefined(emitter, ctx)?;
4770 }
4771 let _: () = if is_host && matches!(ret.lowered(), Type::String) {
4772 let scratch = emitter.add_anonymous_local(ValType::Ref(RefType {
4773 nullable: false,
4774 heap_type: HeapType::Concrete(raw_string_type_idx),
4775 }))?;
4776 emitter.instruction(Instruction::LocalSet(scratch));
4777 let vtable_global = ctx
4778 .symbols
4779 .prelude_global_idx("string_vtable")
4780 .ok_or_else(|| {
4781 crate::codegen::internal_failure("string_vtable imported from prelude")
4782 })?;
4783 emitter.instruction(Instruction::GlobalGet(vtable_global));
4784 emitter.instruction(Instruction::LocalGet(scratch));
4785 emitter.instruction(Instruction::I64Const(0));
4786 emitter.instruction(Instruction::StructNew(string_type_idx));
4787 };
4788 Ok(())
4789}
4790
4791fn emit_indirect_closure_call(
4800 emitter: &mut FunctionEmitter,
4801 ctx: &CodegenCtx,
4802 callee: ExprId,
4803 args: &[ExprId],
4804 result_ty: &Type,
4805) -> Result<(), crate::compiler_error::CompilerFailure> {
4806 let callee_ty = ctx
4807 .ta
4808 .source_type(callee)
4809 .map_err(crate::codegen::arena_failure)?
4810 .clone();
4811 let receiver = emit_callee(emitter, ctx, callee)?;
4812 emitter.call_receiver = receiver;
4813 emit_indirect_closure_call_with_receiver_on_stack(emitter, ctx, &callee_ty, args, result_ty)?;
4814 Ok(())
4815}
4816
4817fn emit_callee(
4819 emitter: &mut FunctionEmitter,
4820 ctx: &CodegenCtx,
4821 callee: ExprId,
4822) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
4823 let reference = reference_expression(ctx.ta, callee)?;
4824 let receiver = match &ctx
4825 .ta
4826 .try_expr(reference)
4827 .map_err(crate::codegen::arena_failure)?
4828 .kind
4829 {
4830 TypedExprKind::FieldAccess { receiver, .. }
4831 | TypedExprKind::IndexAccess { receiver, .. }
4832 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => Some(*receiver),
4833 _ => None,
4834 };
4835 let mark = emitter.single_evaluation_mark();
4836 let slot = receiver
4837 .map(|receiver| {
4838 emit_expr(emitter, ctx, receiver)?;
4839 let slot = emitter.add_anonymous_local(
4840 ctx.symbols.value_type(
4841 &ctx.ta
4842 .try_expr(receiver)
4843 .map_err(crate::codegen::arena_failure)?
4844 .ty,
4845 )?,
4846 )?;
4847 emitter.instruction(Instruction::LocalSet(slot));
4848 emitter.record_single_evaluation(receiver, slot)?;
4849 Ok::<_, crate::compiler_error::CompilerFailure>(slot)
4850 })
4851 .transpose()?;
4852 emit_expr(emitter, ctx, callee)?;
4853 emitter.end_single_evaluations(mark)?;
4854 Ok(
4855 if matches!(
4856 ctx.ta
4857 .try_expr(reference)
4858 .map_err(crate::codegen::arena_failure)?
4859 .kind,
4860 TypedExprKind::OptionalChain { .. }
4861 ) {
4862 emitter.call_receiver.take()
4863 } else {
4864 slot
4865 },
4866 )
4867}
4868
4869fn reference_expression(
4870 ast: &crate::TypedAst,
4871 mut id: ExprId,
4872) -> Result<ExprId, crate::compiler_error::CompilerFailure> {
4873 loop {
4874 match ast
4875 .try_expr(id)
4876 .map_err(crate::codegen::arena_failure)?
4877 .kind
4878 {
4879 TypedExprKind::Cast { value, .. } | TypedExprKind::NonNullAssert { value } => {
4880 id = value;
4881 }
4882 _ => return Ok(id),
4883 }
4884 }
4885}
4886
4887fn emit_indirect_closure_call_with_receiver_on_stack(
4893 emitter: &mut FunctionEmitter,
4894 ctx: &CodegenCtx,
4895 callee_ty: &Type,
4896 args: &[ExprId],
4897 result_ty: &Type,
4898) -> Result<(), crate::compiler_error::CompilerFailure> {
4899 let receiver = emitter.call_receiver.take();
4900 let callee = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4901 emitter.instruction(Instruction::LocalSet(callee));
4902 let mut arguments = Vec::with_capacity(args.len());
4903 for &arg in args {
4904 emit_expr(emitter, ctx, arg)?;
4905 cast::emit_box(
4906 emitter,
4907 ctx,
4908 &ctx.ta
4909 .try_expr(arg)
4910 .map_err(crate::codegen::arena_failure)?
4911 .ty,
4912 )?;
4913 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4914 emitter.instruction(Instruction::LocalSet(slot));
4915 arguments.push(slot);
4916 }
4917 emitter.instruction(Instruction::LocalGet(callee));
4918 crate::codegen::cast_check::emit_operation_cast_on_stack(
4919 emitter,
4920 ctx,
4921 &Type::Unknown,
4922 callee_ty,
4923 )?;
4924 let callee_ty = callee_ty.lowered();
4929 let closure_sig = crate::codegen::closures::classify(callee_ty)?;
4930 let closure_struct_idx = ctx
4931 .symbols
4932 .closure_struct_type_idx(closure_sig)
4933 .ok_or_else(|| {
4934 crate::codegen::internal_failure(
4935 "closure struct type registered for every closure-typed callee",
4936 )
4937 })?;
4938 let fn_type_idx = ctx
4939 .symbols
4940 .closure_func_type_idx(closure_sig)
4941 .ok_or_else(|| {
4942 crate::codegen::internal_failure(
4943 "closure funcref type registered for every closure-typed callee",
4944 )
4945 })?;
4946 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4947 nullable: false,
4948 heap_type: HeapType::Concrete(closure_struct_idx),
4949 }))?;
4950 emitter.instruction(Instruction::LocalTee(closure_local));
4951 emitter.instruction(Instruction::StructGet {
4952 struct_type_index: closure_struct_idx,
4953 field_index: 2,
4954 });
4955 if let Some(receiver) = receiver {
4956 crate::codegen::this_binding::bind(emitter, ctx, receiver)?;
4957 }
4958 for argument in arguments {
4966 emitter.instruction(Instruction::LocalGet(argument));
4967 }
4968 emitter.instruction(Instruction::LocalGet(closure_local));
4969 emitter.instruction(Instruction::StructGet {
4970 struct_type_index: closure_struct_idx,
4971 field_index: 1,
4972 });
4973 emitter.instruction(Instruction::CallRef(fn_type_idx));
4974 let Type::Function { ret, .. } = callee_ty.lowered() else {
4977 return Err(crate::codegen::internal_failure(
4978 "indirect call requires a function type",
4979 ));
4980 };
4981 let _: () = if !ret.is_void() {
4982 crate::codegen::cast_check::emit_checked_cast_on_stack(
4983 emitter,
4984 ctx,
4985 &Type::Unknown,
4986 result_ty,
4987 )?;
4988 };
4989 Ok(())
4990}
4991
4992fn emit_typeof_tag(
4995 emitter: &mut FunctionEmitter,
4996 ctx: &CodegenCtx,
4997 value: ExprId,
4998 tag: crate::TypeofTagKind,
4999) -> Result<(), crate::compiler_error::CompilerFailure> {
5000 let _: () = match tag {
5001 crate::TypeofTagKind::BigInt => {
5002 emit_expr(emitter, ctx, value)?;
5003 cast::emit_box(
5004 emitter,
5005 ctx,
5006 &ctx.ta
5007 .try_expr(value)
5008 .map_err(crate::codegen::arena_failure)?
5009 .ty,
5010 )?;
5011 let intrinsic = ctx
5012 .symbols
5013 .bigint_type_idx()
5014 .ok_or_else(|| crate::codegen::internal_failure("bigint intrinsic missing"))?;
5015 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(intrinsic)));
5016 }
5017 crate::TypeofTagKind::Undefined => {
5018 emit_expr(emitter, ctx, value)?;
5019 cast::emit_box(
5020 emitter,
5021 ctx,
5022 &ctx.ta
5023 .try_expr(value)
5024 .map_err(crate::codegen::arena_failure)?
5025 .ty,
5026 )?;
5027 emit_is_undefined(emitter, ctx)?;
5028 }
5029 crate::TypeofTagKind::Number => {
5030 let boxed = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
5036 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
5037 })?;
5038 emit_expr(emitter, ctx, value)?;
5039 cast::emit_box(
5040 emitter,
5041 ctx,
5042 &ctx.ta
5043 .try_expr(value)
5044 .map_err(crate::codegen::arena_failure)?
5045 .ty,
5046 )?;
5047 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5048 }
5049 crate::TypeofTagKind::String => {
5050 let string = ctx.symbols.string_type_idx().ok_or_else(|| {
5051 crate::codegen::internal_failure("$string intrinsic type registered")
5052 })?;
5053 emit_expr(emitter, ctx, value)?;
5054 cast::emit_box(
5055 emitter,
5056 ctx,
5057 &ctx.ta
5058 .try_expr(value)
5059 .map_err(crate::codegen::arena_failure)?
5060 .ty,
5061 )?;
5062 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
5063 }
5064 crate::TypeofTagKind::Boolean => {
5065 let boxed = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
5066 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
5067 })?;
5068 emit_expr(emitter, ctx, value)?;
5069 cast::emit_box(
5070 emitter,
5071 ctx,
5072 &ctx.ta
5073 .try_expr(value)
5074 .map_err(crate::codegen::arena_failure)?
5075 .ty,
5076 )?;
5077 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5078 }
5079 crate::TypeofTagKind::Function => {
5080 let closure_idx = ctx.symbols.closure_type_idx().ok_or_else(|| {
5081 crate::codegen::internal_failure("$Closure intrinsic type registered")
5082 })?;
5083 emit_expr(emitter, ctx, value)?;
5084 cast::emit_box(
5085 emitter,
5086 ctx,
5087 &ctx.ta
5088 .try_expr(value)
5089 .map_err(crate::codegen::arena_failure)?
5090 .ty,
5091 )?;
5092 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
5093 }
5094 crate::TypeofTagKind::Object => {
5095 let scratch_ty = ctx.symbols.value_type(&Type::Unknown)?;
5096 let nullable = matches!(scratch_ty, ValType::Ref(rt) if rt.nullable);
5097 let scratch = emitter.add_anonymous_local(scratch_ty)?;
5098 emit_expr(emitter, ctx, value)?;
5099 cast::emit_box(
5100 emitter,
5101 ctx,
5102 &ctx.ta
5103 .try_expr(value)
5104 .map_err(crate::codegen::arena_failure)?
5105 .ty,
5106 )?;
5107 emitter.instruction(Instruction::LocalSet(scratch));
5108 emit_is_non_primitive(emitter, ctx, scratch, Closures::AreNotObjects)?;
5109 if nullable {
5110 emitter.instruction(Instruction::LocalGet(scratch));
5115 emitter.instruction(Instruction::RefIsNull);
5116 emitter.instruction(Instruction::I32Or);
5117 }
5118 }
5119 };
5120 Ok(())
5121}
5122
5123#[derive(Clone, Copy, PartialEq, Eq)]
5126pub(crate) enum Closures {
5127 AreObjects,
5128 AreNotObjects,
5129}
5130
5131pub(crate) fn emit_is_non_primitive(
5140 emitter: &mut FunctionEmitter,
5141 ctx: &CodegenCtx,
5142 local: u32,
5143 closures: Closures,
5144) -> Result<(), crate::compiler_error::CompilerFailure> {
5145 let intrinsics = ctx
5146 .symbols
5147 .intrinsic_type_indices()
5148 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5149 emitter.instruction(Instruction::LocalGet(local));
5150 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5151 intrinsics.object,
5152 )));
5153 let mut excluded = vec![
5154 intrinsics.string,
5155 intrinsics.boxed_number,
5156 intrinsics.boxed_boolean,
5157 intrinsics.bigint,
5158 intrinsics.undefined,
5159 ];
5160 if closures == Closures::AreNotObjects {
5161 excluded.push(intrinsics.closure);
5162 }
5163 for primitive in excluded {
5164 emitter.instruction(Instruction::LocalGet(local));
5165 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(primitive)));
5166 emitter.instruction(Instruction::I32Eqz);
5167 emitter.instruction(Instruction::I32And);
5168 }
5169 Ok(())
5170}
5171
5172fn has_numeric_operands(
5175 ctx: &CodegenCtx,
5176 operands: &[ExprId],
5177 result_ty: &Type,
5178) -> Result<bool, crate::compiler_error::CompilerFailure> {
5179 if !matches!(result_ty.lowered(), Type::Number | Type::NumberLiteral(_)) {
5180 return Ok(false);
5181 }
5182 for &operand in operands {
5183 let ty = &ctx
5184 .ta
5185 .try_expr(operand)
5186 .map_err(crate::codegen::arena_failure)?
5187 .ty;
5188 if !matches!(
5189 ty.primitive_behavior(),
5190 Type::Number | Type::NumberLiteral(_)
5191 ) {
5192 return Ok(false);
5193 }
5194 }
5195 Ok(true)
5196}
5197
5198fn emit_number_bitwise(
5199 emitter: &mut FunctionEmitter,
5200 ctx: &CodegenCtx,
5201 op: BinOp,
5202 lhs: ExprId,
5203 rhs: ExprId,
5204) -> Result<(), crate::compiler_error::CompilerFailure> {
5205 emit_primitive_operand(emitter, ctx, lhs)?;
5206 emit_to_int32(emitter, ctx)?;
5207 emit_primitive_operand(emitter, ctx, rhs)?;
5208 emit_to_int32(emitter, ctx)?;
5209 let (instruction, unsigned) = match op {
5211 BinOp::BitAnd => (Instruction::I32And, false),
5212 BinOp::BitOr => (Instruction::I32Or, false),
5213 BinOp::BitXor => (Instruction::I32Xor, false),
5214 BinOp::Shl => (Instruction::I32Shl, false),
5215 BinOp::Shr => (Instruction::I32ShrS, false),
5216 BinOp::UnsignedShr => (Instruction::I32ShrU, true),
5217 _ => {
5218 return Err(crate::codegen::internal_failure(
5219 "a numeric bitwise operation has a bitwise operator",
5220 ));
5221 }
5222 };
5223 emitter.instruction(instruction);
5224 emitter.instruction(if unsigned {
5225 Instruction::F64ConvertI32U
5226 } else {
5227 Instruction::F64ConvertI32S
5228 });
5229 Ok(())
5230}
5231
5232fn emit_to_int32(
5236 emitter: &mut FunctionEmitter,
5237 ctx: &CodegenCtx,
5238) -> Result<(), crate::compiler_error::CompilerFailure> {
5239 let value = emitter.add_anonymous_local(ValType::F64)?;
5240 emitter.instruction(Instruction::LocalTee(value));
5241 emitter.instruction(Instruction::F64Abs);
5242 emitter.instruction(Instruction::F64Const(Ieee64::from(
5243 9_223_372_036_854_775_808.0,
5244 )));
5245 emitter.instruction(Instruction::F64Lt);
5246 emitter.emit_if(BlockType::Result(ValType::I32));
5247 emitter.instruction(Instruction::LocalGet(value));
5248 emitter.instruction(Instruction::I64TruncSatF64S);
5249 emitter.instruction(Instruction::I32WrapI64);
5250 emitter.emit_else();
5251 emitter.instruction(Instruction::LocalGet(value));
5252 cast::emit_box(emitter, ctx, &Type::Number)?;
5253 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
5254 cast::emit_box(emitter, ctx, &Type::Number)?;
5255 let host = ctx
5256 .symbols
5257 .func_idx(&crate::mangle::prelude("__value_bitor"))
5258 .ok_or_else(|| crate::codegen::internal_failure("missing bitwise host import: bitor"))?;
5259 emitter.instruction(Instruction::Call(host));
5260 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5261 emitter.instruction(Instruction::I32TruncF64S);
5262 emitter.emit_end();
5263 Ok(())
5264}
5265
5266fn emit_bitwise_host(
5267 emitter: &mut FunctionEmitter,
5268 ctx: &CodegenCtx,
5269 name: &str,
5270 operands: &[ExprId],
5271 result_ty: &Type,
5272) -> Result<(), crate::compiler_error::CompilerFailure> {
5273 for &operand in operands {
5274 emit_expr(emitter, ctx, operand)?;
5275 let ty = &ctx
5276 .ta
5277 .try_expr(operand)
5278 .map_err(crate::codegen::arena_failure)?
5279 .ty;
5280 cast::emit_box(emitter, ctx, ty)?;
5281 }
5282 let mangled = crate::mangle::prelude(&format!("__value_{name}"));
5283 let host = ctx.symbols.func_idx(&mangled).ok_or_else(|| {
5284 crate::codegen::internal_failure(format!("missing bitwise host import: {name}"))
5285 })?;
5286 emitter.instruction(Instruction::Call(host));
5287 cast::emit_cast_to(emitter, ctx, result_ty)
5288}
5289
5290fn emit_unary(
5291 emitter: &mut FunctionEmitter,
5292 ctx: &CodegenCtx,
5293 op: UnOp,
5294 operand: ExprId,
5295 result_ty: &Type,
5296) -> Result<(), crate::compiler_error::CompilerFailure> {
5297 if matches!(op, UnOp::Neg | UnOp::Pos)
5298 && try_emit_unreachable_for_never_operand(emitter, ctx, &[operand])?
5299 {
5300 return Ok(());
5301 }
5302 let _: () = match op {
5303 UnOp::BitNot if has_numeric_operands(ctx, &[operand], result_ty)? => {
5304 emit_primitive_operand(emitter, ctx, operand)?;
5305 emit_to_int32(emitter, ctx)?;
5306 emitter.instruction(Instruction::I32Const(-1));
5307 emitter.instruction(Instruction::I32Xor);
5308 emitter.instruction(Instruction::F64ConvertI32S);
5309 }
5310 UnOp::BitNot => emit_bitwise_host(emitter, ctx, "bitnot", &[operand], result_ty)?,
5311 UnOp::Neg => {
5312 if ctx
5314 .ta
5315 .try_expr(operand)
5316 .map_err(crate::codegen::arena_failure)?
5317 .ty
5318 .is_bigint()
5319 {
5320 emit_primitive_operand(emitter, ctx, operand)?;
5321 emit_bigint_extract_to_stack(emitter, ctx);
5322 let host_idx = ctx
5323 .symbols
5324 .func_idx(&crate::mangle::host(
5325 crate::runtime::BIGINT_MODULE_NAME,
5326 "neg",
5327 ))
5328 .ok_or_else(|| {
5329 crate::codegen::internal_failure(
5330 "submilli:bigint.neg import recorded by codegen bootstrap",
5331 )
5332 })?;
5333 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
5334 return Ok(());
5335 }
5336 emit_primitive_operand(emitter, ctx, operand)?;
5337 emitter.instruction(Instruction::F64Neg);
5338 }
5339 UnOp::Pos => {
5340 let operand_ty = ctx
5345 .ta
5346 .try_expr(operand)
5347 .map_err(crate::codegen::arena_failure)?
5348 .ty
5349 .clone();
5350 if operand_ty.is_string_shaped() {
5351 emit_primitive_operand(emitter, ctx, operand)?;
5352 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
5353 } else if matches!(operand_ty.primitive_behavior(), Type::Union(_)) {
5354 emit_expr(emitter, ctx, operand)?;
5355 cast::emit_box(emitter, ctx, &operand_ty)?;
5356 emit_union_to_number(emitter, ctx)?;
5357 } else {
5358 emit_primitive_operand(emitter, ctx, operand)?;
5359 }
5360 }
5361 UnOp::Not => {
5362 let operand_ty = ctx
5368 .ta
5369 .try_expr(operand)
5370 .map_err(crate::codegen::arena_failure)?
5371 .ty
5372 .clone();
5373 emit_expr(emitter, ctx, operand)?;
5374 crate::codegen::function_emitter::cast::emit_condition_to_i32(
5375 emitter,
5376 ctx,
5377 &operand_ty,
5378 )?;
5379 emitter.instruction(Instruction::I32Eqz);
5380 }
5381 };
5382 Ok(())
5383}
5384
5385fn to_number_host(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
5386 ctx.symbols
5387 .func_idx(&crate::mangle::host(
5388 crate::runtime::NUMBER_MODULE_NAME,
5389 "toNumber",
5390 ))
5391 .ok_or_else(|| {
5392 crate::codegen::internal_failure(
5393 "submilli:number.toNumber import recorded by codegen analysis",
5394 )
5395 })
5396}
5397
5398fn emit_union_to_number(
5401 emitter: &mut FunctionEmitter,
5402 ctx: &CodegenCtx,
5403) -> Result<(), crate::compiler_error::CompilerFailure> {
5404 let string = ctx
5405 .symbols
5406 .string_type_idx()
5407 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic type registered"))?;
5408 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5409 emitter.instruction(Instruction::LocalTee(tmp));
5410 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
5411 emitter.emit_if(BlockType::Result(ValType::F64));
5412 emitter.instruction(Instruction::LocalGet(tmp));
5413 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(string)));
5414 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
5415 emitter.emit_else();
5416 emitter.instruction(Instruction::LocalGet(tmp));
5417 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5418 emitter.emit_end();
5419 Ok(())
5420}
5421
5422#[allow(clippy::too_many_arguments)]
5426pub(crate) fn emit_method_call(
5427 emitter: &mut FunctionEmitter,
5428 ctx: &CodegenCtx,
5429 receiver: ExprId,
5430 iface: &crate::MangledName,
5431 method: &str,
5432 args: &[ExprId],
5433 generic_args: Option<&[crate::GenericArgument]>,
5434 return_cast: Option<&Type>,
5435 call_ret_ty: &Type,
5436) -> Result<(), crate::compiler_error::CompilerFailure> {
5437 let recv_ty = ctx
5438 .ta
5439 .source_type(receiver)
5440 .map_err(crate::codegen::arena_failure)?
5441 .clone();
5442 if !emit_dropped_static_receiver(emitter, ctx, receiver, iface)? {
5443 emit_receiver(emitter, ctx, receiver)?;
5444 }
5445 emit_method_call_with_receiver_on_stack(
5446 emitter,
5447 ctx,
5448 &recv_ty,
5449 iface,
5450 method,
5451 args,
5452 generic_args,
5453 return_cast,
5454 call_ret_ty,
5455 )?;
5456 Ok(())
5457}
5458
5459fn emit_dropped_static_receiver(
5463 emitter: &mut FunctionEmitter,
5464 ctx: &CodegenCtx,
5465 receiver: ExprId,
5466 iface: &crate::MangledName,
5467) -> Result<bool, crate::compiler_error::CompilerFailure> {
5468 let expr = ctx
5469 .ta
5470 .try_expr(receiver)
5471 .map_err(crate::codegen::arena_failure)?;
5472 if !matches!(expr.kind, TypedExprKind::GlobalRef { .. })
5473 || static_interface(ctx, &expr.ty) != Some(iface)
5474 {
5475 return Ok(false);
5476 }
5477 match ctx.symbols.value_type(&expr.ty)? {
5478 ValType::Ref(RefType { heap_type, .. }) => {
5479 emitter.instruction(Instruction::RefNull(heap_type));
5480 }
5481 other => {
5482 return Err(crate::codegen::internal_failure(format!(
5483 "static-interface binding lowered to {other:?}"
5484 )));
5485 }
5486 }
5487 Ok(true)
5488}
5489
5490#[allow(clippy::too_many_arguments)]
5497fn emit_method_call_with_receiver_on_stack(
5498 emitter: &mut FunctionEmitter,
5499 ctx: &CodegenCtx,
5500 recv_ty: &Type,
5501 iface: &crate::MangledName,
5502 method: &str,
5503 args: &[ExprId],
5504 generic_args: Option<&[crate::GenericArgument]>,
5505 return_cast: Option<&Type>,
5506 call_ret_ty: &Type,
5507) -> Result<(), crate::compiler_error::CompilerFailure> {
5508 let concrete_receiver = crate::typechecker::infer::narrowing::strip_nullish(recv_ty);
5509 crate::codegen::field_guards::attach(emitter, ctx, &concrete_receiver)?;
5510 let direct_key = crate::mangle::extend(iface, method);
5511 if let Some(func_idx) = ctx.symbols.func_idx(&direct_key) {
5512 if matches!(recv_ty.primitive_behavior(), Type::Number)
5513 && ctx.symbols.value_type(recv_ty)? != ValType::F64
5514 {
5515 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
5516 }
5517 match ctx.symbols.iface_dispatch(iface) {
5526 Some(crate::Dispatch::Direct) => {
5527 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
5539 emitter.instruction(Instruction::RefAsNonNull);
5540 }
5541 }
5542 Some(crate::Dispatch::Static) => {
5543 emitter.instruction(Instruction::Drop);
5544 }
5545 None | Some(crate::Dispatch::VTable) => {}
5550 }
5551 let abi = wrapper_abi_for_call(
5561 ctx,
5562 &direct_key,
5563 args,
5564 generic_args,
5565 return_cast,
5566 call_ret_ty,
5567 )?;
5568 let defaults = ctx.symbols.host_call_defaults(func_idx, args.len());
5569 emit_args_into_slots_with_defaults(emitter, ctx, args, Some(&abi.params), defaults)?;
5570 emitter.instruction(Instruction::Call(func_idx));
5571 emit_slot_return_cast(emitter, ctx, call_ret_ty, Some(&abi))?;
5572 return Ok(());
5573 }
5574 if ctx.symbols.is_rewritable_class_method(iface, method) {
5577 emit_interface_method_via_shape_with_receiver_on_stack(
5578 emitter,
5579 ctx,
5580 method,
5581 args,
5582 call_ret_ty,
5583 None,
5584 )?;
5585 return Ok(());
5586 }
5587 if ctx.symbols.class_method_slot(iface, method).is_some() {
5592 emit_class_vtable_dispatch(emitter, ctx, iface, method, args, call_ret_ty)?;
5593 return Ok(());
5594 }
5595 if let Some(slot) = vtable_slot_for_method(method) {
5596 emit_vtable_dispatch_on_object_stack(emitter, ctx, slot);
5597 return Ok(());
5598 }
5599 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
5608 emit_interface_method_via_shape_with_receiver_on_stack(
5609 emitter,
5610 ctx,
5611 method,
5612 args,
5613 call_ret_ty,
5614 None,
5615 )?;
5616 return Ok(());
5617 }
5618 Err(crate::codegen::internal_failure(format!(
5619 "method dispatch was not registered for `{iface}.{method}`"
5620 )))
5621}
5622
5623fn emit_class_method_value(
5624 emitter: &mut FunctionEmitter,
5625 ctx: &CodegenCtx,
5626 receiver_ty: &Type,
5627 method: &str,
5628 result_ty: &Type,
5629 check_ty: &Type,
5630) -> Result<(), crate::compiler_error::CompilerFailure> {
5631 let object_shape = ctx
5632 .symbols
5633 .intrinsic_type_indices()
5634 .ok_or_else(|| {
5635 crate::codegen::internal_failure("class method value requires object shape intrinsic")
5636 })?
5637 .object_shape;
5638 let receiver = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
5639 emit_object_property_read_as(emitter, ctx, receiver, method, result_ty, check_ty)
5640}
5641
5642pub(in crate::codegen) fn emit_interface_method_via_shape_with_receiver_on_stack(
5658 emitter: &mut FunctionEmitter,
5659 ctx: &CodegenCtx,
5660 method: &str,
5661 args: &[ExprId],
5662 call_ret_ty: &Type,
5663 slot_index_local: Option<u32>,
5664) -> Result<(), crate::compiler_error::CompilerFailure> {
5665 let args: Vec<_> = args
5666 .iter()
5667 .copied()
5668 .map(ShapeArgument::Expression)
5669 .collect();
5670 emit_shape_method_with_arguments(emitter, ctx, method, &args, call_ret_ty, slot_index_local)?;
5671 Ok(())
5672}
5673
5674enum ShapeArgument {
5675 Expression(ExprId),
5676 Local { slot: u32, ty: Type },
5677}
5678
5679impl ShapeArgument {
5680 fn ty(&self, ctx: &CodegenCtx) -> Result<Type, crate::compiler_error::CompilerFailure> {
5681 Ok(match self {
5682 Self::Expression(id) => ctx
5683 .ta
5684 .try_expr(*id)
5685 .map_err(crate::codegen::arena_failure)?
5686 .ty
5687 .clone(),
5688 Self::Local { ty, .. } => ty.clone(),
5689 })
5690 }
5691
5692 fn emit(
5693 &self,
5694 emitter: &mut FunctionEmitter,
5695 ctx: &CodegenCtx,
5696 ) -> Result<(), crate::compiler_error::CompilerFailure> {
5697 let _: () = match self {
5698 Self::Expression(id) => emit_expr(emitter, ctx, *id)?,
5699 Self::Local { slot, .. } => {
5700 emitter.instruction(Instruction::LocalGet(*slot));
5701 }
5702 };
5703 Ok(())
5704 }
5705}
5706
5707fn emit_shape_method_with_arguments(
5708 emitter: &mut FunctionEmitter,
5709 ctx: &CodegenCtx,
5710 method: &str,
5711 args: &[ShapeArgument],
5712 call_ret_ty: &Type,
5713 slot_index_local: Option<u32>,
5714) -> Result<(), crate::compiler_error::CompilerFailure> {
5715 let intrinsics = ctx
5716 .symbols
5717 .intrinsic_type_indices()
5718 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5719
5720 let arg_tys: Vec<Type> = args.iter().map(|a| a.ty(ctx)).collect::<Result<_, _>>()?;
5723 let fn_ty = Type::Function {
5724 params: arg_tys,
5725 ret: Box::new(call_ret_ty.clone()),
5726 predicate: None,
5727 has_rest: false,
5731 optional: 0,
5732 };
5733 let closure_sig = crate::codegen::closures::classify(&fn_ty)?;
5734 let closure_struct_idx = ctx
5735 .symbols
5736 .closure_struct_type_idx(closure_sig)
5737 .ok_or_else(|| {
5738 crate::codegen::internal_failure(
5739 "closure struct registered for the method's signature (SUB-166)",
5740 )
5741 })?;
5742 let fn_type_idx = ctx
5743 .symbols
5744 .closure_func_type_idx(closure_sig)
5745 .ok_or_else(|| {
5746 crate::codegen::internal_failure(
5747 "closure funcref type registered for the method's signature",
5748 )
5749 })?;
5750
5751 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5752 nullable: false,
5753 heap_type: HeapType::Concrete(intrinsics.object_shape),
5754 }))?;
5755 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5756 nullable: false,
5757 heap_type: HeapType::Concrete(closure_struct_idx),
5758 }))?;
5759
5760 emitter.instruction(Instruction::RefAsNonNull);
5764 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
5765 intrinsics.object_shape,
5766 )));
5767 emitter.instruction(Instruction::LocalSet(rcv_local));
5768
5769 if let Some(index_local) = slot_index_local {
5772 emit_field_slot_get(emitter, intrinsics, rcv_local, index_local);
5773 } else {
5774 let name_global = ctx
5775 .symbols
5776 .field_name_string_global_idx(method)
5777 .ok_or_else(|| {
5778 crate::codegen::internal_failure(
5779 ("per-name string global recorded during field-name-strings emission \
5780 for interface method names")
5781 .to_string(),
5782 )
5783 })?;
5784 emit_object_field_read_by_name(emitter, ctx, rcv_local, name_global);
5785 }
5786
5787 let method_value = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5788 emitter.instruction(Instruction::LocalSet(method_value));
5789 let mut evaluated_args = Vec::with_capacity(args.len());
5790 for arg in args {
5791 arg.emit(emitter, ctx)?;
5792 cast::emit_box(emitter, ctx, &arg.ty(ctx)?)?;
5793 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5794 emitter.instruction(Instruction::LocalSet(slot));
5795 evaluated_args.push(slot);
5796 }
5797 emitter.instruction(Instruction::LocalGet(method_value));
5798 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5799 intrinsics.closure,
5800 )));
5801 emitter.instruction(Instruction::I32Eqz);
5802 emitter.emit_if(BlockType::Empty);
5803 crate::codegen::throw::emit_type_error_throw(emitter, ctx, "Value is not callable");
5804 emitter.emit_end();
5805 emitter.instruction(Instruction::LocalGet(method_value));
5806
5807 crate::codegen::closure_coercions::emit_erased_cast(
5811 emitter,
5812 ctx,
5813 crate::codegen::closures::ClosureSig::of(args.len(), call_ret_ty)?,
5814 )?;
5815 emitter.instruction(Instruction::LocalTee(closure_local));
5816
5817 emitter.instruction(Instruction::StructGet {
5819 struct_type_index: closure_struct_idx,
5820 field_index: 2,
5821 });
5822
5823 crate::codegen::this_binding::bind(emitter, ctx, rcv_local)?;
5824 for slot in evaluated_args {
5827 emitter.instruction(Instruction::LocalGet(slot));
5828 }
5829
5830 emitter.instruction(Instruction::LocalGet(closure_local));
5832 emitter.instruction(Instruction::StructGet {
5833 struct_type_index: closure_struct_idx,
5834 field_index: 1,
5835 });
5836 emitter.instruction(Instruction::CallRef(fn_type_idx));
5837
5838 let _: () = if !call_ret_ty.is_void() {
5841 crate::codegen::cast_check::emit_checked_cast_on_stack(
5842 emitter,
5843 ctx,
5844 &Type::Unknown,
5845 call_ret_ty,
5846 )?;
5847 };
5848 Ok(())
5849}
5850
5851fn vtable_slot_for_method(method: &str) -> Option<u32> {
5862 match method {
5863 "toString" => Some(0),
5864 "toJson" => Some(1),
5865 "equals" => Some(2),
5866 "hash" => Some(3),
5867 _ => None,
5868 }
5869}
5870
5871pub(super) fn emit_vtable_dispatch_on_object_stack(
5882 emitter: &mut FunctionEmitter,
5883 ctx: &CodegenCtx,
5884 slot: u32,
5885) {
5886 let Some(intrinsics) = ctx.require(
5887 ctx.symbols.intrinsic_type_indices(),
5888 "intrinsics declared by codegen entry",
5889 ) else {
5890 return;
5891 };
5892 let fn_type_idx = match slot {
5897 0 => intrinsics.to_string_fn,
5898 1 => intrinsics.to_json_fn,
5899 _ => {
5900 ctx.fail("vtable string dispatch requires toString or toJson slot");
5901 return;
5902 }
5903 };
5904 let Some(obj_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5905 nullable: false,
5906 heap_type: HeapType::Concrete(intrinsics.object),
5907 }))) else {
5908 return;
5909 };
5910 let Some(fn_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
5911 nullable: false,
5912 heap_type: HeapType::Concrete(fn_type_idx),
5913 }))) else {
5914 return;
5915 };
5916 emitter.instruction(Instruction::RefAsNonNull);
5922 emitter.instruction(Instruction::LocalTee(obj_local));
5923 emitter.instruction(Instruction::StructGet {
5924 struct_type_index: intrinsics.object,
5925 field_index: 0,
5926 });
5927 emitter.instruction(Instruction::StructGet {
5928 struct_type_index: intrinsics.vtable,
5929 field_index: slot,
5930 });
5931 emitter.instruction(Instruction::LocalSet(fn_local));
5932 emitter.instruction(Instruction::LocalGet(obj_local));
5933 emitter.instruction(Instruction::LocalGet(fn_local));
5934 emitter.instruction(Instruction::CallRef(fn_type_idx));
5935}
5936
5937fn emit_args_into_slots(
5941 emitter: &mut FunctionEmitter,
5942 ctx: &CodegenCtx,
5943 args: &[ExprId],
5944 slots: Option<&[ValType]>,
5945) -> Result<(), crate::compiler_error::CompilerFailure> {
5946 emit_args_into_slots_with_defaults(emitter, ctx, args, slots, &[])
5947}
5948
5949fn emit_args_into_slots_with_defaults(
5950 emitter: &mut FunctionEmitter,
5951 ctx: &CodegenCtx,
5952 args: &[ExprId],
5953 slots: Option<&[ValType]>,
5954 defaults: &[Option<crate::DefaultValue>],
5955) -> Result<(), crate::compiler_error::CompilerFailure> {
5956 if slots.is_some_and(|slots| slots.len() != args.len()) {
5957 return Err(crate::codegen::internal_failure(
5958 "call ABI argument count mismatch",
5959 ));
5960 }
5961 let locals: Vec<_> = args
5962 .iter()
5963 .map(|&arg| {
5964 emit_expr(emitter, ctx, arg)?;
5965 let local = emitter.add_anonymous_local(
5966 ctx.symbols.value_type(
5967 &ctx.ta
5968 .try_expr(arg)
5969 .map_err(crate::codegen::arena_failure)?
5970 .ty,
5971 )?,
5972 )?;
5973 emitter.instruction(Instruction::LocalSet(local));
5974 Ok::<_, crate::compiler_error::CompilerFailure>(local)
5975 })
5976 .collect::<Result<_, _>>()?;
5977 for (i, (&arg, local)) in args.iter().zip(locals).enumerate() {
5978 emitter.instruction(Instruction::LocalGet(local));
5979 if let Some(slot) = slots.and_then(|s| s.get(i).copied()) {
5980 crate::codegen::argument_defaults::emit_argument(
5981 emitter,
5982 ctx,
5983 &ctx.ta
5984 .try_expr(arg)
5985 .map_err(crate::codegen::arena_failure)?
5986 .ty,
5987 slot,
5988 defaults.get(i).and_then(Option::as_ref),
5989 )?;
5990 }
5991 }
5992 Ok(())
5993}
5994
5995fn wrapper_abi_for_call(
6010 ctx: &CodegenCtx,
6011 direct_key: &crate::MangledName,
6012 args: &[ExprId],
6013 generic_args: Option<&[crate::GenericArgument]>,
6014 return_cast: Option<&Type>,
6015 call_ret_ty: &Type,
6016) -> Result<MethodSlotAbi, crate::compiler_error::CompilerFailure> {
6017 if let Some(recorded) = ctx.symbols.iface_method_abi(direct_key) {
6018 return Ok(recorded.clone());
6019 }
6020 let erased = ctx.symbols.value_type(&Type::Unknown)?;
6021 let slot_is_erased =
6022 |i: usize| generic_args.is_some_and(|flags| flags.get(i).is_some_and(|g| g.is_generic));
6023 Ok(MethodSlotAbi {
6024 params: args
6025 .iter()
6026 .enumerate()
6027 .map(|(i, &a)| {
6028 Ok::<_, crate::compiler_error::CompilerFailure>(if slot_is_erased(i) {
6029 erased
6030 } else {
6031 ctx.symbols.value_type(
6032 &ctx.ta
6033 .try_expr(a)
6034 .map_err(crate::codegen::arena_failure)?
6035 .ty,
6036 )?
6037 })
6038 })
6039 .collect::<Result<_, _>>()?,
6040 ret: match return_cast {
6041 Some(_) => Some(erased),
6042 None => (!call_ret_ty.is_void())
6043 .then(|| ctx.symbols.value_type(call_ret_ty))
6044 .transpose()?,
6045 },
6046 })
6047}
6048
6049fn emit_slot_return_cast(
6052 emitter: &mut FunctionEmitter,
6053 ctx: &CodegenCtx,
6054 call_ret_ty: &Type,
6055 abi: Option<&MethodSlotAbi>,
6056) -> Result<(), crate::compiler_error::CompilerFailure> {
6057 if matches!(call_ret_ty.lowered(), Type::Never) {
6058 emitter.instruction(Instruction::Unreachable);
6059 return Ok(());
6060 }
6061 if abi.is_some_and(|abi| abi.ret.is_none()) {
6064 emit_undefined(emitter, ctx)?;
6065 }
6066 if call_ret_ty.is_void() {
6067 return Ok(());
6068 }
6069 if matches!(call_ret_ty, Type::Unknown) {
6070 match abi.and_then(|abi| abi.ret) {
6071 Some(ValType::F64) => cast::emit_box(emitter, ctx, &Type::Number)?,
6072 Some(ValType::I32) => cast::emit_box(emitter, ctx, &Type::Boolean)?,
6073 _ => {}
6074 }
6075 return Ok(());
6076 }
6077 if abi.and_then(|a| a.ret) != Some(ctx.symbols.value_type(call_ret_ty)?) {
6078 crate::codegen::cast_check::emit_checked_cast_on_stack(
6079 emitter,
6080 ctx,
6081 &Type::Unknown,
6082 call_ret_ty,
6083 )?;
6084 }
6085 crate::codegen::field_guards::attach(emitter, ctx, call_ret_ty)?;
6086 Ok(())
6087}
6088
6089fn emit_class_vtable_dispatch(
6102 emitter: &mut FunctionEmitter,
6103 ctx: &CodegenCtx,
6104 class: &crate::MangledName,
6105 method: &str,
6106 args: &[ExprId],
6107 call_ret_ty: &Type,
6108) -> Result<(), crate::compiler_error::CompilerFailure> {
6109 let struct_idx = ctx
6110 .symbols
6111 .class_struct_type_idx(class)
6112 .ok_or_else(|| crate::codegen::internal_failure("class struct type recorded"))?;
6113 let vtable_idx = ctx
6114 .symbols
6115 .class_vtable_type_idx(class)
6116 .ok_or_else(|| crate::codegen::internal_failure("class vtable type recorded"))?;
6117 let slot = ctx
6118 .symbols
6119 .class_method_slot(class, method)
6120 .ok_or_else(|| crate::codegen::internal_failure("class method slot recorded"))?;
6121 let sig_idx = ctx
6122 .symbols
6123 .class_method_sig(class, method)
6124 .ok_or_else(|| crate::codegen::internal_failure("class method sig recorded"))?;
6125
6126 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6127 nullable: false,
6128 heap_type: HeapType::Concrete(struct_idx),
6129 }))?;
6130 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
6133 emitter.instruction(Instruction::LocalSet(rcv_local));
6134
6135 emitter.instruction(Instruction::LocalGet(rcv_local));
6137 emitter.instruction(Instruction::StructGet {
6138 struct_type_index: struct_idx,
6139 field_index: 0,
6140 });
6141 emitter.instruction(Instruction::StructGet {
6142 struct_type_index: vtable_idx,
6143 field_index: slot,
6144 });
6145 let fn_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6146 nullable: false,
6147 heap_type: HeapType::Concrete(sig_idx),
6148 }))?;
6149 emitter.instruction(Instruction::LocalSet(fn_local));
6150
6151 let abi = ctx.symbols.class_method_abi(class, method).cloned();
6153 emitter.instruction(Instruction::LocalGet(rcv_local));
6154 emit_args_into_slots(
6155 emitter,
6156 ctx,
6157 args,
6158 abi.as_ref().map(|a| a.params.as_slice()),
6159 )?;
6160 emitter.instruction(Instruction::LocalGet(fn_local));
6161 emitter.instruction(Instruction::CallRef(sig_idx));
6162 emit_slot_return_cast(emitter, ctx, call_ret_ty, abi.as_ref())?;
6163 Ok(())
6164}
6165
6166fn emit_intrinsic_call(
6167 emitter: &mut FunctionEmitter,
6168 ctx: &CodegenCtx,
6169 kind: Intrinsic,
6170 args: &[ExprId],
6171 result_ty: &Type,
6172) -> Result<(), crate::compiler_error::CompilerFailure> {
6173 let _: () = match kind {
6174 Intrinsic::Assert => {
6175 if args.len() != 2 {
6179 return Err(crate::codegen::internal_failure(
6180 "assert requires two arguments",
6181 ));
6182 }
6183 emit_expr(emitter, ctx, args[0])?;
6184 cast::emit_condition_to_i32(
6185 emitter,
6186 ctx,
6187 &ctx.ta
6188 .try_expr(args[0])
6189 .map_err(crate::codegen::arena_failure)?
6190 .ty,
6191 )?;
6192 emit_expr(emitter, ctx, args[1])?;
6193 let message_type = &ctx
6194 .ta
6195 .try_expr(args[1])
6196 .map_err(crate::codegen::arena_failure)?
6197 .ty;
6198 let msg_local = emitter.add_anonymous_local(ctx.symbols.value_type(message_type)?)?;
6199 emitter.instruction(Instruction::LocalSet(msg_local));
6200 emitter.instruction(Instruction::I32Eqz);
6201 emitter.emit_if(BlockType::Empty);
6202 emitter.instruction(Instruction::LocalGet(msg_local));
6203 cast::emit_coerce_to_slot(emitter, ctx, message_type, &Type::String)?;
6204 let ctor_idx = ctx
6205 .symbols
6206 .func_idx(&crate::mangle::prelude("Error#constructor"))
6207 .ok_or_else(|| {
6208 crate::codegen::internal_failure("Error#constructor exported from prelude")
6209 })?;
6210 crate::codegen::throw::emit_no_error_options(emitter);
6211 emitter.instruction(Instruction::Call(ctor_idx));
6212 crate::codegen::throw::emit_error_throw(emitter, ctx);
6213 emitter.emit_end();
6214 emit_undefined(emitter, ctx)?;
6215 }
6216 Intrinsic::JsonStringify => {
6217 super::json::emit_stringify(emitter, ctx, args, result_ty)?;
6218 }
6219 Intrinsic::JsonParse => {
6220 super::json::emit_parse(emitter, ctx, args)?;
6221 }
6222 Intrinsic::ToString => {
6223 let [value] = args else {
6224 return Err(crate::codegen::internal_failure(
6225 "string conversion requires one argument",
6226 ));
6227 };
6228 emit_expr(emitter, ctx, *value)?;
6229 cast::emit_box(
6230 emitter,
6231 ctx,
6232 &ctx.ta
6233 .try_expr(*value)
6234 .map_err(crate::codegen::arena_failure)?
6235 .ty,
6236 )?;
6237 let function = ctx
6238 .symbols
6239 .prelude_func_idx("__value_to_string")
6240 .ok_or_else(|| {
6241 crate::codegen::internal_failure("dynamic string conversion collected")
6242 })?;
6243 emitter.instruction(Instruction::Call(function));
6244 }
6245 Intrinsic::ToObject => {
6246 let [value] = args else {
6247 return Err(crate::codegen::internal_failure(
6248 "object conversion requires one argument",
6249 ));
6250 };
6251 emit_expr(emitter, ctx, *value)?;
6252 let function = ctx
6253 .symbols
6254 .prelude_func_idx("ObjectConstructor##toObject")
6255 .ok_or_else(|| {
6256 crate::codegen::internal_failure("object conversion helper collected")
6257 })?;
6258 emitter.instruction(Instruction::Call(function));
6259 }
6260 Intrinsic::BigIntFromString => {
6267 if args.len() != 1 {
6268 return Err(crate::codegen::internal_failure(
6269 "BigInt.fromString requires one argument",
6270 ));
6271 }
6272 emit_expr(emitter, ctx, args[0])?;
6273 let string_type_idx = ctx
6276 .symbols
6277 .string_type_idx()
6278 .ok_or_else(|| crate::codegen::internal_failure("$string registered"))?;
6279 emitter.instruction(Instruction::StructGet {
6280 struct_type_index: string_type_idx,
6281 field_index: 1,
6282 });
6283 let host_idx = ctx
6284 .symbols
6285 .func_idx(&crate::mangle::host(
6286 crate::runtime::BIGINT_MODULE_NAME,
6287 "fromString",
6288 ))
6289 .ok_or_else(|| {
6290 crate::codegen::internal_failure(
6291 "submilli:bigint.fromString import recorded by codegen bootstrap",
6292 )
6293 })?;
6294 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6295 }
6296 };
6297 Ok(())
6298}
6299
6300fn emit_vtable_equality(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: BinOp) {
6305 ctx.latch(emit_vtable_equality_checked(emitter, ctx, op));
6306}
6307
6308fn emit_vtable_equality_checked(
6309 emitter: &mut FunctionEmitter,
6310 ctx: &CodegenCtx,
6311 op: BinOp,
6312) -> Result<(), crate::compiler_error::CompilerFailure> {
6313 let intrinsics = ctx
6314 .symbols
6315 .intrinsic_type_indices()
6316 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6317 let operand_ref = ValType::Ref(RefType {
6320 nullable: true,
6321 heap_type: HeapType::Concrete(intrinsics.object),
6322 });
6323 let object_ref = ValType::Ref(RefType {
6324 nullable: false,
6325 heap_type: HeapType::Concrete(intrinsics.object),
6326 });
6327 let equals_fn_ref = ValType::Ref(RefType {
6328 nullable: false,
6329 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6330 });
6331
6332 let Some(lhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6334 return Ok(());
6335 };
6336 let Some(rhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
6337 return Ok(());
6338 };
6339 let Some(lhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6340 return Ok(());
6341 };
6342 let Some(rhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
6343 return Ok(());
6344 };
6345 let Some(eq_fn_local) = ctx.latch(emitter.add_anonymous_local(equals_fn_ref)) else {
6346 return Ok(());
6347 };
6348
6349 emitter.instruction(Instruction::LocalSet(rhs_local));
6350 emitter.instruction(Instruction::LocalSet(lhs_local));
6351
6352 emitter.instruction(Instruction::LocalGet(lhs_local));
6353 emitter.instruction(Instruction::RefAsNonNull);
6354 emitter.instruction(Instruction::LocalSet(lhs_object));
6355 emitter.instruction(Instruction::LocalGet(rhs_local));
6356 emitter.instruction(Instruction::RefAsNonNull);
6357 emitter.instruction(Instruction::LocalSet(rhs_object));
6358
6359 emitter.instruction(Instruction::LocalGet(lhs_object));
6360 emitter.instruction(Instruction::StructGet {
6361 struct_type_index: intrinsics.object,
6362 field_index: 0,
6363 });
6364 emitter.instruction(Instruction::StructGet {
6365 struct_type_index: intrinsics.vtable,
6366 field_index: 2,
6367 });
6368 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6369
6370 emitter.instruction(Instruction::LocalGet(lhs_object));
6371 emitter.instruction(Instruction::LocalGet(rhs_object));
6372 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6373 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6374
6375 if matches!(op, BinOp::NotEq) {
6376 emitter.instruction(Instruction::I32Eqz);
6377 }
6378
6379 Ok(())
6380}
6381
6382fn emit_boxed_eq(
6395 emitter: &mut FunctionEmitter,
6396 ctx: &CodegenCtx,
6397 lhs: ExprId,
6398 rhs: ExprId,
6399 op: BinOp,
6400) -> Result<(), crate::compiler_error::CompilerFailure> {
6401 let intrinsics = ctx
6402 .symbols
6403 .intrinsic_type_indices()
6404 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6405 let object_null_ref = ValType::Ref(RefType {
6406 nullable: true,
6407 heap_type: HeapType::Concrete(intrinsics.object),
6408 });
6409 let object_ref = ValType::Ref(RefType {
6410 nullable: false,
6411 heap_type: HeapType::Concrete(intrinsics.object),
6412 });
6413 let equals_fn_ref = ValType::Ref(RefType {
6414 nullable: false,
6415 heap_type: HeapType::Concrete(intrinsics.equals_fn),
6416 });
6417
6418 let lhs_ty = ctx
6419 .ta
6420 .try_expr(lhs)
6421 .map_err(crate::codegen::arena_failure)?
6422 .ty
6423 .clone();
6424 let rhs_ty = ctx
6425 .ta
6426 .try_expr(rhs)
6427 .map_err(crate::codegen::arena_failure)?
6428 .ty
6429 .clone();
6430
6431 let lhs_local = emitter.add_anonymous_local(object_null_ref)?;
6436 let rhs_local = emitter.add_anonymous_local(object_null_ref)?;
6437 let lhs_nonnull_local = emitter.add_anonymous_local(object_ref)?;
6438 let eq_fn_local = emitter.add_anonymous_local(equals_fn_ref)?;
6439
6440 emit_expr(emitter, ctx, lhs)?;
6441 cast::emit_box(emitter, ctx, &lhs_ty)?;
6442 emitter.instruction(Instruction::LocalSet(lhs_local));
6443
6444 emit_expr(emitter, ctx, rhs)?;
6445 cast::emit_box(emitter, ctx, &rhs_ty)?;
6446 emitter.instruction(Instruction::LocalSet(rhs_local));
6447
6448 emitter.instruction(Instruction::LocalGet(lhs_local));
6452 emitter.instruction(Instruction::RefIsNull);
6453 emitter.emit_if(BlockType::Result(ValType::I32));
6454 emitter.instruction(Instruction::LocalGet(rhs_local));
6455 emitter.instruction(Instruction::RefIsNull);
6456 emitter.emit_else();
6457 emitter.instruction(Instruction::LocalGet(rhs_local));
6458 emitter.instruction(Instruction::RefIsNull);
6459 emitter.emit_if(BlockType::Result(ValType::I32));
6460 emitter.instruction(Instruction::I32Const(0));
6461 emitter.emit_else();
6462 emitter.instruction(Instruction::LocalGet(lhs_local));
6464 emitter.instruction(Instruction::RefAsNonNull);
6465 emitter.instruction(Instruction::LocalTee(lhs_nonnull_local));
6466 emitter.instruction(Instruction::StructGet {
6467 struct_type_index: intrinsics.object,
6468 field_index: 0,
6469 });
6470 emitter.instruction(Instruction::StructGet {
6471 struct_type_index: intrinsics.vtable,
6472 field_index: 2,
6473 });
6474 emitter.instruction(Instruction::LocalSet(eq_fn_local));
6475 emitter.instruction(Instruction::LocalGet(lhs_nonnull_local));
6476 emitter.instruction(Instruction::LocalGet(rhs_local));
6477 emitter.instruction(Instruction::RefAsNonNull);
6478 emitter.instruction(Instruction::LocalGet(eq_fn_local));
6479 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
6480 emitter.emit_end();
6481 emitter.emit_end();
6482
6483 let _: () = if matches!(op, BinOp::NotEq) {
6484 emitter.instruction(Instruction::I32Eqz);
6485 };
6486 Ok(())
6487}
6488
6489fn emit_bigint_literal(
6495 emitter: &mut FunctionEmitter,
6496 ctx: &CodegenCtx,
6497 digits: &str,
6498) -> Result<(), crate::compiler_error::CompilerFailure> {
6499 use crate::codegen::internal_failure;
6500 if let Some(entry) = ctx.bigints.lookup(digits)? {
6501 let raw_bigint_idx = ctx
6506 .symbols
6507 .raw_bigint_type_idx()
6508 .ok_or_else(|| internal_failure("the $rawBigInt intrinsic is not registered"))?;
6509 let bigint_idx = ctx
6510 .symbols
6511 .bigint_type_idx()
6512 .ok_or_else(|| internal_failure("the $bigint intrinsic is not registered"))?;
6513 let vtable_global = ctx
6514 .symbols
6515 .prelude_global_idx("bigint_vtable")
6516 .ok_or_else(|| internal_failure("bigint_vtable is not imported from the prelude"))?;
6517 let data_idx = crate::codegen::wasm_u32(ctx.strings.strings.len())?
6518 .checked_add(entry.data_idx)
6519 .ok_or_else(|| internal_failure("the module has too many data segments"))?;
6520 emitter.instruction(Instruction::GlobalGet(vtable_global));
6521 emitter.instruction(Instruction::I32Const(i32::from(entry.sign)));
6522 emitter.instruction(Instruction::I32Const(0));
6523 emitter.instruction(Instruction::I32Const(entry.limb_count.cast_signed()));
6524 emitter.instruction(Instruction::ArrayNewData {
6525 array_type_index: raw_bigint_idx,
6526 array_data_index: data_idx,
6527 });
6528 emitter.instruction(Instruction::StructNew(bigint_idx));
6529 return Ok(());
6530 }
6531 let v: i64 = digits
6535 .parse()
6536 .map_err(|_| internal_failure("an unpooled bigint literal is not a safe integer"))?;
6537 let host_idx = ctx
6538 .symbols
6539 .func_idx(&crate::mangle::host(
6540 crate::runtime::BIGINT_MODULE_NAME,
6541 "fromNumber",
6542 ))
6543 .ok_or_else(|| internal_failure("submilli:bigint.fromNumber is not imported"))?;
6544 emitter.instruction(Instruction::F64Const(Ieee64::from(v as f64)));
6545 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6546 Ok(())
6547}
6548
6549fn emit_regex_literal(
6561 emitter: &mut FunctionEmitter,
6562 ctx: &CodegenCtx,
6563 source: &str,
6564 flags: &str,
6565) -> Result<(), crate::compiler_error::CompilerFailure> {
6566 super::emit_const_string_by_text(emitter, ctx, source)?;
6567 super::emit_const_string_by_text(emitter, ctx, flags)?;
6568 let ctor_idx = ctx
6569 .symbols
6570 .func_idx(&crate::mangle::prelude("RegExpConstructor#new"))
6571 .ok_or_else(|| crate::codegen::internal_failure("RegExpConstructor#new is not imported"))?;
6572 emitter.instruction(Instruction::Call(ctor_idx));
6573 Ok(())
6574}
6575
6576fn emit_bigint_extract_to_stack(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
6582 ctx.latch(emit_bigint_extract_to_stack_checked(emitter, ctx));
6583}
6584
6585fn emit_bigint_extract_to_stack_checked(
6586 emitter: &mut FunctionEmitter,
6587 ctx: &CodegenCtx,
6588) -> Result<(), crate::compiler_error::CompilerFailure> {
6589 let bigint_idx = ctx
6590 .symbols
6591 .bigint_type_idx()
6592 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6593 let Some(scratch) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6596 wasm_encoder::RefType {
6597 nullable: false,
6598 heap_type: wasm_encoder::HeapType::Concrete(bigint_idx),
6599 },
6600 ))) else {
6601 return Ok(());
6602 };
6603 emitter.instruction(Instruction::LocalSet(scratch));
6604 emitter.instruction(Instruction::LocalGet(scratch));
6605 emitter.instruction(Instruction::StructGet {
6606 struct_type_index: bigint_idx,
6607 field_index: 1, });
6609 emitter.instruction(Instruction::LocalGet(scratch));
6610 emitter.instruction(Instruction::StructGet {
6611 struct_type_index: bigint_idx,
6612 field_index: 2, });
6614
6615 Ok(())
6616}
6617
6618fn emit_bigint_wrap_host_result(
6623 emitter: &mut FunctionEmitter,
6624 ctx: &CodegenCtx,
6625 host_func_idx: u32,
6626) {
6627 ctx.latch(emit_bigint_wrap_host_result_checked(
6628 emitter,
6629 ctx,
6630 host_func_idx,
6631 ));
6632}
6633
6634fn emit_bigint_wrap_host_result_checked(
6635 emitter: &mut FunctionEmitter,
6636 ctx: &CodegenCtx,
6637 host_func_idx: u32,
6638) -> Result<(), crate::compiler_error::CompilerFailure> {
6639 emitter.instruction(Instruction::Call(host_func_idx));
6640 let bigint_idx = ctx
6641 .symbols
6642 .bigint_type_idx()
6643 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
6644 let raw_bigint_idx = ctx
6645 .symbols
6646 .raw_bigint_type_idx()
6647 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6648 let vtable_global = ctx
6649 .symbols
6650 .prelude_global_idx("bigint_vtable")
6651 .ok_or_else(|| {
6652 crate::codegen::internal_failure("bigint_vtable imported from prelude bootstrap")
6653 })?;
6654 let Some(scratch_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6657 wasm_encoder::RefType {
6658 nullable: false,
6659 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6660 },
6661 ))) else {
6662 return Ok(());
6663 };
6664 let Some(scratch_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32))
6665 else {
6666 return Ok(());
6667 };
6668 emitter.instruction(Instruction::LocalSet(scratch_limbs));
6669 emitter.instruction(Instruction::LocalSet(scratch_sign));
6670 emitter.instruction(Instruction::GlobalGet(vtable_global));
6671 emitter.instruction(Instruction::LocalGet(scratch_sign));
6672 emitter.instruction(Instruction::LocalGet(scratch_limbs));
6673 emitter.instruction(Instruction::StructNew(bigint_idx));
6674
6675 Ok(())
6676}
6677
6678fn emit_bigint_binop_inline(
6682 emitter: &mut FunctionEmitter,
6683 ctx: &CodegenCtx,
6684 lhs: ExprId,
6685 rhs: ExprId,
6686 op_name: &str,
6687) -> Result<(), crate::compiler_error::CompilerFailure> {
6688 emit_expr(emitter, ctx, lhs)?;
6689 emit_bigint_extract_to_stack(emitter, ctx);
6690 let raw_bigint_idx = ctx
6691 .symbols
6692 .raw_bigint_type_idx()
6693 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6694 let lhs_limbs =
6695 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6696 nullable: false,
6697 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6698 }))?;
6699 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6700 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6701 emitter.instruction(Instruction::LocalSet(lhs_sign));
6702 emitter.instruction(Instruction::LocalGet(lhs_sign));
6703 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6704 emit_expr(emitter, ctx, rhs)?;
6705 emit_bigint_extract_to_stack(emitter, ctx);
6706 let host_idx = ctx
6707 .symbols
6708 .func_idx(&crate::mangle::host(
6709 crate::runtime::BIGINT_MODULE_NAME,
6710 op_name,
6711 ))
6712 .ok_or_else(|| {
6713 crate::codegen::internal_failure(format!(
6714 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6715 ))
6716 })?;
6717 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6718 Ok(())
6719}
6720
6721fn emit_bigint_pm_one(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: crate::PostfixOp) {
6728 ctx.latch(emit_bigint_pm_one_checked(emitter, ctx, op));
6729}
6730
6731fn emit_bigint_pm_one_checked(
6732 emitter: &mut FunctionEmitter,
6733 ctx: &CodegenCtx,
6734 op: crate::PostfixOp,
6735) -> Result<(), crate::compiler_error::CompilerFailure> {
6736 emit_bigint_extract_to_stack(emitter, ctx);
6737 let raw_bigint_idx = ctx
6738 .symbols
6739 .raw_bigint_type_idx()
6740 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6741 let Some(lhs_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
6742 wasm_encoder::RefType {
6743 nullable: false,
6744 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6745 },
6746 ))) else {
6747 return Ok(());
6748 };
6749 let Some(lhs_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32)) else {
6750 return Ok(());
6751 };
6752 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6753 emitter.instruction(Instruction::LocalSet(lhs_sign));
6754 emitter.instruction(Instruction::LocalGet(lhs_sign));
6755 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6756 if ctx.latch(emit_bigint_literal(emitter, ctx, "1")).is_none() {
6757 return Ok(());
6758 }
6759 emit_bigint_extract_to_stack(emitter, ctx);
6760 let op_name = match op {
6761 crate::PostfixOp::Inc => "add",
6762 crate::PostfixOp::Dec => "sub",
6763 crate::PostfixOp::NonNullAssert => {
6764 return Err(crate::codegen::internal_failure(
6765 "non-null assertion is not PostfixUnary",
6766 ));
6767 }
6768 };
6769 let host_idx = ctx
6770 .symbols
6771 .func_idx(&crate::mangle::host(
6772 crate::runtime::BIGINT_MODULE_NAME,
6773 op_name,
6774 ))
6775 .ok_or_else(|| {
6776 crate::codegen::internal_failure(format!(
6777 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
6778 ))
6779 })?;
6780 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6781
6782 Ok(())
6783}
6784
6785fn emit_bigint_cmp_inline(
6788 emitter: &mut FunctionEmitter,
6789 ctx: &CodegenCtx,
6790 lhs: ExprId,
6791 rhs: ExprId,
6792 op: BinOp,
6793) -> Result<(), crate::compiler_error::CompilerFailure> {
6794 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6795 emitter.instruction(Instruction::I32Const(0));
6796 let inst = match op {
6797 BinOp::Lt => Instruction::I32LtS,
6798 BinOp::Gt => Instruction::I32GtS,
6799 BinOp::Le => Instruction::I32LeS,
6800 BinOp::Ge => Instruction::I32GeS,
6801 _ => {
6802 return Err(crate::codegen::internal_failure(
6803 "emit_bigint_cmp_inline called with non-cmp op",
6804 ));
6805 }
6806 };
6807 emitter.instruction(inst);
6808 Ok(())
6809}
6810
6811fn emit_bigint_cmp_eq_inline(
6813 emitter: &mut FunctionEmitter,
6814 ctx: &CodegenCtx,
6815 lhs: ExprId,
6816 rhs: ExprId,
6817 op: BinOp,
6818) -> Result<(), crate::compiler_error::CompilerFailure> {
6819 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
6820 emitter.instruction(Instruction::I32Eqz);
6821 let _: () = if matches!(op, BinOp::NotEq) {
6822 emitter.instruction(Instruction::I32Eqz);
6823 };
6824 Ok(())
6825}
6826
6827fn emit_bigint_cmp_call(
6830 emitter: &mut FunctionEmitter,
6831 ctx: &CodegenCtx,
6832 lhs: ExprId,
6833 rhs: ExprId,
6834) -> Result<(), crate::compiler_error::CompilerFailure> {
6835 emit_expr(emitter, ctx, lhs)?;
6836 emit_bigint_extract_to_stack(emitter, ctx);
6837 let raw_bigint_idx = ctx
6838 .symbols
6839 .raw_bigint_type_idx()
6840 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
6841 let lhs_limbs =
6842 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
6843 nullable: false,
6844 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
6845 }))?;
6846 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
6847 emitter.instruction(Instruction::LocalSet(lhs_limbs));
6848 emitter.instruction(Instruction::LocalSet(lhs_sign));
6849 emitter.instruction(Instruction::LocalGet(lhs_sign));
6850 emitter.instruction(Instruction::LocalGet(lhs_limbs));
6851 emit_expr(emitter, ctx, rhs)?;
6852 emit_bigint_extract_to_stack(emitter, ctx);
6853 let cmp_idx = ctx
6854 .symbols
6855 .func_idx(&crate::mangle::host(
6856 crate::runtime::BIGINT_MODULE_NAME,
6857 "cmp",
6858 ))
6859 .ok_or_else(|| {
6860 crate::codegen::internal_failure(
6861 "submilli:bigint.cmp import recorded by codegen bootstrap",
6862 )
6863 })?;
6864 emitter.instruction(Instruction::Call(cmp_idx));
6865 Ok(())
6866}
6867
6868fn evaluate_object_members(
6870 emitter: &mut FunctionEmitter<'_>,
6871 ctx: &CodegenCtx<'_>,
6872 members: &[TypedObjectMember],
6873) -> Result<HashMap<ExprId, u32>, crate::compiler_error::CompilerFailure> {
6874 let intrinsics = ctx
6875 .symbols
6876 .intrinsic_type_indices()
6877 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
6878 let mut values = HashMap::new();
6879 for member in members {
6880 let TypedObjectMember::Value(value_id) = *member else {
6881 return Err(crate::codegen::internal_failure(
6882 "spread field reached plain object lowering",
6883 ));
6884 };
6885 let value_ty = &ctx
6886 .ta
6887 .try_expr(value_id)
6888 .map_err(crate::codegen::arena_failure)?
6889 .ty;
6890 emit_expr(emitter, ctx, value_id)?;
6891 cast::emit_box(emitter, ctx, value_ty)?;
6892 let local = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
6893 emitter.instruction(Instruction::LocalSet(local));
6894 values.insert(value_id, local);
6895 }
6896 Ok(values)
6897}
6898
6899fn emit_field_holds_value_of(
6902 emitter: &mut FunctionEmitter<'_>,
6903 ctx: &CodegenCtx<'_>,
6904 object: u32,
6905 index: u32,
6906 value: u32,
6907 field_ty: &Type,
6908) -> Result<(), crate::compiler_error::CompilerFailure> {
6909 let intrinsics = ctx
6910 .symbols
6911 .intrinsic_type_indices()
6912 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6913 emitter.instruction(Instruction::LocalGet(object));
6914 emitter.instruction(Instruction::StructGet {
6915 struct_type_index: intrinsics.object_shape,
6916 field_index: 1,
6917 });
6918 emitter.instruction(Instruction::LocalGet(index));
6919 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
6920 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
6921 emitter.instruction(Instruction::LocalGet(value));
6922 emit_is_nullish(emitter, ctx)?;
6923 emitter.instruction(Instruction::I32Eqz);
6924 emitter.instruction(Instruction::I32Or);
6925 let _: () = if field_runtime_type_is_testable(field_ty) {
6926 emit_structural_test(emitter, ctx, value, field_ty, field_ty)?;
6927 emitter.instruction(Instruction::I32And);
6928 };
6929 Ok(())
6930}
6931
6932fn object_ref(object_type: u32) -> ValType {
6933 ValType::Ref(RefType {
6934 nullable: true,
6935 heap_type: HeapType::Concrete(object_type),
6936 })
6937}
6938
6939pub(super) fn emit_index_number(
6941 emitter: &mut FunctionEmitter,
6942 ctx: &CodegenCtx,
6943 ty: &Type,
6944) -> Result<(), crate::compiler_error::CompilerFailure> {
6945 if ty == &Type::Unknown {
6946 let function = ctx
6947 .symbols
6948 .prelude_func_idx("__value_to_index")
6949 .ok_or_else(|| crate::codegen::internal_failure("dynamic index helper collected"))?;
6950 emitter.instruction(Instruction::Call(function));
6951 } else {
6952 cast::emit_coerce_to_slot(emitter, ctx, ty, &Type::Number)?;
6953 };
6954 Ok(())
6955}
6956
6957fn emit_computed_object(
6958 emitter: &mut FunctionEmitter,
6959 ctx: &CodegenCtx,
6960 members: &[TypedObjectMember],
6961) -> Result<(), crate::compiler_error::CompilerFailure> {
6962 let Some(intr) = ctx.require(ctx.symbols.intrinsic_type_indices(), "intrinsics declared")
6963 else {
6964 return Ok(());
6965 };
6966 let Some(merge) = ctx.require(
6967 ctx.symbols.prelude_func_idx("ObjectConstructor##spread"),
6968 "spread helper imported",
6969 ) else {
6970 return Ok(());
6971 };
6972 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
6973 nullable: false,
6974 heap_type: HeapType::Concrete(intr.object_shape),
6975 }))?;
6976 for _ in 0..4 {
6977 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
6978 }
6979 emitter.instruction(Instruction::Call(merge));
6980 emitter.instruction(Instruction::LocalSet(object));
6981 for member in members {
6982 emitter.instruction(Instruction::LocalGet(object));
6983 match member {
6984 TypedObjectMember::Computed { key, value } => {
6985 emit_expr(emitter, ctx, *key)?;
6986 crate::codegen::cast_check::emit_operation_cast_on_stack(
6987 emitter,
6988 ctx,
6989 &ctx.ta
6990 .try_expr(*key)
6991 .map_err(crate::codegen::arena_failure)?
6992 .ty,
6993 &Type::String,
6994 )?;
6995 emit_expr(emitter, ctx, *value)?;
6996 cast::emit_box(
6997 emitter,
6998 ctx,
6999 &ctx.ta
7000 .try_expr(*value)
7001 .map_err(crate::codegen::arena_failure)?
7002 .ty,
7003 )?;
7004 let Some(symbol) = ctx.require(
7005 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7006 "dynamic write imported",
7007 ) else {
7008 return Ok(());
7009 };
7010 emitter.instruction(Instruction::Call(symbol));
7011 }
7012 TypedObjectMember::Spread { source, .. } => {
7013 emit_expr(emitter, ctx, *source)?;
7014 for _ in 0..2 {
7015 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
7016 }
7017 emitter.instruction(Instruction::Call(merge));
7018 emitter.instruction(Instruction::LocalSet(object));
7019 }
7020 TypedObjectMember::Value(_) => {
7021 ctx.fail("computed literal contains an unlowered named field");
7022 return Ok(());
7023 }
7024 }
7025 }
7026 emitter.instruction(Instruction::LocalGet(object));
7027 Ok(())
7028}
7029
7030fn emit_object_index_postfix(
7031 emitter: &mut FunctionEmitter,
7032 ctx: &CodegenCtx,
7033 receiver: ExprId,
7034 index: ExprId,
7035 op: crate::PostfixOp,
7036 read_ty: &Type,
7037) -> Result<(), crate::compiler_error::CompilerFailure> {
7038 emit_expr(emitter, ctx, receiver)?;
7039 cast::emit_box(
7040 emitter,
7041 ctx,
7042 &ctx.ta
7043 .try_expr(receiver)
7044 .map_err(crate::codegen::arena_failure)?
7045 .ty,
7046 )?;
7047 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7048 emitter.instruction(Instruction::LocalSet(object));
7049 emit_expr(emitter, ctx, index)?;
7050 crate::codegen::cast_check::emit_operation_cast_on_stack(
7051 emitter,
7052 ctx,
7053 &ctx.ta
7054 .try_expr(index)
7055 .map_err(crate::codegen::arena_failure)?
7056 .ty,
7057 &Type::String,
7058 )?;
7059 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
7060 emitter.instruction(Instruction::LocalSet(key));
7061 emitter.instruction(Instruction::LocalGet(object));
7062 emitter.instruction(Instruction::LocalGet(key));
7063 let Some(symbol) = ctx.require(
7064 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
7065 "record read imported",
7066 ) else {
7067 return Ok(());
7068 };
7069 emitter.instruction(Instruction::Call(symbol));
7070 crate::codegen::cast_check::emit_operation_cast_on_stack(
7071 emitter,
7072 ctx,
7073 &Type::Unknown,
7074 read_ty,
7075 )?;
7076 cast::emit_box(emitter, ctx, read_ty)?;
7077 emit_postfix_numeric(emitter, ctx, &Type::Unknown);
7078 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7079 emitter.instruction(Instruction::LocalTee(old));
7080 emit_postfix_delta(emitter, ctx, op, &Type::Unknown);
7081 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7082 emitter.instruction(Instruction::LocalSet(updated));
7083 emitter.instruction(Instruction::LocalGet(object));
7084 emitter.instruction(Instruction::LocalGet(key));
7085 emitter.instruction(Instruction::LocalGet(updated));
7086 let Some(symbol) = ctx.require(
7087 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7088 "record write imported",
7089 ) else {
7090 return Ok(());
7091 };
7092 emitter.instruction(Instruction::Call(symbol));
7093 emitter.instruction(Instruction::LocalGet(old));
7094 Ok(())
7095}
7096
7097#[cfg(test)]
7098mod tests {
7099 use super::*;
7100 use crate::codegen::invariant_tests::{assert_internal, with_context};
7101 use crate::{FileId, Span, TypedAst, TypedExpr};
7102
7103 #[test]
7104 fn missing_method_dispatch_is_a_compiler_error() {
7105 with_context(
7106 &TypedAst::new(),
7107 &crate::codegen::SymbolTable::default(),
7108 |ctx| {
7109 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7110 let error = emit_method_call_with_receiver_on_stack(
7111 &mut emitter,
7112 ctx,
7113 &Type::Number,
7114 &crate::mangle::prelude("MissingInterface"),
7115 "missingMethod",
7116 &[],
7117 None,
7118 None,
7119 &Type::Number,
7120 )
7121 .unwrap_err();
7122 assert!(error.to_string().contains("MissingInterface.missingMethod"));
7123 assert_internal(error);
7124 },
7125 );
7126 }
7127
7128 #[test]
7129 fn call_and_chain_metadata_are_checked_before_operands() {
7130 with_context(
7131 &TypedAst::new(),
7132 &crate::codegen::SymbolTable::default(),
7133 |ctx| {
7134 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7135 assert_internal(
7136 emit_intrinsic_call(&mut emitter, ctx, Intrinsic::Assert, &[], &Type::Void)
7137 .unwrap_err(),
7138 );
7139 assert_internal(
7140 emit_intrinsic_call(
7141 &mut emitter,
7142 ctx,
7143 Intrinsic::BigIntFromString,
7144 &[],
7145 &Type::BigInt,
7146 )
7147 .unwrap_err(),
7148 );
7149 assert_internal(
7150 emit_direct_call(
7151 &mut emitter,
7152 ctx,
7153 false,
7154 0,
7155 &[Type::Number],
7156 &Type::Void,
7157 &[],
7158 )
7159 .unwrap_err(),
7160 );
7161 assert_internal(
7162 emit_args_into_slots(&mut emitter, ctx, &[], Some(&[ValType::F64]))
7163 .unwrap_err(),
7164 );
7165 assert_internal(
7166 emit_chain_parts(
7167 &mut emitter,
7168 ctx,
7169 &[],
7170 1,
7171 &Type::Number,
7172 &Type::Number,
7173 None,
7174 )
7175 .unwrap_err(),
7176 );
7177 assert_internal(
7178 emit_chain_parts(
7179 &mut emitter,
7180 ctx,
7181 &[],
7182 0,
7183 &Type::Number,
7184 &Type::Number,
7185 Some(&[]),
7186 )
7187 .unwrap_err(),
7188 );
7189 },
7190 );
7191 }
7192
7193 #[test]
7194 fn uninterned_literal_fails_and_a_fresh_emitter_still_works() {
7195 let mut ta = TypedAst::new();
7196 let string = ta
7197 .try_push_expr(TypedExpr {
7198 kind: TypedExprKind::String("missing".into()),
7199 ty: Type::String,
7200 span: Span::at(FileId(0)),
7201 })
7202 .unwrap();
7203 let number = ta
7204 .try_push_expr(TypedExpr {
7205 kind: TypedExprKind::Number(3.0),
7206 ty: Type::Number,
7207 span: Span::at(FileId(0)),
7208 })
7209 .unwrap();
7210 with_context(&ta, &crate::codegen::SymbolTable::default(), |ctx| {
7211 let mut failed = FunctionEmitter::new(ctx, &[]).unwrap();
7212 assert_internal(emit_expr(&mut failed, ctx, string).unwrap_err());
7213 drop(failed);
7214 let mut healthy = FunctionEmitter::new(ctx, &[]).unwrap();
7215 emit_expr(&mut healthy, ctx, number).unwrap();
7216 healthy.instruction(Instruction::Drop);
7217 healthy.build().unwrap();
7218 });
7219 }
7220}