// Declarations lowering: static boxes and box declarations use super::{MirInstruction, ValueId}; use crate::ast::ASTNode; use crate::mir::slot_registry::{get_or_assign_type_id, reserve_method_slot}; use serde_json; use std::collections::HashSet; impl super::MirBuilder { /// Build static box (e.g., Main) - extracts main() method body and converts to Program /// Also lowers other static methods into standalone MIR functions: BoxName.method/N pub(super) fn build_static_main_box( &mut self, box_name: String, methods: std::collections::HashMap, ) -> Result { // Lower other static methods (except main) to standalone MIR functions so JIT can see them for (mname, mast) in methods.iter() { if mname == "main" { continue; } if let ASTNode::FunctionDeclaration { params, body, .. } = mast { // NamingBox 経由で static メソッド名を一元管理する let func_name = crate::mir::naming::encode_static_method(&box_name, mname, params.len()); self.lower_static_method_as_function(func_name, params.clone(), body.clone())?; } } // Within this lowering, treat `me` receiver as this static box let saved_static = self.current_static_box.clone(); self.current_static_box = Some(box_name.clone()); // Look for the main() method let out = if let Some(main_method) = methods.get("main") { if let ASTNode::FunctionDeclaration { params, body, .. } = main_method { // Optional: materialize a callable function entry "BoxName.main/N" for harness/PyVM. // This static entryは通常の VM 実行では使用されず、過去の Hotfix 4 絡みの loop/control-flow // バグの温床になっていたため、Phase 25.1m では明示トグルが立っている場合だけ生成する。 if std::env::var("NYASH_BUILD_STATIC_MAIN_ENTRY") .ok() .as_deref() == Some("1") { // NamingBox SSOT: Use encode_static_method for main/arity entry let func_name = crate::mir::naming::encode_static_method(&box_name, "main", params.len()); eprintln!( "[DEBUG] build_static_main_box: Before lower_static_method_as_function" ); eprintln!("[DEBUG] params.len() = {}", params.len()); eprintln!("[DEBUG] body.len() = {}", body.len()); eprintln!("[DEBUG] variable_map = {:?}", self.variable_map); // Note: Metadata clearing is now handled by BoxCompilationContext (箱理論) // See lifecycle.rs and builder_calls.rs for context swap implementation let _ = self.lower_static_method_as_function( func_name, params.clone(), body.clone(), ); eprintln!( "[DEBUG] build_static_main_box: After lower_static_method_as_function" ); eprintln!("[DEBUG] variable_map = {:?}", self.variable_map); } // Initialize local variables for Main.main() parameters // Note: These are local variables in the wrapper main() function, NOT parameters let saved_var_map = std::mem::take(&mut self.variable_map); let script_args = collect_script_args_from_env(); for p in params.iter() { // Allocate a value ID using the current function's value generator // This creates a local variable, not a parameter let pid = self.next_value_id(); if p == "args" { // new ArrayBox() with no args self.emit_instruction(MirInstruction::NewBox { dst: pid, box_type: "ArrayBox".to_string(), args: vec![], })?; self.value_origin_newbox.insert(pid, "ArrayBox".to_string()); self.value_types .insert(pid, super::MirType::Box("ArrayBox".to_string())); // Explicitly call birth() to initialize internal state self.emit_instruction(MirInstruction::BoxCall { dst: None, box_val: pid, method: "birth".to_string(), method_id: None, args: vec![], effects: super::EffectMask::MUT, })?; if let Some(args) = script_args.as_ref() { for arg in args { let val = crate::mir::builder::emission::constant::emit_string( self, arg.clone(), ); self.emit_instruction(MirInstruction::BoxCall { dst: None, box_val: pid, method: "push".to_string(), method_id: None, args: vec![val], effects: super::EffectMask::MUT, })?; } } } else { let v = crate::mir::builder::emission::constant::emit_void(self); // ensure pid holds the emitted const id self.emit_instruction(MirInstruction::Copy { dst: pid, src: v })?; crate::mir::builder::metadata::propagate::propagate(self, v, pid); } self.variable_map.insert(p.clone(), pid); // 関数スコープ SlotRegistry にも登録しておくよ(観測専用) if let Some(reg) = self.current_slot_registry.as_mut() { let ty = self.value_types.get(&pid).cloned(); reg.ensure_slot(p, ty); } } // Lower statements in order to preserve def→use let lowered = self.cf_block(body.clone()); self.variable_map = saved_var_map; lowered } else { Err("main method in static box is not a FunctionDeclaration".to_string()) } } else { Err("static box must contain a main() method".to_string()) }; // Restore static box context self.current_static_box = saved_static; out } /// Build box declaration: box Name { fields... methods... } pub(super) fn build_box_declaration( &mut self, name: String, methods: std::collections::HashMap, fields: Vec, weak_fields: Vec, ) -> Result<(), String> { // Create a type registration constant (marker) crate::mir::builder::emission::constant::emit_string(self, format!("__box_type_{}", name)); // Emit field metadata markers for field in fields { let _field_id = crate::mir::builder::emission::constant::emit_string( self, format!("__field_{}_{}", name, field), ); } // Record weak fields for this box if !weak_fields.is_empty() { let set: HashSet = weak_fields.into_iter().collect(); self.weak_fields_by_box.insert(name.clone(), set); } // Reserve method slots for user-defined instance methods (deterministic, starts at 4) let mut instance_methods: Vec = Vec::new(); for (mname, mast) in &methods { if let ASTNode::FunctionDeclaration { is_static, .. } = mast { if !*is_static { instance_methods.push(mname.clone()); } } } instance_methods.sort(); if !instance_methods.is_empty() { let tyid = get_or_assign_type_id(&name); for (i, m) in instance_methods.iter().enumerate() { let slot = 4u16.saturating_add(i as u16); reserve_method_slot(tyid, m, slot); } } // Emit markers for declared methods (kept as metadata hints) for (method_name, method_ast) in methods { if let ASTNode::FunctionDeclaration { .. } = method_ast { let _method_id = crate::mir::builder::emission::constant::emit_string( self, format!("__method_{}_{}", name, method_name), ); // Track unified member getters: __get_ | __get_once_ | __get_birth_ let kind_and_prop: Option<(super::PropertyKind, String)> = if let Some(rest) = method_name.strip_prefix("__get_once_") { Some((super::PropertyKind::Once, rest.to_string())) } else if let Some(rest) = method_name.strip_prefix("__get_birth_") { Some((super::PropertyKind::BirthOnce, rest.to_string())) } else if let Some(rest) = method_name.strip_prefix("__get_") { Some((super::PropertyKind::Computed, rest.to_string())) } else { None }; if let Some((k, prop)) = kind_and_prop { use std::collections::HashMap; let entry: &mut HashMap = self .property_getters_by_box .entry(name.clone()) .or_insert_with(HashMap::new); entry.insert(prop, k); } } } Ok(()) } } fn collect_script_args_from_env() -> Option> { let raw = std::env::var("NYASH_SCRIPT_ARGS_JSON") .or_else(|_| std::env::var("HAKO_SCRIPT_ARGS_JSON")) .ok()?; match serde_json::from_str::>(&raw) { Ok(list) if !list.is_empty() => Some(list), _ => None, } }