diff --git a/src/interpreter/method_dispatch.rs.legacy b/src/interpreter/method_dispatch.rs.legacy deleted file mode 100644 index c43ab290..00000000 --- a/src/interpreter/method_dispatch.rs.legacy +++ /dev/null @@ -1,539 +0,0 @@ -/*! - * Method Dispatch Module - * - * Extracted from expressions.rs lines 383-900 (~517 lines) - * Handles method call dispatch for all Box types and static function calls - * Core philosophy: "Everything is Box" with unified method dispatch - */ - -use super::*; -use crate::boxes::{buffer::BufferBox, JSONBox, HttpClientBox, StreamBox, RegexBox, IntentBox, SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox}; -use crate::boxes::{FloatBox, MathBox, ConsoleBox, TimeBox, DateTimeBox, RandomBox, SoundBox, DebugBox, file::FileBox, MapBox}; -use crate::bid::plugin_box::PluginFileBox; -use crate::runtime::plugin_loader_v2::PluginBoxV2; -use std::sync::Arc; - -impl NyashInterpreter { - /// メソッド呼び出しを実行 - 全Box型の統一ディスパッチ - pub(super) fn execute_method_call(&mut self, object: &ASTNode, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { - - // 🔥 static関数のチェック - if let ASTNode::Variable { name, .. } = object { - // static関数が存在するかチェック - let static_func = { - let static_funcs = self.shared.static_functions.read().unwrap(); - if let Some(box_statics) = static_funcs.get(name) { - if let Some(func) = box_statics.get(method) { - Some(func.clone()) - } else { - None - } - } else { - None - } - }; - - if let Some(static_func) = static_func { - return self.execute_static_function(static_func, name, method, arguments); - } - - // 📚 nyashstd標準ライブラリのメソッドチェック - if let Some(stdlib_result) = self.try_execute_stdlib_method(name, method, arguments)? { - return Ok(stdlib_result); - } - } - - // オブジェクトを評価(通常のメソッド呼び出し) - let obj_value = self.execute_expression(object)?; - - // Fallback: built-in type ops as instance methods: value.is("Type"), value.as("Type") - if (method == "is" || method == "as") && arguments.len() == 1 { - let ty_box = self.execute_expression(&arguments[0])?; - let type_name = if let Some(s) = ty_box.as_any().downcast_ref::() { - s.value.clone() - } else { - return Err(RuntimeError::InvalidOperation { message: "Type name must be a string".to_string() }); - }; - if method == "is" { - let matched = super::functions::NyashInterpreter::matches_type_name(&obj_value, &type_name); - return Ok(Box::new(crate::box_trait::BoolBox::new(matched))); - } else { - return super::functions::NyashInterpreter::cast_to_type(obj_value, &type_name); - } - } - - eprintln!("🔍 DEBUG: execute_method_call - object evaluated to type_name='{}', box_id={}", - obj_value.type_name(), obj_value.box_id()); - - // 各Box型に対するメソッドディスパッチ - self.dispatch_builtin_method(&obj_value, method, arguments, object) - } - - /// static関数を実行 - fn execute_static_function( - &mut self, - static_func: ASTNode, - box_name: &str, - method: &str, - arguments: &[ASTNode] - ) -> Result, RuntimeError> { - if let ASTNode::FunctionDeclaration { params, body, .. } = static_func { - // 引数を評価 - let mut arg_values = Vec::new(); - for arg in arguments { - arg_values.push(self.execute_expression(arg)?); - } - - // パラメータ数チェック - if arg_values.len() != params.len() { - return Err(RuntimeError::InvalidOperation { - message: format!("Static method {}.{} expects {} arguments, got {}", - box_name, method, params.len(), arg_values.len()), - }); - } - - // 🌍 local変数スタックを保存・クリア(static関数呼び出し開始) - let saved_locals = self.save_local_vars(); - self.local_vars.clear(); - - // 📤 outbox変数スタックも保存・クリア(static関数専用) - let saved_outbox = self.save_outbox_vars(); - self.outbox_vars.clear(); - - // 引数をlocal変数として設定 - for (param, value) in params.iter().zip(arg_values.iter()) { - self.declare_local_variable(param, value.clone_box()); - } - - // static関数の本体を実行 - let mut result = Box::new(VoidBox::new()) as Box; - for statement in &body { - result = self.execute_statement(statement)?; - - // return文チェック - if let super::ControlFlow::Return(return_val) = &self.control_flow { - result = return_val.clone_box(); - self.control_flow = super::ControlFlow::None; - break; - } - } - - // local変数スタックを復元 - self.restore_local_vars(saved_locals); - - // outbox変数スタックを復元 - self.restore_outbox_vars(saved_outbox); - - Ok(result) - } else { - Err(RuntimeError::InvalidOperation { - message: format!("Invalid static function: {}.{}", box_name, method), - }) - } - } - - /// nyashstd標準ライブラリメソッド実行を試行 - fn try_execute_stdlib_method( - &mut self, - box_name: &str, - method: &str, - arguments: &[ASTNode] - ) -> Result>, RuntimeError> { - let stdlib_method = if let Some(ref stdlib) = self.stdlib { - if let Some(nyashstd_namespace) = stdlib.namespaces.get("nyashstd") { - if let Some(static_box) = nyashstd_namespace.static_boxes.get(box_name) { - if let Some(builtin_method) = static_box.methods.get(method) { - Some(*builtin_method) // Copyトレイトで関数ポインターをコピー - } else { - eprintln!("🔍 Method '{}' not found in nyashstd.{}", method, box_name); - None - } - } else { - eprintln!("🔍 Static box '{}' not found in nyashstd", box_name); - None - } - } else { - eprintln!("🔍 nyashstd namespace not found in stdlib"); - None - } - } else { - eprintln!("🔍 stdlib not initialized for method call"); - None - }; - - if let Some(builtin_method) = stdlib_method { - eprintln!("🌟 Calling nyashstd method: {}.{}", box_name, method); - - // 引数を評価 - let mut arg_values = Vec::new(); - for arg in arguments { - arg_values.push(self.execute_expression(arg)?); - } - - // 標準ライブラリのメソッドを実行 - let result = builtin_method(&arg_values)?; - eprintln!("✅ nyashstd method completed: {}.{}", box_name, method); - return Ok(Some(result)); - } - - Ok(None) - } - - /// ビルトインBox型メソッドディスパッチ - fn dispatch_builtin_method( - &mut self, - obj_value: &Box, - method: &str, - arguments: &[ASTNode], - object: &ASTNode - ) -> Result, RuntimeError> { - // Debug: Log the actual type - eprintln!("🔍 DEBUG: dispatch_builtin_method called for type_name='{}', method='{}'", - obj_value.type_name(), method); - eprintln!("🔍 DEBUG: obj_value box_id={}", obj_value.box_id()); - - // StringBox method calls - if let Some(string_box) = obj_value.as_any().downcast_ref::() { - eprintln!("🔍 DEBUG: Matched as StringBox!"); - return self.execute_string_method(string_box, method, arguments); - } - - // IntegerBox method calls - if let Some(integer_box) = obj_value.as_any().downcast_ref::() { - return self.execute_integer_method(integer_box, method, arguments); - } - - // FloatBox method calls - if let Some(float_box) = obj_value.as_any().downcast_ref::() { - return self.execute_float_method(float_box, method, arguments); - } - - // BoolBox method calls - if let Some(bool_box) = obj_value.as_any().downcast_ref::() { - return self.execute_bool_method(bool_box, method, arguments); - } - - // ArrayBox method calls - if let Some(array_box) = obj_value.as_any().downcast_ref::() { - return self.execute_array_method(array_box, method, arguments); - } - - // BufferBox method calls - if let Some(buffer_box) = obj_value.as_any().downcast_ref::() { - return self.execute_buffer_method(buffer_box, method, arguments); - } - - // FileBox method calls - if let Some(file_box) = obj_value.as_any().downcast_ref::() { - return self.execute_file_method(file_box, method, arguments); - } - // Plugin-backed FileBox method calls - if let Some(pfile) = obj_value.as_any().downcast_ref::() { - return self.execute_plugin_file_method(pfile, method, arguments); - } - - // ResultBox method calls - if let Some(result_box) = obj_value.as_any().downcast_ref::() { - return self.execute_result_method(result_box, method, arguments); - } - - // FutureBox method calls - if let Some(future_box) = obj_value.as_any().downcast_ref::() { - return self.execute_future_method(future_box, method, arguments); - } - - // ChannelBox method calls - if let Some(channel_box) = obj_value.as_any().downcast_ref::() { - return self.execute_channel_method(channel_box, method, arguments); - } - - // JSONBox method calls - if let Some(json_box) = obj_value.as_any().downcast_ref::() { - return self.execute_json_method(json_box, method, arguments); - } - - // HttpClientBox method calls - if let Some(http_box) = obj_value.as_any().downcast_ref::() { - return self.execute_http_method(http_box, method, arguments); - } - - // StreamBox method calls - if let Some(stream_box) = obj_value.as_any().downcast_ref::() { - return self.execute_stream_method(stream_box, method, arguments); - } - - // RegexBox method calls - if let Some(regex_box) = obj_value.as_any().downcast_ref::() { - return self.execute_regex_method(regex_box, method, arguments); - } - - // MathBox method calls - if let Some(math_box) = obj_value.as_any().downcast_ref::() { - return self.execute_math_method(math_box, method, arguments); - } - - // NullBox method calls - if let Some(null_box) = obj_value.as_any().downcast_ref::() { - return self.execute_null_method(null_box, method, arguments); - } - - // TimeBox method calls - if let Some(time_box) = obj_value.as_any().downcast_ref::() { - return self.execute_time_method(time_box, method, arguments); - } - - // DateTimeBox method calls - if let Some(datetime_box) = obj_value.as_any().downcast_ref::() { - return self.execute_datetime_method(datetime_box, method, arguments); - } - - // TimerBox method calls - if let Some(timer_box) = obj_value.as_any().downcast_ref::() { - return self.execute_timer_method(timer_box, method, arguments); - } - - // MapBox method calls - if let Some(map_box) = obj_value.as_any().downcast_ref::() { - return self.execute_map_method(map_box, method, arguments); - } - - // RandomBox method calls - if let Some(random_box) = obj_value.as_any().downcast_ref::() { - return self.execute_random_method(random_box, method, arguments); - } - - // SoundBox method calls - if let Some(sound_box) = obj_value.as_any().downcast_ref::() { - return self.execute_sound_method(sound_box, method, arguments); - } - - // DebugBox method calls - if let Some(debug_box) = obj_value.as_any().downcast_ref::() { - return self.execute_debug_method(debug_box, method, arguments); - } - - // ConsoleBox method calls - if let Some(console_box) = obj_value.as_any().downcast_ref::() { - return self.execute_console_method(console_box, method, arguments); - } - - // IntentBox method calls - if let Some(intent_box) = obj_value.as_any().downcast_ref::() { - return self.execute_intent_box_method(intent_box, method, arguments); - } - - // SocketBox method calls - if let Some(socket_box) = obj_value.as_any().downcast_ref::() { - let result = self.execute_socket_method(socket_box, method, arguments)?; - - // 🔧 FIX: Update stored variable for stateful SocketBox methods - if matches!(method, "bind" | "connect" | "close") { - self.update_stateful_socket_box(object, socket_box)?; - } - - return Ok(result); - } - - // HTTPServerBox method calls - if let Some(http_server_box) = obj_value.as_any().downcast_ref::() { - return self.execute_http_server_method(http_server_box, method, arguments); - } - - // HTTPRequestBox method calls - if let Some(http_request_box) = obj_value.as_any().downcast_ref::() { - return self.execute_http_request_method(http_request_box, method, arguments); - } - - // HTTPResponseBox method calls - if let Some(http_response_box) = obj_value.as_any().downcast_ref::() { - return self.execute_http_response_method(http_response_box, method, arguments); - } - - // P2PBox method calls - Temporarily disabled - // if let Some(p2p_box) = obj_value.as_any().downcast_ref::() { - // return self.execute_p2p_box_method(p2p_box, method, arguments); - // } - - // EguiBox method calls (非WASM環境のみ) - #[cfg(all(feature = "gui", not(target_arch = "wasm32")))] - if let Some(egui_box) = obj_value.as_any().downcast_ref::() { - return self.execute_egui_method(egui_box, method, arguments); - } - - // WebDisplayBox method calls (WASM環境のみ) - #[cfg(target_arch = "wasm32")] - if let Some(web_display_box) = obj_value.as_any().downcast_ref::() { - return self.execute_web_display_method(web_display_box, method, arguments); - } - - // WebConsoleBox method calls (WASM環境のみ) - #[cfg(target_arch = "wasm32")] - if let Some(web_console_box) = obj_value.as_any().downcast_ref::() { - return self.execute_web_console_method(web_console_box, method, arguments); - } - - // WebCanvasBox method calls (WASM環境のみ) - #[cfg(target_arch = "wasm32")] - if let Some(web_canvas_box) = obj_value.as_any().downcast_ref::() { - return self.execute_web_canvas_method(web_canvas_box, method, arguments); - } - - // PluginBoxV2 method calls - eprintln!("🔍 DEBUG: Checking for PluginBoxV2..."); - if let Some(plugin_box) = obj_value.as_any().downcast_ref::() { - eprintln!("🔍 DEBUG: Matched as PluginBoxV2! box_type={}, instance_id={}", - plugin_box.box_type, plugin_box.instance_id); - return self.execute_plugin_box_v2_method(plugin_box, method, arguments); - } - eprintln!("🔍 DEBUG: Not matched as PluginBoxV2") - - // ユーザー定義Boxのメソッド呼び出し - self.execute_user_defined_method(obj_value, method, arguments) - } - - fn execute_plugin_file_method( - &mut self, - pfile: &PluginFileBox, - method: &str, - arguments: &[ASTNode], - ) -> Result, RuntimeError> { - match method { - "write" => { - if arguments.len() != 1 { - return Err(RuntimeError::InvalidOperation { message: "FileBox.write expects 1 argument".into() }); - } - let arg0 = self.execute_expression(&arguments[0])?; - let data = arg0.to_string_box().value; - pfile.write_bytes(data.as_bytes()).map_err(|e| RuntimeError::RuntimeFailure { message: format!("plugin write error: {:?}", e) })?; - Ok(Box::new(StringBox::new("ok"))) - } - "read" => { - // Default read size - let size = 1_048_576usize; // 1MB max - let bytes = pfile.read_bytes(size).map_err(|e| RuntimeError::RuntimeFailure { message: format!("plugin read error: {:?}", e) })?; - let s = String::from_utf8_lossy(&bytes).to_string(); - Ok(Box::new(StringBox::new(s))) - } - "close" => { - pfile.close().map_err(|e| RuntimeError::RuntimeFailure { message: format!("plugin close error: {:?}", e) })?; - Ok(Box::new(StringBox::new("ok"))) - } - _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method FileBox.{} (plugin)", method) }) - } - } - - fn execute_plugin_box_v2_method( - &mut self, - plugin_box: &PluginBoxV2, - method: &str, - arguments: &[ASTNode], - ) -> Result, RuntimeError> { - // Delegate to unified facade for correct TLV typing and config resolution - self.call_plugin_method(plugin_box, method, arguments) - } - - /// SocketBoxの状態変更を反映 - fn update_stateful_socket_box( - &mut self, - object: &ASTNode, - socket_box: &SocketBox - ) -> Result<(), RuntimeError> { - eprintln!("🔧 DEBUG: Stateful method called, updating stored instance"); - let updated_instance = socket_box.clone(); - eprintln!("🔧 DEBUG: Updated instance created with ID={}", updated_instance.box_id()); - - match object { - ASTNode::Variable { name, .. } => { - eprintln!("🔧 DEBUG: Updating local variable '{}'", name); - if let Some(stored_var) = self.local_vars.get_mut(name) { - eprintln!("🔧 DEBUG: Found local variable '{}', updating from id={} to id={}", - name, stored_var.box_id(), updated_instance.box_id()); - *stored_var = Arc::new(updated_instance); - } else { - eprintln!("🔧 DEBUG: Local variable '{}' not found", name); - } - }, - ASTNode::FieldAccess { object: field_obj, field, .. } => { - eprintln!("🔧 DEBUG: Updating field access '{}'", field); - self.update_field_with_socket_box(field_obj, field, updated_instance)?; - }, - _ => { - eprintln!("🔧 DEBUG: Object type not handled: {:?}", object); - } - } - - Ok(()) - } - - /// フィールドアクセスでのSocketBox更新 - fn update_field_with_socket_box( - &mut self, - field_obj: &ASTNode, - field: &str, - updated_instance: SocketBox - ) -> Result<(), RuntimeError> { - match field_obj { - ASTNode::Variable { name, .. } => { - eprintln!("🔧 DEBUG: Field object is variable '{}'", name); - if name == "me" { - eprintln!("🔧 DEBUG: Updating me.{} (via variable)", field); - if let Ok(me_instance) = self.resolve_variable("me") { - eprintln!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id()); - if let Some(instance) = (*me_instance).as_any().downcast_ref::() { - eprintln!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id()); - let result = instance.set_field(field, Arc::new(updated_instance)); - eprintln!("🔧 DEBUG: set_field result: {:?}", result); - } else { - eprintln!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name()); - } - } else { - eprintln!("🔧 DEBUG: Failed to resolve 'me'"); - } - } else { - eprintln!("🔧 DEBUG: Field object is not 'me', it's '{}'", name); - } - }, - ASTNode::Me { .. } => { - eprintln!("🔧 DEBUG: Field object is Me node, updating me.{}", field); - if let Ok(me_instance) = self.resolve_variable("me") { - eprintln!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id()); - if let Some(instance) = (*me_instance).as_any().downcast_ref::() { - eprintln!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id()); - let result = instance.set_field(field, Arc::new(updated_instance)); - eprintln!("🔧 DEBUG: set_field result: {:?}", result); - } else { - eprintln!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name()); - } - } else { - eprintln!("🔧 DEBUG: Failed to resolve 'me'"); - } - }, - _ => { - eprintln!("🔧 DEBUG: Field object is not a variable or me, type: {:?}", field_obj); - } - } - - Ok(()) - } - - /// ユーザー定義Boxメソッド実行 - fn execute_user_defined_method( - &mut self, - obj_value: &Box, - method: &str, - arguments: &[ASTNode] - ) -> Result, RuntimeError> { - // InstanceBox method calls (user-defined methods) - if let Some(instance) = obj_value.as_any().downcast_ref::() { - return self.execute_instance_method(instance, method, arguments); - } - - // Static box method calls would be handled here if implemented - // (Currently handled via different mechanism in static function dispatch) - - Err(RuntimeError::InvalidOperation { - message: format!("Method '{}' not found on type '{}'", method, obj_value.type_name()), - }) - } -} diff --git a/tests/e2e_plugin_net.rs b/tests/e2e_plugin_net.rs deleted file mode 100644 index a8f5bbd6..00000000 --- a/tests/e2e_plugin_net.rs +++ /dev/null @@ -1,355 +0,0 @@ - -#![cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - -use nyash_rust::parser::NyashParser; -use nyash_rust::runtime::plugin_loader_v2::{init_global_loader_v2, get_global_loader_v2}; -use nyash_rust::runtime::box_registry::get_global_registry; -use nyash_rust::runtime::PluginConfig; -use nyash_rust::runtime::NyashRuntime; -use nyash_rust::backend::VM; - -fn try_init_plugins() -> bool { - if !std::path::Path::new("nyash.toml").exists() { return false; } - if let Err(e) = init_global_loader_v2("nyash.toml") { eprintln!("init failed: {:?}", e); return false; } - let loader = get_global_loader_v2(); - let loader = loader.read().unwrap(); - if let Some(conf) = &loader.config { - let mut map = std::collections::HashMap::new(); - for (lib, def) in &conf.libraries { for b in &def.boxes { map.insert(b.clone(), lib.clone()); } } - get_global_registry().apply_plugin_config(&PluginConfig { plugins: map }); - true - } else { false } -} - -#[test] -fn e2e_http_stub_end_to_end() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r, resp, req, body -srv = new HttpServerBox() -srv.start(8080) - -cli = new HttpClientBox() -r = cli.get("http://localhost:8080/hello") - -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.setStatus(200) -resp.write("OK") -req.respond(resp) - -resp = r.get_value() -body = resp.readBody() -body -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let mut interpreter = nyash_rust::interpreter::NyashInterpreter::new(); - let result = interpreter.execute(ast).expect("exec failed"); - assert_eq!(result.to_string_box().value, "OK"); -} - -#[test] -fn e2e_vm_http_get_basic() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin3.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r, resp, req, body -srv = new HttpServerBox() -srv.start(8085) - -cli = new HttpClientBox() -r = cli.get("http://localhost:8085/hello") - -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.write("OK") -req.respond(resp) - -resp = r.get_value() -body = resp.readBody() -body -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let runtime = NyashRuntime::new(); - let mut compiler = nyash_rust::mir::MirCompiler::new(); - let compile_result = compiler.compile(ast).expect("mir compile failed"); - let mut vm = VM::with_runtime(runtime); - let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); - assert_eq!(result.to_string_box().value, "OK"); -} - -#[test] -fn e2e_vm_http_status_404() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin3.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r, resp, req, body, st -srv = new HttpServerBox() -srv.start(8086) - -cli = new HttpClientBox() -r = cli.get("http://localhost:8086/notfound") - -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.setStatus(404) -resp.write("NF") -req.respond(resp) - -resp = r.get_value() -st = resp.getStatus() -body = resp.readBody() -st.toString() + ":" + body -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let runtime = NyashRuntime::new(); - let mut compiler = nyash_rust::mir::MirCompiler::new(); - let compile_result = compiler.compile(ast).expect("mir compile failed"); - let mut vm = VM::with_runtime(runtime); - let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); - assert_eq!(result.to_string_box().value, "404:NF"); -} - -#[test] -fn e2e_vm_http_status_500() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin3.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r, resp, req, body, st -srv = new HttpServerBox() -srv.start(8084) - -cli = new HttpClientBox() -r = cli.get("http://localhost:8084/error") - -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.setStatus(500) -resp.write("ERR") -req.respond(resp) - -resp = r.get_value() -st = resp.getStatus() -body = resp.readBody() -st.toString() + ":" + body -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let runtime = NyashRuntime::new(); - let mut compiler = nyash_rust::mir::MirCompiler::new(); - let compile_result = compiler.compile(ast).expect("mir compile failed"); - let mut vm = VM::with_runtime(runtime); - let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); - assert_eq!(result.to_string_box().value, "500:ERR"); -} - -#[test] -fn e2e_vm_http_post_and_headers() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin3.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r, resp, req, body, st, hv -srv = new HttpServerBox() -srv.start(8086) - -cli = new HttpClientBox() -r = cli.post("http://localhost:8086/api", "DATA") - -req = srv.accept().get_value() -// check server saw body -body = req.readBody() -// prepare response -resp = new HttpResponseBox() -resp.setStatus(201) -resp.setHeader("X-Test", "V") -resp.write("R") -req.respond(resp) - -// client reads status, header, body -resp = r.get_value() -st = resp.getStatus() -hv = resp.getHeader("X-Test") -body = resp.readBody() -st.toString() + ":" + hv + ":" + body -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let runtime = NyashRuntime::new(); - let mut compiler = nyash_rust::mir::MirCompiler::new(); - let compile_result = compiler.compile(ast).expect("mir compile failed"); - let mut vm = VM::with_runtime(runtime); - let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); - assert_eq!(result.to_string_box().value, "201:V:R"); -} - -#[test] -fn e2e_http_server_restart() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r, resp, req, body -srv = new HttpServerBox() -srv.start(8081) - -cli = new HttpClientBox() -r = cli.get("http://localhost:8081/test1") -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.write("A") -req.respond(resp) - -srv.stop() -srv.start(8081) - -resp = r.get_value() -_ = resp.readBody() # consume first response (optional) - -r = cli.get("http://localhost:8081/test2") -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.write("B") -req.respond(resp) - -resp = r.get_value() -body = resp.readBody() -body -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let mut interpreter = nyash_rust::interpreter::NyashInterpreter::new(); - let result = interpreter.execute(ast).expect("exec failed"); - assert_eq!(result.to_string_box().value, "B"); -} - -#[test] -fn e2e_http_server_shutdown_and_restart() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin.log"); - if !try_init_plugins() { return; } - - // First run: start and respond - let code1 = r#" -local srv, cli, r, resp, req -srv = new HttpServerBox() -srv.start(8082) -cli = new HttpClientBox() -r = cli.get("http://localhost:8082/first") -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.write("X") -req.respond(resp) -"#; - let ast1 = NyashParser::parse_from_string(code1).expect("parse1"); - let mut i1 = nyash_rust::interpreter::NyashInterpreter::new(); - i1.execute(ast1).expect("exec1"); - - // Shutdown plugins (finalize singleton) and re-init - nyash_rust::runtime::plugin_loader_v2::shutdown_plugins_v2().expect("shutdown ok"); - assert!(try_init_plugins()); - - // Second run: ensure fresh instance works - let code2 = r#" -local srv, cli, r, req, resp, body -srv = new HttpServerBox() -srv.start(8083) -cli = new HttpClientBox() -r = cli.get("http://localhost:8083/second") -req = srv.accept().get_value() -resp = new HttpResponseBox() -resp.write("Y") -req.respond(resp) -resp = r.get_value() -body = resp.readBody() -body -"#; - let ast2 = NyashParser::parse_from_string(code2).expect("parse2"); - let mut i2 = nyash_rust::interpreter::NyashInterpreter::new(); - let result = i2.execute(ast2).expect("exec2"); - assert_eq!(result.to_string_box().value, "Y"); -} - -#[test] -fn e2e_http_post_and_headers() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r, resp, req, body, st, hv -srv = new HttpServerBox() -srv.start(8090) - -cli = new HttpClientBox() -r = cli.post("http://localhost:8090/api", "DATA") - -req = srv.accept().get_value() -// check server saw body -body = req.readBody() -// prepare response -resp = new HttpResponseBox() -resp.setStatus(201) -resp.setHeader("X-Test", "V") -resp.write("R") -req.respond(resp) - -// client reads status, header, body -resp = r.get_value() -st = resp.getStatus() -hv = resp.getHeader("X-Test") -body = resp.readBody() -st.toString() + ":" + hv + ":" + body -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let mut interpreter = nyash_rust::interpreter::NyashInterpreter::new(); - let result = interpreter.execute(ast).expect("exec failed"); - assert_eq!(result.to_string_box().value, "201:V:R"); -} - -#[test] -fn e2e_http_multiple_requests_order() { - std::env::set_var("NYASH_NET_LOG", "1"); - std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin.log"); - if !try_init_plugins() { return; } - - let code = r#" -local srv, cli, r1, r2, r3, req1, req2, req3, q1, q2, q3 -srv = new HttpServerBox() -srv.start(8091) - -cli = new HttpClientBox() -r1 = cli.get("http://localhost:8091/a") -r2 = cli.get("http://localhost:8091/b") -r3 = cli.get("http://localhost:8091/c") - -req1 = srv.accept().get_value() -q1 = req1.path() -req2 = srv.accept().get_value() -q2 = req2.path() -req3 = srv.accept().get_value() -q3 = req3.path() - -q1 + "," + q2 + "," + q3 -"#; - - let ast = NyashParser::parse_from_string(code).expect("parse failed"); - let mut interpreter = nyash_rust::interpreter::NyashInterpreter::new(); - let result = interpreter.execute(ast).expect("exec failed"); - assert_eq!(result.to_string_box().value, "/a,/b,/c"); -}