Skip to content

Commit

Permalink
feat: Allow the c api to push and call functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Jun 19, 2016
1 parent f5b5164 commit e358570
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 23 deletions.
23 changes: 21 additions & 2 deletions src/c_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ use std::str;
use std::slice;

use vm::api::generic::A;
use vm::api::{Getable, Pushable, Generic};
use vm::api::{Getable, Pushable, Generic, CPrimitive};
use vm::types::{VMIndex, VMInt};
use vm::thread::{RootedThread, Thread, ThreadInternal};
use vm::thread::{RootedThread, Thread, ThreadInternal, Status};

use super::Compiler;

pub type Function = extern "C" fn (&Thread) -> Status;

// TODO What should the c api return as errors
// TODO How should error messages be returned
#[repr(C)]
Expand Down Expand Up @@ -68,6 +70,14 @@ pub unsafe extern "C" fn load_script(vm: &Thread,
}
}

pub extern "C" fn call_function(thread: &Thread, arguments: VMIndex) -> Error {
let stack = thread.current_frame();
match thread.call_function(stack, arguments) {
Ok(_) => Error::Ok,
Err(_) => Error::Unknown,
}
}

pub extern "C" fn push_int(vm: &Thread, int: VMInt) {
Thread::push(vm, int);
}
Expand All @@ -76,6 +86,15 @@ pub extern "C" fn push_float(vm: &Thread, float: f64) {
Thread::push(vm, float);
}

pub unsafe extern "C" fn push_function(vm: &Thread, name: *const u8, len: usize, function: Function, arguments: VMIndex) -> Error {
let s = match str::from_utf8(slice::from_raw_parts(name, len)) {
Ok(s) => s,
Err(_) => return Error::Unknown,
};
Thread::push(vm, CPrimitive::new(function, arguments, s));
Error::Ok
}

/// Push a string to the stack. The string must be valid utf-8 or an error will be returned
pub unsafe extern "C" fn push_string(vm: &Thread, s: &u8, len: usize) -> Error {
let s = match str::from_utf8(slice::from_raw_parts(s, len)) {
Expand Down
78 changes: 58 additions & 20 deletions vm/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ pub trait VMType {


/// Trait which allows a rust value to be pushed to the virtual machine
pub trait Pushable<'vm>: VMType {
pub trait Pushable<'vm> {
/// Pushes `self` to `stack`. If the call is successful a single element should have been added
/// to the stack and `Status::Ok` should be returned. If the call is unsuccessful `Status:Error`
/// should be returned and the stack should be left intact
Expand Down Expand Up @@ -505,8 +505,7 @@ impl<T> VMType for Vec<T>
}

impl<'vm, T> Pushable<'vm> for Vec<T>
where T: Pushable<'vm>,
T::Type: Sized
where T: Pushable<'vm>
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
let len = self.len() as VMIndex;
Expand Down Expand Up @@ -579,7 +578,6 @@ impl<T: VMType> VMType for Option<T>
}
}
impl<'vm, T: Pushable<'vm>> Pushable<'vm> for Option<T>
where T::Type: Sized
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
match self {
Expand Down Expand Up @@ -623,8 +621,6 @@ impl<T: VMType, E: VMType> VMType for Result<T, E>
}

impl<'vm, T: Pushable<'vm>, E: Pushable<'vm>> Pushable<'vm> for Result<T, E>
where T::Type: Sized,
E::Type: Sized
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
let tag = match self {
Expand Down Expand Up @@ -713,7 +709,6 @@ impl<'vm, T: Getable<'vm>> Getable<'vm> for IO<T> {
}

