Skip to content

Commit

Permalink
feat: Allow mutable references to be passed to gluon
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Apr 20, 2019
1 parent 3a92b17 commit 602220b
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 53 deletions.
2 changes: 1 addition & 1 deletion check/src/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2846,7 +2846,7 @@ impl<'a> Typecheck<'a> {
Some((found_arg_type, arg, ret))
if function_arg_type == Some(found_arg_type) || function_arg_type == None =>
{
return (found_arg_type, arg.clone(), ret.clone())
return (found_arg_type, arg.clone(), ret.clone());
}
_ => (),
}
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/getable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fn gen_impl(
Ok(value)
}

fn from_proxy(vm: &'__vm _gluon_thread::Thread, proxy: &'__value Self::Proxy) -> Self {
fn from_proxy(vm: &'__vm _gluon_thread::Thread, proxy: &'__value mut Self::Proxy) -> Self {
Self::from_value(vm, *proxy)
}

Expand Down
18 changes: 1 addition & 17 deletions codegen/src/userdata.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro2::{Ident, Span, TokenStream};
use shared::{map_lifetimes, map_type_params, split_for_impl};
use syn::{self, Data, DeriveInput, GenericParam, Generics};
use syn::{self, Data, DeriveInput, Generics};

use attr::{Container, CrateName};

Expand Down Expand Up @@ -48,12 +48,6 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr
},
};

