Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bindings for error reporting and C++ support #25

Merged
merged 11 commits into from
Nov 5, 2019
Merged
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "crates/mun_abi/c"]
path = crates/mun_abi/c
url = ../../mun-lang/abi-c
[submodule "crates/mun_runtime_capi/c"]
path = crates/mun_runtime_capi/c
url = ../../mun-lang/runtime-c.git
[submodule "crates/mun_runtime_capi/ffi"]
path = crates/mun_runtime_capi/ffi
url = ../../mun-lang/runtime-ffi.git
4 changes: 2 additions & 2 deletions crates/mun/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::time::Duration;

use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use mun_compiler::PathOrInline;
use mun_runtime::{invoke_fn, MunRuntime, RuntimeBuilder};
use mun_runtime::{invoke_fn, Runtime, RuntimeBuilder};

fn main() -> Result<(), failure::Error> {
let matches = App::new("mun")
Expand Down Expand Up @@ -109,7 +109,7 @@ fn compiler_options(matches: &ArgMatches) -> Result<mun_compiler::CompilerOption
})
}

fn runtime(matches: &ArgMatches) -> Result<MunRuntime, failure::Error> {
fn runtime(matches: &ArgMatches) -> Result<Runtime, failure::Error> {
let mut builder = RuntimeBuilder::new(
matches.value_of("LIBRARY").unwrap(), // Safe because its a required arg
);
Expand Down
7 changes: 7 additions & 0 deletions crates/mun_abi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ fn main() {
.header("c/include/mun_abi.h")
.whitelist_type("Mun.*")
.blacklist_type("MunPrivacy.*")
// Remove type aliasing on Linux
.blacklist_type("__uint8_t")
.blacklist_type("__uint16_t")
.blacklist_type("__uint32_t")
.parse_callbacks(Box::new(RemoveVendorName))
// FIXME: Prevent double derivation of Copy and Debug attributes on Windows
.derive_copy(false)
.derive_debug(false)
.raw_line("#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]")
.raw_line("use crate::Privacy;")
.generate()
Expand Down
2 changes: 1 addition & 1 deletion crates/mun_abi/c
Submodule c updated 1 files
+68 −3 include/mun_abi.h
66 changes: 56 additions & 10 deletions crates/mun_abi/src/autogen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
use crate::Privacy;

#[doc = " <div rustbindgen derive=\"PartialEq\">"]
#[doc = " Represents a globally unique identifier (GUID)."]
#[doc = ""]
#[doc = " GUIDs are generated by taking the MD5 hash of a type's name."]
#[doc = ""]
#[doc = " <div rustbindgen derive=\"Clone\" derive=\"Copy\" derive=\"Debug\" derive=\"PartialEq\"></div>"]
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Guid {
#[doc = " 16-byte MD5 hash"]
pub b: [u8; 16usize],
}
#[test]
Expand All @@ -27,10 +32,17 @@ fn bindgen_test_layout_Guid() {
concat!("Offset of field: ", stringify!(Guid), "::", stringify!(b))
);
}
#[doc = " Represents the type declaration for a value type."]
#[doc = ""]
#[doc = " TODO: add support for structs, polymorphism, enumerations, type parameters, generic type definitions, and constructed generic types."]
#[doc = ""]
#[doc = " <div rustbindgen derive=\"Debug\"></div>"]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug)]
pub struct TypeInfo {
#[doc = " Type GUID"]
pub guid: Guid,
#[doc = " Type name"]
pub name: *const ::std::os::raw::c_char,
}
#[test]
Expand Down Expand Up @@ -66,14 +78,21 @@ fn bindgen_test_layout_TypeInfo() {
)
);
}
#[doc = " <div rustbindgen derive=\"Clone\">"]
#[doc = " Represents a function signature."]
#[doc = ""]
#[doc = " <div rustbindgen derive=\"Clone\" derive=\"Debug\"></div>"]
#[repr(C)]
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct FunctionSignature {
#[doc = " Function name"]
pub name: *const ::std::os::raw::c_char,
#[doc = " Argument types"]
pub arg_types: *const TypeInfo,
#[doc = " Optional return type"]
pub return_type: *const TypeInfo,
#[doc = " Number of argument types"]
pub num_arg_types: u16,
#[doc = " Function accessibility level"]
pub privacy: Privacy,
}
#[test]
Expand Down Expand Up @@ -139,11 +158,17 @@ fn bindgen_test_layout_FunctionSignature() {
)
);
}
#[doc = " <div rustbindgen derive=\"Clone\">"]
#[doc = " Represents a function declaration."]
#[doc = ""]
#[doc = " `fn_ptr` can be used to call the declared function."]
#[doc = ""]
#[doc = " <div rustbindgen derive=\"Clone\" derive=\"Debug\"></div>"]
#[repr(C)]
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct FunctionInfo {
#[doc = " Function signature"]
pub signature: FunctionSignature,
#[doc = " Function pointer"]
pub fn_ptr: *const ::std::os::raw::c_void,
}
#[test]
Expand Down Expand Up @@ -179,11 +204,17 @@ fn bindgen_test_layout_FunctionInfo() {
)
);
}
#[doc = " Represents a module declaration."]
#[doc = ""]
#[doc = " <div rustbindgen derive=\"Debug\"></div>"]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug)]
pub struct ModuleInfo {
#[doc = " Module path"]
pub path: *const ::std::os::raw::c_char,
#[doc = " Module functions"]
pub functions: *const FunctionInfo,
#[doc = " Number of module functions"]
pub num_functions: u32,
}
#[test]
Expand Down Expand Up @@ -229,11 +260,19 @@ fn bindgen_test_layout_ModuleInfo() {
)
);
}
#[doc = " Represents a function dispatch table. This is used for runtime linking."]
#[doc = ""]
#[doc = " Function signatures and pointers are stored separately for cache efficiency."]
#[doc = ""]
#[doc = " <div rustbindgen derive=\"Debug\"></div>"]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug)]
pub struct DispatchTable {
#[doc = " Function signatures"]
pub signatures: *const FunctionSignature,
#[doc = " Function pointers"]
pub fn_ptrs: *mut *const ::std::os::raw::c_void,
#[doc = " Number of functions"]
pub num_entries: u32,
}
#[test]
Expand Down Expand Up @@ -279,12 +318,19 @@ fn bindgen_test_layout_DispatchTable() {
)
);
}
#[doc = " Represents an assembly declaration."]
#[doc = ""]
#[doc = " <div rustbindgen derive=\"Debug\"></div>"]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug)]
pub struct AssemblyInfo {
#[doc = " Symbols of the top-level module"]
pub symbols: ModuleInfo,
#[doc = " Dispatch table"]
pub dispatch_table: DispatchTable,
#[doc = " Paths to assembly dependencies"]
pub dependencies: *const *const ::std::os::raw::c_char,
#[doc = " Number of dependencies"]
pub num_dependencies: u32,
}
#[test]
Expand Down
11 changes: 11 additions & 0 deletions crates/mun_abi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! The Mun ABI
//!
//! The Mun ABI defines the binary format used to communicate between the Mun Compiler and Mun
//! Runtime.
#![warn(missing_docs)]

