Fixed the method ID order in HttpRequestBox configuration to match plugin implementation: - path: method_id 1 (was incorrectly 2) - readBody: method_id 2 (was incorrectly 3) - respond: method_id 3 (was incorrectly 1) This resolves the 45-day debugging issue where req.respond(resp) was calling the wrong plugin method, causing HTTP responses to have empty bodies. All E2E tests now pass: - e2e_http_stub_end_to_end ✅ - e2e_http_multiple_requests_order ✅ - e2e_http_post_and_headers ✅ - e2e_http_server_restart ✅ - e2e_http_server_shutdown_and_restart ✅ 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
222 lines
9.0 KiB
Rust
222 lines
9.0 KiB
Rust
/*!
|
||
* Special Methods Module
|
||
*
|
||
* Extracted from box_methods.rs
|
||
* Contains specialized Box method implementations:
|
||
*
|
||
* - execute_method_box_method (MethodBox) - イベントハンドラー/関数ポインタ機能
|
||
* - execute_sound_method (SoundBox) - オーディオ機能
|
||
*
|
||
* These are critical special-purpose Box implementations:
|
||
* - MethodBox: Essential for event handling and callback functionality
|
||
* - SoundBox: Essential for audio feedback and game sound effects
|
||
*/
|
||
|
||
use super::*;
|
||
use crate::boxes::SoundBox;
|
||
use crate::method_box::MethodBox;
|
||
use crate::instance_v2::InstanceBox;
|
||
|
||
impl NyashInterpreter {
|
||
/// SoundBoxのメソッド呼び出しを実行
|
||
///
|
||
/// SoundBoxはオーディオ機能を提供する重要なBox:
|
||
/// - beep(), beeps() - 基本的なビープ音
|
||
/// - tone() - カスタム周波数/期間の音
|
||
/// - alert(), success(), error() - UI音効果
|
||
/// - pattern() - 音パターン再生
|
||
/// - volumeTest() - 音量テスト
|
||
/// - interval() - 間隔付き音再生
|
||
pub(super) fn execute_sound_method(&mut self, sound_box: &SoundBox, method: &str, arguments: &[ASTNode])
|
||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
// 引数を評価
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.execute_expression(arg)?);
|
||
}
|
||
|
||
// メソッドを実行
|
||
match method {
|
||
"beep" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("beep() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.beep())
|
||
}
|
||
"beeps" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("beeps() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.beeps(arg_values[0].clone_box()))
|
||
}
|
||
"tone" => {
|
||
if arg_values.len() != 2 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("tone() expects 2 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.tone(arg_values[0].clone_box(), arg_values[1].clone_box()))
|
||
}
|
||
"alert" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("alert() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.alert())
|
||
}
|
||
"success" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("success() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.success())
|
||
}
|
||
"error" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("error() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.error())
|
||
}
|
||
"pattern" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("pattern() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.pattern(arg_values[0].clone_box()))
|
||
}
|
||
"volumeTest" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("volumeTest() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.volumeTest())
|
||
}
|
||
"interval" => {
|
||
if arg_values.len() != 2 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("interval() expects 2 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(sound_box.interval(arg_values[0].clone_box(), arg_values[1].clone_box()))
|
||
}
|
||
_ => {
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Unknown SoundBox method: {}", method),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
/// MethodBoxのメソッド呼び出しを実行
|
||
///
|
||
/// MethodBoxはイベントハンドラー機能の核心:
|
||
/// - invoke() - メソッド参照を実際に呼び出し
|
||
/// - 関数ポインタ相当の機能を提供
|
||
/// - GUI/イベント駆動プログラミングに必須
|
||
pub(super) fn execute_method_box_method(&mut self, method_box: &MethodBox, method: &str, arguments: &[ASTNode])
|
||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
match method {
|
||
"invoke" => {
|
||
// 引数を評価
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.execute_expression(arg)?);
|
||
}
|
||
|
||
// MethodBoxのinvokeを呼び出す
|
||
self.invoke_method_box(method_box, arg_values)
|
||
}
|
||
_ => {
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Unknown MethodBox method: {}", method),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
/// MethodBoxでメソッドを実際に呼び出す
|
||
///
|
||
/// この関数はMethodBoxの中核機能:
|
||
/// 1. インスタンスとメソッド名からメソッドを取得
|
||
/// 2. 引数数の検証
|
||
/// 3. local変数スタック管理
|
||
/// 4. 'me' 変数の設定
|
||
/// 5. メソッド実行
|
||
/// 6. 戻り値処理
|
||
fn invoke_method_box(&mut self, method_box: &MethodBox, args: Vec<Box<dyn NyashBox>>)
|
||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
// インスタンスを取得
|
||
let instance_arc = method_box.get_instance();
|
||
let instance = instance_arc.lock().unwrap();
|
||
|
||
// InstanceBoxにダウンキャスト
|
||
if let Some(instance_box) = instance.as_any().downcast_ref::<InstanceBox>() {
|
||
// メソッドを取得
|
||
let method_ast = instance_box.get_method(&method_box.method_name)
|
||
.ok_or(RuntimeError::InvalidOperation {
|
||
message: format!("Method '{}' not found", method_box.method_name),
|
||
})?
|
||
.clone();
|
||
|
||
// メソッド呼び出しを実行
|
||
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast {
|
||
// パラメータ数チェック
|
||
if args.len() != params.len() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("Method {} expects {} arguments, got {}",
|
||
method_box.method_name, params.len(), args.len()),
|
||
});
|
||
}
|
||
|
||
// local変数スタックを保存
|
||
let saved_locals = self.save_local_vars();
|
||
self.local_vars.clear();
|
||
|
||
// meをlocal変数として設定(インスタンス自体)
|
||
self.declare_local_variable("me", instance.clone_or_share());
|
||
|
||
// パラメータをlocal変数として設定
|
||
for (param, arg) in params.iter().zip(args.iter()) {
|
||
self.declare_local_variable(param, arg.clone_or_share());
|
||
}
|
||
|
||
// メソッド本体を実行
|
||
let mut result = Box::new(crate::box_trait::VoidBox::new()) as Box<dyn NyashBox>;
|
||
for statement in &body {
|
||
result = self.execute_statement(statement)?;
|
||
|
||
// return文チェック
|
||
if let super::ControlFlow::Return(ret_val) = &self.control_flow {
|
||
result = ret_val.clone_box();
|
||
self.control_flow = super::ControlFlow::None;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// local変数スタックを復元
|
||
self.restore_local_vars(saved_locals);
|
||
|
||
Ok(result)
|
||
} else {
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Method '{}' is not a valid function declaration", method_box.method_name),
|
||
})
|
||
}
|
||
} else {
|
||
Err(RuntimeError::TypeError {
|
||
message: "MethodBox instance is not an InstanceBox".to_string(),
|
||
})
|
||
}
|
||
}
|
||
}
|