1use crate::codegen::CodegenCtx;
12use crate::codegen::bounds::receiver_name;
13use crate::codegen::bounds::{
14 emit_checked_index, emit_checked_index_with_length, stash_array_length, stash_index_operand,
15};
16use crate::codegen::cast_check::emit_structural_test;
17use crate::codegen::function_emitter::FunctionEmitter;
18use crate::codegen::function_emitter::cast;
19use crate::codegen::symbol_table::{MethodSlotAbi, may_hold_null};
20use crate::runtime::prelude::index::IndexAccess;
21use crate::typechecker::infer::narrowing::{
22 BindingId, ReferencePath, cast_info_for, falsy_part, truthy_part,
23};
24use crate::typed_ast::field_runtime_type_is_testable;
25use crate::{
26 BinOp, ExprId, Ident, Intrinsic, Type, TypedExprKind, TypedObjectFieldSource,
27 TypedObjectMember, UnOp,
28};
29use std::collections::HashMap;
30use wasm_encoder::{BlockType, HeapType, Ieee64, Instruction, RefType, ValType};
31
32pub fn emit_expr(
37 emitter: &mut FunctionEmitter,
38 ctx: &CodegenCtx,
39 id: ExprId,
40) -> Result<(), crate::compiler_error::CompilerFailure> {
41 if let Some(class) = ctx.ta.inherited_static_receivers.get(&id) {
43 crate::codegen::init_guard::emit_check(emitter, ctx, class);
44 }
45 let kind = &ctx
46 .ta
47 .try_expr(id)
48 .map_err(crate::codegen::arena_failure)?
49 .kind;
50 let call_boundary = matches!(
51 kind,
52 TypedExprKind::Call { .. }
53 | TypedExprKind::CallClosure { .. }
54 | TypedExprKind::VirtualCall { .. }
55 | TypedExprKind::GenericCall { .. }
56 | TypedExprKind::MethodCall { .. }
57 | TypedExprKind::GenericMethodCall { .. }
58 | TypedExprKind::IntrinsicCall { .. }
59 | TypedExprKind::SuperCtorCall { .. }
60 | TypedExprKind::SuperMethodCall { .. }
61 | TypedExprKind::OptionalChain { .. }
62 );
63 let mark = call_boundary.then(|| emitter.single_evaluation_mark());
64 let cache_tuple_source =
65 ctx.ta.tuple_call_spread_sources.contains(&id) && emitter.evaluated_slot(id).is_none();
66 emit_expr_value(emitter, ctx, id)?;
67 if let Some(mark) = mark {
68 emitter.end_single_evaluations(mark)?;
69 }
70 if cache_tuple_source {
71 let ty = &ctx
72 .ta
73 .try_expr(id)
74 .map_err(crate::codegen::arena_failure)?
75 .ty;
76 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(ty)?)?;
77 emitter.instruction(Instruction::LocalTee(slot));
78 emitter.record_single_evaluation(id, slot)?;
79 }
80 crate::codegen::field_guards::attach(
83 emitter,
84 ctx,
85 &ctx.ta
86 .try_expr(id)
87 .map_err(crate::codegen::arena_failure)?
88 .ty,
89 )?;
90 Ok(())
91}
92
93fn emit_receiver(
94 emitter: &mut FunctionEmitter,
95 ctx: &CodegenCtx,
96 id: ExprId,
97) -> Result<(), crate::compiler_error::CompilerFailure> {
98 emit_expr(emitter, ctx, id)?;
99 let source = &ctx
100 .ta
101 .try_expr(id)
102 .map_err(crate::codegen::arena_failure)?
103 .ty;
104 let target = ctx
105 .ta
106 .source_type(id)
107 .map_err(crate::codegen::arena_failure)?;
108 let _: () = if source != target {
109 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, source, target)?;
110 };
111 Ok(())
112}
113
114fn emit_expr_value(
115 emitter: &mut FunctionEmitter,
116 ctx: &CodegenCtx,
117 id: ExprId,
118) -> Result<(), crate::compiler_error::CompilerFailure> {
119 let expr = ctx.ta.try_expr(id).map_err(crate::codegen::arena_failure)?;
120 emitter.record_span(expr.span);
121 if let Some(slot) = emitter.evaluated_slot(id) {
124 emitter.instruction(Instruction::LocalGet(slot));
125 return Ok(());
126 }
127 let _: () = match &expr.kind {
128 TypedExprKind::Number(v) => {
129 emitter.instruction(Instruction::F64Const(Ieee64::from(*v)));
130 }
131 TypedExprKind::BigInt(digits) => emit_bigint_literal(emitter, ctx, digits)?,
132 TypedExprKind::Boolean(b) => {
133 emitter.instruction(Instruction::I32Const(if *b { 1 } else { 0 }));
134 }
135 TypedExprKind::String(_) => {
139 let pool_idx = ctx.strings.locations.get(&id).copied().ok_or_else(|| {
140 crate::codegen::internal_failure("a string literal was not interned")
141 })?;
142 super::emit_pooled_string(emitter, ctx, pool_idx)?;
143 }
144 TypedExprKind::Regex { source, flags } => {
145 emit_regex_literal(emitter, ctx, source, flags)?;
146 }
147 TypedExprKind::Sequence { stmts, result } => {
148 for &stmt in stmts {
149 super::stmt::emit_statement(emitter, ctx, stmt)?;
150 }
151 emit_expr(emitter, ctx, *result)?;
152 }
153 TypedExprKind::EffectThen { effect, result } => {
154 emit_expr(emitter, ctx, *effect)?;
155 emitter.instruction(Instruction::Drop);
156 emit_expr(emitter, ctx, *result)?;
157 }
158 TypedExprKind::Binary { op, lhs, rhs } => {
159 emit_binary(emitter, ctx, *op, *lhs, *rhs, &expr.ty)?;
160 }
161 TypedExprKind::Unary { op, operand } => {
162 emit_unary(emitter, ctx, *op, *operand, &expr.ty)?;
163 }
164 TypedExprKind::LocalRef { ident, boxed } => {
165 if ctx.ta.parameter_inputs.contains(&id) {
166 let (slot, source) = emitter
167 .parameter_input_slots
168 .get(&ident.name)
169 .cloned()
170 .ok_or_else(|| {
171 crate::codegen::internal_failure("parameter input slot is missing")
172 })?;
173 emitter.instruction(Instruction::LocalGet(slot));
174 cast::emit_coerce_to_slot(emitter, ctx, &source, &expr.ty)?;
175 return Ok(());
176 }
177 let slot = emitter.require_local_slot(&ident.name)?;
185 let maybe_missing = emitter.maybe_missing_slots.get(&slot).cloned();
188 let stored_ty = maybe_missing
189 .as_ref()
190 .map_or(&expr.ty, |local| &local.storage);
191 if *boxed {
192 let box_idx = ctx.symbols.box_type_idx(stored_ty)?.ok_or_else(|| {
193 crate::codegen::internal_failure("box type registered for every boxed LocalRef")
194 })?;
195 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
196 cast::emit_unerase(emitter, ctx, stored_ty)?;
197 } else {
198 emitter.instruction(Instruction::LocalGet(slot));
199 }
200 if let Some(local) = maybe_missing.as_ref() {
201 let checked = ctx.ta.widened_missing_reads.contains_key(&id)
211 || (local.present_flag.is_none()
212 && ctx
213 .ta
214 .source_type(id)
215 .map_err(crate::codegen::arena_failure)?
216 .holds_undefined());
217 if checked {
218 cast::emit_coerce_to_slot(emitter, ctx, &local.storage, &expr.ty)?;
219 } else {
220 let stack = ctx.symbols.value_type(stored_ty)?;
221 emit_missing_local_check(emitter, ctx, slot, stack)?;
222 cast::emit_cast_to(emitter, ctx, &expr.ty)?;
223 }
224 }
225 }
226 TypedExprKind::LocalNarrowRef { .. } if inferred_never(ctx, id)? => {
229 emitter.instruction(Instruction::Unreachable);
230 }
231 TypedExprKind::LocalNarrowRef { binding, path, .. } => {
232 emit_local_narrow_ref(emitter, ctx, binding, path, &expr.ty)?;
233 }
234 TypedExprKind::GlobalRef { mangled, .. } => {
235 emit_global_ref(emitter, ctx, mangled, &expr.ty)?;
236 }
237 TypedExprKind::FunctionRef { mangled, .. } => {
238 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
239 emit_function_ref(emitter, ctx, mangled, &expr.ty)?;
240 }
241 TypedExprKind::Call { mangled, args, .. } => {
242 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
243 emit_symbol_call(emitter, ctx, mangled, args, &expr.ty)?;
244 }
245 TypedExprKind::SuperCtorCall { parent, args } => {
246 let this = emitter.this_local().ok_or_else(|| {
250 crate::codegen::internal_failure("super(...) only inside a constructor body")
251 })?;
252 emitter.instruction(Instruction::LocalGet(this));
253 let ctor_abi = ctx.symbols.class_ctor_abi(parent).map(<[ValType]>::to_vec);
254 let init = ctx
255 .symbols
256 .class_ctor_init_func_idx(parent)
257 .ok_or_else(|| crate::codegen::internal_failure("parent ctor init fn allocated"))?;
258 let defaults = ctx.symbols.host_call_defaults(init, args.len()).to_vec();
260 emit_args_into_slots_with_defaults(emitter, ctx, args, ctor_abi.as_deref(), &defaults)?;
261 emitter.instruction(Instruction::Call(init));
262 if let Some(mangled) = emitter.ctor_class().cloned() {
265 emit_class_field_setup(emitter, ctx, &mangled)?;
266 }
267 emit_undefined(emitter, ctx)?;
268 }
269 TypedExprKind::SuperMethodCall { owner, name, args } => {
270 let this = emitter.this_local().ok_or_else(|| {
275 crate::codegen::internal_failure("super.method() only inside a method body")
276 })?;
277 emitter.instruction(Instruction::LocalGet(this));
278 let abi = ctx.symbols.class_method_abi(owner, &name.name).cloned();
279 emit_args_into_slots(
280 emitter,
281 ctx,
282 args,
283 abi.as_ref().map(|a| a.params.as_slice()),
284 )?;
285 let func = ctx
286 .symbols
287 .class_method_func_idx(owner, &name.name)
288 .ok_or_else(|| {
289 crate::codegen::internal_failure("parent method body fn allocated")
290 })?;
291 emitter.instruction(Instruction::Call(func));
292 emit_slot_return_cast(emitter, ctx, &expr.ty.clone(), abi.as_ref())?;
293 }
294 TypedExprKind::VirtualCall { target, tool, args } => {
295 super::virtual_call::emit_virtual_call(emitter, ctx, target, tool, args)?;
299 }
300 TypedExprKind::CallClosure { callee, args } => {
301 emit_indirect_closure_call(emitter, ctx, *callee, args, &expr.ty)?;
307 }
308 TypedExprKind::GenericCall {
309 mangled,
310 type_args,
311 args,
312 return_cast,
313 type_predicate: _,
314 } => {
315 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
316 emit_generic_call(
317 emitter,
318 ctx,
319 &expr.ty,
320 mangled,
321 type_args,
322 args,
323 return_cast,
324 )?;
325 }
326 TypedExprKind::IntrinsicCall { kind, args } => {
327 let source_ty = ctx
328 .ta
329 .source_type(id)
330 .map_err(crate::codegen::arena_failure)?;
331 emit_intrinsic_call(emitter, ctx, *kind, args, &expr.ty, source_ty)?;
332 }
333 TypedExprKind::MethodCall {
334 receiver,
335 iface,
336 name,
337 args,
338 type_predicate: _,
339 } => {
340 emit_method_call(
341 emitter,
342 ctx,
343 *receiver,
344 iface,
345 &name.name,
346 args,
347 None,
348 &[],
349 None,
350 &expr.ty,
351 )?;
352 }
353 TypedExprKind::GenericMethodCall {
354 receiver,
355 iface,
356 name,
357 type_args,
358 args,
359 return_cast,
360 type_predicate: _,
361 } => {
362 let plain_args: Vec<ExprId> = args.iter().map(|a| a.expr).collect();
363 emit_method_call(
364 emitter,
365 ctx,
366 *receiver,
367 iface,
368 &name.name,
369 &plain_args,
370 Some(args),
371 type_args,
372 return_cast.as_ref(),
373 &expr.ty,
374 )?;
375 }
376 TypedExprKind::ObjectLiteral { members, fields } => {
377 emit_object_literal(emitter, ctx, &expr.ty, members, fields)?;
378 }
379 TypedExprKind::FieldAccess { receiver, name } => {
380 emit_field_access(emitter, ctx, id, &expr.ty, receiver, name)?;
381 }
382 TypedExprKind::InterfacePropertyAccess {
383 receiver,
384 iface,
385 name,
386 } => emit_interface_property(emitter, ctx, receiver, iface, name)?,
387 TypedExprKind::ArrayLiteral { elements, .. } => {
388 emit_array_literal(emitter, ctx, elements)?;
389 }
390 TypedExprKind::TupleLiteral { elements, .. } => {
391 emit_tuple_literal(emitter, ctx, elements)?;
392 }
393 TypedExprKind::IndexAccess { receiver, index } => {
394 emit_index_access(emitter, ctx, id, receiver, index, &expr.ty)?;
395 }
396 TypedExprKind::Undefined => emit_undefined(emitter, ctx)?,
397 TypedExprKind::Null => {
398 let _ = ctx; emitter.instruction(Instruction::RefNull(HeapType::Abstract {
410 shared: false,
411 ty: wasm_encoder::AbstractHeapType::None,
412 }));
413 }
414 TypedExprKind::This => {
415 let slot = emitter.this_local().ok_or_else(|| {
416 crate::codegen::internal_failure("`this` has a local receiver or captured binding")
417 })?;
418 if emitter.dynamic_this {
419 emitter.instruction(Instruction::LocalGet(slot));
420 emitter.instruction(Instruction::RefIsNull);
421 emitter.emit_if(BlockType::Empty);
422 crate::codegen::throw::emit_type_error_throw(
423 emitter,
424 ctx,
425 "Unbound function has no this receiver",
426 );
427 emitter.emit_end();
428 }
429 emitter.instruction(Instruction::LocalGet(slot));
430 if emitter.dynamic_this {
431 crate::codegen::cast_check::emit_operation_cast_on_stack(
432 emitter,
433 ctx,
434 &Type::Unknown,
435 &expr.ty,
436 )?;
437 }
438 }
439 TypedExprKind::NumberEnumMember { value, .. } => {
440 let boxed_idx = ctx
447 .symbols
448 .boxed_number_type_idx()
449 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
450 let vtable_global = ctx
451 .symbols
452 .prelude_global_idx("boxed_number_vtable")
453 .ok_or_else(|| {
454 crate::codegen::internal_failure("boxed_number_vtable imported from prelude")
455 })?;
456 emitter.instruction(Instruction::GlobalGet(vtable_global));
457 emitter.instruction(Instruction::F64Const(Ieee64::from(*value)));
458 emitter.instruction(Instruction::StructNew(boxed_idx));
459 }
460 TypedExprKind::StringEnumMember { .. } => {
461 let pool_idx = ctx.strings.locations.get(&id).copied().ok_or_else(|| {
466 crate::codegen::internal_failure("a string enum value was not interned")
467 })?;
468 super::emit_pooled_string(emitter, ctx, pool_idx)?;
469 }
470 TypedExprKind::TypeofTag { value, tag } => {
471 emit_typeof_tag(emitter, ctx, *value, *tag)?;
472 }
473 TypedExprKind::InstanceOf { value, class } => match class.lowered() {
474 Type::ClassRef { mangled, .. } => {
475 let vtable_global =
476 ctx.symbols
477 .class_vtable_global_idx(mangled)
478 .ok_or_else(|| {
479 crate::codegen::internal_failure("class vtable global recorded")
480 })?;
481 emit_expr(emitter, ctx, *value)?;
482 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
483 cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
484 }
485 Type::Uint8Array => {
489 let idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
490 crate::codegen::internal_failure("$Uint8Array intrinsic registered")
491 })?;
492 emit_expr(emitter, ctx, *value)?;
493 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
494 }
495 _ => {
496 return Err(crate::codegen::internal_failure(format!(
497 "instanceof codegen with a non-class RHS: {class:?}"
498 )));
499 }
500 },
501 TypedExprKind::Narrowed {
502 path,
503 source,
504 binding,
505 cast_info,
506 inner,
507 } => {
508 emitter.push_scope();
512 if !path.chain.is_empty() {
513 emitter.register_narrow_source(&binding.name, *source)?;
514 } else if !reads_maybe_missing_local(emitter, ctx, *source)? {
515 let shadow_val = ctx.symbols.value_type(&cast_info.to_ty)?;
516 let shadow = emitter.define_local(binding, shadow_val)?;
517 emit_expr(emitter, ctx, *source)?;
518 crate::codegen::function_emitter::cast::emit_narrowing_cast(
519 emitter, ctx, cast_info,
520 )?;
521 emitter.instruction(Instruction::LocalSet(shadow));
522 }
523 emit_expr(emitter, ctx, *inner)?;
524 emitter.pop_scope()?;
525 }
526 TypedExprKind::Closure {
527 params,
528 captured,
529 runtime_generics,
530 ..
531 } => {
532 emit_closure_value(
533 emitter,
534 ctx,
535 id,
536 &expr.ty,
537 params,
538 captured,
539 runtime_generics,
540 )?;
541 }
542 TypedExprKind::Ternary { cond, then_, else_ } => {
547 let result_ty = expr.ty.clone();
548 let block_ty = conditional_block_type(ctx, &result_ty)?;
549 let cond_ty = ctx
550 .ta
551 .try_expr(*cond)
552 .map_err(crate::codegen::arena_failure)?
553 .ty
554 .clone();
555 emit_expr(emitter, ctx, *cond)?;
556 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
557 emitter.emit_if(block_ty);
558 emit_conditional_operand(emitter, ctx, *then_, &result_ty)?;
559 emitter.emit_else();
560 emit_conditional_operand(emitter, ctx, *else_, &result_ty)?;
561 emitter.emit_end();
562 }
563 TypedExprKind::NullishCoalesce { lhs, rhs } => {
566 emit_nullish_coalesce(emitter, ctx, lhs, rhs, &expr.ty)?;
567 }
568 TypedExprKind::OptionalChain { base, parts } => {
576 let present_flag = take_present_flag(emitter, id);
577 if let Some(flag) = present_flag {
578 set_present_flag(emitter, flag, true);
579 }
580 let result_ty = expr.ty.clone();
581 let base_ty = ctx
582 .ta
583 .try_expr(*base)
584 .map_err(crate::codegen::arena_failure)?
585 .ty
586 .clone();
587 let receiver = if matches!(parts.first(), Some(crate::TypedChainPart::Call { .. })) {
588 emit_callee(emitter, ctx, *base)?
589 } else {
590 emit_expr(emitter, ctx, *base)?;
591 None
592 };
593 emitter.call_receiver = receiver;
594 emit_chain_parts(
595 emitter,
596 ctx,
597 *base,
598 parts,
599 0,
600 &base_ty,
601 &result_ty,
602 ctx.ta.runtime_chain_types.get(&id).map(Vec::as_slice),
603 present_flag,
604 )?;
605 }
606 TypedExprKind::PostfixUnary { op, target } => {
607 if let crate::PostfixTarget::Index {
608 receiver, index, ..
609 } = target
610 && ctx
611 .ta
612 .source_type(*receiver)
613 .map_err(crate::codegen::arena_failure)?
614 .indexes_by_name()
615 {
616 emit_object_index_postfix(
617 emitter,
618 ctx,
619 *receiver,
620 *index,
621 *op,
622 ctx.ta
623 .source_type(id)
624 .map_err(crate::codegen::arena_failure)?,
625 )?;
626 cast::emit_coerce_to_slot(emitter, ctx, &Type::Unknown, &expr.ty)?;
627 } else {
628 emit_postfix_unary(emitter, ctx, *op, target, &expr.ty)?;
629 }
630 }
631 TypedExprKind::NonNullAssert { value } => {
632 crate::codegen::cast_check::emit_non_null_assert(emitter, ctx, *value, &expr.ty)?;
633 }
634 TypedExprKind::Cast {
635 value,
636 target_ty,
637 check,
638 } => {
639 crate::codegen::cast_check::emit_cast(
640 emitter,
641 ctx,
642 *value,
643 target_ty,
644 check.as_deref(),
645 expr.span,
646 )?;
647 }
648 };
649 if matches!(expr.ty, Type::Never) && !completes_when_never(&expr.kind) {
654 emitter.instruction(Instruction::Unreachable);
655 }
656 Ok(())
657}
658
659fn completes_when_never(kind: &TypedExprKind) -> bool {
665 matches!(
666 kind,
667 TypedExprKind::LocalNarrowRef { .. }
668 | TypedExprKind::FieldAccess { .. }
669 | TypedExprKind::IndexAccess { .. }
670 )
671}
672
673pub(crate) fn emit_undefined(
675 emitter: &mut FunctionEmitter<'_>,
676 ctx: &CodegenCtx<'_>,
677) -> Result<(), crate::compiler_error::CompilerFailure> {
678 let global = ctx
679 .symbols
680 .prelude_global_idx(crate::runtime::prelude::undefined::GLOBAL_NAME)
681 .ok_or_else(|| crate::codegen::internal_failure("undefined global was not imported"))?;
682 emitter.instruction(Instruction::GlobalGet(global));
683 Ok(())
684}
685
686pub(crate) fn emit_is_nullish(
688 emitter: &mut FunctionEmitter<'_>,
689 ctx: &CodegenCtx<'_>,
690) -> Result<(), crate::compiler_error::CompilerFailure> {
691 let intrinsic = ctx
692 .symbols
693 .intrinsic_type_indices()
694 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
695 let value = emitter.add_anonymous_local(ValType::Ref(RefType {
696 nullable: true,
697 heap_type: HeapType::Concrete(intrinsic.object),
698 }))?;
699 emitter.instruction(Instruction::LocalSet(value));
700 emitter.instructions(crate::codegen::nullish::is_nullish(
701 &[Instruction::LocalGet(value)],
702 intrinsic,
703 ));
704 Ok(())
705}
706
707pub(crate) fn emit_is_undefined(
708 emitter: &mut FunctionEmitter<'_>,
709 ctx: &CodegenCtx<'_>,
710) -> Result<(), crate::compiler_error::CompilerFailure> {
711 let intrinsic = ctx
712 .symbols
713 .intrinsic_type_indices()
714 .ok_or_else(|| crate::codegen::internal_failure("undefined intrinsic was not declared"))?;
715 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
716 intrinsic.undefined,
717 )));
718 Ok(())
719}
720
721fn emit_global_ref(
722 emitter: &mut FunctionEmitter,
723 ctx: &CodegenCtx,
724 mangled: &crate::MangledName,
725 result_ty: &Type,
726) -> Result<(), crate::compiler_error::CompilerFailure> {
727 if let Some(iface) = static_interface(ctx, result_ty) {
736 emit_static_value(emitter, ctx, iface)?;
737 if let ValType::Ref(RefType {
738 nullable: false, ..
739 }) = ctx.symbols.value_type(result_ty)?
740 {
741 emitter.instruction(Instruction::RefAsNonNull);
742 }
743 return Ok(());
744 }
745 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
746 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
747 crate::codegen::internal_failure("top-level let/const recorded during codegen")
748 })?;
749 emitter.instruction(Instruction::GlobalGet(idx));
750 if let ValType::Ref(RefType {
751 nullable: false, ..
752 }) = ctx.symbols.value_type(result_ty)?
753 {
754 emitter.instruction(Instruction::RefAsNonNull);
755 }
756
757 Ok(())
758}
759
760fn static_interface<'a>(ctx: &CodegenCtx, ty: &'a Type) -> Option<&'a crate::MangledName> {
762 match ty.lowered() {
763 Type::InterfaceRef { mangled, .. }
764 if ctx.symbols.iface_dispatch(mangled) == Some(crate::Dispatch::Static) =>
765 {
766 Some(mangled)
767 }
768 _ => None,
769 }
770}
771
772fn emit_static_value(
773 emitter: &mut FunctionEmitter,
774 ctx: &CodegenCtx,
775 iface: &crate::MangledName,
776) -> Result<(), crate::compiler_error::CompilerFailure> {
777 let tag = ctx.symbols.static_value_tag(iface).ok_or_else(|| {
778 crate::codegen::internal_failure("a static binding read as a value has a recorded tag")
779 })?;
780 super::emit_inline_string_literal(emitter, ctx, tag)?;
781 let helper = ctx
782 .symbols
783 .prelude_func_idx("ObjectConstructor##staticValue")
784 .ok_or_else(|| crate::codegen::internal_failure("the static value helper is imported"))?;
785 emitter.instruction(Instruction::Call(helper));
786 Ok(())
787}
788
789fn emit_function_ref(
790 emitter: &mut FunctionEmitter,
791 ctx: &CodegenCtx,
792 mangled: &crate::MangledName,
793 result_ty: &Type,
794) -> Result<(), crate::compiler_error::CompilerFailure> {
795 let closure_struct_idx = ctx
806 .symbols
807 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
808 .ok_or_else(|| {
809 crate::codegen::internal_failure(
810 "closure struct type registered for every function-as-value",
811 )
812 })?;
813 let adapter_idx = ctx.symbols.adapter_func_idx(mangled).ok_or_else(|| {
814 crate::codegen::internal_failure("adapter func recorded for every function-as-value")
815 })?;
816 let closure_global_idx = ctx
817 .symbols
818 .adapter_closure_global_idx(mangled)
819 .ok_or_else(|| {
820 crate::codegen::internal_failure("closure global recorded for every function-as-value")
821 })?;
822 let vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
823 crate::codegen::internal_failure(
824 "closure vtable global emitted whenever closures or adapters exist",
825 )
826 })?;
827 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
828 emitter.instruction(Instruction::RefIsNull);
829 emitter.emit_if(BlockType::Empty);
830 emitter.instruction(Instruction::GlobalGet(vtable_idx));
831 emitter.instruction(Instruction::RefFunc(adapter_idx));
832 emitter.instruction(Instruction::GlobalGet(vtable_idx));
833 if let Some(metadata) = ctx.symbols.function_argument_metadata.get(mangled) {
834 crate::codegen::call_arguments::wrap(emitter, ctx, metadata)?;
835 }
836 emitter.instruction(Instruction::I64Const(0));
837 emitter.instruction(Instruction::StructNew(closure_struct_idx));
838 emitter.instruction(Instruction::GlobalSet(closure_global_idx));
839 emitter.emit_end();
840 emitter.instruction(Instruction::GlobalGet(closure_global_idx));
841 if ctx.symbols.is_shared_closure_global(mangled) {
842 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
843 closure_struct_idx,
844 )));
845 } else {
846 emitter.instruction(Instruction::RefAsNonNull);
847 }
848
849 Ok(())
850}
851
852fn emit_symbol_call(
853 emitter: &mut FunctionEmitter,
854 ctx: &CodegenCtx,
855 mangled: &crate::MangledName,
856 args: &[ExprId],
857 result_ty: &Type,
858) -> Result<(), crate::compiler_error::CompilerFailure> {
859 let target = ctx.symbols.top_level_fn(mangled).ok_or_else(|| {
872 crate::codegen::internal_failure(format!(
873 "Call references unknown top-level fn `{}`",
874 mangled.as_str()
875 ))
876 })?;
877 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
878 let Type::ClassRef { mangled: class, .. } = result_ty.lowered() else {
879 return Err(crate::codegen::internal_failure("constructor class"));
880 };
881 emit_args_into_slots(emitter, ctx, args, ctx.symbols.class_ctor_abi(class))?;
882 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
883 emitter.instruction(Instruction::Call(target.wasm_idx));
884 } else {
885 emit_direct_call(
886 emitter,
887 ctx,
888 target.is_host,
889 target.wasm_idx,
890 &target.params,
891 &target.ret,
892 args,
893 )?;
894 }
895 if result_ty.is_void() {
896 cast::emit_box(emitter, ctx, &target.ret)?;
897 }
898
899 Ok(())
900}
901
902fn emit_interface_property(
903 emitter: &mut FunctionEmitter,
904 ctx: &CodegenCtx,
905 receiver: &ExprId,
906 iface: &crate::MangledName,
907 name: &Ident,
908) -> Result<(), crate::compiler_error::CompilerFailure> {
909 let key = crate::mangle::extend(iface, &name.name);
915 if ctx.symbols.iface_dispatch(iface) == Some(crate::Dispatch::Static) {
918 let global_idx = ctx.symbols.global_idx(&key).ok_or_else(|| {
919 crate::codegen::internal_failure(
920 "static interface property recorded as a global import",
921 )
922 })?;
923 emitter.instruction(Instruction::GlobalGet(global_idx));
924 return Ok(());
925 }
926 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
927 emit_receiver(emitter, ctx, *receiver)?;
928 emit_inline_length(emitter, ctx, struct_idx);
929 return Ok(());
930 }
931 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
932 crate::codegen::internal_failure(
933 "interface property getter recorded during the dependency-import pass",
934 )
935 })?;
936 emit_receiver(emitter, ctx, *receiver)?;
937 if matches!(
950 ctx.ta
951 .source_type(*receiver)
952 .map_err(crate::codegen::arena_failure)?
953 .lowered(),
954 Type::InterfaceRef { .. }
955 ) {
956 emitter.instruction(Instruction::RefAsNonNull);
957 }
958 emitter.instruction(Instruction::Call(func_idx));
959
960 Ok(())
961}
962
963fn emit_index_access(
964 emitter: &mut FunctionEmitter,
965 ctx: &CodegenCtx,
966 id: ExprId,
967 receiver: &ExprId,
968 index: &ExprId,
969 result_ty: &Type,
970) -> Result<(), crate::compiler_error::CompilerFailure> {
971 let mut present_flag = take_present_flag(emitter, id);
981 let missing_reads_undefined = ctx.ta.missing_reads_undefined.contains(&id);
982 if missing_reads_undefined && let Some(flag) = present_flag.take() {
985 set_present_flag(emitter, flag, true);
986 }
987 let recv_ty = ctx
988 .ta
989 .source_type(*receiver)
990 .map_err(crate::codegen::arena_failure)?
991 .clone();
992 let source_ty = ctx
998 .ta
999 .source_type(id)
1000 .map_err(crate::codegen::arena_failure)?;
1001 let checked_ty = if missing_reads_undefined {
1002 Type::union(vec![source_ty.clone(), Type::Undefined])
1003 } else {
1004 source_ty.clone()
1005 };
1006 let missing_gives_undefined = checked_ty.holds_undefined();
1007 if recv_ty.indexes_by_name() {
1008 emit_expr(emitter, ctx, *receiver)?;
1009 cast::emit_box(
1010 emitter,
1011 ctx,
1012 &ctx.ta
1013 .try_expr(*receiver)
1014 .map_err(crate::codegen::arena_failure)?
1015 .ty,
1016 )?;
1017 let probe = stash_probed_record(emitter, ctx, &checked_ty, present_flag)?;
1018 emit_expr(emitter, ctx, *index)?;
1019 crate::codegen::cast_check::emit_operation_cast_on_stack(
1020 emitter,
1021 ctx,
1022 &ctx.ta
1023 .try_expr(*index)
1024 .map_err(crate::codegen::arena_failure)?
1025 .ty,
1026 &Type::String,
1027 )?;
1028 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
1029 emitter.instruction(Instruction::LocalTee(key));
1030 let Some(symbol) = ctx.require(
1031 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
1032 "dynamic read imported",
1033 ) else {
1034 return Ok(());
1035 };
1036 emitter.instruction(Instruction::Call(symbol));
1037 emit_checked_record_read(
1038 emitter,
1039 ctx,
1040 &checked_ty,
1041 KeyOperand::Local(key),
1042 probe,
1043 &receiver_name(ctx.ta, *receiver)?,
1044 )?;
1045 cast::emit_coerce_to_slot(emitter, ctx, &checked_ty, result_ty)?;
1046 } else if recv_ty.lowered() == &Type::Uint8Array {
1047 emit_expr(emitter, ctx, *receiver)?;
1048 emit_uint8_index_with_receiver_on_stack(
1049 emitter,
1050 ctx,
1051 *index,
1052 missing_gives_undefined.then_some(result_ty),
1053 &receiver_name(ctx.ta, *receiver)?,
1054 )?;
1055 if let Some(flag) = present_flag {
1056 set_present_flag_from_value(emitter, ctx, flag, result_ty)?;
1057 }
1058 } else if recv_ty.is_string_shaped() {
1059 emit_expr(emitter, ctx, *receiver)?;
1060 emit_string_index_with_receiver_on_stack(
1061 emitter,
1062 ctx,
1063 *index,
1064 missing_gives_undefined.then_some(result_ty),
1065 &receiver_name(ctx.ta, *receiver)?,
1066 )?;
1067 if let Some(flag) = present_flag {
1068 set_present_flag_from_value(emitter, ctx, flag, result_ty)?;
1069 }
1070 } else {
1071 emit_expr(emitter, ctx, *receiver)?;
1072 emit_bounds_checked_index_with_receiver_on_stack(
1073 emitter,
1074 ctx,
1075 *index,
1076 result_ty,
1077 MissingElement {
1078 gives_undefined: ctx.ta.call_spread_index_reads.contains(&id)
1079 || missing_gives_undefined
1080 || is_optional_tuple_position(ctx, &recv_ty, *index)?,
1081 throws_on_absent_group: recv_ty.is_regexp_match_array() && !missing_gives_undefined,
1082 present_flag,
1083 },
1084 &receiver_name(ctx.ta, *receiver)?,
1085 )?;
1086 }
1087
1088 Ok(())
1089}
1090
1091fn emit_string_index_with_receiver_on_stack(
1096 emitter: &mut FunctionEmitter,
1097 ctx: &CodegenCtx,
1098 index: ExprId,
1099 undefined_into: Option<&Type>,
1100 receiver_name: &str,
1101) -> Result<(), crate::compiler_error::CompilerFailure> {
1102 if let Some(result_ty) = undefined_into {
1103 return emit_string_index_or_undefined(emitter, ctx, index, result_ty, receiver_name);
1104 }
1105 let Some(helper) = ctx.require(
1106 ctx.symbols.prelude_func_idx("string_index"),
1107 "string index helper imported",
1108 ) else {
1109 return Ok(());
1110 };
1111 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
1114 emitter.instruction(Instruction::LocalSet(receiver));
1115 let label = ctx
1119 .symbols
1120 .field_name_string_global_idx(receiver_name)
1121 .ok_or_else(|| crate::codegen::internal_failure("string index label global collected"))?;
1122 emitter.instruction(Instruction::GlobalGet(label));
1123 emit_expr(emitter, ctx, index)?;
1124 emit_index_number(
1125 emitter,
1126 ctx,
1127 &ctx.ta
1128 .try_expr(index)
1129 .map_err(crate::codegen::arena_failure)?
1130 .ty,
1131 )?;
1132 emitter.instruction(Instruction::LocalGet(receiver));
1133 crate::codegen::cast_check::emit_operation_cast_on_stack(
1134 emitter,
1135 ctx,
1136 &Type::Unknown,
1137 &Type::String,
1138 )?;
1139 emitter.instruction(Instruction::Call(helper));
1140 Ok(())
1141}
1142
1143fn emit_string_index_or_undefined(
1146 emitter: &mut FunctionEmitter,
1147 ctx: &CodegenCtx,
1148 index: ExprId,
1149 result_ty: &Type,
1150 receiver_name: &str,
1151) -> Result<(), crate::compiler_error::CompilerFailure> {
1152 let Some(helper) = ctx.require(
1153 ctx.symbols.prelude_func_idx("string_index"),
1154 "string index helper imported",
1155 ) else {
1156 return Ok(());
1157 };
1158 let string_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
1159 crate::codegen::internal_failure("the $string intrinsic is not registered")
1160 })?;
1161 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
1162 emitter.instruction(Instruction::LocalSet(receiver));
1163 emit_expr(emitter, ctx, index)?;
1164 emit_index_number(
1165 emitter,
1166 ctx,
1167 &ctx.ta
1168 .try_expr(index)
1169 .map_err(crate::codegen::arena_failure)?
1170 .ty,
1171 )?;
1172 let idx_f64_local = stash_index_operand(emitter)?;
1173 emitter.instruction(Instruction::LocalGet(receiver));
1174 crate::codegen::cast_check::emit_operation_cast_on_stack(
1175 emitter,
1176 ctx,
1177 &Type::Unknown,
1178 &Type::String,
1179 )?;
1180 let string = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
1181 emitter.instruction(Instruction::LocalTee(string));
1182 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(string_idx)));
1183 emitter.instruction(Instruction::StructGet {
1184 struct_type_index: string_idx,
1185 field_index: 1,
1186 });
1187 emitter.instruction(Instruction::ArrayLen);
1188 let length = emitter.add_anonymous_local(ValType::I32)?;
1189 emitter.instruction(Instruction::LocalSet(length));
1190 crate::codegen::bounds::emit_index_in_range(emitter, length, idx_f64_local);
1191 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
1192 let label = ctx
1193 .symbols
1194 .field_name_string_global_idx(receiver_name)
1195 .ok_or_else(|| crate::codegen::internal_failure("string index label global collected"))?;
1196 emitter.instruction(Instruction::GlobalGet(label));
1197 emitter.instruction(Instruction::LocalGet(idx_f64_local));
1198 emitter.instruction(Instruction::LocalGet(string));
1199 emitter.instruction(Instruction::Call(helper));
1200 cast::emit_coerce_to_slot(emitter, ctx, &Type::String, result_ty)?;
1201 emitter.emit_else();
1202 emit_undefined(emitter, ctx)?;
1203 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
1204 emitter.emit_end();
1205 Ok(())
1206}
1207
1208#[derive(Clone, Copy)]
1210enum KeyOperand {
1211 Local(u32),
1212 Global(u32),
1213}
1214
1215fn emit_checked_record_read(
1228 emitter: &mut FunctionEmitter,
1229 ctx: &CodegenCtx,
1230 read_ty: &Type,
1231 key: KeyOperand,
1232 probe: RecordProbe,
1233 receiver_label: &str,
1234) -> Result<(), crate::compiler_error::CompilerFailure> {
1235 let present_ty = crate::typechecker::infer::narrowing::strip_undefined(read_ty);
1236 let value = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
1237 emitter.instruction(Instruction::LocalTee(value));
1238 emit_is_undefined(emitter, ctx)?;
1239 if read_ty.holds_undefined() && !present_ty.holds_undefined() {
1240 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(read_ty)?));
1241 if let Some(flag) = probe.present_flag {
1244 match probe.record {
1245 Some(record) => {
1246 let Some(has_field) = ctx.require(
1247 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
1248 "record key probe imported",
1249 ) else {
1250 return Ok(());
1251 };
1252 emitter.instruction(Instruction::LocalGet(record));
1253 push_record_key(emitter, key);
1254 emitter.instruction(Instruction::Call(has_field));
1255 emitter.instruction(Instruction::LocalSet(flag));
1256 }
1257 None => set_present_flag(emitter, flag, false),
1258 }
1259 }
1260 emit_undefined(emitter, ctx)?;
1261 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, read_ty)?;
1262 emitter.emit_else();
1263 if let Some(flag) = probe.present_flag {
1264 set_present_flag(emitter, flag, true);
1265 }
1266 emitter.instruction(Instruction::LocalGet(value));
1267 crate::codegen::cast_check::emit_checked_cast_on_stack(
1268 emitter,
1269 ctx,
1270 &Type::Unknown,
1271 &present_ty,
1272 )?;
1273 cast::emit_coerce_to_slot(emitter, ctx, &present_ty, read_ty)?;
1274 emitter.emit_end();
1275 return Ok(());
1276 }
1277 if let Some(flag) = probe.present_flag {
1280 set_present_flag(emitter, flag, true);
1281 }
1282 emitter.emit_if(BlockType::Empty);
1283 if !read_ty.holds_undefined() && !matches!(read_ty.peel(), Type::Error | Type::Never) {
1284 let Some(helper) = ctx.require(
1285 ctx.symbols.prelude_func_idx("missing_key_error"),
1286 "missing key helper imported",
1287 ) else {
1288 return Ok(());
1289 };
1290 if let Some(record) = probe.record {
1293 let Some(has_field) = ctx.require(
1294 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
1295 "record key probe imported",
1296 ) else {
1297 return Ok(());
1298 };
1299 emitter.instruction(Instruction::LocalGet(record));
1300 push_record_key(emitter, key);
1301 emitter.instruction(Instruction::Call(has_field));
1302 emitter.instruction(Instruction::I32Eqz);
1303 emitter.emit_if(BlockType::Empty);
1304 }
1305 push_record_key(emitter, key);
1306 let label = if ctx.strings.lookup_text(receiver_label).is_some() {
1308 receiver_label
1309 } else {
1310 ""
1311 };
1312 crate::codegen::function_emitter::emit_const_string_by_text(emitter, ctx, label)?;
1313 emitter.instruction(Instruction::Call(helper));
1314 emitter.instruction(Instruction::Unreachable);
1315 if probe.record.is_some() {
1316 emitter.emit_end();
1317 }
1318 }
1319 emitter.emit_end();
1320 emitter.instruction(Instruction::LocalGet(value));
1321 crate::codegen::cast_check::emit_checked_cast_on_stack(emitter, ctx, &Type::Unknown, read_ty)?;
1322 Ok(())
1323}
1324
1325fn push_record_key(emitter: &mut FunctionEmitter, key: KeyOperand) {
1327 match key {
1328 KeyOperand::Local(local) => emitter.instruction(Instruction::LocalGet(local)),
1329 KeyOperand::Global(global) => emitter.instruction(Instruction::GlobalGet(global)),
1330 }
1331}
1332
1333#[derive(Clone, Copy)]
1336struct RecordProbe {
1337 record: Option<u32>,
1340 present_flag: Option<u32>,
1342}
1343
1344fn stash_probed_record(
1348 emitter: &mut FunctionEmitter,
1349 ctx: &CodegenCtx,
1350 read_ty: &Type,
1351 present_flag: Option<u32>,
1352) -> Result<RecordProbe, crate::compiler_error::CompilerFailure> {
1353 if !read_ty.has_type_parameter_member() || (read_ty.holds_undefined() && present_flag.is_none())
1356 {
1357 return Ok(RecordProbe {
1358 record: None,
1359 present_flag,
1360 });
1361 }
1362 let record = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
1363 emitter.instruction(Instruction::LocalTee(record));
1364 Ok(RecordProbe {
1365 record: Some(record),
1366 present_flag,
1367 })
1368}
1369
1370fn emit_nullish_coalesce(
1371 emitter: &mut FunctionEmitter,
1372 ctx: &CodegenCtx,
1373 lhs: &ExprId,
1374 rhs: &ExprId,
1375 result_ty: &Type,
1376) -> Result<(), crate::compiler_error::CompilerFailure> {
1377 let result_ty = result_ty.clone();
1378
1379 let result_val = ctx.symbols.value_type(&result_ty)?;
1380 let lhs_ty = ctx
1381 .ta
1382 .try_expr(*lhs)
1383 .map_err(crate::codegen::arena_failure)?
1384 .ty
1385 .clone();
1386 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
1387 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
1388 if lhs_is_ref {
1389 let tmp = emitter.add_anonymous_local(lhs_val)?;
1390 emit_expr(emitter, ctx, *lhs)?;
1391 emitter.instruction(Instruction::LocalTee(tmp));
1392 emit_is_nullish(emitter, ctx)?;
1393 emitter.emit_if(BlockType::Result(result_val));
1394 emit_conditional_operand(emitter, ctx, *rhs, &result_ty)?;
1395 emitter.emit_else();
1396 emitter.instruction(Instruction::LocalGet(tmp));
1397 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, &result_ty)?;
1407 emitter.emit_end();
1408 } else {
1409 emit_expr(emitter, ctx, *lhs)?;
1411 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
1412 emitter, ctx, &lhs_ty, &result_ty,
1413 )?;
1414 }
1415
1416 Ok(())
1417}
1418
1419fn conditional_block_type(
1422 ctx: &CodegenCtx,
1423 result_ty: &Type,
1424) -> Result<BlockType, crate::compiler_error::CompilerFailure> {
1425 Ok(BlockType::Result(ctx.symbols.value_type(result_ty)?))
1426}
1427
1428fn emit_conditional_operand(
1432 emitter: &mut FunctionEmitter,
1433 ctx: &CodegenCtx,
1434 operand: ExprId,
1435 result_ty: &Type,
1436) -> Result<(), crate::compiler_error::CompilerFailure> {
1437 let operand_ty = ctx
1438 .ta
1439 .try_expr(operand)
1440 .map_err(crate::codegen::arena_failure)?
1441 .ty
1442 .clone();
1443 emit_expr(emitter, ctx, operand)?;
1444 cast::emit_coerce_to_slot(emitter, ctx, &operand_ty, result_ty)
1445}
1446
1447fn emit_generic_call(
1454 emitter: &mut FunctionEmitter,
1455 ctx: &CodegenCtx,
1456 result_ty: &Type,
1457 mangled: &crate::MangledName,
1458 type_args: &[Type],
1459 args: &[crate::GenericArgument],
1460 return_cast: &Option<Type>,
1461) -> Result<(), crate::compiler_error::CompilerFailure> {
1462 let (wasm_idx, target_params, target_return) = match ctx.symbols.top_level_fn(mangled) {
1477 Some(target) => (target.wasm_idx, target.params.clone(), target.ret.clone()),
1478 None => {
1479 return Err(crate::codegen::internal_failure(
1480 "generic call target is not registered",
1481 ));
1482 }
1483 };
1484 for (i, arg) in args.iter().enumerate() {
1485 emit_expr(emitter, ctx, arg.expr)?;
1486 if arg.is_generic {
1487 let arg_ty = ctx
1488 .ta
1489 .try_expr(arg.expr)
1490 .map_err(crate::codegen::arena_failure)?
1491 .ty
1492 .clone();
1493 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &arg_ty)?;
1494 } else if let Some(param_ty) = target_params.get(i) {
1495 let arg_ty = ctx
1500 .ta
1501 .try_expr(arg.expr)
1502 .map_err(crate::codegen::arena_failure)?
1503 .ty
1504 .clone();
1505 cast::emit_coerce_to_slot(emitter, ctx, &arg_ty, param_ty)?;
1506 }
1507 }
1508 if crate::codegen::field_guards::guarded_constructor(ctx, mangled, result_ty)? {
1509 crate::codegen::field_guards::constructor_argument(emitter, ctx, result_ty)?;
1510 }
1511 if ctx.symbols.runtime_generic_functions.contains(mangled) {
1512 crate::codegen::runtime_descriptors::require_direct_schema(ctx, mangled, type_args)?;
1513 crate::codegen::runtime_descriptors::environment(emitter, ctx, type_args)?;
1514 }
1515 emitter.instruction(Instruction::Call(wasm_idx));
1516 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
1517 emit_undefined(emitter, ctx)?;
1518 }
1519 if let Some(ty) = return_cast {
1520 cast::emit_cast_to(emitter, ctx, ty)?;
1521 } else {
1522 cast::emit_coerce_to_slot(emitter, ctx, &target_return, result_ty)?;
1523 }
1524
1525 Ok(())
1526}
1527
1528fn emit_object_literal(
1529 emitter: &mut FunctionEmitter,
1530 ctx: &CodegenCtx,
1531 result_ty: &Type,
1532 members: &[TypedObjectMember],
1533 fields: &[crate::TypedObjectFieldOrigin],
1534) -> Result<(), crate::compiler_error::CompilerFailure> {
1535 if members
1536 .iter()
1537 .any(|member| matches!(member, TypedObjectMember::Computed { .. }))
1538 {
1539 emit_computed_object(emitter, ctx, members)?;
1540 return Ok(());
1541 }
1542 if !matches!(result_ty, Type::Object { .. } | Type::InterfaceRef { .. }) {
1543 return Err(crate::codegen::internal_failure(
1544 "object literal requires an object representation",
1545 ));
1546 }
1547 let structural_ty = crate::typed_ast::object_literal_layout(result_ty, fields);
1548 if members
1549 .iter()
1550 .any(|member| matches!(member, TypedObjectMember::Spread { .. }))
1551 {
1552 emit_object_spread(emitter, ctx, members, &structural_ty)?;
1553 return Ok(());
1554 }
1555 let vtable_idx = ctx
1556 .symbols
1557 .vtable_global_idx(&structural_ty)
1558 .ok_or_else(|| {
1559 crate::codegen::internal_failure("vtable global recorded during object-emission pass")
1560 })?;
1561 let object_shape_idx = ctx
1562 .symbols
1563 .object_subtype_idx(&structural_ty)
1564 .ok_or_else(|| {
1565 crate::codegen::internal_failure(
1566 "object shape type recorded during object-emission pass",
1567 )
1568 })?;
1569 let Type::Object {
1570 fields: declared_fields,
1571 ..
1572 } = &structural_ty
1573 else {
1574 return Err(crate::codegen::internal_failure(
1575 "object literal has no structural representation",
1576 ));
1577 };
1578 let shape_key: Vec<_> = declared_fields
1579 .iter()
1580 .map(|(name, field)| crate::codegen::field_names::FieldName {
1581 name: name.clone(),
1582 optional: field.optional,
1583 is_accessor: false,
1584 is_method: false,
1585 is_private: false,
1586 })
1587 .collect();
1588 let intrinsics = ctx
1589 .symbols
1590 .intrinsic_type_indices()
1591 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1592
1593 let evaluated = evaluate_object_members(emitter, ctx, members)?;
1595
1596 emitter.instruction(Instruction::GlobalGet(vtable_idx));
1597 crate::codegen::field_names::emit_instance_names(emitter, ctx, &shape_key, |name| {
1598 fields.iter().any(|field| {
1599 field.name.name == name && !matches!(field.source, TypedObjectFieldSource::Absent(_))
1600 })
1601 })?;
1602
1603 let by_name: std::collections::BTreeMap<&str, &crate::TypedObjectFieldOrigin> =
1608 fields.iter().map(|f| (f.name.name.as_str(), f)).collect();
1609 for (name, field) in declared_fields {
1610 if let Some(origin) = by_name.get(name.as_str()) {
1611 let (TypedObjectFieldSource::Literal(value) | TypedObjectFieldSource::Absent(value)) =
1612 &origin.source
1613 else {
1614 return Err(crate::codegen::internal_failure(
1615 "spread field reached plain object lowering",
1616 ));
1617 };
1618 emitter.instruction(Instruction::LocalGet(*evaluated.get(value).ok_or_else(
1619 || crate::codegen::internal_failure("object field value was not evaluated"),
1620 )?));
1621 } else {
1622 if !field.optional {
1623 return Err(crate::codegen::internal_failure(
1624 "required object field is missing from the literal",
1625 ));
1626 }
1627 emit_undefined(emitter, ctx)?;
1628 }
1629 }
1630 emitter.instruction(Instruction::ArrayNewFixed {
1631 array_type_index: intrinsics.object_fields,
1632 array_size: crate::codegen::wasm_u32(declared_fields.len())?,
1633 });
1634 emitter.instruction(Instruction::RefNull(HeapType::ANY));
1635 emitter.instruction(Instruction::StructNew(object_shape_idx));
1636
1637 Ok(())
1638}
1639
1640fn emit_field_access(
1641 emitter: &mut FunctionEmitter,
1642 ctx: &CodegenCtx,
1643 id: ExprId,
1644 result_ty: &Type,
1645 receiver: &ExprId,
1646 name: &Ident,
1647) -> Result<(), crate::compiler_error::CompilerFailure> {
1648 let receiver_ty = ctx
1649 .ta
1650 .source_type(*receiver)
1651 .map_err(crate::codegen::arena_failure)?
1652 .clone();
1653 if ctx.ta.has_string_index(&receiver_ty) {
1654 let present_flag = take_present_flag(emitter, id);
1656 emit_expr(emitter, ctx, *receiver)?;
1657 cast::emit_box(
1658 emitter,
1659 ctx,
1660 &ctx.ta
1661 .try_expr(*receiver)
1662 .map_err(crate::codegen::arena_failure)?
1663 .ty,
1664 )?;
1665 let checked_ty = ctx
1666 .ta
1667 .source_type(id)
1668 .map_err(crate::codegen::arena_failure)?;
1669 let probe = stash_probed_record(emitter, ctx, checked_ty, present_flag)?;
1670 let Some(symbol) = ctx.require(
1671 ctx.symbols.field_name_string_global_idx(&name.name),
1672 "property name collected",
1673 ) else {
1674 return Ok(());
1675 };
1676 emitter.instruction(Instruction::GlobalGet(symbol));
1677 let key = KeyOperand::Global(symbol);
1678 let Some(symbol) = ctx.require(
1679 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
1680 "record read imported",
1681 ) else {
1682 return Ok(());
1683 };
1684 emitter.instruction(Instruction::Call(symbol));
1685 emit_checked_record_read(
1686 emitter,
1687 ctx,
1688 checked_ty,
1689 key,
1690 probe,
1691 &receiver_name(ctx.ta, *receiver)?,
1692 )?;
1693 cast::emit_coerce_to_slot(emitter, ctx, checked_ty, result_ty)?;
1694 return Ok(());
1695 }
1696 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
1698 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
1699 emit_receiver(emitter, ctx, *receiver)?;
1700 emit_class_method_value(
1701 emitter,
1702 ctx,
1703 &receiver_ty,
1704 &name.name,
1705 result_ty,
1706 ctx.ta
1707 .source_type(id)
1708 .map_err(crate::codegen::arena_failure)?,
1709 )?;
1710 return Ok(());
1711 }
1712 if ctx.symbols.class_field_slot(mangled, &name.name).is_none() {
1714 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
1715 emit_method_call(
1716 emitter,
1717 ctx,
1718 *receiver,
1719 mangled,
1720 &getter,
1721 &[],
1722 None,
1723 &[],
1724 None,
1725 &ctx.ta
1726 .try_expr(id)
1727 .map_err(crate::codegen::arena_failure)?
1728 .ty,
1729 )?;
1730 return Ok(());
1731 }
1732 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
1733 crate::codegen::internal_failure("class struct type recorded in classes::emit")
1734 })?;
1735 let slot = ctx
1736 .symbols
1737 .class_field_slot(mangled, &name.name)
1738 .ok_or_else(|| {
1739 crate::codegen::internal_failure("class field slot recorded in classes::emit")
1740 })?;
1741 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1742 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1743 })?;
1744 emit_receiver(emitter, ctx, *receiver)?;
1745 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&receiver_ty)?)?;
1746 emitter.instruction(Instruction::LocalTee(object));
1747 emitter.instruction(Instruction::StructGet {
1748 struct_type_index: struct_idx,
1749 field_index: 2,
1750 });
1751 emitter.instruction(Instruction::I32Const(slot as i32));
1752 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
1753 if ctx
1754 .symbols
1755 .class_field_narrowing_check(mangled, &name.name)
1756 .is_some()
1757 {
1758 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
1759 }
1760 emit_class_field_slot_cast_as(
1761 emitter,
1762 ctx,
1763 mangled,
1764 &name.name,
1765 &ctx.ta
1766 .try_expr(id)
1767 .map_err(crate::codegen::arena_failure)?
1768 .ty,
1769 ctx.ta
1770 .source_type(id)
1771 .map_err(crate::codegen::arena_failure)?,
1772 )?;
1773 return Ok(());
1774 }
1775 let intrinsics = ctx
1776 .symbols
1777 .intrinsic_type_indices()
1778 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1779 let field_ty = ctx
1782 .ta
1783 .try_expr(id)
1784 .map_err(crate::codegen::arena_failure)?
1785 .ty
1786 .clone();
1787 emit_receiver(emitter, ctx, *receiver)?;
1790 let rcv_local = stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
1791 emit_object_property_read_as(
1792 emitter,
1793 ctx,
1794 rcv_local,
1795 &name.name,
1796 &field_ty,
1797 ctx.ta
1798 .source_type(id)
1799 .map_err(crate::codegen::arena_failure)?,
1800 )?;
1801
1802 Ok(())
1803}
1804
1805fn emit_closure_value(
1806 emitter: &mut FunctionEmitter,
1807 ctx: &CodegenCtx,
1808 id: ExprId,
1809 result_ty: &Type,
1810 params: &[crate::TypedParam],
1811 captured: &[crate::CapturedVar],
1812 runtime_generics: &[String],
1813) -> Result<(), crate::compiler_error::CompilerFailure> {
1814 let closure_struct_idx = ctx
1832 .symbols
1833 .closure_struct_type_idx(crate::codegen::closures::classify(result_ty)?)
1834 .ok_or_else(|| {
1835 crate::codegen::internal_failure(
1836 "closure struct type registered for every closure-typed expression",
1837 )
1838 })?;
1839 let env_type_idx = ctx.symbols.env_type_idx(id).ok_or_else(|| {
1840 crate::codegen::internal_failure("env type registered for every closure expression")
1841 })?;
1842 let closure_func_idx = ctx
1843 .symbols
1844 .closure_func_idx(id)
1845 .ok_or_else(|| crate::codegen::internal_failure("closure body function index allocated"))?;
1846 let closure_vtable_idx = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
1847 crate::codegen::internal_failure("closure vtable global emitted whenever closures exist")
1848 })?;
1849
1850 emitter.instruction(Instruction::GlobalGet(closure_vtable_idx));
1852
1853 emitter.instruction(Instruction::RefFunc(closure_func_idx));
1855
1856 for c in captured {
1859 emit_captured_load(emitter, ctx, c)?;
1860 }
1861 if !runtime_generics.is_empty() {
1862 let types: Vec<_> = runtime_generics
1863 .iter()
1864 .cloned()
1865 .map(Type::TypeVar)
1866 .collect();
1867 crate::codegen::runtime_descriptors::environment(emitter, ctx, &types)?;
1868 }
1869 let self_environment = if ctx.ta.closure_names.contains_key(&id) {
1870 emitter.instruction(Instruction::RefNull(HeapType::Concrete(
1871 ctx.symbols
1872 .intrinsic_type_indices()
1873 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
1874 .object,
1875 )));
1876 Some(emitter.add_anonymous_local(ValType::Ref(RefType {
1877 nullable: false,
1878 heap_type: HeapType::Concrete(env_type_idx),
1879 }))?)
1880 } else {
1881 None
1882 };
1883 emitter.instruction(Instruction::StructNew(env_type_idx));
1884 if let Some(local) = self_environment {
1885 emitter.instruction(Instruction::LocalTee(local));
1886 }
1887 if let Some(metadata) = crate::codegen::call_arguments::typed_metadata(params) {
1888 crate::codegen::call_arguments::wrap(emitter, ctx, &metadata)?;
1889 }
1890
1891 if ctx.ta.closure_this.contains_key(&id) {
1892 crate::codegen::this_binding::wrap(emitter, ctx)?;
1893 }
1894 emitter.instruction(Instruction::I64Const(0));
1898 emitter.instruction(Instruction::StructNew(closure_struct_idx));
1899 if let Some(environment) = self_environment {
1900 let closure = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
1901 emitter.instruction(Instruction::LocalSet(closure));
1902 emitter.instruction(Instruction::LocalGet(environment));
1903 emitter.instruction(Instruction::LocalGet(closure));
1904 emitter.instruction(Instruction::StructSet {
1905 struct_type_index: env_type_idx,
1906 field_index: crate::codegen::wasm_u32(
1907 captured
1908 .len()
1909 .checked_add(usize::from(!runtime_generics.is_empty()))
1910 .ok_or_else(|| {
1911 crate::codegen::internal_failure("closure capture count overflow")
1912 })?,
1913 )?,
1914 });
1915 emitter.instruction(Instruction::LocalGet(closure));
1916 }
1917
1918 Ok(())
1919}
1920
1921fn inferred_never(
1924 ctx: &CodegenCtx,
1925 id: ExprId,
1926) -> Result<bool, crate::compiler_error::CompilerFailure> {
1927 let ty = ctx
1928 .ta
1929 .source_type(id)
1930 .map_err(crate::codegen::arena_failure)?;
1931 Ok(matches!(ty, Type::Never))
1932}
1933
1934pub(super) fn emit_value_with_present_flag(
1941 emitter: &mut FunctionEmitter,
1942 ctx: &CodegenCtx,
1943 value: ExprId,
1944 present_flag: Option<u32>,
1945) -> Result<(), crate::compiler_error::CompilerFailure> {
1946 let Some(flag) = present_flag else {
1947 return emit_expr(emitter, ctx, value);
1948 };
1949 let expr = ctx
1950 .ta
1951 .try_expr(value)
1952 .map_err(crate::codegen::arena_failure)?;
1953 if !ctx.ta.widened_missing_reads.contains_key(&value) {
1954 emit_expr(emitter, ctx, value)?;
1955 set_present_flag(emitter, flag, true);
1956 return Ok(());
1957 }
1958 match &expr.kind {
1959 TypedExprKind::LocalRef { ident, .. } => {
1960 let source = emitter
1961 .local_slot(&ident.name)
1962 .and_then(|slot| emitter.present_flag(slot));
1963 emit_expr(emitter, ctx, value)?;
1964 match source {
1965 Some(source) => {
1966 emitter.instruction(Instruction::LocalGet(source));
1967 emitter.instruction(Instruction::LocalSet(flag));
1968 }
1969 None => set_present_flag_from_value(emitter, ctx, flag, &expr.ty)?,
1972 }
1973 }
1974 TypedExprKind::IndexAccess { .. }
1975 | TypedExprKind::FieldAccess { .. }
1976 | TypedExprKind::OptionalChain { .. } => {
1977 emitter.present_flag_request = Some((value, flag));
1978 emit_expr(emitter, ctx, value)?;
1979 if emitter.present_flag_request.take().is_some() {
1983 set_present_flag_from_value(emitter, ctx, flag, &expr.ty)?;
1984 }
1985 }
1986 _ => {
1987 emit_expr(emitter, ctx, value)?;
1988 set_present_flag_from_value(emitter, ctx, flag, &expr.ty)?;
1989 }
1990 }
1991 Ok(())
1992}
1993
1994fn take_present_flag(emitter: &mut FunctionEmitter, id: ExprId) -> Option<u32> {
1997 match emitter.present_flag_request {
1998 Some((owner, flag)) if owner == id => {
1999 emitter.present_flag_request = None;
2000 Some(flag)
2001 }
2002 _ => None,
2003 }
2004}
2005
2006fn set_present_flag(emitter: &mut FunctionEmitter, flag: u32, found: bool) {
2007 emitter.instruction(Instruction::I32Const(i32::from(found)));
2008 emitter.instruction(Instruction::LocalSet(flag));
2009}
2010
2011fn set_present_flag_from_value(
2014 emitter: &mut FunctionEmitter,
2015 ctx: &CodegenCtx,
2016 flag: u32,
2017 ty: &Type,
2018) -> Result<(), crate::compiler_error::CompilerFailure> {
2019 let value_ty = ctx.symbols.value_type(ty)?;
2020 if !matches!(value_ty, ValType::Ref(_)) {
2021 set_present_flag(emitter, flag, true);
2022 return Ok(());
2023 }
2024 let value = emitter.add_anonymous_local(value_ty)?;
2025 emitter.instruction(Instruction::LocalTee(value));
2026 emit_is_undefined(emitter, ctx)?;
2027 emitter.instruction(Instruction::I32Eqz);
2028 emitter.instruction(Instruction::LocalSet(flag));
2029 emitter.instruction(Instruction::LocalGet(value));
2030 Ok(())
2031}
2032
2033pub(super) fn reads_maybe_missing_local(
2039 emitter: &FunctionEmitter,
2040 ctx: &CodegenCtx,
2041 source: ExprId,
2042) -> Result<bool, crate::compiler_error::CompilerFailure> {
2043 let expr = ctx
2044 .ta
2045 .try_expr(source)
2046 .map_err(crate::codegen::arena_failure)?;
2047 let TypedExprKind::LocalRef { ident, .. } = &expr.kind else {
2048 return Ok(false);
2049 };
2050 Ok(emitter
2051 .local_slot(&ident.name)
2052 .is_some_and(|slot| emitter.maybe_missing_slots.contains_key(&slot)))
2053}
2054
2055fn emit_missing_local_check(
2061 emitter: &mut FunctionEmitter,
2062 ctx: &CodegenCtx,
2063 slot: u32,
2064 stack: ValType,
2065) -> Result<(), crate::compiler_error::CompilerFailure> {
2066 let Some((message, present_flag)) = emitter
2067 .maybe_missing_slots
2068 .get(&slot)
2069 .map(|local| (local.message.clone(), local.present_flag))
2070 else {
2071 return Ok(());
2072 };
2073 if let Some(flag) = present_flag {
2076 emitter.instruction(Instruction::LocalGet(flag));
2077 emitter.instruction(Instruction::I32Eqz);
2078 emitter.emit_if(BlockType::Empty);
2079 crate::codegen::throw::emit_missing_value_error_throw(emitter, ctx, &message);
2080 emitter.emit_end();
2081 return Ok(());
2082 }
2083 let value = emitter.add_anonymous_local(stack)?;
2084 emitter.instruction(Instruction::LocalTee(value));
2085 emit_is_undefined(emitter, ctx)?;
2086 emitter.emit_if(BlockType::Empty);
2087 crate::codegen::throw::emit_missing_value_error_throw(emitter, ctx, &message);
2088 emitter.emit_end();
2089 emitter.instruction(Instruction::LocalGet(value));
2090 Ok(())
2091}
2092
2093fn emit_local_narrow_ref(
2094 emitter: &mut FunctionEmitter<'_>,
2095 ctx: &CodegenCtx<'_>,
2096 binding: &Ident,
2097 path: &ReferencePath,
2098 narrowed_ty: &Type,
2099) -> Result<(), crate::compiler_error::CompilerFailure> {
2100 if !path.chain.is_empty()
2101 && let Some(source) = emitter.narrow_source(&binding.name)
2102 {
2103 emit_expr(emitter, ctx, source)?;
2106 crate::codegen::cast_check::emit_checked_cast_on_stack(
2107 emitter,
2108 ctx,
2109 &ctx.ta
2110 .try_expr(source)
2111 .map_err(crate::codegen::arena_failure)?
2112 .ty,
2113 narrowed_ty,
2114 )?;
2115 return Ok(());
2116 }
2117 let Some((slot, slot_ty)) = emitter.narrowed_read_slot(&binding.name) else {
2118 if let BindingId::Local { name, .. } = &path.root
2123 && let Some((slot, slot_ty)) = emitter.narrowed_read_slot(name)
2124 {
2125 emitter.instruction(Instruction::LocalGet(slot));
2126 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
2127 emit_missing_local_check(emitter, ctx, slot, stack_ty)?;
2130 if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
2131 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
2132 }
2133 return Ok(());
2134 }
2135 if let BindingId::Global(mangled) = &path.root {
2136 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
2137 crate::codegen::internal_failure("Inferer guarantees the binding exists")
2138 })?;
2139 emitter.instruction(Instruction::GlobalGet(idx));
2140 let source_ty = ctx.symbols.global_type(mangled).ok_or_else(|| {
2141 crate::codegen::internal_failure("language globals retain their declared type")
2142 })?;
2143 crate::codegen::cast_check::emit_checked_cast_on_stack(
2144 emitter,
2145 ctx,
2146 source_ty,
2147 narrowed_ty,
2148 )?;
2149 return Ok(());
2150 }
2151 if let Some(source) = emitter.narrow_source(&binding.name) {
2152 emit_expr(emitter, ctx, source)?;
2153 let source_ty = &ctx
2154 .ta
2155 .try_expr(source)
2156 .map_err(crate::codegen::arena_failure)?
2157 .ty;
2158 let cast_info = cast_info_for(source_ty.clone(), narrowed_ty.clone());
2159 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
2160 return Ok(());
2161 }
2162 return Err(crate::codegen::internal_failure(format!(
2163 "narrow binding `{}` (path {path:?}) not registered in codegen scope — \
2164 the region's source names a shadow from a scope that already closed",
2165 binding.name,
2166 )));
2167 };
2168 emitter.instruction(Instruction::LocalGet(slot));
2169 let stack_ty = unbox_if_boxed(emitter, ctx, slot_ty)?;
2170 let _: () = if stack_ty != ctx.symbols.value_type(narrowed_ty)? {
2171 cast::emit_cast_to(emitter, ctx, narrowed_ty)?;
2172 };
2173 Ok(())
2174}
2175
2176fn unbox_if_boxed(
2184 emitter: &mut FunctionEmitter<'_>,
2185 ctx: &CodegenCtx<'_>,
2186 slot_ty: ValType,
2187) -> Result<ValType, crate::compiler_error::CompilerFailure> {
2188 let ValType::Ref(RefType {
2189 heap_type: HeapType::Concrete(idx),
2190 ..
2191 }) = slot_ty
2192 else {
2193 return Ok(slot_ty);
2194 };
2195 let Some(payload) = ctx.symbols.box_payload_type(idx) else {
2196 return Ok(slot_ty);
2197 };
2198 let local = emitter.add_anonymous_local(slot_ty)?;
2199 emitter.instruction(Instruction::LocalSet(local));
2200 super::binding_cells::read(emitter, ctx, local, idx)?;
2201 Ok(payload)
2202}
2203
2204fn emit_array_literal(
2207 emitter: &mut FunctionEmitter,
2208 ctx: &CodegenCtx,
2209 elements: &[crate::TypedArrayElement],
2210) -> Result<(), crate::compiler_error::CompilerFailure> {
2211 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
2232 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
2233 })?;
2234 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
2235 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
2236 })?;
2237 let array_vtable_global = ctx
2238 .symbols
2239 .prelude_global_idx("array_vtable")
2240 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
2241
2242 let has_spread = elements
2243 .iter()
2244 .any(|e| matches!(e, crate::TypedArrayElement::Spread(_)));
2245
2246 if has_spread {
2247 emit_spread_array_literal(emitter, ctx, elements)?;
2248 } else {
2249 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
2250 for el in elements {
2251 let elem_id = el.expr_id();
2252 emit_expr(emitter, ctx, elem_id)?;
2253 let elem_ty = ctx
2254 .ta
2255 .try_expr(elem_id)
2256 .map_err(crate::codegen::arena_failure)?
2257 .ty
2258 .clone();
2259 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &elem_ty)?;
2260 }
2261 emitter.instruction(Instruction::ArrayNewFixed {
2262 array_type_index: raw_array_idx,
2263 array_size: crate::codegen::wasm_u32(elements.len())?,
2264 });
2265 emitter.instruction(Instruction::I32Const(
2266 i32::try_from(elements.len())
2267 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
2268 ));
2269 emitter.instruction(Instruction::StructNew(array_idx));
2270 }
2271 Ok(())
2272}
2273
2274fn emit_tuple_literal(
2275 emitter: &mut FunctionEmitter,
2276 ctx: &CodegenCtx,
2277 elements: &[ExprId],
2278) -> Result<(), crate::compiler_error::CompilerFailure> {
2279 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
2281 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
2282 })?;
2283 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
2284 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
2285 })?;
2286 let array_vtable_global = ctx
2287 .symbols
2288 .prelude_global_idx("array_vtable")
2289 .ok_or_else(|| crate::codegen::internal_failure("array_vtable imported from prelude"))?;
2290 emitter.instruction(Instruction::GlobalGet(array_vtable_global));
2291 for &elem_id in elements {
2292 emit_expr(emitter, ctx, elem_id)?;
2293 crate::codegen::function_emitter::cast::emit_box(
2294 emitter,
2295 ctx,
2296 &ctx.ta
2297 .try_expr(elem_id)
2298 .map_err(crate::codegen::arena_failure)?
2299 .ty,
2300 )?;
2301 }
2302 emitter.instruction(Instruction::ArrayNewFixed {
2303 array_type_index: raw_array_idx,
2304 array_size: crate::codegen::wasm_u32(elements.len())?,
2305 });
2306 emitter.instruction(Instruction::I32Const(
2307 i32::try_from(elements.len())
2308 .map_err(|_| crate::codegen::internal_failure("array literal too large"))?,
2309 ));
2310 emitter.instruction(Instruction::StructNew(array_idx));
2311 Ok(())
2312}
2313
2314fn emit_spread_array_literal(
2315 emitter: &mut FunctionEmitter,
2316 ctx: &CodegenCtx,
2317 elements: &[crate::TypedArrayElement],
2318) -> Result<(), crate::compiler_error::CompilerFailure> {
2319 let array = ctx
2320 .symbols
2321 .array_type_idx()
2322 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?;
2323 let raw = ctx
2324 .symbols
2325 .raw_array_type_idx()
2326 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?;
2327 let raw_type = ValType::Ref(RefType {
2328 nullable: false,
2329 heap_type: HeapType::Concrete(raw),
2330 });
2331 let total = emitter.add_anonymous_local(ValType::I32)?;
2332 let offset = emitter.add_anonymous_local(ValType::I32)?;
2333 let destination = emitter.add_anonymous_local(raw_type)?;
2334 emitter.instruction(Instruction::I32Const(0));
2335 emitter.instruction(Instruction::LocalSet(total));
2336 let mut chunks = Vec::with_capacity(elements.len());
2339 for element in elements {
2340 emit_literal_chunk(emitter, ctx, element, array, raw)?;
2341 let chunk = emitter.add_anonymous_local(raw_type)?;
2342 emitter.instruction(Instruction::LocalTee(chunk));
2343 emitter.instruction(Instruction::ArrayLen);
2344 emitter.instruction(Instruction::LocalGet(total));
2345 emitter.instruction(Instruction::I32Add);
2346 emitter.instruction(Instruction::LocalSet(total));
2347 chunks.push(chunk);
2348 }
2349 emitter.instruction(Instruction::LocalGet(total));
2350 emitter.instruction(Instruction::ArrayNewDefault(raw));
2351 emitter.instruction(Instruction::LocalSet(destination));
2352 emitter.instruction(Instruction::I32Const(0));
2353 emitter.instruction(Instruction::LocalSet(offset));
2354 for chunk in chunks {
2355 emitter.instruction(Instruction::LocalGet(destination));
2356 emitter.instruction(Instruction::LocalGet(offset));
2357 emitter.instruction(Instruction::LocalGet(chunk));
2358 emitter.instruction(Instruction::I32Const(0));
2359 emitter.instruction(Instruction::LocalGet(chunk));
2360 emitter.instruction(Instruction::ArrayLen);
2361 emitter.instruction(Instruction::ArrayCopy {
2362 array_type_index_dst: raw,
2363 array_type_index_src: raw,
2364 });
2365 emitter.instruction(Instruction::LocalGet(offset));
2366 emitter.instruction(Instruction::LocalGet(chunk));
2367 emitter.instruction(Instruction::ArrayLen);
2368 emitter.instruction(Instruction::I32Add);
2369 emitter.instruction(Instruction::LocalSet(offset));
2370 }
2371 emitter.instruction(Instruction::GlobalGet(
2372 ctx.symbols
2373 .prelude_global_idx("array_vtable")
2374 .ok_or_else(|| crate::codegen::internal_failure("array vtable declared"))?,
2375 ));
2376 emitter.instruction(Instruction::LocalGet(destination));
2377 emitter.instruction(Instruction::LocalGet(total));
2378 emitter.instruction(Instruction::StructNew(array));
2379 Ok(())
2380}
2381
2382fn emit_literal_chunk(
2383 emitter: &mut FunctionEmitter,
2384 ctx: &CodegenCtx,
2385 element: &crate::TypedArrayElement,
2386 array: u32,
2387 raw: u32,
2388) -> Result<(), crate::compiler_error::CompilerFailure> {
2389 if let crate::TypedArrayElement::Value(value) = element {
2390 emit_expr(emitter, ctx, *value)?;
2391 cast::emit_box(
2392 emitter,
2393 ctx,
2394 &ctx.ta
2395 .try_expr(*value)
2396 .map_err(crate::codegen::arena_failure)?
2397 .ty,
2398 )?;
2399 emitter.instruction(Instruction::ArrayNewFixed {
2400 array_type_index: raw,
2401 array_size: 1,
2402 });
2403 return Ok(());
2404 }
2405 emit_receiver(emitter, ctx, element.expr_id())?;
2408 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(array)));
2409
2410 let raw_type = ValType::Ref(RefType {
2411 nullable: false,
2412 heap_type: HeapType::Concrete(raw),
2413 });
2414 let source = emitter.add_anonymous_local(raw_type)?;
2415 let snapshot = emitter.add_anonymous_local(raw_type)?;
2416 let length = stash_array_length(emitter, array)?;
2417 emitter.instruction(Instruction::StructGet {
2418 struct_type_index: array,
2419 field_index: 1,
2420 });
2421 emitter.instruction(Instruction::LocalSet(source));
2422 emitter.instruction(Instruction::LocalGet(length));
2423 emitter.instruction(Instruction::ArrayNewDefault(raw));
2424 emitter.instruction(Instruction::LocalTee(snapshot));
2425 emitter.instruction(Instruction::I32Const(0));
2426 emitter.instruction(Instruction::LocalGet(source));
2427 emitter.instruction(Instruction::I32Const(0));
2428 emitter.instruction(Instruction::LocalGet(length));
2429 emitter.instruction(Instruction::ArrayCopy {
2430 array_type_index_dst: raw,
2431 array_type_index_src: raw,
2432 });
2433 emitter.instruction(Instruction::LocalGet(snapshot));
2434 Ok(())
2435}
2436
2437fn emit_postfix_unary(
2441 emitter: &mut FunctionEmitter<'_>,
2442 ctx: &CodegenCtx<'_>,
2443 op: crate::PostfixOp,
2444 target: &crate::PostfixTarget,
2445 result_ty: &Type,
2446) -> Result<(), crate::compiler_error::CompilerFailure> {
2447 let _: () = match target {
2448 crate::PostfixTarget::Local {
2449 ident,
2450 boxed,
2451 target_ty,
2452 } => {
2453 let slot = emitter.write_slot(&ident.name)?;
2455 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
2456 let box_idx = if *boxed {
2457 Some(ctx.symbols.box_type_idx(target_ty)?.ok_or_else(|| {
2458 crate::codegen::internal_failure("box type registered for postfix operand")
2459 })?)
2460 } else {
2461 None
2462 };
2463 if let Some(box_idx) = box_idx {
2464 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
2465 } else {
2466 emitter.instruction(Instruction::LocalGet(slot));
2467 }
2468 let stack = ctx.symbols.value_type(target_ty)?;
2470 emit_missing_local_check(emitter, ctx, slot, stack)?;
2471 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
2472 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
2473 emit_postfix_numeric(emitter, ctx, result_ty);
2474 emitter.instruction(Instruction::LocalSet(old));
2475 if box_idx.is_some() {
2476 emitter.instruction(Instruction::LocalGet(slot));
2477 }
2478 emitter.instruction(Instruction::LocalGet(old));
2479 emit_postfix_delta(emitter, ctx, op, result_ty);
2480 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
2481 if let Some(box_idx) = box_idx {
2482 emitter.instruction(Instruction::StructSet {
2483 struct_type_index: box_idx,
2484 field_index: 0,
2485 });
2486 } else {
2487 emitter.instruction(Instruction::LocalSet(slot));
2488 }
2489 emitter.instruction(Instruction::LocalGet(old));
2490 }
2491 crate::PostfixTarget::Global {
2492 mangled, target_ty, ..
2493 } => {
2494 let idx = ctx.symbols.global_idx(mangled).ok_or_else(|| {
2495 crate::codegen::internal_failure("Inferer guarantees the binding exists")
2496 })?;
2497 let old = emitter.add_anonymous_local(ctx.symbols.value_type(result_ty)?)?;
2498 crate::codegen::init_guard::emit_check(emitter, ctx, mangled);
2499 emitter.instruction(Instruction::GlobalGet(idx));
2500 if let ValType::Ref(RefType {
2502 nullable: false, ..
2503 }) = ctx.symbols.value_type(target_ty)?
2504 {
2505 emitter.instruction(Instruction::RefAsNonNull);
2506 }
2507 let cast_info = cast_info_for(target_ty.clone(), result_ty.clone());
2508 cast::emit_narrowing_cast(emitter, ctx, &cast_info)?;
2509 emit_postfix_numeric(emitter, ctx, result_ty);
2510 emitter.instruction(Instruction::LocalSet(old));
2511 emitter.instruction(Instruction::LocalGet(old));
2512 emit_postfix_delta(emitter, ctx, op, result_ty);
2513 cast::emit_coerce_to_slot(emitter, ctx, result_ty, target_ty)?;
2514 emitter.instruction(Instruction::GlobalSet(idx));
2515 emitter.instruction(Instruction::LocalGet(old));
2516 }
2517 crate::PostfixTarget::Field {
2518 receiver,
2519 name,
2520 target_ty,
2521 } => {
2522 let receiver_ty = ctx
2527 .ta
2528 .source_type(*receiver)
2529 .map_err(crate::codegen::arena_failure)?
2530 .clone();
2531 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
2532 emit_class_field_postfix(
2533 emitter, ctx, *receiver, mangled, &name.name, op, target_ty,
2534 )?;
2535 return Ok(());
2536 }
2537 if let Some((getter, setter)) = host_property_accessors(ctx, &receiver_ty, name)? {
2538 emit_host_property_postfix(
2539 emitter,
2540 ctx,
2541 *receiver,
2542 &receiver_ty,
2543 (getter, setter),
2544 op,
2545 target_ty,
2546 )?;
2547 return Ok(());
2548 }
2549 if ctx.ta.has_string_index(&receiver_ty) {
2550 emit_expr(emitter, ctx, *receiver)?;
2551 cast::emit_box(emitter, ctx, &receiver_ty)?;
2552 let object =
2553 emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
2554 emitter.instruction(Instruction::LocalSet(object));
2555 super::emit_const_string_by_text(emitter, ctx, &name.name)?;
2556 emit_record_postfix_on_stack(emitter, ctx, *receiver, object, op, target_ty)?;
2557 cast::emit_cast_to(emitter, ctx, result_ty)?;
2558 return Ok(());
2559 }
2560 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
2562 crate::codegen::internal_failure("intrinsics declared by codegen entry")
2563 })?;
2564 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2565 emit_receiver(emitter, ctx, *receiver)?;
2566 let rcv_local =
2567 stash_receiver_as_object_shape(emitter, &receiver_ty, intrinsics.object_shape)?;
2568 emit_object_property_read(emitter, ctx, rcv_local, &name.name, target_ty)?;
2569 emit_postfix_numeric(emitter, ctx, target_ty);
2570 emitter.instruction(Instruction::LocalSet(tmp));
2571 emitter.instruction(Instruction::LocalGet(tmp));
2572 emit_postfix_delta(emitter, ctx, op, target_ty);
2573 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2574 emitter.instruction(Instruction::LocalSet(updated));
2575 emit_object_property_write_value(
2576 emitter,
2577 ctx,
2578 rcv_local,
2579 name,
2580 &ShapeArgument::Local {
2581 slot: updated,
2582 ty: target_ty.clone(),
2583 },
2584 )?;
2585 emitter.instruction(Instruction::LocalGet(tmp));
2586 }
2587 crate::PostfixTarget::Index {
2588 receiver, index, ..
2589 } => {
2590 emit_index_postfix(emitter, ctx, *receiver, *index, op)?;
2591 }
2592 };
2593 Ok(())
2594}
2595
2596enum HostPropertyGetter {
2598 Func(u32),
2599 InlineLength(u32),
2601}
2602
2603fn host_property_accessors(
2606 ctx: &CodegenCtx,
2607 receiver_ty: &Type,
2608 name: &Ident,
2609) -> Result<Option<(HostPropertyGetter, u32)>, crate::compiler_error::CompilerFailure> {
2610 let Some(iface) = crate::codegen::host_property_iface(receiver_ty) else {
2611 return Ok(None);
2612 };
2613 let setter_key = crate::mangle::extend(&iface, &crate::mangle::property_setter(&name.name));
2614 let Some(setter) = ctx.symbols.func_idx(&setter_key) else {
2615 return Ok(None);
2616 };
2617 if let Some(struct_idx) = inline_length_struct_idx(ctx, &iface, &name.name)? {
2618 return Ok(Some((HostPropertyGetter::InlineLength(struct_idx), setter)));
2619 }
2620 let getter = ctx
2621 .symbols
2622 .func_idx(&crate::mangle::extend(&iface, &name.name))
2623 .ok_or_else(|| {
2624 crate::codegen::internal_failure("host property getter recorded beside its setter")
2625 })?;
2626 Ok(Some((HostPropertyGetter::Func(getter), setter)))
2627}
2628
2629fn emit_host_property_postfix(
2630 emitter: &mut FunctionEmitter,
2631 ctx: &CodegenCtx,
2632 receiver: ExprId,
2633 receiver_ty: &Type,
2634 (getter, setter): (HostPropertyGetter, u32),
2635 op: crate::PostfixOp,
2636 target_ty: &Type,
2637) -> Result<(), crate::compiler_error::CompilerFailure> {
2638 let recv = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
2640 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Number)?)?;
2641 emit_receiver(emitter, ctx, receiver)?;
2642 emitter.instruction(Instruction::LocalSet(recv));
2643 emitter.instruction(Instruction::LocalGet(recv));
2645 emitter.instruction(Instruction::RefAsNonNull);
2646 emitter.instruction(Instruction::LocalGet(recv));
2647 emitter.instruction(Instruction::RefAsNonNull);
2648 match getter {
2649 HostPropertyGetter::Func(getter) => {
2650 emitter.instruction(Instruction::Call(getter));
2651 }
2652 HostPropertyGetter::InlineLength(struct_idx) => {
2653 emit_inline_length(emitter, ctx, struct_idx);
2654 }
2655 }
2656 emitter.instruction(Instruction::LocalTee(old));
2657 emit_postfix_delta(emitter, ctx, op, &Type::Number);
2658 emitter.instruction(Instruction::Call(setter));
2659 emitter.instruction(Instruction::LocalGet(old));
2660 if target_ty != &Type::Number {
2661 cast::emit_box(emitter, ctx, &Type::Number)?;
2662 }
2663 Ok(())
2664}
2665
2666fn emit_index_postfix(
2667 emitter: &mut FunctionEmitter,
2668 ctx: &CodegenCtx,
2669 receiver: ExprId,
2670 index: ExprId,
2671 op: crate::PostfixOp,
2672) -> Result<(), crate::compiler_error::CompilerFailure> {
2673 let is_uint8 = ctx
2674 .ta
2675 .source_type(receiver)
2676 .map_err(crate::codegen::arena_failure)?
2677 .lowered()
2678 == &Type::Uint8Array;
2679 let result_ty = if is_uint8 {
2680 Type::Number
2681 } else {
2682 Type::Unknown
2683 };
2684 emit_expr(emitter, ctx, receiver)?;
2685 cast::emit_box(
2686 emitter,
2687 ctx,
2688 &ctx.ta
2689 .try_expr(receiver)
2690 .map_err(crate::codegen::arena_failure)?
2691 .ty,
2692 )?;
2693 let receiver_local = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
2694 emitter.instruction(Instruction::LocalSet(receiver_local));
2695 emit_expr(emitter, ctx, index)?;
2696 let key = emitter.add_anonymous_local(
2697 ctx.symbols.value_type(
2698 &ctx.ta
2699 .try_expr(index)
2700 .map_err(crate::codegen::arena_failure)?
2701 .ty,
2702 )?,
2703 )?;
2704 emitter.instruction(Instruction::LocalSet(key));
2705 let (backing, position, raw_type) = emit_index_location(
2706 emitter,
2707 ctx,
2708 receiver_local,
2709 key,
2710 &ctx.ta
2711 .try_expr(index)
2712 .map_err(crate::codegen::arena_failure)?
2713 .ty,
2714 is_uint8,
2715 &receiver_name(ctx.ta, receiver)?,
2716 IndexAccess::Read,
2717 )?;
2718 emitter.instruction(Instruction::LocalGet(backing));
2719 emitter.instruction(Instruction::LocalGet(position));
2720 if is_uint8 {
2721 emitter.instruction(Instruction::ArrayGetU(raw_type));
2722 emitter.instruction(Instruction::F64ConvertI32U);
2723 } else {
2724 emitter.instruction(Instruction::ArrayGet(raw_type));
2725 }
2726 emit_postfix_numeric(emitter, ctx, &result_ty);
2727 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2728 emitter.instruction(Instruction::LocalTee(old));
2729 emit_postfix_delta(emitter, ctx, op, &result_ty);
2730 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&result_ty)?)?;
2731 emitter.instruction(Instruction::LocalSet(updated));
2732 let (backing, position, _) = emit_index_location(
2735 emitter,
2736 ctx,
2737 receiver_local,
2738 key,
2739 &ctx.ta
2740 .try_expr(index)
2741 .map_err(crate::codegen::arena_failure)?
2742 .ty,
2743 is_uint8,
2744 &receiver_name(ctx.ta, receiver)?,
2745 if is_uint8 {
2746 IndexAccess::FixedWrite
2747 } else {
2748 IndexAccess::Write
2749 },
2750 )?;
2751 emitter.instruction(Instruction::LocalGet(backing));
2752 emitter.instruction(Instruction::LocalGet(position));
2753 emitter.instruction(Instruction::LocalGet(updated));
2754 if is_uint8 {
2755 emitter.instruction(Instruction::I32TruncSatF64S);
2756 }
2757 emitter.instruction(Instruction::ArraySet(raw_type));
2758 emitter.instruction(Instruction::LocalGet(old));
2759 Ok(())
2760}
2761
2762#[allow(clippy::too_many_arguments)]
2763fn emit_index_location(
2764 emitter: &mut FunctionEmitter,
2765 ctx: &CodegenCtx,
2766 receiver: u32,
2767 key: u32,
2768 key_ty: &Type,
2769 is_uint8: bool,
2770 receiver_name: &str,
2771 access: IndexAccess,
2772) -> Result<(u32, u32, u32), crate::compiler_error::CompilerFailure> {
2773 let (container, raw, ty) = if is_uint8 {
2774 (
2775 ctx.symbols
2776 .uint8_array_type_idx()
2777 .ok_or_else(|| crate::codegen::internal_failure("Uint8Array declared"))?,
2778 ctx.symbols
2779 .raw_uint8_array_type_idx()
2780 .ok_or_else(|| crate::codegen::internal_failure("raw Uint8Array declared"))?,
2781 Type::Uint8Array,
2782 )
2783 } else {
2784 (
2785 ctx.symbols
2786 .array_type_idx()
2787 .ok_or_else(|| crate::codegen::internal_failure("Array declared"))?,
2788 ctx.symbols
2789 .raw_array_type_idx()
2790 .ok_or_else(|| crate::codegen::internal_failure("raw Array declared"))?,
2791 Type::Array(Box::new(Type::Unknown)),
2792 )
2793 };
2794 emitter.instruction(Instruction::LocalGet(receiver));
2795 crate::codegen::cast_check::emit_operation_cast_on_stack(emitter, ctx, &Type::Unknown, &ty)?;
2796 emitter.instruction(Instruction::LocalGet(key));
2797 emit_index_number(emitter, ctx, key_ty)?;
2798 let operand = stash_index_operand(emitter)?;
2799 let length = (!is_uint8)
2800 .then(|| stash_array_length(emitter, container))
2801 .transpose()?;
2802 emitter.instruction(Instruction::StructGet {
2803 struct_type_index: container,
2804 field_index: 1,
2805 });
2806 let backing = emitter.add_anonymous_local(ValType::Ref(RefType {
2807 nullable: false,
2808 heap_type: HeapType::Concrete(raw),
2809 }))?;
2810 emitter.instruction(Instruction::LocalSet(backing));
2811 let position = match length {
2812 Some(length) => {
2813 emit_checked_index_with_length(emitter, ctx, length, operand, receiver_name, access)?
2814 }
2815 None => emit_checked_index(emitter, ctx, backing, operand, receiver_name, access)?,
2816 };
2817 Ok((backing, position, raw))
2818}
2819
2820fn emit_postfix_numeric(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, ty: &Type) {
2821 ctx.latch(emit_postfix_numeric_checked(emitter, ctx, ty));
2822}
2823
2824fn emit_postfix_numeric_checked(
2825 emitter: &mut FunctionEmitter,
2826 ctx: &CodegenCtx,
2827 ty: &Type,
2828) -> Result<(), crate::compiler_error::CompilerFailure> {
2829 if matches!(ty, Type::Unknown) {
2830 let function = ctx
2831 .symbols
2832 .prelude_func_idx("__value_numeric")
2833 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2834 emitter.instruction(Instruction::Call(function));
2835 }
2836
2837 Ok(())
2838}
2839
2840fn emit_postfix_delta(
2841 emitter: &mut FunctionEmitter<'_>,
2842 ctx: &CodegenCtx<'_>,
2843 op: crate::PostfixOp,
2844 ty: &Type,
2845) {
2846 ctx.latch(emit_postfix_delta_checked(emitter, ctx, op, ty));
2847}
2848
2849fn emit_postfix_delta_checked(
2850 emitter: &mut FunctionEmitter<'_>,
2851 ctx: &CodegenCtx<'_>,
2852 op: crate::PostfixOp,
2853 ty: &Type,
2854) -> Result<(), crate::compiler_error::CompilerFailure> {
2855 if matches!(ty, Type::Unknown) {
2856 let name = if matches!(op, crate::PostfixOp::Inc) {
2857 "inc"
2858 } else {
2859 "dec"
2860 };
2861 let function = ctx
2862 .symbols
2863 .prelude_func_idx(&format!("__value_{name}"))
2864 .ok_or_else(|| crate::codegen::internal_failure("dynamic update helper collected"))?;
2865 emitter.instruction(Instruction::Call(function));
2866 return Ok(());
2867 }
2868 if ty.is_bigint() {
2869 emit_bigint_pm_one(emitter, ctx, op);
2870 return Ok(());
2871 }
2872 emitter.instruction(Instruction::F64Const(Ieee64::from(1.0)));
2873 emitter.instruction(match op {
2874 crate::PostfixOp::Inc => Instruction::F64Add,
2875 crate::PostfixOp::Dec => Instruction::F64Sub,
2876 crate::PostfixOp::NonNullAssert => {
2877 return Err(crate::codegen::internal_failure(
2878 "non-null assertion is not PostfixUnary",
2879 ));
2880 }
2881 });
2882
2883 Ok(())
2884}
2885
2886#[allow(clippy::too_many_arguments)]
2891fn emit_class_field_postfix(
2892 emitter: &mut FunctionEmitter<'_>,
2893 ctx: &CodegenCtx<'_>,
2894 receiver: ExprId,
2895 mangled: &crate::MangledName,
2896 field: &str,
2897 op: crate::PostfixOp,
2898 target_ty: &Type,
2899) -> Result<(), crate::compiler_error::CompilerFailure> {
2900 let intrinsics = ctx
2901 .symbols
2902 .intrinsic_type_indices()
2903 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
2904 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
2905 crate::codegen::internal_failure("class struct type recorded in classes::emit")
2906 })?;
2907 let slot = ctx
2910 .symbols
2911 .class_field_slot(mangled, field)
2912 .ok_or_else(|| {
2913 crate::codegen::internal_failure(
2914 "data-field slot recorded in classes::emit — accessors are rejected in infer",
2915 )
2916 })?;
2917 let fields_local = emitter.add_anonymous_local(ValType::Ref(RefType {
2918 nullable: false,
2919 heap_type: HeapType::Concrete(intrinsics.object_fields),
2920 }))?;
2921 let old = emitter.add_anonymous_local(ctx.symbols.value_type(target_ty)?)?;
2922 let object = emitter.add_anonymous_local(
2923 ctx.symbols.value_type(
2924 ctx.ta
2925 .source_type(receiver)
2926 .map_err(crate::codegen::arena_failure)?,
2927 )?,
2928 )?;
2929 emit_receiver(emitter, ctx, receiver)?;
2930 emitter.instruction(Instruction::LocalTee(object));
2931 emitter.instruction(Instruction::StructGet {
2932 struct_type_index: struct_idx,
2933 field_index: 2,
2934 });
2935 emitter.instruction(Instruction::LocalSet(fields_local));
2936 emitter.instruction(Instruction::LocalGet(fields_local));
2937 emitter.instruction(Instruction::I32Const(slot as i32));
2938 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
2939 if ctx
2940 .symbols
2941 .class_field_narrowing_check(mangled, field)
2942 .is_some()
2943 {
2944 crate::codegen::field_guards::check(emitter, ctx, object, mangled, field)?;
2945 }
2946 emit_class_field_slot_cast(emitter, ctx, mangled, field, target_ty)?;
2947 emit_postfix_numeric(emitter, ctx, target_ty);
2948 emitter.instruction(Instruction::LocalSet(old));
2949 emitter.instruction(Instruction::LocalGet(fields_local));
2950 emitter.instruction(Instruction::I32Const(slot as i32));
2951 emitter.instruction(Instruction::LocalGet(old));
2952 emit_postfix_delta(emitter, ctx, op, target_ty);
2953 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, target_ty)?;
2954 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
2955 emitter.instruction(Instruction::LocalGet(old));
2956 Ok(())
2957}
2958
2959#[allow(clippy::too_many_arguments)]
2967fn emit_chain_parts(
2968 emitter: &mut FunctionEmitter<'_>,
2969 ctx: &CodegenCtx<'_>,
2970 base: ExprId,
2971 parts: &[crate::TypedChainPart],
2972 idx: usize,
2973 receiver_ty: &Type,
2974 result_ty: &Type,
2975 source_types: Option<&[Type]>,
2976 present_flag: Option<u32>,
2977) -> Result<(), crate::compiler_error::CompilerFailure> {
2978 if source_types.is_some_and(|types| parts.len().checked_add(1) != Some(types.len())) {
2979 return Err(crate::codegen::internal_failure(
2980 "optional-chain source type count mismatch",
2981 ));
2982 }
2983 if idx == parts.len() {
2984 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
2989 emitter,
2990 ctx,
2991 receiver_ty,
2992 result_ty,
2993 )?;
2994 return Ok(());
2995 }
2996 let part = parts.get(idx).ok_or_else(|| {
2997 crate::codegen::internal_failure("optional-chain part index is out of bounds")
2998 })?;
2999 let receiver_name = if matches!(part, crate::TypedChainPart::Index { .. }) {
3000 crate::codegen::bounds::chain_receiver_name(ctx.ta, base, &parts[..idx])?
3001 } else {
3002 String::new()
3003 };
3004 let next = idx
3005 .checked_add(1)
3006 .ok_or_else(|| crate::codegen::internal_failure("optional-chain index overflow"))?;
3007 let source_ty = source_types.and_then(|types| types.get(idx));
3008 let check_ty = source_types.and_then(|types| types.get(next));
3009 let saved_receiver = if matches!(
3010 part,
3011 crate::TypedChainPart::Field { .. }
3012 | crate::TypedChainPart::Index { .. }
3013 | crate::TypedChainPart::InterfaceProperty { .. }
3014 ) {
3015 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(receiver_ty)?)?;
3016 emitter.instruction(Instruction::LocalTee(slot));
3017 Some(slot)
3018 } else if matches!(
3019 part,
3020 crate::TypedChainPart::NonNull { .. } | crate::TypedChainPart::Constraint { .. }
3021 ) {
3022 emitter.call_receiver
3023 } else {
3024 None
3025 };
3026 let part_result_ty = part.result_ty().clone();
3027 let receiver_may_be_nullish = {
3036 let ty = source_ty.unwrap_or(receiver_ty);
3037 crate::codegen::symbol_table::may_hold_null(ty)
3038 || ty.holds_undefined()
3039 || matches!(
3040 part,
3041 crate::TypedChainPart::Call { .. } | crate::TypedChainPart::MethodCall { .. }
3042 )
3043 };
3044 let recv_val = (part.is_optional() && receiver_may_be_nullish)
3045 .then(|| ctx.symbols.value_type(receiver_ty))
3046 .transpose()?;
3047 let _: () = if let Some(recv_val @ ValType::Ref(_)) = recv_val {
3053 let block_ty = BlockType::Result(ctx.symbols.value_type(result_ty)?);
3054 let tmp = emitter.add_anonymous_local(recv_val)?;
3055 emitter.instruction(Instruction::LocalTee(tmp));
3056 emit_is_nullish(emitter, ctx)?;
3057 emitter.emit_if(block_ty);
3058 if let Some(flag) = present_flag {
3059 set_present_flag(emitter, flag, false);
3060 }
3061 emit_undefined_for_chain_result(emitter, ctx, result_ty)?;
3062 emitter.emit_else();
3063 emitter.instruction(Instruction::LocalGet(tmp));
3064 emitter.instruction(Instruction::RefAsNonNull);
3065 emit_chain_operation(
3066 emitter,
3067 ctx,
3068 part,
3069 receiver_ty,
3070 source_ty,
3071 check_ty,
3072 &receiver_name,
3073 present_flag.filter(|_| next == parts.len()),
3074 )?;
3075 emitter.call_receiver = saved_receiver;
3076 emit_chain_parts(
3077 emitter,
3078 ctx,
3079 base,
3080 parts,
3081 next,
3082 &part_result_ty,
3083 result_ty,
3084 source_types,
3085 present_flag,
3086 )?;
3087 emitter.emit_end();
3088 } else {
3089 emit_chain_operation(
3090 emitter,
3091 ctx,
3092 part,
3093 receiver_ty,
3094 source_ty,
3095 check_ty,
3096 &receiver_name,
3097 present_flag.filter(|_| next == parts.len()),
3098 )?;
3099 emitter.call_receiver = saved_receiver;
3100 emit_chain_parts(
3101 emitter,
3102 ctx,
3103 base,
3104 parts,
3105 next,
3106 &part_result_ty,
3107 result_ty,
3108 source_types,
3109 present_flag,
3110 )?;
3111 };
3112 Ok(())
3113}
3114
3115#[allow(clippy::too_many_arguments)]
3116fn emit_chain_operation(
3117 emitter: &mut FunctionEmitter,
3118 ctx: &CodegenCtx,
3119 part: &crate::TypedChainPart,
3120 receiver_ty: &Type,
3121 source_ty: Option<&Type>,
3122 check_ty: Option<&Type>,
3123 receiver_name: &str,
3124 present_flag: Option<u32>,
3125) -> Result<(), crate::compiler_error::CompilerFailure> {
3126 if let crate::TypedChainPart::Constraint { result_ty, .. } = part {
3127 return cast::emit_coerce_to_slot(emitter, ctx, receiver_ty, result_ty);
3128 }
3129 if let crate::TypedChainPart::MethodCall {
3130 iface,
3131 name,
3132 result_ty,
3133 span,
3134 ..
3135 } = part
3136 && !crate::codegen::is_host_package(iface.as_str())
3137 && let Some(args) = ctx.ta.authored_call_arguments(*span)
3138 {
3139 cast::emit_box(emitter, ctx, receiver_ty)?;
3140 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
3141 cast::emit_cast_to(emitter, ctx, result_ty)?;
3142 return Ok(());
3143 }
3144 if receiver_ty == &Type::Unknown {
3145 match part {
3146 crate::TypedChainPart::MethodCall {
3147 iface,
3148 name,
3149 args,
3150 result_ty,
3151 span,
3152 ..
3153 } if crate::codegen::runtime_values::dynamic_member_interface(iface) => {
3154 let args = ctx.ta.authored_call_arguments(*span).unwrap_or(args);
3155 emit_live_member_on_stack(emitter, ctx, iface, &name.name, Some(args))?;
3156 return Ok(());
3157 }
3158 crate::TypedChainPart::InterfaceProperty { iface, name, .. }
3159 if crate::codegen::runtime_values::dynamic_member_interface(iface) =>
3160 {
3161 emit_live_member_on_stack(emitter, ctx, iface, &name.name, None)?;
3162 return Ok(());
3163 }
3164 _ => {}
3165 }
3166 }
3167 let operation_ty =
3168 crate::typechecker::infer::narrowing::strip_nullish(source_ty.unwrap_or(receiver_ty));
3169 if !matches!(
3170 part,
3171 crate::TypedChainPart::Index { .. }
3172 | crate::TypedChainPart::Call { .. }
3173 | crate::TypedChainPart::NonNull { .. }
3174 ) {
3175 let receiver_slot = ctx.symbols.value_type(receiver_ty)?;
3176 if receiver_slot == ctx.symbols.value_type(&operation_ty)? {
3177 if matches!(receiver_slot, ValType::Ref(_)) {
3178 cast::emit_cast_to(emitter, ctx, &operation_ty)?;
3179 }
3180 } else {
3181 crate::codegen::cast_check::emit_operation_cast_on_stack(
3182 emitter,
3183 ctx,
3184 receiver_ty,
3185 &operation_ty,
3186 )?;
3187 }
3188 }
3189 emit_chain_access(
3190 emitter,
3191 ctx,
3192 part,
3193 &operation_ty,
3194 check_ty.unwrap_or(part.result_ty()),
3195 receiver_name,
3196 present_flag,
3197 )?;
3198 Ok(())
3199}
3200
3201fn emit_live_member_on_stack(
3202 emitter: &mut FunctionEmitter,
3203 ctx: &CodegenCtx,
3204 iface: &crate::MangledName,
3205 name: &str,
3206 args: Option<&[ExprId]>,
3207) -> Result<(), crate::compiler_error::CompilerFailure> {
3208 super::emit_const_string_by_text(emitter, ctx, name)?;
3209 super::emit_const_string_by_text(emitter, ctx, iface.as_str())?;
3210 let helper = if args.is_some() {
3211 "__value_member"
3212 } else {
3213 "__value_property"
3214 };
3215 emitter.instruction(Instruction::Call(
3216 ctx.symbols
3217 .prelude_func_idx(helper)
3218 .ok_or_else(|| crate::codegen::internal_failure("live member helper collected"))?,
3219 ));
3220 let Some(args) = args else {
3221 return Ok(());
3222 };
3223 let intr = ctx
3224 .symbols
3225 .intrinsic_type_indices()
3226 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
3227 emitter.instruction(Instruction::GlobalGet(
3228 ctx.symbols
3229 .prelude_global_idx("array_vtable")
3230 .ok_or_else(|| crate::codegen::internal_failure("array vtable"))?,
3231 ));
3232 for &arg in args {
3233 emit_expr(emitter, ctx, arg)?;
3234 cast::emit_box(
3235 emitter,
3236 ctx,
3237 &ctx.ta
3238 .try_expr(arg)
3239 .map_err(crate::codegen::arena_failure)?
3240 .ty,
3241 )?;
3242 }
3243 emitter.instruction(Instruction::ArrayNewFixed {
3244 array_type_index: intr.raw_array,
3245 array_size: crate::codegen::wasm_u32(args.len())?,
3246 });
3247 emitter.instruction(Instruction::I32Const(i32::try_from(args.len()).map_err(
3248 |_| crate::codegen::internal_failure("argument array too large"),
3249 )?));
3250 emitter.instruction(Instruction::StructNew(intr.array));
3251 emitter.instruction(Instruction::Call(
3252 ctx.symbols
3253 .prelude_func_idx("__value_invoke")
3254 .ok_or_else(|| crate::codegen::internal_failure("live invocation collected"))?,
3255 ));
3256 Ok(())
3257}
3258
3259fn inline_length_struct_idx(
3262 ctx: &CodegenCtx<'_>,
3263 iface: &crate::MangledName,
3264 prop: &str,
3265) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
3266 let key = crate::mangle::extend(iface, prop);
3267 if !ctx.symbols.is_intrinsic_member(&key) {
3268 return Ok(None);
3269 }
3270 let index = if *iface == crate::mangle::prelude("String") && prop == "length" {
3271 ctx.symbols.string_type_idx()
3272 } else if *iface == crate::mangle::prelude("Array") && prop == "length" {
3273 ctx.symbols.array_type_idx()
3274 } else {
3275 return Err(crate::codegen::internal_failure(format!(
3276 "no inline lowering for intrinsic member `{}`",
3277 key.as_str()
3278 )));
3279 };
3280 index
3281 .map(Some)
3282 .ok_or_else(|| crate::codegen::internal_failure("length receiver intrinsic missing"))
3283}
3284
3285fn emit_inline_length(emitter: &mut FunctionEmitter<'_>, ctx: &CodegenCtx<'_>, struct_idx: u32) {
3287 let is_array = ctx.symbols.array_type_idx() == Some(struct_idx);
3288 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
3289 emitter.instruction(Instruction::StructGet {
3290 struct_type_index: struct_idx,
3291 field_index: if is_array { 2 } else { 1 },
3292 });
3293 if !is_array {
3294 emitter.instruction(Instruction::ArrayLen);
3295 }
3296 emitter.instruction(Instruction::F64ConvertI32U);
3297}
3298
3299fn emit_undefined_for_chain_result(
3301 emitter: &mut FunctionEmitter<'_>,
3302 ctx: &CodegenCtx<'_>,
3303 result_ty: &Type,
3304) -> Result<(), crate::compiler_error::CompilerFailure> {
3305 emit_undefined(emitter, ctx)?;
3306 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)
3307}
3308
3309fn emit_class_field_slot_cast(
3313 emitter: &mut FunctionEmitter,
3314 ctx: &CodegenCtx,
3315 class: &crate::MangledName,
3316 field: &str,
3317 result_ty: &Type,
3318) -> Result<(), crate::compiler_error::CompilerFailure> {
3319 emit_class_field_slot_cast_as(emitter, ctx, class, field, result_ty, result_ty)?;
3320 Ok(())
3321}
3322
3323fn emit_class_field_slot_cast_as(
3324 emitter: &mut FunctionEmitter<'_>,
3325 ctx: &CodegenCtx<'_>,
3326 class: &crate::MangledName,
3327 field: &str,
3328 result_ty: &Type,
3329 check_ty: &Type,
3330) -> Result<(), crate::compiler_error::CompilerFailure> {
3331 let _: () = match ctx.symbols.class_field_narrowing_check(class, field) {
3332 Some(check) => {
3333 crate::codegen::cast_check::emit_narrowed_field_read_as(
3334 emitter, ctx, check, result_ty, check_ty,
3335 )?;
3336 }
3337 None => crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?,
3338 };
3339 Ok(())
3340}
3341
3342fn emit_object_spread(
3343 emitter: &mut FunctionEmitter,
3344 ctx: &CodegenCtx,
3345 sources: &[TypedObjectMember],
3346 shape: &Type,
3347) -> Result<(), crate::compiler_error::CompilerFailure> {
3348 let intrinsics = ctx
3349 .symbols
3350 .intrinsic_type_indices()
3351 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
3352 let merge = ctx
3353 .symbols
3354 .prelude_func_idx("ObjectConstructor##spread")
3355 .ok_or_else(|| crate::codegen::internal_failure("spread helper collected"))?;
3356 let null_object = Instruction::RefNull(HeapType::Concrete(intrinsics.object));
3357 emitter.instruction(null_object.clone());
3358 for (index, source) in sources.iter().enumerate() {
3359 let final_shape = (index + 1 == sources.len()).then_some(shape);
3360 emit_receiver(emitter, ctx, source.expr_id())?;
3364 let narrowed_ty = ctx
3365 .ta
3366 .source_type(source.expr_id())
3367 .map_err(crate::codegen::arena_failure)?;
3368 let non_shape_source = if may_hold_non_shape(narrowed_ty) {
3373 Some(emit_object_source_test(
3374 emitter,
3375 ctx,
3376 narrowed_ty,
3377 intrinsics.object,
3378 intrinsics.object_shape,
3379 )?)
3380 } else {
3381 None
3382 };
3383 let source_local =
3384 stash_receiver_as_object_shape(emitter, narrowed_ty, intrinsics.object_shape)?;
3385 emitter.instruction(Instruction::LocalGet(source_local));
3386 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
3387 if matches!(source, TypedObjectMember::Spread { by_name: true, .. }) {
3388 emit_spread_mask(emitter, ctx, source_local, source.expr_id(), shape)?;
3389 } else {
3390 emitter.instruction(null_object.clone());
3391 }
3392 emitter.instruction(Instruction::Call(merge));
3393 if let Some(NonShapeSource {
3394 accumulator,
3395 opaque,
3396 }) = non_shape_source
3397 {
3398 emitter.emit_else();
3399 emitter.instruction(Instruction::LocalGet(accumulator));
3400 match opaque {
3403 Some(source) => emitter.instruction(Instruction::LocalGet(source)),
3404 None => emitter.instruction(null_object.clone()),
3405 }
3406 emit_spread_shape_argument(emitter, ctx, final_shape, &null_object);
3407 emitter.instruction(null_object.clone());
3408 emitter.instruction(Instruction::Call(merge));
3409 emitter.emit_end();
3410 }
3411 }
3412 Ok(())
3413}
3414
3415fn emit_spread_shape_argument(
3418 emitter: &mut FunctionEmitter,
3419 ctx: &CodegenCtx,
3420 final_shape: Option<&Type>,
3421 null_object: &Instruction<'static>,
3422) {
3423 match final_shape {
3424 Some(shape) => emit_spread_shape(emitter, ctx, shape),
3425 None => emitter.instruction(null_object.clone()),
3426 }
3427}
3428
3429fn may_hold_non_shape(ty: &Type) -> bool {
3432 match ty.lowered() {
3433 Type::Union(members) => members.iter().any(may_hold_non_shape),
3434 Type::Null
3435 | Type::Undefined
3436 | Type::Boolean
3437 | Type::BooleanLiteral(_)
3438 | Type::Number
3439 | Type::NumberLiteral(_)
3440 | Type::String
3441 | Type::StringLiteral(_)
3442 | Type::InterfaceRef { .. }
3443 | Type::Unknown => true,
3445 _ => false,
3446 }
3447}
3448
3449struct NonShapeSource {
3451 accumulator: u32,
3452 opaque: Option<u32>,
3455}
3456
3457fn emit_object_source_test(
3462 emitter: &mut FunctionEmitter,
3463 ctx: &CodegenCtx,
3464 source_ty: &Type,
3465 object: u32,
3466 object_shape: u32,
3467) -> Result<NonShapeSource, crate::compiler_error::CompilerFailure> {
3468 let source_type = match ctx.symbols.value_type(source_ty)? {
3469 ValType::Ref(reference) => ValType::Ref(RefType {
3470 nullable: true,
3471 ..reference
3472 }),
3473 other => other,
3474 };
3475 let opaque = source_type == object_ref(object);
3476 let source = emitter.add_anonymous_local(source_type)?;
3477 emitter.instruction(Instruction::LocalSet(source));
3478 let accumulator = emitter.add_anonymous_local(object_ref(object))?;
3479 emitter.instruction(Instruction::LocalSet(accumulator));
3480 emitter.instruction(Instruction::LocalGet(source));
3481 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
3482 object_shape,
3483 )));
3484 emitter.emit_if(BlockType::Result(ValType::Ref(RefType {
3485 nullable: false,
3486 heap_type: HeapType::Concrete(object_shape),
3487 })));
3488 emitter.instruction(Instruction::LocalGet(accumulator));
3489 emitter.instruction(Instruction::LocalGet(source));
3490 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
3491 object_shape,
3492 )));
3493 Ok(NonShapeSource {
3494 accumulator,
3495 opaque: opaque.then_some(source),
3496 })
3497}
3498
3499fn emit_spread_mask(
3503 emitter: &mut FunctionEmitter,
3504 ctx: &CodegenCtx,
3505 source: u32,
3506 source_expr: crate::ExprId,
3507 shape: &Type,
3508) -> Result<(), crate::compiler_error::CompilerFailure> {
3509 let checked = ctx
3510 .ta
3511 .spread_mask_fields
3512 .get(&source_expr)
3513 .ok_or_else(|| crate::codegen::internal_failure("by-name spread fields recorded"))?;
3514 let omitted = ctx.ta.spread_omitted_fields.get(&source_expr);
3517 let fields: Vec<(&String, Option<&Type>)> = checked
3518 .iter()
3519 .map(|(name, ty)| (name, Some(ty)))
3520 .chain(omitted.into_iter().flatten().map(|name| (name, None)))
3521 .collect();
3522 let intrinsics = ctx
3523 .symbols
3524 .intrinsic_type_indices()
3525 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
3526 let vtable = ctx
3527 .symbols
3528 .vtable_global_idx(shape)
3529 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
3530 emitter.instruction(Instruction::GlobalGet(vtable));
3531 for (name, _) in &fields {
3532 let global = ctx
3533 .symbols
3534 .field_name_string_global_idx(name)
3535 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
3536 emitter.instruction(Instruction::GlobalGet(global));
3537 }
3538 emitter.instruction(Instruction::ArrayNewFixed {
3539 array_type_index: intrinsics.field_names,
3540 array_size: crate::codegen::wasm_u32(fields.len())?,
3541 });
3542 for (name, ty) in fields.iter().copied() {
3543 let global = ctx
3544 .symbols
3545 .field_name_string_global_idx(name)
3546 .ok_or_else(|| crate::codegen::internal_failure("spread name collected"))?;
3547 let Some(ty) = ty else {
3548 emitter.instruction(Instruction::GlobalGet(global));
3549 continue;
3550 };
3551 let index = emitter.add_anonymous_local(ValType::I32)?;
3552 let value = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
3553 emit_object_field_index_by_name(emitter, ctx, source, global);
3554 emitter.instruction(Instruction::LocalTee(index));
3555 emitter.instruction(Instruction::I32Const(0));
3556 emitter.instruction(Instruction::I32GeS);
3557 emitter.emit_if(BlockType::Result(ValType::I32));
3558 emit_field_slot_get(emitter, intrinsics, source, index);
3559 emitter.instruction(Instruction::LocalSet(value));
3560 emit_field_holds_value_of(emitter, ctx, source, index, value, ty)?;
3561 emitter.emit_else();
3562 emitter.instruction(Instruction::I32Const(0));
3563 emitter.emit_end();
3564 emitter.emit_if(BlockType::Result(object_ref(intrinsics.object)));
3565 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intrinsics.object)));
3566 emitter.emit_else();
3567 emitter.instruction(Instruction::GlobalGet(global));
3568 emitter.emit_end();
3569 }
3570 emitter.instruction(Instruction::ArrayNewFixed {
3571 array_type_index: intrinsics.object_fields,
3572 array_size: crate::codegen::wasm_u32(fields.len())?,
3573 });
3574 emitter.instruction(Instruction::RefNull(HeapType::ANY));
3575 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
3576 Ok(())
3577}
3578
3579fn emit_spread_shape(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, shape: &Type) {
3582 ctx.latch(emit_spread_shape_checked(emitter, ctx, shape));
3583}
3584
3585fn emit_spread_shape_checked(
3586 emitter: &mut FunctionEmitter,
3587 ctx: &CodegenCtx,
3588 shape: &Type,
3589) -> Result<(), crate::compiler_error::CompilerFailure> {
3590 let Type::Object { fields, .. } = shape else {
3591 return Err(crate::codegen::internal_failure("structural spread shape"));
3592 };
3593 let intrinsics = ctx
3594 .symbols
3595 .intrinsic_type_indices()
3596 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
3597 let names: Vec<_> = fields
3598 .iter()
3599 .map(|(name, field)| crate::codegen::field_names::FieldName {
3600 name: name.clone(),
3601 optional: field.optional,
3602 is_accessor: false,
3603 is_method: false,
3604 is_private: false,
3605 })
3606 .collect();
3607 let names_global = ctx
3608 .symbols
3609 .field_names_global_idx(&names)
3610 .ok_or_else(|| crate::codegen::internal_failure("spread shape names collected"))?;
3611 let vtable = ctx
3612 .symbols
3613 .vtable_global_idx(shape)
3614 .ok_or_else(|| crate::codegen::internal_failure("spread shape vtable collected"))?;
3615 emitter.instruction(Instruction::GlobalGet(vtable));
3616 emitter.instruction(Instruction::GlobalGet(names_global));
3617 emitter.instruction(Instruction::I32Const(
3618 crate::codegen::wasm_u32(fields.len())? as i32,
3619 ));
3620 emitter.instruction(Instruction::ArrayNewDefault(intrinsics.object_fields));
3621 emitter.instruction(Instruction::RefNull(HeapType::ANY));
3622 emitter.instruction(Instruction::StructNew(intrinsics.object_shape));
3623
3624 Ok(())
3625}
3626
3627pub(super) fn stash_receiver_as_object_shape(
3643 emitter: &mut FunctionEmitter<'_>,
3644 receiver_ty: &Type,
3645 object_shape: u32,
3646) -> Result<u32, crate::compiler_error::CompilerFailure> {
3647 coerce_field_receiver_to_object_shape(emitter, receiver_ty, object_shape);
3648 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
3649 nullable: false,
3650 heap_type: HeapType::Concrete(object_shape),
3651 }))?;
3652 emitter.instruction(Instruction::LocalSet(rcv_local));
3653 Ok(rcv_local)
3654}
3655
3656fn coerce_field_receiver_to_object_shape(
3657 emitter: &mut FunctionEmitter<'_>,
3658 receiver_ty: &Type,
3659 object_shape: u32,
3660) {
3661 if crate::codegen::may_hold_builtin_value(receiver_ty) {
3662 emitter.instruction(Instruction::RefAsNonNull);
3663 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
3664 object_shape,
3665 )));
3666 }
3667}
3668
3669fn emit_chain_access(
3675 emitter: &mut FunctionEmitter<'_>,
3676 ctx: &CodegenCtx<'_>,
3677 part: &crate::TypedChainPart,
3678 receiver_ty: &Type,
3679 check_ty: &Type,
3680 receiver_name: &str,
3681 present_flag: Option<u32>,
3682) -> Result<(), crate::compiler_error::CompilerFailure> {
3683 let _: () = match part {
3684 crate::TypedChainPart::Constraint { result_ty, .. } => {
3685 cast::emit_coerce_to_slot(emitter, ctx, receiver_ty, result_ty)?;
3686 }
3687 crate::TypedChainPart::Field {
3688 name, result_ty, ..
3689 } => {
3690 if ctx.ta.has_string_index(receiver_ty) {
3691 let probe = stash_probed_record(emitter, ctx, check_ty, present_flag)?;
3692 let Some(symbol) = ctx.require(
3693 ctx.symbols.field_name_string_global_idx(&name.name),
3694 "property name collected",
3695 ) else {
3696 return Ok(());
3697 };
3698 emitter.instruction(Instruction::GlobalGet(symbol));
3699 let key = KeyOperand::Global(symbol);
3700 let Some(symbol) = ctx.require(
3701 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3702 "record read imported",
3703 ) else {
3704 return Ok(());
3705 };
3706 emitter.instruction(Instruction::Call(symbol));
3707 emit_checked_record_read(emitter, ctx, check_ty, key, probe, receiver_name)?;
3708 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3709 return Ok(());
3710 }
3711 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
3712 crate::codegen::internal_failure("intrinsics declared by codegen entry")
3713 })?;
3714 if let Type::ClassRef { mangled, .. } = receiver_ty.lowered() {
3718 if ctx.symbols.is_class_payload_method(mangled, &name.name) {
3719 emit_class_method_value(
3720 emitter,
3721 ctx,
3722 receiver_ty,
3723 &name.name,
3724 result_ty,
3725 check_ty,
3726 )?;
3727 return Ok(());
3728 }
3729 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
3730 let getter = crate::codegen::classes::accessor_getter_name(&name.name);
3731 emit_class_vtable_dispatch(emitter, ctx, mangled, &getter, &[], result_ty)?;
3732 return Ok(());
3733 };
3734 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
3735 crate::codegen::internal_failure("class struct type recorded in classes::emit")
3736 })?;
3737 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
3738 nullable: false,
3739 heap_type: HeapType::Concrete(struct_idx),
3740 }))?;
3741 emitter.instruction(Instruction::LocalTee(object));
3742 emitter.instruction(Instruction::StructGet {
3743 struct_type_index: struct_idx,
3744 field_index: 2,
3745 });
3746 emitter.instruction(Instruction::I32Const(slot as i32));
3747 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
3748 if ctx
3749 .symbols
3750 .class_field_narrowing_check(mangled, &name.name)
3751 .is_some()
3752 {
3753 crate::codegen::field_guards::check(emitter, ctx, object, mangled, &name.name)?;
3754 }
3755 emit_class_field_slot_cast_as(
3756 emitter, ctx, mangled, &name.name, result_ty, check_ty,
3757 )?;
3758 return Ok(());
3759 }
3760 let rcv_local =
3761 stash_receiver_as_object_shape(emitter, receiver_ty, intrinsics.object_shape)?;
3762 emit_object_property_read_as(emitter, ctx, rcv_local, &name.name, result_ty, check_ty)?;
3763 }
3764 crate::TypedChainPart::Index { idx, result_ty, .. } => {
3765 if receiver_ty.indexes_by_name() {
3766 let probe = stash_probed_record(emitter, ctx, check_ty, present_flag)?;
3767 emit_expr(emitter, ctx, *idx)?;
3768 crate::codegen::cast_check::emit_operation_cast_on_stack(
3769 emitter,
3770 ctx,
3771 &ctx.ta
3772 .try_expr(*idx)
3773 .map_err(crate::codegen::arena_failure)?
3774 .ty,
3775 &Type::String,
3776 )?;
3777 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
3778 emitter.instruction(Instruction::LocalTee(key));
3779 let Some(symbol) = ctx.require(
3780 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
3781 "record read imported",
3782 ) else {
3783 return Ok(());
3784 };
3785 emitter.instruction(Instruction::Call(symbol));
3786 emit_checked_record_read(
3787 emitter,
3788 ctx,
3789 check_ty,
3790 KeyOperand::Local(key),
3791 probe,
3792 receiver_name,
3793 )?;
3794 cast::emit_coerce_to_slot(emitter, ctx, check_ty, result_ty)?;
3795 } else if receiver_ty.lowered() == &Type::Uint8Array {
3796 emit_uint8_index_with_receiver_on_stack(
3797 emitter,
3798 ctx,
3799 *idx,
3800 check_ty.holds_undefined().then_some(result_ty),
3801 receiver_name,
3802 )?;
3803 if let Some(flag) = present_flag {
3804 set_present_flag_from_value(emitter, ctx, flag, result_ty)?;
3805 }
3806 } else if receiver_ty.is_string_shaped() {
3807 emit_string_index_with_receiver_on_stack(
3808 emitter,
3809 ctx,
3810 *idx,
3811 check_ty.holds_undefined().then_some(result_ty),
3812 receiver_name,
3813 )?;
3814 if let Some(flag) = present_flag {
3815 set_present_flag_from_value(emitter, ctx, flag, result_ty)?;
3816 }
3817 } else {
3818 let source_ty = match receiver_ty.lowered() {
3819 Type::Array(element) => element.as_ref(),
3820 Type::Tuple(elements) => match &ctx
3821 .ta
3822 .try_expr(*idx)
3823 .map_err(crate::codegen::arena_failure)?
3824 .kind
3825 {
3826 TypedExprKind::Number(index) => {
3827 elements.get(*index as usize).unwrap_or(result_ty)
3828 }
3829 _ => check_ty,
3830 },
3831 _ => check_ty,
3832 };
3833 let missing_gives_undefined =
3837 check_ty.holds_undefined() && !source_ty.holds_undefined();
3838 let source_ty = if missing_gives_undefined {
3839 result_ty
3840 } else {
3841 source_ty
3842 };
3843 emit_bounds_checked_index_with_receiver_on_stack(
3844 emitter,
3845 ctx,
3846 *idx,
3847 source_ty,
3848 MissingElement {
3849 gives_undefined: missing_gives_undefined
3850 || source_ty.holds_undefined()
3851 || is_optional_tuple_position(ctx, receiver_ty, *idx)?,
3852 throws_on_absent_group: crate::typechecker::infer::narrowing::strip_nullish(
3854 receiver_ty,
3855 )
3856 .is_regexp_match_array()
3857 && !check_ty.holds_undefined(),
3858 present_flag,
3859 },
3860 receiver_name,
3861 )?;
3862 if source_ty != result_ty {
3863 crate::codegen::cast_check::emit_checked_cast_on_stack(
3864 emitter, ctx, source_ty, result_ty,
3865 )?;
3866 }
3867 }
3868 }
3869 crate::TypedChainPart::InterfaceProperty { iface, name, .. } => {
3870 if let Some(struct_idx) = inline_length_struct_idx(ctx, iface, &name.name)? {
3881 emit_inline_length(emitter, ctx, struct_idx);
3882 return Ok(());
3883 }
3884 let key = crate::mangle::extend(iface, &name.name);
3885 let func_idx = ctx.symbols.func_idx(&key).ok_or_else(|| {
3886 crate::codegen::internal_failure(
3887 "interface property getter recorded during the dependency-import pass",
3888 )
3889 })?;
3890 if matches!(receiver_ty.lowered(), Type::InterfaceRef { .. }) {
3891 emitter.instruction(Instruction::RefAsNonNull);
3892 }
3893 emitter.instruction(Instruction::Call(func_idx));
3894 }
3895 crate::TypedChainPart::Call {
3896 args,
3897 union_signature,
3898 ..
3899 } => {
3900 if let Some(signature) = union_signature
3907 && crate::codegen::closure_coercions::is_union_call_cast(receiver_ty, signature)
3908 {
3909 crate::codegen::closure_coercions::emit_union_call_cast(emitter, ctx, signature)?;
3910 }
3911 emit_indirect_closure_call_with_receiver_on_stack(
3912 emitter,
3913 ctx,
3914 union_signature.as_ref().unwrap_or(receiver_ty),
3915 args,
3916 part.result_ty(),
3917 )?;
3918 }
3919 crate::TypedChainPart::MethodCall {
3920 iface,
3921 name,
3922 args,
3923 result_ty,
3924 ..
3925 } => {
3926 emit_method_call_with_receiver_on_stack(
3936 emitter,
3937 ctx,
3938 receiver_ty,
3939 iface,
3940 &name.name,
3941 args,
3942 None,
3943 &[],
3944 None,
3945 result_ty,
3946 )?;
3947 }
3948 crate::TypedChainPart::NonNull { result_ty, .. } => {
3949 crate::codegen::cast_check::emit_non_null_assert_on_stack(
3950 emitter,
3951 ctx,
3952 receiver_ty,
3953 result_ty,
3954 )?;
3955 }
3956 };
3957 Ok(())
3958}
3959
3960fn emit_captured_load(
3971 emitter: &mut FunctionEmitter,
3972 _ctx: &CodegenCtx,
3973 c: &crate::CapturedVar,
3974) -> Result<(), crate::compiler_error::CompilerFailure> {
3975 let slot = emitter.local_slot(&c.name.name).ok_or_else(|| {
3976 crate::codegen::internal_failure("captured binding is missing from the outer scope")
3977 })?;
3978 emitter.instruction(Instruction::LocalGet(slot));
3979 Ok(())
3980}
3981
3982fn emit_binary(
3983 emitter: &mut FunctionEmitter,
3984 ctx: &CodegenCtx,
3985 op: BinOp,
3986 lhs: ExprId,
3987 rhs: ExprId,
3988 result_ty: &Type,
3989) -> Result<(), crate::compiler_error::CompilerFailure> {
3990 let admits_never_operand = matches!(
3991 op,
3992 BinOp::Add
3993 | BinOp::Sub
3994 | BinOp::Mul
3995 | BinOp::Div
3996 | BinOp::Rem
3997 | BinOp::Pow
3998 | BinOp::Lt
3999 | BinOp::Gt
4000 | BinOp::Le
4001 | BinOp::Ge
4002 );
4003 if admits_never_operand && try_emit_unreachable_for_never_operand(emitter, ctx, &[lhs, rhs])? {
4004 return Ok(());
4005 }
4006 let _: () = match op {
4007 BinOp::BitAnd
4012 | BinOp::BitOr
4013 | BinOp::BitXor
4014 | BinOp::Shl
4015 | BinOp::Shr
4016 | BinOp::UnsignedShr => {
4017 if has_numeric_operands(ctx, &[lhs, rhs], result_ty)? {
4018 emit_number_bitwise(emitter, ctx, op, lhs, rhs)?;
4019 } else {
4020 let name = op.bitwise_name().ok_or_else(|| {
4021 crate::codegen::internal_failure("missing bitwise host operation")
4022 })?;
4023 emit_bitwise_host(emitter, ctx, name, &[lhs, rhs], result_ty)?;
4024 }
4025 }
4026 BinOp::Add => match &result_ty.widen_literal() {
4029 Type::Number => {
4030 emit_primitive_operand(emitter, ctx, lhs)?;
4031 emit_primitive_operand(emitter, ctx, rhs)?;
4032 emitter.instruction(Instruction::F64Add);
4033 }
4034 Type::String => {
4035 emit_primitive_operand(emitter, ctx, lhs)?;
4036 emit_primitive_operand(emitter, ctx, rhs)?;
4037 let idx = ctx
4038 .symbols
4039 .prelude_func_idx("string_concat")
4040 .ok_or_else(|| {
4041 crate::codegen::internal_failure("string_concat imported from prelude")
4042 })?;
4043 emitter.instruction(Instruction::Call(idx));
4044 }
4045 Type::BigInt => emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "add")?,
4047 other => {
4048 return Err(crate::codegen::internal_failure(format!(
4049 "typechecker rejects `+` for {other:?}"
4050 )));
4051 }
4052 },
4053 BinOp::Sub | BinOp::Mul | BinOp::Div => {
4054 if result_ty.is_bigint() {
4056 let name = match op {
4057 BinOp::Sub => "sub",
4058 BinOp::Mul => "mul",
4059 BinOp::Div => "div",
4060 _ => {
4061 return Err(crate::codegen::internal_failure(
4062 "invalid compiler emission dispatch",
4063 ));
4064 }
4065 };
4066 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, name)?;
4067 return Ok(());
4068 }
4069 if !matches!(result_ty, Type::Number) {
4070 return Err(crate::codegen::internal_failure(
4071 "numeric operator has a non-number result type",
4072 ));
4073 }
4074 emit_primitive_operand(emitter, ctx, lhs)?;
4075 emit_primitive_operand(emitter, ctx, rhs)?;
4076 let inst = match op {
4077 BinOp::Sub => Instruction::F64Sub,
4078 BinOp::Mul => Instruction::F64Mul,
4079 BinOp::Div => Instruction::F64Div,
4080 _ => {
4081 return Err(crate::codegen::internal_failure(
4082 "invalid compiler emission dispatch",
4083 ));
4084 }
4085 };
4086 emitter.instruction(inst);
4087 }
4088 BinOp::Pow => {
4089 if result_ty.is_bigint() {
4094 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "pow")?;
4095 return Ok(());
4096 }
4097 if !matches!(result_ty, Type::Number) {
4098 return Err(crate::codegen::internal_failure(
4099 "numeric operator has a non-number result type",
4100 ));
4101 }
4102 emit_primitive_operand(emitter, ctx, lhs)?;
4103 emit_primitive_operand(emitter, ctx, rhs)?;
4104 let idx = ctx
4105 .symbols
4106 .func_idx(&crate::runtime::prelude::math::math_key("pow"))
4107 .ok_or_else(|| crate::codegen::internal_failure("Math#pow host import recorded"))?;
4108 emitter.instruction(Instruction::Call(idx));
4109 }
4110 BinOp::Rem => {
4111 if result_ty.is_bigint() {
4113 emit_bigint_binop_inline(emitter, ctx, lhs, rhs, "mod")?;
4114 return Ok(());
4115 }
4116 let a = emitter.add_anonymous_local(ValType::F64)?;
4120 let b = emitter.add_anonymous_local(ValType::F64)?;
4121 emit_primitive_operand(emitter, ctx, lhs)?;
4122 emitter.instruction(Instruction::LocalSet(a));
4123 emit_primitive_operand(emitter, ctx, rhs)?;
4124 emitter.instruction(Instruction::LocalSet(b));
4125 emitter.instruction(Instruction::LocalGet(a));
4127 emitter.instruction(Instruction::LocalGet(a));
4128 emitter.instruction(Instruction::LocalGet(b));
4129 emitter.instruction(Instruction::F64Div);
4130 emitter.instruction(Instruction::F64Trunc);
4131 emitter.instruction(Instruction::LocalGet(b));
4132 emitter.instruction(Instruction::F64Mul);
4133 emitter.instruction(Instruction::F64Sub);
4134 }
4135 BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
4136 let operand_ty = ctx
4139 .ta
4140 .try_expr(lhs)
4141 .map_err(crate::codegen::arena_failure)?
4142 .ty
4143 .primitive_behavior()
4144 .clone();
4145 if operand_ty.is_bigint() {
4146 emit_bigint_cmp_inline(emitter, ctx, lhs, rhs, op)?;
4147 return Ok(());
4148 }
4149 emit_primitive_operand(emitter, ctx, lhs)?;
4150 emit_primitive_operand(emitter, ctx, rhs)?;
4151 if matches!(operand_ty, Type::String | Type::StringLiteral(_)) {
4152 let idx = ctx.symbols.prelude_func_idx("string_cmp").ok_or_else(|| {
4153 crate::codegen::internal_failure("string_cmp imported from prelude")
4154 })?;
4155 emitter.instruction(Instruction::Call(idx));
4156 emitter.instruction(Instruction::I32Const(0));
4157 emitter.instruction(match op {
4158 BinOp::Lt => Instruction::I32LtS,
4159 BinOp::Gt => Instruction::I32GtS,
4160 BinOp::Le => Instruction::I32LeS,
4161 BinOp::Ge => Instruction::I32GeS,
4162 _ => {
4163 return Err(crate::codegen::internal_failure(
4164 "invalid compiler emission dispatch",
4165 ));
4166 }
4167 });
4168 } else {
4169 emitter.instruction(match op {
4170 BinOp::Lt => Instruction::F64Lt,
4171 BinOp::Gt => Instruction::F64Gt,
4172 BinOp::Le => Instruction::F64Le,
4173 BinOp::Ge => Instruction::F64Ge,
4174 _ => {
4175 return Err(crate::codegen::internal_failure(
4176 "invalid compiler emission dispatch",
4177 ));
4178 }
4179 });
4180 }
4181 }
4182 BinOp::Eq | BinOp::NotEq => emit_equality(emitter, ctx, op, lhs, rhs)?,
4183 BinOp::And | BinOp::Or => emit_logical(emitter, ctx, op, lhs, rhs, result_ty)?,
4184 BinOp::In => emit_in_operator(emitter, ctx, lhs, rhs)?,
4185 BinOp::NullishCoalesce => {
4189 return Err(crate::codegen::internal_failure(
4190 "BinOp::NullishCoalesce should be lifted into TypedExprKind::NullishCoalesce by infer",
4191 ));
4192 }
4193 };
4194 Ok(())
4195}
4196
4197fn emit_equality(
4200 emitter: &mut FunctionEmitter,
4201 ctx: &CodegenCtx,
4202 op: BinOp,
4203 lhs: ExprId,
4204 rhs: ExprId,
4205) -> Result<(), crate::compiler_error::CompilerFailure> {
4206 let lhs_is_null_lit = matches!(
4213 ctx.ta
4214 .try_expr(lhs)
4215 .map_err(crate::codegen::arena_failure)?
4216 .kind,
4217 TypedExprKind::Null
4218 );
4219 let rhs_is_null_lit = matches!(
4220 ctx.ta
4221 .try_expr(rhs)
4222 .map_err(crate::codegen::arena_failure)?
4223 .kind,
4224 TypedExprKind::Null
4225 );
4226 if lhs_is_null_lit && rhs_is_null_lit {
4227 let v: i32 = if matches!(op, BinOp::Eq) { 1 } else { 0 };
4228 emitter.instruction(Instruction::I32Const(v));
4229 return Ok(());
4230 }
4231 if lhs_is_null_lit || rhs_is_null_lit {
4232 let non_null_side = if lhs_is_null_lit { rhs } else { lhs };
4233 let non_null_ty = ctx
4234 .ta
4235 .try_expr(non_null_side)
4236 .map_err(crate::codegen::arena_failure)?
4237 .ty
4238 .clone();
4239 let non_null_val = ctx.symbols.value_type(&non_null_ty)?;
4240 if matches!(non_null_val, ValType::F64 | ValType::I32) {
4248 emit_expr(emitter, ctx, non_null_side)?;
4249 emitter.instruction(Instruction::Drop);
4250 let v: i32 = if matches!(op, BinOp::NotEq) { 1 } else { 0 };
4251 emitter.instruction(Instruction::I32Const(v));
4252 return Ok(());
4253 }
4254 emit_expr(emitter, ctx, non_null_side)?;
4255 emitter.instruction(Instruction::RefIsNull);
4256 if matches!(op, BinOp::NotEq) {
4257 emitter.instruction(Instruction::I32Eqz);
4258 }
4259 return Ok(());
4260 }
4261 let lhs_ty = ctx
4262 .ta
4263 .try_expr(lhs)
4264 .map_err(crate::codegen::arena_failure)?
4265 .ty
4266 .clone();
4267 let rhs_ty = ctx
4268 .ta
4269 .try_expr(rhs)
4270 .map_err(crate::codegen::arena_failure)?
4271 .ty
4272 .clone();
4273 let operand_val = ctx.symbols.value_type(lhs_ty.primitive_behavior())?;
4274 if may_hold_null(&lhs_ty)
4280 || may_hold_null(&rhs_ty)
4281 || operand_val != ctx.symbols.value_type(rhs_ty.primitive_behavior())?
4282 {
4283 emit_boxed_eq(emitter, ctx, lhs, rhs, op)?;
4284 return Ok(());
4285 }
4286 if lhs_ty.is_bigint() {
4290 emit_bigint_cmp_eq_inline(emitter, ctx, lhs, rhs, op)?;
4291 return Ok(());
4292 }
4293 let string_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
4294 crate::codegen::internal_failure("string type registered with intrinsics")
4295 })?;
4296 match operand_val {
4302 ValType::F64 => {
4303 emit_primitive_operand(emitter, ctx, lhs)?;
4304 emit_primitive_operand(emitter, ctx, rhs)?;
4305 emitter.instruction(if matches!(op, BinOp::Eq) {
4306 Instruction::F64Eq
4307 } else {
4308 Instruction::F64Ne
4309 });
4310 }
4311 ValType::I32 => {
4312 emit_expr(emitter, ctx, lhs)?;
4313 emit_expr(emitter, ctx, rhs)?;
4314 emitter.instruction(if matches!(op, BinOp::Eq) {
4315 Instruction::I32Eq
4316 } else {
4317 Instruction::I32Ne
4318 });
4319 }
4320 ValType::Ref(RefType {
4321 heap_type: HeapType::Concrete(idx),
4322 ..
4323 }) if idx == string_idx => {
4324 emit_expr(emitter, ctx, lhs)?;
4325 emit_expr(emitter, ctx, rhs)?;
4326 let func = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
4327 crate::codegen::internal_failure("string_eq imported from prelude")
4328 })?;
4329 emitter.instruction(Instruction::Call(func));
4330 if matches!(op, BinOp::NotEq) {
4331 emitter.instruction(Instruction::I32Eqz);
4332 }
4333 }
4334 ValType::Ref(_) => {
4335 emit_expr(emitter, ctx, lhs)?;
4346 cast::emit_box(emitter, ctx, &lhs_ty)?;
4347 emit_expr(emitter, ctx, rhs)?;
4348 cast::emit_box(emitter, ctx, &rhs_ty)?;
4349 emit_vtable_equality(emitter, ctx, op);
4350 }
4351 other => {
4352 return Err(crate::codegen::internal_failure(format!(
4353 "typechecker rejects equality on Wasm value-type `{other:?}`"
4354 )));
4355 }
4356 }
4357 Ok(())
4358}
4359
4360fn emit_logical(
4361 emitter: &mut FunctionEmitter,
4362 ctx: &CodegenCtx,
4363 op: BinOp,
4364 lhs: ExprId,
4365 rhs: ExprId,
4366 result_ty: &Type,
4367) -> Result<(), crate::compiler_error::CompilerFailure> {
4368 let result_val = ctx.symbols.value_type(result_ty)?;
4374 let lhs_ty = ctx
4375 .ta
4376 .try_expr(lhs)
4377 .map_err(crate::codegen::arena_failure)?
4378 .ty
4379 .clone();
4380 let lhs_val = ctx.symbols.value_type(&lhs_ty)?;
4381 let lhs_is_ref = matches!(lhs_val, ValType::Ref(_));
4382 let tmp = emitter.add_anonymous_local(lhs_val)?;
4383 emit_expr(emitter, ctx, lhs)?;
4384 emitter.instruction(Instruction::LocalTee(tmp));
4385 crate::codegen::function_emitter::cast::emit_condition_to_i32(emitter, ctx, &lhs_ty)?;
4386 let emit_rhs_branch =
4387 |emitter: &mut FunctionEmitter| -> Result<(), crate::compiler_error::CompilerFailure> {
4388 let rhs_ty = ctx
4389 .ta
4390 .try_expr(rhs)
4391 .map_err(crate::codegen::arena_failure)?
4392 .ty
4393 .clone();
4394 emit_expr(emitter, ctx, rhs)?;
4395 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
4396 emitter, ctx, &rhs_ty, result_ty,
4397 )?;
4398 Ok(())
4399 };
4400 let kept_lhs_ty = match op {
4404 BinOp::And => falsy_part(&lhs_ty),
4405 _ => truthy_part(&lhs_ty),
4406 };
4407 let lhs_is_never_kept = matches!(kept_lhs_ty.lowered(), Type::Never);
4408 let emit_kept_lhs_branch = |emitter: &mut FunctionEmitter| {
4409 if lhs_is_never_kept {
4410 emitter.instruction(Instruction::Unreachable);
4411 return Ok(());
4412 }
4413 emitter.instruction(Instruction::LocalGet(tmp));
4414 if lhs_is_ref {
4415 crate::codegen::function_emitter::cast::emit_cast_to(emitter, ctx, result_ty)?;
4419 } else {
4420 crate::codegen::function_emitter::cast::emit_coerce_to_slot(
4421 emitter, ctx, &lhs_ty, result_ty,
4422 )?;
4423 }
4424 Ok::<(), crate::compiler_error::CompilerFailure>(())
4425 };
4426 emitter.emit_if(BlockType::Result(result_val));
4427 match op {
4428 BinOp::And => emit_rhs_branch(emitter)?,
4429 _ => emit_kept_lhs_branch(emitter)?,
4430 }
4431 emitter.emit_else();
4432 match op {
4433 BinOp::And => emit_kept_lhs_branch(emitter)?,
4434 _ => emit_rhs_branch(emitter)?,
4435 }
4436 emitter.emit_end();
4437 Ok(())
4438}
4439
4440fn try_emit_unreachable_for_never_operand(
4445 emitter: &mut FunctionEmitter,
4446 ctx: &CodegenCtx,
4447 operands: &[ExprId],
4448) -> Result<bool, crate::compiler_error::CompilerFailure> {
4449 let mut has_never_operand = false;
4450 for &operand in operands {
4451 let ty = &ctx
4452 .ta
4453 .try_expr(operand)
4454 .map_err(crate::codegen::arena_failure)?
4455 .ty;
4456 has_never_operand |= matches!(ty.lowered(), Type::Never);
4457 }
4458 if !has_never_operand {
4459 return Ok(false);
4460 }
4461 for &operand in operands {
4462 emit_expr(emitter, ctx, operand)?;
4463 emitter.instruction(Instruction::Drop);
4464 }
4465 emitter.instruction(Instruction::Unreachable);
4466 Ok(true)
4467}
4468
4469fn emit_primitive_operand(
4470 emitter: &mut FunctionEmitter,
4471 ctx: &CodegenCtx,
4472 expr: ExprId,
4473) -> Result<(), crate::compiler_error::CompilerFailure> {
4474 emit_expr(emitter, ctx, expr)?;
4475 let ty = &ctx
4476 .ta
4477 .try_expr(expr)
4478 .map_err(crate::codegen::arena_failure)?
4479 .ty;
4480 let _: () = if matches!(ty.primitive_behavior(), Type::Number)
4481 && ctx.symbols.value_type(ty)? != ValType::F64
4482 {
4483 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
4484 };
4485 Ok(())
4486}
4487
4488fn emit_in_operator(
4496 emitter: &mut FunctionEmitter,
4497 ctx: &CodegenCtx,
4498 lhs: ExprId,
4499 rhs: ExprId,
4500) -> Result<(), crate::compiler_error::CompilerFailure> {
4501 emit_expr(emitter, ctx, lhs)?;
4502 crate::codegen::cast_check::emit_operation_cast_on_stack(
4503 emitter,
4504 ctx,
4505 &ctx.ta
4506 .try_expr(lhs)
4507 .map_err(crate::codegen::arena_failure)?
4508 .ty,
4509 &Type::String,
4510 )?;
4511 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
4512 emitter.instruction(Instruction::LocalSet(key));
4513 emit_expr(emitter, ctx, rhs)?;
4514 cast::emit_box(
4515 emitter,
4516 ctx,
4517 &ctx.ta
4518 .try_expr(rhs)
4519 .map_err(crate::codegen::arena_failure)?
4520 .ty,
4521 )?;
4522 emitter.instruction(Instruction::LocalGet(key));
4523 let Some(symbol) = ctx.require(
4524 ctx.symbols.prelude_func_idx("ObjectConstructor##hasField"),
4525 "presence helper imported",
4526 ) else {
4527 return Ok(());
4528 };
4529 emitter.instruction(Instruction::Call(symbol));
4530 Ok(())
4531}
4532
4533pub(crate) fn emit_class_field_setup(
4538 emitter: &mut FunctionEmitter,
4539 ctx: &CodegenCtx,
4540 mangled: &crate::MangledName,
4541) -> Result<(), crate::compiler_error::CompilerFailure> {
4542 emit_class_field_setup_phase(emitter, ctx, mangled, FieldSetupPhase::All)
4543}
4544
4545#[derive(Clone, Copy)]
4546pub(crate) enum FieldSetupPhase {
4547 All,
4548 Initializers,
4549 ParameterProperties,
4550}
4551
4552pub(crate) fn emit_class_field_setup_phase(
4553 emitter: &mut FunctionEmitter,
4554 ctx: &CodegenCtx,
4555 mangled: &crate::MangledName,
4556 phase: FieldSetupPhase,
4557) -> Result<(), crate::compiler_error::CompilerFailure> {
4558 use crate::codegen::symbol_table::FieldSetup;
4559 let setup: Vec<FieldSetup> = match ctx.symbols.class_field_setup(mangled) {
4560 Some(s) if !s.is_empty() => s.to_vec(),
4561 _ => return Ok(()),
4562 };
4563 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
4564 crate::codegen::internal_failure("class struct type recorded in classes::emit")
4565 })?;
4566 let intrinsics = ctx
4567 .symbols
4568 .intrinsic_type_indices()
4569 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4570 let this = emitter.this_local().ok_or_else(|| {
4571 crate::codegen::internal_failure("field setup runs inside a constructor init fn")
4572 })?;
4573 for step in &setup {
4574 let parameter_property = matches!(step, FieldSetup::ParamCopy { .. });
4575 if matches!(phase, FieldSetupPhase::Initializers) && parameter_property
4576 || matches!(phase, FieldSetupPhase::ParameterProperties) && !parameter_property
4577 {
4578 continue;
4579 }
4580 let field = match step {
4581 FieldSetup::Init { field, .. }
4582 | FieldSetup::ParamCopy { field, .. }
4583 | FieldSetup::Reset { field } => field,
4584 };
4585 let slot = ctx
4586 .symbols
4587 .class_field_slot(mangled, field)
4588 .ok_or_else(|| {
4589 crate::codegen::internal_failure("class field slot recorded in classes::emit")
4590 })?;
4591 match step {
4592 FieldSetup::Init { value, .. } => {
4593 let value_ty = ctx
4594 .ta
4595 .try_expr(*value)
4596 .map_err(crate::codegen::arena_failure)?
4597 .ty
4598 .clone();
4599 emit_expr(emitter, ctx, *value)?;
4600 crate::codegen::function_emitter::cast::emit_box(emitter, ctx, &value_ty)?;
4601 }
4602 FieldSetup::ParamCopy { param, .. } => {
4603 let local = emitter.require_local_slot(¶m.name.name)?;
4604 if param.boxed {
4605 let box_type = ctx.symbols.box_type_idx(¶m.ty)?.ok_or_else(|| {
4606 crate::codegen::internal_failure(
4607 "constructor parameter cell is not registered",
4608 )
4609 })?;
4610 super::binding_cells::read(emitter, ctx, local, box_type)?;
4611 cast::emit_unerase(emitter, ctx, ¶m.ty)?;
4612 } else {
4613 emitter.instruction(Instruction::LocalGet(local));
4614 }
4615 cast::emit_box(emitter, ctx, ¶m.ty)?;
4616 }
4617 FieldSetup::Reset { .. } => {
4618 emit_undefined(emitter, ctx)?;
4619 }
4620 }
4621 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
4622 emitter.instruction(Instruction::LocalSet(stored));
4623 emitter.instruction(Instruction::LocalGet(this));
4624 emitter.instruction(Instruction::StructGet {
4625 struct_type_index: struct_idx,
4626 field_index: 2,
4627 });
4628 emitter.instruction(Instruction::I32Const(slot as i32));
4629 emitter.instruction(Instruction::LocalGet(stored));
4630 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
4631 let index = emitter.add_anonymous_local(ValType::I32)?;
4632 emitter.instruction(Instruction::I32Const(slot as i32));
4633 emitter.instruction(Instruction::LocalSet(index));
4634 crate::codegen::field_names::emit_mark_present(emitter, ctx, this, index)?;
4635 }
4636 Ok(())
4637}
4638
4639pub(crate) fn emit_field_slot_get(
4643 emitter: &mut FunctionEmitter,
4644 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
4645 object_local: u32,
4646 index_local: u32,
4647) {
4648 emitter.instruction(Instruction::LocalGet(object_local));
4649 emitter.instruction(Instruction::StructGet {
4650 struct_type_index: intrinsics.object_shape,
4651 field_index: 2,
4652 });
4653 emitter.instruction(Instruction::LocalGet(index_local));
4654 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
4655}
4656
4657pub(crate) fn emit_object_field_read_by_name(
4658 emitter: &mut FunctionEmitter,
4659 ctx: &CodegenCtx,
4660 object_local: u32,
4661 name_global: u32,
4662) {
4663 ctx.latch(emit_object_field_read_by_name_checked(
4664 emitter,
4665 ctx,
4666 object_local,
4667 name_global,
4668 ));
4669}
4670
4671fn emit_object_field_read_by_name_checked(
4672 emitter: &mut FunctionEmitter,
4673 ctx: &CodegenCtx,
4674 object_local: u32,
4675 name_global: u32,
4676) -> Result<(), crate::compiler_error::CompilerFailure> {
4677 let intrinsics = ctx
4678 .symbols
4679 .intrinsic_type_indices()
4680 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4681 let Some(index_local) = ctx.latch(emitter.add_anonymous_local(ValType::I32)) else {
4682 return Ok(());
4683 };
4684 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4685 emitter.instruction(Instruction::LocalSet(index_local));
4686 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
4687 nullable: true,
4688 heap_type: HeapType::Concrete(intrinsics.object),
4689 })));
4690 emitter.instruction(Instruction::LocalGet(index_local));
4691 emitter.instruction(Instruction::I32Const(0));
4692 emitter.instruction(Instruction::I32LtS);
4693 emitter.emit_if(BlockType::Empty);
4694 emit_undefined(emitter, ctx)?;
4695 emitter.instruction(Instruction::Br(1));
4696 emitter.emit_end();
4697 emitter.instruction(Instruction::LocalGet(object_local));
4698 emitter.instruction(Instruction::StructGet {
4699 struct_type_index: intrinsics.object_shape,
4700 field_index: 2,
4701 });
4702 emitter.instruction(Instruction::LocalGet(index_local));
4703 emitter.instruction(Instruction::ArrayGet(intrinsics.object_fields));
4704 emitter.emit_end();
4705
4706 Ok(())
4707}
4708
4709pub(crate) fn emit_object_field_write_by_name(
4710 emitter: &mut FunctionEmitter,
4711 ctx: &CodegenCtx,
4712 object_local: u32,
4713 name_global: u32,
4714) -> Result<(), crate::compiler_error::CompilerFailure> {
4715 let intrinsics = ctx
4716 .symbols
4717 .intrinsic_type_indices()
4718 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4719 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
4720 nullable: true,
4721 heap_type: HeapType::Concrete(intrinsics.object),
4722 }))?;
4723 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4724 emitter.instruction(Instruction::LocalSet(value_local));
4725 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4726 emitter.instruction(Instruction::LocalTee(index_local));
4727 emitter.instruction(Instruction::I32Const(0));
4728 emitter.instruction(Instruction::I32GeS);
4729 emitter.emit_if(BlockType::Empty);
4730 emitter.instruction(Instruction::LocalGet(object_local));
4731 emitter.instruction(Instruction::StructGet {
4732 struct_type_index: intrinsics.object_shape,
4733 field_index: 2,
4734 });
4735 emitter.instruction(Instruction::LocalGet(index_local));
4736 emitter.instruction(Instruction::LocalGet(value_local));
4737 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
4738 crate::codegen::field_names::emit_mark_present(emitter, ctx, object_local, index_local)?;
4739 emitter.emit_else();
4740 emitter.instruction(Instruction::LocalGet(object_local));
4741 emitter.instruction(Instruction::GlobalGet(name_global));
4742 emitter.instruction(Instruction::LocalGet(value_local));
4743 let insert = ctx
4744 .symbols
4745 .prelude_func_idx("ObjectConstructor##insertField")
4746 .ok_or_else(|| {
4747 crate::codegen::internal_failure("field insertion helper is not imported")
4748 })?;
4749 emitter.instruction(Instruction::Call(insert));
4750 emitter.emit_end();
4751 Ok(())
4752}
4753
4754pub(crate) fn accessor_branch_emittable(ctx: &CodegenCtx, getter_name: &str) -> bool {
4761 ctx.symbols
4762 .field_name_string_global_idx(getter_name)
4763 .is_some()
4764}
4765
4766pub(crate) fn emit_is_accessor_backed(
4783 emitter: &mut FunctionEmitter,
4784 ctx: &CodegenCtx,
4785 object_local: u32,
4786 accessor_name: &str,
4787 kind: crate::AccessorKind,
4788) -> Result<u32, crate::compiler_error::CompilerFailure> {
4789 let intrinsics = ctx
4790 .symbols
4791 .intrinsic_type_indices()
4792 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4793 let accessor_global = ctx
4794 .symbols
4795 .field_name_string_global_idx(accessor_name)
4796 .ok_or_else(|| {
4797 crate::codegen::internal_failure("caller checked the accessor name has a string global")
4798 })?;
4799 let closure_struct_idx = ctx
4800 .symbols
4801 .closure_struct_type_idx(crate::codegen::classes::accessor_closure_sig(kind))
4802 .ok_or_else(|| {
4803 crate::codegen::internal_failure(
4804 "accessor closure struct collected by analysis::note_shaped_property_access",
4805 )
4806 })?;
4807 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4808 emit_field_index_by_name(emitter, ctx, object_local, accessor_global, true);
4809 emitter.instruction(Instruction::LocalTee(index_local));
4810 emitter.instruction(Instruction::I32Const(0));
4811 emitter.instruction(Instruction::I32GeS);
4812 emitter.instruction(Instruction::LocalGet(object_local));
4813 emitter.instruction(Instruction::StructGet {
4814 struct_type_index: intrinsics.object,
4815 field_index: 0,
4816 });
4817 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4818 intrinsics.class_vtable,
4819 )));
4820 emitter.instruction(Instruction::I32And);
4821 emitter.emit_if(BlockType::Result(ValType::I32));
4822 emitter.instruction(Instruction::LocalGet(object_local));
4823 emitter.instruction(Instruction::StructGet {
4824 struct_type_index: intrinsics.object_shape,
4825 field_index: 1,
4826 });
4827 emitter.instruction(Instruction::LocalGet(index_local));
4828 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
4829 crate::codegen::field_names::emit_name_is_accessor(emitter, ctx)?;
4830 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4831 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
4832 closure_struct_idx,
4833 )));
4834 emitter.instruction(Instruction::I32And);
4835 emitter.emit_else();
4836 emitter.instruction(Instruction::I32Const(0));
4837 emitter.emit_end();
4838 Ok(index_local)
4839}
4840
4841pub(crate) fn emit_object_property_read(
4851 emitter: &mut FunctionEmitter,
4852 ctx: &CodegenCtx,
4853 object_local: u32,
4854 prop_name: &str,
4855 field_ty: &Type,
4856) -> Result<(), crate::compiler_error::CompilerFailure> {
4857 emit_object_property_read_as(emitter, ctx, object_local, prop_name, field_ty, field_ty)?;
4858 Ok(())
4859}
4860
4861fn emit_object_property_read_as(
4862 emitter: &mut FunctionEmitter,
4863 ctx: &CodegenCtx,
4864 object_local: u32,
4865 prop_name: &str,
4866 field_ty: &Type,
4867 check_ty: &Type,
4868) -> Result<(), crate::compiler_error::CompilerFailure> {
4869 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
4870 let name_global = ctx
4871 .symbols
4872 .field_name_string_global_idx(prop_name)
4873 .ok_or_else(|| {
4874 crate::codegen::internal_failure(
4875 "per-name string global recorded during field-name-strings emission",
4876 )
4877 })?;
4878 if !accessor_branch_emittable(ctx, &getter) {
4879 emit_object_field_read_by_name(emitter, ctx, object_local, name_global);
4880 emit_dynamic_narrowed_property_read(
4881 emitter,
4882 ctx,
4883 object_local,
4884 prop_name,
4885 field_ty,
4886 check_ty,
4887 )?;
4888 return Ok(());
4889 }
4890 let intrinsics = ctx
4891 .symbols
4892 .intrinsic_type_indices()
4893 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4894 let result_vt = ctx.symbols.value_type(field_ty)?;
4895 let index_local = emitter.add_anonymous_local(ValType::I32)?;
4896 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
4897 emitter.instruction(Instruction::LocalSet(index_local));
4898 emitter.instruction(Instruction::LocalGet(index_local));
4899 emitter.instruction(Instruction::I32Const(0));
4900 emitter.instruction(Instruction::I32GeS);
4901 emitter.emit_if(BlockType::Result(result_vt));
4902 emit_field_slot_get(emitter, intrinsics, object_local, index_local);
4904 emit_dynamic_narrowed_property_read(emitter, ctx, object_local, prop_name, field_ty, check_ty)?;
4905 emitter.emit_else();
4906 let getter_slot_local = emit_is_accessor_backed(
4907 emitter,
4908 ctx,
4909 object_local,
4910 &getter,
4911 crate::AccessorKind::Get,
4912 )?;
4913 emitter.emit_if(BlockType::Result(result_vt));
4914 emitter.instruction(Instruction::LocalGet(object_local));
4916 emit_interface_method_via_shape_with_receiver_on_stack(
4917 emitter,
4918 ctx,
4919 &getter,
4920 &[],
4921 field_ty,
4922 Some(getter_slot_local),
4923 )?;
4924 emitter.emit_else();
4925 emit_undefined(emitter, ctx)?;
4931 cast::emit_cast_to(emitter, ctx, field_ty)?;
4932 emitter.emit_end();
4933 emitter.emit_end();
4934 Ok(())
4935}
4936
4937fn emit_dynamic_narrowed_property_read(
4938 emitter: &mut FunctionEmitter,
4939 ctx: &CodegenCtx,
4940 object_local: u32,
4941 prop_name: &str,
4942 field_ty: &Type,
4943 check_ty: &Type,
4944) -> Result<(), crate::compiler_error::CompilerFailure> {
4945 let checks: Vec<_> = ctx
4946 .symbols
4947 .class_field_narrowing_checks(prop_name)
4948 .filter_map(|(class, check)| {
4949 ctx.symbols
4950 .class_vtable_global_idx(class)
4951 .map(|vtable| (vtable, check))
4952 })
4953 .collect();
4954 if checks.is_empty() {
4955 cast::emit_cast_to(emitter, ctx, field_ty)?;
4956 return Ok(());
4957 }
4958
4959 let intrinsics = ctx
4960 .symbols
4961 .intrinsic_type_indices()
4962 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
4963 let raw = emitter.add_anonymous_local(ValType::Ref(RefType {
4964 nullable: true,
4965 heap_type: HeapType::Concrete(intrinsics.object),
4966 }))?;
4967 emitter.instruction(Instruction::LocalSet(raw));
4968 emit_dynamic_narrowing_check(
4969 emitter,
4970 ctx,
4971 object_local,
4972 raw,
4973 field_ty,
4974 check_ty,
4975 &checks,
4976 0,
4977 )?;
4978 Ok(())
4979}
4980
4981#[allow(clippy::too_many_arguments)]
4982fn emit_dynamic_narrowing_check(
4983 emitter: &mut FunctionEmitter,
4984 ctx: &CodegenCtx,
4985 object_local: u32,
4986 raw: u32,
4987 field_ty: &Type,
4988 check_ty: &Type,
4989 checks: &[(u32, &crate::FieldNarrowingCheck)],
4990 index: usize,
4991) -> Result<(), crate::compiler_error::CompilerFailure> {
4992 let Some((vtable, check)) = checks.get(index) else {
4993 emitter.instruction(Instruction::LocalGet(raw));
4994 cast::emit_cast_to(emitter, ctx, field_ty)?;
4995 return Ok(());
4996 };
4997 emitter.instruction(Instruction::LocalGet(object_local));
4998 crate::codegen::function_emitter::cast::emit_nominal_instance_test(emitter, ctx, *vtable)?;
4999 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(field_ty)?));
5000 emitter.instruction(Instruction::LocalGet(raw));
5001 crate::codegen::cast_check::emit_narrowed_field_read_as(
5002 emitter, ctx, check, field_ty, check_ty,
5003 )?;
5004 emitter.emit_else();
5005 emit_dynamic_narrowing_check(
5006 emitter,
5007 ctx,
5008 object_local,
5009 raw,
5010 field_ty,
5011 check_ty,
5012 checks,
5013 index + 1,
5014 )?;
5015 emitter.emit_end();
5016 Ok(())
5017}
5018
5019pub(crate) fn emit_object_property_write(
5025 emitter: &mut FunctionEmitter,
5026 ctx: &CodegenCtx,
5027 object_local: u32,
5028 prop: &Ident,
5029 value: ExprId,
5030) -> Result<(), crate::compiler_error::CompilerFailure> {
5031 emit_object_property_write_value(
5032 emitter,
5033 ctx,
5034 object_local,
5035 prop,
5036 &ShapeArgument::Expression(value),
5037 )?;
5038 Ok(())
5039}
5040
5041fn emit_object_property_write_value(
5042 emitter: &mut FunctionEmitter,
5043 ctx: &CodegenCtx,
5044 object_local: u32,
5045 prop: &Ident,
5046 value: &ShapeArgument,
5047) -> Result<(), crate::compiler_error::CompilerFailure> {
5048 let prop_name = prop.name.as_str();
5049 let setter = crate::codegen::classes::accessor_setter_name(prop_name);
5050 let getter = crate::codegen::classes::accessor_getter_name(prop_name);
5051 let has_setter_name = ctx.symbols.field_name_string_global_idx(&setter).is_some();
5052 let has_getter_name = ctx.symbols.field_name_string_global_idx(&getter).is_some();
5053 let name_global = ctx
5054 .symbols
5055 .field_name_string_global_idx(prop_name)
5056 .ok_or_else(|| {
5057 crate::codegen::internal_failure(
5058 "per-name string global recorded during field-name-strings emission",
5059 )
5060 })?;
5061 if !has_setter_name && !has_getter_name {
5065 let value_ty = value.ty(ctx)?;
5066 value.emit(emitter, ctx)?;
5067 cast::emit_box(emitter, ctx, &value_ty)?;
5068 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
5069 return Ok(());
5070 }
5071 let index_local = emitter.add_anonymous_local(ValType::I32)?;
5072 emit_object_field_index_by_name(emitter, ctx, object_local, name_global);
5073 emitter.instruction(Instruction::LocalSet(index_local));
5074 emitter.instruction(Instruction::LocalGet(index_local));
5075 emitter.instruction(Instruction::I32Const(0));
5076 emitter.instruction(Instruction::I32GeS);
5077 emitter.emit_if(BlockType::Empty);
5078 let value_ty = value.ty(ctx)?;
5081 value.emit(emitter, ctx)?;
5082 cast::emit_box(emitter, ctx, &value_ty)?;
5083 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
5084 emitter.emit_else();
5085 let arms: Vec<(&str, AccessorWrite)> = [
5090 has_setter_name.then_some((setter.as_str(), AccessorWrite::Setter)),
5091 has_getter_name.then_some((getter.as_str(), AccessorWrite::ReadOnly)),
5092 ]
5093 .into_iter()
5094 .flatten()
5095 .collect();
5096 for (accessor, arm) in &arms {
5097 let slot_index_local =
5098 emit_is_accessor_backed(emitter, ctx, object_local, accessor, arm.scanned_kind())?;
5099 emitter.emit_if(BlockType::Empty);
5100 match arm {
5101 AccessorWrite::Setter => {
5102 emitter.instruction(Instruction::LocalGet(object_local));
5103 emit_shape_method_with_arguments(
5104 emitter,
5105 ctx,
5106 accessor,
5107 std::slice::from_ref(value),
5108 &Type::Void,
5109 Some(slot_index_local),
5110 )?;
5111 emitter.instruction(Instruction::Drop);
5112 }
5113 AccessorWrite::ReadOnly => {
5116 value.emit(emitter, ctx)?;
5117 emitter.instruction(Instruction::Drop);
5118 emitter.record_span(prop.span);
5121 crate::codegen::throw::emit_type_error_throw(
5122 emitter,
5123 ctx,
5124 crate::codegen::throw::READ_ONLY_PROPERTY_MESSAGE,
5125 );
5126 }
5127 }
5128 emitter.emit_else();
5129 }
5130 let value_ty = value.ty(ctx)?;
5131 value.emit(emitter, ctx)?;
5132 cast::emit_box(emitter, ctx, &value_ty)?;
5133 emit_object_field_write_by_name(emitter, ctx, object_local, name_global)?;
5134 for _ in &arms {
5135 emitter.emit_end();
5136 }
5137 emitter.emit_end();
5138 Ok(())
5139}
5140
5141#[derive(Clone, Copy)]
5143struct MissingElement {
5144 gives_undefined: bool,
5146 throws_on_absent_group: bool,
5149 present_flag: Option<u32>,
5151}
5152
5153fn emit_bounds_checked_index_with_receiver_on_stack(
5167 emitter: &mut FunctionEmitter,
5168 ctx: &CodegenCtx,
5169 idx: ExprId,
5170 result_ty: &Type,
5171 missing: MissingElement,
5172 receiver_name: &str,
5173) -> Result<(), crate::compiler_error::CompilerFailure> {
5174 let MissingElement {
5175 gives_undefined: allow_missing,
5176 throws_on_absent_group: match_groups,
5177 present_flag,
5178 } = missing;
5179 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
5180 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
5181 })?;
5182 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
5183 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
5184 })?;
5185 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5186 nullable: false,
5187 heap_type: HeapType::Concrete(raw_array_idx),
5188 }))?;
5189 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5190 emitter.instruction(Instruction::LocalSet(receiver));
5191 emit_expr(emitter, ctx, idx)?;
5192 let key = emitter.add_anonymous_local(
5193 ctx.symbols.value_type(
5194 &ctx.ta
5195 .try_expr(idx)
5196 .map_err(crate::codegen::arena_failure)?
5197 .ty,
5198 )?,
5199 )?;
5200 emitter.instruction(Instruction::LocalSet(key));
5201 emitter.instruction(Instruction::LocalGet(receiver));
5202 crate::codegen::cast_check::emit_operation_cast_on_stack(
5203 emitter,
5204 ctx,
5205 &Type::Unknown,
5206 &Type::Array(Box::new(Type::Unknown)),
5207 )?;
5208 emitter.instruction(Instruction::LocalGet(key));
5209 emit_index_number(
5210 emitter,
5211 ctx,
5212 &ctx.ta
5213 .try_expr(idx)
5214 .map_err(crate::codegen::arena_failure)?
5215 .ty,
5216 )?;
5217 let idx_f64_local = stash_index_operand(emitter)?;
5218 let length = stash_array_length(emitter, array_idx)?;
5219 emitter.instruction(Instruction::StructGet {
5220 struct_type_index: array_idx,
5221 field_index: 1,
5222 });
5223 emitter.instruction(Instruction::LocalSet(raw_arr_local));
5224 if allow_missing {
5228 crate::codegen::bounds::emit_index_in_range(emitter, length, idx_f64_local);
5229 emitter.instruction(Instruction::I32Eqz);
5230 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
5231 if let Some(flag) = present_flag {
5232 set_present_flag(emitter, flag, false);
5233 }
5234 emit_undefined(emitter, ctx)?;
5235 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
5236 emitter.emit_else();
5237 }
5238 if let Some(flag) = present_flag {
5239 set_present_flag(emitter, flag, true);
5240 }
5241 let idx_local = emit_checked_index_with_length(
5242 emitter,
5243 ctx,
5244 length,
5245 idx_f64_local,
5246 receiver_name,
5247 IndexAccess::Read,
5248 )?;
5249 emitter.instruction(Instruction::LocalGet(raw_arr_local));
5250 emitter.instruction(Instruction::LocalGet(idx_local));
5251 emitter.instruction(Instruction::ArrayGet(raw_array_idx));
5252 if match_groups {
5255 crate::codegen::bounds::emit_group_participation_check(
5256 emitter,
5257 ctx,
5258 length,
5259 idx_f64_local,
5260 receiver_name,
5261 )?;
5262 }
5263 cast::emit_cast_to(emitter, ctx, result_ty)?;
5264 if allow_missing {
5265 emitter.emit_end();
5266 }
5267 Ok(())
5268}
5269
5270fn is_optional_tuple_position(
5271 ctx: &CodegenCtx<'_>,
5272 receiver: &Type,
5273 index: ExprId,
5274) -> Result<bool, crate::compiler_error::CompilerFailure> {
5275 let expression = ctx
5276 .ta
5277 .try_expr(index)
5278 .map_err(crate::codegen::arena_failure)?;
5279 let TypedExprKind::Number(index) = expression.kind else {
5280 return Ok(false);
5281 };
5282 Ok(index.fract() == 0.0 && tuple_permits_missing_index(receiver, index))
5283}
5284
5285fn tuple_permits_missing_index(receiver: &Type, index: f64) -> bool {
5289 match receiver.lowered() {
5290 Type::Tuple(elements) => index >= elements.required_len() as f64,
5291 Type::Union(members) => members
5292 .iter()
5293 .any(|member| tuple_permits_missing_index(member, index)),
5294 _ => false,
5295 }
5296}
5297
5298fn emit_uint8_index_with_receiver_on_stack(
5299 emitter: &mut FunctionEmitter,
5300 ctx: &CodegenCtx,
5301 index: ExprId,
5302 undefined_into: Option<&Type>,
5303 receiver_name: &str,
5304) -> Result<(), crate::compiler_error::CompilerFailure> {
5305 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
5306 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
5307 })?;
5308 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
5309 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
5310 })?;
5311 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5312 nullable: false,
5313 heap_type: HeapType::Concrete(raw_uint8_idx),
5314 }))?;
5315 let receiver = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5316 emitter.instruction(Instruction::LocalSet(receiver));
5317 emit_expr(emitter, ctx, index)?;
5318 let key = emitter.add_anonymous_local(
5319 ctx.symbols.value_type(
5320 &ctx.ta
5321 .try_expr(index)
5322 .map_err(crate::codegen::arena_failure)?
5323 .ty,
5324 )?,
5325 )?;
5326 emitter.instruction(Instruction::LocalSet(key));
5327 emitter.instruction(Instruction::LocalGet(receiver));
5328 crate::codegen::cast_check::emit_operation_cast_on_stack(
5329 emitter,
5330 ctx,
5331 &Type::Unknown,
5332 &Type::Uint8Array,
5333 )?;
5334 emitter.instruction(Instruction::LocalGet(key));
5335 emit_index_number(
5336 emitter,
5337 ctx,
5338 &ctx.ta
5339 .try_expr(index)
5340 .map_err(crate::codegen::arena_failure)?
5341 .ty,
5342 )?;
5343 let idx_f64_local = stash_index_operand(emitter)?;
5344 emitter.instruction(Instruction::StructGet {
5345 struct_type_index: uint8_idx,
5346 field_index: 1,
5347 });
5348 emitter.instruction(Instruction::LocalSet(raw_local));
5349 if let Some(result_ty) = undefined_into {
5351 let length = emitter.add_anonymous_local(ValType::I32)?;
5352 emitter.instruction(Instruction::LocalGet(raw_local));
5353 emitter.instruction(Instruction::ArrayLen);
5354 emitter.instruction(Instruction::LocalSet(length));
5355 crate::codegen::bounds::emit_index_in_range(emitter, length, idx_f64_local);
5356 emitter.instruction(Instruction::I32Eqz);
5357 emitter.emit_if(BlockType::Result(ctx.symbols.value_type(result_ty)?));
5358 emit_undefined(emitter, ctx)?;
5359 cast::emit_coerce_to_slot(emitter, ctx, &Type::Undefined, result_ty)?;
5360 emitter.emit_else();
5361 }
5362 let idx_local = emit_checked_index(
5363 emitter,
5364 ctx,
5365 raw_local,
5366 idx_f64_local,
5367 receiver_name,
5368 IndexAccess::Read,
5369 )?;
5370 emitter.instruction(Instruction::LocalGet(raw_local));
5371 emitter.instruction(Instruction::LocalGet(idx_local));
5372 emitter.instruction(Instruction::ArrayGetU(raw_uint8_idx));
5373 emitter.instruction(Instruction::F64ConvertI32U);
5374 if let Some(result_ty) = undefined_into {
5375 cast::emit_coerce_to_slot(emitter, ctx, &Type::Number, result_ty)?;
5376 emitter.emit_end();
5377 }
5378 Ok(())
5379}
5380
5381#[derive(Clone, Copy)]
5383enum AccessorWrite {
5384 Setter,
5385 ReadOnly,
5386}
5387
5388impl AccessorWrite {
5389 fn scanned_kind(self) -> crate::AccessorKind {
5392 match self {
5393 AccessorWrite::Setter => crate::AccessorKind::Set,
5394 AccessorWrite::ReadOnly => crate::AccessorKind::Get,
5395 }
5396 }
5397}
5398
5399pub(crate) fn emit_object_field_index_by_name(
5400 emitter: &mut FunctionEmitter,
5401 ctx: &CodegenCtx,
5402 object_local: u32,
5403 name_global: u32,
5404) {
5405 emit_field_index_by_name(emitter, ctx, object_local, name_global, false);
5406}
5407
5408fn emit_field_index_by_name(
5409 emitter: &mut FunctionEmitter,
5410 ctx: &CodegenCtx,
5411 object_local: u32,
5412 name_global: u32,
5413 accessor: bool,
5414) {
5415 ctx.latch(emit_field_index_by_name_checked(
5416 emitter,
5417 ctx,
5418 object_local,
5419 name_global,
5420 accessor,
5421 ));
5422}
5423
5424fn emit_field_index_by_name_checked(
5425 emitter: &mut FunctionEmitter,
5426 ctx: &CodegenCtx,
5427 object_local: u32,
5428 name_global: u32,
5429 accessor: bool,
5430) -> Result<(), crate::compiler_error::CompilerFailure> {
5431 emitter.instruction(Instruction::LocalGet(object_local));
5432 emitter.instruction(Instruction::GlobalGet(name_global));
5433 emitter.instruction(Instruction::I32Const(i32::from(accessor)));
5434 emitter.instruction(Instruction::Call(
5435 ctx.symbols
5436 .field_lookup_function
5437 .ok_or_else(|| crate::codegen::internal_failure("field lookup allocated"))?,
5438 ));
5439
5440 Ok(())
5441}
5442
5443fn emit_direct_call(
5449 emitter: &mut FunctionEmitter,
5450 ctx: &CodegenCtx,
5451 is_host: bool,
5452 wasm_idx: u32,
5453 params: &[Type],
5454 ret: &Type,
5455 args: &[ExprId],
5456) -> Result<(), crate::compiler_error::CompilerFailure> {
5457 if params.len() != args.len() {
5458 return Err(crate::codegen::internal_failure(
5459 "direct-call argument count mismatch",
5460 ));
5461 }
5462 let string_type_idx = ctx.symbols.string_type_idx().ok_or_else(|| {
5463 crate::codegen::internal_failure("$string type registered before call codegen")
5464 })?;
5465 let raw_string_type_idx = ctx.symbols.raw_string_type_idx().ok_or_else(|| {
5466 crate::codegen::internal_failure("$rawString type registered before call codegen")
5467 })?;
5468 let mut evaluated = Vec::with_capacity(args.len());
5469 for &arg in args {
5470 emit_expr(emitter, ctx, arg)?;
5471 let slot = emitter.add_anonymous_local(
5472 ctx.symbols.value_type(
5473 &ctx.ta
5474 .try_expr(arg)
5475 .map_err(crate::codegen::arena_failure)?
5476 .ty,
5477 )?,
5478 )?;
5479 emitter.instruction(Instruction::LocalSet(slot));
5480 evaluated.push(slot);
5481 }
5482 for (i, (&arg_id, local)) in args.iter().zip(evaluated).enumerate() {
5483 emitter.instruction(Instruction::LocalGet(local));
5484 if let Some(param_ty) = params.get(i) {
5490 let arg_ty = ctx
5491 .ta
5492 .try_expr(arg_id)
5493 .map_err(crate::codegen::arena_failure)?
5494 .ty
5495 .clone();
5496 let default = ctx
5497 .symbols
5498 .host_call_defaults(wasm_idx, args.len())
5499 .get(i)
5500 .and_then(Option::as_ref);
5501 crate::codegen::argument_defaults::emit_argument(
5502 emitter,
5503 ctx,
5504 &arg_ty,
5505 ctx.symbols.value_type(param_ty)?,
5506 default,
5507 )?;
5508 }
5509 if is_host
5514 && params
5515 .get(i)
5516 .is_some_and(|p| matches!(p.lowered(), Type::String))
5517 {
5518 emitter.instruction(Instruction::StructGet {
5519 struct_type_index: string_type_idx,
5520 field_index: 1,
5521 });
5522 }
5523 }
5524 emitter.instruction(Instruction::Call(wasm_idx));
5525 if ctx.symbols.resultless_functions.contains(&wasm_idx) {
5526 emit_undefined(emitter, ctx)?;
5527 }
5528 let _: () = if is_host && matches!(ret.lowered(), Type::String) {
5529 let scratch = emitter.add_anonymous_local(ValType::Ref(RefType {
5530 nullable: false,
5531 heap_type: HeapType::Concrete(raw_string_type_idx),
5532 }))?;
5533 emitter.instruction(Instruction::LocalSet(scratch));
5534 let vtable_global = ctx
5535 .symbols
5536 .prelude_global_idx("string_vtable")
5537 .ok_or_else(|| {
5538 crate::codegen::internal_failure("string_vtable imported from prelude")
5539 })?;
5540 emitter.instruction(Instruction::GlobalGet(vtable_global));
5541 emitter.instruction(Instruction::LocalGet(scratch));
5542 emitter.instruction(Instruction::I64Const(0));
5543 emitter.instruction(Instruction::StructNew(string_type_idx));
5544 };
5545 Ok(())
5546}
5547
5548fn emit_indirect_closure_call(
5557 emitter: &mut FunctionEmitter,
5558 ctx: &CodegenCtx,
5559 callee: ExprId,
5560 args: &[ExprId],
5561 result_ty: &Type,
5562) -> Result<(), crate::compiler_error::CompilerFailure> {
5563 let callee_ty = ctx
5564 .ta
5565 .source_type(callee)
5566 .map_err(crate::codegen::arena_failure)?
5567 .clone();
5568 let receiver = emit_callee(emitter, ctx, callee)?;
5569 emitter.call_receiver = receiver;
5570 emit_indirect_closure_call_with_receiver_on_stack(emitter, ctx, &callee_ty, args, result_ty)?;
5571 Ok(())
5572}
5573
5574fn emit_callee(
5576 emitter: &mut FunctionEmitter,
5577 ctx: &CodegenCtx,
5578 callee: ExprId,
5579) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
5580 let reference = reference_expression(ctx.ta, callee)?;
5581 let receiver = match &ctx
5582 .ta
5583 .try_expr(reference)
5584 .map_err(crate::codegen::arena_failure)?
5585 .kind
5586 {
5587 TypedExprKind::FieldAccess { receiver, .. }
5588 | TypedExprKind::IndexAccess { receiver, .. }
5589 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => Some(*receiver),
5590 _ => None,
5591 };
5592 let mark = emitter.single_evaluation_mark();
5593 let slot = receiver
5594 .map(|receiver| {
5595 emit_expr(emitter, ctx, receiver)?;
5596 let slot = emitter.add_anonymous_local(
5597 ctx.symbols.value_type(
5598 &ctx.ta
5599 .try_expr(receiver)
5600 .map_err(crate::codegen::arena_failure)?
5601 .ty,
5602 )?,
5603 )?;
5604 emitter.instruction(Instruction::LocalSet(slot));
5605 emitter.record_single_evaluation(receiver, slot)?;
5606 Ok::<_, crate::compiler_error::CompilerFailure>(slot)
5607 })
5608 .transpose()?;
5609 emit_expr(emitter, ctx, callee)?;
5610 emitter.end_single_evaluations(mark)?;
5611 Ok(
5612 if matches!(
5613 ctx.ta
5614 .try_expr(reference)
5615 .map_err(crate::codegen::arena_failure)?
5616 .kind,
5617 TypedExprKind::OptionalChain { .. }
5618 ) {
5619 emitter.call_receiver.take()
5620 } else {
5621 slot
5622 },
5623 )
5624}
5625
5626fn reference_expression(
5627 ast: &crate::TypedAst,
5628 mut id: ExprId,
5629) -> Result<ExprId, crate::compiler_error::CompilerFailure> {
5630 loop {
5631 match ast
5632 .try_expr(id)
5633 .map_err(crate::codegen::arena_failure)?
5634 .kind
5635 {
5636 TypedExprKind::Cast { value, .. } | TypedExprKind::NonNullAssert { value } => {
5637 id = value;
5638 }
5639 _ => return Ok(id),
5640 }
5641 }
5642}
5643
5644fn emit_indirect_closure_call_with_receiver_on_stack(
5650 emitter: &mut FunctionEmitter,
5651 ctx: &CodegenCtx,
5652 callee_ty: &Type,
5653 args: &[ExprId],
5654 result_ty: &Type,
5655) -> Result<(), crate::compiler_error::CompilerFailure> {
5656 let receiver = emitter.call_receiver.take();
5657 let callee = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5658 emitter.instruction(Instruction::LocalSet(callee));
5659 let mut arguments = Vec::with_capacity(args.len());
5660 for &arg in args {
5661 emit_expr(emitter, ctx, arg)?;
5662 cast::emit_box(
5663 emitter,
5664 ctx,
5665 &ctx.ta
5666 .try_expr(arg)
5667 .map_err(crate::codegen::arena_failure)?
5668 .ty,
5669 )?;
5670 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
5671 emitter.instruction(Instruction::LocalSet(slot));
5672 arguments.push(slot);
5673 }
5674 emitter.instruction(Instruction::LocalGet(callee));
5675 crate::codegen::cast_check::emit_operation_cast_on_stack(
5676 emitter,
5677 ctx,
5678 &Type::Unknown,
5679 callee_ty,
5680 )?;
5681 let callee_ty = callee_ty.lowered();
5686 let closure_sig = crate::codegen::closures::classify(callee_ty)?;
5687 let closure_struct_idx = ctx
5688 .symbols
5689 .closure_struct_type_idx(closure_sig)
5690 .ok_or_else(|| {
5691 crate::codegen::internal_failure(
5692 "closure struct type registered for every closure-typed callee",
5693 )
5694 })?;
5695 let fn_type_idx = ctx
5696 .symbols
5697 .closure_func_type_idx(closure_sig)
5698 .ok_or_else(|| {
5699 crate::codegen::internal_failure(
5700 "closure funcref type registered for every closure-typed callee",
5701 )
5702 })?;
5703 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
5704 nullable: false,
5705 heap_type: HeapType::Concrete(closure_struct_idx),
5706 }))?;
5707 emitter.instruction(Instruction::LocalTee(closure_local));
5708 emitter.instruction(Instruction::StructGet {
5709 struct_type_index: closure_struct_idx,
5710 field_index: 2,
5711 });
5712 if let Some(receiver) = receiver {
5713 crate::codegen::this_binding::bind(emitter, ctx, receiver)?;
5714 }
5715 for argument in arguments {
5723 emitter.instruction(Instruction::LocalGet(argument));
5724 }
5725 emitter.instruction(Instruction::LocalGet(closure_local));
5726 emitter.instruction(Instruction::StructGet {
5727 struct_type_index: closure_struct_idx,
5728 field_index: 1,
5729 });
5730 emitter.instruction(Instruction::CallRef(fn_type_idx));
5731 let Type::Function { ret, .. } = callee_ty.lowered() else {
5734 return Err(crate::codegen::internal_failure(
5735 "indirect call requires a function type",
5736 ));
5737 };
5738 let _: () = if !ret.is_void() {
5739 crate::codegen::cast_check::emit_checked_cast_on_stack(
5740 emitter,
5741 ctx,
5742 &Type::Unknown,
5743 result_ty,
5744 )?;
5745 };
5746 Ok(())
5747}
5748
5749fn emit_typeof_tag(
5752 emitter: &mut FunctionEmitter,
5753 ctx: &CodegenCtx,
5754 value: ExprId,
5755 tag: crate::TypeofTagKind,
5756) -> Result<(), crate::compiler_error::CompilerFailure> {
5757 let _: () = match tag {
5758 crate::TypeofTagKind::BigInt => {
5759 emit_expr(emitter, ctx, value)?;
5760 cast::emit_box(
5761 emitter,
5762 ctx,
5763 &ctx.ta
5764 .try_expr(value)
5765 .map_err(crate::codegen::arena_failure)?
5766 .ty,
5767 )?;
5768 let intrinsic = ctx
5769 .symbols
5770 .bigint_type_idx()
5771 .ok_or_else(|| crate::codegen::internal_failure("bigint intrinsic missing"))?;
5772 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(intrinsic)));
5773 }
5774 crate::TypeofTagKind::Undefined => {
5775 emit_expr(emitter, ctx, value)?;
5776 cast::emit_box(
5777 emitter,
5778 ctx,
5779 &ctx.ta
5780 .try_expr(value)
5781 .map_err(crate::codegen::arena_failure)?
5782 .ty,
5783 )?;
5784 emit_is_undefined(emitter, ctx)?;
5785 }
5786 crate::TypeofTagKind::Number => {
5787 let boxed = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
5793 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
5794 })?;
5795 emit_expr(emitter, ctx, value)?;
5796 cast::emit_box(
5797 emitter,
5798 ctx,
5799 &ctx.ta
5800 .try_expr(value)
5801 .map_err(crate::codegen::arena_failure)?
5802 .ty,
5803 )?;
5804 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5805 }
5806 crate::TypeofTagKind::String => {
5807 let string = ctx.symbols.string_type_idx().ok_or_else(|| {
5808 crate::codegen::internal_failure("$string intrinsic type registered")
5809 })?;
5810 emit_expr(emitter, ctx, value)?;
5811 cast::emit_box(
5812 emitter,
5813 ctx,
5814 &ctx.ta
5815 .try_expr(value)
5816 .map_err(crate::codegen::arena_failure)?
5817 .ty,
5818 )?;
5819 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
5820 }
5821 crate::TypeofTagKind::Boolean => {
5822 let boxed = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
5823 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
5824 })?;
5825 emit_expr(emitter, ctx, value)?;
5826 cast::emit_box(
5827 emitter,
5828 ctx,
5829 &ctx.ta
5830 .try_expr(value)
5831 .map_err(crate::codegen::arena_failure)?
5832 .ty,
5833 )?;
5834 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(boxed)));
5835 }
5836 crate::TypeofTagKind::Function => {
5837 let closure_idx = ctx.symbols.closure_type_idx().ok_or_else(|| {
5838 crate::codegen::internal_failure("$Closure intrinsic type registered")
5839 })?;
5840 emit_expr(emitter, ctx, value)?;
5841 cast::emit_box(
5842 emitter,
5843 ctx,
5844 &ctx.ta
5845 .try_expr(value)
5846 .map_err(crate::codegen::arena_failure)?
5847 .ty,
5848 )?;
5849 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(closure_idx)));
5850 }
5851 crate::TypeofTagKind::Object => {
5852 let scratch_ty = ctx.symbols.value_type(&Type::Unknown)?;
5853 let nullable = matches!(scratch_ty, ValType::Ref(rt) if rt.nullable);
5854 let scratch = emitter.add_anonymous_local(scratch_ty)?;
5855 emit_expr(emitter, ctx, value)?;
5856 cast::emit_box(
5857 emitter,
5858 ctx,
5859 &ctx.ta
5860 .try_expr(value)
5861 .map_err(crate::codegen::arena_failure)?
5862 .ty,
5863 )?;
5864 emitter.instruction(Instruction::LocalSet(scratch));
5865 emit_is_non_primitive(emitter, ctx, scratch, Closures::AreNotObjects)?;
5866 if nullable {
5867 emitter.instruction(Instruction::LocalGet(scratch));
5872 emitter.instruction(Instruction::RefIsNull);
5873 emitter.instruction(Instruction::I32Or);
5874 }
5875 }
5876 };
5877 Ok(())
5878}
5879
5880#[derive(Clone, Copy, PartialEq, Eq)]
5883pub(crate) enum Closures {
5884 AreObjects,
5885 AreNotObjects,
5886}
5887
5888pub(crate) fn emit_is_non_primitive(
5897 emitter: &mut FunctionEmitter,
5898 ctx: &CodegenCtx,
5899 local: u32,
5900 closures: Closures,
5901) -> Result<(), crate::compiler_error::CompilerFailure> {
5902 let intrinsics = ctx
5903 .symbols
5904 .intrinsic_type_indices()
5905 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
5906 emitter.instruction(Instruction::LocalGet(local));
5907 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
5908 intrinsics.object,
5909 )));
5910 let mut excluded = vec![
5911 intrinsics.string,
5912 intrinsics.boxed_number,
5913 intrinsics.boxed_boolean,
5914 intrinsics.bigint,
5915 intrinsics.undefined,
5916 ];
5917 if closures == Closures::AreNotObjects {
5918 excluded.push(intrinsics.closure);
5919 }
5920 for primitive in excluded {
5921 emitter.instruction(Instruction::LocalGet(local));
5922 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(primitive)));
5923 emitter.instruction(Instruction::I32Eqz);
5924 emitter.instruction(Instruction::I32And);
5925 }
5926 Ok(())
5927}
5928
5929fn has_numeric_operands(
5932 ctx: &CodegenCtx,
5933 operands: &[ExprId],
5934 result_ty: &Type,
5935) -> Result<bool, crate::compiler_error::CompilerFailure> {
5936 if !matches!(result_ty.lowered(), Type::Number | Type::NumberLiteral(_)) {
5937 return Ok(false);
5938 }
5939 for &operand in operands {
5940 let ty = &ctx
5941 .ta
5942 .try_expr(operand)
5943 .map_err(crate::codegen::arena_failure)?
5944 .ty;
5945 if !matches!(
5946 ty.primitive_behavior(),
5947 Type::Number | Type::NumberLiteral(_)
5948 ) {
5949 return Ok(false);
5950 }
5951 }
5952 Ok(true)
5953}
5954
5955fn emit_number_bitwise(
5956 emitter: &mut FunctionEmitter,
5957 ctx: &CodegenCtx,
5958 op: BinOp,
5959 lhs: ExprId,
5960 rhs: ExprId,
5961) -> Result<(), crate::compiler_error::CompilerFailure> {
5962 emit_primitive_operand(emitter, ctx, lhs)?;
5963 emit_to_int32(emitter, ctx)?;
5964 emit_primitive_operand(emitter, ctx, rhs)?;
5965 emit_to_int32(emitter, ctx)?;
5966 let (instruction, unsigned) = match op {
5968 BinOp::BitAnd => (Instruction::I32And, false),
5969 BinOp::BitOr => (Instruction::I32Or, false),
5970 BinOp::BitXor => (Instruction::I32Xor, false),
5971 BinOp::Shl => (Instruction::I32Shl, false),
5972 BinOp::Shr => (Instruction::I32ShrS, false),
5973 BinOp::UnsignedShr => (Instruction::I32ShrU, true),
5974 _ => {
5975 return Err(crate::codegen::internal_failure(
5976 "a numeric bitwise operation has a bitwise operator",
5977 ));
5978 }
5979 };
5980 emitter.instruction(instruction);
5981 emitter.instruction(if unsigned {
5982 Instruction::F64ConvertI32U
5983 } else {
5984 Instruction::F64ConvertI32S
5985 });
5986 Ok(())
5987}
5988
5989fn emit_to_int32(
5993 emitter: &mut FunctionEmitter,
5994 ctx: &CodegenCtx,
5995) -> Result<(), crate::compiler_error::CompilerFailure> {
5996 let value = emitter.add_anonymous_local(ValType::F64)?;
5997 emitter.instruction(Instruction::LocalTee(value));
5998 emitter.instruction(Instruction::F64Abs);
5999 emitter.instruction(Instruction::F64Const(Ieee64::from(
6000 9_223_372_036_854_775_808.0,
6001 )));
6002 emitter.instruction(Instruction::F64Lt);
6003 emitter.emit_if(BlockType::Result(ValType::I32));
6004 emitter.instruction(Instruction::LocalGet(value));
6005 emitter.instruction(Instruction::I64TruncSatF64S);
6006 emitter.instruction(Instruction::I32WrapI64);
6007 emitter.emit_else();
6008 emitter.instruction(Instruction::LocalGet(value));
6009 cast::emit_box(emitter, ctx, &Type::Number)?;
6010 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
6011 cast::emit_box(emitter, ctx, &Type::Number)?;
6012 let host = ctx
6013 .symbols
6014 .func_idx(&crate::mangle::prelude("__value_bitor"))
6015 .ok_or_else(|| crate::codegen::internal_failure("missing bitwise host import: bitor"))?;
6016 emitter.instruction(Instruction::Call(host));
6017 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
6018 emitter.instruction(Instruction::I32TruncF64S);
6019 emitter.emit_end();
6020 Ok(())
6021}
6022
6023fn emit_bitwise_host(
6024 emitter: &mut FunctionEmitter,
6025 ctx: &CodegenCtx,
6026 name: &str,
6027 operands: &[ExprId],
6028 result_ty: &Type,
6029) -> Result<(), crate::compiler_error::CompilerFailure> {
6030 for &operand in operands {
6031 emit_expr(emitter, ctx, operand)?;
6032 let ty = &ctx
6033 .ta
6034 .try_expr(operand)
6035 .map_err(crate::codegen::arena_failure)?
6036 .ty;
6037 cast::emit_box(emitter, ctx, ty)?;
6038 }
6039 let mangled = crate::mangle::prelude(&format!("__value_{name}"));
6040 let host = ctx.symbols.func_idx(&mangled).ok_or_else(|| {
6041 crate::codegen::internal_failure(format!("missing bitwise host import: {name}"))
6042 })?;
6043 emitter.instruction(Instruction::Call(host));
6044 cast::emit_cast_to(emitter, ctx, result_ty)
6045}
6046
6047fn emit_unary(
6048 emitter: &mut FunctionEmitter,
6049 ctx: &CodegenCtx,
6050 op: UnOp,
6051 operand: ExprId,
6052 result_ty: &Type,
6053) -> Result<(), crate::compiler_error::CompilerFailure> {
6054 if matches!(op, UnOp::Neg | UnOp::Pos)
6055 && try_emit_unreachable_for_never_operand(emitter, ctx, &[operand])?
6056 {
6057 return Ok(());
6058 }
6059 let _: () = match op {
6060 UnOp::BitNot if has_numeric_operands(ctx, &[operand], result_ty)? => {
6061 emit_primitive_operand(emitter, ctx, operand)?;
6062 emit_to_int32(emitter, ctx)?;
6063 emitter.instruction(Instruction::I32Const(-1));
6064 emitter.instruction(Instruction::I32Xor);
6065 emitter.instruction(Instruction::F64ConvertI32S);
6066 }
6067 UnOp::BitNot => emit_bitwise_host(emitter, ctx, "bitnot", &[operand], result_ty)?,
6068 UnOp::Neg => {
6069 if ctx
6071 .ta
6072 .try_expr(operand)
6073 .map_err(crate::codegen::arena_failure)?
6074 .ty
6075 .is_bigint()
6076 {
6077 emit_primitive_operand(emitter, ctx, operand)?;
6078 emit_bigint_extract_to_stack(emitter, ctx);
6079 let host_idx = ctx
6080 .symbols
6081 .func_idx(&crate::mangle::host(
6082 crate::runtime::BIGINT_MODULE_NAME,
6083 "neg",
6084 ))
6085 .ok_or_else(|| {
6086 crate::codegen::internal_failure(
6087 "submilli:bigint.neg import recorded by codegen bootstrap",
6088 )
6089 })?;
6090 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
6091 return Ok(());
6092 }
6093 emit_primitive_operand(emitter, ctx, operand)?;
6094 emitter.instruction(Instruction::F64Neg);
6095 }
6096 UnOp::Pos => {
6097 let operand_ty = ctx
6102 .ta
6103 .try_expr(operand)
6104 .map_err(crate::codegen::arena_failure)?
6105 .ty
6106 .clone();
6107 if operand_ty.is_string_shaped() {
6108 emit_primitive_operand(emitter, ctx, operand)?;
6109 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
6110 } else if matches!(operand_ty.primitive_behavior(), Type::Union(_)) {
6111 emit_expr(emitter, ctx, operand)?;
6112 cast::emit_box(emitter, ctx, &operand_ty)?;
6113 emit_union_to_number(emitter, ctx)?;
6114 } else {
6115 emit_primitive_operand(emitter, ctx, operand)?;
6116 }
6117 }
6118 UnOp::Not => {
6119 let operand_ty = ctx
6125 .ta
6126 .try_expr(operand)
6127 .map_err(crate::codegen::arena_failure)?
6128 .ty
6129 .clone();
6130 emit_expr(emitter, ctx, operand)?;
6131 crate::codegen::function_emitter::cast::emit_condition_to_i32(
6132 emitter,
6133 ctx,
6134 &operand_ty,
6135 )?;
6136 emitter.instruction(Instruction::I32Eqz);
6137 }
6138 };
6139 Ok(())
6140}
6141
6142fn to_number_host(ctx: &CodegenCtx) -> Result<u32, crate::compiler_error::CompilerFailure> {
6143 ctx.symbols
6144 .func_idx(&crate::mangle::host(
6145 crate::runtime::NUMBER_MODULE_NAME,
6146 "toNumber",
6147 ))
6148 .ok_or_else(|| {
6149 crate::codegen::internal_failure(
6150 "submilli:number.toNumber import recorded by codegen analysis",
6151 )
6152 })
6153}
6154
6155fn emit_union_to_number(
6158 emitter: &mut FunctionEmitter,
6159 ctx: &CodegenCtx,
6160) -> Result<(), crate::compiler_error::CompilerFailure> {
6161 let string = ctx
6162 .symbols
6163 .string_type_idx()
6164 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic type registered"))?;
6165 let tmp = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6166 emitter.instruction(Instruction::LocalTee(tmp));
6167 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(string)));
6168 emitter.emit_if(BlockType::Result(ValType::F64));
6169 emitter.instruction(Instruction::LocalGet(tmp));
6170 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(string)));
6171 emitter.instruction(Instruction::Call(to_number_host(ctx)?));
6172 emitter.emit_else();
6173 emitter.instruction(Instruction::LocalGet(tmp));
6174 cast::emit_cast_to(emitter, ctx, &Type::Number)?;
6175 emitter.emit_end();
6176 Ok(())
6177}
6178
6179#[allow(clippy::too_many_arguments)]
6183pub(crate) fn emit_method_call(
6184 emitter: &mut FunctionEmitter,
6185 ctx: &CodegenCtx,
6186 receiver: ExprId,
6187 iface: &crate::MangledName,
6188 method: &str,
6189 args: &[ExprId],
6190 generic_args: Option<&[crate::GenericArgument]>,
6191 type_args: &[Type],
6192 return_cast: Option<&Type>,
6193 call_ret_ty: &Type,
6194) -> Result<(), crate::compiler_error::CompilerFailure> {
6195 let recv_ty = ctx
6196 .ta
6197 .source_type(receiver)
6198 .map_err(crate::codegen::arena_failure)?
6199 .clone();
6200 if !emit_dropped_static_receiver(emitter, ctx, receiver, iface)? {
6201 emit_receiver(emitter, ctx, receiver)?;
6202 }
6203 emit_method_call_with_receiver_on_stack(
6204 emitter,
6205 ctx,
6206 &recv_ty,
6207 iface,
6208 method,
6209 args,
6210 generic_args,
6211 type_args,
6212 return_cast,
6213 call_ret_ty,
6214 )?;
6215 Ok(())
6216}
6217
6218fn emit_dropped_static_receiver(
6222 emitter: &mut FunctionEmitter,
6223 ctx: &CodegenCtx,
6224 receiver: ExprId,
6225 iface: &crate::MangledName,
6226) -> Result<bool, crate::compiler_error::CompilerFailure> {
6227 let expr = ctx
6228 .ta
6229 .try_expr(receiver)
6230 .map_err(crate::codegen::arena_failure)?;
6231 if !matches!(expr.kind, TypedExprKind::GlobalRef { .. })
6232 || static_interface(ctx, &expr.ty) != Some(iface)
6233 {
6234 return Ok(false);
6235 }
6236 match ctx.symbols.value_type(&expr.ty)? {
6237 ValType::Ref(RefType { heap_type, .. }) => {
6238 emitter.instruction(Instruction::RefNull(heap_type));
6239 }
6240 other => {
6241 return Err(crate::codegen::internal_failure(format!(
6242 "static-interface binding lowered to {other:?}"
6243 )));
6244 }
6245 }
6246 Ok(true)
6247}
6248
6249#[allow(clippy::too_many_arguments)]
6256fn emit_method_call_with_receiver_on_stack(
6257 emitter: &mut FunctionEmitter,
6258 ctx: &CodegenCtx,
6259 recv_ty: &Type,
6260 iface: &crate::MangledName,
6261 method: &str,
6262 args: &[ExprId],
6263 generic_args: Option<&[crate::GenericArgument]>,
6264 type_args: &[Type],
6265 return_cast: Option<&Type>,
6266 call_ret_ty: &Type,
6267) -> Result<(), crate::compiler_error::CompilerFailure> {
6268 let concrete_receiver = crate::typechecker::infer::narrowing::strip_nullish(recv_ty);
6269 if concrete_receiver.primitive_union_members().is_some() {
6270 cast::emit_box(emitter, ctx, recv_ty)?;
6271 emit_live_member_on_stack(emitter, ctx, iface, method, Some(args))?;
6272 return cast::emit_cast_to(emitter, ctx, call_ret_ty);
6273 }
6274 crate::codegen::field_guards::attach(emitter, ctx, &concrete_receiver)?;
6275 if recv_ty.is_regexp_match_array() && *iface == crate::mangle::prelude("Array") {
6276 cast::emit_cast_to(emitter, ctx, &Type::Array(Box::new(Type::regexp_element())))?;
6277 }
6278 let direct_key = crate::mangle::extend(iface, method);
6279 if let Some(func_idx) = ctx.symbols.func_idx(&direct_key) {
6280 if matches!(recv_ty.primitive_behavior(), Type::Number)
6281 && ctx.symbols.value_type(recv_ty)? != ValType::F64
6282 {
6283 super::cast::emit_cast_to(emitter, ctx, &Type::Number)?;
6284 }
6285 match ctx.symbols.iface_dispatch(iface) {
6294 Some(crate::Dispatch::Direct) => {
6295 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
6307 emitter.instruction(Instruction::RefAsNonNull);
6308 }
6309 }
6310 Some(crate::Dispatch::Static) => {
6311 emitter.instruction(Instruction::Drop);
6312 }
6313 None | Some(crate::Dispatch::VTable) => {}
6318 }
6319 let abi = wrapper_abi_for_call(
6329 ctx,
6330 &direct_key,
6331 args,
6332 generic_args,
6333 return_cast,
6334 call_ret_ty,
6335 )?;
6336 let defaults = ctx.symbols.host_call_defaults(func_idx, args.len());
6337 emit_args_into_slots_with_defaults(emitter, ctx, args, Some(&abi.params), defaults)?;
6338 if ctx.symbols.runtime_generic_functions.contains(&direct_key) {
6342 crate::codegen::runtime_descriptors::environment(emitter, ctx, type_args)?;
6343 }
6344 emitter.instruction(Instruction::Call(func_idx));
6345 emit_slot_return_cast(emitter, ctx, call_ret_ty, Some(&abi))?;
6346 return Ok(());
6347 }
6348 if ctx.symbols.is_rewritable_class_method(iface, method) {
6351 emit_interface_method_via_shape_with_receiver_on_stack(
6352 emitter,
6353 ctx,
6354 method,
6355 args,
6356 call_ret_ty,
6357 None,
6358 )?;
6359 return Ok(());
6360 }
6361 if ctx.symbols.class_method_slot(iface, method).is_some() {
6366 emit_class_vtable_dispatch(emitter, ctx, iface, method, args, call_ret_ty)?;
6367 return Ok(());
6368 }
6369 if let Some(slot) = vtable_slot_for_method(method) {
6370 emit_vtable_dispatch_on_object_stack(emitter, ctx, slot);
6371 return Ok(());
6372 }
6373 if matches!(recv_ty.lowered(), Type::InterfaceRef { .. }) {
6382 if is_call_signature_call(iface, method) {
6383 cast::emit_box(emitter, ctx, recv_ty)?;
6387 emit_live_member_on_stack(emitter, ctx, iface, method, Some(args))?;
6388 return cast::emit_cast_to(emitter, ctx, call_ret_ty);
6389 }
6390 emit_interface_method_via_shape_with_receiver_on_stack(
6391 emitter,
6392 ctx,
6393 method,
6394 args,
6395 call_ret_ty,
6396 None,
6397 )?;
6398 return Ok(());
6399 }
6400 Err(crate::codegen::internal_failure(format!(
6401 "method dispatch was not registered for `{iface}.{method}`"
6402 )))
6403}
6404
6405pub(in crate::codegen) fn is_call_signature_call(iface: &crate::MangledName, method: &str) -> bool {
6409 method == "@call" && !crate::codegen::is_host_package(iface.as_str())
6410}
6411
6412fn emit_class_method_value(
6413 emitter: &mut FunctionEmitter,
6414 ctx: &CodegenCtx,
6415 receiver_ty: &Type,
6416 method: &str,
6417 result_ty: &Type,
6418 check_ty: &Type,
6419) -> Result<(), crate::compiler_error::CompilerFailure> {
6420 let object_shape = ctx
6421 .symbols
6422 .intrinsic_type_indices()
6423 .ok_or_else(|| {
6424 crate::codegen::internal_failure("class method value requires object shape intrinsic")
6425 })?
6426 .object_shape;
6427 let receiver = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
6428 emit_object_property_read_as(emitter, ctx, receiver, method, result_ty, check_ty)
6429}
6430
6431pub(in crate::codegen) fn emit_interface_method_via_shape_with_receiver_on_stack(
6447 emitter: &mut FunctionEmitter,
6448 ctx: &CodegenCtx,
6449 method: &str,
6450 args: &[ExprId],
6451 call_ret_ty: &Type,
6452 slot_index_local: Option<u32>,
6453) -> Result<(), crate::compiler_error::CompilerFailure> {
6454 let args: Vec<_> = args
6455 .iter()
6456 .copied()
6457 .map(ShapeArgument::Expression)
6458 .collect();
6459 emit_shape_method_with_arguments(emitter, ctx, method, &args, call_ret_ty, slot_index_local)?;
6460 Ok(())
6461}
6462
6463enum ShapeArgument {
6464 Expression(ExprId),
6465 Local { slot: u32, ty: Type },
6466}
6467
6468impl ShapeArgument {
6469 fn ty(&self, ctx: &CodegenCtx) -> Result<Type, crate::compiler_error::CompilerFailure> {
6470 Ok(match self {
6471 Self::Expression(id) => ctx
6472 .ta
6473 .try_expr(*id)
6474 .map_err(crate::codegen::arena_failure)?
6475 .ty
6476 .clone(),
6477 Self::Local { ty, .. } => ty.clone(),
6478 })
6479 }
6480
6481 fn emit(
6482 &self,
6483 emitter: &mut FunctionEmitter,
6484 ctx: &CodegenCtx,
6485 ) -> Result<(), crate::compiler_error::CompilerFailure> {
6486 let _: () = match self {
6487 Self::Expression(id) => emit_expr(emitter, ctx, *id)?,
6488 Self::Local { slot, .. } => {
6489 emitter.instruction(Instruction::LocalGet(*slot));
6490 }
6491 };
6492 Ok(())
6493 }
6494}
6495
6496fn emit_shape_method_with_arguments(
6497 emitter: &mut FunctionEmitter,
6498 ctx: &CodegenCtx,
6499 method: &str,
6500 args: &[ShapeArgument],
6501 call_ret_ty: &Type,
6502 slot_index_local: Option<u32>,
6503) -> Result<(), crate::compiler_error::CompilerFailure> {
6504 let intrinsics = ctx
6505 .symbols
6506 .intrinsic_type_indices()
6507 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
6508
6509 let arg_tys: Vec<Type> = args.iter().map(|a| a.ty(ctx)).collect::<Result<_, _>>()?;
6512 let fn_ty = Type::Function {
6513 params: arg_tys,
6514 ret: Box::new(call_ret_ty.clone()),
6515 predicate: None,
6516 has_rest: false,
6520 optional: 0,
6521 };
6522 let closure_sig = crate::codegen::closures::classify(&fn_ty)?;
6523 let closure_struct_idx = ctx
6524 .symbols
6525 .closure_struct_type_idx(closure_sig)
6526 .ok_or_else(|| {
6527 crate::codegen::internal_failure(
6528 "closure struct registered for the method's signature (SUB-166)",
6529 )
6530 })?;
6531 let fn_type_idx = ctx
6532 .symbols
6533 .closure_func_type_idx(closure_sig)
6534 .ok_or_else(|| {
6535 crate::codegen::internal_failure(
6536 "closure funcref type registered for the method's signature",
6537 )
6538 })?;
6539
6540 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6541 nullable: false,
6542 heap_type: HeapType::Concrete(intrinsics.object_shape),
6543 }))?;
6544 let closure_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6545 nullable: false,
6546 heap_type: HeapType::Concrete(closure_struct_idx),
6547 }))?;
6548
6549 emitter.instruction(Instruction::RefAsNonNull);
6553 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
6554 intrinsics.object_shape,
6555 )));
6556 emitter.instruction(Instruction::LocalSet(rcv_local));
6557
6558 if let Some(index_local) = slot_index_local {
6561 emit_field_slot_get(emitter, intrinsics, rcv_local, index_local);
6562 } else {
6563 let name_global = ctx
6564 .symbols
6565 .field_name_string_global_idx(method)
6566 .ok_or_else(|| {
6567 crate::codegen::internal_failure(
6568 ("per-name string global recorded during field-name-strings emission \
6569 for interface method names")
6570 .to_string(),
6571 )
6572 })?;
6573 emit_object_field_read_by_name(emitter, ctx, rcv_local, name_global);
6574 }
6575
6576 let method_value = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6577 emitter.instruction(Instruction::LocalSet(method_value));
6578 let mut evaluated_args = Vec::with_capacity(args.len());
6579 for arg in args {
6580 arg.emit(emitter, ctx)?;
6581 cast::emit_box(emitter, ctx, &arg.ty(ctx)?)?;
6582 let slot = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
6583 emitter.instruction(Instruction::LocalSet(slot));
6584 evaluated_args.push(slot);
6585 }
6586 emitter.instruction(Instruction::LocalGet(method_value));
6587 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
6588 intrinsics.closure,
6589 )));
6590 emitter.instruction(Instruction::I32Eqz);
6591 emitter.emit_if(BlockType::Empty);
6592 crate::codegen::throw::emit_type_error_throw(emitter, ctx, "Value is not callable");
6593 emitter.emit_end();
6594 emitter.instruction(Instruction::LocalGet(method_value));
6595
6596 crate::codegen::closure_coercions::emit_erased_cast(
6600 emitter,
6601 ctx,
6602 crate::codegen::closures::ClosureSig::of(args.len(), call_ret_ty)?,
6603 )?;
6604 emitter.instruction(Instruction::LocalTee(closure_local));
6605
6606 emitter.instruction(Instruction::StructGet {
6608 struct_type_index: closure_struct_idx,
6609 field_index: 2,
6610 });
6611
6612 crate::codegen::this_binding::bind(emitter, ctx, rcv_local)?;
6613 for slot in evaluated_args {
6616 emitter.instruction(Instruction::LocalGet(slot));
6617 }
6618
6619 emitter.instruction(Instruction::LocalGet(closure_local));
6621 emitter.instruction(Instruction::StructGet {
6622 struct_type_index: closure_struct_idx,
6623 field_index: 1,
6624 });
6625 emitter.instruction(Instruction::CallRef(fn_type_idx));
6626
6627 let _: () = if !call_ret_ty.is_void() {
6630 crate::codegen::cast_check::emit_checked_cast_on_stack(
6631 emitter,
6632 ctx,
6633 &Type::Unknown,
6634 call_ret_ty,
6635 )?;
6636 };
6637 Ok(())
6638}
6639
6640fn vtable_slot_for_method(method: &str) -> Option<u32> {
6651 match method {
6652 "toString" => Some(0),
6653 "toJson" => Some(1),
6654 "equals" => Some(2),
6655 "hash" => Some(3),
6656 _ => None,
6657 }
6658}
6659
6660pub(super) fn emit_vtable_dispatch_on_object_stack(
6671 emitter: &mut FunctionEmitter,
6672 ctx: &CodegenCtx,
6673 slot: u32,
6674) {
6675 let Some(intrinsics) = ctx.require(
6676 ctx.symbols.intrinsic_type_indices(),
6677 "intrinsics declared by codegen entry",
6678 ) else {
6679 return;
6680 };
6681 let fn_type_idx = match slot {
6686 0 => intrinsics.to_string_fn,
6687 1 => intrinsics.to_json_fn,
6688 _ => {
6689 ctx.fail("vtable string dispatch requires toString or toJson slot");
6690 return;
6691 }
6692 };
6693 let Some(obj_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
6694 nullable: false,
6695 heap_type: HeapType::Concrete(intrinsics.object),
6696 }))) else {
6697 return;
6698 };
6699 let Some(fn_local) = ctx.latch(emitter.add_anonymous_local(ValType::Ref(RefType {
6700 nullable: false,
6701 heap_type: HeapType::Concrete(fn_type_idx),
6702 }))) else {
6703 return;
6704 };
6705 emitter.instruction(Instruction::RefAsNonNull);
6711 emitter.instruction(Instruction::LocalTee(obj_local));
6712 emitter.instruction(Instruction::StructGet {
6713 struct_type_index: intrinsics.object,
6714 field_index: 0,
6715 });
6716 emitter.instruction(Instruction::StructGet {
6717 struct_type_index: intrinsics.vtable,
6718 field_index: slot,
6719 });
6720 emitter.instruction(Instruction::LocalSet(fn_local));
6721 emitter.instruction(Instruction::LocalGet(obj_local));
6722 emitter.instruction(Instruction::LocalGet(fn_local));
6723 emitter.instruction(Instruction::CallRef(fn_type_idx));
6724}
6725
6726fn emit_args_into_slots(
6730 emitter: &mut FunctionEmitter,
6731 ctx: &CodegenCtx,
6732 args: &[ExprId],
6733 slots: Option<&[ValType]>,
6734) -> Result<(), crate::compiler_error::CompilerFailure> {
6735 emit_args_into_slots_with_defaults(emitter, ctx, args, slots, &[])
6736}
6737
6738fn emit_args_into_slots_with_defaults(
6739 emitter: &mut FunctionEmitter,
6740 ctx: &CodegenCtx,
6741 args: &[ExprId],
6742 slots: Option<&[ValType]>,
6743 defaults: &[Option<crate::DefaultValue>],
6744) -> Result<(), crate::compiler_error::CompilerFailure> {
6745 if slots.is_some_and(|slots| slots.len() != args.len()) {
6746 return Err(crate::codegen::internal_failure(
6747 "call ABI argument count mismatch",
6748 ));
6749 }
6750 let locals: Vec<_> = args
6751 .iter()
6752 .map(|&arg| {
6753 emit_expr(emitter, ctx, arg)?;
6754 let local = emitter.add_anonymous_local(
6755 ctx.symbols.value_type(
6756 &ctx.ta
6757 .try_expr(arg)
6758 .map_err(crate::codegen::arena_failure)?
6759 .ty,
6760 )?,
6761 )?;
6762 emitter.instruction(Instruction::LocalSet(local));
6763 Ok::<_, crate::compiler_error::CompilerFailure>(local)
6764 })
6765 .collect::<Result<_, _>>()?;
6766 for (i, (&arg, local)) in args.iter().zip(locals).enumerate() {
6767 emitter.instruction(Instruction::LocalGet(local));
6768 if let Some(slot) = slots.and_then(|s| s.get(i).copied()) {
6769 crate::codegen::argument_defaults::emit_argument(
6770 emitter,
6771 ctx,
6772 &ctx.ta
6773 .try_expr(arg)
6774 .map_err(crate::codegen::arena_failure)?
6775 .ty,
6776 slot,
6777 defaults.get(i).and_then(Option::as_ref),
6778 )?;
6779 }
6780 }
6781 Ok(())
6782}
6783
6784fn wrapper_abi_for_call(
6799 ctx: &CodegenCtx,
6800 direct_key: &crate::MangledName,
6801 args: &[ExprId],
6802 generic_args: Option<&[crate::GenericArgument]>,
6803 return_cast: Option<&Type>,
6804 call_ret_ty: &Type,
6805) -> Result<MethodSlotAbi, crate::compiler_error::CompilerFailure> {
6806 if let Some(recorded) = ctx.symbols.iface_method_abi(direct_key) {
6807 return Ok(recorded.clone());
6808 }
6809 let erased = ctx.symbols.value_type(&Type::Unknown)?;
6810 let slot_is_erased =
6811 |i: usize| generic_args.is_some_and(|flags| flags.get(i).is_some_and(|g| g.is_generic));
6812 Ok(MethodSlotAbi {
6813 params: args
6814 .iter()
6815 .enumerate()
6816 .map(|(i, &a)| {
6817 Ok::<_, crate::compiler_error::CompilerFailure>(if slot_is_erased(i) {
6818 erased
6819 } else {
6820 ctx.symbols.value_type(
6821 &ctx.ta
6822 .try_expr(a)
6823 .map_err(crate::codegen::arena_failure)?
6824 .ty,
6825 )?
6826 })
6827 })
6828 .collect::<Result<_, _>>()?,
6829 ret: match return_cast {
6830 Some(_) => Some(erased),
6831 None => (!call_ret_ty.is_void())
6832 .then(|| ctx.symbols.value_type(call_ret_ty))
6833 .transpose()?,
6834 },
6835 })
6836}
6837
6838fn emit_slot_return_cast(
6841 emitter: &mut FunctionEmitter,
6842 ctx: &CodegenCtx,
6843 call_ret_ty: &Type,
6844 abi: Option<&MethodSlotAbi>,
6845) -> Result<(), crate::compiler_error::CompilerFailure> {
6846 if matches!(call_ret_ty.lowered(), Type::Never) {
6847 emitter.instruction(Instruction::Unreachable);
6848 return Ok(());
6849 }
6850 if abi.is_some_and(|abi| abi.ret.is_none()) {
6853 emit_undefined(emitter, ctx)?;
6854 }
6855 if call_ret_ty.is_void() {
6856 return Ok(());
6857 }
6858 if matches!(call_ret_ty, Type::Unknown) {
6859 match abi.and_then(|abi| abi.ret) {
6860 Some(ValType::F64) => cast::emit_box(emitter, ctx, &Type::Number)?,
6861 Some(ValType::I32) => cast::emit_box(emitter, ctx, &Type::Boolean)?,
6862 _ => {}
6863 }
6864 return Ok(());
6865 }
6866 if abi.and_then(|a| a.ret) != Some(ctx.symbols.value_type(call_ret_ty)?) {
6867 crate::codegen::cast_check::emit_checked_cast_on_stack(
6868 emitter,
6869 ctx,
6870 &Type::Unknown,
6871 call_ret_ty,
6872 )?;
6873 }
6874 crate::codegen::field_guards::attach(emitter, ctx, call_ret_ty)?;
6875 Ok(())
6876}
6877
6878fn emit_class_vtable_dispatch(
6891 emitter: &mut FunctionEmitter,
6892 ctx: &CodegenCtx,
6893 class: &crate::MangledName,
6894 method: &str,
6895 args: &[ExprId],
6896 call_ret_ty: &Type,
6897) -> Result<(), crate::compiler_error::CompilerFailure> {
6898 let struct_idx = ctx
6899 .symbols
6900 .class_struct_type_idx(class)
6901 .ok_or_else(|| crate::codegen::internal_failure("class struct type recorded"))?;
6902 let vtable_idx = ctx
6903 .symbols
6904 .class_vtable_type_idx(class)
6905 .ok_or_else(|| crate::codegen::internal_failure("class vtable type recorded"))?;
6906 let slot = ctx
6907 .symbols
6908 .class_method_slot(class, method)
6909 .ok_or_else(|| crate::codegen::internal_failure("class method slot recorded"))?;
6910 let sig_idx = ctx
6911 .symbols
6912 .class_method_sig(class, method)
6913 .ok_or_else(|| crate::codegen::internal_failure("class method sig recorded"))?;
6914
6915 let rcv_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6916 nullable: false,
6917 heap_type: HeapType::Concrete(struct_idx),
6918 }))?;
6919 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
6922 emitter.instruction(Instruction::LocalSet(rcv_local));
6923
6924 emitter.instruction(Instruction::LocalGet(rcv_local));
6926 emitter.instruction(Instruction::StructGet {
6927 struct_type_index: struct_idx,
6928 field_index: 0,
6929 });
6930 emitter.instruction(Instruction::StructGet {
6931 struct_type_index: vtable_idx,
6932 field_index: slot,
6933 });
6934 let fn_local = emitter.add_anonymous_local(ValType::Ref(RefType {
6935 nullable: false,
6936 heap_type: HeapType::Concrete(sig_idx),
6937 }))?;
6938 emitter.instruction(Instruction::LocalSet(fn_local));
6939
6940 let abi = ctx.symbols.class_method_abi(class, method).cloned();
6942 emitter.instruction(Instruction::LocalGet(rcv_local));
6943 emit_args_into_slots(
6944 emitter,
6945 ctx,
6946 args,
6947 abi.as_ref().map(|a| a.params.as_slice()),
6948 )?;
6949 emitter.instruction(Instruction::LocalGet(fn_local));
6950 emitter.instruction(Instruction::CallRef(sig_idx));
6951 emit_slot_return_cast(emitter, ctx, call_ret_ty, abi.as_ref())?;
6952 Ok(())
6953}
6954
6955fn emit_intrinsic_call(
6956 emitter: &mut FunctionEmitter,
6957 ctx: &CodegenCtx,
6958 kind: Intrinsic,
6959 args: &[ExprId],
6960 result_ty: &Type,
6961 source_ty: &Type,
6962) -> Result<(), crate::compiler_error::CompilerFailure> {
6963 let _: () = match kind {
6964 Intrinsic::Assert => {
6965 if args.len() != 2 {
6969 return Err(crate::codegen::internal_failure(
6970 "assert requires two arguments",
6971 ));
6972 }
6973 emit_expr(emitter, ctx, args[0])?;
6974 cast::emit_condition_to_i32(
6975 emitter,
6976 ctx,
6977 &ctx.ta
6978 .try_expr(args[0])
6979 .map_err(crate::codegen::arena_failure)?
6980 .ty,
6981 )?;
6982 emit_expr(emitter, ctx, args[1])?;
6983 let message_type = &ctx
6984 .ta
6985 .try_expr(args[1])
6986 .map_err(crate::codegen::arena_failure)?
6987 .ty;
6988 let msg_local = emitter.add_anonymous_local(ctx.symbols.value_type(message_type)?)?;
6989 emitter.instruction(Instruction::LocalSet(msg_local));
6990 emitter.instruction(Instruction::I32Eqz);
6991 emitter.emit_if(BlockType::Empty);
6992 emitter.instruction(Instruction::LocalGet(msg_local));
6993 cast::emit_coerce_to_slot(emitter, ctx, message_type, &Type::String)?;
6994 let ctor_idx = ctx
6995 .symbols
6996 .func_idx(&crate::mangle::prelude("Error#constructor"))
6997 .ok_or_else(|| {
6998 crate::codegen::internal_failure("Error#constructor exported from prelude")
6999 })?;
7000 crate::codegen::throw::emit_no_error_options(emitter);
7001 emitter.instruction(Instruction::Call(ctor_idx));
7002 crate::codegen::throw::emit_error_throw(emitter, ctx);
7003 emitter.emit_end();
7004 emit_undefined(emitter, ctx)?;
7005 }
7006 Intrinsic::JsonStringify => {
7007 super::json::emit_stringify(emitter, ctx, args, result_ty, source_ty)?;
7008 }
7009 Intrinsic::JsonParse => {
7010 super::json::emit_parse(emitter, ctx, args)?;
7011 }
7012 Intrinsic::ToString => {
7013 let [value] = args else {
7014 return Err(crate::codegen::internal_failure(
7015 "string conversion requires one argument",
7016 ));
7017 };
7018 emit_expr(emitter, ctx, *value)?;
7019 cast::emit_box(
7020 emitter,
7021 ctx,
7022 &ctx.ta
7023 .try_expr(*value)
7024 .map_err(crate::codegen::arena_failure)?
7025 .ty,
7026 )?;
7027 let function = ctx
7028 .symbols
7029 .prelude_func_idx("__value_to_string")
7030 .ok_or_else(|| {
7031 crate::codegen::internal_failure("dynamic string conversion collected")
7032 })?;
7033 emitter.instruction(Instruction::Call(function));
7034 }
7035 Intrinsic::ToObject => {
7036 let [value] = args else {
7037 return Err(crate::codegen::internal_failure(
7038 "object conversion requires one argument",
7039 ));
7040 };
7041 emit_expr(emitter, ctx, *value)?;
7042 let function = ctx
7043 .symbols
7044 .prelude_func_idx("ObjectConstructor##toObject")
7045 .ok_or_else(|| {
7046 crate::codegen::internal_failure("object conversion helper collected")
7047 })?;
7048 emitter.instruction(Instruction::Call(function));
7049 }
7050 Intrinsic::BigIntFromString => {
7057 if args.len() != 1 {
7058 return Err(crate::codegen::internal_failure(
7059 "BigInt.fromString requires one argument",
7060 ));
7061 }
7062 emit_expr(emitter, ctx, args[0])?;
7063 let string_type_idx = ctx
7066 .symbols
7067 .string_type_idx()
7068 .ok_or_else(|| crate::codegen::internal_failure("$string registered"))?;
7069 emitter.instruction(Instruction::StructGet {
7070 struct_type_index: string_type_idx,
7071 field_index: 1,
7072 });
7073 let host_idx = ctx
7074 .symbols
7075 .func_idx(&crate::mangle::host(
7076 crate::runtime::BIGINT_MODULE_NAME,
7077 "fromString",
7078 ))
7079 .ok_or_else(|| {
7080 crate::codegen::internal_failure(
7081 "submilli:bigint.fromString import recorded by codegen bootstrap",
7082 )
7083 })?;
7084 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
7085 }
7086 };
7087 Ok(())
7088}
7089
7090fn emit_vtable_equality(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: BinOp) {
7095 ctx.latch(emit_vtable_equality_checked(emitter, ctx, op));
7096}
7097
7098fn emit_vtable_equality_checked(
7099 emitter: &mut FunctionEmitter,
7100 ctx: &CodegenCtx,
7101 op: BinOp,
7102) -> Result<(), crate::compiler_error::CompilerFailure> {
7103 let intrinsics = ctx
7104 .symbols
7105 .intrinsic_type_indices()
7106 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
7107 let operand_ref = ValType::Ref(RefType {
7110 nullable: true,
7111 heap_type: HeapType::Concrete(intrinsics.object),
7112 });
7113 let object_ref = ValType::Ref(RefType {
7114 nullable: false,
7115 heap_type: HeapType::Concrete(intrinsics.object),
7116 });
7117 let equals_fn_ref = ValType::Ref(RefType {
7118 nullable: false,
7119 heap_type: HeapType::Concrete(intrinsics.equals_fn),
7120 });
7121
7122 let Some(lhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
7124 return Ok(());
7125 };
7126 let Some(rhs_local) = ctx.latch(emitter.add_anonymous_local(operand_ref)) else {
7127 return Ok(());
7128 };
7129 let Some(lhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
7130 return Ok(());
7131 };
7132 let Some(rhs_object) = ctx.latch(emitter.add_anonymous_local(object_ref)) else {
7133 return Ok(());
7134 };
7135 let Some(eq_fn_local) = ctx.latch(emitter.add_anonymous_local(equals_fn_ref)) else {
7136 return Ok(());
7137 };
7138
7139 emitter.instruction(Instruction::LocalSet(rhs_local));
7140 emitter.instruction(Instruction::LocalSet(lhs_local));
7141
7142 emitter.instruction(Instruction::LocalGet(lhs_local));
7143 emitter.instruction(Instruction::RefAsNonNull);
7144 emitter.instruction(Instruction::LocalSet(lhs_object));
7145 emitter.instruction(Instruction::LocalGet(rhs_local));
7146 emitter.instruction(Instruction::RefAsNonNull);
7147 emitter.instruction(Instruction::LocalSet(rhs_object));
7148
7149 emitter.instruction(Instruction::LocalGet(lhs_object));
7150 emitter.instruction(Instruction::StructGet {
7151 struct_type_index: intrinsics.object,
7152 field_index: 0,
7153 });
7154 emitter.instruction(Instruction::StructGet {
7155 struct_type_index: intrinsics.vtable,
7156 field_index: 2,
7157 });
7158 emitter.instruction(Instruction::LocalSet(eq_fn_local));
7159
7160 emitter.instruction(Instruction::LocalGet(lhs_object));
7161 emitter.instruction(Instruction::LocalGet(rhs_object));
7162 emitter.instruction(Instruction::LocalGet(eq_fn_local));
7163 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
7164
7165 if matches!(op, BinOp::NotEq) {
7166 emitter.instruction(Instruction::I32Eqz);
7167 }
7168
7169 Ok(())
7170}
7171
7172fn emit_boxed_eq(
7185 emitter: &mut FunctionEmitter,
7186 ctx: &CodegenCtx,
7187 lhs: ExprId,
7188 rhs: ExprId,
7189 op: BinOp,
7190) -> Result<(), crate::compiler_error::CompilerFailure> {
7191 let intrinsics = ctx
7192 .symbols
7193 .intrinsic_type_indices()
7194 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
7195 let object_null_ref = ValType::Ref(RefType {
7196 nullable: true,
7197 heap_type: HeapType::Concrete(intrinsics.object),
7198 });
7199 let object_ref = ValType::Ref(RefType {
7200 nullable: false,
7201 heap_type: HeapType::Concrete(intrinsics.object),
7202 });
7203 let equals_fn_ref = ValType::Ref(RefType {
7204 nullable: false,
7205 heap_type: HeapType::Concrete(intrinsics.equals_fn),
7206 });
7207
7208 let lhs_ty = ctx
7209 .ta
7210 .try_expr(lhs)
7211 .map_err(crate::codegen::arena_failure)?
7212 .ty
7213 .clone();
7214 let rhs_ty = ctx
7215 .ta
7216 .try_expr(rhs)
7217 .map_err(crate::codegen::arena_failure)?
7218 .ty
7219 .clone();
7220
7221 let lhs_local = emitter.add_anonymous_local(object_null_ref)?;
7226 let rhs_local = emitter.add_anonymous_local(object_null_ref)?;
7227 let lhs_nonnull_local = emitter.add_anonymous_local(object_ref)?;
7228 let eq_fn_local = emitter.add_anonymous_local(equals_fn_ref)?;
7229
7230 emit_expr(emitter, ctx, lhs)?;
7231 cast::emit_box(emitter, ctx, &lhs_ty)?;
7232 emitter.instruction(Instruction::LocalSet(lhs_local));
7233
7234 emit_expr(emitter, ctx, rhs)?;
7235 cast::emit_box(emitter, ctx, &rhs_ty)?;
7236 emitter.instruction(Instruction::LocalSet(rhs_local));
7237
7238 emitter.instruction(Instruction::LocalGet(lhs_local));
7242 emitter.instruction(Instruction::RefIsNull);
7243 emitter.emit_if(BlockType::Result(ValType::I32));
7244 emitter.instruction(Instruction::LocalGet(rhs_local));
7245 emitter.instruction(Instruction::RefIsNull);
7246 emitter.emit_else();
7247 emitter.instruction(Instruction::LocalGet(rhs_local));
7248 emitter.instruction(Instruction::RefIsNull);
7249 emitter.emit_if(BlockType::Result(ValType::I32));
7250 emitter.instruction(Instruction::I32Const(0));
7251 emitter.emit_else();
7252 emitter.instruction(Instruction::LocalGet(lhs_local));
7254 emitter.instruction(Instruction::RefAsNonNull);
7255 emitter.instruction(Instruction::LocalTee(lhs_nonnull_local));
7256 emitter.instruction(Instruction::StructGet {
7257 struct_type_index: intrinsics.object,
7258 field_index: 0,
7259 });
7260 emitter.instruction(Instruction::StructGet {
7261 struct_type_index: intrinsics.vtable,
7262 field_index: 2,
7263 });
7264 emitter.instruction(Instruction::LocalSet(eq_fn_local));
7265 emitter.instruction(Instruction::LocalGet(lhs_nonnull_local));
7266 emitter.instruction(Instruction::LocalGet(rhs_local));
7267 emitter.instruction(Instruction::RefAsNonNull);
7268 emitter.instruction(Instruction::LocalGet(eq_fn_local));
7269 emitter.instruction(Instruction::CallRef(intrinsics.equals_fn));
7270 emitter.emit_end();
7271 emitter.emit_end();
7272
7273 let _: () = if matches!(op, BinOp::NotEq) {
7274 emitter.instruction(Instruction::I32Eqz);
7275 };
7276 Ok(())
7277}
7278
7279fn emit_bigint_literal(
7285 emitter: &mut FunctionEmitter,
7286 ctx: &CodegenCtx,
7287 digits: &str,
7288) -> Result<(), crate::compiler_error::CompilerFailure> {
7289 use crate::codegen::internal_failure;
7290 if let Some(entry) = ctx.bigints.lookup(digits)? {
7291 let raw_bigint_idx = ctx
7296 .symbols
7297 .raw_bigint_type_idx()
7298 .ok_or_else(|| internal_failure("the $rawBigInt intrinsic is not registered"))?;
7299 let bigint_idx = ctx
7300 .symbols
7301 .bigint_type_idx()
7302 .ok_or_else(|| internal_failure("the $bigint intrinsic is not registered"))?;
7303 let vtable_global = ctx
7304 .symbols
7305 .prelude_global_idx("bigint_vtable")
7306 .ok_or_else(|| internal_failure("bigint_vtable is not imported from the prelude"))?;
7307 let data_idx = crate::codegen::wasm_u32(ctx.strings.strings.len())?
7308 .checked_add(entry.data_idx)
7309 .ok_or_else(|| internal_failure("the module has too many data segments"))?;
7310 emitter.instruction(Instruction::GlobalGet(vtable_global));
7311 emitter.instruction(Instruction::I32Const(i32::from(entry.sign)));
7312 emitter.instruction(Instruction::I32Const(0));
7313 emitter.instruction(Instruction::I32Const(entry.limb_count.cast_signed()));
7314 emitter.instruction(Instruction::ArrayNewData {
7315 array_type_index: raw_bigint_idx,
7316 array_data_index: data_idx,
7317 });
7318 emitter.instruction(Instruction::StructNew(bigint_idx));
7319 return Ok(());
7320 }
7321 let v: i64 = digits
7325 .parse()
7326 .map_err(|_| internal_failure("an unpooled bigint literal is not a safe integer"))?;
7327 let host_idx = ctx
7328 .symbols
7329 .func_idx(&crate::mangle::host(
7330 crate::runtime::BIGINT_MODULE_NAME,
7331 "fromNumber",
7332 ))
7333 .ok_or_else(|| internal_failure("submilli:bigint.fromNumber is not imported"))?;
7334 emitter.instruction(Instruction::F64Const(Ieee64::from(v as f64)));
7335 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
7336 Ok(())
7337}
7338
7339fn emit_regex_literal(
7351 emitter: &mut FunctionEmitter,
7352 ctx: &CodegenCtx,
7353 source: &str,
7354 flags: &str,
7355) -> Result<(), crate::compiler_error::CompilerFailure> {
7356 super::emit_const_string_by_text(emitter, ctx, source)?;
7357 super::emit_const_string_by_text(emitter, ctx, flags)?;
7358 let ctor_idx = ctx
7359 .symbols
7360 .func_idx(&crate::mangle::prelude("RegExpConstructor#new"))
7361 .ok_or_else(|| crate::codegen::internal_failure("RegExpConstructor#new is not imported"))?;
7362 emitter.instruction(Instruction::Call(ctor_idx));
7363 Ok(())
7364}
7365
7366fn emit_bigint_extract_to_stack(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
7372 ctx.latch(emit_bigint_extract_to_stack_checked(emitter, ctx));
7373}
7374
7375fn emit_bigint_extract_to_stack_checked(
7376 emitter: &mut FunctionEmitter,
7377 ctx: &CodegenCtx,
7378) -> Result<(), crate::compiler_error::CompilerFailure> {
7379 let bigint_idx = ctx
7380 .symbols
7381 .bigint_type_idx()
7382 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
7383 let Some(scratch) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
7386 wasm_encoder::RefType {
7387 nullable: false,
7388 heap_type: wasm_encoder::HeapType::Concrete(bigint_idx),
7389 },
7390 ))) else {
7391 return Ok(());
7392 };
7393 emitter.instruction(Instruction::LocalSet(scratch));
7394 emitter.instruction(Instruction::LocalGet(scratch));
7395 emitter.instruction(Instruction::StructGet {
7396 struct_type_index: bigint_idx,
7397 field_index: 1, });
7399 emitter.instruction(Instruction::LocalGet(scratch));
7400 emitter.instruction(Instruction::StructGet {
7401 struct_type_index: bigint_idx,
7402 field_index: 2, });
7404
7405 Ok(())
7406}
7407
7408fn emit_bigint_wrap_host_result(
7413 emitter: &mut FunctionEmitter,
7414 ctx: &CodegenCtx,
7415 host_func_idx: u32,
7416) {
7417 ctx.latch(emit_bigint_wrap_host_result_checked(
7418 emitter,
7419 ctx,
7420 host_func_idx,
7421 ));
7422}
7423
7424fn emit_bigint_wrap_host_result_checked(
7425 emitter: &mut FunctionEmitter,
7426 ctx: &CodegenCtx,
7427 host_func_idx: u32,
7428) -> Result<(), crate::compiler_error::CompilerFailure> {
7429 emitter.instruction(Instruction::Call(host_func_idx));
7430 let bigint_idx = ctx
7431 .symbols
7432 .bigint_type_idx()
7433 .ok_or_else(|| crate::codegen::internal_failure("$bigint registered"))?;
7434 let raw_bigint_idx = ctx
7435 .symbols
7436 .raw_bigint_type_idx()
7437 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
7438 let vtable_global = ctx
7439 .symbols
7440 .prelude_global_idx("bigint_vtable")
7441 .ok_or_else(|| {
7442 crate::codegen::internal_failure("bigint_vtable imported from prelude bootstrap")
7443 })?;
7444 let Some(scratch_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
7447 wasm_encoder::RefType {
7448 nullable: false,
7449 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
7450 },
7451 ))) else {
7452 return Ok(());
7453 };
7454 let Some(scratch_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32))
7455 else {
7456 return Ok(());
7457 };
7458 emitter.instruction(Instruction::LocalSet(scratch_limbs));
7459 emitter.instruction(Instruction::LocalSet(scratch_sign));
7460 emitter.instruction(Instruction::GlobalGet(vtable_global));
7461 emitter.instruction(Instruction::LocalGet(scratch_sign));
7462 emitter.instruction(Instruction::LocalGet(scratch_limbs));
7463 emitter.instruction(Instruction::StructNew(bigint_idx));
7464
7465 Ok(())
7466}
7467
7468fn emit_bigint_binop_inline(
7472 emitter: &mut FunctionEmitter,
7473 ctx: &CodegenCtx,
7474 lhs: ExprId,
7475 rhs: ExprId,
7476 op_name: &str,
7477) -> Result<(), crate::compiler_error::CompilerFailure> {
7478 emit_expr(emitter, ctx, lhs)?;
7479 emit_bigint_extract_to_stack(emitter, ctx);
7480 let raw_bigint_idx = ctx
7481 .symbols
7482 .raw_bigint_type_idx()
7483 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
7484 let lhs_limbs =
7485 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
7486 nullable: false,
7487 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
7488 }))?;
7489 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
7490 emitter.instruction(Instruction::LocalSet(lhs_limbs));
7491 emitter.instruction(Instruction::LocalSet(lhs_sign));
7492 emitter.instruction(Instruction::LocalGet(lhs_sign));
7493 emitter.instruction(Instruction::LocalGet(lhs_limbs));
7494 emit_expr(emitter, ctx, rhs)?;
7495 emit_bigint_extract_to_stack(emitter, ctx);
7496 let host_idx = ctx
7497 .symbols
7498 .func_idx(&crate::mangle::host(
7499 crate::runtime::BIGINT_MODULE_NAME,
7500 op_name,
7501 ))
7502 .ok_or_else(|| {
7503 crate::codegen::internal_failure(format!(
7504 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
7505 ))
7506 })?;
7507 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
7508 Ok(())
7509}
7510
7511fn emit_bigint_pm_one(emitter: &mut FunctionEmitter, ctx: &CodegenCtx, op: crate::PostfixOp) {
7518 ctx.latch(emit_bigint_pm_one_checked(emitter, ctx, op));
7519}
7520
7521fn emit_bigint_pm_one_checked(
7522 emitter: &mut FunctionEmitter,
7523 ctx: &CodegenCtx,
7524 op: crate::PostfixOp,
7525) -> Result<(), crate::compiler_error::CompilerFailure> {
7526 emit_bigint_extract_to_stack(emitter, ctx);
7527 let raw_bigint_idx = ctx
7528 .symbols
7529 .raw_bigint_type_idx()
7530 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
7531 let Some(lhs_limbs) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::Ref(
7532 wasm_encoder::RefType {
7533 nullable: false,
7534 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
7535 },
7536 ))) else {
7537 return Ok(());
7538 };
7539 let Some(lhs_sign) = ctx.latch(emitter.add_anonymous_local(wasm_encoder::ValType::I32)) else {
7540 return Ok(());
7541 };
7542 emitter.instruction(Instruction::LocalSet(lhs_limbs));
7543 emitter.instruction(Instruction::LocalSet(lhs_sign));
7544 emitter.instruction(Instruction::LocalGet(lhs_sign));
7545 emitter.instruction(Instruction::LocalGet(lhs_limbs));
7546 if ctx.latch(emit_bigint_literal(emitter, ctx, "1")).is_none() {
7547 return Ok(());
7548 }
7549 emit_bigint_extract_to_stack(emitter, ctx);
7550 let op_name = match op {
7551 crate::PostfixOp::Inc => "add",
7552 crate::PostfixOp::Dec => "sub",
7553 crate::PostfixOp::NonNullAssert => {
7554 return Err(crate::codegen::internal_failure(
7555 "non-null assertion is not PostfixUnary",
7556 ));
7557 }
7558 };
7559 let host_idx = ctx
7560 .symbols
7561 .func_idx(&crate::mangle::host(
7562 crate::runtime::BIGINT_MODULE_NAME,
7563 op_name,
7564 ))
7565 .ok_or_else(|| {
7566 crate::codegen::internal_failure(format!(
7567 "submilli:bigint.{op_name} import recorded by codegen bootstrap"
7568 ))
7569 })?;
7570 emit_bigint_wrap_host_result(emitter, ctx, host_idx);
7571
7572 Ok(())
7573}
7574
7575fn emit_bigint_cmp_inline(
7578 emitter: &mut FunctionEmitter,
7579 ctx: &CodegenCtx,
7580 lhs: ExprId,
7581 rhs: ExprId,
7582 op: BinOp,
7583) -> Result<(), crate::compiler_error::CompilerFailure> {
7584 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
7585 emitter.instruction(Instruction::I32Const(0));
7586 let inst = match op {
7587 BinOp::Lt => Instruction::I32LtS,
7588 BinOp::Gt => Instruction::I32GtS,
7589 BinOp::Le => Instruction::I32LeS,
7590 BinOp::Ge => Instruction::I32GeS,
7591 _ => {
7592 return Err(crate::codegen::internal_failure(
7593 "emit_bigint_cmp_inline called with non-cmp op",
7594 ));
7595 }
7596 };
7597 emitter.instruction(inst);
7598 Ok(())
7599}
7600
7601fn emit_bigint_cmp_eq_inline(
7603 emitter: &mut FunctionEmitter,
7604 ctx: &CodegenCtx,
7605 lhs: ExprId,
7606 rhs: ExprId,
7607 op: BinOp,
7608) -> Result<(), crate::compiler_error::CompilerFailure> {
7609 emit_bigint_cmp_call(emitter, ctx, lhs, rhs)?;
7610 emitter.instruction(Instruction::I32Eqz);
7611 let _: () = if matches!(op, BinOp::NotEq) {
7612 emitter.instruction(Instruction::I32Eqz);
7613 };
7614 Ok(())
7615}
7616
7617fn emit_bigint_cmp_call(
7620 emitter: &mut FunctionEmitter,
7621 ctx: &CodegenCtx,
7622 lhs: ExprId,
7623 rhs: ExprId,
7624) -> Result<(), crate::compiler_error::CompilerFailure> {
7625 emit_expr(emitter, ctx, lhs)?;
7626 emit_bigint_extract_to_stack(emitter, ctx);
7627 let raw_bigint_idx = ctx
7628 .symbols
7629 .raw_bigint_type_idx()
7630 .ok_or_else(|| crate::codegen::internal_failure("$rawBigInt registered"))?;
7631 let lhs_limbs =
7632 emitter.add_anonymous_local(wasm_encoder::ValType::Ref(wasm_encoder::RefType {
7633 nullable: false,
7634 heap_type: wasm_encoder::HeapType::Concrete(raw_bigint_idx),
7635 }))?;
7636 let lhs_sign = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
7637 emitter.instruction(Instruction::LocalSet(lhs_limbs));
7638 emitter.instruction(Instruction::LocalSet(lhs_sign));
7639 emitter.instruction(Instruction::LocalGet(lhs_sign));
7640 emitter.instruction(Instruction::LocalGet(lhs_limbs));
7641 emit_expr(emitter, ctx, rhs)?;
7642 emit_bigint_extract_to_stack(emitter, ctx);
7643 let cmp_idx = ctx
7644 .symbols
7645 .func_idx(&crate::mangle::host(
7646 crate::runtime::BIGINT_MODULE_NAME,
7647 "cmp",
7648 ))
7649 .ok_or_else(|| {
7650 crate::codegen::internal_failure(
7651 "submilli:bigint.cmp import recorded by codegen bootstrap",
7652 )
7653 })?;
7654 emitter.instruction(Instruction::Call(cmp_idx));
7655 Ok(())
7656}
7657
7658fn evaluate_object_members(
7660 emitter: &mut FunctionEmitter<'_>,
7661 ctx: &CodegenCtx<'_>,
7662 members: &[TypedObjectMember],
7663) -> Result<HashMap<ExprId, u32>, crate::compiler_error::CompilerFailure> {
7664 let intrinsics = ctx
7665 .symbols
7666 .intrinsic_type_indices()
7667 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
7668 let mut values = HashMap::new();
7669 for member in members {
7670 let TypedObjectMember::Value(value_id) = *member else {
7671 return Err(crate::codegen::internal_failure(
7672 "spread field reached plain object lowering",
7673 ));
7674 };
7675 let value_ty = &ctx
7676 .ta
7677 .try_expr(value_id)
7678 .map_err(crate::codegen::arena_failure)?
7679 .ty;
7680 emit_expr(emitter, ctx, value_id)?;
7681 cast::emit_box(emitter, ctx, value_ty)?;
7682 let local = emitter.add_anonymous_local(object_ref(intrinsics.object))?;
7683 emitter.instruction(Instruction::LocalSet(local));
7684 values.insert(value_id, local);
7685 }
7686 Ok(values)
7687}
7688
7689fn emit_field_holds_value_of(
7692 emitter: &mut FunctionEmitter<'_>,
7693 ctx: &CodegenCtx<'_>,
7694 object: u32,
7695 index: u32,
7696 value: u32,
7697 field_ty: &Type,
7698) -> Result<(), crate::compiler_error::CompilerFailure> {
7699 let intrinsics = ctx
7700 .symbols
7701 .intrinsic_type_indices()
7702 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
7703 emitter.instruction(Instruction::LocalGet(object));
7704 emitter.instruction(Instruction::StructGet {
7705 struct_type_index: intrinsics.object_shape,
7706 field_index: 1,
7707 });
7708 emitter.instruction(Instruction::LocalGet(index));
7709 emitter.instruction(Instruction::ArrayGet(intrinsics.field_names));
7710 crate::codegen::field_names::emit_name_presence(emitter, ctx)?;
7711 emitter.instruction(Instruction::LocalGet(value));
7712 emit_is_nullish(emitter, ctx)?;
7713 emitter.instruction(Instruction::I32Eqz);
7714 emitter.instruction(Instruction::I32Or);
7715 let _: () = if field_runtime_type_is_testable(field_ty) {
7716 emit_structural_test(emitter, ctx, value, field_ty, field_ty)?;
7717 emitter.instruction(Instruction::I32And);
7718 };
7719 Ok(())
7720}
7721
7722fn object_ref(object_type: u32) -> ValType {
7723 ValType::Ref(RefType {
7724 nullable: true,
7725 heap_type: HeapType::Concrete(object_type),
7726 })
7727}
7728
7729pub(super) fn emit_index_number(
7731 emitter: &mut FunctionEmitter,
7732 ctx: &CodegenCtx,
7733 ty: &Type,
7734) -> Result<(), crate::compiler_error::CompilerFailure> {
7735 if ty == &Type::Unknown {
7736 let function = ctx
7737 .symbols
7738 .prelude_func_idx("__value_to_index")
7739 .ok_or_else(|| crate::codegen::internal_failure("dynamic index helper collected"))?;
7740 emitter.instruction(Instruction::Call(function));
7741 } else {
7742 cast::emit_coerce_to_slot(emitter, ctx, ty, &Type::Number)?;
7743 };
7744 Ok(())
7745}
7746
7747fn emit_computed_object(
7748 emitter: &mut FunctionEmitter,
7749 ctx: &CodegenCtx,
7750 members: &[TypedObjectMember],
7751) -> Result<(), crate::compiler_error::CompilerFailure> {
7752 let Some(intr) = ctx.require(ctx.symbols.intrinsic_type_indices(), "intrinsics declared")
7753 else {
7754 return Ok(());
7755 };
7756 let Some(merge) = ctx.require(
7757 ctx.symbols.prelude_func_idx("ObjectConstructor##spread"),
7758 "spread helper imported",
7759 ) else {
7760 return Ok(());
7761 };
7762 let object = emitter.add_anonymous_local(ValType::Ref(RefType {
7763 nullable: false,
7764 heap_type: HeapType::Concrete(intr.object_shape),
7765 }))?;
7766 for _ in 0..4 {
7767 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
7768 }
7769 emitter.instruction(Instruction::Call(merge));
7770 emitter.instruction(Instruction::LocalSet(object));
7771 for member in members {
7772 emitter.instruction(Instruction::LocalGet(object));
7773 match member {
7774 TypedObjectMember::Computed { key, value } => {
7775 emit_expr(emitter, ctx, *key)?;
7776 crate::codegen::cast_check::emit_operation_cast_on_stack(
7777 emitter,
7778 ctx,
7779 &ctx.ta
7780 .try_expr(*key)
7781 .map_err(crate::codegen::arena_failure)?
7782 .ty,
7783 &Type::String,
7784 )?;
7785 emit_expr(emitter, ctx, *value)?;
7786 cast::emit_box(
7787 emitter,
7788 ctx,
7789 &ctx.ta
7790 .try_expr(*value)
7791 .map_err(crate::codegen::arena_failure)?
7792 .ty,
7793 )?;
7794 let Some(symbol) = ctx.require(
7795 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7796 "dynamic write imported",
7797 ) else {
7798 return Ok(());
7799 };
7800 emitter.instruction(Instruction::Call(symbol));
7801 }
7802 TypedObjectMember::Spread { source, .. } => {
7803 emit_expr(emitter, ctx, *source)?;
7804 for _ in 0..2 {
7805 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intr.object)));
7806 }
7807 emitter.instruction(Instruction::Call(merge));
7808 emitter.instruction(Instruction::LocalSet(object));
7809 }
7810 TypedObjectMember::Value(_) => {
7811 ctx.fail("computed literal contains an unlowered named field");
7812 return Ok(());
7813 }
7814 }
7815 }
7816 emitter.instruction(Instruction::LocalGet(object));
7817 Ok(())
7818}
7819
7820fn emit_object_index_postfix(
7821 emitter: &mut FunctionEmitter,
7822 ctx: &CodegenCtx,
7823 receiver: ExprId,
7824 index: ExprId,
7825 op: crate::PostfixOp,
7826 read_ty: &Type,
7827) -> Result<(), crate::compiler_error::CompilerFailure> {
7828 emit_expr(emitter, ctx, receiver)?;
7829 cast::emit_box(
7830 emitter,
7831 ctx,
7832 &ctx.ta
7833 .try_expr(receiver)
7834 .map_err(crate::codegen::arena_failure)?
7835 .ty,
7836 )?;
7837 let object = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7838 emitter.instruction(Instruction::LocalSet(object));
7839 emit_expr(emitter, ctx, index)?;
7840 crate::codegen::cast_check::emit_operation_cast_on_stack(
7841 emitter,
7842 ctx,
7843 &ctx.ta
7844 .try_expr(index)
7845 .map_err(crate::codegen::arena_failure)?
7846 .ty,
7847 &Type::String,
7848 )?;
7849 emit_record_postfix_on_stack(emitter, ctx, receiver, object, op, read_ty)
7850}
7851
7852fn emit_record_postfix_on_stack(
7854 emitter: &mut FunctionEmitter,
7855 ctx: &CodegenCtx,
7856 receiver: ExprId,
7857 object: u32,
7858 op: crate::PostfixOp,
7859 read_ty: &Type,
7860) -> Result<(), crate::compiler_error::CompilerFailure> {
7861 let key = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::String)?)?;
7862 emitter.instruction(Instruction::LocalSet(key));
7863 emitter.instruction(Instruction::LocalGet(object));
7864 emitter.instruction(Instruction::LocalGet(key));
7865 let Some(symbol) = ctx.require(
7866 ctx.symbols.prelude_func_idx("ObjectConstructor##getField"),
7867 "record read imported",
7868 ) else {
7869 return Ok(());
7870 };
7871 emitter.instruction(Instruction::Call(symbol));
7872 let label = crate::codegen::bounds::receiver_name(ctx.ta, receiver)?;
7873 emit_checked_record_read(
7874 emitter,
7875 ctx,
7876 read_ty,
7877 KeyOperand::Local(key),
7878 RecordProbe {
7879 record: Some(object),
7880 present_flag: None,
7881 },
7882 &label,
7883 )?;
7884 cast::emit_box(emitter, ctx, read_ty)?;
7885 emit_postfix_numeric(emitter, ctx, &Type::Unknown);
7886 let old = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7887 emitter.instruction(Instruction::LocalTee(old));
7888 emit_postfix_delta(emitter, ctx, op, &Type::Unknown);
7889 let updated = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
7890 emitter.instruction(Instruction::LocalSet(updated));
7891 emitter.instruction(Instruction::LocalGet(object));
7892 emitter.instruction(Instruction::LocalGet(key));
7893 emitter.instruction(Instruction::LocalGet(updated));
7894 let Some(symbol) = ctx.require(
7895 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
7896 "record write imported",
7897 ) else {
7898 return Ok(());
7899 };
7900 emitter.instruction(Instruction::Call(symbol));
7901 emitter.instruction(Instruction::LocalGet(old));
7902 Ok(())
7903}
7904
7905#[cfg(test)]
7906mod tests {
7907 use super::*;
7908 use crate::codegen::invariant_tests::{assert_internal, with_context};
7909 use crate::{FileId, Span, TypedAst, TypedExpr};
7910
7911 #[test]
7912 fn missing_method_dispatch_is_a_compiler_error() {
7913 with_context(
7914 &TypedAst::new(),
7915 &crate::codegen::SymbolTable::default(),
7916 |ctx| {
7917 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7918 let error = emit_method_call_with_receiver_on_stack(
7919 &mut emitter,
7920 ctx,
7921 &Type::Number,
7922 &crate::mangle::prelude("MissingInterface"),
7923 "missingMethod",
7924 &[],
7925 None,
7926 &[],
7927 None,
7928 &Type::Number,
7929 )
7930 .unwrap_err();
7931 assert!(error.to_string().contains("MissingInterface.missingMethod"));
7932 assert_internal(error);
7933 },
7934 );
7935 }
7936
7937 #[test]
7938 fn call_and_chain_metadata_are_checked_before_operands() {
7939 with_context(
7940 &TypedAst::new(),
7941 &crate::codegen::SymbolTable::default(),
7942 |ctx| {
7943 let mut emitter = FunctionEmitter::new(ctx, &[]).unwrap();
7944 assert_internal(
7945 emit_intrinsic_call(
7946 &mut emitter,
7947 ctx,
7948 Intrinsic::Assert,
7949 &[],
7950 &Type::Void,
7951 &Type::Void,
7952 )
7953 .unwrap_err(),
7954 );
7955 assert_internal(
7956 emit_intrinsic_call(
7957 &mut emitter,
7958 ctx,
7959 Intrinsic::BigIntFromString,
7960 &[],
7961 &Type::BigInt,
7962 &Type::BigInt,
7963 )
7964 .unwrap_err(),
7965 );
7966 assert_internal(
7967 emit_direct_call(
7968 &mut emitter,
7969 ctx,
7970 false,
7971 0,
7972 &[Type::Number],
7973 &Type::Void,
7974 &[],
7975 )
7976 .unwrap_err(),
7977 );
7978 assert_internal(
7979 emit_args_into_slots(&mut emitter, ctx, &[], Some(&[ValType::F64]))
7980 .unwrap_err(),
7981 );
7982 assert_internal(
7983 emit_chain_parts(
7984 &mut emitter,
7985 ctx,
7986 ExprId(0),
7987 &[],
7988 1,
7989 &Type::Number,
7990 &Type::Number,
7991 None,
7992 None,
7993 )
7994 .unwrap_err(),
7995 );
7996 assert_internal(
7997 emit_chain_parts(
7998 &mut emitter,
7999 ctx,
8000 ExprId(0),
8001 &[],
8002 0,
8003 &Type::Number,
8004 &Type::Number,
8005 Some(&[]),
8006 None,
8007 )
8008 .unwrap_err(),
8009 );
8010 },
8011 );
8012 }
8013
8014 #[test]
8015 fn uninterned_literal_fails_and_a_fresh_emitter_still_works() {
8016 let mut ta = TypedAst::new();
8017 let string = ta
8018 .try_push_expr(TypedExpr {
8019 kind: TypedExprKind::String("missing".into()),
8020 ty: Type::String,
8021 span: Span::at(FileId(0)),
8022 })
8023 .unwrap();
8024 let number = ta
8025 .try_push_expr(TypedExpr {
8026 kind: TypedExprKind::Number(3.0),
8027 ty: Type::Number,
8028 span: Span::at(FileId(0)),
8029 })
8030 .unwrap();
8031 with_context(&ta, &crate::codegen::SymbolTable::default(), |ctx| {
8032 let mut failed = FunctionEmitter::new(ctx, &[]).unwrap();
8033 assert_internal(emit_expr(&mut failed, ctx, string).unwrap_err());
8034 drop(failed);
8035 let mut healthy = FunctionEmitter::new(ctx, &[]).unwrap();
8036 emit_expr(&mut healthy, ctx, number).unwrap();
8037 healthy.instruction(Instruction::Drop);
8038 healthy.build().unwrap();
8039 });
8040 }
8041}