cleanup: Remove temporary file ExternCall test code
- Remove file static box from stdlib/mod.rs - Remove file.read/write/exists ExternCall handling from MIR builder - Delete test_file_ffi.nyash test file - Clean build successful
This commit is contained in:
43
docs/archive/gemini_dynamic_library_advice.md
Normal file
43
docs/archive/gemini_dynamic_library_advice.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Gemini先生のビルトインBox動的ライブラリ化アドバイス
|
||||||
|
|
||||||
|
2025-08-17
|
||||||
|
|
||||||
|
## 質問への回答
|
||||||
|
|
||||||
|
### 1. Rustで最も安定した動的ライブラリ実装方法は?
|
||||||
|
|
||||||
|
現時点で最も安定し、枯れている方法は**C ABI (`#[repr(C)]`, `extern "C"`) を利用する方法**です。
|
||||||
|
|
||||||
|
- **理由:** C ABIはあらゆる言語とプラットフォームでサポートされている、事実上の標準
|
||||||
|
- **実装:** `#[no_mangle]` と `extern "C"` を付け、`libloading` クレートを使用
|
||||||
|
|
||||||
|
### 2. `abi_stable` crateは本番環境で信頼できるか?
|
||||||
|
|
||||||
|
**はい、信頼できる選択肢です。** ただし、まずはC ABIで実装し、制約が問題になったら移行を検討。
|
||||||
|
|
||||||
|
### 3. プラグイン間の依存関係管理のベストプラクティスは?
|
||||||
|
|
||||||
|
**原則として、プラグイン間の直接的な依存は避けるべき。**
|
||||||
|
- 疎結合の維持: 各プラグインは`nyash-core`とのみ通信
|
||||||
|
- シンボル名の衝突回避: プラグイン名をプレフィックスに
|
||||||
|
|
||||||
|
### 4. セキュリティ(信頼できないプラグイン)の考慮は必要か?
|
||||||
|
|
||||||
|
**はい、絶対に必要です。**
|
||||||
|
- サンドボックス: WASM実行モードを残す
|
||||||
|
- 権限モデル: Capability-based security
|
||||||
|
- 署名検証: 将来的に暗号署名
|
||||||
|
|
||||||
|
### 5. 実装の落とし穴や注意点は?
|
||||||
|
|
||||||
|
- **メモリ管理:** 最大の落とし穴
|
||||||
|
- 所有権のルールを明確に
|
||||||
|
- データ生成側が解放関数も提供
|
||||||
|
- **`Arc<RwLock<T>>`の直接共有は不可能**
|
||||||
|
- ハンドルパターンを使用
|
||||||
|
- **エラーハンドリング**
|
||||||
|
- ステータスコードで返す
|
||||||
|
|
||||||
|
## 段階的移行戦略への評価
|
||||||
|
|
||||||
|
提案されている4フェーズの戦略は非常に現実的で、リスクが低く、素晴らしいです。
|
||||||
@ -1,46 +0,0 @@
|
|||||||
// FFI-ABI File I/O test
|
|
||||||
// 純粋FFI-ABI方式でのファイル操作デモ
|
|
||||||
|
|
||||||
using nyashstd
|
|
||||||
|
|
||||||
// テスト用のファイル名
|
|
||||||
local test_file = "test_output.txt"
|
|
||||||
|
|
||||||
// ファイルに書き込み
|
|
||||||
file.write(test_file, "Hello from Nyash FFI-ABI!\nThis is a test of file I/O.")
|
|
||||||
console.log("✅ File written: " + test_file)
|
|
||||||
|
|
||||||
// ファイルの存在確認
|
|
||||||
if file.exists(test_file) {
|
|
||||||
console.log("✅ File exists!")
|
|
||||||
|
|
||||||
// ファイルを読み込み
|
|
||||||
local content = file.read(test_file)
|
|
||||||
if content != null {
|
|
||||||
console.log("✅ File content:")
|
|
||||||
console.log(content)
|
|
||||||
|
|
||||||
// 内容を追記
|
|
||||||
file.write(test_file, content + "\nAdded line at: " + new StringBox("timestamp"))
|
|
||||||
console.log("✅ Content appended")
|
|
||||||
|
|
||||||
// 再度読み込み
|
|
||||||
local updated = file.read(test_file)
|
|
||||||
console.log("✅ Updated content:")
|
|
||||||
console.log(updated)
|
|
||||||
} else {
|
|
||||||
console.log("❌ Failed to read file")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("❌ File does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 存在しないファイルを読もうとする
|
|
||||||
local missing = file.read("non_existent_file.txt")
|
|
||||||
if missing == null {
|
|
||||||
console.log("✅ Non-existent file correctly returns null")
|
|
||||||
} else {
|
|
||||||
console.log("❌ Unexpected content from non-existent file")
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("\n🎉 FFI-ABI File I/O test complete!")
|
|
||||||
@ -926,48 +926,6 @@ impl MirBuilder {
|
|||||||
})?;
|
})?;
|
||||||
return Ok(void_id);
|
return Ok(void_id);
|
||||||
},
|
},
|
||||||
("file", "read") => {
|
|
||||||
// Generate ExternCall for file.read
|
|
||||||
let result_id = self.value_gen.next();
|
|
||||||
self.emit_instruction(MirInstruction::ExternCall {
|
|
||||||
dst: Some(result_id), // file.read returns string
|
|
||||||
iface_name: "env.file".to_string(),
|
|
||||||
method_name: "read".to_string(),
|
|
||||||
args: arg_values,
|
|
||||||
effects: EffectMask::IO, // File operations are I/O
|
|
||||||
})?;
|
|
||||||
return Ok(result_id);
|
|
||||||
},
|
|
||||||
("file", "write") => {
|
|
||||||
// Generate ExternCall for file.write
|
|
||||||
self.emit_instruction(MirInstruction::ExternCall {
|
|
||||||
dst: None, // file.write is void
|
|
||||||
iface_name: "env.file".to_string(),
|
|
||||||
method_name: "write".to_string(),
|
|
||||||
args: arg_values,
|
|
||||||
effects: EffectMask::IO, // File operations are I/O
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Return void value
|
|
||||||
let void_id = self.value_gen.next();
|
|
||||||
self.emit_instruction(MirInstruction::Const {
|
|
||||||
dst: void_id,
|
|
||||||
value: ConstValue::Void,
|
|
||||||
})?;
|
|
||||||
return Ok(void_id);
|
|
||||||
},
|
|
||||||
("file", "exists") => {
|
|
||||||
// Generate ExternCall for file.exists
|
|
||||||
let result_id = self.value_gen.next();
|
|
||||||
self.emit_instruction(MirInstruction::ExternCall {
|
|
||||||
dst: Some(result_id), // file.exists returns bool
|
|
||||||
iface_name: "env.file".to_string(),
|
|
||||||
method_name: "exists".to_string(),
|
|
||||||
args: arg_values,
|
|
||||||
effects: EffectMask::IO, // File operations are I/O
|
|
||||||
})?;
|
|
||||||
return Ok(result_id);
|
|
||||||
},
|
|
||||||
_ => {
|
_ => {
|
||||||
// Regular method call - continue with BoxCall
|
// Regular method call - continue with BoxCall
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,9 +65,6 @@ impl BuiltinStdlib {
|
|||||||
// console static box
|
// console static box
|
||||||
nyashstd.static_boxes.insert("console".to_string(), Self::create_console_box());
|
nyashstd.static_boxes.insert("console".to_string(), Self::create_console_box());
|
||||||
|
|
||||||
// file static box (FFI-ABI demonstration)
|
|
||||||
nyashstd.static_boxes.insert("file".to_string(), Self::create_file_box());
|
|
||||||
|
|
||||||
self.namespaces.insert("nyashstd".to_string(), nyashstd);
|
self.namespaces.insert("nyashstd".to_string(), nyashstd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,89 +222,4 @@ impl BuiltinStdlib {
|
|||||||
|
|
||||||
console_box
|
console_box
|
||||||
}
|
}
|
||||||
|
|
||||||
/// file static boxを作成 (FFI-ABI demonstration)
|
|
||||||
fn create_file_box() -> BuiltinStaticBox {
|
|
||||||
let mut file_box = BuiltinStaticBox {
|
|
||||||
name: "file".to_string(),
|
|
||||||
methods: HashMap::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// file.read(path) -> string (or null on error)
|
|
||||||
file_box.methods.insert("read".to_string(), |args| {
|
|
||||||
if args.len() != 1 {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: "file.read() takes exactly 1 argument".to_string()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringBoxにダウンキャスト
|
|
||||||
if let Some(path_arg) = args[0].as_any().downcast_ref::<StringBox>() {
|
|
||||||
// Rust標準ライブラリでファイル読み込み
|
|
||||||
match std::fs::read_to_string(&path_arg.value) {
|
|
||||||
Ok(content) => Ok(Box::new(StringBox::new(content))),
|
|
||||||
Err(_) => {
|
|
||||||
// エラー時はnullを返す
|
|
||||||
use crate::boxes::NullBox;
|
|
||||||
Ok(Box::new(NullBox::new()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::TypeError {
|
|
||||||
message: format!("file.read() expects string argument, got {:?}", args[0].type_name())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// file.write(path, content) -> void
|
|
||||||
file_box.methods.insert("write".to_string(), |args| {
|
|
||||||
if args.len() != 2 {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: "file.write() takes exactly 2 arguments".to_string()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 両方の引数をStringBoxにダウンキャスト
|
|
||||||
if let (Some(path_arg), Some(content_arg)) =
|
|
||||||
(args[0].as_any().downcast_ref::<StringBox>(),
|
|
||||||
args[1].as_any().downcast_ref::<StringBox>()) {
|
|
||||||
// Rust標準ライブラリでファイル書き込み
|
|
||||||
if let Err(e) = std::fs::write(&path_arg.value, &content_arg.value) {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: format!("Failed to write file: {}", e)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// VoidBoxを返す
|
|
||||||
use crate::box_trait::VoidBox;
|
|
||||||
Ok(Box::new(VoidBox::new()))
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::TypeError {
|
|
||||||
message: "file.write() expects two string arguments".to_string()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// file.exists(path) -> bool
|
|
||||||
file_box.methods.insert("exists".to_string(), |args| {
|
|
||||||
if args.len() != 1 {
|
|
||||||
return Err(RuntimeError::InvalidOperation {
|
|
||||||
message: "file.exists() takes exactly 1 argument".to_string()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringBoxにダウンキャスト
|
|
||||||
if let Some(path_arg) = args[0].as_any().downcast_ref::<StringBox>() {
|
|
||||||
// Rust標準ライブラリでファイル存在確認
|
|
||||||
let exists = std::path::Path::new(&path_arg.value).exists();
|
|
||||||
Ok(Box::new(BoolBox::new(exists)))
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::TypeError {
|
|
||||||
message: format!("file.exists() expects string argument, got {:?}", args[0].type_name())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
file_box
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user