impl<'vm, T: Pushable<'vm>> Pushable<'vm> for IO<T>
where T::Type: Sized
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
match self {
Expand Down Expand Up @@ -892,8 +887,7 @@ macro_rules! define_tuple {
}
}
impl<'vm, $($id),+> Pushable<'vm> for ($($id),+)
where $($id: Pushable<'vm>),+,
$($id::Type: Sized),+
where $($id: Pushable<'vm>),+
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
let ( $($id),+ ) = self;
Expand Down Expand Up @@ -957,25 +951,33 @@ pub mod record {

pub trait FieldList {
fn len() -> VMIndex;
}

pub trait FieldTypes: FieldList {
fn field_types(vm: &Thread, fields: &mut Vec<types::Field<Symbol, TcType>>);
}

impl FieldList for () {
fn len() -> VMIndex {
0
}
}

impl FieldTypes for () {
fn field_types(_: &Thread, _: &mut Vec<types::Field<Symbol, TcType>>) {}
}

impl<F: Field, H: VMType, T> FieldList for HList<(F, H), T>
impl<F, H, T> FieldList for HList<(F, H), T>
where T: FieldList
{
fn len() -> VMIndex {
1 + T::len()
}
}

impl<F: Field, H: VMType, T> FieldTypes for HList<(F, H), T>
where T: FieldTypes
{
fn field_types(vm: &Thread, fields: &mut Vec<types::Field<Symbol, TcType>>) {
fields.push(types::Field {
name: Symbol::new(F::name()),
Expand Down Expand Up @@ -1026,7 +1028,7 @@ pub mod record {
}
}

impl<A: VMType, F: Field, T: FieldList> VMType for Record<HList<(F, A), T>>
impl<A: VMType, F: Field, T: FieldTypes> VMType for Record<HList<(F, A), T>>
where A::Type: Sized
{
type Type = Record<((&'static str, A::Type),)>;
Expand All @@ -1040,8 +1042,7 @@ pub mod record {
impl<'vm, A, F, T> Pushable<'vm> for Record<HList<(F, A), T>>
where A: Pushable<'vm>,
F: Field,
T: PushableFieldList<'vm>,
A::Type: Sized
T: PushableFieldList<'vm>
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
self.fields.push(vm, stack);
Expand Down Expand Up @@ -1142,27 +1143,60 @@ impl<F: VMType> VMType for Primitive<F> {

impl<'vm, F> Pushable<'vm> for Primitive<F>
where F: FunctionType + VMType + Send + Sync
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
let extern_function = Box::new(self.function);
let id = Symbol::new(self.name);
let value = Value::Function(vm.alloc(stack,
Move(ExternFunction {
id: id,
args: F::arguments(),
function: extern_function,
})));
stack.push(value);
Status::Ok
}
}

pub struct CPrimitive {
function: extern "C" fn (&Thread) -> Status,
arguments: VMIndex,
id: Symbol,
}

impl CPrimitive {
pub unsafe fn new(function: extern "C" fn (&Thread) -> Status, arguments: VMIndex, id: &str) -> CPrimitive {
CPrimitive {
id: Symbol::new(id),
function: function,
arguments: arguments,
}
}
}

impl<'vm> Pushable<'vm> for CPrimitive
{
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
use std::mem::transmute;
let function = self.function;
let extern_function = unsafe {
// The VM guarantess that it only ever calls this function with itself which should
// make sure that ignoring the lifetime is safe
transmute::<Box<Fn(&'vm Thread) -> Status + Send + Sync>,
Box<Fn(&Thread) -> Status + Send + Sync>>(Box::new(self.function))
Box<Fn(&Thread) -> Status + Send + Sync>>(Box::new(move |vm| function(vm)))
};
let id = Symbol::new(self.name);
let value = Value::Function(vm.alloc(stack,
Move(ExternFunction {
id: id,
args: F::arguments(),
id: self.id,
args: self.arguments,
function: extern_function,
})));
stack.push(value);
Status::Ok
}
}


fn make_type<T: ?Sized + VMType>(vm: &Thread) -> TcType {
<T as VMType>::make_type(vm)
}
Expand Down Expand Up @@ -1269,7 +1303,7 @@ impl <$($args: VMType,)* R: VMType> VMType for fn ($($args),*) -> R {
}

impl <'vm, $($args,)* R> Pushable<'vm> for fn ($($args),*) -> R
where $($args: Getable<'vm> + VMType + 'vm,)* R: Pushable<'vm> + 'vm {
where $($args: Getable<'vm> + VMType + 'vm,)* R: Pushable<'vm> + VMType + 'vm {
fn push(self, vm: &'vm Thread, stack: &mut Stack) -> Status {
let f = Box::new(move |vm| self.unpack_and_call(vm));
let extern_function = unsafe {
Expand Down Expand Up @@ -1298,7 +1332,9 @@ impl <'vm, $($args,)* R: VMType> FunctionType for fn ($($args),*) -> R {
}

impl <'vm, $($args,)* R> VMFunction<'vm> for fn ($($args),*) -> R
where $($args: Getable<'vm> + 'vm,)* R: Pushable<'vm> + 'vm {
where $($args: Getable<'vm> + 'vm,)*
R: Pushable<'vm> + VMType + 'vm
{
#[allow(non_snake_case, unused_mut, unused_assignments, unused_variables, unused_unsafe)]
fn unpack_and_call(&self, vm: &'vm Thread) -> Status {
let n_args = Self::arguments();
Expand Down Expand Up @@ -1341,7 +1377,9 @@ impl <'s, $($args: VMType,)* R: VMType> VMType for Fn($($args),*) -> R + 's {
}

impl <'vm, $($args,)* R> VMFunction<'vm> for Fn($($args),*) -> R + 'vm
where $($args: Getable<'vm> + 'vm,)* R: Pushable<'vm> + 'vm {
where $($args: Getable<'vm> + 'vm,)*
R: Pushable<'vm> + VMType + 'vm
{
#[allow(non_snake_case, unused_mut, unused_assignments, unused_variables, unused_unsafe)]
fn unpack_and_call(&self, vm: &'vm Thread) -> Status {
let n_args = Self::arguments();
Expand Down
2 changes: 1 addition & 1 deletion vm/src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ impl Thread {
/// Creates a new global value at `name`.
/// Fails if a global called `name` already exists.
pub fn define_global<'vm, T>(&'vm self, name: &str, value: T) -> Result<()>
where T: Pushable<'vm>
where T: Pushable<'vm> + VMType
{
let (status, value) = {
let mut stack = self.get_stack();
Expand Down

0 comments on commit e358570

Please sign in to comment.