//! Method invocation (invokevirtual, invokestatic, execute_method).

use crate::class_file::{ClassFile, ConstantPoolEntry, MethodInfo};
use crate::error::{to_runtime_error_enum, JvmError, NativeError, RuntimeError};
use crate::inline_cache::InlineCacheEntry;
use crate::memory::{Memory, StackFrame, Value};
use crate::profiler::ProfileGuard;
use log::info;
use std::sync::Arc;

use super::descriptor;
use super::utils;
use super::Interpreter;
use super::InterpreterResult;

impl Interpreter {
    pub(crate) fn collect_method_args(
        &self,
        frame: &mut StackFrame,
        descriptor_str: &str,
    ) -> Result<Vec<Value>, JvmError> {
        let param_types = descriptor::parse_method_params(descriptor_str);
        let mut args = Vec::new();
        for _ in 0..param_types.len() {
            args.push(frame.pop()?);
        }
        args.reverse();
        Ok(args)
    }

    pub(crate) fn resolve_method(
        &self,
        class_name: &str,
        method_name: &str,
        #[cfg_attr(not(feature = "interop"), allow(unused_variables))]
        descriptor: &str,
    ) -> Option<(String, String)> {
        let class = self.class_loader.get_class(class_name)?;
        if class.find_method(method_name, descriptor).is_some() {
            return Some((class_name.to_string(), method_name.to_string()));
        }
        if let Some(super_class_name) = class.get_super_class_name() {
            return self.resolve_method(&super_class_name, method_name, descriptor);
        }
        None
    }

    pub(crate) fn resolve_interface_method(
        &self,
        interface_name: &str,
        method_name: &str,
        #[cfg_attr(not(feature = "interop"), allow(unused_variables))]
        descriptor: &str,
    ) -> Option<(String, String)> {
        self.resolve_method(interface_name, method_name, descriptor)
    }

    pub(crate) fn invoke_native_method(
        &mut self,
        class_name: &str,
        method_name: &str,
        #[cfg_attr(not(feature = "interop"), allow(unused_variables))]
        descriptor: &str,
        is_instance: bool,
        caller_frame: &mut StackFrame,
    ) -> InterpreterResult {
        if let Some(native) = self.native_registry.find_method(class_name, method_name) {
            let sig = native.signature();
            let mut args = if is_instance {
                vec![caller_frame.pop()?]
            } else {
                Vec::new()
            };
            args.extend(self.collect_method_args(caller_frame, sig)?);
            match native.invoke(&args, &mut self.memory) {
                Ok(result) => {
                    if !sig.ends_with(")V") {
                        caller_frame.push(result)?;
                    }
                    Ok(())
                }
                Err(e) => Err(JvmError::NativeError(e)),
            }
        } else {
            #[cfg(feature = "interop")]
            if let Ok(result) = self.try_invoke_rust_callback(
                class_name,
                method_name,
                descriptor,
                is_instance,
                caller_frame,
            ) {
                return result;
            }
            Err(JvmError::NativeError(NativeError::NativeMethodNotFound(
                class_name.to_string(),
                method_name.to_string(),
            )))
        }
    }

    /// Invoke a registered Rust callback (interop feature). No JNI overhead.
    #[cfg(feature = "interop")]
    fn try_invoke_rust_callback(
        &mut self,
        class_name: &str,
        method_name: &str,
        #[cfg_attr(not(feature = "interop"), allow(unused_variables))]
        descriptor: &str,
        is_instance: bool,
        caller_frame: &mut StackFrame,
    ) -> Result<InterpreterResult, ()> {
        use crate::interop;
        let key = format!("{}.{}", class_name.replace('/', "."), method_name);
        if !interop::has_rust_callback(&key) {
            return Err(());
        }
        let mut args = if is_instance {
            vec![caller_frame.pop().map_err(|_| ())?]
        } else {
            Vec::new()
        };
        args.extend(
            self.collect_method_args(caller_frame, descriptor)
                .map_err(|_| ())?,
        );
        match interop::invoke_rust_callback(&key, &args) {
            Ok(result) => {
                if !descriptor.ends_with(")V") {
                    let _ = caller_frame.push(result);
                }
                Ok(Ok(()))
            }
            Err(_) => Err(()),
        }
    }

