inkwell 0.9.0

Inkwell aims to help you pen your own programming languages by safely wrapping llvm-sys.
Documentation
use llvm_sys::core::{
    LLVMGetMDNodeNumOperands, LLVMGetMDNodeOperands, LLVMGetMDString, LLVMIsAMDNode, LLVMIsAMDString,
};
use llvm_sys::prelude::LLVMValueRef;

use llvm_sys::core::LLVMValueAsMetadata;
use llvm_sys::prelude::LLVMMetadataRef;

use crate::values::traits::AsValueRef;
use crate::values::{BasicMetadataValueEnum, Value};

use super::AnyValue;

use std::ffi::CStr;
use std::fmt::{self, Display};

/// Value returned by [`Context::get_kind_id()`](crate::context::Context::get_kind_id)
/// for the first input string that isn't known.
///
/// Each LLVM version has a different set of pre-defined metadata kinds.
pub const FIRST_CUSTOM_METADATA_KIND_ID: u32 = if cfg!(feature = "llvm11-0") {
    30
} else if cfg!(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0",)) {
    31
} else if cfg!(feature = "llvm15-0") {
    36
} else if cfg!(any(feature = "llvm16-0", feature = "llvm17-0")) {
    39
} else if cfg!(feature = "llvm18-1") {
    40
} else if cfg!(feature = "llvm19-1") {
    41
} else if cfg!(any(feature = "llvm20-1", feature = "llvm21-1")) {
    42
} else if cfg!(feature = "llvm22-1") {
    47
} else {
    panic!("Unhandled LLVM version")
};

#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct MetadataValue<'ctx> {
    metadata_value: Value<'ctx>,
}

impl<'ctx> MetadataValue<'ctx> {
    /// Get a value from an [LLVMValueRef].
    ///
    /// # Safety
    ///
    /// The ref must be valid and of type metadata.
    pub unsafe fn new(value: LLVMValueRef) -> Self {
        unsafe {
            assert!(!value.is_null());
            assert!(!LLVMIsAMDNode(value).is_null() || !LLVMIsAMDString(value).is_null());

            MetadataValue {
                metadata_value: Value::new(value),
            }
        }
    }

    pub(crate) fn as_metadata_ref(self) -> LLVMMetadataRef {
        unsafe { LLVMValueAsMetadata(self.as_value_ref()) }
    }

    /// Get name of the `MetadataValue`.
    pub fn get_name(&self) -> &CStr {
        self.metadata_value.get_name()
    }

    // SubTypes: This can probably go away with subtypes
    pub fn is_node(self) -> bool {
        unsafe { LLVMIsAMDNode(self.as_value_ref()) == self.as_value_ref() }
    }

    // SubTypes: This can probably go away with subtypes
    pub fn is_string(self) -> bool {
        unsafe { LLVMIsAMDString(self.as_value_ref()) == self.as_value_ref() }
    }

    pub fn get_string_value(&self) -> Option<&[u8]> {
        let mut len = 0;
        let ptr = unsafe { LLVMGetMDString(self.as_value_ref(), &mut len) };
        if ptr.is_null() {
            return None;
        }

        // Since LLVM 22, the verifier does not allow including the '\0' in the created MDString values.
        unsafe { Some(core::slice::from_raw_parts(ptr as *const u8, len as usize)) }
    }

    // SubTypes: Node only one day
    pub fn get_node_size(self) -> Option<usize> {
        if self.is_string() {
            return None;
        }

        let size = unsafe { LLVMGetMDNodeNumOperands(self.as_value_ref()) } as usize;
        Some(size)
    }

    // SubTypes: Node only one day
    // REVIEW: BasicMetadataValueEnum only if you can put metadata in metadata...
    pub fn get_node_values(self) -> Option<Vec<BasicMetadataValueEnum<'ctx>>> {
        let count = self.get_node_size()?;
        let mut vec: Vec<LLVMValueRef> = Vec::with_capacity(count);
        let ptr = vec.as_mut_ptr();

        unsafe {
            LLVMGetMDNodeOperands(self.as_value_ref(), ptr);
            vec.set_len(count);
        };

        let values = vec
            .iter()
            .map(|val| unsafe { BasicMetadataValueEnum::new(*val) })
            .collect();
        Some(values)
    }

    pub fn print_to_stderr(self) {
        self.metadata_value.print_to_stderr()
    }

    pub fn replace_all_uses_with(self, other: &MetadataValue<'ctx>) {
        self.metadata_value.replace_all_uses_with(other.as_value_ref())
    }
}

unsafe impl AsValueRef for MetadataValue<'_> {
    fn as_value_ref(&self) -> LLVMValueRef {
        self.metadata_value.value
    }
}

impl Display for MetadataValue<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.print_to_string())
    }
}

impl fmt::Debug for MetadataValue<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut d = f.debug_struct("MetadataValue");
        d.field("address", &self.as_value_ref());

        if self.is_string() {
            d.field("value", &self.get_string_value().unwrap());
        } else {
            d.field("values", &self.get_node_values());
        }

        d.field("repr", &self.print_to_string());

        d.finish()
    }
}