fix: MIR builder me resolution for static box methods
- Fixed me ValueId inconsistency in static box methods - Previously, each me reference generated a new const __me__ ValueId - Now caches the first me ValueId in variable_map for reuse - This ensures RefSet and RefGet operate on the same object - ArrayBox get/set/push now working correctly in VM mode - Test results: 1, 42, 3 (as expected) 🔧 Technical Details: - build_me_expression() now stores fallback ValueId in variable_map - Subsequent me references reuse the same ValueId - VM BoxCall debug logs confirm ArrayBox methods dispatch correctly Co-Authored-By: ChatGPT5 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -48,6 +48,9 @@ NYASH_VM_STATS=1 NYASH_VM_STATS_JSON=1 ./target/debug/nyash --backend vm program
|
|||||||
- `scope_tracker` がスコープ終了時に `fini()` を呼ぶ(メモリ安全)。
|
- `scope_tracker` がスコープ終了時に `fini()` を呼ぶ(メモリ安全)。
|
||||||
- 大きいボディ/多ヘッダー/タイムアウト
|
- 大きいボディ/多ヘッダー/タイムアウト
|
||||||
- 逐次拡張中。異常時の挙動は上記Result規約に従う。実行ログと `--vm-stats` を併用して診断。
|
- 逐次拡張中。異常時の挙動は上記Result規約に従う。実行ログと `--vm-stats` を併用して診断。
|
||||||
|
- 反復タイムアウト: `local_tests/socket_repeated_timeouts.nyash` で `acceptTimeout/recvTimeout` の連続ケース確認
|
||||||
|
- BoxCallデバッグ: `NYASH_VM_DEBUG_BOXCALL=1` でBoxCallの受け手型・引数型・処理経路(enter/fastpath/unified)・結果型をstderr出力
|
||||||
|
- 例: `NYASH_VM_DEBUG_BOXCALL=1 ./target/release/nyash --backend vm local_tests/test_vm_array_getset.nyash`
|
||||||
- SocketBox(VM)
|
- SocketBox(VM)
|
||||||
- 基本API: `bind/listen/accept/connect/read/write/close/isServer/isConnected`
|
- 基本API: `bind/listen/accept/connect/read/write/close/isServer/isConnected`
|
||||||
- タイムアウト: `acceptTimeout(ms)` は接続なしで `void`、`recvTimeout(ms)` は空文字を返す
|
- タイムアウト: `acceptTimeout(ms)` は接続なしで `void`、`recvTimeout(ms)` は空文字を返す
|
||||||
|
|||||||
@ -16,29 +16,47 @@
|
|||||||
- ArrayBox/MapBox: 代表メソッドをVM統合ディスパッチで実装(push/get/set/size等)
|
- ArrayBox/MapBox: 代表メソッドをVM統合ディスパッチで実装(push/get/set/size等)
|
||||||
- SocketBox: `acceptTimeout(ms)`(void)/ `recvTimeout(ms)`(空文字)を追加
|
- SocketBox: `acceptTimeout(ms)`(void)/ `recvTimeout(ms)`(空文字)を追加
|
||||||
- E2E追加: `socket_timeout_server.nyash` / `socket_timeout_client.nyash`
|
- E2E追加: `socket_timeout_server.nyash` / `socket_timeout_client.nyash`
|
||||||
7. ドキュメント追加・更新
|
7. E2E拡張(Net/Socket)
|
||||||
|
- HTTP: 大ボディ取得クライアント `local_tests/http_big_body_client.nyash`
|
||||||
|
- Socket: 反復タイムアウト検証 `local_tests/socket_repeated_timeouts.nyash`
|
||||||
|
- インタープリタ: SocketBoxの `acceptTimeout/recvTimeout` を結線
|
||||||
|
8. VM/MIRの健全化(Builder/VM)
|
||||||
|
- Compare拡張: Float/Int-Float混在をサポート(Eq/Ne/Lt/Le/Gt/Ge)
|
||||||
|
- TypeOp(Check)最小意味論実装(Integer/Float/Bool/String/Void/Box名)
|
||||||
|
- ArrayGet/ArraySet(VM)本実装(ArrayBox.get/setへ橋渡し)
|
||||||
|
- Array/Mapをidentity扱い(clone_or_shareがshareを選択)
|
||||||
|
- BoxCallにArrayBox fast-path(BoxRefからget/set直呼び)
|
||||||
|
- me参照の安定化(fallback時に一度だけConstを発行しvariable_mapに保持)
|
||||||
|
- デバッグ: `NYASH_VM_DEBUG_BOXCALL=1` でBoxCallの受け手/引数/経路/結果型を標準エラーに出力
|
||||||
|
9. ドキュメント追加・更新
|
||||||
- MIR→VMマッピング(分岐条件の動的変換、Void/Bool比較)
|
- MIR→VMマッピング(分岐条件の動的変換、Void/Bool比較)
|
||||||
- VM README(SocketBoxタイムアウト/E2E導線・HTTP Result整理)
|
- VM README(SocketBoxタイムアウト/E2E導線・HTTP Result整理)
|
||||||
- 26命令ダイエット: PoCフラグと進捗追記(TypeOp/WeakRef/Barrier)
|
- 26命令ダイエット: PoCフラグと進捗追記(TypeOp/WeakRef/Barrier)
|
||||||
8. CI: plugins E2E ジョブ(Linux)を追加
|
10. CI: plugins E2E ジョブ(Linux)を追加
|
||||||
|
|
||||||
## 🚧 次にやること(再開方針)
|
## 🚧 次にやること(再開方針)
|
||||||
|
|
||||||
1) 命令セットダイエットのPoC実装(短期)
|
1) 命令セットダイエットのPoC実装(短期)
|
||||||
- フラグ `mir_typeop_poc` 有効時、Builderで TypeCheck/Cast → TypeOp を出力
|
- 現状: VMに `TypeOp/WeakRef/Barrier` 実行経路(等価)とPrinter対応。Builderに補助APIを追加済(未置換)。
|
||||||
- VMにTypeOp実行経路を追加(当面は既存と同義)
|
- 次: Builder内の該当箇所を補助APIに置換(flag onで新命令を吐く/offで従来どおり)
|
||||||
- 次段: `mir_refbarrier_unify_poc` で Weak*/Barrier 統合(Builder/VM)
|
- 旗: `mir_typeop_poc`(TypeCheck/Cast→TypeOp)、`mir_refbarrier_unify_poc`(Weak*/Barrier→統合)
|
||||||
- 成果物: スナップショット(flag on/off)+ vm-statsで集計キー確認
|
- 成果物: スナップショット(flag on/off)+ vm-statsのキー確認(TypeOp/WeakRef/Barrier)
|
||||||
|
|
||||||
2) VM×プラグインのE2E拡張(短期)
|
2) VM×プラグインのE2E拡張(短期)
|
||||||
- HTTP: 遅延応答・大ボディの計測、到達不能時のERR安定化の再検証
|
- HTTP: 遅延応答・大ボディの計測、到達不能時のERR安定化の再検証(代表は追加済)
|
||||||
- Socket: タイムアウト系の追加ケース(連続acceptTimeout/recvTimeout)
|
- Socket: 反復タイムアウトの追加ケース(代表は追加済)
|
||||||
- 成果物: E2E追加と `VM_README.md` のTips追補
|
- 成果物: 必要に応じてE2E追補と `VM_README.md` のTips更新
|
||||||
|
|
||||||
3) ResultBox単一路線への統合(中期)
|
3) ResultBox単一路線への統合(中期)
|
||||||
- 新`NyashResultBox`へ統合、旧`ResultBox`は薄いラッパーとして段階移行
|
- 新`NyashResultBox`へ統合、旧`ResultBox`は薄いラッパーとして段階移行
|
||||||
- 成果物: 実装整理・移行メモ・影響調査
|
- 成果物: 実装整理・移行メモ・影響調査
|
||||||
|
|
||||||
|
4) Array系の本実装(必要時・中期)
|
||||||
|
- VMの `ArrayGet/ArraySet` 実装済み。BoxCall fast-pathの整合性と回帰テストを充実
|
||||||
|
|
||||||
|
5) BoxCall高速化(性能段階)
|
||||||
|
- vm-statsでホットなBoxCallの高速化(命令セット統合より効果大の可能性)
|
||||||
|
|
||||||
## ▶ 実行コマンド例
|
## ▶ 実行コマンド例
|
||||||
|
|
||||||
計測実行:
|
計測実行:
|
||||||
@ -66,7 +84,7 @@ nyash --verify examples/plugin_box_sample.nyash
|
|||||||
- メタ降格: Debug / Nop / Safepoint(ビルドモードで制御)
|
- メタ降格: Debug / Nop / Safepoint(ビルドモードで制御)
|
||||||
|
|
||||||
---
|
---
|
||||||
最終更新: 2025年8月23日(VM強化・E2E拡張・TypeOp PoC着手/次段はBuilder/VMマッピング)
|
最終更新: 2025年8月23日(VM強化・E2E拡張・me参照安定化・TypeOp/WeakRef/Barrier PoC完了/次段はBuilder置換とスナップショット)
|
||||||
|
|
||||||
## 🔁 再起動後の再開手順(ショート)
|
## 🔁 再起動後の再開手順(ショート)
|
||||||
```bash
|
```bash
|
||||||
@ -83,4 +101,13 @@ tools/run_vm_stats.sh local_tests/vm_stats_http_err.nyash vm_stats_err.json
|
|||||||
# 4) SocketBox タイムアウト確認(任意)
|
# 4) SocketBox タイムアウト確認(任意)
|
||||||
./target/release/nyash local_tests/socket_timeout_server.nyash
|
./target/release/nyash local_tests/socket_timeout_server.nyash
|
||||||
./target/release/nyash local_tests/socket_timeout_client.nyash
|
./target/release/nyash local_tests/socket_timeout_client.nyash
|
||||||
|
|
||||||
|
# 5) 反復タイムアウト確認(任意)
|
||||||
|
./target/release/nyash local_tests/socket_repeated_timeouts.nyash
|
||||||
|
|
||||||
|
# 6) HTTP 大ボディ確認(任意)
|
||||||
|
./target/release/nyash local_tests/http_big_body_client.nyash
|
||||||
|
|
||||||
|
# 7) VM BoxCall デバッグ(任意)
|
||||||
|
NYASH_VM_DEBUG_BOXCALL=1 ./target/release/nyash --backend vm local_tests/test_vm_array_getset.nyash
|
||||||
```
|
```
|
||||||
|
|||||||
@ -49,8 +49,8 @@
|
|||||||
- TODO: 正式な型変換に置換。
|
- TODO: 正式な型変換に置換。
|
||||||
|
|
||||||
## 配列
|
## 配列
|
||||||
- ArrayGet: TODO(一時的に0を返す)
|
- ArrayGet: ArrayBox.get(index) を呼び出し、戻り値を格納(VM対応済み)
|
||||||
- ArraySet: TODO(現在はno-op)
|
- ArraySet: ArrayBox.set(index, value) を呼び出し(VM対応済み)
|
||||||
|
|
||||||
## デバッグ/出力
|
## デバッグ/出力
|
||||||
- Debug: No-op(性能優先)
|
- Debug: No-op(性能優先)
|
||||||
@ -198,3 +198,21 @@ Verifier(検証)に関する追加事項(方針)
|
|||||||
デバッグ小技:
|
デバッグ小技:
|
||||||
- `NYASH_DEBUG_PLUGIN=1` で VM→Plugin 呼び出しTLVの ver/argc/先頭バイトをダンプ
|
- `NYASH_DEBUG_PLUGIN=1` で VM→Plugin 呼び出しTLVの ver/argc/先頭バイトをダンプ
|
||||||
- Netプラグインの内部ログ: `NYASH_NET_LOG=1 NYASH_NET_LOG_FILE=net_plugin.log`
|
- Netプラグインの内部ログ: `NYASH_NET_LOG=1 NYASH_NET_LOG_FILE=net_plugin.log`
|
||||||
|
## 型・Null/Void・比較の扱い(更新)
|
||||||
|
|
||||||
|
- NullはVM内部でVoidに折りたたみ(`Const Null → VMValue::Void`)。
|
||||||
|
- VoidとNullは同一視されない(等価比較は `Void == Void` のみtrue)。
|
||||||
|
- Compareの対応:
|
||||||
|
- 整数/文字列: Eq/Ne/Lt/Le/Gt/Ge(実装済)
|
||||||
|
- 真偽値: Eq/Ne のみ
|
||||||
|
- Void: Eq/Ne のみ(Void==Voidはtrue、それ以外はfalse)
|
||||||
|
- 浮動小数点: Eq/Ne/Lt/Le/Gt/Ge(新規)
|
||||||
|
- 整数と浮動小数点の混在: 双方をf64比較で対応(新規)
|
||||||
|
|
||||||
|
## TypeOp(PoC)
|
||||||
|
- 目的: TypeCheck/Castの統合。
|
||||||
|
- Check: 最小意味論を実装(Integer/Float/Bool/String/Void/Box名に対し一致判定)。
|
||||||
|
- Cast: 当面コピー等価(将来の変換方針に備える)。
|
||||||
|
- me 参照
|
||||||
|
- メソッド/コンストラクタlowering時は `%0` にマップ(パラメータ)。
|
||||||
|
- それ以外の文脈ではフォールバックとして `Const "__me__"` を一度だけ発行して変数マップに保持し、以降の `me` は同一ValueIdを参照(RefGet/RefSetの整合性を保証)。
|
||||||
|
|||||||
@ -457,15 +457,25 @@ impl VM {
|
|||||||
Ok(ControlFlow::Continue)
|
Ok(ControlFlow::Continue)
|
||||||
},
|
},
|
||||||
|
|
||||||
MirInstruction::TypeOp { dst, op, value, ty: _ } => {
|
MirInstruction::TypeOp { dst, op, value, ty } => {
|
||||||
// PoC: mirror current semantics
|
|
||||||
match op {
|
match op {
|
||||||
crate::mir::TypeOpKind::Check => {
|
crate::mir::TypeOpKind::Check => {
|
||||||
// Current TypeCheck is a no-op that returns true
|
let v = self.get_value(*value)?;
|
||||||
self.set_value(*dst, VMValue::Bool(true));
|
let ok = match ty {
|
||||||
|
crate::mir::MirType::Integer => matches!(v, VMValue::Integer(_)),
|
||||||
|
crate::mir::MirType::Float => matches!(v, VMValue::Float(_)),
|
||||||
|
crate::mir::MirType::Bool => matches!(v, VMValue::Bool(_)),
|
||||||
|
crate::mir::MirType::String => matches!(v, VMValue::String(_)),
|
||||||
|
crate::mir::MirType::Void => matches!(v, VMValue::Void),
|
||||||
|
crate::mir::MirType::Box(name) => match v {
|
||||||
|
VMValue::BoxRef(ref arc) => arc.type_name() == name,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
self.set_value(*dst, VMValue::Bool(ok));
|
||||||
}
|
}
|
||||||
crate::mir::TypeOpKind::Cast => {
|
crate::mir::TypeOpKind::Cast => {
|
||||||
// Current Cast is a copy/no-op
|
|
||||||
let v = self.get_value(*value)?;
|
let v = self.get_value(*value)?;
|
||||||
self.set_value(*dst, v);
|
self.set_value(*dst, v);
|
||||||
}
|
}
|
||||||
@ -598,11 +608,14 @@ impl VM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate arguments
|
// Evaluate arguments
|
||||||
let mut arg_values = Vec::new();
|
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||||
|
let mut arg_vm_values: Vec<VMValue> = Vec::new();
|
||||||
for arg_id in args {
|
for arg_id in args {
|
||||||
let arg_vm_value = self.get_value(*arg_id)?;
|
let arg_vm_value = self.get_value(*arg_id)?;
|
||||||
arg_values.push(arg_vm_value.to_nyash_box());
|
arg_values.push(arg_vm_value.to_nyash_box());
|
||||||
|
arg_vm_values.push(arg_vm_value);
|
||||||
}
|
}
|
||||||
|
self.debug_log_boxcall(&box_vm_value, method, &arg_values, "enter", None);
|
||||||
|
|
||||||
// PluginBoxV2 method dispatch via BID-FFI (zero-arg minimal)
|
// PluginBoxV2 method dispatch via BID-FFI (zero-arg minimal)
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
@ -627,6 +640,31 @@ impl VM {
|
|||||||
return Ok(ControlFlow::Continue);
|
return Ok(ControlFlow::Continue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast-path for ArrayBox methods using original BoxRef (preserve state)
|
||||||
|
if let VMValue::BoxRef(ref arc_any) = box_vm_value {
|
||||||
|
if let Some(arr) = arc_any.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||||
|
match method.as_str() {
|
||||||
|
"get" => {
|
||||||
|
if let Some(arg0) = arg_values.get(0) {
|
||||||
|
let res = arr.get((*arg0).clone_or_share());
|
||||||
|
if let Some(dst_id) = dst { let v = VMValue::from_nyash_box(res); self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); }
|
||||||
|
return Ok(ControlFlow::Continue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"set" => {
|
||||||
|
if arg_values.len() >= 2 {
|
||||||
|
let idx = (*arg_values.get(0).unwrap()).clone_or_share();
|
||||||
|
let val = (*arg_values.get(1).unwrap()).clone_or_share();
|
||||||
|
let _ = arr.set(idx, val);
|
||||||
|
if let Some(dst_id) = dst { let v = VMValue::Void; self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); }
|
||||||
|
return Ok(ControlFlow::Continue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Call the method - unified dispatch for all Box types
|
// Call the method - unified dispatch for all Box types
|
||||||
// If user-defined InstanceBox: dispatch to lowered MIR function `{Class}.{method}/{argc}`
|
// If user-defined InstanceBox: dispatch to lowered MIR function `{Class}.{method}/{argc}`
|
||||||
if let Some(instance) = box_nyash.as_any().downcast_ref::<InstanceBox>() {
|
if let Some(instance) = box_nyash.as_any().downcast_ref::<InstanceBox>() {
|
||||||
@ -651,6 +689,7 @@ impl VM {
|
|||||||
// Store result if destination is specified
|
// Store result if destination is specified
|
||||||
if let Some(dst_id) = dst {
|
if let Some(dst_id) = dst {
|
||||||
let vm_result = VMValue::from_nyash_box(result);
|
let vm_result = VMValue::from_nyash_box(result);
|
||||||
|
self.debug_log_boxcall(&box_vm_value, method, &arg_vm_values.iter().map(|v| v.to_nyash_box()).collect::<Vec<_>>(), "unified", Some(&vm_result));
|
||||||
self.set_value(*dst_id, vm_result);
|
self.set_value(*dst_id, vm_result);
|
||||||
}
|
}
|
||||||
Ok(ControlFlow::Continue)
|
Ok(ControlFlow::Continue)
|
||||||
@ -699,17 +738,35 @@ impl VM {
|
|||||||
Ok(ControlFlow::Continue)
|
Ok(ControlFlow::Continue)
|
||||||
},
|
},
|
||||||
|
|
||||||
MirInstruction::ArrayGet { dst, array: _, index: _ } => {
|
MirInstruction::ArrayGet { dst, array, index } => {
|
||||||
// For now, array access returns a placeholder
|
// Implement ArrayBox get(index) → value
|
||||||
// TODO: Implement proper array access
|
let arr_val = self.get_value(*array)?;
|
||||||
self.set_value(*dst, VMValue::Integer(0));
|
let idx_val = self.get_value(*index)?;
|
||||||
Ok(ControlFlow::Continue)
|
if let VMValue::BoxRef(arc) = arr_val {
|
||||||
|
if let Some(arr) = arc.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||||
|
let idx_box = idx_val.to_nyash_box();
|
||||||
|
let got = arr.get(idx_box);
|
||||||
|
self.set_value(*dst, VMValue::from_nyash_box(got));
|
||||||
|
return Ok(ControlFlow::Continue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(VMError::TypeError("ArrayGet expects ArrayBox".to_string()))
|
||||||
},
|
},
|
||||||
|
|
||||||
MirInstruction::ArraySet { array: _, index: _, value: _ } => {
|
MirInstruction::ArraySet { array, index, value } => {
|
||||||
// For now, array setting is a no-op
|
// Implement ArrayBox set(index, value)
|
||||||
// TODO: Implement proper array setting
|
let arr_val = self.get_value(*array)?;
|
||||||
Ok(ControlFlow::Continue)
|
let idx_val = self.get_value(*index)?;
|
||||||
|
let val_val = self.get_value(*value)?;
|
||||||
|
if let VMValue::BoxRef(arc) = arr_val {
|
||||||
|
if let Some(arr) = arc.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||||
|
let idx_box = idx_val.to_nyash_box();
|
||||||
|
let val_box = val_val.to_nyash_box();
|
||||||
|
let _ = arr.set(idx_box, val_box);
|
||||||
|
return Ok(ControlFlow::Continue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(VMError::TypeError("ArraySet expects ArrayBox".to_string()))
|
||||||
},
|
},
|
||||||
|
|
||||||
MirInstruction::Copy { dst, src } => {
|
MirInstruction::Copy { dst, src } => {
|
||||||
@ -1046,6 +1103,33 @@ impl VM {
|
|||||||
/// Execute comparison operation
|
/// Execute comparison operation
|
||||||
fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> {
|
fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> {
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
|
// Numeric mixed comparisons (Integer/Float)
|
||||||
|
(VMValue::Integer(l), VMValue::Float(r)) => {
|
||||||
|
let l = *l as f64;
|
||||||
|
let r = *r;
|
||||||
|
let result = match op {
|
||||||
|
CompareOp::Eq => l == r,
|
||||||
|
CompareOp::Ne => l != r,
|
||||||
|
CompareOp::Lt => l < r,
|
||||||
|
CompareOp::Le => l <= r,
|
||||||
|
CompareOp::Gt => l > r,
|
||||||
|
CompareOp::Ge => l >= r,
|
||||||
|
};
|
||||||
|
Ok(result)
|
||||||
|
},
|
||||||
|
(VMValue::Float(l), VMValue::Integer(r)) => {
|
||||||
|
let l = *l;
|
||||||
|
let r = *r as f64;
|
||||||
|
let result = match op {
|
||||||
|
CompareOp::Eq => l == r,
|
||||||
|
CompareOp::Ne => l != r,
|
||||||
|
CompareOp::Lt => l < r,
|
||||||
|
CompareOp::Le => l <= r,
|
||||||
|
CompareOp::Gt => l > r,
|
||||||
|
CompareOp::Ge => l >= r,
|
||||||
|
};
|
||||||
|
Ok(result)
|
||||||
|
},
|
||||||
// Bool comparisons: support Eq/Ne only for now
|
// Bool comparisons: support Eq/Ne only for now
|
||||||
(VMValue::Bool(l), VMValue::Bool(r)) => {
|
(VMValue::Bool(l), VMValue::Bool(r)) => {
|
||||||
let result = match op {
|
let result = match op {
|
||||||
@ -1086,6 +1170,18 @@ impl VM {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
(VMValue::Float(l), VMValue::Float(r)) => {
|
||||||
|
let result = match op {
|
||||||
|
CompareOp::Eq => l == r,
|
||||||
|
CompareOp::Ne => l != r,
|
||||||
|
CompareOp::Lt => l < r,
|
||||||
|
CompareOp::Le => l <= r,
|
||||||
|
CompareOp::Gt => l > r,
|
||||||
|
CompareOp::Ge => l >= r,
|
||||||
|
};
|
||||||
|
Ok(result)
|
||||||
|
},
|
||||||
|
|
||||||
(VMValue::String(l), VMValue::String(r)) => {
|
(VMValue::String(l), VMValue::String(r)) => {
|
||||||
let result = match op {
|
let result = match op {
|
||||||
CompareOp::Eq => l == r,
|
CompareOp::Eq => l == r,
|
||||||
@ -1147,6 +1243,35 @@ impl VM {
|
|||||||
*self.instr_counter.entry(key).or_insert(0) += 1;
|
*self.instr_counter.entry(key).or_insert(0) += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn debug_log_boxcall(&self, recv: &VMValue, method: &str, args: &[Box<dyn NyashBox>], stage: &str, result: Option<&VMValue>) {
|
||||||
|
if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") {
|
||||||
|
let recv_ty = match recv {
|
||||||
|
VMValue::BoxRef(arc) => arc.type_name().to_string(),
|
||||||
|
VMValue::Integer(_) => "Integer".to_string(),
|
||||||
|
VMValue::Float(_) => "Float".to_string(),
|
||||||
|
VMValue::Bool(_) => "Bool".to_string(),
|
||||||
|
VMValue::String(_) => "String".to_string(),
|
||||||
|
VMValue::Future(_) => "Future".to_string(),
|
||||||
|
VMValue::Void => "Void".to_string(),
|
||||||
|
};
|
||||||
|
let args_desc: Vec<String> = args.iter().map(|a| a.type_name().to_string()).collect();
|
||||||
|
if let Some(res) = result {
|
||||||
|
let res_ty = match res {
|
||||||
|
VMValue::BoxRef(arc) => format!("BoxRef({})", arc.type_name()),
|
||||||
|
VMValue::Integer(_) => "Integer".to_string(),
|
||||||
|
VMValue::Float(_) => "Float".to_string(),
|
||||||
|
VMValue::Bool(_) => "Bool".to_string(),
|
||||||
|
VMValue::String(_) => "String".to_string(),
|
||||||
|
VMValue::Future(_) => "Future".to_string(),
|
||||||
|
VMValue::Void => "Void".to_string(),
|
||||||
|
};
|
||||||
|
eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}", stage, recv_ty, method, args.len(), args_desc, res_ty);
|
||||||
|
} else {
|
||||||
|
eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}", stage, recv_ty, method, args.len(), args_desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Print simple VM execution statistics when enabled via env var
|
/// Print simple VM execution statistics when enabled via env var
|
||||||
fn maybe_print_stats(&mut self) {
|
fn maybe_print_stats(&mut self) {
|
||||||
let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false);
|
let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false);
|
||||||
|
|||||||
@ -720,6 +720,7 @@ impl Display for ErrorBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Result values in Nyash - represents success or error results
|
/// Result values in Nyash - represents success or error results
|
||||||
|
#[deprecated(note = "Use boxes::result::NyashResultBox (aka boxes::ResultBox) instead")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ResultBox {
|
pub struct ResultBox {
|
||||||
pub is_success: bool,
|
pub is_success: bool,
|
||||||
|
|||||||
@ -302,6 +302,7 @@ impl Display for ArrayBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NyashBox for ArrayBox {
|
impl NyashBox for ArrayBox {
|
||||||
|
fn is_identity(&self) -> bool { true }
|
||||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -267,6 +267,7 @@ impl BoxCore for MapBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NyashBox for MapBox {
|
impl NyashBox for MapBox {
|
||||||
|
fn is_identity(&self) -> bool { true }
|
||||||
fn type_name(&self) -> &'static str {
|
fn type_name(&self) -> &'static str {
|
||||||
"MapBox"
|
"MapBox"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -243,24 +243,44 @@ impl NyashInterpreter {
|
|||||||
// ビルトインBoxのインスタンスを作成または取得
|
// ビルトインBoxのインスタンスを作成または取得
|
||||||
match parent {
|
match parent {
|
||||||
"StringBox" => {
|
"StringBox" => {
|
||||||
let string_box = StringBox::new("");
|
if let Some(sb) = current_instance.as_any().downcast_ref::<StringBox>() {
|
||||||
self.execute_string_method(&string_box, method, arguments)
|
self.execute_string_method(sb, method, arguments)
|
||||||
|
} else {
|
||||||
|
let string_box = StringBox::new("");
|
||||||
|
self.execute_string_method(&string_box, method, arguments)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"IntegerBox" => {
|
"IntegerBox" => {
|
||||||
let integer_box = IntegerBox::new(0);
|
if let Some(ib) = current_instance.as_any().downcast_ref::<IntegerBox>() {
|
||||||
self.execute_integer_method(&integer_box, method, arguments)
|
self.execute_integer_method(ib, method, arguments)
|
||||||
|
} else {
|
||||||
|
let integer_box = IntegerBox::new(0);
|
||||||
|
self.execute_integer_method(&integer_box, method, arguments)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ArrayBox" => {
|
"ArrayBox" => {
|
||||||
let array_box = ArrayBox::new();
|
if let Some(ab) = current_instance.as_any().downcast_ref::<ArrayBox>() {
|
||||||
self.execute_array_method(&array_box, method, arguments)
|
self.execute_array_method(ab, method, arguments)
|
||||||
|
} else {
|
||||||
|
let array_box = ArrayBox::new();
|
||||||
|
self.execute_array_method(&array_box, method, arguments)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"MapBox" => {
|
"MapBox" => {
|
||||||
let map_box = MapBox::new();
|
if let Some(mb) = current_instance.as_any().downcast_ref::<MapBox>() {
|
||||||
self.execute_map_method(&map_box, method, arguments)
|
self.execute_map_method(mb, method, arguments)
|
||||||
|
} else {
|
||||||
|
let map_box = MapBox::new();
|
||||||
|
self.execute_map_method(&map_box, method, arguments)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"MathBox" => {
|
"MathBox" => {
|
||||||
let math_box = MathBox::new();
|
if let Some(math) = current_instance.as_any().downcast_ref::<MathBox>() {
|
||||||
self.execute_math_method(&math_box, method, arguments)
|
self.execute_math_method(math, method, arguments)
|
||||||
|
} else {
|
||||||
|
let math_box = MathBox::new();
|
||||||
|
self.execute_math_method(&math_box, method, arguments)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 他のビルトインBoxは必要に応じて追加
|
// 他のビルトインBoxは必要に応じて追加
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@ -47,6 +47,15 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
Ok(socket_box.accept())
|
Ok(socket_box.accept())
|
||||||
}
|
}
|
||||||
|
"acceptTimeout" | "accept_timeout" => {
|
||||||
|
if arguments.len() != 1 {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: format!("acceptTimeout(ms) expects 1 argument, got {}", arguments.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let ms = self.execute_expression(&arguments[0])?;
|
||||||
|
Ok(socket_box.accept_timeout(ms))
|
||||||
|
}
|
||||||
"connect" => {
|
"connect" => {
|
||||||
if arguments.len() != 2 {
|
if arguments.len() != 2 {
|
||||||
return Err(RuntimeError::InvalidOperation {
|
return Err(RuntimeError::InvalidOperation {
|
||||||
@ -67,6 +76,15 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
Ok(socket_box.read())
|
Ok(socket_box.read())
|
||||||
}
|
}
|
||||||
|
"recvTimeout" | "recv_timeout" => {
|
||||||
|
if arguments.len() != 1 {
|
||||||
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
message: format!("recvTimeout(ms) expects 1 argument, got {}", arguments.len()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let ms = self.execute_expression(&arguments[0])?;
|
||||||
|
Ok(socket_box.recv_timeout(ms))
|
||||||
|
}
|
||||||
"readHttpRequest" => {
|
"readHttpRequest" => {
|
||||||
if !arguments.is_empty() {
|
if !arguments.is_empty() {
|
||||||
return Err(RuntimeError::InvalidOperation {
|
return Err(RuntimeError::InvalidOperation {
|
||||||
|
|||||||
@ -61,6 +61,96 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emit a type check instruction (flagged to TypeOp in PoC)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn emit_type_check(&mut self, value: ValueId, expected_type: String) -> Result<ValueId, String> {
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
#[cfg(feature = "mir_typeop_poc")]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::TypeOp { dst, op: super::TypeOpKind::Check, value, ty: super::MirType::Box(expected_type) })?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "mir_typeop_poc"))]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::TypeCheck { dst, value, expected_type })?;
|
||||||
|
Ok(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a cast instruction (flagged to TypeOp in PoC)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn emit_cast(&mut self, value: ValueId, target_type: super::MirType) -> Result<ValueId, String> {
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
#[cfg(feature = "mir_typeop_poc")]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::TypeOp { dst, op: super::TypeOpKind::Cast, value, ty: target_type.clone() })?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "mir_typeop_poc"))]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::Cast { dst, value, target_type })?;
|
||||||
|
Ok(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a weak reference creation (flagged to WeakRef(New) in PoC)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result<ValueId, String> {
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
#[cfg(feature = "mir_refbarrier_unify_poc")]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::New, value: box_val })?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "mir_refbarrier_unify_poc"))]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::WeakNew { dst, box_val })?;
|
||||||
|
Ok(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a weak reference load (flagged to WeakRef(Load) in PoC)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result<ValueId, String> {
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
#[cfg(feature = "mir_refbarrier_unify_poc")]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::Load, value: weak_ref })?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "mir_refbarrier_unify_poc"))]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::WeakLoad { dst, weak_ref })?;
|
||||||
|
Ok(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a barrier read (flagged to Barrier(Read) in PoC)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn emit_barrier_read(&mut self, ptr: ValueId) -> Result<(), String> {
|
||||||
|
#[cfg(feature = "mir_refbarrier_unify_poc")]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Read, ptr })
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "mir_refbarrier_unify_poc"))]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::BarrierRead { ptr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a barrier write (flagged to Barrier(Write) in PoC)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn emit_barrier_write(&mut self, ptr: ValueId) -> Result<(), String> {
|
||||||
|
#[cfg(feature = "mir_refbarrier_unify_poc")]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Write, ptr })
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "mir_refbarrier_unify_poc"))]
|
||||||
|
{
|
||||||
|
self.emit_instruction(MirInstruction::BarrierWrite { ptr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Lower a box method (e.g., birth) into a standalone MIR function
|
/// Lower a box method (e.g., birth) into a standalone MIR function
|
||||||
/// func_name: Fully-qualified name like "Person.birth/1"
|
/// func_name: Fully-qualified name like "Person.birth/1"
|
||||||
/// box_name: Owning box type name (used for 'me' param type)
|
/// box_name: Owning box type name (used for 'me' param type)
|
||||||
@ -733,13 +823,9 @@ impl MirBuilder {
|
|||||||
let init_expr = initial_values[i].as_ref().unwrap();
|
let init_expr = initial_values[i].as_ref().unwrap();
|
||||||
self.build_expression(*init_expr.clone())?
|
self.build_expression(*init_expr.clone())?
|
||||||
} else {
|
} else {
|
||||||
// No initial value - assign void (uninitialized)
|
// No initial value - do not emit a const; leave uninitialized until assigned
|
||||||
let void_dst = self.value_gen.next();
|
// Use a fresh SSA id only for name binding; consumers should not use it before assignment
|
||||||
self.emit_instruction(MirInstruction::Const {
|
self.value_gen.next()
|
||||||
dst: void_dst,
|
|
||||||
value: ConstValue::Void,
|
|
||||||
})?;
|
|
||||||
void_dst
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register variable in SSA form
|
// Register variable in SSA form
|
||||||
@ -747,14 +833,10 @@ impl MirBuilder {
|
|||||||
last_value = Some(value_id);
|
last_value = Some(value_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the last assigned value, or void if no variables
|
// Return the last bound value id (no emission); callers shouldn't rely on this value
|
||||||
Ok(last_value.unwrap_or_else(|| {
|
Ok(last_value.unwrap_or_else(|| {
|
||||||
let void_val = self.value_gen.next();
|
// create a dummy id without emission
|
||||||
self.emit_instruction(MirInstruction::Const {
|
self.value_gen.next()
|
||||||
dst: void_val,
|
|
||||||
value: ConstValue::Void,
|
|
||||||
}).unwrap();
|
|
||||||
void_val
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -970,6 +1052,8 @@ impl MirBuilder {
|
|||||||
dst: me_value,
|
dst: me_value,
|
||||||
value: ConstValue::String("__me__".to_string()),
|
value: ConstValue::String("__me__".to_string()),
|
||||||
})?;
|
})?;
|
||||||
|
// Register a stable mapping so subsequent 'me' resolves to the same ValueId
|
||||||
|
self.variable_map.insert("me".to_string(), me_value);
|
||||||
Ok(me_value)
|
Ok(me_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user