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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
use crate::errors::{BackendError, TraceError}; use glob; use libloading::Library; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::ffi::OsStr; use std::path::Path; use std::rc::Rc; #[macro_export] macro_rules! export_backend { ($register:expr) => { #[doc(hidden)] #[no_mangle] pub static backend_declaration: $crate::core::BackendDeclaration = $crate::core::BackendDeclaration { rustc_version: $crate::RUSTC_VERSION, core_version: $crate::CORE_VERSION, register: $register, }; }; } #[derive(Debug, Serialize, Deserialize, Copy, PartialEq, Clone)] pub enum Ops { Trace, } pub trait Backend { fn trace(&self, args: BackendArgs) -> Result<(), TraceError>; fn help(&self) -> Option<&str> { None } } pub trait Registrar { fn register_backend(&mut self, name: &str, backend: Box<dyn Backend>); } pub struct BackendDeclaration { pub rustc_version: &'static str, pub core_version: &'static str, pub register: unsafe extern "C" fn(&mut dyn Registrar), } pub struct BackendArgs { pub refresh: bool, } pub struct BackendProxy { backend: Box<dyn Backend>, _lib: Rc<Library>, } impl Backend for BackendProxy { fn trace(&self, args: BackendArgs) -> Result<(), TraceError> { self.backend.trace(args) } fn help(&self) -> Option<&str> { self.backend.help() } } struct BackendRegistrar { backends: HashMap<String, BackendProxy>, lib: Rc<Library>, } impl BackendRegistrar { fn new(lib: Rc<Library>) -> BackendRegistrar { BackendRegistrar { backends: HashMap::default(), lib, } } } impl Registrar for BackendRegistrar { fn register_backend(&mut self, name: &str, backend: Box<dyn Backend>) { let proxy = BackendProxy { backend, _lib: Rc::clone(&self.lib), }; self.backends.insert(name.to_string().to_lowercase(), proxy); } } pub struct ExternalBackends { pub backends: HashMap<String, BackendProxy>, libraries: Vec<Rc<Library>>, } impl ExternalBackends { pub fn new() -> ExternalBackends { ExternalBackends { backends: Default::default(), libraries: Default::default(), } } pub fn from_dir<P>(dir_path: P) -> Result<ExternalBackends, BackendError> where P: AsRef<Path>, { let mut ext_backends = ExternalBackends::new(); for lib_so in glob::glob(&format!("{}/*.so", dir_path.as_ref().display())).unwrap() { unsafe { ext_backends.load(lib_so?)?; } } Ok(ext_backends) } pub unsafe fn load<P: AsRef<OsStr>>(&mut self, lib_path: P) -> Result<(), BackendError> { let library = Rc::new(Library::new(lib_path)?); let decl = library .get::<*mut BackendDeclaration>(b"backend_declaration\0")? .read(); if decl.rustc_version != crate::RUSTC_VERSION || decl.core_version != crate::CORE_VERSION { return Err(BackendError::VersionMismatch); } let mut registrar = BackendRegistrar::new(Rc::clone(&library)); (decl.register)(&mut registrar); self.backends.extend(registrar.backends); self.libraries.push(library); Ok(()) } pub fn trace(&self, backend: &str, args: BackendArgs) -> Result<(), TraceError> { self.backends .get(backend) .ok_or_else(|| BackendError::NotFound { backend_str: backend.to_string(), })? .trace(args) .into() } }