feat: WASMビルド完全対応+finiシステム修正 🎉
## WASMビルド対応 - TimerBox、AudioBox等の問題のあるBoxをWASM環境では条件付きコンパイルで除外 - WebBox (WebDisplayBox, WebConsoleBox, WebCanvasBox) にas_anyメソッド追加 - プラグイン関連コードに#[cfg]ガードを追加 - web-sysフィーチャーを追加(Performance、MouseEvent等) - projects/nyash-wasmのビルドが完全に通るように! ## finiシステム修正 - フィールド差し替え時の自動fini削除(Nyashの明示的哲学に従う) - スコープ離脱時のみfini実行(meは除外) - ドキュメント更新で正しいfiniルールを明記 ## その他 - CLAUDE.mdにWASMビルド方法を追記(wasm-pack build --target web) - 開発サーバー起動方法を記載(python3 -m http.server 8010) - cargo testで全テスト成功を確認 これでNyashがブラウザで動作可能に!🐱✨ 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
17
CLAUDE.md
17
CLAUDE.md
@ -54,12 +54,25 @@ target/x86_64-pc-windows-msvc/release/nyash.exe
|
|||||||
|
|
||||||
### 🌐 WebAssembly版
|
### 🌐 WebAssembly版
|
||||||
```bash
|
```bash
|
||||||
# ブラウザープレイグラウンド
|
# WASMビルド方法1: nyash-wasmプロジェクトで直接ビルド
|
||||||
|
cd projects/nyash-wasm
|
||||||
|
wasm-pack build --target web
|
||||||
|
|
||||||
|
# WASMビルド方法2: build.shスクリプト使用(古い方法)
|
||||||
cd projects/nyash-wasm
|
cd projects/nyash-wasm
|
||||||
./build.sh
|
./build.sh
|
||||||
# nyash_playground.html をブラウザーで開く
|
|
||||||
|
# 開発サーバー起動(ポート8010推奨)
|
||||||
|
python3 -m http.server 8010
|
||||||
|
|
||||||
|
# ブラウザでアクセス
|
||||||
|
# http://localhost:8010/nyash_playground.html
|
||||||
|
# http://localhost:8010/enhanced_playground.html
|
||||||
|
# http://localhost:8010/canvas_playground.html
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**注意**: WASMビルドでは一部のBox(TimerBox、AudioBox等)は除外されます。
|
||||||
|
|
||||||
## 📚 ドキュメント構造
|
## 📚 ドキュメント構造
|
||||||
|
|
||||||
### 🎯 **最重要ドキュメント(開発者向け)**
|
### 🎯 **最重要ドキュメント(開発者向け)**
|
||||||
|
|||||||
13
Cargo.toml
13
Cargo.toml
@ -165,6 +165,19 @@ features = [
|
|||||||
"CanvasGradient",
|
"CanvasGradient",
|
||||||
"CanvasPattern",
|
"CanvasPattern",
|
||||||
"Path2d",
|
"Path2d",
|
||||||
|
"Performance",
|
||||||
|
"MouseEvent",
|
||||||
|
"TouchEvent",
|
||||||
|
"KeyboardEvent",
|
||||||
|
"AudioContext",
|
||||||
|
"AudioContextState",
|
||||||
|
"AudioBuffer",
|
||||||
|
"AudioBufferSourceNode",
|
||||||
|
"GainNode",
|
||||||
|
"AnalyserNode",
|
||||||
|
"AudioDestinationNode",
|
||||||
|
"PeriodicWave",
|
||||||
|
"OscillatorNode",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@ -223,6 +223,40 @@ cargo build --release -j32 --features wasm-backend
|
|||||||
|
|
||||||
## 🎯 今後の優先事項(copilot_issues.txt参照)
|
## 🎯 今後の優先事項(copilot_issues.txt参照)
|
||||||
|
|
||||||
|
### 🌐 **WASMブラウザー版ビルド修正**
|
||||||
|
- **問題**: projects/nyash-wasmのビルドが失敗(28個のコンパイルエラー)
|
||||||
|
- **原因と解決策(3ステップ)**:
|
||||||
|
|
||||||
|
#### **Step 1: プラグイン関連の条件コンパイル修正**
|
||||||
|
- **問題箇所**:
|
||||||
|
- `src/interpreter/expressions/calls.rs`: `use PluginBoxV2` が無条件
|
||||||
|
- `src/bid/loader.rs`: `use libloading` が無条件
|
||||||
|
- **修正内容**:
|
||||||
|
```rust
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **Step 2: web-sysフィーチャー追加**
|
||||||
|
- **不足フィーチャー**:
|
||||||
|
- Performance
|
||||||
|
- MouseEvent, TouchEvent, KeyboardEvent
|
||||||
|
- AudioContext, AudioBuffer, GainNode 等
|
||||||
|
- **修正内容**: Cargo.tomlの`[dependencies.web-sys]`に追加
|
||||||
|
|
||||||
|
#### **Step 3: wasm-pack buildコマンド修正**
|
||||||
|
- **現在**: デフォルトフィーチャー(plugins含む)でビルド
|
||||||
|
- **修正**: `wasm-pack build --target web --no-default-features --out-dir projects/nyash-wasm/pkg`
|
||||||
|
- **または**: WASM専用フィーチャー作成
|
||||||
|
|
||||||
|
- **最終確認**: `wasm-pack build`成功 → `nyash_playground.html`で動作確認
|
||||||
|
|
||||||
|
### 🚨 **緊急修正: finiシステムの統一**
|
||||||
|
- **問題**: ビルトインBoxにfiniメソッドがない(設計の不統一)
|
||||||
|
- **解決**: 全Box型(ビルトイン含む)にfiniメソッド追加
|
||||||
|
- **理由**: スコープ離脱時の統一的リソース管理
|
||||||
|
- **影響**: StringBox、IntegerBox等16種類のビルトインBox
|
||||||
|
|
||||||
### Phase 8.4: AST→MIR Lowering完全実装
|
### Phase 8.4: AST→MIR Lowering完全実装
|
||||||
- MIR命令セット設計済み(35命令)
|
- MIR命令セット設計済み(35命令)
|
||||||
- Lowering実装開始準備
|
- Lowering実装開始準備
|
||||||
|
|||||||
@ -231,18 +231,60 @@ Nyashは古典的な継承ではなく、デリゲーション(委譲)モデ
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **ファイナライズ (`fini`キーワード):**
|
- **ファイナライズ (`fini`キーワード):**
|
||||||
- `fini()`は「論理的な解放フック」として機能する特別なメソッドです。
|
- `fini()`は「リソース解放フック」として機能する特別なメソッドです。
|
||||||
- インスタンスに対して呼び出されると、そのインスタンスがもはや使用されるべきではないことを示します。
|
- **Nyashの明示的哲学**: 自動的な解放は最小限に留め、プログラマーが制御します。
|
||||||
- クリーンアップ処理を実行し、所有するすべてのフィールドに対して再帰的に`fini()`を呼び出します。
|
|
||||||
- ファイナライズされたオブジェクトを使用しようとすると(`fini`の再呼び出しを除く)、実行時エラーが発生します。
|
**🎯 finiが呼ばれる3つのタイミング:**
|
||||||
|
|
||||||
|
1. **スコープ離脱時(自動)**
|
||||||
|
- ローカル変数がスコープを抜ける時に自動的に呼ばれます
|
||||||
|
- ただし`me`(インスタンス自身)は除外されます
|
||||||
|
```nyash
|
||||||
|
function test() {
|
||||||
|
local resource = new FileBox("data.txt")
|
||||||
|
// 関数終了時、resourceのfini()が自動的に呼ばれる
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **明示的呼び出し(推奨)**
|
||||||
|
- プログラマーが必要に応じて明示的に呼び出します
|
||||||
|
```nyash
|
||||||
|
local file = new FileBox("temp.txt")
|
||||||
|
file.write("data")
|
||||||
|
file.fini() // 明示的にリソースを解放
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **インスタンスのfini時(カスケード)**
|
||||||
|
- インスタンスがfiniされる時、そのフィールドもfiniされます
|
||||||
|
- ただしweakフィールドは除外されます
|
||||||
|
|
||||||
|
**⚠️ 重要な注意点:**
|
||||||
|
- **フィールド差し替え時にはfiniは呼ばれません**(GC的な「おせっかい」を避ける設計)
|
||||||
|
- ファイナライズ後のオブジェクト使用は実行時エラーになります
|
||||||
|
- `fini()`の重複呼び出しは安全(何もしない)
|
||||||
|
- **すべてのBox型(ビルトイン含む)にfini実装が必要**(設計統一性)
|
||||||
|
|
||||||
```nyash
|
```nyash
|
||||||
box ManagedResource {
|
box ManagedResource {
|
||||||
init { handle }
|
init { handle, weak observer } // observerはweakなのでfiniされない
|
||||||
|
|
||||||
fini() {
|
fini() {
|
||||||
// ハンドルを解放したり、他のクリーンアップ処理を実行
|
// リソースのクリーンアップ
|
||||||
me.console.log("リソースをファイナライズしました。")
|
me.console.log("リソースをファイナライズしました。")
|
||||||
|
// handleは自動的にfiniされる(weakでない限り)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用例
|
||||||
|
local res = new ManagedResource()
|
||||||
|
res.handle = new FileBox("data.txt")
|
||||||
|
|
||||||
|
// ❌ これではfiniは呼ばれない(明示的管理)
|
||||||
|
res.handle = new FileBox("other.txt") // 古いhandleは解放されない!
|
||||||
|
|
||||||
|
// ✅ 正しい方法
|
||||||
|
res.handle.fini() // 明示的に古いリソースを解放
|
||||||
|
res.handle = new FileBox("other.txt")
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. 標準ライブラリアクセス (using & namespace) 🎉 **Phase 9.75e完了**
|
## 3. 標準ライブラリアクセス (using & namespace) 🎉 **Phase 9.75e完了**
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
mod plugin_impl {
|
||||||
|
|
||||||
use crate::bid::{BidError, BidResult, LoadedPlugin};
|
use crate::bid::{BidError, BidResult, LoadedPlugin};
|
||||||
use crate::bid::tlv::{TlvEncoder, TlvDecoder};
|
use crate::bid::tlv::{TlvEncoder, TlvDecoder};
|
||||||
use crate::bid::types::BidTag;
|
use crate::bid::types::BidTag;
|
||||||
@ -130,3 +133,8 @@ impl fmt::Display for GenericPluginBox {
|
|||||||
self.fmt_box(f)
|
self.fmt_box(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // mod plugin_impl
|
||||||
|
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
pub use plugin_impl::*;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::bid::{BidError, BidResult, NyashHostVtable, NyashPluginInfo, PluginHandle, PLUGIN_ABI_SYMBOL, PLUGIN_INIT_SYMBOL, PLUGIN_INVOKE_SYMBOL, PLUGIN_SHUTDOWN_SYMBOL};
|
use crate::bid::{BidError, BidResult, NyashHostVtable, NyashPluginInfo, PluginHandle, PLUGIN_ABI_SYMBOL, PLUGIN_INIT_SYMBOL, PLUGIN_INVOKE_SYMBOL, PLUGIN_SHUTDOWN_SYMBOL};
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
use libloading::{Library, Symbol};
|
use libloading::{Library, Symbol};
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|||||||
@ -8,6 +8,7 @@ pub mod metadata;
|
|||||||
pub mod plugin_api;
|
pub mod plugin_api;
|
||||||
pub mod bridge;
|
pub mod bridge;
|
||||||
pub mod plugins;
|
pub mod plugins;
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
// pub mod registry; // legacy - v2 plugin system uses BoxFactoryRegistry instead
|
// pub mod registry; // legacy - v2 plugin system uses BoxFactoryRegistry instead
|
||||||
// pub mod plugin_box; // legacy - FileBox専用実装
|
// pub mod plugin_box; // legacy - FileBox専用実装
|
||||||
@ -19,6 +20,7 @@ pub use error::*;
|
|||||||
pub use metadata::*;
|
pub use metadata::*;
|
||||||
pub use plugin_api::*;
|
pub use plugin_api::*;
|
||||||
pub use bridge::*;
|
pub use bridge::*;
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
pub use loader::*;
|
pub use loader::*;
|
||||||
// pub use registry::*; // legacy - v2 plugin system uses BoxFactoryRegistry instead
|
// pub use registry::*; // legacy - v2 plugin system uses BoxFactoryRegistry instead
|
||||||
// pub use plugin_box::*; // legacy
|
// pub use plugin_box::*; // legacy
|
||||||
|
|||||||
@ -66,38 +66,14 @@ use web_sys::{
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AudioBox {
|
pub struct AudioBox {
|
||||||
base: BoxBase,
|
base: BoxBase,
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
context: Option<AudioContext>,
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
gain_node: Option<GainNode>,
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
analyser_node: Option<AnalyserNode>,
|
|
||||||
volume: f64,
|
volume: f64,
|
||||||
is_playing: bool,
|
is_playing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioBox {
|
impl AudioBox {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let context = AudioContext::new().ok();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let (gain_node, analyser_node) = if let Some(ctx) = &context {
|
|
||||||
let gain = ctx.create_gain().ok();
|
|
||||||
let analyser = ctx.create_analyser().ok();
|
|
||||||
(gain, analyser)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base: BoxBase::new(),
|
base: BoxBase::new(),
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
context,
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
gain_node,
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
analyser_node,
|
|
||||||
volume: 1.0,
|
volume: 1.0,
|
||||||
is_playing: false,
|
is_playing: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,10 +60,16 @@ pub mod math_box;
|
|||||||
pub mod time_box;
|
pub mod time_box;
|
||||||
pub mod debug_box;
|
pub mod debug_box;
|
||||||
pub mod random_box;
|
pub mod random_box;
|
||||||
|
// These boxes use web APIs that require special handling in WASM
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod timer_box;
|
pub mod timer_box;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod canvas_event_box;
|
pub mod canvas_event_box;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod canvas_loop_box;
|
pub mod canvas_loop_box;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod audio_box;
|
pub mod audio_box;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod qr_box;
|
pub mod qr_box;
|
||||||
pub mod sound_box;
|
pub mod sound_box;
|
||||||
pub mod map_box;
|
pub mod map_box;
|
||||||
@ -85,10 +91,15 @@ pub use math_box::{MathBox, FloatBox};
|
|||||||
pub use time_box::{TimeBox, DateTimeBox};
|
pub use time_box::{TimeBox, DateTimeBox};
|
||||||
pub use debug_box::DebugBox;
|
pub use debug_box::DebugBox;
|
||||||
pub use random_box::RandomBox;
|
pub use random_box::RandomBox;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use timer_box::TimerBox;
|
pub use timer_box::TimerBox;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use canvas_event_box::CanvasEventBox;
|
pub use canvas_event_box::CanvasEventBox;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use canvas_loop_box::CanvasLoopBox;
|
pub use canvas_loop_box::CanvasLoopBox;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use audio_box::AudioBox;
|
pub use audio_box::AudioBox;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use qr_box::QRBox;
|
pub use qr_box::QRBox;
|
||||||
pub use sound_box::SoundBox;
|
pub use sound_box::SoundBox;
|
||||||
pub use map_box::MapBox;
|
pub use map_box::MapBox;
|
||||||
|
|||||||
@ -52,25 +52,18 @@ use std::any::Any;
|
|||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use web_sys::{window, Performance};
|
use web_sys::window;
|
||||||
|
|
||||||
/// タイマー管理Box
|
/// タイマー管理Box
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TimerBox {
|
pub struct TimerBox {
|
||||||
base: BoxBase,
|
base: BoxBase,
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
performance: Option<Performance>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimerBox {
|
impl TimerBox {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let performance = window().and_then(|w| w.performance().ok());
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base: BoxBase::new(),
|
base: BoxBase::new(),
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
performance,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,8 +71,12 @@ impl TimerBox {
|
|||||||
pub fn now(&self) -> f64 {
|
pub fn now(&self) -> f64 {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
{
|
{
|
||||||
if let Some(perf) = &self.performance {
|
if let Some(window) = window() {
|
||||||
perf.now()
|
if let Ok(perf) = window.performance() {
|
||||||
|
perf.now()
|
||||||
|
} else {
|
||||||
|
js_sys::Date::now()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
js_sys::Date::now()
|
js_sys::Date::now()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -275,6 +275,9 @@ impl BoxCore for WebCanvasBox {
|
|||||||
write!(f, "WebCanvasBox({}, {}x{})", self.canvas_id, self.width, self.height)
|
write!(f, "WebCanvasBox({}, {}x{})", self.canvas_id, self.width, self.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
|
|||||||
@ -152,6 +152,9 @@ impl BoxCore for WebConsoleBox {
|
|||||||
write!(f, "WebConsoleBox({})", self.target_element_id)
|
write!(f, "WebConsoleBox({})", self.target_element_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
|
|||||||
@ -145,6 +145,9 @@ impl BoxCore for WebDisplayBox {
|
|||||||
write!(f, "WebDisplayBox({})", self.target_element_id)
|
write!(f, "WebDisplayBox({})", self.target_element_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
|
|||||||
@ -503,6 +503,30 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn restore_local_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
|
pub(super) fn restore_local_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
|
||||||
|
// 🎯 スコープ離脱時:現在のローカル変数に対してfiniを呼ぶ
|
||||||
|
// ただし「me」は特別扱い(インスタンス自身なのでfiniしない)
|
||||||
|
for (name, value) in &self.local_vars {
|
||||||
|
// 「me」はインスタンス自身なのでスコープ離脱時にfiniしない
|
||||||
|
if name == "me" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ユーザー定義Box(InstanceBox)の場合
|
||||||
|
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
|
||||||
|
let _ = instance.fini();
|
||||||
|
eprintln!("🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)", name);
|
||||||
|
}
|
||||||
|
// プラグインBoxの場合
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
if let Some(plugin) = (**value).as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
|
plugin.call_fini();
|
||||||
|
eprintln!("🔄 Scope exit: Called fini() on local variable '{}' (PluginBox)", name);
|
||||||
|
}
|
||||||
|
// ビルトインBoxは元々finiメソッドを持たないので呼ばない
|
||||||
|
// (StringBox、IntegerBox等はリソース管理不要)
|
||||||
|
}
|
||||||
|
|
||||||
|
// その後、保存されていた変数で復元
|
||||||
self.local_vars = saved.into_iter()
|
self.local_vars = saved.into_iter()
|
||||||
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
|
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
|
||||||
.collect();
|
.collect();
|
||||||
@ -516,6 +540,23 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn restore_outbox_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
|
pub(super) fn restore_outbox_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
|
||||||
|
// 🎯 スコープ離脱時:現在のoutbox変数に対してもfiniを呼ぶ
|
||||||
|
for (name, value) in &self.outbox_vars {
|
||||||
|
// ユーザー定義Box(InstanceBox)の場合
|
||||||
|
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
|
||||||
|
let _ = instance.fini();
|
||||||
|
eprintln!("🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)", name);
|
||||||
|
}
|
||||||
|
// プラグインBoxの場合
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
if let Some(plugin) = (**value).as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
|
plugin.call_fini();
|
||||||
|
eprintln!("🔄 Scope exit: Called fini() on outbox variable '{}' (PluginBox)", name);
|
||||||
|
}
|
||||||
|
// ビルトインBoxは元々finiメソッドを持たないので呼ばない(要修正)
|
||||||
|
}
|
||||||
|
|
||||||
|
// その後、保存されていた変数で復元
|
||||||
self.outbox_vars = saved.into_iter()
|
self.outbox_vars = saved.into_iter()
|
||||||
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
|
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@ -13,6 +13,7 @@ use crate::instance_v2::InstanceBox;
|
|||||||
use crate::channel_box::ChannelBox;
|
use crate::channel_box::ChannelBox;
|
||||||
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
use crate::interpreter::core::{NyashInterpreter, RuntimeError};
|
||||||
use crate::interpreter::finalization;
|
use crate::interpreter::finalization;
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -489,6 +490,7 @@ impl NyashInterpreter {
|
|||||||
// RangeBox method calls (将来的に追加予定)
|
// RangeBox method calls (将来的に追加予定)
|
||||||
|
|
||||||
// PluginBoxV2 method calls
|
// PluginBoxV2 method calls
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
if let Some(plugin_box) = obj_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
if let Some(plugin_box) = obj_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
return self.execute_plugin_box_v2_method(plugin_box, method, arguments);
|
return self.execute_plugin_box_v2_method(plugin_box, method, arguments);
|
||||||
}
|
}
|
||||||
@ -902,6 +904,7 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Execute method call on PluginBoxV2
|
/// Execute method call on PluginBoxV2
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
fn execute_plugin_box_v2_method(
|
fn execute_plugin_box_v2_method(
|
||||||
&mut self,
|
&mut self,
|
||||||
plugin_box: &PluginBoxV2,
|
plugin_box: &PluginBoxV2,
|
||||||
|
|||||||
@ -305,17 +305,8 @@ impl NyashInterpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 既存のフィールド値があれば fini() を呼ぶ
|
// 🚨 フィールド差し替え時の自動finiは削除(Nyashの明示的哲学)
|
||||||
if let Some(old_field_value) = instance.get_field(field) {
|
// プログラマーが必要なら明示的にfini()を呼ぶべき
|
||||||
if let Some(old_instance) = (*old_field_value).as_any().downcast_ref::<InstanceBox>() {
|
|
||||||
let _ = old_instance.fini();
|
|
||||||
finalization::mark_as_finalized(old_instance.box_id());
|
|
||||||
}
|
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
|
||||||
if let Some(old_plugin) = (*old_field_value).as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
|
||||||
old_plugin.call_fini();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.set_field(field, Arc::from(val.clone_box()))
|
instance.set_field(field, Arc::from(val.clone_box()))
|
||||||
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
||||||
@ -342,17 +333,8 @@ impl NyashInterpreter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 既存のthis.field値があれば fini() を呼ぶ
|
// 🚨 フィールド差し替え時の自動finiは削除(Nyashの明示的哲学)
|
||||||
if let Some(old_field_value) = instance.get_field(field) {
|
// プログラマーが必要なら明示的にfini()を呼ぶべき
|
||||||
if let Some(old_instance) = (*old_field_value).as_any().downcast_ref::<InstanceBox>() {
|
|
||||||
let _ = old_instance.fini();
|
|
||||||
finalization::mark_as_finalized(old_instance.box_id());
|
|
||||||
}
|
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
|
||||||
if let Some(old_plugin) = (*old_field_value).as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
|
||||||
old_plugin.call_fini();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.set_field(field, Arc::from(val.clone_box()))
|
instance.set_field(field, Arc::from(val.clone_box()))
|
||||||
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
||||||
@ -379,17 +361,8 @@ impl NyashInterpreter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 既存のme.field値があれば fini() を呼ぶ
|
// 🚨 フィールド差し替え時の自動finiは削除(Nyashの明示的哲学)
|
||||||
if let Some(old_field_value) = instance.get_field(field) {
|
// プログラマーが必要なら明示的にfini()を呼ぶべき
|
||||||
if let Some(old_instance) = (*old_field_value).as_any().downcast_ref::<InstanceBox>() {
|
|
||||||
let _ = old_instance.fini();
|
|
||||||
finalization::mark_as_finalized(old_instance.box_id());
|
|
||||||
}
|
|
||||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
|
||||||
if let Some(old_plugin) = (*old_field_value).as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
|
||||||
old_plugin.call_fini();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.set_field(field, Arc::from(val.clone_box()))
|
instance.set_field(field, Arc::from(val.clone_box()))
|
||||||
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use crate::boxes::web::{WebDisplayBox, WebConsoleBox, WebCanvasBox};
|
use crate::boxes::web::{WebDisplayBox, WebConsoleBox, WebCanvasBox};
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use crate::boxes::FloatBox;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
impl NyashInterpreter {
|
impl NyashInterpreter {
|
||||||
|
|||||||
@ -399,8 +399,14 @@ mod stub {
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
pub struct PluginLoaderV2;
|
pub struct PluginLoaderV2 {
|
||||||
impl PluginLoaderV2 { pub fn new() -> Self { Self } }
|
pub config: Option<()>, // Dummy config for compatibility
|
||||||
|
}
|
||||||
|
impl PluginLoaderV2 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { config: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
impl PluginLoaderV2 {
|
impl PluginLoaderV2 {
|
||||||
pub fn load_config(&mut self, _p: &str) -> BidResult<()> { Ok(()) }
|
pub fn load_config(&mut self, _p: &str) -> BidResult<()> { Ok(()) }
|
||||||
pub fn load_all_plugins(&self) -> BidResult<()> { Ok(()) }
|
pub fn load_all_plugins(&self) -> BidResult<()> { Ok(()) }
|
||||||
|
|||||||
Reference in New Issue
Block a user