Files
hakorune/src/tests/policy_mutdeny.rs
nyash-codex 757b0fcfc9 feat(mir/builder): implement BoxCompilationContext for structural metadata isolation
箱理論の完璧な実装!各static boxコンパイルを独立したコンテキストで実行。

設計:
- BoxCompilationContext: variable_map, value_origin_newbox, value_types を箱化
- MirBuilder: compilation_context: Option<BoxCompilationContext> フィールド追加
- context swap: lower_static_method_as_function 開始/終了時に std::mem::swap
- 自動クリーンアップ: スコープ終了でコンテキスト破棄

実装:
1. src/mir/builder/context.rs: BoxCompilationContext構造体定義(テスト付き)
2. src/mir/builder.rs: compilation_contextフィールド追加、既存フィールドにコメント追加
3. src/mir/builder/lifecycle.rs: 各static boxでコンテキスト作成・破棄
4. src/mir/builder/builder_calls.rs: lower_static_method_as_functionでcontext swap
5. src/mir/builder/decls.rs, exprs.rs: 古いmanual clear()削除

効果:
 グローバル状態汚染を構造的に不可能化
 各static boxが完全に独立したコンテキストでコンパイル
 既存コード変更なし(swap技法で完全後方互換性)
 StageBArgsBox ValueId(21)エラー完全解決

箱理論的評価: 🟢 95点
- 明示的な境界: 各boxのコンテキストが物理的に分離
- 汚染不可能: 前の箱の状態が構造的に残らない
- 戻せる: コンテキスト差し替えで簡単ロールバック
- 美しい設計: スコープベースのリソース管理

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 11:28:18 +09:00

210 lines
6.6 KiB
Rust

#[cfg(feature = "cranelift-jit")]
#[test]
#[ignore]
fn jit_readonly_array_push_denied() {
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
// Ensure read-only policy is on
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
// Build: a = new ArrayBox(); a.push(3); ret a.len()
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let a = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a,
box_type: "ArrayBox".into(),
args: vec![],
});
let three = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: three,
value: ConstValue::Integer(3),
});
// push should be denied under read-only policy, effectively no-op for length
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a,
method: "push".into(),
args: vec![three],
method_id: None,
effects: EffectMask::PURE,
});
let ln = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(ln),
box_val: a,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m = MirModule::new("jit_readonly_array_push_denied".into());
m.add_function(f);
let out = crate::backend::cranelift_compile_and_execute(&m, "jit_readonly_array_push_denied")
.expect("JIT exec");
assert_eq!(
out.to_string_box().value,
"0",
"Array.push must be denied under read-only policy"
);
}
#[cfg(feature = "cranelift-jit")]
#[test]
#[ignore]
fn jit_readonly_map_set_denied() {
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
// Ensure read-only policy is on
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
// Build: m = new MapBox(); m.set("a", 2); ret m.size()
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let mbox = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: mbox,
box_type: "MapBox".into(),
args: vec![],
});
let key = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: key,
value: ConstValue::String("a".into()),
});
let val = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: val,
value: ConstValue::Integer(2),
});
// set should be denied under read-only policy
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: mbox,
method: "set".into(),
args: vec![key, val],
method_id: None,
effects: EffectMask::PURE,
});
let sz = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(sz),
box_val: mbox,
method: "size".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sz) });
let mut module = MirModule::new("jit_readonly_map_set_denied".into());
module.add_function(f);
let out = crate::backend::cranelift_compile_and_execute(&module, "jit_readonly_map_set_denied")
.expect("JIT exec");
assert_eq!(
out.to_string_box().value,
"0",
"Map.set must be denied under read-only policy"
);
}
// Engine-independent smoke: validate policy denial via host externs
#[cfg(any(feature = "vm-legacy", feature = "phi-legacy"))]
#[test]
fn extern_readonly_array_push_denied() {
use crate::backend::vm::VMValue;
use crate::boxes::array::ArrayBox;
use crate::jit::r#extern::collections as c;
use std::sync::Arc;
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
let arr = Arc::new(ArrayBox::new());
let recv = VMValue::BoxRef(arr.clone());
let val = VMValue::Integer(3);
let _ = c::array_push(&[recv.clone(), val]);
let len = c::array_len(&[recv]);
assert_eq!(len.to_string(), "0");
}
#[cfg(any(feature = "vm-legacy", feature = "phi-legacy"))]
#[test]
fn extern_readonly_map_set_denied() {
use crate::backend::vm::VMValue;
use crate::boxes::map_box::MapBox;
use crate::jit::r#extern::collections as c;
use std::sync::Arc;
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
let map = Arc::new(MapBox::new());
let recv = VMValue::BoxRef(map);
let key = VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new("a")));
let val = VMValue::Integer(2);
let _ = c::map_set(&[recv.clone(), key, val]);
let sz = c::map_size(&[recv]);
assert_eq!(sz.to_string(), "0");
}
#[cfg(any(feature = "vm-legacy", feature = "phi-legacy"))]
#[test]
fn extern_readonly_read_ops_allowed() {
use crate::backend::vm::VMValue;
use crate::boxes::{array::ArrayBox, map_box::MapBox};
use crate::jit::r#extern::collections as c;
use std::sync::Arc;
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
// Array: len/get are read-only
let arr = Arc::new(ArrayBox::new());
let recv_a = VMValue::BoxRef(arr.clone());
let len = c::array_len(&[recv_a.clone()]);
assert_eq!(len.to_string(), "0");
let zero = VMValue::Integer(0);
let got = c::array_get(&[recv_a.clone(), zero]);
assert_eq!(got.to_string(), "void");
// Map: size is read-only
let map = Arc::new(MapBox::new());
let recv_m = VMValue::BoxRef(map);
let size = c::map_size(&[recv_m]);
assert_eq!(size.to_string(), "0");
}