5.7 KiB
5.7 KiB
Technical Details
1. ライフサイクル設計の詳細
1.1 所有権の森(Ownership Forest)
A (strong owner)
├── B (strong)
│ └── D (strong)
└── C (strong)
└── E (weak) → B
不変条件:
- 各ノードの強参照in-degreeは最大1
- weakエッジは所有権に関与しない
- サイクルはweakエッジでのみ許可
1.2 決定的破棄順序
// ScopeTrackerの実装
impl ScopeTracker {
fn exit_scope(&mut self) {
// LIFO順で破棄
while let Some(box_id) = self.scope_stack.pop() {
if let Some(pbox) = self.get_box(box_id) {
// finiを呼ぶ
pbox.call_fini();
}
}
}
}
2. プラグインシステムの実装
2.1 TLVエンコーディング
// Type定義
const TLV_BOOL: u16 = 1; // 1 byte: 0 or 1
const TLV_I64: u16 = 3; // 8 bytes: little endian
const TLV_STRING: u16 = 4; // n bytes: UTF-8
const TLV_HANDLE: u16 = 8; // 8 bytes: type_id(4) + instance_id(4)
const TLV_VOID: u16 = 9; // 0 bytes
// エンコード例
fn encode_string(s: &str) -> Vec<u8> {
let mut buf = vec![];
buf.extend(&TLV_STRING.to_le_bytes());
buf.extend(&(s.len() as u16).to_le_bytes());
buf.extend(s.as_bytes());
buf
}
2.2 プラグイン実装例(StringBox)
// StringBoxプラグイン
struct StringInstance {
data: String,
}
static INSTANCES: Lazy<Mutex<HashMap<u32, StringInstance>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
#[no_mangle]
extern "C" fn nyash_plugin_invoke(
type_id: u32,
method_id: u32,
instance_id: u32,
args: *const u8,
args_len: usize,
result: *mut u8,
result_len: *mut usize,
) -> i32 {
match method_id {
METHOD_BIRTH => {
// 新しいインスタンス作成
let inst = StringInstance {
data: String::new(),
};
let id = INSTANCE_COUNTER.fetch_add(1, Ordering::SeqCst);
INSTANCES.lock().unwrap().insert(id, inst);
write_tlv_handle(type_id, id, result, result_len)
}
METHOD_LENGTH => {
// 文字列長を返す
let instances = INSTANCES.lock().unwrap();
if let Some(inst) = instances.get(&instance_id) {
let len = inst.data.len() as i64;
write_tlv_i64(len, result, result_len)
} else {
NYB_E_INVALID_HANDLE
}
}
METHOD_FINI => {
// インスタンス破棄(Dropが自動実行)
INSTANCES.lock().unwrap().remove(&instance_id);
NYB_OK
}
_ => NYB_E_INVALID_METHOD,
}
}
3. JIT統合
3.1 プラグイン呼び出しのJITコンパイル
// LowerCoreでのプラグイン呼び出し生成
fn lower_box_call(&mut self, method: &str, args: &[ValueId]) {
if let Ok(h) = plugin_host.resolve_method("StringBox", method) {
// スタックに引数を積む
self.builder.emit_param_i64(receiver_idx);
for arg in args {
self.push_value(arg);
}
// プラグイン呼び出し命令を生成
self.builder.emit_plugin_invoke(
h.type_id,
h.method_id,
args.len() + 1,
has_return
);
}
}
3.2 Craneliftでのコード生成
// nyash_plugin_invoke3_i64 シムの実装
extern "C" fn nyash_plugin_invoke3_i64(
type_id: i64,
method_id: i64,
argc: i64,
a0: i64, // receiver (param index)
a1: i64, // arg1
a2: i64, // arg2
) -> i64 {
// レシーバーをVMの引数から解決
let instance_id = resolve_instance_from_vm_args(a0);
// TLVエンコードで引数準備
let mut tlv_args = encode_tlv_header(argc - 1);
if argc >= 2 { encode_i64(&mut tlv_args, a1); }
if argc >= 3 { encode_i64(&mut tlv_args, a2); }
// プラグイン呼び出し
let mut result = [0u8; 32];
let mut result_len = result.len();
let rc = invoke_plugin(
type_id as u32,
method_id as u32,
instance_id,
&tlv_args,
&mut result,
&mut result_len
);
// 結果をデコードして返す
decode_tlv_i64(&result[..result_len]).unwrap_or(0)
}
4. GCオン/オフ等価性
4.1 アノテーション処理
enum BoxAnnotation {
MustDrop, // @must_drop - 即時破棄必須
GCable, // @gcable - GC可能(遅延OK)
}
impl GarbageCollector {
fn should_collect(&self, box_type: &BoxType) -> bool {
match box_type.annotation {
BoxAnnotation::MustDrop => false, // GC対象外
BoxAnnotation::GCable => self.gc_enabled,
}
}
}
4.2 観測不可能性の保証
// テストケース:GCオン/オフで同じ結果
#[test]
fn test_gc_equivalence() {
let output_gc_on = run_with_gc(true, "test.nyash");
let output_gc_off = run_with_gc(false, "test.nyash");
assert_eq!(output_gc_on.io_trace, output_gc_off.io_trace);
assert_eq!(output_gc_on.result, output_gc_off.result);
}
5. 静的リンクによる最適化
5.1 AOT変換
# NyashからCLIFへ
nyashc --emit-clif program.nyash > program.clif
# CLIFからオブジェクトファイルへ
cranelift-objdump program.clif -o program.o
# 静的リンク(PLT回避)
cc program.o -static \
-L. -lnyrt \
-lnyplug_array \
-lnyplug_string \
-o program
5.2 パフォーマンス比較
動的リンク(PLT経由): ~15ns/call
静的リンク(直接呼出): ~2ns/call
インライン展開後: ~0.5ns/call