Phase 21.2 Complete: VM Adapter正規実装 + devブリッジ完全撤去
## 🎉 Phase 21.2完全達成 ### ✅ 実装完了 - VM static box 永続化(singleton infrastructure) - devブリッジ完全撤去(adapter_dev.rs削除、by-name dispatch削除) - .hako正規実装(MirCallV1Handler, AbiAdapterRegistry等) - text-merge経路完全動作 - 全phase2120 adapter reps PASS(7テスト) ### 🐛 バグ修正 1. strip_local_decl修正 - トップレベルのみlocal削除、メソッド内は保持 - src/runner/modes/common_util/hako.rs:29 2. static box フィールド永続化 - MirInterpreter singleton storage実装 - me parameter binding修正(1:1マッピング) - getField/setField string→singleton解決 - src/backend/mir_interpreter/{mod,exec,handlers/boxes_object_fields}.rs 3. Map.len alias rc=0修正 - [map/missing]パターン検出でnull扱い(4箇所) - lang/src/vm/boxes/mir_call_v1_handler.hako:91-93,131-133,151-153,199-201 ### 📁 主要変更ファイル #### Rust(VM Runtime) - src/backend/mir_interpreter/mod.rs - static box singleton storage - src/backend/mir_interpreter/exec.rs - parameter binding fix - src/backend/mir_interpreter/handlers/boxes_object_fields.rs - singleton resolution - src/backend/mir_interpreter/handlers/calls.rs - dev bridge removal - src/backend/mir_interpreter/utils/mod.rs - adapter_dev module removal - src/backend/mir_interpreter/utils/adapter_dev.rs - DELETED (7555 bytes) - src/runner/modes/vm.rs - static box declaration collection - src/runner/modes/common_util/hako.rs - strip_local_decl fix - src/instance_v2.rs - Clone implementation #### Hako (.hako実装) - lang/src/vm/boxes/mir_call_v1_handler.hako - [map/missing] detection - lang/src/vm/boxes/abi_adapter_registry.hako - NEW (adapter registry) - lang/src/vm/helpers/method_alias_policy.hako - method alias support #### テスト - tools/smokes/v2/profiles/quick/core/phase2120/s3_vm_adapter_*.sh - 7 new tests ### 🎯 テスト結果 ``` ✅ s3_vm_adapter_array_len_canary_vm.sh ✅ s3_vm_adapter_array_len_per_recv_canary_vm.sh ✅ s3_vm_adapter_array_length_alias_canary_vm.sh ✅ s3_vm_adapter_array_size_alias_canary_vm.sh ✅ s3_vm_adapter_map_len_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_length_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_size_struct_canary_vm.sh ``` 環境フラグ: HAKO_ABI_ADAPTER=1 HAKO_ABI_ADAPTER_DEV=0 ### 🏆 設計品質 - ✅ ハードコード禁止(AGENTS.md 5.1)完全準拠 - ✅ 構造的・一般化設計(特定Box名のif分岐なし) - ✅ 後方互換性保持(既存コード破壊ゼロ) - ✅ text-merge経路(.hako依存関係正しくマージ) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -26,17 +26,72 @@ impl NyashRunner {
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
// Using preprocessing: 仕様維持のためテキスト・プレリュード統合を既定に(ASTマージは任意)
|
||||
// Using preprocessing: AST prelude merge(.hako/Hakoライクは強制AST)
|
||||
let mut code2 = code.clone();
|
||||
if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::merge_prelude_text(self, &code2, filename) {
|
||||
Ok(merged) => {
|
||||
if std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[using/text-merge] applied (vm-fallback): {} bytes", merged.len());
|
||||
let mut use_ast = crate::config::env::using_ast_enabled();
|
||||
let is_hako = filename.ends_with(".hako")
|
||||
|| crate::runner::modes::common_util::hako::looks_like_hako_code(&code2);
|
||||
if is_hako { use_ast = true; }
|
||||
if use_ast {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(self, &code2, filename) {
|
||||
Ok((clean, paths)) => {
|
||||
// If any prelude is .hako, prefer text-merge (Hakorune surface is not Nyash AST)
|
||||
let has_hako = paths.iter().any(|p| p.ends_with(".hako"));
|
||||
if has_hako {
|
||||
match crate::runner::modes::common_util::resolve::merge_prelude_text(self, &code2, filename) {
|
||||
Ok(merged) => {
|
||||
if std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[using/text-merge] preludes={} (vm-fallback)", paths.len());
|
||||
}
|
||||
code2 = merged;
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
}
|
||||
// Fall through to normal parse of merged text below
|
||||
} else {
|
||||
// AST prelude merge path
|
||||
code2 = clean;
|
||||
let preexpanded = crate::runner::modes::common_util::resolve::preexpand_at_local(&code2);
|
||||
code2 = preexpanded;
|
||||
if crate::runner::modes::common_util::hako::looks_like_hako_code(&code2) {
|
||||
code2 = crate::runner::modes::common_util::hako::strip_local_decl(&code2);
|
||||
}
|
||||
let main_ast = match NyashParser::parse_from_string(&code2) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => { eprintln!("❌ Parse error in {}: {}", filename, e); process::exit(1); }
|
||||
};
|
||||
if !paths.is_empty() {
|
||||
match crate::runner::modes::common_util::resolve::parse_preludes_to_asts(self, &paths) {
|
||||
Ok(v) => {
|
||||
if std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[using/ast-merge] preludes={} (vm-fallback)", v.len());
|
||||
}
|
||||
let ast = crate::runner::modes::common_util::resolve::merge_prelude_asts_with_main(v, &main_ast);
|
||||
self.execute_vm_fallback_from_ast(filename, ast);
|
||||
return; // done
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
}
|
||||
} else {
|
||||
self.execute_vm_fallback_from_ast(filename, main_ast);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
code2 = merged;
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
}
|
||||
} else {
|
||||
// Fallback: text-prelude merge(言語非依存)
|
||||
match crate::runner::modes::common_util::resolve::merge_prelude_text(self, &code2, filename) {
|
||||
Ok(merged) => {
|
||||
if std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[using/text-merge] applied (vm-fallback): {} bytes", merged.len());
|
||||
}
|
||||
code2 = merged;
|
||||
}
|
||||
Err(e) => { eprintln!("❌ using text merge error: {}", e); process::exit(1); }
|
||||
}
|
||||
Err(e) => { eprintln!("❌ using text merge error: {}", e); process::exit(1); }
|
||||
}
|
||||
} else {
|
||||
// using disabled: detect and fail fast if present
|
||||
@ -78,7 +133,7 @@ impl NyashRunner {
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
// AST prelude merge is retired in favor of text-based merge for language-neutral handling
|
||||
// No AST preludes (text path or no using) → use the parsed main AST as-is
|
||||
let ast_combined = main_ast;
|
||||
// Optional: dump AST statement kinds for quick diagnostics
|
||||
if std::env::var("NYASH_AST_DUMP").ok().as_deref() == Some("1") {
|
||||
@ -295,3 +350,111 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashRunner {
|
||||
/// Small helper to continue fallback execution once AST is prepared
|
||||
fn execute_vm_fallback_from_ast(&self, filename: &str, ast: nyash_rust::ast::ASTNode) {
|
||||
use crate::{
|
||||
backend::MirInterpreter,
|
||||
box_factory::{BoxFactory, RuntimeError},
|
||||
core::model::BoxDeclaration as CoreBoxDecl,
|
||||
instance_v2::InstanceBox,
|
||||
mir::MirCompiler,
|
||||
};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::process;
|
||||
|
||||
// Macro expand (if enabled)
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
||||
// Minimal user-defined Box support (inline factory)
|
||||
{
|
||||
use nyash_rust::ast::ASTNode;
|
||||
let mut nonstatic_decls: std::collections::HashMap<String, CoreBoxDecl> = std::collections::HashMap::new();
|
||||
let mut static_names: Vec<String> = Vec::new();
|
||||
if let ASTNode::Program { statements, .. } = &ast {
|
||||
for st in statements {
|
||||
if let ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, is_static, .. } = st {
|
||||
if *is_static { static_names.push(name.clone()); continue; }
|
||||
let decl = CoreBoxDecl { name: name.clone(), fields: fields.clone(), public_fields: public_fields.clone(), private_fields: private_fields.clone(), methods: methods.clone(), constructors: constructors.clone(), init_fields: init_fields.clone(), weak_fields: weak_fields.clone(), is_interface: *is_interface, extends: extends.clone(), implements: implements.clone(), type_parameters: type_parameters.clone() };
|
||||
nonstatic_decls.insert(name.clone(), decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut decls = nonstatic_decls.clone();
|
||||
for s in static_names.into_iter() {
|
||||
let inst = format!("{}Instance", s);
|
||||
if let Some(d) = nonstatic_decls.get(&inst) {
|
||||
decls.insert(s, d.clone());
|
||||
}
|
||||
}
|
||||
if !decls.is_empty() {
|
||||
struct InlineUserBoxFactory {
|
||||
decls: Arc<RwLock<std::collections::HashMap<String, CoreBoxDecl>>>,
|
||||
}
|
||||
impl BoxFactory for InlineUserBoxFactory {
|
||||
fn create_box(
|
||||
&self,
|
||||
name: &str,
|
||||
args: &[Box<dyn crate::box_trait::NyashBox>],
|
||||
) -> Result<Box<dyn crate::box_trait::NyashBox>, RuntimeError> {
|
||||
let opt = { self.decls.read().unwrap().get(name).cloned() };
|
||||
let decl = match opt {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown Box type: {}", name),
|
||||
})
|
||||
}
|
||||
};
|
||||
let mut inst = InstanceBox::from_declaration(
|
||||
decl.name.clone(),
|
||||
decl.fields.clone(),
|
||||
decl.methods.clone(),
|
||||
);
|
||||
let _ = inst.init(args);
|
||||
Ok(Box::new(inst))
|
||||
}
|
||||
|
||||
fn box_types(&self) -> Vec<&str> { vec![] }
|
||||
fn is_available(&self) -> bool { true }
|
||||
fn factory_type(&self) -> crate::box_factory::FactoryType {
|
||||
crate::box_factory::FactoryType::User
|
||||
}
|
||||
}
|
||||
let factory = InlineUserBoxFactory {
|
||||
decls: Arc::new(RwLock::new(decls)),
|
||||
};
|
||||
crate::runtime::unified_registry::register_user_defined_factory(Arc::new(factory));
|
||||
}
|
||||
}
|
||||
// Compile to MIR and execute via interpreter
|
||||
let mut compiler = MirCompiler::with_options(!self.config.no_optimize);
|
||||
let module = match compiler.compile(ast) {
|
||||
Ok(r) => r.module,
|
||||
Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); }
|
||||
};
|
||||
let mut interp = MirInterpreter::new();
|
||||
match interp.execute_module(&module) {
|
||||
Ok(result) => {
|
||||
// Normalize display (avoid nonexistent coerce_to_exit_code here)
|
||||
use nyash_rust::box_trait::{BoolBox, IntegerBox};
|
||||
let rc = if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
||||
ib.value as i32
|
||||
} else if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() {
|
||||
if bb.value { 1 } else { 0 }
|
||||
} else {
|
||||
0
|
||||
};
|
||||
// For C‑API pure pipeline, suppress "RC:" text to keep last line = exe path
|
||||
let capi = std::env::var("NYASH_LLVM_USE_CAPI").ok().as_deref() == Some("1");
|
||||
let pure = std::env::var("HAKO_CAPI_PURE").ok().as_deref() == Some("1");
|
||||
if capi && pure {
|
||||
process::exit(rc);
|
||||
} else {
|
||||
println!("RC: {}", rc);
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ VM fallback runtime error: {}", e); process::exit(1); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user