// Bindings are automatically generated from C on `cargo build`
mod autogen;

Expand All @@ -8,6 +14,9 @@ mod reflection;
pub use autogen::*;
pub use reflection::Reflection;

/// The Mun ABI prelude
///
/// The *prelude* contains imports that are used almost every time.
pub mod prelude {
pub use crate::autogen::*;
pub use crate::reflection::Reflection;
Expand All @@ -18,6 +27,8 @@ pub mod prelude {
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Privacy {
/// Publicly (and privately) accessible
Public = 0,
/// Privately accessible
Private = 1,
}
1 change: 0 additions & 1 deletion crates/mun_runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ authors = ["Wodann <[email protected]>"]
edition = "2018"

[dependencies]
cargo = "0.37"
failure = "0.1.5"
libloading = "0.5"
mun_abi = { path = "../mun_abi" }
Expand Down
22 changes: 22 additions & 0 deletions crates/mun_runtime/examples/hot_reloading.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use mun_runtime::{invoke_fn, RetryResultExt, RuntimeBuilder};
use std::env;

// How to run?
// 1. On the CLI, navigate to the `crates/mun_runtime/examples` directory.
// 2. Run the compiler daemon from the CLI: `/path/to/mun build resources/fibonacci.mun --watch`
// 3. Run the application from the CLI: cargo run --example hot_reloading -- fibonacci.dll
fn main() {
let lib_dir = env::args().nth(1).expect("Expected path to a Mun library.");
println!("lib: {}", lib_dir);

let mut runtime = RuntimeBuilder::new(lib_dir)
.spawn()
.expect("Failed to spawn Runtime");

loop {
let n: i64 = invoke_fn!(runtime, "nth").wait();
let result: i64 = invoke_fn!(runtime, "fibonacci", n).wait();
println!("fibonacci({}) = {}", n, result);
runtime.update();
}
}
11 changes: 11 additions & 0 deletions crates/mun_runtime/examples/resources/fibonacci.mun
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fn nth(): int {
3
}

fn fibonacci(n:int):int {
if n <= 1 {
n
} else {
fibonacci(n-1) + fibonacci(n-2)
}
}
8 changes: 4 additions & 4 deletions crates/mun_runtime/src/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ impl Assembly {
) -> Result<(), Error> {
// let library_path = library_path.canonicalize()?;

// Drop the old library, as some operating systems don't allow editing of in-use shared
// libraries
self.library.take();

for function in self.info.symbols.functions() {
runtime_dispatch_table.remove(function.signature.name());
}

// Drop the old library, as some operating systems don't allow editing of in-use shared
// libraries
self.library.take();

// TODO: Partial hot reload of an assembly
*self = Assembly::load(library_path, runtime_dispatch_table)?;
Ok(())
Expand Down
35 changes: 25 additions & 10 deletions crates/mun_runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! The Mun Runtime
//!
//! The Mun Runtime provides functionality for automatically hot reloading Mun C ABI
//! compliant shared libraries.
#![warn(missing_docs)]

mod assembly;
#[macro_use]
mod macros;
Expand All @@ -17,18 +23,22 @@ use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};

pub use crate::assembly::Assembly;

/// Options for the construction of a [`Runtime`].
#[derive(Clone, Debug)]
pub struct RuntimeOptions {
/// Path to the entry point library
pub library_path: PathBuf,
/// Delay during which filesystem events are collected, deduplicated, and after which emitted.
pub delay: Duration,
}

/// A builder for the runtime.
/// A builder for the [`Runtime`].
pub struct RuntimeBuilder {
options: RuntimeOptions,
}

impl RuntimeBuilder {
/// Constructs a new `RuntimeBuilder` for the shared library at `library_path`.
pub fn new<P: Into<PathBuf>>(library_path: P) -> Self {
Self {
options: RuntimeOptions {
Expand All @@ -38,13 +48,15 @@ impl RuntimeBuilder {
}
}

/// Sets the `delay`.
pub fn set_delay(&mut self, delay: Duration) -> &mut Self {
self.options.delay = delay;
self
}

pub fn spawn(self) -> Result<MunRuntime, Error> {
MunRuntime::new(self.options)
/// Spawns a [`Runtime`] with the builder's options.
pub fn spawn(self) -> Result<Runtime, Error> {
Runtime::new(self.options)
}
}

Expand All @@ -55,6 +67,7 @@ pub struct DispatchTable {
}

impl DispatchTable {
/// Retrieves the [`FunctionInfo`] corresponding to `fn_path`, if it exists.
pub fn get(&self, fn_path: &str) -> Option<&FunctionInfo> {
self.functions.get(fn_path)
}
Expand All @@ -67,28 +80,29 @@ impl DispatchTable {
self.functions.insert(fn_path.to_string(), fn_info)
}

/// Removes and returns the `fn_info` corresponding to `fn_path`, if it exists.
pub fn remove(&mut self, fn_path: &str) -> Option<FunctionInfo> {
self.functions.remove(fn_path)
}
}

/// A runtime for the Mun scripting language.
pub struct MunRuntime {
/// A runtime for the Mun language.
pub struct Runtime {
assemblies: HashMap<PathBuf, Assembly>,
dispatch_table: DispatchTable,
watcher: RecommendedWatcher,
watcher_rx: Receiver<DebouncedEvent>,
}

impl MunRuntime {
/// Constructs a new `MunRuntime` that loads the library at `library_path` and its
/// dependencies. The `MunRuntime` contains a file watcher that is triggered with an interval
impl Runtime {
/// Constructs a new `Runtime` that loads the library at `library_path` and its
/// dependencies. The `Runtime` contains a file watcher that is triggered with an interval
/// of `dur`.
pub fn new(options: RuntimeOptions) -> Result<MunRuntime, Error> {
pub fn new(options: RuntimeOptions) -> Result<Runtime, Error> {
let (tx, rx) = channel();

let watcher: RecommendedWatcher = Watcher::new(tx, options.delay)?;
let mut runtime = MunRuntime {
let mut runtime = Runtime {
assemblies: HashMap::new(),
dispatch_table: DispatchTable::default(),
watcher,
Expand Down Expand Up @@ -153,6 +167,7 @@ impl MunRuntime {

/// Extends a result object with functions that allow retrying of an action.
pub trait RetryResultExt: Sized {
/// Output type on success
type Output;

/// Retries an action, resulting in a potentially mutated version of itself.
Expand Down
Loading