diff --git a/Cargo.toml b/Cargo.toml index 14bbc765..910107e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,7 +111,7 @@ js-sys = "0.3" # WASM backend dependencies (Phase 8) wabt = "0.10" -wasmtime = "18.0" +wasmtime = "35.0.0" # GUI フレームワーク - only when gui feature is enabled egui = { version = "0.29", optional = true } diff --git a/docs/CURRENT_TASK.md b/docs/CURRENT_TASK.md index 92245bd0..f3013a50 100644 --- a/docs/CURRENT_TASK.md +++ b/docs/CURRENT_TASK.md @@ -1,4 +1,56 @@ -# 🎯 現在のタスク (2025-08-16 警告削減100%完了!) +# 🎯 現在のタスク (2025-08-16 Phase 9.77 WASM緊急復旧開始!) + +## 🚨 **Phase 9.77進行中: WASM緊急復旧作業** + +### ✅ **Task 1.1完了: BoxCall命令実装** +- **BoxCall実装**: toString(), print(), equals(), clone(), log()メソッド完全実装 ✅ +- **codegen.rs修正**: generate_box_call関数とヘルパー関数5個追加 ✅ +- **パターンマッチ追加**: MirInstruction::BoxCall対応 ✅ +- **ビルド成功**: コンパイルエラーなし ✅ + +### ✅ **Task 1.2完了: wasmtimeバージョン統一 + RuntimeImports** +- **wasmtime更新**: 18.0 → 35.0.0 完了 ✅ +- **RuntimeImports追加**: box_to_string, box_print, box_equals, box_clone 実装済み ✅ +- **ビルド成功**: バージョン互換性問題解決 ✅ + +### 🔄 **Task 1.3進行中: WASM出力UTF-8エラー修正** +**現状**: 「Generated WASM is not valid UTF-8」エラーが継続 +```bash +❌ Generated WASM is not valid UTF-8 +``` + +**実装済み修正**: +- wabt::wat2wasm呼び出し修正(wat_source.as_bytes()使用) +- UTF-8検証とデバッグ出力追加 +- しかしエラーメッセージの発生元が不明 + +**調査結果**: +- エラーメッセージがソースコード内に見つからない +- wabt crateまたは外部ツールから発生している可能性 +- 簡単なテストケース(`local result = 42`)でも同じエラー + +### 📋 **Phase 9.77緊急復旧計画** +詳細: [phase_9_77_wasm_emergency.md](docs/予定/native-plan/issues/phase_9_77_wasm_emergency.md) + +**Task 1.1**: ✅ BoxCall命令実装(完了) +**Task 1.2**: ✅ wasmtimeバージョン統一 + RuntimeImports(完了) +**Task 1.3**: 🔄 WASM出力エラー修正(進行中) + +### 🎯 **Copilotへの引き継ぎ事項** +1. **UTF-8エラーの原因特定** + - エラーメッセージ「Generated WASM is not valid UTF-8」の発生元調査 + - wabt::wat2wasm以外の場所でエラーが出ている可能性 + - runner.rsやmain.rsでのエラー処理確認 + +2. **デバッグアプローチ** + - WAT生成内容の詳細確認(デバッグ出力は実装済み) + - WASM生成パイプライン全体の調査 + - 最小再現テストケースでの検証 + +3. **修正候補** + - エラーメッセージの発生元を特定してから対応 + - WAT形式の検証強化 + - バイナリ出力処理の見直し ## 🎉 **Phase 9.75j完了: 警告削減100%達成!** diff --git a/local_tests/debug_wasm_generation.nyash b/local_tests/debug_wasm_generation.nyash new file mode 100644 index 00000000..581832e2 --- /dev/null +++ b/local_tests/debug_wasm_generation.nyash @@ -0,0 +1,8 @@ +// 🧪 最も単純なWASM生成テスト +// 変数操作も避けて、純粋な計算のみ + +static box Main { + main() { + return "成功" + } +} \ No newline at end of file diff --git a/local_tests/test_mir_wasm_minimal.nyash b/local_tests/test_mir_wasm_minimal.nyash new file mode 100644 index 00000000..92d73ecc --- /dev/null +++ b/local_tests/test_mir_wasm_minimal.nyash @@ -0,0 +1,17 @@ +// 🧪 MIR → WASM変換テスト(最小版) +// メソッド呼び出しを避けて基本演算のみテスト + +static box Main { + init { result } + + main() { + // 基本演算のみ(メソッド呼び出しなし) + local a = 10 + local b = 20 + local sum = a + b + local product = sum * 3 + + me.result = product + return "計算完了" + } +} \ No newline at end of file diff --git a/local_tests/test_mir_wasm_simple.nyash b/local_tests/test_mir_wasm_simple.nyash new file mode 100644 index 00000000..0f7269f1 --- /dev/null +++ b/local_tests/test_mir_wasm_simple.nyash @@ -0,0 +1,28 @@ +// 🧪 MIR → WASM変換テスト用シンプルプログラム +// 基本的な演算とBox操作をテスト + +static box Main { + init { console, result } + + main() { + me.console = new ConsoleBox() + me.console.log("🚀 MIR → WASM変換テスト開始") + + // 基本演算テスト + local a = 10 + local b = 20 + local sum = a + b + + me.console.log("計算結果: " + sum.toString()) + + // StringBox操作テスト + local greeting = "Hello" + local name = "WASM" + local message = greeting + " " + name + "!" + + me.console.log("メッセージ: " + message) + + me.result = sum + return "MIR → WASM変換テスト完了" + } +} \ No newline at end of file diff --git a/local_tests/test_simple_wasm.nyash b/local_tests/test_simple_wasm.nyash new file mode 100644 index 00000000..da18925c --- /dev/null +++ b/local_tests/test_simple_wasm.nyash @@ -0,0 +1 @@ +local result = 42 diff --git a/src/backend/wasm/codegen.rs b/src/backend/wasm/codegen.rs index a2d0e3df..9da43a40 100644 --- a/src/backend/wasm/codegen.rs +++ b/src/backend/wasm/codegen.rs @@ -403,6 +403,11 @@ impl WasmCodegen { Ok(instructions) }, + // Phase 9.77: BoxCall Implementation - Critical Box method calls + MirInstruction::BoxCall { dst, box_val, method, args, effects: _ } => { + self.generate_box_call(*dst, *box_val, method, args) + }, + // Unsupported instructions _ => Err(WasmError::UnsupportedInstruction( format!("Instruction not yet supported: {:?}", instruction) @@ -570,6 +575,117 @@ impl WasmCodegen { .copied() .ok_or_else(|| WasmError::CodegenError(format!("Local variable not found for ValueId: {:?}", value_id))) } + + /// Phase 9.77: Generate BoxCall method invocation + /// Implements critical Box methods: toString, print, equals, clone + fn generate_box_call(&mut self, dst: Option, box_val: ValueId, method: &str, args: &[ValueId]) -> Result, WasmError> { + match method { + "toString" => self.generate_to_string_call(dst, box_val), + "print" => self.generate_print_call(dst, box_val), + "equals" => self.generate_equals_call(dst, box_val, args), + "clone" => self.generate_clone_call(dst, box_val), + "log" => self.generate_log_call(dst, box_val, args), + _ => Err(WasmError::UnsupportedInstruction( + format!("Unsupported BoxCall method: {}", method) + )) + } + } + + /// Generate toString() method call - Box → String conversion + fn generate_to_string_call(&mut self, dst: Option, box_val: ValueId) -> Result, WasmError> { + let Some(dst) = dst else { + return Err(WasmError::CodegenError("toString() requires destination".to_string())); + }; + + Ok(vec![ + format!(";; toString() implementation for ValueId({})", box_val.as_u32()), + format!("local.get ${}", self.get_local_index(box_val)?), + "call $box_to_string".to_string(), + format!("local.set ${}", self.get_local_index(dst)?), + ]) + } + + /// Generate print() method call - Basic output + fn generate_print_call(&mut self, dst: Option, box_val: ValueId) -> Result, WasmError> { + let mut instructions = vec![ + format!(";; print() implementation for ValueId({})", box_val.as_u32()), + format!("local.get ${}", self.get_local_index(box_val)?), + "call $box_print".to_string(), + ]; + + // Store void result if destination is provided + if let Some(dst) = dst { + instructions.extend(vec![ + "i32.const 0".to_string(), // Void result + format!("local.set ${}", self.get_local_index(dst)?), + ]); + } + + Ok(instructions) + } + + /// Generate equals() method call - Box comparison + fn generate_equals_call(&mut self, dst: Option, box_val: ValueId, args: &[ValueId]) -> Result, WasmError> { + let Some(dst) = dst else { + return Err(WasmError::CodegenError("equals() requires destination".to_string())); + }; + + if args.len() != 1 { + return Err(WasmError::CodegenError( + format!("equals() expects 1 argument, got {}", args.len()) + )); + } + + Ok(vec![ + format!(";; equals() implementation for ValueId({}) == ValueId({})", box_val.as_u32(), args[0].as_u32()), + format!("local.get ${}", self.get_local_index(box_val)?), + format!("local.get ${}", self.get_local_index(args[0])?), + "call $box_equals".to_string(), + format!("local.set ${}", self.get_local_index(dst)?), + ]) + } + + /// Generate clone() method call - Box duplication + fn generate_clone_call(&mut self, dst: Option, box_val: ValueId) -> Result, WasmError> { + let Some(dst) = dst else { + return Err(WasmError::CodegenError("clone() requires destination".to_string())); + }; + + Ok(vec![ + format!(";; clone() implementation for ValueId({})", box_val.as_u32()), + format!("local.get ${}", self.get_local_index(box_val)?), + "call $box_clone".to_string(), + format!("local.set ${}", self.get_local_index(dst)?), + ]) + } + + /// Generate log() method call - Console logging (ConsoleBox.log) + fn generate_log_call(&mut self, dst: Option, box_val: ValueId, args: &[ValueId]) -> Result, WasmError> { + let mut instructions = vec![ + format!(";; log() implementation for ValueId({})", box_val.as_u32()), + ]; + + // Load box_val (ConsoleBox instance) + instructions.push(format!("local.get ${}", self.get_local_index(box_val)?)); + + // Load all arguments + for arg in args { + instructions.push(format!("local.get ${}", self.get_local_index(*arg)?)); + } + + // Call console log function + instructions.push("call $console_log".to_string()); + + // Store void result if destination is provided + if let Some(dst) = dst { + instructions.extend(vec![ + "i32.const 0".to_string(), // Void result + format!("local.set ${}", self.get_local_index(dst)?), + ]); + } + + Ok(instructions) + } } #[cfg(test)] diff --git a/src/backend/wasm/mod.rs b/src/backend/wasm/mod.rs index b18ae8c9..40f503e0 100644 --- a/src/backend/wasm/mod.rs +++ b/src/backend/wasm/mod.rs @@ -61,9 +61,36 @@ impl WasmBackend { // Generate WAT (WebAssembly Text) first for debugging let wat_text = self.compile_to_wat(mir_module)?; - // Convert WAT to WASM binary using wabt - wabt::wat2wasm(&wat_text) - .map_err(|e| WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e))) + // Phase 9.77 Task 1.3: Fix UTF-8 encoding error in WAT→WASM conversion + self.wat_to_wasm(&wat_text) + } + + /// Convert WAT text to WASM binary with proper UTF-8 handling + fn wat_to_wasm(&self, wat_source: &str) -> Result, WasmError> { + // Debug: Print WAT source for analysis + eprintln!("🔍 WAT Source Debug (length: {}):", wat_source.len()); + eprintln!("WAT Content:\n{}", wat_source); + + // UTF-8 validation to prevent encoding errors + if !wat_source.is_ascii() { + eprintln!("❌ WAT source contains non-ASCII characters"); + return Err(WasmError::WasmValidationError( + "WAT source contains non-ASCII characters".to_string() + )); + } + + eprintln!("✅ WAT source is ASCII-compatible"); + + // Convert to bytes as required by wabt::wat2wasm + eprintln!("🔄 Converting WAT to WASM bytes..."); + let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes()) + .map_err(|e| { + eprintln!("❌ wabt::wat2wasm failed: {}", e); + WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e)) + })?; + + eprintln!("✅ WASM conversion successful, {} bytes generated", wasm_bytes.len()); + Ok(wasm_bytes) } /// Compile MIR module to WAT text format (for debugging) diff --git a/src/backend/wasm/runtime.rs b/src/backend/wasm/runtime.rs index ea284bbc..df73b82c 100644 --- a/src/backend/wasm/runtime.rs +++ b/src/backend/wasm/runtime.rs @@ -89,6 +89,40 @@ impl RuntimeImports { result: None, }); + // Phase 9.77: BoxCall runtime functions + + // box_to_string - Convert any Box to string representation + self.imports.push(ImportFunction { + module: "env".to_string(), + name: "box_to_string".to_string(), + params: vec!["i32".to_string()], // box_ptr + result: Some("i32".to_string()), // string_box_ptr + }); + + // box_print - Print any Box to console + self.imports.push(ImportFunction { + module: "env".to_string(), + name: "box_print".to_string(), + params: vec!["i32".to_string()], // box_ptr + result: None, + }); + + // box_equals - Compare two Boxes for equality + self.imports.push(ImportFunction { + module: "env".to_string(), + name: "box_equals".to_string(), + params: vec!["i32".to_string(), "i32".to_string()], // box1_ptr, box2_ptr + result: Some("i32".to_string()), // bool result + }); + + // box_clone - Clone a Box + self.imports.push(ImportFunction { + module: "env".to_string(), + name: "box_clone".to_string(), + params: vec!["i32".to_string()], // box_ptr + result: Some("i32".to_string()), // cloned_box_ptr + }); + // Future: env.file_read, env.file_write for file I/O // Future: env.http_request for network access }