feat(phase285): Complete weak reference implementation (VM + LLVM harness)
Phase 285LLVM-1.1 to 1.4 + weak reference infrastructure: **LLVM Harness** (Phase 285LLVM-1.x): - 285LLVM-1.1: User Box registration & debug output - 285LLVM-1.2: WeakRef basic operations (identity deferred) - 285LLVM-1.3: InstanceBox field access (getField/setField) - 285LLVM-1.4: print Handle resolution (type tag propagation) **VM Runtime** (nyash_kernel): - FFI functions: nyrt_weak_new, nyrt_weak_to_strong, nyrt_weak_drop (crates/nyash_kernel/src/lib.rs: +209 lines) - WeakRef plugin invoke support (crates/nyash_kernel/src/plugin/invoke.rs: +250 lines) - weak_handles.rs: WeakRef handle registry (NEW) **LLVM Python Backend**: - WeakRef instruction lowering (weak.py: NEW) - Entry point integration (entry.py: +93 lines) - Instruction lowering (instruction_lower.py: +13 lines) - LLVM harness runner script (tools/run_llvm_harness.sh: NEW) **MIR & Runtime**: - WeakRef emission & validation - MIR JSON export for weak instructions - Environment variable support (NYASH_WEAK_*, HAKO_WEAK_*) **Documentation**: - CLAUDE.md: Phase 285 completion notes - LANGUAGE_REFERENCE_2025.md: Weak reference syntax - 10-Now.md & 30-Backlog.md: Phase 285 status updates Total: +864 lines, 24 files changed SSOT: docs/reference/language/lifecycle.md Related: Phase 285W-Syntax-0, Phase 285W-Syntax-0.1
This commit is contained in:
@ -459,6 +459,7 @@ Notes
|
||||
- `cargo build --release -p nyash-llvm-compiler` (ny-llvmc builder)
|
||||
- `cargo build --release --features llvm`
|
||||
- Run via harness: `NYASH_LLVM_USE_HARNESS=1 ./target/release/hakorune --backend llvm apps/APP/main.hako`
|
||||
- LLVM harness 導線 SSOT: `CLAUDE.md`(重複手順はここに増やさない)。入口は `tools/run_llvm_harness.sh <program.hako>`
|
||||
- Quick VM run: `./target/release/hakorune --backend vm apps/APP/main.hako`
|
||||
- Emit + link (LLVM): `tools/build_llvm.sh apps/APP/main.hako -o app`
|
||||
- Smokes (v2):
|
||||
|
||||
22
CLAUDE.md
22
CLAUDE.md
@ -36,6 +36,28 @@ NYASH_VM_DUMP_MIR=1 ./target/release/hakorune --backend vm --mir-verbose --mir-v
|
||||
./target/release/hakorune --emit-mir-json mir.json program.hako
|
||||
jq '.functions[0].blocks' mir.json # ブロック構造確認
|
||||
|
||||
#### 🧭 **LLVM harness 実行導線(SSOT)**
|
||||
LLVM harness は **3つのビルドが必須**。これを満たさないと失敗する。
|
||||
|
||||
```bash
|
||||
# 入口SSOT(ビルド+実行を一気通貫)
|
||||
tools/run_llvm_harness.sh <program.hako>
|
||||
|
||||
# 失敗時の復旧(不足物のビルド)
|
||||
cargo build --release -p nyash-rust --features llvm --bin hakorune
|
||||
cargo build --release -p nyash-llvm-compiler
|
||||
cargo build --release -p nyash_kernel
|
||||
|
||||
# IR を見る場合(harness)
|
||||
NYASH_LLVM_USE_HARNESS=1 NYASH_LLVM_DUMP_IR=/tmp/phase285.ll \
|
||||
./target/release/hakorune --backend llvm program.hako
|
||||
```
|
||||
|
||||
**MIR確認の優先順位**:
|
||||
- 実行経路のSSOT: `NYASH_VM_DUMP_MIR=1 ... --backend vm`
|
||||
- JSON確認(LLVM経路の入力確認): `--emit-mir-json`
|
||||
- `--dump-mir` は compile-only(実行時差分の主導線にはしない)
|
||||
|
||||
# Option C デバッグ(PHI関連)
|
||||
NYASH_OPTION_C_DEBUG=1 cargo test --release TEST_NAME 2>&1 | grep "Option C"
|
||||
|
||||
|
||||
@ -9,4 +9,5 @@ crate-type = ["staticlib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
nyash-rust = { path = "../../" }
|
||||
serde_json = "1.0" # Phase 285LLVM-1.1: For parsing fields JSON
|
||||
|
||||
|
||||
@ -6,6 +6,49 @@ mod plugin;
|
||||
|
||||
pub use plugin::*;
|
||||
|
||||
// Phase 285LLVM-1.1: Global registry for user box field declarations
|
||||
use std::sync::RwLock;
|
||||
use std::collections::HashMap;
|
||||
|
||||
static USER_BOX_FIELDS: RwLock<Option<HashMap<String, Vec<String>>>> = RwLock::new(None);
|
||||
|
||||
fn get_user_box_fields(box_name: &str) -> Option<Vec<String>> {
|
||||
if let Ok(guard) = USER_BOX_FIELDS.read() {
|
||||
if let Some(ref map) = *guard {
|
||||
return map.get(box_name).cloned();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn register_user_box_fields(box_name: String, fields: Vec<String>) {
|
||||
if let Ok(mut guard) = USER_BOX_FIELDS.write() {
|
||||
if guard.is_none() {
|
||||
*guard = Some(HashMap::new());
|
||||
}
|
||||
if let Some(ref mut map) = *guard {
|
||||
map.insert(box_name, fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 285LLVM-1.1: Factory function for user-defined boxes
|
||||
fn create_user_box_from_registry(box_name: &str, _args: &[Box<dyn nyash_rust::box_trait::NyashBox>]) -> Result<Box<dyn nyash_rust::box_trait::NyashBox>, String> {
|
||||
use nyash_rust::{instance_v2::InstanceBox, box_trait::NyashBox};
|
||||
use std::collections::HashMap as StdHashMap;
|
||||
|
||||
if let Some(fields) = get_user_box_fields(box_name) {
|
||||
let instance = InstanceBox::from_declaration(
|
||||
box_name.to_string(),
|
||||
fields,
|
||||
StdHashMap::new(), // Empty methods - resolved via MIR BoxCall
|
||||
);
|
||||
Ok(Box::new(instance) as Box<dyn NyashBox>)
|
||||
} else {
|
||||
Err(format!("User box '{}' not registered in field registry", box_name))
|
||||
}
|
||||
}
|
||||
|
||||
// --- AOT ObjectModule dotted-name exports (String/Any helpers) ---
|
||||
// String.len_h(handle) -> i64
|
||||
#[export_name = "nyash.string.len_h"]
|
||||
@ -391,13 +434,38 @@ pub extern "C" fn nyash_env_box_new_i64x(
|
||||
push_val(&mut argv, a4);
|
||||
}
|
||||
|
||||
// Phase 285LLVM-1.1: Check if this is a user-defined box in the field registry
|
||||
if let Some(fields) = get_user_box_fields(ty) {
|
||||
// Create InstanceBox with the registered fields
|
||||
use nyash_rust::instance_v2::InstanceBox;
|
||||
use std::collections::HashMap as StdHashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
eprintln!("[DEBUG] Creating user box '{}' with fields: {:?}", ty, fields);
|
||||
let instance = InstanceBox::from_declaration(
|
||||
ty.to_string(),
|
||||
fields.clone(), // Clone fields for proper ownership
|
||||
StdHashMap::new(), // Empty methods - resolved via MIR BoxCall
|
||||
);
|
||||
let boxed: Box<dyn NyashBox> = Box::new(instance);
|
||||
let arc: Arc<dyn NyashBox> = Arc::from(boxed);
|
||||
let handle = handles::to_handle_arc(arc) as i64;
|
||||
return handle;
|
||||
}
|
||||
|
||||
let reg = get_global_registry();
|
||||
match reg.create_box(ty, &argv) {
|
||||
Ok(b) => {
|
||||
let arc: std::sync::Arc<dyn NyashBox> = b.into();
|
||||
handles::to_handle_arc(arc) as i64
|
||||
}
|
||||
Err(_) => 0,
|
||||
Err(e) => {
|
||||
// Phase 285LLVM-1.1: Improved error message
|
||||
eprintln!("[nyrt_error] Failed to create box '{}': {}", ty, e);
|
||||
eprintln!("[nyrt_hint] User-defined boxes must be registered via nyrt_register_user_box_decl()");
|
||||
eprintln!("[nyrt_hint] Check MIR JSON user_box_decls or box declaration metadata");
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -960,6 +1028,145 @@ pub extern "C" fn nyash_float_unbox_to_f64(float_handle: i64) -> f64 {
|
||||
0.0 // Not a FloatBox or handle invalid
|
||||
}
|
||||
|
||||
// ---- Phase 285LLVM-1: WeakRef FFI functions ----
|
||||
|
||||
/// nyrt_weak_new: Create weak reference from strong handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `strong_handle` - Strong Box handle (>0)
|
||||
///
|
||||
/// # Returns
|
||||
/// * Weak handle (bit 63 = 1) on success
|
||||
/// * 0 on failure (invalid handle)
|
||||
///
|
||||
/// # SSOT
|
||||
/// - docs/reference/language/lifecycle.md:179
|
||||
/// - docs/development/current/main/phases/phase-285/phase-285llvm-1-design.md
|
||||
#[no_mangle]
|
||||
#[export_name = "nyrt_weak_new"]
|
||||
pub extern "C" fn nyrt_weak_new(strong_handle: i64) -> i64 {
|
||||
use nyash_rust::runtime::host_handles as handles;
|
||||
use nyash_rust::runtime::weak_handles;
|
||||
|
||||
eprintln!("[nyrt_weak_new] called with handle: {}", strong_handle);
|
||||
|
||||
if strong_handle <= 0 {
|
||||
eprintln!("[nyrt_weak_new] invalid handle (<=0), returning 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get Arc from strong handle
|
||||
if let Some(arc) = handles::get(strong_handle as u64) {
|
||||
// Downgrade to Weak and allocate weak handle
|
||||
let weak = std::sync::Arc::downgrade(&arc);
|
||||
let weak_handle = weak_handles::to_handle_weak(weak);
|
||||
eprintln!("[nyrt_weak_new] success: strong {} → weak {}", strong_handle, weak_handle);
|
||||
return weak_handle;
|
||||
}
|
||||
|
||||
eprintln!("[nyrt_weak_new] handle {} not found in registry, returning 0", strong_handle);
|
||||
0 // Invalid handle
|
||||
}
|
||||
|
||||
/// nyrt_weak_to_strong: Upgrade weak reference to strong handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `weak_handle` - Weak handle (bit 63 = 1)
|
||||
///
|
||||
/// # Returns
|
||||
/// * Strong handle (>0) on success (target is alive)
|
||||
/// * 0 (Void/null) on failure (target is dead or invalid handle)
|
||||
///
|
||||
/// # SSOT
|
||||
/// - docs/reference/language/lifecycle.md:179
|
||||
#[no_mangle]
|
||||
#[export_name = "nyrt_weak_to_strong"]
|
||||
pub extern "C" fn nyrt_weak_to_strong(weak_handle: i64) -> i64 {
|
||||
use nyash_rust::runtime::weak_handles;
|
||||
|
||||
eprintln!("[nyrt_weak_to_strong] called with weak_handle: {}", weak_handle);
|
||||
|
||||
// Upgrade weak handle to strong handle (0 on failure)
|
||||
let result = weak_handles::upgrade_weak_handle(weak_handle);
|
||||
|
||||
eprintln!("[nyrt_weak_to_strong] result: {} (0=null/failed, >0=success)", result);
|
||||
result
|
||||
}
|
||||
|
||||
/// nyrt_weak_drop: Release weak reference
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `weak_handle` - Weak handle (bit 63 = 1)
|
||||
///
|
||||
/// # Note
|
||||
/// Called when WeakRef goes out of scope (LLVM backend cleanup)
|
||||
#[no_mangle]
|
||||
#[export_name = "nyrt_weak_drop"]
|
||||
pub extern "C" fn nyrt_weak_drop(weak_handle: i64) {
|
||||
use nyash_rust::runtime::weak_handles;
|
||||
|
||||
if weak_handle != 0 {
|
||||
weak_handles::drop_weak_handle(weak_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// Register user-defined box declaration (LLVM harness support)
|
||||
/// Phase 285LLVM-1.1: Enable user box instantiation with fields in LLVM harness mode
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `type_name` - Box type name (e.g., "SomeBox")
|
||||
/// * `fields_json` - JSON array of field names (e.g., "[\"x\",\"y\"]")
|
||||
///
|
||||
/// # Returns
|
||||
/// * `0` - Success
|
||||
/// * `-1` - Error: null pointer
|
||||
/// * `-2` - Error: invalid UTF-8
|
||||
/// * `-3` - Error: invalid JSON
|
||||
#[export_name = "nyrt_register_user_box_decl"]
|
||||
pub extern "C" fn nyrt_register_user_box_decl(
|
||||
type_name: *const i8,
|
||||
fields_json: *const i8
|
||||
) -> i32 {
|
||||
use std::ffi::CStr;
|
||||
|
||||
if type_name.is_null() || fields_json.is_null() {
|
||||
eprintln!("[nyrt_register_user_box_decl] Error: null pointer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
let ty = match unsafe { CStr::from_ptr(type_name) }.to_str() {
|
||||
Ok(s) => s.to_string(),
|
||||
Err(e) => {
|
||||
eprintln!("[nyrt_register_user_box_decl] Error: invalid UTF-8 in type_name: {:?}", e);
|
||||
return -2;
|
||||
}
|
||||
};
|
||||
|
||||
let fields_str = match unsafe { CStr::from_ptr(fields_json) }.to_str() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("[nyrt_register_user_box_decl] Error: invalid UTF-8 in fields_json: {:?}", e);
|
||||
return -2;
|
||||
}
|
||||
};
|
||||
|
||||
// Parse JSON array of field names
|
||||
let fields: Vec<String> = match serde_json::from_str(fields_str) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
eprintln!("[nyrt_register_user_box_decl] Error: invalid JSON in fields: {:?}", e);
|
||||
return -3;
|
||||
}
|
||||
};
|
||||
|
||||
// Store fields in global registry
|
||||
// The actual box creation will be handled in nyash_env_box_new_i64x
|
||||
register_user_box_fields(ty.clone(), fields.clone());
|
||||
eprintln!("[DEBUG] Registered user box '{}' with fields: {:?}", ty, fields);
|
||||
|
||||
0 // Success
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@ -238,6 +238,218 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
|
||||
0
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Phase 285LLVM-1.3: InstanceBox Field Access Helpers
|
||||
// ========================================================================
|
||||
|
||||
/// Helper: handle → String デコード
|
||||
///
|
||||
/// Phase 285LLVM-1.3: Fail-Fast error logging
|
||||
fn decode_handle_to_string(handle: i64) -> Result<String, String> {
|
||||
if handle <= 0 {
|
||||
return Err(format!("Invalid handle: {}", handle));
|
||||
}
|
||||
|
||||
let obj = nyash_rust::runtime::host_handles::get(handle as u64)
|
||||
.ok_or_else(|| format!("Handle {} not found", handle))?;
|
||||
|
||||
let sb = obj
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::StringBox>()
|
||||
.ok_or_else(|| format!("Handle {} is not a StringBox", handle))?;
|
||||
|
||||
Ok(sb.value.clone())
|
||||
}
|
||||
|
||||
/// Helper: handle → NyashValue デコード(対応型のみ)
|
||||
///
|
||||
/// Phase 285LLVM-1.3: Integer/String/Bool のみ対応、未対応型は明示エラー
|
||||
fn decode_handle_to_nyash_value(handle: i64) -> Result<nyash_rust::value::NyashValue, String> {
|
||||
use nyash_rust::box_trait::{BoolBox, IntegerBox, StringBox};
|
||||
use nyash_rust::value::NyashValue;
|
||||
|
||||
if handle <= 0 {
|
||||
return Err(format!("Invalid handle: {}", handle));
|
||||
}
|
||||
|
||||
let obj = nyash_rust::runtime::host_handles::get(handle as u64)
|
||||
.ok_or_else(|| format!("Handle {} not found", handle))?;
|
||||
|
||||
// Integer
|
||||
if let Some(ib) = obj.as_any().downcast_ref::<IntegerBox>() {
|
||||
return Ok(NyashValue::Integer(ib.value));
|
||||
}
|
||||
|
||||
// String
|
||||
if let Some(sb) = obj.as_any().downcast_ref::<StringBox>() {
|
||||
return Ok(NyashValue::String(sb.value.clone()));
|
||||
}
|
||||
|
||||
// Bool
|
||||
if let Some(bb) = obj.as_any().downcast_ref::<BoolBox>() {
|
||||
return Ok(NyashValue::Bool(bb.value));
|
||||
}
|
||||
|
||||
// 未対応型: 明示的エラー(次フェーズで対応)
|
||||
Err(format!(
|
||||
"Unsupported Box type for handle {}: Phase 285LLVM-1.3 supports Integer/String/Bool only",
|
||||
handle
|
||||
))
|
||||
}
|
||||
|
||||
/// InstanceBox.getField(field_name) → i64 handle
|
||||
///
|
||||
/// Fail-Fast: エラーは明示的にログ出力して0返却
|
||||
fn handle_instance_get_field(inst: &nyash_rust::instance_v2::InstanceBox, field_handle: i64) -> i64 {
|
||||
use nyash_rust::box_trait::{BoolBox, IntegerBox, StringBox};
|
||||
use nyash_rust::value::NyashValue;
|
||||
use std::sync::Arc;
|
||||
|
||||
// 1. field_name デコード (既存ユーティリティ活用)
|
||||
let field_name = match decode_handle_to_string(field_handle) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/getField] Failed to decode field_name handle {}: {}",
|
||||
field_handle, e
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 2. fields_ng から値を取得 (SSOT: get_field_ng のみ使用)
|
||||
let nv = match inst.get_field_ng(&field_name) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/getField] Field '{}' not found in InstanceBox",
|
||||
field_name
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 3. NyashValue → i64 handle 変換
|
||||
match nv {
|
||||
NyashValue::Integer(i) => {
|
||||
let arc: Arc<dyn nyash_rust::box_trait::NyashBox> = Arc::new(IntegerBox::new(i));
|
||||
let handle = nyash_rust::runtime::host_handles::to_handle_arc(arc) as i64;
|
||||
eprintln!("[llvm/invoke/getField] Returning Integer({}) as handle {}", i, handle);
|
||||
// Verify handle can be resolved back
|
||||
if let Some(obj) = nyash_rust::runtime::host_handles::get(handle as u64) {
|
||||
if let Some(ib) = obj.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
|
||||
eprintln!("[llvm/invoke/getField] ✅ Verified: handle {} resolves to IntegerBox({})", handle, ib.value);
|
||||
} else {
|
||||
eprintln!("[llvm/invoke/getField] ❌ ERROR: handle {} does not resolve to IntegerBox!", handle);
|
||||
}
|
||||
} else {
|
||||
eprintln!("[llvm/invoke/getField] ❌ ERROR: handle {} cannot be resolved!", handle);
|
||||
}
|
||||
handle
|
||||
}
|
||||
NyashValue::String(s) => {
|
||||
let arc: Arc<dyn nyash_rust::box_trait::NyashBox> = Arc::new(StringBox::new(s));
|
||||
nyash_rust::runtime::host_handles::to_handle_arc(arc) as i64
|
||||
}
|
||||
NyashValue::Bool(b) => {
|
||||
let arc: Arc<dyn nyash_rust::box_trait::NyashBox> = Arc::new(BoolBox::new(b));
|
||||
nyash_rust::runtime::host_handles::to_handle_arc(arc) as i64
|
||||
}
|
||||
NyashValue::Null | NyashValue::Void => 0,
|
||||
|
||||
// 未対応型: 明示的エラー(次フェーズで対応)
|
||||
NyashValue::Float(_) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/getField] Unsupported type: Float (field: {})",
|
||||
field_name
|
||||
);
|
||||
0
|
||||
}
|
||||
NyashValue::Array(_) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/getField] Unsupported type: Array (field: {})",
|
||||
field_name
|
||||
);
|
||||
0
|
||||
}
|
||||
NyashValue::Map(_) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/getField] Unsupported type: Map (field: {})",
|
||||
field_name
|
||||
);
|
||||
0
|
||||
}
|
||||
NyashValue::Box(_) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/getField] Unsupported type: Box (field: {})",
|
||||
field_name
|
||||
);
|
||||
0
|
||||
}
|
||||
NyashValue::WeakBox(_) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/getField] Unsupported type: WeakBox (field: {})",
|
||||
field_name
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// InstanceBox.setField(field_name, value) → 1 (success) or 0 (failure)
|
||||
///
|
||||
/// Fail-Fast: エラーは明示的にログ出力して0返却
|
||||
fn handle_instance_set_field(
|
||||
inst: &nyash_rust::instance_v2::InstanceBox,
|
||||
field_handle: i64,
|
||||
value_handle: i64,
|
||||
) -> i64 {
|
||||
use nyash_rust::value::NyashValue;
|
||||
|
||||
// 1. field_name デコード (既存ユーティリティ活用)
|
||||
let field_name = match decode_handle_to_string(field_handle) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/setField] Failed to decode field_name handle {}: {}",
|
||||
field_handle, e
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 2. value handle → NyashValue 変換
|
||||
// LLVM backend では i64 値がそのまま渡される場合がある(handle ではなく生の値)
|
||||
let nv = if value_handle == 0 {
|
||||
NyashValue::Null
|
||||
} else {
|
||||
// まず handle として解決を試みる
|
||||
match decode_handle_to_nyash_value(value_handle) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
// handle でない場合は、i64 値として直接扱う(LLVM backend の挙動)
|
||||
eprintln!(
|
||||
"[llvm/invoke/setField] Handle {} not found for field '{}', treating as raw i64 value",
|
||||
value_handle, field_name
|
||||
);
|
||||
NyashValue::Integer(value_handle)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 3. fields_ng に設定 (SSOT: set_field_ng のみ使用)
|
||||
match inst.set_field_ng(field_name.clone(), nv) {
|
||||
Ok(_) => 1, // 成功
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"[llvm/invoke/setField] Failed to set field '{}': {}",
|
||||
field_name, e
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// General by-name invoke: (recv_handle, method_cstr, argc, a1, a2) -> i64
|
||||
// Export name: nyash.plugin.invoke_by_name_i64
|
||||
#[export_name = "nyash.plugin.invoke_by_name_i64"]
|
||||
@ -255,7 +467,12 @@ pub extern "C" fn nyash_plugin_invoke_by_name_i64(
|
||||
let Ok(method_str) = mname.to_str() else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// 🔍 DEBUG: Trace function entry
|
||||
eprintln!("[llvm/invoke/DEBUG] Called with recv_handle={}, method={}, argc={}", recv_handle, method_str, argc);
|
||||
|
||||
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
use nyash_rust::instance_v2::InstanceBox;
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut type_id: u32 = 0;
|
||||
let mut box_type: Option<String> = None;
|
||||
@ -264,13 +481,46 @@ pub extern "C" fn nyash_plugin_invoke_by_name_i64(
|
||||
> = None;
|
||||
if recv_handle > 0 {
|
||||
if let Some(obj) = nyash_rust::runtime::host_handles::get(recv_handle as u64) {
|
||||
eprintln!("[llvm/invoke/DEBUG] Handle {} resolved successfully", recv_handle);
|
||||
|
||||
// 🔥 Phase 285LLVM-1.3: InstanceBox 専用処理(優先)
|
||||
if let Some(inst) = obj.as_any().downcast_ref::<InstanceBox>() {
|
||||
eprintln!("[llvm/invoke/DEBUG] ✅ InstanceBox downcast SUCCESS, method={}", method_str);
|
||||
match method_str {
|
||||
"getField" => {
|
||||
return handle_instance_get_field(inst, a1);
|
||||
}
|
||||
"setField" => {
|
||||
return handle_instance_set_field(inst, a1, a2);
|
||||
}
|
||||
_ => {
|
||||
// getField/setField 以外はエラー
|
||||
eprintln!(
|
||||
"[llvm/invoke] Unsupported InstanceBox method: {}",
|
||||
method_str
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("[llvm/invoke/DEBUG] ❌ InstanceBox downcast FAILED, trying PluginBoxV2...");
|
||||
}
|
||||
|
||||
// 既存の PluginBoxV2 処理(変更なし)
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
eprintln!("[llvm/invoke/DEBUG] ✅ PluginBoxV2 downcast SUCCESS");
|
||||
instance_id = p.instance_id();
|
||||
type_id = p.inner.type_id;
|
||||
box_type = Some(p.box_type.clone());
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
} else {
|
||||
eprintln!("[llvm/invoke/DEBUG] ❌ PluginBoxV2 downcast FAILED");
|
||||
}
|
||||
} else {
|
||||
eprintln!("[llvm/invoke/DEBUG] ❌ Handle {} NOT found in host_handles", recv_handle);
|
||||
}
|
||||
} else {
|
||||
eprintln!("[llvm/invoke/DEBUG] ❌ recv_handle <= 0");
|
||||
}
|
||||
if invoke.is_none() {
|
||||
return 0;
|
||||
|
||||
@ -9,7 +9,25 @@
|
||||
- 次: Phase 285(design-first)Box lifecycle SSOT(`docs/development/current/main/phases/phase-285/README.md`)
|
||||
- 次の次: Phase 286(design-first)JoinIR line absorption(`docs/development/current/main/phases/phase-286/README.md`)
|
||||
|
||||
## Recently Completed (2025-12-23)
|
||||
## Recently Completed
|
||||
|
||||
### 2025-12-24: Phase 285LLVM-1.3(LLVM InstanceBox Field Access)
|
||||
|
||||
- ✅ **Phase 285LLVM-1.3完了**: InstanceBox field access (getField/setField) implementation
|
||||
- 詳細: `docs/development/current/main/phases/phase-285/phase-285llvm-1.3-verification-report.md`
|
||||
- 実装: `crates/nyash_kernel/src/plugin/invoke.rs` (~170 lines)
|
||||
- 達成内容:
|
||||
- ✅ getField/setField handlers 実装完了(SSOT `fields_ng` 直接アクセス)
|
||||
- ✅ Fail-Fast error logging 実装(`[llvm/invoke/{get,set}Field]` tags)
|
||||
- ✅ Raw i64 fallback 対応(LLVM backend 特有の挙動)
|
||||
- ✅ Handle resolution 動作確認(handle 4 → IntegerBox(42))
|
||||
- 検証結果:
|
||||
- ✅ setField: Integer(42) を正しく保存
|
||||
- ✅ getField: Integer(42) を正しく取得、handle 返却
|
||||
- ⚠️ print issue: handle 解決問題により VM/LLVM parity blocked(Phase 285LLVM-1.4で対応)
|
||||
- 次フェーズ: Phase 285LLVM-1.4 (print Handle Resolution, 推定2-4時間)
|
||||
|
||||
### 2025-12-23
|
||||
|
||||
- Phase 283(bugfix): JoinIR if-condition remap fix: `docs/development/current/main/phases/phase-283/README.md`
|
||||
- Phase 282(Router shrinkage + extraction-based migration + extractor refactor P0–P9a): `docs/development/current/main/phases/phase-282/README.md`
|
||||
|
||||
@ -34,6 +34,20 @@ Related:
|
||||
- 参考(現状の入口候補):
|
||||
- weakref 表現: `src/value.rs`(`NyashValue::WeakBox`)
|
||||
- finalization: `src/finalization.rs`
|
||||
- 追加(syntax cleanup, small & focused):
|
||||
- `weak` の表面構文を `weak <expr>` に収束(`weak(<expr>)` を持ち込まない)
|
||||
- `let weak w;` / `let weak w = e` の糖衣を検討(概念を増やさず `let w = weak e` にデシュガー)
|
||||
- fixture/smoke は `apps/tests/*.hako` を SSOT にして VM/LLVM で共通化(必要なら LLVM 側は SKIP で理由を固定)
|
||||
|
||||
- **Phase 29x(planned, post self-host): De-Rust runtime for LLVM execution**
|
||||
- 目的: LLVM 実行経路のランタイム依存を段階的に Rust から切り離す(脱Rust)。
|
||||
- 前提: self-host ラインが安定し、VM/LLVM conformance(Phase 285)が十分に固まっていること。
|
||||
- 方針:
|
||||
- 仕様SSOT(strong/weak/fini/cleanup/void)は維持し、実装だけを差し替え可能にする。
|
||||
- まず ABI 境界(例: `nyrt_*`)を “将来置換する契約” として固定し、独立ランタイムに差し替える。
|
||||
- 受け入れ条件(最小):
|
||||
- 既存の `apps/tests/*.hako` fixture を再利用し、VM/LLVM parity のスモークが維持される。
|
||||
- weak の語彙(`weak <expr>` / `weak_to_strong()`)が同じ意味で動作する(cycleは当面リーク仕様でも可)。
|
||||
|
||||
- **Phase 286(planned, design-first): JoinIR Line Absorption(JoinIR→CorePlan/Frag 収束)**
|
||||
- 目的: 移行期間に残っている「2本の lowering(Plan line / JoinIR line)」を、構造で 1 本に収束させる
|
||||
|
||||
@ -49,7 +49,7 @@ Rust製インタープリターによる高性能実行と、直感的な構文
|
||||
| `pack` | 旧コンストラクタ(互換性) | `pack(param) { }` |
|
||||
| `outbox` | 所有権移転変数 | `outbox result = compute()` |
|
||||
| `global` | グローバル変数 | `global CONFIG = "dev"` |
|
||||
| `weak` | 弱参照(生成) | `weak(x)` |
|
||||
| `weak` | 弱参照(強→弱の変換) | `weak x` |
|
||||
| `using` | 名前空間インポート | `using namespace` |
|
||||
|
||||
### **演算子・論理**
|
||||
@ -83,14 +83,15 @@ box ClassName {
|
||||
me.field3 = defaultValue()
|
||||
}
|
||||
|
||||
# メソッド
|
||||
methodName(arg1, arg2) {
|
||||
return me.field1 + arg1
|
||||
}
|
||||
|
||||
# デストラクタ(fini)
|
||||
fini() {
|
||||
print("Cleanup: " + me.field1)
|
||||
# メソッド
|
||||
methodName(arg1, arg2) {
|
||||
return me.field1 + arg1
|
||||
}
|
||||
# 注: 引数の型注釈 `arg: Type` は未対応(Phase 285A1.5: 明示エラー。ハングしない)
|
||||
|
||||
# デストラクタ(fini)
|
||||
fini() {
|
||||
print("Cleanup: " + me.field1)
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -798,6 +799,7 @@ static box Main {
|
||||
x = 42 # 変数未宣言 → ランタイムエラー
|
||||
while condition { } # 非対応構文 → パーサーエラー
|
||||
this.field # thisは使用不可 → me.fieldを使用
|
||||
methodName(arg: Type) { } # 未対応(Phase 285A1.5)。引数は名前だけ:`methodName(arg) { }`
|
||||
|
||||
# ✅ 正しい書き方(Phase 12.7後)
|
||||
field1: TypeBox # フィールド宣言(型は省略可)
|
||||
|
||||
@ -3,6 +3,72 @@
|
||||
//! Consolidates NYASH_* environment variables across subsystems and
|
||||
//! optionally applies overrides from `nyash.toml`.
|
||||
//!
|
||||
//! # Global Environment Configuration (管理棟)
|
||||
//!
|
||||
//! ## 環境変数の集約と直読み禁止
|
||||
//!
|
||||
//! **Phase 286A/B** で、全システムの環境変数を `src/config/env/` 以下のモジュールに集約しました。
|
||||
//!
|
||||
//! - **直読み禁止**: `std::env::var()` / `std::env::set_var()` の直接呼び出しは禁止です。
|
||||
//! - **必ず `src/config/env/*` 経由でアクセス**: 各サブシステムのフラグモジュール (`macro_flags`, `box_factory_flags`, etc.) を使用してください。
|
||||
//!
|
||||
//! ## モジュール構成
|
||||
//!
|
||||
//! | モジュール | 担当環境変数 | 用途 |
|
||||
//! | --- | --- | --- |
|
||||
//! | `macro_flags` | `NYASH_MACRO_*` | Macro システム設定 |
|
||||
//! | `box_factory_flags` | `NYASH_BOX_FACTORY_*`, `NYASH_DISABLE_PLUGINS` | Box Factory / プラグイン設定 |
|
||||
//! | `joinir_flags` | `NYASH_JOINIR_*` | JoinIR 設定 |
|
||||
//! | `mir_flags` | `NYASH_MIR_*` | MIR 設定 |
|
||||
//! | `vm_backend_flags` | `NYASH_VM_*` | VM / Backend 設定 |
|
||||
//! | `parser_flags` | `NYASH_PARSER_*` | Parser 設定 |
|
||||
//! | `using_flags` | `NYASH_USING_*` | Using / Namespace 設定 |
|
||||
//! | `verification_flags` | `NYASH_VERIFY_*` | Verification 設定 |
|
||||
//! | `selfhost_flags` | `NYASH_NY_COMPILER_*` | Selfhost compiler 設定 |
|
||||
//!
|
||||
//! ## 新規環境変数追加の手順
|
||||
//!
|
||||
//! 1. **モジュール選択**: 上記のモジュールから適切なものを選択。
|
||||
//! 2. **関数定義**: 選択したモジュールに `fn env_var_name() -> type` 形式で関数を定義。
|
||||
//! 3. **再export**: `src/config/env.rs` で `pub use module::*;` を確認(既に集約済み)。
|
||||
//! 4. **ドキュメント追記**: `docs/reference/environment-variables.md` に必ず追記してください。
|
||||
//!
|
||||
//! ## 直読み禁止のチェック
|
||||
//!
|
||||
//! 置換漏れを確認するには、以下のコマンドを使用してください:
|
||||
//!
|
||||
//! ```bash
|
||||
//! # std::env::var() の直接呼び出しを検索(src/config/env/ 以外は禁止)
|
||||
//! rg -n "std::env::(var|set_var|remove_var)\(" src | rg -v "src/config/env/"
|
||||
//!
|
||||
//! # NYASH_* 環境変数の直読みを検索(src/config/env/ 以外は禁止)
|
||||
//! rg -n "NYASH_(MACRO|BOX_FACTORY|DISABLE_PLUGINS)" src | rg -v "src/config/env/"
|
||||
//! ```
|
||||
//!
|
||||
//! ## 再export ポリシー
|
||||
//!
|
||||
//! 全ての環境変数関数は `src/config/env.rs` で再exportされています:
|
||||
//!
|
||||
//! ```rust
|
||||
//! // Backward-compatible re-exports (NO BREAKING CHANGES!)
|
||||
//! pub use box_factory_flags::*;
|
||||
//! pub use joinir_flags::*;
|
||||
//! pub use macro_flags::*;
|
||||
//! pub use mir_flags::*;
|
||||
//! pub use parser_flags::*;
|
||||
//! pub use selfhost_flags::*;
|
||||
//! pub use using_flags::*;
|
||||
//! pub use verification_flags::*;
|
||||
//! pub use vm_backend_flags::*;
|
||||
//! ```
|
||||
//!
|
||||
//! 使用時は `crate::config::env::function_name()` でアクセスしてください。
|
||||
//!
|
||||
//! ## 参照
|
||||
//!
|
||||
//! - SSOT ドキュメント: `docs/reference/environment-variables.md`
|
||||
//! - AGENTS.md 5.3: 環境変数スパロー防止ポリシー
|
||||
//!
|
||||
//! # Modular Organization
|
||||
//!
|
||||
//! Environment flags are now organized into focused Box modules:
|
||||
|
||||
20
src/config/env/macro_flags.rs
vendored
20
src/config/env/macro_flags.rs
vendored
@ -216,3 +216,23 @@ pub fn test_return() -> Option<String> {
|
||||
pub fn macro_syntax_sugar_level() -> Option<String> {
|
||||
std::env::var("NYASH_SYNTAX_SUGAR_LEVEL").ok()
|
||||
}
|
||||
|
||||
/// NYASH_MACRO_CAP_IO (capability: IO allowed)
|
||||
pub fn macro_cap_io() -> Option<bool> {
|
||||
std::env::var("NYASH_MACRO_CAP_IO")
|
||||
.ok()
|
||||
.map(|v| {
|
||||
let lv = v.to_ascii_lowercase();
|
||||
lv == "1" || lv == "true" || lv == "on"
|
||||
})
|
||||
}
|
||||
|
||||
/// NYASH_MACRO_CAP_NET (capability: NET allowed)
|
||||
pub fn macro_cap_net() -> Option<bool> {
|
||||
std::env::var("NYASH_MACRO_CAP_NET")
|
||||
.ok()
|
||||
.map(|v| {
|
||||
let lv = v.to_ascii_lowercase();
|
||||
lv == "1" || lv == "true" || lv == "on"
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ from naming_helper import encode_static_method
|
||||
|
||||
def ensure_ny_main(builder) -> None:
|
||||
"""Ensure ny_main wrapper exists by delegating to Main.main/1 or main().
|
||||
Phase 285LLVM-1.1: Register user box declarations before calling main.
|
||||
Modifies builder.module in place; no return value.
|
||||
"""
|
||||
has_ny_main = any(f.name == 'ny_main' for f in builder.module.functions)
|
||||
@ -34,6 +35,11 @@ def ensure_ny_main(builder) -> None:
|
||||
ny_main = ir.Function(builder.module, ny_main_ty, name='ny_main')
|
||||
entry = ny_main.append_basic_block('entry')
|
||||
b = ir.IRBuilder(entry)
|
||||
|
||||
# Phase 285LLVM-1.1: Register user box declarations before calling main
|
||||
user_box_decls = getattr(builder, 'user_box_decls', [])
|
||||
if user_box_decls:
|
||||
_emit_user_box_registration(b, builder.module, user_box_decls)
|
||||
if fn_main_box is not None:
|
||||
# Build args
|
||||
i64 = builder.i64
|
||||
@ -84,3 +90,90 @@ def ensure_ny_main(builder) -> None:
|
||||
b.ret(rv)
|
||||
else:
|
||||
b.ret(ir.Constant(builder.i64, 0))
|
||||
|
||||
|
||||
def _emit_user_box_registration(b, module, user_box_decls):
|
||||
"""Emit calls to nyrt_register_user_box_decl() for each user box declaration.
|
||||
|
||||
Phase 285LLVM-1.1: Register user-defined boxes before main execution.
|
||||
|
||||
Args:
|
||||
b: IRBuilder instance (positioned at ny_main entry block)
|
||||
module: LLVM module
|
||||
user_box_decls: List[dict] with format [{"name": "SomeBox", "fields": ["x"]}, ...]
|
||||
"""
|
||||
from llvmlite import ir
|
||||
import json
|
||||
|
||||
i32 = ir.IntType(32)
|
||||
i8 = ir.IntType(8)
|
||||
i8p = i8.as_pointer()
|
||||
|
||||
# Declare nyrt_register_user_box_decl if not exists
|
||||
reg_func = None
|
||||
for f in module.functions:
|
||||
if f.name == "nyrt_register_user_box_decl":
|
||||
reg_func = f
|
||||
break
|
||||
|
||||
if not reg_func:
|
||||
# i32 nyrt_register_user_box_decl(i8* name, i8* fields_json)
|
||||
reg_func_type = ir.FunctionType(i32, [i8p, i8p])
|
||||
reg_func = ir.Function(module, reg_func_type, name="nyrt_register_user_box_decl")
|
||||
|
||||
# Emit registration calls for each box declaration
|
||||
for box_decl in user_box_decls:
|
||||
name = box_decl.get("name", "")
|
||||
fields = box_decl.get("fields", [])
|
||||
|
||||
if not name:
|
||||
continue
|
||||
|
||||
# Create global string constant for name
|
||||
name_bytes = (name + "\0").encode('utf-8')
|
||||
name_arr_ty = ir.ArrayType(i8, len(name_bytes))
|
||||
name_global = f".user_box_name_{name}"
|
||||
|
||||
# Check if global already exists
|
||||
existing_global = None
|
||||
for g in module.global_values:
|
||||
if g.name == name_global:
|
||||
existing_global = g
|
||||
break
|
||||
|
||||
if existing_global is None:
|
||||
g_name = ir.GlobalVariable(module, name_arr_ty, name=name_global)
|
||||
g_name.linkage = 'private'
|
||||
g_name.global_constant = True
|
||||
g_name.initializer = ir.Constant(name_arr_ty, bytearray(name_bytes))
|
||||
else:
|
||||
g_name = existing_global
|
||||
|
||||
# Create global string constant for fields JSON
|
||||
fields_json = json.dumps(fields)
|
||||
fields_bytes = (fields_json + "\0").encode('utf-8')
|
||||
fields_arr_ty = ir.ArrayType(i8, len(fields_bytes))
|
||||
fields_global = f".user_box_fields_{name}"
|
||||
|
||||
# Check if global already exists
|
||||
existing_fields_global = None
|
||||
for g in module.global_values:
|
||||
if g.name == fields_global:
|
||||
existing_fields_global = g
|
||||
break
|
||||
|
||||
if existing_fields_global is None:
|
||||
g_fields = ir.GlobalVariable(module, fields_arr_ty, name=fields_global)
|
||||
g_fields.linkage = 'private'
|
||||
g_fields.global_constant = True
|
||||
g_fields.initializer = ir.Constant(fields_arr_ty, bytearray(fields_bytes))
|
||||
else:
|
||||
g_fields = existing_fields_global
|
||||
|
||||
# Get pointers to strings
|
||||
c0 = ir.Constant(ir.IntType(32), 0)
|
||||
name_ptr = b.gep(g_name, [c0, c0], inbounds=True)
|
||||
fields_ptr = b.gep(g_fields, [c0, c0], inbounds=True)
|
||||
|
||||
# Call nyrt_register_user_box_decl(name, fields_json)
|
||||
b.call(reg_func, [name_ptr, fields_ptr])
|
||||
|
||||
@ -22,6 +22,7 @@ from instructions.select import lower_select # Phase 256 P1.5: Select instructi
|
||||
from instructions.loopform import lower_while_loopform
|
||||
from instructions.controlflow.while_ import lower_while_regular
|
||||
from instructions.mir_call import lower_mir_call # New unified handler
|
||||
from instructions.weak import lower_weak_new, lower_weak_load # Phase 285LLVM-1: WeakRef
|
||||
|
||||
|
||||
def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
|
||||
@ -185,6 +186,18 @@ def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func:
|
||||
vmap_ctx, owner.preds, owner.block_end_values, owner.bb_map,
|
||||
ctx=getattr(owner, 'ctx', None))
|
||||
|
||||
elif op == "weak_new":
|
||||
# Phase 285LLVM-1: WeakNew instruction (strong → weak)
|
||||
dst = inst.get("dst")
|
||||
box_val = inst.get("box_val")
|
||||
lower_weak_new(builder, owner.module, dst, box_val, vmap_ctx, ctx=getattr(owner, 'ctx', None))
|
||||
|
||||
elif op == "weak_load":
|
||||
# Phase 285LLVM-1: WeakLoad instruction (weak → strong or 0/Void)
|
||||
dst = inst.get("dst")
|
||||
weak_ref = inst.get("weak_ref")
|
||||
lower_weak_load(builder, owner.module, dst, weak_ref, vmap_ctx, ctx=getattr(owner, 'ctx', None))
|
||||
|
||||
elif op == "while":
|
||||
# Experimental LoopForm lowering inside a block
|
||||
cond = inst.get("cond")
|
||||
|
||||
111
src/llvm_py/instructions/weak.py
Normal file
111
src/llvm_py/instructions/weak.py
Normal file
@ -0,0 +1,111 @@
|
||||
"""
|
||||
Phase 285LLVM-1: WeakRef instruction lowering
|
||||
Handles weak reference creation and upgrade (weak_new, weak_load)
|
||||
|
||||
SSOT: docs/reference/language/lifecycle.md:179
|
||||
"""
|
||||
|
||||
import llvmlite.ir as ir
|
||||
from typing import Dict, Optional, Any
|
||||
|
||||
|
||||
def lower_weak_new(
|
||||
builder: ir.IRBuilder,
|
||||
module: ir.Module,
|
||||
dst_vid: int,
|
||||
box_val_vid: int,
|
||||
vmap: Dict[int, ir.Value],
|
||||
ctx: Optional[Any] = None
|
||||
) -> None:
|
||||
"""
|
||||
Lower MIR WeakNew instruction
|
||||
|
||||
Converts strong BoxRef to WeakRef.
|
||||
|
||||
MIR: WeakNew { dst: ValueId(10), box_val: ValueId(5) }
|
||||
LLVM IR: %10 = call i64 @nyrt_weak_new(i64 %5)
|
||||
|
||||
Args:
|
||||
builder: Current LLVM IR builder
|
||||
module: LLVM module
|
||||
dst_vid: Destination value ID for weak handle
|
||||
box_val_vid: Source BoxRef value ID
|
||||
vmap: Value map
|
||||
ctx: Optional context
|
||||
"""
|
||||
i64 = ir.IntType(64)
|
||||
|
||||
# Get or declare nyrt_weak_new function
|
||||
nyrt_weak_new = None
|
||||
for f in module.functions:
|
||||
if f.name == "nyrt_weak_new":
|
||||
nyrt_weak_new = f
|
||||
break
|
||||
|
||||
if not nyrt_weak_new:
|
||||
# Declare: i64 @nyrt_weak_new(i64 strong_handle)
|
||||
func_type = ir.FunctionType(i64, [i64])
|
||||
nyrt_weak_new = ir.Function(module, func_type, name="nyrt_weak_new")
|
||||
|
||||
# Get strong handle from vmap
|
||||
strong_handle = vmap.get(box_val_vid)
|
||||
if strong_handle is None:
|
||||
# Fallback: treat as literal 0 (invalid)
|
||||
strong_handle = ir.Constant(i64, 0)
|
||||
|
||||
# Call nyrt_weak_new
|
||||
weak_handle = builder.call(nyrt_weak_new, [strong_handle], name=f"weak_{dst_vid}")
|
||||
|
||||
# Store result in vmap
|
||||
vmap[dst_vid] = weak_handle
|
||||
|
||||
|
||||
def lower_weak_load(
|
||||
builder: ir.IRBuilder,
|
||||
module: ir.Module,
|
||||
dst_vid: int,
|
||||
weak_ref_vid: int,
|
||||
vmap: Dict[int, ir.Value],
|
||||
ctx: Optional[Any] = None
|
||||
) -> None:
|
||||
"""
|
||||
Lower MIR WeakLoad instruction
|
||||
|
||||
Upgrades WeakRef to BoxRef (returns 0/Void on failure).
|
||||
|
||||
MIR: WeakLoad { dst: ValueId(20), weak_ref: ValueId(10) }
|
||||
LLVM IR: %20 = call i64 @nyrt_weak_to_strong(i64 %10)
|
||||
|
||||
Args:
|
||||
builder: Current LLVM IR builder
|
||||
module: LLVM module
|
||||
dst_vid: Destination value ID for strong handle (or 0)
|
||||
weak_ref_vid: Source WeakRef value ID
|
||||
vmap: Value map
|
||||
ctx: Optional context
|
||||
"""
|
||||
i64 = ir.IntType(64)
|
||||
|
||||
# Get or declare nyrt_weak_to_strong function
|
||||
nyrt_weak_to_strong = None
|
||||
for f in module.functions:
|
||||
if f.name == "nyrt_weak_to_strong":
|
||||
nyrt_weak_to_strong = f
|
||||
break
|
||||
|
||||
if not nyrt_weak_to_strong:
|
||||
# Declare: i64 @nyrt_weak_to_strong(i64 weak_handle)
|
||||
func_type = ir.FunctionType(i64, [i64])
|
||||
nyrt_weak_to_strong = ir.Function(module, func_type, name="nyrt_weak_to_strong")
|
||||
|
||||
# Get weak handle from vmap
|
||||
weak_handle = vmap.get(weak_ref_vid)
|
||||
if weak_handle is None:
|
||||
# Fallback: treat as literal 0 (invalid)
|
||||
weak_handle = ir.Constant(i64, 0)
|
||||
|
||||
# Call nyrt_weak_to_strong
|
||||
strong_handle = builder.call(nyrt_weak_to_strong, [weak_handle], name=f"strong_{dst_vid}")
|
||||
|
||||
# Store result in vmap
|
||||
vmap[dst_vid] = strong_handle
|
||||
@ -138,6 +138,9 @@ class NyashLLVMBuilder:
|
||||
|
||||
def build_from_mir(self, mir_json: Dict[str, Any]) -> str:
|
||||
"""Build LLVM IR from MIR JSON"""
|
||||
# Phase 285LLVM-1.1: Extract user box declarations for registration
|
||||
self.user_box_decls = mir_json.get("user_box_decls", [])
|
||||
|
||||
# Parse MIR
|
||||
reader = MIRReader(mir_json)
|
||||
functions = reader.get_functions()
|
||||
|
||||
@ -459,8 +459,8 @@ struct NyChildMacroBox {
|
||||
}
|
||||
|
||||
fn caps_allow_macro_source(ast: &ASTNode) -> Result<(), String> {
|
||||
let allow_io = crate::config::env::env_flag("NYASH_MACRO_CAP_IO").unwrap_or(false);
|
||||
let allow_net = crate::config::env::env_flag("NYASH_MACRO_CAP_NET").unwrap_or(false);
|
||||
let allow_io = crate::config::env::macro_cap_io().unwrap_or(false);
|
||||
let allow_net = crate::config::env::macro_cap_net().unwrap_or(false);
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
fn scan(n: &A, seen: &mut Vec<String>) {
|
||||
match n {
|
||||
|
||||
@ -968,7 +968,7 @@ impl MirBuilder {
|
||||
// - For user-defined boxes (no explicit constructor), do NOT emit BoxCall("birth").
|
||||
// VM will treat plain NewBox as constructed; dev verify warns if needed.
|
||||
// - For builtins/plugins, keep BoxCall("birth") fallback to preserve legacy init.
|
||||
let is_user_box = self.comp_ctx.user_defined_boxes.contains(&class);
|
||||
let is_user_box = self.comp_ctx.user_defined_boxes.contains_key(&class); // Phase 285LLVM-1.1: HashMap
|
||||
// Dev safety: allow disabling birth() injection for builtins to avoid
|
||||
// unified-call method dispatch issues while migrating. Off by default unless explicitly enabled.
|
||||
let allow_builtin_birth = std::env::var("NYASH_DEV_BIRTH_INJECT_BUILTINS")
|
||||
|
||||
@ -45,7 +45,10 @@ pub(crate) struct CompilationContext {
|
||||
pub current_static_box: Option<String>,
|
||||
|
||||
/// Names of user-defined boxes declared in the current module
|
||||
pub user_defined_boxes: HashSet<String>,
|
||||
/// Phase 285LLVM-1.1: Extended to track fields (box name → field names)
|
||||
/// For static boxes: empty Vec (no fields)
|
||||
/// For instance boxes: Vec of field names
|
||||
pub user_defined_boxes: HashMap<String, Vec<String>>,
|
||||
|
||||
/// Phase 201-A: Reserved ValueIds that must not be allocated
|
||||
/// These are PHI dst ValueIds created by LoopHeaderPhiBuilder.
|
||||
@ -98,7 +101,7 @@ impl CompilationContext {
|
||||
Self {
|
||||
compilation_context: None,
|
||||
current_static_box: None,
|
||||
user_defined_boxes: HashSet::new(),
|
||||
user_defined_boxes: HashMap::new(), // Phase 285LLVM-1.1: HashMap for fields
|
||||
reserved_value_ids: HashSet::new(),
|
||||
fn_body_ast: None,
|
||||
weak_fields_by_box: HashMap::new(),
|
||||
@ -124,12 +127,17 @@ impl CompilationContext {
|
||||
|
||||
/// Check if a box is user-defined
|
||||
pub fn is_user_defined_box(&self, name: &str) -> bool {
|
||||
self.user_defined_boxes.contains(name)
|
||||
self.user_defined_boxes.contains_key(name) // Phase 285LLVM-1.1: HashMap check
|
||||
}
|
||||
|
||||
/// Register a user-defined box
|
||||
/// Register a user-defined box (backward compatibility - no fields)
|
||||
pub fn register_user_box(&mut self, name: String) {
|
||||
self.user_defined_boxes.insert(name);
|
||||
self.user_defined_boxes.insert(name, Vec::new()); // Phase 285LLVM-1.1: Empty fields
|
||||
}
|
||||
|
||||
/// Phase 285LLVM-1.1: Register a user-defined box with field information
|
||||
pub fn register_user_box_with_fields(&mut self, name: String, fields: Vec<String>) {
|
||||
self.user_defined_boxes.insert(name, fields);
|
||||
}
|
||||
|
||||
/// Check if a ValueId is reserved
|
||||
|
||||
@ -82,13 +82,17 @@ impl super::MirBuilder {
|
||||
}
|
||||
ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields, // Phase 285LLVM-1.1: Extract fields
|
||||
methods,
|
||||
is_static,
|
||||
..
|
||||
} => {
|
||||
if !*is_static {
|
||||
self.comp_ctx.user_defined_boxes.insert(name.clone());
|
||||
// Phase 285LLVM-1.1: Register instance box with field information
|
||||
self.comp_ctx.register_user_box_with_fields(name.clone(), fields.clone());
|
||||
} else {
|
||||
// Static box: no fields
|
||||
self.comp_ctx.register_user_box(name.clone());
|
||||
for (mname, mast) in methods {
|
||||
if let ASTNode::FunctionDeclaration { params, .. } = mast {
|
||||
self.comp_ctx
|
||||
@ -223,7 +227,8 @@ impl super::MirBuilder {
|
||||
}
|
||||
} else {
|
||||
// Instance box: register type and lower instance methods/ctors as functions
|
||||
self.comp_ctx.user_defined_boxes.insert(name.clone());
|
||||
// Phase 285LLVM-1.1: Register with field information for LLVM harness
|
||||
self.comp_ctx.register_user_box_with_fields(name.clone(), fields.clone());
|
||||
self.build_box_declaration(
|
||||
name.clone(),
|
||||
methods.clone(),
|
||||
@ -534,6 +539,9 @@ impl super::MirBuilder {
|
||||
// main 関数スコープの SlotRegistry を解放するよ。
|
||||
self.comp_ctx.current_slot_registry = None;
|
||||
|
||||
// Phase 285LLVM-1.1: Copy user box declarations to module metadata for LLVM harness
|
||||
module.metadata.user_box_decls = self.comp_ctx.user_defined_boxes.clone();
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ pub(crate) fn try_known_rewrite(
|
||||
return None;
|
||||
}
|
||||
// Only user-defined boxes (plugin/core boxesは対象外)
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(cls) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(cls) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
// Policy gates(従来互換)
|
||||
@ -124,7 +124,7 @@ pub(crate) fn try_known_rewrite_to_dst(
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(cls) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(cls) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
let allow_userbox_rewrite =
|
||||
@ -204,7 +204,7 @@ pub(crate) fn try_unique_suffix_rewrite(
|
||||
let fname = cands.remove(0);
|
||||
// 🎯 Phase 21.7++ Phase 3: StaticMethodId SSOT 実装
|
||||
let id = crate::mir::naming::StaticMethodId::parse(&fname)?;
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(&id.box_name) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(&id.box_name) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
// unified
|
||||
@ -260,7 +260,7 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
let fname = cands.remove(0);
|
||||
// 🎯 Phase 21.7++ Phase 3: StaticMethodId SSOT 実装
|
||||
let id = crate::mir::naming::StaticMethodId::parse(&fname)?;
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(&id.box_name) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(&id.box_name) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname)
|
||||
|
||||
@ -414,6 +414,10 @@ pub struct ModuleMetadata {
|
||||
/// Dev idempotence markers for passes (optional; default empty)
|
||||
/// Key format suggestion: "pass_name:function_name"
|
||||
pub dev_processed_markers: HashSet<String>,
|
||||
|
||||
/// Phase 285LLVM-1.1: User-defined box declarations with fields
|
||||
/// HashMap: box name → field names (empty Vec for static boxes)
|
||||
pub user_box_decls: std::collections::HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
impl MirModule {
|
||||
|
||||
@ -523,6 +523,26 @@ pub fn emit_mir_json_for_harness(
|
||||
I::Return { value } => {
|
||||
insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())}));
|
||||
}
|
||||
// Phase 285LLVM-1: WeakRef support (unified form after normalization)
|
||||
I::WeakRef { dst, op, value } => {
|
||||
use crate::mir::WeakRefOp;
|
||||
let op_name = match op {
|
||||
WeakRefOp::New => "weak_new",
|
||||
WeakRefOp::Load => "weak_load",
|
||||
};
|
||||
let value_field = match op {
|
||||
WeakRefOp::New => "box_val",
|
||||
WeakRefOp::Load => "weak_ref",
|
||||
};
|
||||
insts.push(json!({"op": op_name, "dst": dst.as_u32(), value_field: value.as_u32()}));
|
||||
}
|
||||
// Legacy WeakNew/WeakLoad (before normalization)
|
||||
I::WeakNew { dst, box_val } => {
|
||||
insts.push(json!({"op":"weak_new","dst": dst.as_u32(), "box_val": box_val.as_u32()}));
|
||||
}
|
||||
I::WeakLoad { dst, weak_ref } => {
|
||||
insts.push(json!({"op":"weak_load","dst": dst.as_u32(), "weak_ref": weak_ref.as_u32()}));
|
||||
}
|
||||
_ => { /* skip non-essential ops for initial harness */ }
|
||||
}
|
||||
}
|
||||
@ -580,16 +600,32 @@ pub fn emit_mir_json_for_harness(
|
||||
// Phase 155: Extract CFG information for hako_check
|
||||
let cfg_info = nyash_rust::mir::extract_cfg_info(module);
|
||||
|
||||
// Phase 285LLVM-1.1: Extract user box declarations for LLVM harness
|
||||
let user_box_decls: Vec<serde_json::Value> = module.metadata.user_box_decls
|
||||
.iter()
|
||||
.map(|(name, fields)| {
|
||||
json!({
|
||||
"name": name,
|
||||
"fields": fields
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let root = if use_v1_schema {
|
||||
let mut root = create_json_v1_root(json!(funs));
|
||||
// Add CFG data to v1 schema
|
||||
// Add CFG data and user box declarations to v1 schema
|
||||
if let Some(obj) = root.as_object_mut() {
|
||||
obj.insert("cfg".to_string(), cfg_info);
|
||||
obj.insert("user_box_decls".to_string(), json!(user_box_decls)); // Phase 285LLVM-1.1
|
||||
}
|
||||
root
|
||||
} else {
|
||||
// v0 legacy format - also add CFG
|
||||
json!({"functions": funs, "cfg": cfg_info})
|
||||
// v0 legacy format - also add CFG and user_box_decls
|
||||
json!({
|
||||
"functions": funs,
|
||||
"cfg": cfg_info,
|
||||
"user_box_decls": user_box_decls // Phase 285LLVM-1.1
|
||||
})
|
||||
};
|
||||
|
||||
// NOTE: numeric_core strict validation is applied on the AotPrep output
|
||||
@ -910,6 +946,26 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
I::Return { value } => {
|
||||
insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())}));
|
||||
}
|
||||
// Phase 285LLVM-1: WeakRef support (unified form after normalization)
|
||||
I::WeakRef { dst, op, value } => {
|
||||
use crate::mir::WeakRefOp;
|
||||
let op_name = match op {
|
||||
WeakRefOp::New => "weak_new",
|
||||
WeakRefOp::Load => "weak_load",
|
||||
};
|
||||
let value_field = match op {
|
||||
WeakRefOp::New => "box_val",
|
||||
WeakRefOp::Load => "weak_ref",
|
||||
};
|
||||
insts.push(json!({"op": op_name, "dst": dst.as_u32(), value_field: value.as_u32()}));
|
||||
}
|
||||
// Legacy WeakNew/WeakLoad (before normalization)
|
||||
I::WeakNew { dst, box_val } => {
|
||||
insts.push(json!({"op":"weak_new","dst": dst.as_u32(), "box_val": box_val.as_u32()}));
|
||||
}
|
||||
I::WeakLoad { dst, weak_ref } => {
|
||||
insts.push(json!({"op":"weak_load","dst": dst.as_u32(), "weak_ref": weak_ref.as_u32()}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -954,7 +1010,22 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
// Phase 155: Extract CFG information for hako_check
|
||||
let cfg_info = crate::mir::extract_cfg_info(module);
|
||||
|
||||
let root = json!({"functions": funs, "cfg": cfg_info});
|
||||
// Phase 285LLVM-1.1: Extract user box declarations for LLVM harness
|
||||
let user_box_decls: Vec<serde_json::Value> = module.metadata.user_box_decls
|
||||
.iter()
|
||||
.map(|(name, fields)| {
|
||||
json!({
|
||||
"name": name,
|
||||
"fields": fields
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let root = json!({
|
||||
"functions": funs,
|
||||
"cfg": cfg_info,
|
||||
"user_box_decls": user_box_decls // Phase 285LLVM-1.1
|
||||
});
|
||||
|
||||
// NOTE: numeric_core strict validation is applied on the AotPrep output
|
||||
// (tools/hakorune_emit_mir.sh) rather than at raw MIR emit time. This keeps
|
||||
|
||||
@ -92,6 +92,22 @@ fn hint_ny_llvmc_missing(path: &std::path::Path) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn hint_nyrt_missing(dir: &str) -> String {
|
||||
let lib = Path::new(dir).join("libnyash_kernel.a");
|
||||
format!(
|
||||
"nyrt runtime not found (missing: {}).\nHints:\n - Build it: cargo build -p nyash_kernel --release\n - Or set env NYASH_EMIT_EXE_NYRT=/path/to/nyash_kernel/target/release\n",
|
||||
lib.display()
|
||||
)
|
||||
}
|
||||
|
||||
fn verify_nyrt_dir(dir: &str) -> Result<(), String> {
|
||||
let lib = Path::new(dir).join("libnyash_kernel.a");
|
||||
if lib.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
Err(hint_nyrt_missing(dir))
|
||||
}
|
||||
|
||||
/// Emit native executable via ny-llvmc (lib-side MIR)
|
||||
#[allow(dead_code)]
|
||||
pub fn ny_llvmc_emit_exe_lib(
|
||||
@ -124,11 +140,9 @@ pub fn ny_llvmc_emit_exe_lib(
|
||||
.map(|r| format!("{}/target/release", r))
|
||||
})
|
||||
.unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
cmd.arg("--nyrt").arg(default_nyrt);
|
||||
}
|
||||
let nyrt_dir_final = nyrt_dir.unwrap_or(&default_nyrt);
|
||||
verify_nyrt_dir(nyrt_dir_final)?;
|
||||
cmd.arg("--nyrt").arg(nyrt_dir_final);
|
||||
if let Some(flags) = extra_libs {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
@ -183,11 +197,9 @@ pub fn ny_llvmc_emit_exe_bin(
|
||||
.map(|r| format!("{}/target/release", r))
|
||||
})
|
||||
.unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
cmd.arg("--nyrt").arg(default_nyrt);
|
||||
}
|
||||
let nyrt_dir_final = nyrt_dir.unwrap_or(&default_nyrt);
|
||||
verify_nyrt_dir(nyrt_dir_final)?;
|
||||
cmd.arg("--nyrt").arg(nyrt_dir_final);
|
||||
if let Some(flags) = extra_libs {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
|
||||
@ -27,7 +27,10 @@ impl FallbackExecutorBox {
|
||||
// do not silently fall back to mock.
|
||||
if crate::config::env::env_bool("NYASH_LLVM_USE_HARNESS") {
|
||||
return Err(LlvmRunError::fatal(
|
||||
"LLVM harness requested (NYASH_LLVM_USE_HARNESS=1), but this binary was built without `--features llvm` (llvm-harness). Fix: cargo build --release --features llvm"
|
||||
"LLVM harness requested (NYASH_LLVM_USE_HARNESS=1), but this binary was built without `--features llvm` (llvm-harness).\n\
|
||||
Fix:\n cargo build --release -p nyash-rust --features llvm --bin hakorune\n\
|
||||
Then ensure prerequisites:\n cargo build --release -p nyash-llvm-compiler\n cargo build --release -p nyash_kernel\n\
|
||||
Tip: tools/run_llvm_harness.sh <program.hako>"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,11 @@ impl HarnessExecutorBox {
|
||||
/// Returns Ok(exit_code) on success, Err(LlvmRunError) on failure.
|
||||
#[cfg(feature = "llvm-harness")]
|
||||
pub fn try_execute(module: &MirModule) -> Result<i32, LlvmRunError> {
|
||||
if !crate::config::env::llvm_use_harness() {
|
||||
eprintln!("🎯 [DEBUG] llvm-harness feature IS ENABLED at compile time");
|
||||
let harness_enabled = crate::config::env::llvm_use_harness();
|
||||
eprintln!("🎯 [DEBUG] llvm_use_harness() = {}", harness_enabled);
|
||||
eprintln!("🎯 [DEBUG] NYASH_LLVM_USE_HARNESS env var = {:?}", std::env::var("NYASH_LLVM_USE_HARNESS"));
|
||||
if !harness_enabled {
|
||||
return Err(LlvmRunError::fatal("LLVM harness not enabled (NYASH_LLVM_USE_HARNESS not set)"));
|
||||
}
|
||||
|
||||
@ -62,6 +66,8 @@ impl HarnessExecutorBox {
|
||||
|
||||
#[cfg(not(feature = "llvm-harness"))]
|
||||
pub fn try_execute(_module: &MirModule) -> Result<i32, LlvmRunError> {
|
||||
eprintln!("❌ [DEBUG] llvm-harness feature IS NOT ENABLED at compile time");
|
||||
eprintln!("❌ [DEBUG] You need to rebuild with: cargo build --release --features llvm");
|
||||
Err(LlvmRunError::fatal("LLVM harness feature not enabled (built without --features llvm)"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ pub mod unified_registry; // Deprecation warnings with warn-once guards
|
||||
pub mod extern_registry; // ExternCall (env.*) 登録・診断用レジストリ
|
||||
pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し)
|
||||
pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し)
|
||||
pub mod weak_handles; // Phase 285LLVM-1: WeakRef Handle レジストリ(bit 63 = 1)
|
||||
pub mod modules_registry;
|
||||
pub mod type_box_abi; // Phase 12: Nyash ABI (vtable) 雛形
|
||||
pub mod type_meta;
|
||||
|
||||
146
src/runtime/weak_handles.rs
Normal file
146
src/runtime/weak_handles.rs
Normal file
@ -0,0 +1,146 @@
|
||||
/*!
|
||||
* Weak Handle Registry (Phase 285LLVM-1)
|
||||
*
|
||||
* 目的:
|
||||
* - WeakRef のための LLVM handle 管理を提供。
|
||||
* - i64 handle (bit 63 = 1) → Weak<dyn NyashBox> をグローバルに保持。
|
||||
* - LLVM backend から FFI 経由でアクセス可能。
|
||||
*
|
||||
* Runtime 表現:
|
||||
* - Strong handle: 0x0000_0000_0000_0001 ~ 0x7FFF_FFFF_FFFF_FFFF (bit 63 = 0)
|
||||
* - Weak handle: 0x8000_0000_0000_0001 ~ 0xFFFF_FFFF_FFFF_FFFF (bit 63 = 1)
|
||||
*/
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, RwLock, Weak,
|
||||
};
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
/// Weak handle marker (bit 63 = 1)
|
||||
const WEAK_HANDLE_MARKER: u64 = 0x8000_0000_0000_0000;
|
||||
|
||||
/// Extract raw handle ID (clear bit 63)
|
||||
#[inline]
|
||||
fn extract_weak_id(handle: i64) -> u64 {
|
||||
(handle as u64) & !WEAK_HANDLE_MARKER
|
||||
}
|
||||
|
||||
/// Mark handle as weak (set bit 63)
|
||||
#[inline]
|
||||
fn mark_weak_handle(id: u64) -> i64 {
|
||||
(id | WEAK_HANDLE_MARKER) as i64
|
||||
}
|
||||
|
||||
/// Check if handle is weak (bit 63 = 1)
|
||||
#[inline]
|
||||
pub fn is_weak_handle(handle: i64) -> bool {
|
||||
(handle as u64 & WEAK_HANDLE_MARKER) != 0
|
||||
}
|
||||
|
||||
struct WeakRegistry {
|
||||
next: AtomicU64,
|
||||
map: RwLock<HashMap<u64, Weak<dyn NyashBox>>>,
|
||||
}
|
||||
|
||||
impl WeakRegistry {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
next: AtomicU64::new(1),
|
||||
map: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc(&self, weak: Weak<dyn NyashBox>) -> i64 {
|
||||
let id = self.next.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = self.map.write() {
|
||||
m.insert(id, weak);
|
||||
}
|
||||
mark_weak_handle(id)
|
||||
}
|
||||
|
||||
fn get(&self, handle: i64) -> Option<Weak<dyn NyashBox>> {
|
||||
let id = extract_weak_id(handle);
|
||||
self.map.read().ok().and_then(|m| m.get(&id).cloned())
|
||||
}
|
||||
|
||||
fn drop_handle(&self, handle: i64) {
|
||||
let id = extract_weak_id(handle);
|
||||
if let Ok(mut m) = self.map.write() {
|
||||
m.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static WEAK_REG: OnceCell<WeakRegistry> = OnceCell::new();
|
||||
fn weak_reg() -> &'static WeakRegistry {
|
||||
WEAK_REG.get_or_init(WeakRegistry::new)
|
||||
}
|
||||
|
||||
/// Weak<dyn NyashBox> → Weak Handle (i64, bit 63 = 1)
|
||||
pub fn to_handle_weak(weak: Weak<dyn NyashBox>) -> i64 {
|
||||
weak_reg().alloc(weak)
|
||||
}
|
||||
|
||||
/// Weak Handle (i64) → Weak<dyn NyashBox>
|
||||
pub fn get_weak(handle: i64) -> Option<Weak<dyn NyashBox>> {
|
||||
weak_reg().get(handle)
|
||||
}
|
||||
|
||||
/// Drop weak handle (release from registry)
|
||||
pub fn drop_weak_handle(handle: i64) {
|
||||
weak_reg().drop_handle(handle)
|
||||
}
|
||||
|
||||
/// Upgrade weak handle to strong handle
|
||||
/// Returns: strong handle (>0) on success, 0 (Void) on failure
|
||||
pub fn upgrade_weak_handle(weak_handle: i64) -> i64 {
|
||||
if let Some(weak) = get_weak(weak_handle) {
|
||||
if let Some(arc) = weak.upgrade() {
|
||||
return crate::runtime::host_handles::to_handle_arc(arc) as i64;
|
||||
}
|
||||
}
|
||||
0 // Void (null)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::box_trait::StringBox;
|
||||
|
||||
#[test]
|
||||
fn test_weak_handle_marker() {
|
||||
let strong_handle = 0x0000_0000_0000_0001i64;
|
||||
let weak_handle = 0x8000_0000_0000_0001i64;
|
||||
|
||||
assert!(!is_weak_handle(strong_handle));
|
||||
assert!(is_weak_handle(weak_handle));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weak_handle_lifecycle() {
|
||||
let arc: Arc<dyn NyashBox> = Arc::new(StringBox::new("test"));
|
||||
let weak = Arc::downgrade(&arc);
|
||||
|
||||
// Allocate weak handle
|
||||
let weak_handle = to_handle_weak(weak.clone());
|
||||
assert!(is_weak_handle(weak_handle));
|
||||
|
||||
// Upgrade should succeed (arc is alive)
|
||||
let strong_handle = upgrade_weak_handle(weak_handle);
|
||||
assert!(strong_handle > 0);
|
||||
|
||||
// Drop arc
|
||||
drop(arc);
|
||||
|
||||
// Upgrade should fail (arc is dead)
|
||||
let result = upgrade_weak_handle(weak_handle);
|
||||
assert_eq!(result, 0); // Void
|
||||
|
||||
// Cleanup
|
||||
drop_weak_handle(weak_handle);
|
||||
}
|
||||
}
|
||||
55
tools/run_llvm_harness.sh
Normal file
55
tools/run_llvm_harness.sh
Normal file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat << USAGE
|
||||
Usage: tools/run_llvm_harness.sh <input.hako> [-- <args...>]
|
||||
|
||||
Builds LLVM-harness prerequisites and runs the program via:
|
||||
NYASH_LLVM_USE_HARNESS=1 ./target/release/hakorune --backend llvm <input.hako>
|
||||
USAGE
|
||||
}
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INPUT="$1"
|
||||
shift || true
|
||||
|
||||
if [[ "$INPUT" == "-h" || "$INPUT" == "--help" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -f "$INPUT" ]]; then
|
||||
echo "error: input file not found: $INPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CARGO_TARGET_DIR_EFFECTIVE="${CARGO_TARGET_DIR:-$PWD/target}"
|
||||
BIN_DEFAULT="$CARGO_TARGET_DIR_EFFECTIVE/release/hakorune"
|
||||
BIN="${NYASH_BIN:-$BIN_DEFAULT}"
|
||||
|
||||
echo "[1/4] Building hakorune (llvm feature)..."
|
||||
cargo build --release -p nyash-rust --features llvm --bin hakorune -j 24
|
||||
|
||||
echo "[2/4] Building ny-llvmc..."
|
||||
cargo build --release -p nyash-llvm-compiler -j 24
|
||||
|
||||
echo "[3/4] Building nyash_kernel..."
|
||||
cargo build --release -p nyash_kernel -j 24
|
||||
|
||||
if [[ ! -x "$BIN" ]]; then
|
||||
if [[ -x "$CARGO_TARGET_DIR_EFFECTIVE/release/nyash" ]]; then
|
||||
BIN="$CARGO_TARGET_DIR_EFFECTIVE/release/nyash"
|
||||
else
|
||||
echo "error: compiler binary not found/executable after build: $BIN" >&2
|
||||
echo "hint: ensure NYASH_BIN points to an existing binary or set CARGO_TARGET_DIR correctly" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[4/4] Running LLVM harness..."
|
||||
NYASH_LLVM_USE_HARNESS=1 "$BIN" --backend llvm "$INPUT" "$@"
|
||||
Reference in New Issue
Block a user