    pub(crate) fn invoke_virtual(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let _class_name = class.get_class_name().unwrap_or_else(|| "Unknown".to_string());
        let opcode_addr = frame.pc.saturating_sub(2);
        
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;

        let (class_index, name_and_type_index) = match cp_entry {
            ConstantPoolEntry::ConstantMethodref {
                class_index,
                name_and_type_index,
            }
            | ConstantPoolEntry::ConstantInterfaceMethodref {
                class_index,
                name_and_type_index,
            } => (class_index, name_and_type_index),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected MethodRef or InterfaceMethodRef constant".to_string(),
                )));
            }
        };

        let target_class_name = match class.constant_pool.get(*class_index as usize) {
            Some(ConstantPoolEntry::ConstantClass { name_index }) => {
                class.get_string(*name_index).unwrap_or_default()
            }
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid class reference".to_string(),
                )));
            }
        };

        let (method_name, descriptor) = match class.constant_pool.get(*name_and_type_index as usize)
        {
            Some(ConstantPoolEntry::ConstantNameAndType {
                name_index,
                descriptor_index,
            }) => (
                class.get_string(*name_index).unwrap_or_default(),
                class.get_string(*descriptor_index).unwrap_or_default(),
            ),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid NameAndType reference".to_string(),
                )));
            }
        };

        if target_class_name == "java/io/PrintStream" && method_name == "println" {
            if frame.stack.len() >= 2 {
                let value = frame.pop()?;
                let _objectref = frame.pop()?;
                self.native_println(value)?;
            } else if !frame.stack.is_empty() {
                let _objectref = frame.pop()?;
                println!();
            } else {
                return Err(to_runtime_error_enum(RuntimeError::StackUnderflow));
            }
            return Ok(());
        }

        // Try inline cache first if enabled
        if self.inline_cache_manager.is_enabled() {
            if let Some(cache_entry) = self.inline_cache_manager.get_cache(
                &target_class_name,
                &method_name,
                &descriptor,
                opcode_addr,
            ) {
                if let Some(cached) = cache_entry.lookup(&target_class_name, &method_name, &descriptor) {
                    // Check if cached method is still valid
                    let resolved_class_name = cached.resolved_class.clone();
                    let class_ref = self.class_loader.get_class(&resolved_class_name);
                    if let Some(class_ref) = class_ref.clone() {
                        if let Some(method_info) = class_ref.find_method(&method_name, &descriptor) {
                            // Update cache stats with the actual cached entry
                            let cache_key = (target_class_name.clone(), method_name.clone(), descriptor.clone(), opcode_addr);
                            let entry = InlineCacheEntry {
                                receiver_class: target_class_name.clone(),
                                method_name: method_name.clone(),
                                descriptor: descriptor.clone(),
                                resolved_class: resolved_class_name.clone(),
                                method: Arc::new(method_info.clone()),
                                hit_count: cached.hit_count + 1,
                            };
                            self.inline_cache_manager.update_cache(&cache_key.0, &cache_key.1, &cache_key.2, cache_key.3, entry);
                            
                            // Clone the class and method info to avoid borrow conflicts
                            let class_ref_clone = class_ref.clone();
                            let method_info_clone = method_info.clone();
                            return self.execute_method(&class_ref_clone, &method_info_clone, frame);
                        }
                    }
                }
            }
        }

        // Fall back to regular method resolution
        if let Some((resolved_class_name, _)) =
            self.resolve_method(&target_class_name, &method_name, &descriptor)
        {
            if let Some(class_ref) = self.class_loader.get_class(&resolved_class_name) {
                let method_name_clone = method_name.clone();
                let descriptor_clone = descriptor.clone();
                let class_clone = class_ref.clone();

                if let Some(method) = class_clone.find_method(&method_name_clone, &descriptor_clone)
                {
                    // Update inline cache if enabled
                    if self.inline_cache_manager.is_enabled() {
                        let entry = InlineCacheEntry {
                            receiver_class: target_class_name.clone(),
                            method_name: method_name.clone(),
                            descriptor: descriptor.clone(),
                            resolved_class: resolved_class_name.clone(),
                            method: Arc::new(method.clone()),
                            hit_count: 0,
                        };
                        self.inline_cache_manager.update_cache(
                            &target_class_name,
                            &method_name,
                            &descriptor,
                            opcode_addr,
                            entry,
                        );
                    }
                    return self.execute_method(&class_clone, method, frame);
                }
            }
        }

        Err(to_runtime_error_enum(RuntimeError::MethodNotFound(
            target_class_name,
            method_name,
        )))
    }

    pub(crate) fn get_static(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;

        let (class_index, name_and_type_index) = match cp_entry {
            ConstantPoolEntry::ConstantFieldref {
                class_index,
                name_and_type_index,
            } => (class_index, name_and_type_index),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected FieldRef for getstatic".to_string(),
                )));
            }
        };

        let class_name = match class.constant_pool.get(*class_index as usize) {
            Some(ConstantPoolEntry::ConstantClass { name_index }) => {
                class.get_string(*name_index).unwrap_or_default()
            }
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid class reference for getstatic".to_string(),
                )));
            }
        };
        let name_and_type = class.constant_pool.get(*name_and_type_index as usize);
        let (field_name, _desc) = match name_and_type {
            Some(ConstantPoolEntry::ConstantNameAndType {
                name_index,
                descriptor_index,
            }) => (
                class.get_string(*name_index).unwrap_or_default(),
                class.get_string(*descriptor_index).unwrap_or_default(),
            ),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid NameAndType".to_string(),
                )));
            }
        };

        self.load_class_by_name(&class_name).ok();
        self.ensure_system_out();

        if class_name == "java/lang/System" && field_name == "out" {
            let val = self
                .memory
                .get_static(&class_name, &field_name)
                .cloned()
                .ok_or_else(|| {
                    to_runtime_error_enum(RuntimeError::FieldNotFound(class_name, field_name))
                })?;
            frame.push(val)?;
            return Ok(());
        }

        let val = self
            .memory
            .get_static(&class_name, &field_name)
            .cloned()
            .ok_or_else(|| {
                to_runtime_error_enum(RuntimeError::FieldNotFound(
                    class_name.clone(),
                    field_name.clone(),
                ))
            })?;
        frame.push(val)?;
        Ok(())
    }

    pub(crate) fn put_static(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;

        let (class_index, name_and_type_index) = match cp_entry {
            ConstantPoolEntry::ConstantFieldref {
                class_index,
                name_and_type_index,
            } => (class_index, name_and_type_index),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected FieldRef for putstatic".to_string(),
                )));
            }
        };

        let class_name = match class.constant_pool.get(*class_index as usize) {
            Some(ConstantPoolEntry::ConstantClass { name_index }) => {
                class.get_string(*name_index).unwrap_or_default()
            }
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid class reference for putstatic".to_string(),
                )));
            }
        };
        let name_and_type = class.constant_pool.get(*name_and_type_index as usize);
        let (field_name, _desc) = match name_and_type {
            Some(ConstantPoolEntry::ConstantNameAndType {
                name_index,
                descriptor_index,
            }) => (
                class.get_string(*name_index).unwrap_or_default(),
                class.get_string(*descriptor_index).unwrap_or_default(),
            ),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid NameAndType".to_string(),
                )));
            }
        };

        let val = frame.pop()?;
        self.memory.set_static(class_name, field_name, val);
        Ok(())
    }

    pub(crate) fn get_field(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let obj_ref = frame
            .pop()?
            .as_reference()
            .ok_or_else(|| to_runtime_error_enum(RuntimeError::NullPointerException))?;
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;
        let (_class_index, name_and_type_index) = match cp_entry {
            ConstantPoolEntry::ConstantFieldref {
                class_index,
                name_and_type_index,
            } => (class_index, name_and_type_index),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected FieldRef".to_string(),
                )));
            }
        };
        let name_and_type = class.constant_pool.get(*name_and_type_index as usize);
        let field_name = match name_and_type {
            Some(ConstantPoolEntry::ConstantNameAndType { name_index, .. }) => {
                class.get_string(*name_index).unwrap_or_default()
            }
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid NameAndType".to_string(),
                )));
            }
        };
        let val = self
            .memory
            .heap
            .get_field(obj_ref, &field_name)
            .ok_or_else(|| {
                to_runtime_error_enum(RuntimeError::FieldNotFound(
                    "object".to_string(),
                    field_name.clone(),
                ))
            })?;
        frame.push(val)?;
        Ok(())
    }

    pub(crate) fn put_field(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let val = frame.pop()?;
        let obj_ref = frame
            .pop()?
            .as_reference()
            .ok_or_else(|| to_runtime_error_enum(RuntimeError::NullPointerException))?;
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;
        let (_class_index, name_and_type_index) = match cp_entry {
            ConstantPoolEntry::ConstantFieldref {
                class_index,
                name_and_type_index,
            } => (class_index, name_and_type_index),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected FieldRef".to_string(),
                )));
            }
        };
        let name_and_type = class.constant_pool.get(*name_and_type_index as usize);
        let field_name = match name_and_type {
            Some(ConstantPoolEntry::ConstantNameAndType { name_index, .. }) => {
                class.get_string(*name_index).unwrap_or_default()
            }
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid NameAndType".to_string(),
                )));
            }
        };
        self.memory
            .heap
            .set_field(obj_ref, field_name, val)
            .map_err(|e| to_runtime_error_enum(RuntimeError::from(e)))?;
        Ok(())
    }

    pub(crate) fn invoke_special(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;
        let (class_index, name_and_type_index) = match cp_entry {
            ConstantPoolEntry::ConstantMethodref {
                class_index,
                name_and_type_index,
            } => (class_index, name_and_type_index),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected MethodRef for invokespecial".to_string(),
                )));
            }
        };
        let target_class_name = class.get_string(*class_index).unwrap_or_default();
        let (method_name, descriptor) = match class.constant_pool.get(*name_and_type_index as usize)
        {
            Some(ConstantPoolEntry::ConstantNameAndType {
                name_index,
                descriptor_index,
            }) => (
                class.get_string(*name_index).unwrap_or_default(),
                class.get_string(*descriptor_index).unwrap_or_default(),
            ),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid NameAndType".to_string(),
                )));
            }
        };
        self.load_class_by_name(&target_class_name).ok();
        if let Some(class_ref) = self.class_loader.get_class(&target_class_name) {
            if let Some(method) = class_ref.find_method(&method_name, &descriptor) {
                let code_attr = self.find_code_attribute(class_ref, method);
                if method_name == "<init>" && code_attr.map(|a| a.info.len()).unwrap_or(0) <= 8 {
                    return Ok(());
                }
                let class_clone = class_ref.clone();
                let method_clone = method.clone();
                return self.execute_method(&class_clone, &method_clone, frame);
            }
        }
        Err(to_runtime_error_enum(RuntimeError::MethodNotFound(
            target_class_name,
            method_name,
        )))
    }

    pub(crate) fn do_new(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;
        let class_index = match cp_entry {
            ConstantPoolEntry::ConstantClass { name_index } => *name_index,
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected Class constant for new".to_string(),
                )));
            }
        };
        let class_name = class.get_string(class_index).unwrap_or_default();
        self.load_class_by_name(&class_name).ok();
        let addr = self.memory.heap.allocate(class_name);
        frame.push(Value::Reference(addr))?;
        Ok(())
    }

    pub(crate) fn do_anewarray(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;
        let class_name = match cp_entry {
            ConstantPoolEntry::ConstantClass { name_index } => class
                .get_string(*name_index)
                .unwrap_or_else(|| "java/lang/Object".to_string()),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected Class constant for anewarray".to_string(),
                )));
            }
        };
        let len = frame.pop()?.as_int();
        if len < 0 {
            return Err(to_runtime_error_enum(
                RuntimeError::NegativeArraySizeException(len),
            ));
        }
        use crate::memory::HeapArray;
        let arr = HeapArray::ReferenceArray(class_name, vec![0; len as usize]);
        let addr = self.memory.heap.allocate_array(arr);
        frame.push(Value::ArrayRef(addr))?;
        Ok(())
    }

    pub(crate) fn invoke_static(
        &mut self,
        class: &ClassFile,
        frame: &mut StackFrame,
        index: usize,
    ) -> InterpreterResult {
        let cp_entry = class.constant_pool.get(index).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::IllegalArgument(format!(
                "Constant pool index {} out of bounds",
                index
            )))
        })?;

        let (class_index, name_and_type_index) = match cp_entry {
            ConstantPoolEntry::ConstantMethodref {
                class_index,
                name_and_type_index,
            } => (class_index, name_and_type_index),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Expected MethodRef for invokestatic".to_string(),
                )));
            }
        };

        let target_class_name = match class.constant_pool.get(*class_index as usize) {
            Some(ConstantPoolEntry::ConstantClass { name_index }) => {
                class.get_string(*name_index).unwrap_or_default()
            }
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid class reference".to_string(),
                )));
            }
        };

        let (method_name, descriptor) = match class.constant_pool.get(*name_and_type_index as usize)
        {
            Some(ConstantPoolEntry::ConstantNameAndType {
                name_index,
                descriptor_index,
            }) => (
                class.get_string(*name_index).unwrap_or_default(),
                class.get_string(*descriptor_index).unwrap_or_default(),
            ),
            _ => {
                return Err(to_runtime_error_enum(RuntimeError::IllegalArgument(
                    "Invalid NameAndType reference".to_string(),
                )));
            }
        };

        self.load_class_by_name(&target_class_name).ok();
        if let Some(class_ref) = self.class_loader.get_class(&target_class_name) {
            if let Some(method) = class_ref.find_method(&method_name, &descriptor) {
                let class_clone = class_ref.clone();
                let method_clone = method.clone();
                return self.execute_method(&class_clone, &method_clone, frame);
            }
        }

        Err(to_runtime_error_enum(RuntimeError::MethodNotFound(
            target_class_name,
            method_name,
        )))
    }

    pub(crate) fn execute_method(
        &mut self,
        class: &ClassFile,
        method: &MethodInfo,
        caller_frame: &mut StackFrame,
    ) -> InterpreterResult {
        let method_name = class
            .get_string(method.name_index)
            .unwrap_or_else(|| "unknown".to_string());
        let class_name = class.get_class_name().unwrap_or("Unknown".to_string());

        if (method.access_flags & 0x0100) != 0 {
            let descriptor = class
                .get_string(method.descriptor_index)
                .unwrap_or_else(|| "()V".to_string());
            return self.invoke_native_method(
                &class_name,
                &method_name,
                &descriptor,
                (method.access_flags & 0x0008) == 0,
                caller_frame,
            );
        }

        let profiler_clone = self.profiler.clone();
        let _profile_guard = profiler_clone
            .as_ref()
            .map(|p| ProfileGuard::new(p.as_ref(), &class_name, &method_name));

        if let Some(jit_manager) = &mut self.jit_manager {
            if let Some(level) = jit_manager.record_and_check_compilation(&class_name, &method_name)
            {
                info!(
                    "Hot method detected: {}::{} - compiling at level {:?}",
                    class_name, method_name, level
                );
                if let Ok(compiled) =
                    jit_manager.get_or_compile_method_at(class, method, Some(level))
                {
                    info!(
                        "Method {}::{} compiled in {}ms ({} bytes)",
                        class_name, method_name, compiled.compile_time_ms, compiled.code_size
                    );
                }
            }
        }

        let full_method_name = format!("{}.{}", class_name, method_name);

        if let Some(jit_manager) = &self.jit_manager {
            if let Some(compiled_code) = jit_manager
                .compiler
                .get_compiled_function(&full_method_name)
            {
                if compiled_code.code_size == 0 {
                    // Placeholder - fall through to interpreter
                } else {
                    info!("Executing JIT compiled method: {}", full_method_name);

                    let code_attr = self.find_code_attribute(class, method).ok_or_else(|| {
                        to_runtime_error_enum(RuntimeError::UnsupportedOperation(
                            "Code attribute not found".to_string(),
                        ))
                    })?;

                    let max_locals = utils::read_u16(&code_attr.info, 0) as usize;
                    let max_stack = utils::read_u16(&code_attr.info, 2) as usize;

                    let mut frame = StackFrame::new(max_locals, max_stack, method_name.clone());

                    let descriptor = class
                        .get_string(method.descriptor_index)
                        .unwrap_or_default();
                    let mut param_count = descriptor::count_parameters(&descriptor);
                    if (method.access_flags & 0x0008) == 0 {
                        param_count += 1;
                    }

                    for i in 0..param_count {
                        let value = caller_frame.pop()?;
                        frame.store_local(param_count - 1 - i, value)?;
                    }

                    unsafe {
                        let result = (compiled_code.func)(
                            &mut self.memory as *mut Memory,
                            &mut frame as *mut StackFrame,
                        );

                        if result != 0 {
                            return Err(to_runtime_error_enum(RuntimeError::UnsupportedOperation(
                                format!("JIT compiled method returned error code: {}", result),
                            )));
                        }
                    }

                    if !frame.stack.is_empty() {
                        let return_value = frame.pop()?;
                        caller_frame.push(return_value)?;
                    }

                    return Ok(());
                }
            }
        }

        let code_attr = self.find_code_attribute(class, method).ok_or_else(|| {
            to_runtime_error_enum(RuntimeError::UnsupportedOperation(
                "Code attribute not found".to_string(),
            ))
        })?;

        let max_stack = utils::read_u16(&code_attr.info, 0) as usize;
        let max_locals = utils::read_u16(&code_attr.info, 2) as usize;
        let code_length = utils::read_u32(&code_attr.info, 4) as usize;

        let mut frame = StackFrame::new(max_locals, max_stack, method_name.clone());

        let descriptor = class
            .get_string(method.descriptor_index)
            .unwrap_or_default();
        let mut param_count = descriptor::count_parameters(&descriptor);
        if (method.access_flags & 0x0008) == 0 {
            param_count += 1;
        }

        for i in 0..param_count {
            let value = caller_frame.pop()?;
            frame.store_local(param_count - 1 - i, value)?;
        }

        let code = &code_attr.info[8..8 + code_length];
        let class_clone = class.clone();

        while frame.pc < code.len() {
            let opcode = code[frame.pc];
            let pc_before = frame.pc;
            frame.pc += 1;

            if let Some(ref mut tr) = self.trace_recorder {
                tr.record(
                    pc_before,
                    opcode,
                    &format!("{}.{}", class_name, method_name),
                    frame.stack.len(),
                    frame.locals.len(),
                );
            }

            self.debugger.log_instruction(&frame, &class_clone, opcode);

            // Return opcodes transfer the top-of-stack value to the caller
            // before breaking out of the dispatch loop.
            if opcode == 0xb1 {
                break;
            } else if opcode == 0xac || opcode == 0xb0 {
                if !frame.stack.is_empty() {
                    let return_value = frame.pop()?;
                    caller_frame.push(return_value)?;
                }
                break;
            } else if !super::DISPATCH_TABLE[opcode as usize](
                self,
                &class_clone,
                code,
                &mut frame,
                opcode,
            )? {
                break;
            }
        }

        if !frame.stack.is_empty() {
            let return_value = frame.pop()?;
            caller_frame.push(return_value)?;
        }

        Ok(())
    }
}
