1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::module::Module;

use llvm_sys::core::*;
use llvm_sys::LLVMContext;

use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::ptr::NonNull;

pub struct Context {
  pub(crate) llvm_ptr: NonNull<LLVMContext>,
  is_owned: bool,
}

impl Context {
  pub fn new() -> Self {
    Self {
      llvm_ptr: unsafe {
        NonNull::new(LLVMContextCreate()).expect("Failed to create an LLVM context")
      },
      is_owned: true,
    }
  }

  pub fn global() -> Self {
    Self {
      llvm_ptr: unsafe {
        NonNull::new(LLVMGetGlobalContext()).expect("Failed to get the global LLVM context")
      },
      is_owned: false,
    }
  }

  pub fn create_module(&self, name: &str) -> Module {
    Module::with_context(name, self)
  }

  pub fn is_owned(&self) -> bool {
    self.is_owned
  }

  pub fn should_discard_value_names(&self) -> bool {
    unsafe { LLVMContextShouldDiscardValueNames(self.llvm_ptr.as_ptr()) != 0 }
  }

  pub fn set_discard_value_names(&mut self, discard_value_names: bool) {
    unsafe { LLVMContextSetDiscardValueNames(self.llvm_ptr.as_mut(), discard_value_names as i32) }
  }
}

impl Debug for Context {
  fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
    formatter
      .debug_struct("Context")
      .field("is_owned", &self.is_owned())
      .field("discard_value_names", &self.should_discard_value_names())
      .finish()
  }
}

impl Drop for Context {
  fn drop(&mut self) {
    if self.is_owned {
      unsafe { LLVMContextDispose(self.llvm_ptr.as_mut()) }
    }
  }
}

// It isn't unused, but rustc still complains...
#[allow(unused_imports)]
mod tests {
  use crate::context::Context;

  #[test]
  fn create_an_owned_context() {
    let context = Context::new();

    assert_eq!(context.is_owned(), true);
  }

  #[test]
  fn obtain_a_global_context() {
    let context = Context::global();

    assert_eq!(context.is_owned(), false);
  }

  #[test]
  fn discard_value_names() {
    let mut context = Context::new();

    assert_eq!(context.should_discard_value_names(), false);

    context.set_discard_value_names(true);

    assert_eq!(context.should_discard_value_names(), true);
  }

  #[test]
  fn context_volatility() {
    // Create an owned context and obtain the global one
    let mut owned_context = Context::new();
    let mut global_context = Context::global();

    // Check their values beforehand
    assert_eq!(owned_context.should_discard_value_names(), false);
    assert_eq!(global_context.should_discard_value_names(), false);

    // Modify both contexts
    owned_context.set_discard_value_names(true);
    global_context.set_discard_value_names(true);

    // Check their values afterwards
    assert_eq!(owned_context.should_discard_value_names(), true);
    assert_eq!(global_context.should_discard_value_names(), true);

    // Now drop the global context and obtain it again
    std::mem::drop(global_context);

    let global_context = Context::global();

    // The value should be retained
    assert_eq!(global_context.should_discard_value_names(), true);
  }
}