Files
hakorune/src/interpreter/special_methods.rs
Moe Charm 080458d4d4 fix: Correct HttpRequestBox method_id mapping in nyash.toml
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>
2025-08-22 12:09:06 +09:00

222 lines
9.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* 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(),
})
}
}
}