/*
 * Copyright (c) 2026-present, the Ladybird developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

//! FFI bridge between the codegen and C++ runtime.
//!
//! This module handles the boundary between the bytecode generator
//! and the C++ `Bytecode::Executable` / `SharedFunctionInstanceData` types.
//!
//! ## Key operations
//!
//! - `create_executable()` -- packages assembled bytecode, tables, and
//!   metadata into a C++ `Bytecode::Executable` via `rust_create_executable()`
//! - `create_shared_function_data()` -- creates a C++ `SharedFunctionInstanceData`
//!   for a parsed function, transferring ownership of the AST
//! - `compile_regex()` -- delegates regex compilation to the C++ regex engine
//!
//! ## FFI types
//!
//! All `FFI*` structs are `#[repr(C)]`.

use std::ffi::c_void;
use std::mem::align_of;

use super::generator::AssembledBytecode;
use super::generator::ConstantValue;
use super::generator::ExceptionHandler;
use super::generator::Generator;
use super::generator::PendingClassBlueprint;
use super::generator::PendingClassElement;
use super::generator::PendingLiteralValueKind;
use crate::ast::Utf16String;
use crate::bytecode::basic_block::SourceMapEntry;
use crate::u32_from_usize;

/// Opaque pointer returned from rust_create_executable.
pub type ExecutableHandle = *mut c_void;

#[derive(Clone, Copy)]
pub enum SharedFunctionDataOwner {
    None,
    List(*mut c_void),
}

#[derive(Clone, Copy)]
pub struct SharedFunctionDataCreationContext {
    pub vm_ptr: *mut c_void,
    pub source_code_ptr: *const c_void,
    pub owner: SharedFunctionDataOwner,
}

/// Exception handler range (C++ `BytecodeFactory::ExceptionHandlerData`).
#[repr(C)]
pub struct FFIExceptionHandler {
    pub start_offset: u32,
    pub end_offset: u32,
    pub handler_offset: u32,
}

/// Source map entry mapping bytecode offset to source position.
#[repr(C)]
pub struct FFISourceMapEntry {
    pub bytecode_offset: u32,
    pub source_start_line: u32,
    pub source_start_column: u32,
}

/// A borrowed UTF-16 string slice for passing across FFI.
/// Points into Rust-owned memory; valid only for the duration of the FFI call.
#[repr(C)]
pub struct FFIUtf16Slice {
    pub data: *const u16,
    pub length: usize,
}

impl From<&[u16]> for FFIUtf16Slice {
    fn from(slice: &[u16]) -> Self {
        Self {
            data: slice.as_ptr(),
            length: slice.len(),
        }
    }
}

impl From<&Utf16String> for FFIUtf16Slice {
    fn from(s: &Utf16String) -> Self {
        Self {
            data: s.as_ptr(),
            length: s.len(),
        }
    }
}

/// C-compatible `Optional<u32>` (C++ doesn't have a standard Optional ABI).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FFIOptionalU32 {
    pub value: u32,
    pub has_value: bool,
}

impl FFIOptionalU32 {
    pub fn none() -> Self {
        Self {
            value: 0,
            has_value: false,
        }
    }

    pub fn some(value: u32) -> Self {
        Self { value, has_value: true }
    }
}

impl From<Option<u32>> for FFIOptionalU32 {
    fn from(opt: Option<u32>) -> Self {
        match opt {
            Some(value) => Self::some(value),
            None => Self::none(),
        }
    }
}

/// Literal value kind for class field initializers
#[repr(u8)]
pub enum LiteralValueKind {
    None = 0,
    Number = 1,
    BooleanTrue = 2,
    BooleanFalse = 3,
    Null = 4,
    String = 5,
}

fn literal_value_kind_to_ffi(kind: PendingLiteralValueKind) -> LiteralValueKind {
    match kind {
        PendingLiteralValueKind::None => LiteralValueKind::None,
        PendingLiteralValueKind::Number => LiteralValueKind::Number,
        PendingLiteralValueKind::BooleanTrue => LiteralValueKind::BooleanTrue,
        PendingLiteralValueKind::BooleanFalse => LiteralValueKind::BooleanFalse,
        PendingLiteralValueKind::Null => LiteralValueKind::Null,
        PendingLiteralValueKind::String => LiteralValueKind::String,
    }
}

/// Well-known symbol IDs resolved by C++ when materializing an Executable.
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum WellKnownSymbolKind {
    SymbolIterator = 0,
    SymbolAsyncIterator = 1,
}

/// NativeJavaScriptBackedFunction intrinsic IDs resolved by C++ when materializing an Executable.
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum AbstractOperationKind {
    AsyncIteratorClose = 0,
    GetMethod = 1,
    GetIteratorDirect = 2,
    GetIteratorFromMethod = 3,
    IteratorComplete = 4,
}

/// Class element descriptor for ClassBlueprint creation
/// (C++ `BytecodeFactory::ClassElementData`).
#[repr(C)]
pub struct FFIClassElement {
    pub kind: u8, // ClassElementKind
    pub is_static: bool,
    pub is_private: bool,
    pub private_identifier: *const u16,
    pub private_identifier_len: usize,
    pub shared_function_data_index: FFIOptionalU32,
    pub has_initializer: bool,
    pub literal_value_kind: LiteralValueKind,
    pub literal_value_number: f64,
    pub literal_value_string: *const u16,
    pub literal_value_string_len: usize,
}

/// Data for creating a C++ `SharedFunctionInstanceData`.
/// Passed to `rust_create_sfd()`.
#[repr(C)]
pub struct FFISharedFunctionData {
    pub name: *const u16,
    pub name_len: usize,
    pub function_kind: u8,
    pub function_length: i32,
    pub formal_parameter_count: u32,
    pub strict: bool,
    pub is_arrow: bool,
    pub has_simple_parameter_list: bool,
    pub parameter_names: *const FFIUtf16Slice,
    pub parameter_name_count: usize,
    pub source_text_offset: usize,
    pub source_text_length: usize,
    pub rust_function_ast: *mut c_void,
    pub uses_this: bool,
    pub uses_this_from_environment: bool,
}

/// All data needed to create a C++ `Bytecode::Executable`.
/// Passed to `rust_create_executable()`.
#[repr(C)]
pub struct FFIExecutableData {
    pub bytecode: *const u8,
    pub bytecode_length: usize,
    pub bytecode_owner: *mut c_void,
    pub identifier_table: *const FFIUtf16Slice,
    pub identifier_count: usize,
    pub property_key_table: *const FFIUtf16Slice,
    pub property_key_count: usize,
    pub string_table: *const FFIUtf16Slice,
    pub string_count: usize,
    pub constants_data: *const u8,
    pub constants_data_length: usize,
    pub constants_count: usize,
    pub exception_handlers: *const FFIExceptionHandler,
    pub exception_handler_count: usize,
    pub source_map: *const FFISourceMapEntry,
    pub source_map_count: usize,
    pub basic_block_offsets: *const usize,
    pub basic_block_count: usize,
    pub local_variable_names: *const FFIUtf16Slice,
    pub local_variable_count: usize,
    pub property_lookup_cache_count: u32,
    pub global_variable_cache_count: u32,
    pub environment_coordinate_cache_count: u32,
    pub template_object_cache_count: u32,
    pub object_shape_cache_count: u32,
    pub object_property_iterator_cache_count: u32,
    pub number_of_registers: u32,
    pub number_of_arguments: u32,
    pub is_strict: bool,
    pub length_identifier: FFIOptionalU32,
    pub shared_function_data: *const *const c_void,
    pub shared_function_data_count: usize,
    pub class_blueprints: *const *mut c_void,
    pub class_blueprint_count: usize,
    pub compiled_regexes: *const *mut c_void,
    pub regex_count: usize,
}

unsafe extern "C" {
    fn rust_create_executable(
        vm_ptr: *mut c_void,
        source_code_ptr: *const c_void,
        data: *const FFIExecutableData,
    ) -> *mut c_void;

    pub fn rust_create_sfd(
        vm_ptr: *mut c_void,
        source_code_ptr: *const c_void,
        data: *const FFISharedFunctionData,
    ) -> *mut c_void;
    pub fn rust_create_sfd_in_list(
        vm_ptr: *mut c_void,
        source_code_ptr: *const c_void,
        shared_function_data_list_ptr: *mut c_void,
        data: *const FFISharedFunctionData,
    ) -> *mut c_void;

    pub fn rust_sfd_set_class_field_initializer_name(
        sfd_ptr: *mut c_void,
        name: *const u16,
        name_len: usize,
        is_private: bool,
    );

    pub fn rust_sfd_set_precompiled_executable(
        sfd_ptr: *mut c_void,
        executable_ptr: *mut c_void,
        uses_this: bool,
        this_value_needs_environment_resolution: bool,
        function_environment_needed: bool,
        function_environment_bindings_count: usize,
        var_environment_bindings_count: usize,
        might_need_arguments_object: bool,
        contains_direct_call_to_eval: bool,
    );

    pub fn rust_sfd_set_cached_bytecode_executable(
        sfd_ptr: *mut c_void,
        cached_executable_ptr: *mut c_void,
        uses_this: bool,
        this_value_needs_environment_resolution: bool,
        function_environment_needed: bool,
        function_environment_bindings_count: usize,
        var_environment_bindings_count: usize,
        might_need_arguments_object: bool,
        contains_direct_call_to_eval: bool,
    );

    pub fn rust_sfd_set_precompiled_bytecode_executable(
        sfd_ptr: *mut c_void,
        precompiled_executable_ptr: *mut c_void,
        uses_this: bool,
        this_value_needs_environment_resolution: bool,
        function_environment_needed: bool,
        function_environment_bindings_count: usize,
        var_environment_bindings_count: usize,
        might_need_arguments_object: bool,
        contains_direct_call_to_eval: bool,
    );

    pub fn rust_executable_shared_function_data_count(executable_ptr: *const c_void) -> usize;
    pub fn rust_executable_shared_function_data_at(executable_ptr: *const c_void, index: usize) -> *mut c_void;
    pub fn rust_sfd_executable(sfd_ptr: *const c_void) -> *mut c_void;
    pub fn rust_sfd_matches_bytecode_cache_function(sfd_ptr: *const c_void, data: *const FFISharedFunctionData)
    -> bool;
    pub fn rust_sfd_install_bytecode_cache_executable(
        sfd_ptr: *mut c_void,
        executable_ptr: *mut c_void,
        uses_this: bool,
        this_value_needs_environment_resolution: bool,
        function_environment_needed: bool,
        function_environment_bindings_count: usize,
        var_environment_bindings_count: usize,
        might_need_arguments_object: bool,
        contains_direct_call_to_eval: bool,
    );
    pub fn rust_sfd_install_cached_bytecode_executable(
        sfd_ptr: *mut c_void,
        cached_executable_ptr: *mut c_void,
        uses_this: bool,
        this_value_needs_environment_resolution: bool,
        function_environment_needed: bool,
        function_environment_bindings_count: usize,
        var_environment_bindings_count: usize,
        might_need_arguments_object: bool,
        contains_direct_call_to_eval: bool,
    );

    pub fn rust_create_class_blueprint(
        vm_ptr: *mut c_void,
        source_code_ptr: *const c_void,
        name: *const u16,
        name_len: usize,
        source_text_offset: usize,
        source_text_len: usize,
        constructor_sfd_index: u32,
        has_super_class: bool,
        has_name: bool,
        elements: *const FFIClassElement,
        element_count: usize,
    ) -> *mut c_void;

    // Callbacks for populating Script GDI data from Rust.
    pub fn script_gdi_push_lexical_name(ctx: *mut c_void, name: *const u16, len: usize);
    pub fn script_gdi_push_var_name(ctx: *mut c_void, name: *const u16, len: usize);
    pub fn script_gdi_push_function(ctx: *mut c_void, sfd: *mut c_void, name: *const u16, len: usize);
    pub fn script_gdi_push_var_scoped_name(ctx: *mut c_void, name: *const u16, len: usize);
    pub fn script_gdi_push_annex_b_name(ctx: *mut c_void, name: *const u16, len: usize);
    pub fn script_gdi_push_lexical_binding(ctx: *mut c_void, name: *const u16, len: usize, is_constant: bool);

    // Callbacks for populating eval EDI data from Rust.
    pub fn eval_gdi_set_strict(ctx: *mut c_void, is_strict: bool);
    pub fn eval_gdi_push_var_name(ctx: *mut c_void, name: *const u16, len: usize);
    pub fn eval_gdi_push_function(ctx: *mut c_void, sfd: *mut c_void, name: *const u16, len: usize);
    pub fn eval_gdi_push_var_scoped_name(ctx: *mut c_void, name: *const u16, len: usize);
    pub fn eval_gdi_push_annex_b_name(ctx: *mut c_void, name: *const u16, len: usize);
    pub fn eval_gdi_push_lexical_binding(ctx: *mut c_void, name: *const u16, len: usize, is_constant: bool);
    pub fn eval_gdi_push_private_name(ctx: *mut c_void, name: *const u16, len: usize);

    pub fn rust_compile_regex(
        pattern_data: *const u16,
        pattern_len: usize,
        flags_data: *const u16,
        flags_len: usize,
        error_out: *mut *const std::os::raw::c_char,
    ) -> *mut c_void;

    pub fn rust_free_error_string(str: *const std::os::raw::c_char);

    pub fn rust_number_to_utf16(value: f64, buffer: *mut u16, buffer_len: usize) -> usize;

}

/// Create a SharedFunctionInstanceData from a FunctionData.
///
/// Computes has_simple_parameter_list, builds parameter name slices,
/// transfers ownership of the Box to C++ via `Box::into_raw`, and
/// calls `rust_create_sfd`.
///
/// Used by both `emit_new_function` in codegen.rs (for function
/// expressions/declarations) and `create_sfd_for_gdi` below (for
/// top-level GDI function initialization).
///
/// # Safety
/// `vm_ptr` and `source_code_ptr` must be valid pointers.
#[allow(clippy::boxed_local)] // Callers produce Box<FunctionData>; unboxing would copy a large struct.
pub unsafe fn create_shared_function_data(
    function_data: Box<crate::ast::FunctionData>,
    subtable: crate::ast::FunctionTable,
    context: SharedFunctionDataCreationContext,
    is_strict: bool,
    name_override: Option<&[u16]>,
    arena: std::sync::Arc<crate::ast::AstArena>,
) -> *mut c_void {
    unsafe {
        use crate::ast::FunctionParameterBinding;

        let source_start = function_data.source_text_start as usize;
        let source_end = function_data.source_text_end as usize;
        let source_text_len = source_end - source_start;

        let (name_ptr, name_len) = if let Some(name) = name_override {
            (name.as_ptr(), name.len())
        } else if let Some(name_ident) = function_data.name {
            let name = arena.name_of(name_ident);
            (name.as_ptr(), name.len())
        } else {
            (std::ptr::null(), 0)
        };

        let has_simple_parameter_list = function_data.parameters.iter().all(|p| {
            !p.is_rest && p.default_value.is_none() && matches!(p.binding, FunctionParameterBinding::Identifier(_))
        });

        let parameter_name_slices: Vec<FFIUtf16Slice> = if has_simple_parameter_list {
            function_data
                .parameters
                .iter()
                .map(|p| {
                    if let FunctionParameterBinding::Identifier(id) = p.binding {
                        FFIUtf16Slice::from(arena.name_slice(id))
                    } else {
                        unreachable!("has_simple_parameter_list guarantees all bindings are identifiers")
                    }
                })
                .collect()
        } else {
            Vec::new()
        };

        let function_kind = function_data.kind as u8;
        let strict = function_data.is_strict_mode || is_strict;
        let function_length = function_data.function_length;
        let formal_parameter_count = u32_from_usize(function_data.parameters.len());
        let is_arrow = function_data.is_arrow_function;
        let uses_this = function_data.parsing_insights.uses_this;
        let uses_this_from_environment = function_data.parsing_insights.uses_this_from_environment;

        let payload = Box::new(crate::ast::FunctionPayload {
            data: *function_data,
            function_table: subtable,
            arena,
        });
        let rust_ast_ptr = Box::into_raw(payload) as *mut c_void;

        let ffi_data = FFISharedFunctionData {
            name: name_ptr,
            name_len,
            function_kind,
            function_length,
            formal_parameter_count,
            strict,
            is_arrow,
            has_simple_parameter_list,
            parameter_names: parameter_name_slices.as_ptr(),
            parameter_name_count: parameter_name_slices.len(),
            source_text_offset: source_start,
            source_text_length: source_text_len,
            rust_function_ast: rust_ast_ptr,
            uses_this,
            uses_this_from_environment,
        };

        let sfd_ptr = match context.owner {
            SharedFunctionDataOwner::None => {
                rust_create_sfd(context.vm_ptr, context.source_code_ptr, &raw const ffi_data)
            }
            SharedFunctionDataOwner::List(list_ptr) => {
                assert!(!list_ptr.is_null(), "SharedFunctionDataOwner::List must not be null");
                rust_create_sfd_in_list(context.vm_ptr, context.source_code_ptr, list_ptr, &raw const ffi_data)
            }
        };

        assert!(
            !sfd_ptr.is_null(),
            "create_shared_function_data: rust_create_sfd returned null"
        );
        sfd_ptr
    }
}

/// Create a SharedFunctionInstanceData for GDI use (no name override).
///
/// # Safety
/// `vm_ptr` and `source_code_ptr` must be valid pointers.
pub unsafe fn create_sfd_for_gdi(
    function_data: Box<crate::ast::FunctionData>,
    subtable: crate::ast::FunctionTable,
    context: SharedFunctionDataCreationContext,
    is_strict: bool,
    arena: std::sync::Arc<crate::ast::AstArena>,
) -> *mut c_void {
    unsafe { create_shared_function_data(function_data, subtable, context, is_strict, None, arena) }
}

unsafe fn materialize_shared_function_data(
    generator: &mut Generator,
    context: SharedFunctionDataCreationContext,
) -> Vec<*const c_void> {
    unsafe {
        let mut sfd_ptrs = Vec::with_capacity(generator.shared_function_data.len());
        for pending in &mut generator.shared_function_data {
            let function_data = pending
                .function_data
                .take()
                .expect("pending shared function data was already materialized");
            let subtable = pending
                .subtable
                .take()
                .expect("pending shared function data subtable was already materialized");
            let arena = pending.arena.clone().unwrap_or_else(|| generator.arena.clone());
            let sfd_ptr = create_shared_function_data(
                function_data,
                subtable,
                context,
                generator.strict,
                pending.name_override.as_ref().map(|name| name.as_slice()),
                arena,
            );
            if let Some((name, is_private)) = &pending.class_field_initializer_name {
                rust_sfd_set_class_field_initializer_name(sfd_ptr, name.as_ptr(), name.len(), *is_private);
            }
            if let Some(precompiled) = pending.precompiled_function.take() {
                let uses_this = precompiled.metadata.uses_this;
                let this_value_needs_environment_resolution =
                    precompiled.metadata.this_value_needs_environment_resolution;
                let function_environment_needed = precompiled.metadata.function_environment_needed;
                let function_environment_bindings_count = precompiled.metadata.function_environment_bindings_count;
                let var_environment_bindings_count = precompiled.metadata.var_environment_bindings_count;
                let might_need_arguments = precompiled.metadata.might_need_arguments;
                let contains_eval = precompiled.metadata.contains_eval;
                let precompiled_ptr = Box::into_raw(precompiled) as *mut c_void;
                rust_sfd_set_precompiled_bytecode_executable(
                    sfd_ptr,
                    precompiled_ptr,
                    uses_this,
                    this_value_needs_environment_resolution,
                    function_environment_needed,
                    function_environment_bindings_count,
                    var_environment_bindings_count,
                    might_need_arguments,
                    contains_eval,
                );
            }
            sfd_ptrs.push(sfd_ptr as *const c_void);
        }
        sfd_ptrs
    }
}

unsafe fn materialize_class_blueprints(
    generator: &mut Generator,
    vm_ptr: *mut c_void,
    source_code_ptr: *const c_void,
) -> Vec<*mut c_void> {
    unsafe {
        generator
            .class_blueprints
            .iter()
            .map(|blueprint| materialize_class_blueprint(blueprint, vm_ptr, source_code_ptr))
            .collect()
    }
}

/// Create a C++ ClassBlueprint from a pending blueprint record.
///
/// # Safety
/// `vm_ptr` and `source_code_ptr` must be valid pointers.
pub unsafe fn materialize_class_blueprint(
    blueprint: &PendingClassBlueprint,
    vm_ptr: *mut c_void,
    source_code_ptr: *const c_void,
) -> *mut c_void {
    unsafe {
        let ffi_elements: Vec<FFIClassElement> = blueprint.elements.iter().map(class_element_to_ffi).collect();
        let (name, name_len) = blueprint
            .name
            .as_ref()
            .map(|name| (name.as_ptr(), name.len()))
            .unwrap_or((std::ptr::null(), 0));
        let bp_ptr = rust_create_class_blueprint(
            vm_ptr,
            source_code_ptr,
            name,
            name_len,
            blueprint.source_text_offset,
            blueprint.source_text_length,
            blueprint.constructor_sfd_index,
            blueprint.has_super_class,
            blueprint.has_name,
            ffi_elements.as_ptr(),
            ffi_elements.len(),
        );
        assert!(!bp_ptr.is_null(), "rust_create_class_blueprint returned null");
        bp_ptr
    }
}

fn class_element_to_ffi(element: &PendingClassElement) -> FFIClassElement {
    let (private_identifier, private_identifier_len) = element
        .private_identifier
        .as_ref()
        .map(|name| (name.as_ptr(), name.len()))
        .unwrap_or((std::ptr::null(), 0));
    let (literal_value_string, literal_value_string_len) = element
        .literal_value_string
        .as_ref()
        .map(|string| (string.as_ptr(), string.len()))
        .unwrap_or((std::ptr::null(), 0));
    FFIClassElement {
        kind: element.kind,
        is_static: element.is_static,
        is_private: element.is_private,
        private_identifier,
        private_identifier_len,
        shared_function_data_index: element
            .shared_function_data_index
            .map(FFIOptionalU32::some)
            .unwrap_or_else(FFIOptionalU32::none),
        has_initializer: element.has_initializer,
        literal_value_kind: literal_value_kind_to_ffi(element.literal_value_kind),
        literal_value_number: element.literal_value_number,
        literal_value_string,
        literal_value_string_len,
    }
}

/// Constant tags for the FFI constant buffer (ABI-compatible).
#[repr(u8)]
pub enum ConstantTag {
    Number = 0,
    BooleanTrue = 1,
    BooleanFalse = 2,
    Null = 3,
    Undefined = 4,
    Empty = 5,
    String = 6,
    BigInt = 7,
    WellKnownSymbol = 8,
    AbstractOperation = 9,
}

/// Encode constants into a tagged byte buffer for FFI.
pub(crate) fn encode_constants(constants: &[ConstantValue]) -> Vec<u8> {
    let mut buffer = Vec::new();
    for c in constants {
        match c {
            ConstantValue::Number(v) => {
                buffer.push(ConstantTag::Number as u8);
                buffer.extend_from_slice(&v.to_le_bytes());
            }
            ConstantValue::Boolean(true) => buffer.push(ConstantTag::BooleanTrue as u8),
            ConstantValue::Boolean(false) => buffer.push(ConstantTag::BooleanFalse as u8),
            ConstantValue::Null => buffer.push(ConstantTag::Null as u8),
            ConstantValue::Undefined => buffer.push(ConstantTag::Undefined as u8),
            ConstantValue::Empty => buffer.push(ConstantTag::Empty as u8),
            ConstantValue::String(s) => {
                buffer.push(ConstantTag::String as u8);
                align_buffer_to(&mut buffer, align_of::<u16>());
                let len = u32_from_usize(s.len());
                buffer.extend_from_slice(&len.to_le_bytes());
                for &code_unit in s {
                    buffer.extend_from_slice(&code_unit.to_le_bytes());
                }
            }
            ConstantValue::BigInt(s) => {
                buffer.push(ConstantTag::BigInt as u8);
                let len = u32_from_usize(s.len());
                buffer.extend_from_slice(&len.to_le_bytes());
                buffer.extend_from_slice(s.as_bytes());
            }
            ConstantValue::WellKnownSymbol(symbol) => {
                buffer.push(ConstantTag::WellKnownSymbol as u8);
                buffer.push(*symbol as u8);
            }
            ConstantValue::AbstractOperation(name) => {
                buffer.push(ConstantTag::AbstractOperation as u8);
                buffer.push(*name as u8);
            }
        }
    }
    buffer
}

fn align_buffer_to(buffer: &mut Vec<u8>, alignment: usize) {
    let padding = buffer.len().next_multiple_of(alignment) - buffer.len();
    buffer.extend(std::iter::repeat_n(0, padding));
}

/// Create a C++ Executable from the generator's assembled output.
///
/// # Safety
/// `vm_ptr` must be a valid `JS::VM*` and `source_code_ptr` a valid
/// `JS::SourceCode const*`.
pub unsafe fn create_executable(
    generator: &mut Generator,
    assembled: &AssembledBytecode,
    vm_ptr: *mut c_void,
    source_code_ptr: *const c_void,
    owner: SharedFunctionDataOwner,
) -> ExecutableHandle {
    unsafe {
        let sfd_ptrs = materialize_shared_function_data(
            generator,
            SharedFunctionDataCreationContext {
                vm_ptr,
                source_code_ptr,
                owner,
            },
        );
        let bp_ptrs = materialize_class_blueprints(generator, vm_ptr, source_code_ptr);
        create_executable_with_dependencies(generator, assembled, vm_ptr, source_code_ptr, &sfd_ptrs, &bp_ptrs)
    }
}

/// Create a C++ Executable from already materialized dependency objects.
///
/// This is used by bytecode cache materialization, where the cache blob
/// contains precompiled nested functions and class blueprints instead of
/// AST-backed `PendingSharedFunctionData` records.
///
/// # Safety
/// `vm_ptr`, `source_code_ptr`, and all dependency pointers must be valid.
pub unsafe fn create_executable_with_dependencies(
    generator: &Generator,
    assembled: &AssembledBytecode,
    vm_ptr: *mut c_void,
    source_code_ptr: *const c_void,
    sfd_ptrs: &[*const c_void],
    bp_ptrs: &[*mut c_void],
) -> ExecutableHandle {
    unsafe {
        let parts = ExecutableParts {
            bytecode: &assembled.bytecode,
            bytecode_owner: std::ptr::null_mut(),
            exception_handlers: &assembled.exception_handlers,
            source_map: &assembled.source_map,
            basic_block_start_offsets: &assembled.basic_block_start_offsets,
            number_of_registers: assembled.number_of_registers,
            number_of_arguments: assembled.number_of_arguments,
        };
        create_executable_with_dependencies_from_parts(generator, parts, vm_ptr, source_code_ptr, sfd_ptrs, bp_ptrs)
    }
}

pub struct ExecutableParts<'a> {
    pub bytecode: &'a [u8],
    pub bytecode_owner: *mut c_void,
    pub exception_handlers: &'a [ExceptionHandler],
    pub source_map: &'a [SourceMapEntry],
    pub basic_block_start_offsets: &'a [usize],
    pub number_of_registers: u32,
    pub number_of_arguments: u32,
}

pub struct ExecutableMetadata {
    pub property_lookup_cache_count: u32,
    pub global_variable_cache_count: u32,
    pub environment_coordinate_cache_count: u32,
    pub template_object_cache_count: u32,
    pub object_shape_cache_count: u32,
    pub object_property_iterator_cache_count: u32,
    pub is_strict: bool,
    pub length_identifier: Option<u32>,
}

pub struct ExecutableSlices<'a> {
    pub identifier_table: &'a [FFIUtf16Slice],
    pub property_key_table: &'a [FFIUtf16Slice],
    pub string_table: &'a [FFIUtf16Slice],
    pub constants_data: &'a [u8],
    pub constants_count: usize,
    pub local_variable_names: &'a [FFIUtf16Slice],
    pub compiled_regexes: &'a [*mut c_void],
}

/// Create a C++ Executable from borrowed FFI slices.
///
/// This is the lowest-level executable constructor wrapper. It lets cache
/// materialization pass table slices borrowed from mmap-backed cache bytes
/// without first copying them into the bytecode generator's owned tables.
///
/// # Safety
/// `vm_ptr`, `source_code_ptr`, all dependency pointers, and all borrowed
/// slices must be valid for the duration of the call.
pub unsafe fn create_executable_from_slices(
    parts: ExecutableParts<'_>,
    metadata: ExecutableMetadata,
    slices: ExecutableSlices<'_>,
    vm_ptr: *mut c_void,
    source_code_ptr: *const c_void,
    sfd_ptrs: &[*const c_void],
    bp_ptrs: &[*mut c_void],
) -> ExecutableHandle {
    unsafe {
        // Build FFI exception handlers
        let ffi_handlers: Vec<FFIExceptionHandler> = parts
            .exception_handlers
            .iter()
            .map(|h| FFIExceptionHandler {
                start_offset: h.start_offset,
                end_offset: h.end_offset,
                handler_offset: h.handler_offset,
            })
            .collect();

        // Build FFI source map
        let ffi_source_map: Vec<FFISourceMapEntry> = parts
            .source_map
            .iter()
            .map(|e| FFISourceMapEntry {
                bytecode_offset: e.bytecode_offset,
                source_start_line: e.line,
                source_start_column: e.column,
            })
            .collect();

        let ffi_data = FFIExecutableData {
            bytecode: parts.bytecode.as_ptr(),
            bytecode_length: parts.bytecode.len(),
            bytecode_owner: parts.bytecode_owner,
            identifier_table: slices.identifier_table.as_ptr(),
            identifier_count: slices.identifier_table.len(),
            property_key_table: slices.property_key_table.as_ptr(),
            property_key_count: slices.property_key_table.len(),
            string_table: slices.string_table.as_ptr(),
            string_count: slices.string_table.len(),
            constants_data: slices.constants_data.as_ptr(),
            constants_data_length: slices.constants_data.len(),
            constants_count: slices.constants_count,
            exception_handlers: ffi_handlers.as_ptr(),
            exception_handler_count: ffi_handlers.len(),
            source_map: ffi_source_map.as_ptr(),
            source_map_count: ffi_source_map.len(),
            basic_block_offsets: parts.basic_block_start_offsets.as_ptr(),
            basic_block_count: parts.basic_block_start_offsets.len(),
            local_variable_names: slices.local_variable_names.as_ptr(),
            local_variable_count: slices.local_variable_names.len(),
            property_lookup_cache_count: metadata.property_lookup_cache_count,
            global_variable_cache_count: metadata.global_variable_cache_count,
            environment_coordinate_cache_count: metadata.environment_coordinate_cache_count,
            template_object_cache_count: metadata.template_object_cache_count,
            object_shape_cache_count: metadata.object_shape_cache_count,
            object_property_iterator_cache_count: metadata.object_property_iterator_cache_count,
            number_of_registers: parts.number_of_registers,
            number_of_arguments: parts.number_of_arguments,
            is_strict: metadata.is_strict,
            length_identifier: FFIOptionalU32::from(metadata.length_identifier),
            shared_function_data: sfd_ptrs.as_ptr(),
            shared_function_data_count: sfd_ptrs.len(),
            class_blueprints: bp_ptrs.as_ptr(),
            class_blueprint_count: bp_ptrs.len(),
            compiled_regexes: slices.compiled_regexes.as_ptr(),
            regex_count: slices.compiled_regexes.len(),
        };

        rust_create_executable(vm_ptr, source_code_ptr, &raw const ffi_data)
    }
}

/// Create a C++ Executable from already materialized dependency objects and
/// borrowed bytecode/table slices.
///
/// This variant lets bytecode cache materialization point at mmap-backed cache
/// blob bytes without first cloning executable bytecode into a Rust Vec.
///
/// # Safety
/// `vm_ptr`, `source_code_ptr`, all dependency pointers, and all borrowed
/// slices must be valid for the duration of the call.
pub unsafe fn create_executable_with_dependencies_from_parts(
    generator: &Generator,
    parts: ExecutableParts<'_>,
    vm_ptr: *mut c_void,
    source_code_ptr: *const c_void,
    sfd_ptrs: &[*const c_void],
    bp_ptrs: &[*mut c_void],
) -> ExecutableHandle {
    unsafe {
        // Build FFI slices for tables
        let ident_slices: Vec<FFIUtf16Slice> = generator
            .identifier_table
            .iter()
            .map(|s| FFIUtf16Slice::from(s.as_ref()))
            .collect();

        let property_key_slices: Vec<FFIUtf16Slice> = generator
            .property_key_table
            .iter()
            .map(|s| FFIUtf16Slice::from(s.as_ref()))
            .collect();

        let string_slices: Vec<FFIUtf16Slice> = generator
            .string_table
            .iter()
            .map(|s| FFIUtf16Slice::from(s.as_ref()))
            .collect();

        // Encode constants
        let constants_buffer = encode_constants(&generator.constants);

        // Build local variable name slices
        let local_var_slices: Vec<FFIUtf16Slice> = generator
            .local_variables
            .iter()
            .map(|v| FFIUtf16Slice::from(v.name.as_ref()))
            .collect();

        let metadata = ExecutableMetadata {
            property_lookup_cache_count: generator.next_property_lookup_cache,
            global_variable_cache_count: generator.next_global_variable_cache,
            environment_coordinate_cache_count: generator.next_environment_coordinate_cache,
            template_object_cache_count: generator.next_template_object_cache,
            object_shape_cache_count: generator.next_object_shape_cache,
            object_property_iterator_cache_count: generator.next_object_property_iterator_cache,
            is_strict: generator.strict,
            length_identifier: generator.length_identifier.map(|index| index.0),
        };

        let slices = ExecutableSlices {
            identifier_table: &ident_slices,
            property_key_table: &property_key_slices,
            string_table: &string_slices,
            constants_data: &constants_buffer,
            constants_count: generator.constants.len(),
            local_variable_names: &local_var_slices,
            compiled_regexes: &generator.compiled_regexes,
        };

        create_executable_from_slices(parts, metadata, slices, vm_ptr, source_code_ptr, sfd_ptrs, bp_ptrs)
    }
}

/// Convert a JS number to its UTF-16 string representation using the
/// ECMA-262 Number::toString algorithm (via C++ runtime).
pub fn js_number_to_utf16(value: f64) -> Utf16String {
    let mut buffer = [0u16; 64];
    let len = unsafe { rust_number_to_utf16(value, buffer.as_mut_ptr(), buffer.len()) };
    Utf16String(buffer[..len].to_vec())
}

/// Compile a regex pattern+flags using the C++ regex engine.
///
/// On success, returns an opaque handle to the compiled regex (a C++
/// RustCompiledRegex*). On failure, returns the error message.
pub fn compile_regex(pattern: &[u16], flags: &[u16]) -> Result<*mut c_void, String> {
    unsafe {
        let mut error: *const std::os::raw::c_char = std::ptr::null();
        let handle = rust_compile_regex(
            pattern.as_ptr(),
            pattern.len(),
            flags.as_ptr(),
            flags.len(),
            &raw mut error,
        );
        if error.is_null() {
            Ok(handle)
        } else {
            let msg = std::ffi::CStr::from_ptr(error).to_string_lossy().into_owned();
            rust_free_error_string(error);
            Err(msg)
        }
    }
}