let associated_type_generics = generics.params.iter().map(|param| match param {
GenericParam::Type(ty) => quote!( #ty :: Type ),
GenericParam::Lifetime(_) => quote!( 'static ),
GenericParam::Const(c) => quote!( #c ),
});

let dummy_const = Ident::new(&format!("_IMPL_USERDATA_FOR_{}", ident), Span::call_site());

quote! {
Expand All @@ -71,16 +65,6 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr
#[automatically_derived]
#[allow(unused_attributes, unused_variables)]
impl #impl_generics _gluon_gc::Traverseable for #ident #ty_generics {}

#[automatically_derived]
#[allow(unused_attributes, unused_variables)]
impl #impl_generics _gluon_api::VmType for #ident #ty_generics
#where_clause #(#trait_bounds,)* #(#lifetime_bounds),*
{
type Type = #ident<
#(#associated_type_generics),*
>;
}
};
}
}
3 changes: 2 additions & 1 deletion src/std_lib/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ fn eprintln(s: &str) -> IO<()> {
IO::Value(())
}

#[derive(Userdata)]
#[derive(Userdata, VmType)]
#[gluon(vm_type = "std.io.File")]
#[gluon(crate_name = "::vm")]
struct GluonFile(Mutex<Option<File>>);

Expand Down
3 changes: 2 additions & 1 deletion src/std_lib/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::vm::thread::Thread;
use crate::vm::types::VmInt;
use crate::vm::{self, ExternModule};

#[derive(Clone, Debug, Userdata)]
#[derive(Clone, Debug, Userdata, VmType)]
#[gluon(vm_type = "std.random.XorShiftRng")]
#[gluon(crate_name = "::vm")]
struct XorShiftRng(self::rand_xorshift::XorShiftRng);

Expand Down
6 changes: 4 additions & 2 deletions src/std_lib/regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use crate::real_std::error::Error as StdError;

use crate::vm::{self, api::Collect, thread::Thread, ExternModule};

#[derive(Debug, Userdata)]
#[derive(Debug, Userdata, VmType)]
#[gluon(vm_type = "std.regex.Regex")]
#[gluon(crate_name = "vm")]
struct Regex(regex::Regex);

#[derive(Debug, Userdata)]
#[derive(Debug, Userdata, VmType)]
#[gluon(vm_type = "std.regex.Error")]
#[gluon(crate_name = "vm")]
struct Error(regex::Error);

Expand Down
46 changes: 45 additions & 1 deletion tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use gluon::{
import::{add_extern_module, Import},
vm::{
api::{
de::De, scoped::Ref, FunctionRef, FutureResult, OpaqueValue, OwnedFunction,
de::De, scoped::{Ref, RefMut}, FunctionRef, FutureResult, OpaqueValue, OwnedFunction,
RuntimeResult, Userdata, VmType, IO,
},
thread::{RootedThread, Thread, Traverseable},
Expand Down Expand Up @@ -483,3 +483,47 @@ fn scoped_reference_out_of_scope() {
Ok(_) => panic!("Unexpected success"),
}
}

#[test]
fn scoped_mutable_reference() {
let _ = ::env_logger::try_init();

fn write(r: &mut Test, i: VmInt) -> IO<()> {
r.0 = i;
IO::Value(())
}

fn read(r: &mut Test) -> IO<VmInt> {
IO::Value(r.0)
}

let expr = r#"
let { read, write } = import! function
let { ? } = import! std.io
\t i ->
seq write t (i + 10)
read t
"#;

let vm = make_vm();
vm.register_type::<Test>("Test", &[])
.unwrap_or_else(|_| panic!("Could not add type"));
add_extern_module(&vm, "function", |thread| {
ExternModule::new(thread, record! {
write => primitive!(2, write),
read => primitive!(1, read)
})
});

let (mut result, _) = Compiler::new()
.run_expr::<OwnedFunction<fn(_, _) -> IO<VmInt>>>(&vm, "<top>", expr)
.unwrap_or_else(|err| panic!("{}", err));

assert_eq!(
result
.call(&mut RefMut::new(&mut Test(2)), 3)
.unwrap_or_else(|err| panic!("{}", err)),
IO::Value(13)
);
}

2 changes: 1 addition & 1 deletion tests/pattern_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ extern crate gluon;
#[macro_use]
mod support;

use gluon::Compiler;
use crate::support::*;
use gluon::Compiler;

test_expr! { prelude match_on_bool,
r#"
Expand Down
4 changes: 2 additions & 2 deletions vm/src/api/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ where $($args: Getable<'vm, 'vm> + 'vm,)*
let stack = StackFrame::<ExternState>::current(context.stack());
$(
let variants = Variants::with_root(stack[i].clone(), vm);
let proxy = match $args::to_proxy(vm, variants) {
let mut proxy = match $args::to_proxy(vm, variants) {
Ok(x) => x,
Err(err) => {
drop(stack);
Expand All @@ -339,7 +339,7 @@ where $($args: Getable<'vm, 'vm> + 'vm,)*
}
};
// The proxy will live as along as the 'value lifetime we just created
let $args = $args::from_proxy(vm, &*(&proxy as *const _));
let $args = $args::from_proxy(vm, &mut *(&mut proxy as *mut _));
i += 1;
)*
// Lock the frame to ensure that any references to the stack stay rooted
Expand Down
59 changes: 53 additions & 6 deletions vm/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ macro_rules! impl_getable_simple {
Ok(value)
}
#[inline(always)]
fn from_proxy(vm: &'vm Thread, proxy: &'value Self::Proxy) -> Self {
fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
<Self as Getable<'vm, 'value>>::from_value(vm, *proxy)
}
};
Expand Down Expand Up @@ -549,11 +549,11 @@ where

/// Trait which allows rust values to be retrieved from the virtual machine
pub trait Getable<'vm, 'value>: Sized {
type Proxy;
type Proxy: 'value;
fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self;

fn to_proxy(vm: &'vm Thread, value: Variants<'value>) -> Result<Self::Proxy>;
fn from_proxy(vm: &'vm Thread, proxy: &'value Self::Proxy) -> Self;
fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self;
}

pub fn convert<'vm, T, U>(thread: &'vm Thread, t: T) -> Result<U>
Expand Down Expand Up @@ -674,15 +674,21 @@ where
ValueRef::Userdata(data) => data.downcast_ref::<T>().map_or_else(
|| {
Ok(RefProxy::Lock(
data.downcast_ref::<scoped::Scoped<T>>().unwrap().read()?,
data.downcast_ref::<scoped::Scoped<T, &'static ()>>()
.map(|s| s.read())
.or_else(|| {
data.downcast_ref::<scoped::Scoped<T, &'static mut ()>>()
.map(|s| s.read())
})
.unwrap()?,
))
},
|x| Ok(RefProxy::Ref(x)),
),
_ => ice!("ValueRef is not an Userdata"),
}
}
fn from_proxy(_vm: &'vm Thread, proxy: &'value Self::Proxy) -> Self {
fn from_proxy(_vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
match proxy {
RefProxy::Lock(v) => &*v,
RefProxy::Ref(v) => v,
Expand All @@ -697,6 +703,37 @@ where
}
}

impl<'vm, T: ?Sized + VmType> VmType for &'vm mut T {
type Type = T::Type;
fn make_type(vm: &Thread) -> ArcType {
T::make_type(vm)
}
}

impl<'vm, 'value, T> Getable<'vm, 'value> for &'value mut T
where
T: vm::Userdata,
{
type Proxy = scoped::WriteGuard<'value, T>;

fn to_proxy(_vm: &'vm Thread, value: Variants<'value>) -> Result<Self::Proxy> {
match value.as_ref() {
ValueRef::Userdata(data) => Ok(data
.downcast_ref::<scoped::Scoped<T, &'static mut ()>>()
.unwrap()
.write()?),
_ => ice!("ValueRef is not an Userdata"),
}
}
fn from_proxy(_vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
proxy
}
// Only allow the unsafe version to be used
fn from_value(_vm: &'vm Thread, _value: Variants<'value>) -> Self {
panic!("Mutable references can only be created via proxies")
}
}

impl<'vm, 'value> Getable<'vm, 'value> for &'value str {
impl_getable_simple!();

Expand Down Expand Up @@ -750,7 +787,17 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for WithVM<'vm, T>
where
T: Getable<'vm, 'value>,
{
impl_getable_simple!();
type Proxy = T::Proxy;

fn to_proxy(vm: &'vm Thread, value: Variants<'value>) -> Result<Self::Proxy> {
T::to_proxy(vm, value)
}
fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
WithVM {
vm,
value: T::from_proxy(vm, proxy),
}
}

fn from_value(vm: &'vm Thread, value: Variants<'value>) -> WithVM<'vm, T> {
let t = T::from_value(vm, value);
Expand Down
Loading

0 comments on commit 602220b

Please sign in to comment.