From 010a7f71fe162265e64bc43f8db3832fcfbda3d1 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 12 Jul 2018 09:15:38 +0200 Subject: [PATCH] feat(vm): Let Getable return types bound to the lifetime of Variants (#587) feat(vm): Let `Getable` return types bound to the lifetime of Variants --- c-api/src/lib.rs | 2 +- codegen/src/getable.rs | 20 +- examples/http/main.rs | 4 +- examples/marshalling.rs | 15 +- src/lib.rs | 4 +- tests/support/mod.rs | 4 +- vm/src/api/de.rs | 1726 ++++++++-------- vm/src/api/mod.rs | 171 +- vm/src/serialization.rs | 2 +- vm/src/thread.rs | 4148 +++++++++++++++++++-------------------- vm/src/vm.rs | 9 +- 11 files changed, 3057 insertions(+), 3048 deletions(-) diff --git a/c-api/src/lib.rs b/c-api/src/lib.rs index dea9e8a1ab..007ebe8c93 100644 --- a/c-api/src/lib.rs +++ b/c-api/src/lib.rs @@ -240,7 +240,7 @@ pub extern "C" fn glu_get_light_userdata( fn get_value(vm: &Thread, index: VmIndex, out: &mut T) -> Error where - T: for<'vm> Getable<'vm>, + T: for<'vm, 'value> Getable<'vm, 'value>, { let mut context = vm.context(); let stack = context.stack.current_frame(); diff --git a/codegen/src/getable.rs b/codegen/src/getable.rs index 8e3d30cc78..e7533b982e 100644 --- a/codegen/src/getable.rs +++ b/codegen/src/getable.rs @@ -48,7 +48,7 @@ where quote! { #ident: if let Some(val) = data.lookup_field(vm, #quoted_ident) { - <#field_ty as ::gluon::vm::api::Getable<'__vm>>::from_value(vm, val) + <#field_ty as ::gluon::vm::api::Getable<'__vm, '__value>>::from_value(vm, val) } else { panic!("Cannot find the field '{}'. Do the type definitions match?", #quoted_ident); } @@ -72,7 +72,7 @@ where quote! { if let Some(val) = data.get_variant(#tag) { - <#field_ty as ::gluon::vm::api::Getable<'__vm>>::from_value(vm, val) + <#field_ty as ::gluon::vm::api::Getable<'__vm, '__value>>::from_value(vm, val) } else { panic!("Cannot find the field with tag '{}'. Do the type definitions match?", #tag); } @@ -89,7 +89,8 @@ where fn derive_enum(ast: DataEnum, ident: Ident, generics: Generics) -> TokenStream { let cons; { - let variants = ast.variants + let variants = ast + .variants .iter() .enumerate() .map(|(tag, variant)| gen_variant_match(&ident, tag, variant)); @@ -116,15 +117,16 @@ fn gen_impl(ident: Ident, generics: Generics, cons_expr: TokenStream) -> TokenSt // generate bounds like T: Getable for every type parameter let getable_bounds = create_getable_bounds(&generics); - let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &["'__vm"]); + let (impl_generics, ty_generics, where_clause) = + split_for_impl(&generics, &["'__vm", "'__value"]); quote! { #[automatically_derived] #[allow(unused_attributes, unused_variables)] - impl #impl_generics ::gluon::vm::api::Getable<'__vm> for #ident #ty_generics + impl #impl_generics ::gluon::vm::api::Getable<'__vm, '__value> for #ident #ty_generics #where_clause #(#getable_bounds,)* #(#lifetime_bounds),* { - fn from_value(vm: &'__vm ::gluon::vm::thread::Thread, variants: ::gluon::vm::Variants) -> Self { + fn from_value(vm: &'__vm ::gluon::vm::thread::Thread, variants: ::gluon::vm::Variants<'__value>) -> Self { let data = match variants.as_ref() { ::gluon::vm::api::ValueRef::Data(data) => data, val => panic!("Unexpected value: '{:?}'. Do the type definitions match?", val), @@ -174,7 +176,7 @@ where quote! { if let Some(val) = data.get_variant(#idx) { - <#field_ty as ::gluon::vm::api::Getable<'__vm>>::from_value(vm, val) + <#field_ty as ::gluon::vm::api::Getable<'__vm, '__value>>::from_value(vm, val) } else { panic!("Enum does not contain data at index '{}'. Do the type definitions match?", #idx) } @@ -199,7 +201,7 @@ where quote! { #field_ident: if let Some(val) = data.get_variant(#idx) { - <#field_ty as ::gluon::vm::api::Getable<'__vm>>::from_value(vm, val) + <#field_ty as ::gluon::vm::api::Getable<'__vm, '__value>>::from_value(vm, val) } else { panic!("Enum does not contain data at index '{}'. Do the type definitions match?", #idx) } @@ -214,7 +216,7 @@ where fn create_getable_bounds(generics: &Generics) -> Vec { map_type_params(generics, |ty| { quote! { - #ty: ::gluon::vm::api::Getable<'__vm> + #ty: ::gluon::vm::api::Getable<'__vm, '__value> } }) } diff --git a/examples/http/main.rs b/examples/http/main.rs index 275b23c514..d232f9026d 100644 --- a/examples/http/main.rs +++ b/examples/http/main.rs @@ -208,8 +208,8 @@ struct StatusCodeContainer(#[serde(with = "RemoteStatusCode")] StatusCode); define_vmtype! { StatusCode, StatusCodeContainer } -impl<'vm> Getable<'vm> for Wrap { - fn from_value(vm: &'vm Thread, value: Variants) -> Self { +impl<'vm, 'value> Getable<'vm, 'value> for Wrap { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self { use gluon::vm::api::de::De; Wrap((De::::from_value(vm, value).0).0) } diff --git a/examples/marshalling.rs b/examples/marshalling.rs index bef1be4915..405d30b225 100644 --- a/examples/marshalling.rs +++ b/examples/marshalling.rs @@ -39,14 +39,14 @@ impl api::VmType for Enum { } } -impl<'vm> api::Pushable<'vm> for Enum { +impl<'vm, 'value> api::Pushable<'vm> for Enum { fn push(self, thread: &'vm Thread, context: &mut Context) -> vm::Result<()> { api::ser::Ser(self).push(thread, context) } } -impl<'vm> api::Getable<'vm> for Enum { - fn from_value(thread: &'vm Thread, value: vm::Variants) -> Self { +impl<'vm, 'value> api::Getable<'vm, 'value> for Enum { + fn from_value(thread: &'vm Thread, value: vm::Variants<'value>) -> Self { api::de::De::from_value(thread, value).0 } } @@ -241,7 +241,8 @@ where fn make_type(vm: &Thread) -> ArcType { // get the type defined in Gluon - let ty = vm.find_type_info("examples.wrapper.User") + let ty = vm + .find_type_info("examples.wrapper.User") .expect("Could not find type") .into_type(); @@ -275,11 +276,11 @@ where } } -impl<'vm, T> Getable<'vm> for GluonUser +impl<'vm, 'value, T> Getable<'vm, 'value> for GluonUser where - T: Getable<'vm>, + T: Getable<'vm, 'value>, { - fn from_value(vm: &'vm Thread, data: Variants) -> GluonUser { + fn from_value(vm: &'vm Thread, data: Variants<'value>) -> GluonUser { // get the data, it must be a complex type let data = match data.as_ref() { ValueRef::Data(data) => data, diff --git a/src/lib.rs b/src/lib.rs index 1cca89a46b..513184c310 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -519,7 +519,7 @@ impl Compiler { expr_str: &str, ) -> Result<(T, ArcType)> where - T: Getable<'vm> + VmType + Send + 'vm, + T: for<'value> Getable<'vm, 'value> + VmType + Send + 'vm, { let expected = T::make_type(vm); expr_str @@ -564,7 +564,7 @@ impl Compiler { expr_str: &str, ) -> BoxFutureValue<'static, (T, ArcType), Error> where - T: for<'vm> Getable<'vm> + VmType + Send + 'static, + T: for<'vm, 'value> Getable<'vm, 'value> + VmType + Send + 'static, { let expected = T::make_type(&vm); let vm = vm.root_thread(); diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 547cf391f4..dadbaa292c 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -21,7 +21,7 @@ pub fn load_script(vm: &Thread, filename: &str, input: &str) -> ::gluon::Result< #[allow(dead_code)] pub fn run_expr_<'vm, T>(vm: &'vm Thread, s: &str, implicit_prelude: bool) -> T where - T: Getable<'vm> + VmType + Send + 'vm, + T: for<'value> Getable<'vm, 'value> + VmType + Send + 'vm, { Compiler::new() .implicit_prelude(implicit_prelude) @@ -33,7 +33,7 @@ where #[allow(dead_code)] pub fn run_expr<'vm, T>(vm: &'vm Thread, s: &str) -> T where - T: Getable<'vm> + VmType + Send + 'vm, + T: for<'value> Getable<'vm, 'value> + VmType + Send + 'vm, { run_expr_(vm, s, false) } diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index 7930f66476..e9176d0494 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -1,863 +1,863 @@ -//! Gluon -> Rust value conversion via the `serde::Deserialize` trait -//! -//! _This module requires Gluon to be built with the `serde` feature._ - -use std::cell::RefCell; -use std::fmt; -use std::marker::PhantomData; -use std::result::Result as StdResult; - -use base::resolve; -use base::symbol::Symbol; -use base::types::{arg_iter, ArcType, BuiltinType, Type, TypeEnv}; - -use api::{Getable, ValueRef, VmType}; -use thread::{RootedThread, RootedValue, Thread, ThreadInternal}; -use {Error as VmError, Result, Variants}; - -use serde::de::{ - self, DeserializeOwned, DeserializeSeed, EnumAccess, Error, IntoDeserializer, MapAccess, - SeqAccess, VariantAccess, Visitor, -}; - -impl de::Error for VmError { - fn custom(msg: T) -> Self - where - T: fmt::Display, - { - VmError::Message(format!("{}", msg)) - } -} - -/** -`Getable` wrapper which extracts `T` by deserializing it into a rust value. - -## Struct - -``` -#[macro_use] -extern crate serde_derive; - -extern crate gluon; -#[macro_use] -extern crate gluon_vm; - -use gluon::{Compiler, Thread, new_vm}; -use gluon::base::types::ArcType; -use gluon::vm::api::VmType; -use gluon::vm::api::de::De; -# fn main() { - -#[derive(Debug, PartialEq, Deserialize)] -struct Vec2 { - x: f64, - y: f64 -} - -impl VmType for Vec2 { - type Type = Self; - - fn make_type(thread: &Thread) -> ArcType { - field_decl!{ x, y } - type T = record_type! { - x => f64, - y => f64 - }; - T::make_type(thread) - } -} - -# if ::std::env::var("GLUON_PATH").is_err() { -# ::std::env::set_var("GLUON_PATH", ".."); -# } - -let thread = new_vm(); - -let (De(vec), _) = Compiler::new() -# .implicit_prelude(false) - .run_expr::>(&thread, "test", "{ x = 1.0, y = 2.0 }") - .unwrap_or_else(|err| panic!("{}", err)); -assert_eq!(vec, Vec2 { - x: 1.0, - y: 2.0 - }); - -# } -``` - -## Enum - -``` -#[macro_use] -extern crate serde_derive; - -extern crate gluon; - -use gluon::{Compiler, Thread, new_vm}; -use gluon::base::types::ArcType; -use gluon::vm::api::VmType; -use gluon::vm::api::de::De; -# fn main() { - -#[derive(Debug, PartialEq, Deserialize)] -enum Enum { - A(i32), - B { string: String, test: f64 }, -} - -impl VmType for Enum { - type Type = Self; - - fn make_type(thread: &Thread) -> ArcType { - // Use the enum type declared in gluon - thread.find_type_info("test.Enum").unwrap().into_type() - } -} - -# if ::std::env::var("GLUON_PATH").is_err() { -# ::std::env::set_var("GLUON_PATH", ".."); -# } - -let thread = new_vm(); - -Compiler::new() -# .implicit_prelude(false) - .load_script( - &thread, - "test", - r#" type Enum = | A Int | B String Float in { Enum } "#, - ) - .unwrap_or_else(|err| panic!("{}", err)); - -let (De(enum_), _) = Compiler::new() -# .implicit_prelude(false) - .run_expr::>( - &thread, - "test", - r#" let { Enum } = import! "test" in A 123 "#, - ) - .unwrap_or_else(|err| panic!("{}", err)); -assert_eq!(enum_, Enum::A(123)); - -// The field names of record variants are ignored so make sure the fields are declared correctly -let (De(enum_), _) = Compiler::new() -# .implicit_prelude(false) - .run_expr::>( - &thread, - "test", - r#" let { Enum } = import! "test" in B "abc" 3.14 "#, - ) - .unwrap_or_else(|err| panic!("{}", err)); -assert_eq!( - enum_, - Enum::B { - string: "abc".to_string(), - test: 3.14, - } -); - -# } -``` -*/ - -pub struct De(pub T); - -impl VmType for De -where - T: VmType, -{ - type Type = T::Type; - - fn make_type(thread: &Thread) -> ArcType { - T::make_type(thread) - } -} - -impl<'vm, T> Getable<'vm> for De -where - T: VmType, - T: DeserializeOwned, -{ - fn from_value(thread: &'vm Thread, value: Variants) -> Self { - let typ = T::make_type(thread); - match from_value(thread, value, &typ).map(De) { - Ok(v) => v, - Err(err) => ice!("Getable::from_value for De: {}", err), - } - } -} - -/// Deserializes `T` from a gluon value assuming that `value` is of type `typ`. -pub fn from_value(thread: &Thread, value: Variants, typ: &ArcType) -> Result -where - T: DeserializeOwned, -{ - let env = thread.global_env().get_env(); - let mut deserializer = Deserializer::from_value(thread, &*env, value, typ); - T::deserialize(&mut deserializer) -} - -#[derive(Clone)] -struct State<'de> { - thread: &'de Thread, - env: &'de TypeEnv, -} - -#[derive(Clone)] -struct Deserializer<'de, 't> { - state: State<'de>, - input: Variants<'de>, - typ: &'t ArcType, -} - -impl<'de, 't> Deserializer<'de, 't> { - fn from_value( - thread: &'de Thread, - env: &'de TypeEnv, - input: Variants<'de>, - typ: &'t ArcType, - ) -> Self { - Deserializer { - state: State { - thread: thread, - env: env, - }, - input: input, - typ: typ, - } - } - - fn deserialize_builtin(&self, expected: BuiltinType, visit: F) -> Result - where - F: FnOnce(T) -> Result, - T: Getable<'de>, - { - self.deserialize_leaf(|t| *t == Type::Builtin(expected), visit) - } - - fn deserialize_leaf(&self, expected: E, visit: F) -> Result - where - E: FnOnce(&Type) -> bool, - F: FnOnce(T) -> Result, - T: Getable<'de>, - { - let typ = resolve::remove_aliases_cow(self.state.env, self.typ); - if expected(&typ) { - // We can rely on `self.input` being rooted for `de` letting us use `from_value_unsafe` - unsafe { visit(T::from_value_unsafe(self.state.thread, self.input)) } - } else { - Err(VmError::Message(format!( - "Unable to deserialize `{}`", - self.typ - ))) - } - } -} - -thread_local! { - static VALUE_TRANSFER: RefCell>> = RefCell::new(None); -} - -/// Allows deserializing a `Value` as-is. Only works with the `Deserializer` defined in this module -pub fn deserialize_raw_value<'de, D>( - deserializer: D, -) -> StdResult, D::Error> -where - D: de::Deserializer<'de>, -{ - use serde::Deserialize; - - VALUE_TRANSFER.with(|t| { - assert!(t.borrow().is_none()); - }); - - RawValueDeserialize::deserialize(deserializer)?; - - let opt_value = VALUE_TRANSFER.with(|t| t.borrow_mut().take()); - opt_value.ok_or_else(|| D::Error::custom("Unable to deserialize raw value")) -} - -impl<'de> de::Deserialize<'de> for RootedValue { - fn deserialize(deserializer: D) -> StdResult - where - D: de::Deserializer<'de>, - { - deserialize_raw_value(deserializer) - } -} - -#[derive(Deserialize)] -struct RawValueDeserialize; - -impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { - type Error = VmError; - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Array(ref array) => match array.as_slice::() { - Some(_) => self.deserialize_bytes(visitor), - _ => self.deserialize_seq(visitor), - }, - ValueRef::Byte(_) => self.deserialize_u8(visitor), - ValueRef::Data(_) => { - let typ = resolve::remove_aliases_cow(self.state.env, self.typ); - let mut deserializer = Deserializer { - typ: &typ, - ..self.clone() - }; - if let Type::Record(_) = **typ { - deserializer.deserialize_enum("", &[], visitor) - } else { - deserializer.deserialize_map(visitor) - } - } - ValueRef::Float(_) => self.deserialize_f64(visitor), - ValueRef::Int(_) => self.deserialize_i64(visitor), - ValueRef::String(ref s) => visitor.visit_borrowed_str(s), - ValueRef::Closure(_) - | ValueRef::Userdata(_) - | ValueRef::Thread(_) - | ValueRef::Internal => Err(VmError::Message(format!( - "Unable to deserialize `{}`", - self.typ - ))), - } - } - - fn deserialize_bool(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Data(data) => visitor.visit_bool(data.tag() != 0), - _ => Err(VmError::Message(format!("Unable to deserialize bool"))), - } - } - - // The `parse_signed` function is generic over the integer type `T` so here - // it is invoked with `T=i8`. The next 8 methods are similar. - fn deserialize_i8(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Byte(b) => visitor.visit_i8(b as i8), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_i16(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Byte(b) => visitor.visit_i16(b as i16), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_i32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Int(b) => visitor.visit_i32(b as i32), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_i64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Int(b) => visitor.visit_i64(b as i64), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_u8(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Byte(b) => visitor.visit_u8(b), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_u16(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Byte(b) => visitor.visit_u16(b as u16), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_u32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Int(b) => visitor.visit_u32(b as u32), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_u64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Int(b) => visitor.visit_u64(b as u64), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_f32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Float(f) => visitor.visit_f32(f as f32), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_f64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Float(f) => visitor.visit_f64(f), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_char(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - use std::char::from_u32; - let typ = resolve::remove_aliases_cow(self.state.env, self.typ); - match (self.input.as_ref(), &**typ) { - (ValueRef::Int(c), &Type::Builtin(BuiltinType::Char)) => match from_u32(c as u32) { - Some(c) => visitor.visit_char(c), - None => self.deserialize_any(visitor), - }, - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_builtin(BuiltinType::String, |s| visitor.visit_borrowed_str(s)) - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - fn deserialize_bytes(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_leaf( - |typ| match *typ { - Type::App(ref func, ref args) if args.len() == 1 => { - **func == Type::Builtin(BuiltinType::Array) - && *args[0] == Type::Builtin(BuiltinType::Byte) - } - _ => false, - }, - |s| visitor.visit_bytes(s), - ) - } - - fn deserialize_byte_buf(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_bytes(visitor) - } - - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let typ = resolve::canonical_alias(self.state.env, self.typ, |alias| { - alias.name.name().as_str() == "std.types.Option" - }); - let option_inner_typ = match **typ { - Type::App(ref func, ref args) if args.len() == 1 => match **func { - Type::Alias(ref alias) if alias.name.name().as_str() == "std.types.Option" => { - Some(&args[0]) - } - _ => None, - }, - _ => None, - }; - // If the type is not `Option` we just visit the value itself - match option_inner_typ { - Some(typ) => match self.input.as_ref() { - ValueRef::Data(data) if data.tag() == 0 => visitor.visit_none(), - ValueRef::Data(data) if data.tag() == 1 => visitor.visit_some(&mut Deserializer { - state: self.state.clone(), - typ: typ, - input: data.get_variant(0).unwrap(), - }), - _ => self.deserialize_any(visitor), - }, - None => visitor.visit_some(self), - } - } - - fn deserialize_unit(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.input.as_ref() { - ValueRef::Data(data) if data.tag() == 0 => visitor.visit_unit(), - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_unit_struct(self, name: &'static str, visitor: V) -> Result - where - V: Visitor<'de>, - { - if name == "RawValueDeserialize" { - VALUE_TRANSFER.with(|t| { - let mut store = t.borrow_mut(); - assert!(store.is_none()); - *store = Some(self.state.thread.root_value(self.input.get_value())); - }); - visitor.visit_unit() - } else { - self.deserialize_unit(visitor) - } - } - - fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_newtype_struct(self) - } - - fn deserialize_seq(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let typ = resolve::remove_aliases_cow(self.state.env, self.typ); - match (self.input.as_ref(), &**typ) { - (ValueRef::Array(values), &Type::App(_, ref args)) if args.len() == 1 => visitor - .visit_seq(SeqDeserializer::new( - self.state.clone(), - values.iter().map(|variant| (variant, &args[0])), - )), - (ValueRef::Data(data), &Type::Variant(ref row)) => { - match row.row_iter().nth(data.tag() as usize) { - Some(field) => { - let iter = (0..data.len()) - .map(|i| data.get_variant(i).unwrap()) - .zip(arg_iter(&field.typ)); - visitor.visit_seq(SeqDeserializer::new(self.state.clone(), iter)) - } - None => self.deserialize_any(visitor), - } - } - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_tuple(self, _len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - - fn deserialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - - fn deserialize_map(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let typ = resolve::remove_aliases_cow(self.state.env, self.typ); - match (self.input.as_ref(), &**typ) { - (ValueRef::Data(ref data), &Type::Record { .. }) => { - let iter = typ.row_iter().flat_map(|field| { - data.lookup_field(self.state.thread, field.name.as_ref()) - .map(|variant| (variant, &field.name, &field.typ)) - }); - visitor.visit_map(MapDeserializer::new(self.state.clone(), iter)) - } - _ => self.deserialize_any(visitor), - } - } - - fn deserialize_struct( - self, - _name: &'static str, - _fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_map(visitor) - } - - fn deserialize_enum( - self, - _name: &'static str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - visitor.visit_enum(Enum::new(self)) - } - - fn deserialize_identifier(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - fn deserialize_ignored_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_any(visitor) - } -} - -struct SeqDeserializer<'de, 't, I> { - state: State<'de>, - iter: I, - _marker: PhantomData<&'t ()>, -} - -impl<'de, 't, I> SeqDeserializer<'de, 't, I> { - fn new(state: State<'de>, iter: I) -> Self { - SeqDeserializer { - state: state, - iter: iter, - _marker: PhantomData, - } - } -} - -impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I> -where - I: Iterator, &'t ArcType)>, -{ - type Error = VmError; - - fn next_element_seed(&mut self, seed: T) -> Result> - where - T: DeserializeSeed<'de>, - { - match self.iter.next() { - Some((value, typ)) => { - seed.deserialize(&mut Deserializer { - state: self.state.clone(), - input: value, - typ: typ, - }).map(Some) - } - None => Ok(None), - } - } -} - -struct MapDeserializer<'de, 't, I> { - state: State<'de>, - iter: I, - value: Option<(Variants<'de>, &'t ArcType)>, -} - -impl<'de, 't, I> MapDeserializer<'de, 't, I> { - fn new(state: State<'de>, iter: I) -> Self { - MapDeserializer { - state: state, - iter: iter, - value: None, - } - } -} - -impl<'de, 'a, 't, I> MapAccess<'de> for MapDeserializer<'de, 't, I> -where - I: Iterator, &'t Symbol, &'t ArcType)>, -{ - type Error = VmError; - - fn next_key_seed(&mut self, seed: K) -> Result> - where - K: DeserializeSeed<'de>, - { - match self.iter.next() { - Some((value, field, typ)) => { - self.value = Some((value, typ)); - seed.deserialize(field.as_ref().into_deserializer()) - .map(Some) - } - None => Ok(None), - } - } - - fn next_value_seed(&mut self, seed: V) -> Result - where - V: DeserializeSeed<'de>, - { - match self.value.take() { - Some((value, typ)) => seed.deserialize(&mut Deserializer { - state: self.state.clone(), - input: value, - typ: typ, - }), - None => Err(Self::Error::custom("Unable to deserialize value")), - } - } -} - -struct Enum<'a, 'de: 'a, 't: 'a> { - de: &'a mut Deserializer<'de, 't>, -} - -impl<'a, 'de, 't> Enum<'a, 'de, 't> { - fn new(de: &'a mut Deserializer<'de, 't>) -> Self { - Enum { de: de } - } -} - -impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { - type Error = VmError; - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.de.input.as_ref() { - ValueRef::Data(data) => visitor.visit_u32(data.tag()), - _ => Err(Self::Error::custom("Unable to deserialize tag")), - } - } - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let tag = match self.de.input.as_ref() { - ValueRef::Data(data) => data.tag(), - _ => return Err(Self::Error::custom("Unable to deserialize tag")), - }; - let typ = resolve::remove_aliases_cow(self.de.state.env, self.de.typ); - match **typ { - Type::Variant(ref variants) => { - let variant = variants - .row_iter() - .nth(tag as usize) - .ok_or_else(|| Self::Error::custom("Unable to deserialize tag"))?; - visitor.visit_str(variant.name.as_ref()) - } - _ => return Err(Self::Error::custom("Unable to deserialize tag")), - } - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char bytes - byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - -impl<'a, 'de, 't> EnumAccess<'de> for Enum<'a, 'de, 't> { - type Error = VmError; - type Variant = Self; - - fn variant_seed(mut self, seed: V) -> Result<(V::Value, Self::Variant)> - where - V: DeserializeSeed<'de>, - { - seed.deserialize(&mut self).map(|value| (value, self)) - } -} - -impl<'de, 'a, 't> VariantAccess<'de> for Enum<'a, 'de, 't> { - type Error = VmError; - - fn unit_variant(self) -> Result<()> { - Ok(()) - } - - fn newtype_variant_seed(self, seed: T) -> Result - where - T: DeserializeSeed<'de>, - { - let typ = resolve::remove_aliases_cow(self.de.state.env, self.de.typ); - match (self.de.input.as_ref(), &**typ) { - (ValueRef::Data(data), &Type::Variant(ref row)) => { - match row.row_iter().nth(data.tag() as usize) { - Some(field) => seed.deserialize(&mut Deserializer { - input: data.get_variant(0).ok_or_else(|| { - VmError::Message("Expected variant to have a value argument".into()) - })?, - typ: arg_iter(&field.typ).next().ok_or_else(|| { - VmError::Message("Expected variant to have a type argument".into()) - })?, - ..self.de.clone() - }), - None => seed.deserialize(self.de), - } - } - _ => seed.deserialize(self.de), - } - } - - fn tuple_variant(self, _len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - de::Deserializer::deserialize_seq(self.de, visitor) - } - - fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result - where - V: Visitor<'de>, - { - de::Deserializer::deserialize_seq(self.de, visitor) - } -} +//! Gluon -> Rust value conversion via the `serde::Deserialize` trait +//! +//! _This module requires Gluon to be built with the `serde` feature._ + +use std::cell::RefCell; +use std::fmt; +use std::marker::PhantomData; +use std::result::Result as StdResult; + +use base::resolve; +use base::symbol::Symbol; +use base::types::{arg_iter, ArcType, BuiltinType, Type, TypeEnv}; + +use api::{Getable, ValueRef, VmType}; +use thread::{RootedThread, RootedValue, Thread, ThreadInternal}; +use {Error as VmError, Result, Variants}; + +use serde::de::{ + self, DeserializeOwned, DeserializeSeed, EnumAccess, Error, IntoDeserializer, MapAccess, + SeqAccess, VariantAccess, Visitor, +}; + +impl de::Error for VmError { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + VmError::Message(format!("{}", msg)) + } +} + +/** +`Getable` wrapper which extracts `T` by deserializing it into a rust value. + +## Struct + +``` +#[macro_use] +extern crate serde_derive; + +extern crate gluon; +#[macro_use] +extern crate gluon_vm; + +use gluon::{Compiler, Thread, new_vm}; +use gluon::base::types::ArcType; +use gluon::vm::api::VmType; +use gluon::vm::api::de::De; +# fn main() { + +#[derive(Debug, PartialEq, Deserialize)] +struct Vec2 { + x: f64, + y: f64 +} + +impl VmType for Vec2 { + type Type = Self; + + fn make_type(thread: &Thread) -> ArcType { + field_decl!{ x, y } + type T = record_type! { + x => f64, + y => f64 + }; + T::make_type(thread) + } +} + +# if ::std::env::var("GLUON_PATH").is_err() { +# ::std::env::set_var("GLUON_PATH", ".."); +# } + +let thread = new_vm(); + +let (De(vec), _) = Compiler::new() +# .implicit_prelude(false) + .run_expr::>(&thread, "test", "{ x = 1.0, y = 2.0 }") + .unwrap_or_else(|err| panic!("{}", err)); +assert_eq!(vec, Vec2 { + x: 1.0, + y: 2.0 + }); + +# } +``` + +## Enum + +``` +#[macro_use] +extern crate serde_derive; + +extern crate gluon; + +use gluon::{Compiler, Thread, new_vm}; +use gluon::base::types::ArcType; +use gluon::vm::api::VmType; +use gluon::vm::api::de::De; +# fn main() { + +#[derive(Debug, PartialEq, Deserialize)] +enum Enum { + A(i32), + B { string: String, test: f64 }, +} + +impl VmType for Enum { + type Type = Self; + + fn make_type(thread: &Thread) -> ArcType { + // Use the enum type declared in gluon + thread.find_type_info("test.Enum").unwrap().into_type() + } +} + +# if ::std::env::var("GLUON_PATH").is_err() { +# ::std::env::set_var("GLUON_PATH", ".."); +# } + +let thread = new_vm(); + +Compiler::new() +# .implicit_prelude(false) + .load_script( + &thread, + "test", + r#" type Enum = | A Int | B String Float in { Enum } "#, + ) + .unwrap_or_else(|err| panic!("{}", err)); + +let (De(enum_), _) = Compiler::new() +# .implicit_prelude(false) + .run_expr::>( + &thread, + "test", + r#" let { Enum } = import! "test" in A 123 "#, + ) + .unwrap_or_else(|err| panic!("{}", err)); +assert_eq!(enum_, Enum::A(123)); + +// The field names of record variants are ignored so make sure the fields are declared correctly +let (De(enum_), _) = Compiler::new() +# .implicit_prelude(false) + .run_expr::>( + &thread, + "test", + r#" let { Enum } = import! "test" in B "abc" 3.14 "#, + ) + .unwrap_or_else(|err| panic!("{}", err)); +assert_eq!( + enum_, + Enum::B { + string: "abc".to_string(), + test: 3.14, + } +); + +# } +``` +*/ + +pub struct De(pub T); + +impl VmType for De +where + T: VmType, +{ + type Type = T::Type; + + fn make_type(thread: &Thread) -> ArcType { + T::make_type(thread) + } +} + +impl<'vm, 'value, T> Getable<'vm, 'value> for De +where + T: VmType, + T: DeserializeOwned, +{ + fn from_value(thread: &'vm Thread, value: Variants<'value>) -> Self { + let typ = T::make_type(thread); + match from_value(thread, value, &typ).map(De) { + Ok(v) => v, + Err(err) => ice!("Getable::from_value for De: {}", err), + } + } +} + +/// Deserializes `T` from a gluon value assuming that `value` is of type `typ`. +pub fn from_value(thread: &Thread, value: Variants, typ: &ArcType) -> Result +where + T: DeserializeOwned, +{ + let env = thread.global_env().get_env(); + let mut deserializer = Deserializer::from_value(thread, &*env, value, typ); + T::deserialize(&mut deserializer) +} + +#[derive(Clone)] +struct State<'de> { + thread: &'de Thread, + env: &'de TypeEnv, +} + +#[derive(Clone)] +struct Deserializer<'de, 't> { + state: State<'de>, + input: Variants<'de>, + typ: &'t ArcType, +} + +impl<'de, 't> Deserializer<'de, 't> { + fn from_value( + thread: &'de Thread, + env: &'de TypeEnv, + input: Variants<'de>, + typ: &'t ArcType, + ) -> Self { + Deserializer { + state: State { + thread: thread, + env: env, + }, + input: input, + typ: typ, + } + } + + fn deserialize_builtin(&self, expected: BuiltinType, visit: F) -> Result + where + F: FnOnce(T) -> Result, + T: for<'value> Getable<'de, 'value>, + { + self.deserialize_leaf(|t| *t == Type::Builtin(expected), visit) + } + + fn deserialize_leaf(&self, expected: E, visit: F) -> Result + where + E: FnOnce(&Type) -> bool, + F: FnOnce(T) -> Result, + T: for<'value> Getable<'de, 'value>, + { + let typ = resolve::remove_aliases_cow(self.state.env, self.typ); + if expected(&typ) { + // We can rely on `self.input` being rooted for `de` letting us use `from_value_unsafe` + unsafe { visit(T::from_value_unsafe(self.state.thread, self.input)) } + } else { + Err(VmError::Message(format!( + "Unable to deserialize `{}`", + self.typ + ))) + } + } +} + +thread_local! { + static VALUE_TRANSFER: RefCell>> = RefCell::new(None); +} + +/// Allows deserializing a `Value` as-is. Only works with the `Deserializer` defined in this module +pub fn deserialize_raw_value<'de, D>( + deserializer: D, +) -> StdResult, D::Error> +where + D: de::Deserializer<'de>, +{ + use serde::Deserialize; + + VALUE_TRANSFER.with(|t| { + assert!(t.borrow().is_none()); + }); + + RawValueDeserialize::deserialize(deserializer)?; + + let opt_value = VALUE_TRANSFER.with(|t| t.borrow_mut().take()); + opt_value.ok_or_else(|| D::Error::custom("Unable to deserialize raw value")) +} + +impl<'de> de::Deserialize<'de> for RootedValue { + fn deserialize(deserializer: D) -> StdResult + where + D: de::Deserializer<'de>, + { + deserialize_raw_value(deserializer) + } +} + +#[derive(Deserialize)] +struct RawValueDeserialize; + +impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { + type Error = VmError; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Array(ref array) => match array.as_slice::() { + Some(_) => self.deserialize_bytes(visitor), + _ => self.deserialize_seq(visitor), + }, + ValueRef::Byte(_) => self.deserialize_u8(visitor), + ValueRef::Data(_) => { + let typ = resolve::remove_aliases_cow(self.state.env, self.typ); + let mut deserializer = Deserializer { + typ: &typ, + ..self.clone() + }; + if let Type::Record(_) = **typ { + deserializer.deserialize_enum("", &[], visitor) + } else { + deserializer.deserialize_map(visitor) + } + } + ValueRef::Float(_) => self.deserialize_f64(visitor), + ValueRef::Int(_) => self.deserialize_i64(visitor), + ValueRef::String(ref s) => visitor.visit_borrowed_str(s), + ValueRef::Closure(_) + | ValueRef::Userdata(_) + | ValueRef::Thread(_) + | ValueRef::Internal => Err(VmError::Message(format!( + "Unable to deserialize `{}`", + self.typ + ))), + } + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Data(data) => visitor.visit_bool(data.tag() != 0), + _ => Err(VmError::Message(format!("Unable to deserialize bool"))), + } + } + + // The `parse_signed` function is generic over the integer type `T` so here + // it is invoked with `T=i8`. The next 8 methods are similar. + fn deserialize_i8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Byte(b) => visitor.visit_i8(b as i8), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_i16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Byte(b) => visitor.visit_i16(b as i16), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_i32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Int(b) => visitor.visit_i32(b as i32), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_i64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Int(b) => visitor.visit_i64(b as i64), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Byte(b) => visitor.visit_u8(b), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_u16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Byte(b) => visitor.visit_u16(b as u16), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_u32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Int(b) => visitor.visit_u32(b as u32), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Int(b) => visitor.visit_u64(b as u64), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Float(f) => visitor.visit_f32(f as f32), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Float(f) => visitor.visit_f64(f), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + use std::char::from_u32; + let typ = resolve::remove_aliases_cow(self.state.env, self.typ); + match (self.input.as_ref(), &**typ) { + (ValueRef::Int(c), &Type::Builtin(BuiltinType::Char)) => match from_u32(c as u32) { + Some(c) => visitor.visit_char(c), + None => self.deserialize_any(visitor), + }, + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_builtin(BuiltinType::String, |s| visitor.visit_borrowed_str(s)) + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_leaf( + |typ| match *typ { + Type::App(ref func, ref args) if args.len() == 1 => { + **func == Type::Builtin(BuiltinType::Array) + && *args[0] == Type::Builtin(BuiltinType::Byte) + } + _ => false, + }, + |s| visitor.visit_bytes(s), + ) + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let typ = resolve::canonical_alias(self.state.env, self.typ, |alias| { + alias.name.name().as_str() == "std.types.Option" + }); + let option_inner_typ = match **typ { + Type::App(ref func, ref args) if args.len() == 1 => match **func { + Type::Alias(ref alias) if alias.name.name().as_str() == "std.types.Option" => { + Some(&args[0]) + } + _ => None, + }, + _ => None, + }; + // If the type is not `Option` we just visit the value itself + match option_inner_typ { + Some(typ) => match self.input.as_ref() { + ValueRef::Data(data) if data.tag() == 0 => visitor.visit_none(), + ValueRef::Data(data) if data.tag() == 1 => visitor.visit_some(&mut Deserializer { + state: self.state.clone(), + typ: typ, + input: data.get_variant(0).unwrap(), + }), + _ => self.deserialize_any(visitor), + }, + None => visitor.visit_some(self), + } + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Data(data) if data.tag() == 0 => visitor.visit_unit(), + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_unit_struct(self, name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + if name == "RawValueDeserialize" { + VALUE_TRANSFER.with(|t| { + let mut store = t.borrow_mut(); + assert!(store.is_none()); + *store = Some(self.state.thread.root_value(self.input.get_value())); + }); + visitor.visit_unit() + } else { + self.deserialize_unit(visitor) + } + } + + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let typ = resolve::remove_aliases_cow(self.state.env, self.typ); + match (self.input.as_ref(), &**typ) { + (ValueRef::Array(values), &Type::App(_, ref args)) if args.len() == 1 => visitor + .visit_seq(SeqDeserializer::new( + self.state.clone(), + values.iter().map(|variant| (variant, &args[0])), + )), + (ValueRef::Data(data), &Type::Variant(ref row)) => { + match row.row_iter().nth(data.tag() as usize) { + Some(field) => { + let iter = (0..data.len()) + .map(|i| data.get_variant(i).unwrap()) + .zip(arg_iter(&field.typ)); + visitor.visit_seq(SeqDeserializer::new(self.state.clone(), iter)) + } + None => self.deserialize_any(visitor), + } + } + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let typ = resolve::remove_aliases_cow(self.state.env, self.typ); + match (self.input.as_ref(), &**typ) { + (ValueRef::Data(ref data), &Type::Record { .. }) => { + let iter = typ.row_iter().flat_map(|field| { + data.lookup_field(self.state.thread, field.name.as_ref()) + .map(|variant| (variant, &field.name, &field.typ)) + }); + visitor.visit_map(MapDeserializer::new(self.state.clone(), iter)) + } + _ => self.deserialize_any(visitor), + } + } + + fn deserialize_struct( + self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_map(visitor) + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_enum(Enum::new(self)) + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_any(visitor) + } +} + +struct SeqDeserializer<'de, 't, I> { + state: State<'de>, + iter: I, + _marker: PhantomData<&'t ()>, +} + +impl<'de, 't, I> SeqDeserializer<'de, 't, I> { + fn new(state: State<'de>, iter: I) -> Self { + SeqDeserializer { + state: state, + iter: iter, + _marker: PhantomData, + } + } +} + +impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I> +where + I: Iterator, &'t ArcType)>, +{ + type Error = VmError; + + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: DeserializeSeed<'de>, + { + match self.iter.next() { + Some((value, typ)) => { + seed.deserialize(&mut Deserializer { + state: self.state.clone(), + input: value, + typ: typ, + }).map(Some) + } + None => Ok(None), + } + } +} + +struct MapDeserializer<'de, 't, I> { + state: State<'de>, + iter: I, + value: Option<(Variants<'de>, &'t ArcType)>, +} + +impl<'de, 't, I> MapDeserializer<'de, 't, I> { + fn new(state: State<'de>, iter: I) -> Self { + MapDeserializer { + state: state, + iter: iter, + value: None, + } + } +} + +impl<'de, 'a, 't, I> MapAccess<'de> for MapDeserializer<'de, 't, I> +where + I: Iterator, &'t Symbol, &'t ArcType)>, +{ + type Error = VmError; + + fn next_key_seed(&mut self, seed: K) -> Result> + where + K: DeserializeSeed<'de>, + { + match self.iter.next() { + Some((value, field, typ)) => { + self.value = Some((value, typ)); + seed.deserialize(field.as_ref().into_deserializer()) + .map(Some) + } + None => Ok(None), + } + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: DeserializeSeed<'de>, + { + match self.value.take() { + Some((value, typ)) => seed.deserialize(&mut Deserializer { + state: self.state.clone(), + input: value, + typ: typ, + }), + None => Err(Self::Error::custom("Unable to deserialize value")), + } + } +} + +struct Enum<'a, 'de: 'a, 't: 'a> { + de: &'a mut Deserializer<'de, 't>, +} + +impl<'a, 'de, 't> Enum<'a, 'de, 't> { + fn new(de: &'a mut Deserializer<'de, 't>) -> Self { + Enum { de: de } + } +} + +impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { + type Error = VmError; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.de.input.as_ref() { + ValueRef::Data(data) => visitor.visit_u32(data.tag()), + _ => Err(Self::Error::custom("Unable to deserialize tag")), + } + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let tag = match self.de.input.as_ref() { + ValueRef::Data(data) => data.tag(), + _ => return Err(Self::Error::custom("Unable to deserialize tag")), + }; + let typ = resolve::remove_aliases_cow(self.de.state.env, self.de.typ); + match **typ { + Type::Variant(ref variants) => { + let variant = variants + .row_iter() + .nth(tag as usize) + .ok_or_else(|| Self::Error::custom("Unable to deserialize tag"))?; + visitor.visit_str(variant.name.as_ref()) + } + _ => return Err(Self::Error::custom("Unable to deserialize tag")), + } + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char bytes + byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } +} + +impl<'a, 'de, 't> EnumAccess<'de> for Enum<'a, 'de, 't> { + type Error = VmError; + type Variant = Self; + + fn variant_seed(mut self, seed: V) -> Result<(V::Value, Self::Variant)> + where + V: DeserializeSeed<'de>, + { + seed.deserialize(&mut self).map(|value| (value, self)) + } +} + +impl<'de, 'a, 't> VariantAccess<'de> for Enum<'a, 'de, 't> { + type Error = VmError; + + fn unit_variant(self) -> Result<()> { + Ok(()) + } + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + let typ = resolve::remove_aliases_cow(self.de.state.env, self.de.typ); + match (self.de.input.as_ref(), &**typ) { + (ValueRef::Data(data), &Type::Variant(ref row)) => { + match row.row_iter().nth(data.tag() as usize) { + Some(field) => seed.deserialize(&mut Deserializer { + input: data.get_variant(0).ok_or_else(|| { + VmError::Message("Expected variant to have a value argument".into()) + })?, + typ: arg_iter(&field.typ).next().ok_or_else(|| { + VmError::Message("Expected variant to have a type argument".into()) + })?, + ..self.de.clone() + }), + None => seed.deserialize(self.de), + } + } + _ => seed.deserialize(self.de), + } + } + + fn tuple_variant(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + de::Deserializer::deserialize_seq(self.de, visitor) + } + + fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result + where + V: Visitor<'de>, + { + de::Deserializer::deserialize_seq(self.de, visitor) + } +} diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index 301177a7d1..d58953a628 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -174,11 +174,14 @@ impl<'a> Data<'a> { /// Creates an iterator over the fields of this value. pub fn iter(&self) -> ::value::VariantIter { - let fields = match self.0 { + ::value::variant_iter(self.fields()) + } + + fn fields(&self) -> &'a [Value] { + match self.0 { DataInner::Tag(_) => &[][..], DataInner::Data(data) => &data.fields, - }; - ::value::variant_iter(fields) + } } /// Retrieves the value of the field at `index`. This is useful if you cannot @@ -316,8 +319,8 @@ impl<'vm, T: VmType> Pushable<'vm> for Generic { Ok(()) } } -impl<'vm, T> Getable<'vm> for Generic { - fn from_value(_: &'vm Thread, value: Variants) -> Generic { +impl<'vm,'value, T> Getable<'vm, 'value> for Generic { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> Generic { Generic::from(value.get_value()) } } @@ -513,19 +516,19 @@ pub trait Pushable<'vm>: AsyncPushable<'vm> { } /// Trait which allows rust values to be retrieved from the virtual machine -pub trait Getable<'vm>: Sized { +pub trait Getable<'vm, 'value>: Sized { /// unsafe version of from_value which allows references to the internal of GcPtr's to be /// extracted if `value` is rooted - unsafe fn from_value_unsafe(vm: &'vm Thread, value: Variants) -> Self { + unsafe fn from_value_unsafe(vm: &'vm Thread, value: Variants<'value>) -> Self { Self::from_value(vm, value) } - fn from_value(vm: &'vm Thread, value: Variants) -> Self; + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self; } pub fn convert<'vm, T, U>(thread: &'vm Thread, t: T) -> Result where T: Pushable<'vm>, - U: Getable<'vm>, + U: for<'value> Getable<'vm, 'value>, { let mut context = thread.context(); t.push(thread, &mut context)?; @@ -544,8 +547,8 @@ impl<'vm, T: vm::Userdata> Pushable<'vm> for T { } } -impl<'vm> Getable<'vm> for Value { - fn from_value(_vm: &'vm Thread, value: Variants) -> Self { +impl<'vm, 'value> Getable<'vm, 'value> for Value { + fn from_value(_vm: &'vm Thread, value: Variants<'value>) -> Self { value.get_value() } } @@ -570,22 +573,22 @@ impl<'vm, T: ?Sized + VmType> VmType for &'vm T { } } -impl<'vm, T> Getable<'vm> for &'vm T +impl<'vm,'value, T> Getable<'vm, 'value> for &'vm T where T: vm::Userdata, { - unsafe fn from_value_unsafe(vm: &'vm Thread, value: Variants) -> Self { - let v = <*const T as Getable<'vm>>::from_value(vm, value); + unsafe fn from_value_unsafe(vm: &'vm Thread, value: Variants<'value>) -> Self { + let v = <*const T as Getable<'vm, 'value>>::from_value(vm, value); &*v } // Only allow the unsafe version to be used - fn from_value(_vm: &'vm Thread, _value: Variants) -> Self { + fn from_value(_vm: &'vm Thread, _value: Variants<'value>) -> Self { panic!("Getable::from_value on references is only allowed in unsafe contexts") } } -impl<'vm> Getable<'vm> for &'vm str { - unsafe fn from_value_unsafe(_vm: &'vm Thread, value: Variants) -> Self { +impl<'vm, 'value> Getable<'vm, 'value> for &'vm str { + unsafe fn from_value_unsafe(_vm: &'vm Thread, value: Variants<'value>) -> Self { match value.as_ref() { ValueRef::String(ref s) => forget_lifetime(s), _ => ice!("ValueRef is not a String"), @@ -593,7 +596,7 @@ impl<'vm> Getable<'vm> for &'vm str { } // Only allow the unsafe version to be used - fn from_value(_vm: &'vm Thread, _value: Variants) -> Self { + fn from_value(_vm: &'vm Thread, _value: Variants<'value>) -> Self { panic!("Getable::from_value on references is only allowed in unsafe contexts") } } @@ -628,16 +631,16 @@ where } } -impl<'vm, T> Getable<'vm> for WithVM<'vm, T> +impl<'vm, 'value, T> Getable<'vm, 'value> for WithVM<'vm, T> where - T: Getable<'vm>, + T: Getable<'vm, 'value>, { - unsafe fn from_value_unsafe(vm: &'vm Thread, value: Variants) -> WithVM<'vm, T> { + unsafe fn from_value_unsafe(vm: &'vm Thread, value: Variants<'value>) -> WithVM<'vm, T> { let t = T::from_value_unsafe(vm, value); WithVM { vm, value: t } } - fn from_value(vm: &'vm Thread, value: Variants) -> WithVM<'vm, T> { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> WithVM<'vm, T> { let t = T::from_value(vm, value); WithVM { vm, value: t } } @@ -652,7 +655,7 @@ impl<'vm> Pushable<'vm> for () { Ok(()) } } -impl<'vm> Getable<'vm> for () { +impl<'vm, 'value> Getable<'vm, 'value> for () { fn from_value(_: &'vm Thread, _: Variants) -> () { () } @@ -667,8 +670,8 @@ impl<'vm> Pushable<'vm> for u8 { Ok(()) } } -impl<'vm> Getable<'vm> for u8 { - fn from_value(_: &'vm Thread, value: Variants) -> u8 { +impl<'vm, 'value> Getable<'vm, 'value> for u8 { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> u8 { match value.as_ref() { ValueRef::Byte(i) => i, _ => ice!("ValueRef is not a Byte"), @@ -688,8 +691,8 @@ macro_rules! int_impls { Ok(()) } } - impl<'vm> Getable<'vm> for $id { - fn from_value(_: &'vm Thread, value: Variants) -> Self { + impl<'vm, 'value> Getable<'vm, 'value> for $id { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> Self { match value.as_ref() { ValueRef::Int(i) => i as $id, _ => ice!("expected ValueRef to be an Int, got {:?}", value.as_ref()), @@ -711,8 +714,8 @@ impl<'vm> Pushable<'vm> for f64 { Ok(()) } } -impl<'vm> Getable<'vm> for f64 { - fn from_value(_: &'vm Thread, value: Variants) -> f64 { +impl<'vm, 'value> Getable<'vm, 'value> for f64 { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> f64 { match value.as_ref() { ValueRef::Float(f) => f, _ => ice!("ValueRef is not a Float"), @@ -736,8 +739,8 @@ impl<'vm> Pushable<'vm> for bool { Ok(()) } } -impl<'vm> Getable<'vm> for bool { - fn from_value(_: &'vm Thread, value: Variants) -> bool { +impl<'vm, 'value> Getable<'vm, 'value> for bool { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> bool { match value.as_ref() { ValueRef::Data(data) => data.tag() == 1, _ => ice!("ValueRef is not a Bool"), @@ -765,8 +768,8 @@ impl<'vm> Pushable<'vm> for Ordering { Ok(()) } } -impl<'vm> Getable<'vm> for Ordering { - fn from_value(_: &'vm Thread, value: Variants) -> Ordering { +impl<'vm, 'value> Getable<'vm, 'value> for Ordering { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> Ordering { let tag = match value.as_ref() { ValueRef::Data(data) => data.tag(), _ => ice!("ValueRef is not an Ordering"), @@ -798,8 +801,8 @@ impl<'vm, 's> Pushable<'vm> for &'s str { Ok(()) } } -impl<'vm> Getable<'vm> for String { - fn from_value(_: &'vm Thread, value: Variants) -> String { +impl<'vm, 'value> Getable<'vm, 'value> for String { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> String { match value.as_ref() { ValueRef::String(i) => String::from(&i[..]), _ => ice!("ValueRef is not a String"), @@ -821,8 +824,8 @@ impl<'vm> Pushable<'vm> for char { Ok(()) } } -impl<'vm> Getable<'vm> for char { - fn from_value(_: &'vm Thread, value: Variants) -> char { +impl<'vm, 'value> Getable<'vm, 'value> for char { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> char { match value.as_ref() { ValueRef::Int(x) => match ::std::char::from_u32(x as u32) { Some(ch) => ch, @@ -875,8 +878,8 @@ where Ok(()) } } -impl<'s, 'vm, T: Copy + ArrayRepr> Getable<'vm> for &'s [T] { - unsafe fn from_value_unsafe(_: &'vm Thread, value: Variants) -> Self { +impl<'s, 'vm, 'value, T: Copy + ArrayRepr> Getable<'vm, 'value> for &'s [T] { + unsafe fn from_value_unsafe(_: &'vm Thread, value: Variants<'value>) -> Self { match value.as_ref() { ValueRef::Array(ptr) => { let s = ptr.0.as_slice().unwrap(); @@ -886,7 +889,7 @@ impl<'s, 'vm, T: Copy + ArrayRepr> Getable<'vm> for &'s [T] { } } // Only allow the unsafe version to be used - fn from_value(_vm: &'vm Thread, _value: Variants) -> Self { + fn from_value(_vm: &'vm Thread, _value: Variants<'value>) -> Self { ice!("Getable::from_value usage") } } @@ -938,8 +941,8 @@ impl<'s, T: VmType> VmType for *const T { } } -impl<'vm, T: vm::Userdata> Getable<'vm> for *const T { - fn from_value(_: &'vm Thread, value: Variants) -> *const T { +impl<'vm, 'value, T: vm::Userdata> Getable<'vm, 'value> for *const T { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> *const T { match value.as_ref() { ValueRef::Userdata(data) => { let x = data.downcast_ref::().unwrap(); @@ -980,8 +983,8 @@ impl<'vm, T: Pushable<'vm>> Pushable<'vm> for Option { Ok(()) } } -impl<'vm, T: Getable<'vm>> Getable<'vm> for Option { - fn from_value(vm: &'vm Thread, value: Variants) -> Option { +impl<'vm, 'value, T: Getable<'vm, 'value>> Getable<'vm, 'value> for Option { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Option { match value.as_ref() { ValueRef::Data(data) => if data.tag() == 0 { None @@ -1034,8 +1037,8 @@ impl<'vm, T: Pushable<'vm>, E: Pushable<'vm>> Pushable<'vm> for StdResult } } -impl<'vm, T: Getable<'vm>, E: Getable<'vm>> Getable<'vm> for StdResult { - fn from_value(vm: &'vm Thread, value: Variants) -> StdResult { +impl<'vm, 'value, T: Getable<'vm, 'value>, E: Getable<'vm, 'value>> Getable<'vm, 'value> for StdResult { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> StdResult { match value.as_ref() { ValueRef::Data(data) => match data.tag() { 0 => Err(E::from_value(vm, data.get_variant(0).unwrap())), @@ -1178,8 +1181,8 @@ where } } -impl<'vm, T: Getable<'vm>> Getable<'vm> for IO { - fn from_value(vm: &'vm Thread, value: Variants) -> IO { +impl<'vm, 'value, T: Getable<'vm, 'value>> Getable<'vm, 'value> for IO { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> IO { IO::Value(T::from_value(vm, value)) } } @@ -1293,14 +1296,14 @@ where } } -impl<'vm, V> Getable<'vm> for OpaqueValue<&'vm Thread, V> { - fn from_value(vm: &'vm Thread, value: Variants) -> OpaqueValue<&'vm Thread, V> { +impl<'vm, 'value, V> Getable<'vm, 'value> for OpaqueValue<&'vm Thread, V> { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> OpaqueValue<&'vm Thread, V> { OpaqueValue::from_value(vm.root_value(value.get_value())) } } -impl<'vm, V> Getable<'vm> for OpaqueValue { - fn from_value(vm: &'vm Thread, value: Variants) -> OpaqueValue { +impl<'vm, 'value, V> Getable<'vm, 'value> for OpaqueValue { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> OpaqueValue { OpaqueValue::from_value(vm.root_value(value.get_value())) } } @@ -1354,7 +1357,7 @@ impl<'vm, T> Array<'vm, T> { } } -impl<'vm, T: for<'vm2> Getable<'vm2>> Array<'vm, T> { +impl<'vm, T: for<'vm2, 'value> Getable<'vm2, 'value>> Array<'vm, T> { pub fn get(&self, index: VmInt) -> Option { match self.0.get_variant().as_ref() { ValueRef::Array(data) => { @@ -1386,8 +1389,8 @@ where } } -impl<'vm, T> Getable<'vm> for Array<'vm, T> { - fn from_value(vm: &'vm Thread, value: Variants) -> Array<'vm, T> { +impl<'vm, 'value, T> Getable<'vm, 'value> for Array<'vm, T> { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Array<'vm, T> { Array(vm.root_value(value.get_value()), PhantomData) } } @@ -1395,8 +1398,8 @@ impl<'vm, T> Getable<'vm> for Array<'vm, T> { impl<'vm, T: Any> VmType for Root<'vm, T> { type Type = T; } -impl<'vm, T: vm::Userdata> Getable<'vm> for Root<'vm, T> { - fn from_value(vm: &'vm Thread, value: Variants) -> Root<'vm, T> { +impl<'vm, 'value, T: vm::Userdata> Getable<'vm, 'value> for Root<'vm, T> { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Root<'vm, T> { match value.0 { ValueRepr::Userdata(data) => From::from(vm.root::(data).unwrap()), _ => ice!("Value is not a Root"), @@ -1407,8 +1410,8 @@ impl<'vm, T: vm::Userdata> Getable<'vm> for Root<'vm, T> { impl<'vm> VmType for RootStr<'vm> { type Type = ::Type; } -impl<'vm> Getable<'vm> for RootStr<'vm> { - fn from_value(vm: &'vm Thread, value: Variants) -> RootStr<'vm> { +impl<'vm, 'value> Getable<'vm, 'value> for RootStr<'vm> { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> RootStr<'vm> { match value.0 { ValueRepr::String(v) => vm.root_string(v), _ => ice!("Value is not a String"), @@ -1467,9 +1470,9 @@ macro_rules! define_tuple { } #[allow(non_snake_case)] - impl<'vm, $($id: Getable<'vm>),+> Getable<'vm> for ($($id),+) { + impl<'vm, 'value, $($id: Getable<'vm, 'value>),+> Getable<'vm, 'value> for ($($id),+) { #[allow(unused_assignments)] - fn from_value(vm: &'vm Thread, value: Variants) -> ($($id),+) { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> ($($id),+) { match value.as_ref() { ValueRef::Data(v) => { assert!(v.len() == count!($($id),+)); @@ -1530,7 +1533,7 @@ pub mod record { use base::symbol::Symbol; use base::types::{self, Alias, AliasData, ArcType, Generic, Type}; - use super::{Getable, Pushable, VmType}; + use super::{Getable, ValueRef, Pushable, VmType}; use thread::{self, Context, ThreadInternal}; use types::VmIndex; use value::{Def, Value, ValueRepr}; @@ -1566,8 +1569,8 @@ pub mod record { fn push(self, vm: &'vm Thread, fields: &mut Context) -> Result<()>; } - pub trait GetableFieldList<'vm>: HList + Sized { - fn from_value(vm: &'vm Thread, values: &[Value]) -> Option; + pub trait GetableFieldList<'vm, 'value>: HList + Sized { + fn from_value(vm: &'vm Thread, values: &'value [Value]) -> Option; } impl<'vm> PushableFieldList<'vm> for HNil { @@ -1576,8 +1579,8 @@ pub mod record { } } - impl<'vm> GetableFieldList<'vm> for HNil { - fn from_value(_vm: &'vm Thread, values: &[Value]) -> Option { + impl<'vm, 'value> GetableFieldList<'vm, 'value> for HNil { + fn from_value(_vm: &'vm Thread, values: &'value [Value]) -> Option { debug_assert!(values.is_empty(), "Retrieving type {:?}", values); Some(HNil) } @@ -1669,13 +1672,13 @@ pub mod record { } } - impl<'vm, F, H, T> GetableFieldList<'vm> for HCons<(F, H), T> + impl<'vm, 'value, F, H, T> GetableFieldList<'vm, 'value> for HCons<(F, H), T> where F: Field, - H: Getable<'vm> + VmType, - T: GetableFieldList<'vm>, + H: Getable<'vm, 'value> + VmType, + T: GetableFieldList<'vm, 'value>, { - fn from_value(vm: &'vm Thread, values: &[Value]) -> Option { + fn from_value(vm: &'vm Thread, values: &'value [Value]) -> Option { let head = unsafe { H::from_value(vm, Variants::new(&values[0])) }; T::from_value(vm, &values[1..]).map(move |tail| h_cons((F::default(), head), tail)) } @@ -1716,15 +1719,15 @@ pub mod record { Ok(()) } } - impl<'vm, T, U> Getable<'vm> for Record + impl<'vm, 'value, T, U> Getable<'vm, 'value> for Record where T: Default, - U: GetableFieldList<'vm>, + U: GetableFieldList<'vm, 'value>, { - fn from_value(vm: &'vm Thread, value: Variants) -> Self { - match value.0 { - ValueRepr::Data(ref data) => { - let fields = U::from_value(vm, &data.fields).unwrap(); + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self { + match value.as_ref() { + ValueRef::Data(ref data) => { + let fields = U::from_value(vm, data.fields()).unwrap(); Record { type_fields: T::default(), fields, @@ -1909,8 +1912,8 @@ where } } -impl<'vm, F> Getable<'vm> for Function<&'vm Thread, F> { - fn from_value(vm: &'vm Thread, value: Variants) -> Function<&'vm Thread, F> { +impl<'vm, 'value, F> Getable<'vm, 'value> for Function<&'vm Thread, F> { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Function<&'vm Thread, F> { Function { value: vm.root_value(value.get_value()), _marker: PhantomData, @@ -1918,8 +1921,8 @@ impl<'vm, F> Getable<'vm> for Function<&'vm Thread, F> { } } -impl<'vm, F> Getable<'vm> for Function { - fn from_value(vm: &'vm Thread, value: Variants) -> Self { +impl<'vm, 'value, F> Getable<'vm, 'value> for Function { + fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self { Function { value: vm.root_value(value.get_value()), _marker: PhantomData, @@ -1992,7 +1995,7 @@ 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,)* +where $($args: for<'value> Getable<'vm, 'value> + 'vm,)* R: AsyncPushable<'vm> + VmType + 'vm { #[allow(non_snake_case, unused_mut, unused_assignments, unused_variables, unused_unsafe)] @@ -2039,7 +2042,7 @@ 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,)* +where $($args: for<'value> Getable<'vm, 'value> + 'vm,)* R: AsyncPushable<'vm> + VmType + 'vm { #[allow(non_snake_case, unused_mut, unused_assignments, unused_variables, unused_unsafe)] @@ -2073,7 +2076,7 @@ where $($args: Getable<'vm> + 'vm,)* impl Function R> where $($args: for<'vm> Pushable<'vm>,)* T: Deref, - R: VmType + for<'x> Getable<'x>, + R: VmType + for<'x, 'value> Getable<'x, 'value>, { #[allow(non_snake_case)] pub fn call(&mut self $(, $args: $args)*) -> Result { @@ -2116,7 +2119,7 @@ impl Function R> impl Function R> where $($args: for<'vm> Pushable<'vm>,)* T: Deref + Clone + Send, - R: VmType + for<'x> Getable<'x> + Send + Sync + 'static, + R: VmType + for<'x, 'value> Getable<'x, 'value> + Send + Sync + 'static, { #[allow(non_snake_case)] pub fn call_async( diff --git a/vm/src/serialization.rs b/vm/src/serialization.rs index 6d7c7dba4b..d121f234f0 100644 --- a/vm/src/serialization.rs +++ b/vm/src/serialization.rs @@ -719,7 +719,7 @@ impl<'de> DeserializeState<'de, DeSeed> for ExternFunction { } let function = seed .thread - .get_global::>(&escaped_id) + .get_global::>(&escaped_id) .map_err(|err| D::Error::custom(err))?; unsafe { match function.get_value().get_repr() { diff --git a/vm/src/thread.rs b/vm/src/thread.rs index f672288c2b..f56e51172d 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -1,2074 +1,2074 @@ -//! The thread/vm type -use std::any::{Any, TypeId}; -use std::cmp::Ordering; -use std::fmt; -use std::mem; -use std::ops::{Add, Deref, DerefMut, Div, Mul, Sub}; -use std::result::Result as StdResult; -use std::string::String as StdString; -use std::sync::atomic::{self, AtomicBool}; -use std::sync::Arc; -use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use std::usize; - -use future::FutureValue; -use futures::{Async, Future, Poll}; - -use base::metadata::Metadata; -use base::pos::Line; -use base::symbol::Symbol; -use base::types::{self, Alias, ArcType}; - -use api::{Getable, Pushable, ValueRef, VmType}; -use compiler::UpvarInfo; -use gc::{DataDef, Gc, GcPtr, Generation, Move}; -use macros::MacroEnv; -use source_map::LocalIter; -use stack::{Frame, Lock, Stack, StackFrame, State}; -use types::*; -use value::{ - BytecodeFunction, Callable, ClosureData, ClosureDataDef, ClosureInitDef, Def, ExternFunction, - GcStr, PartialApplicationDataDef, RecordDef, Userdata, Value, ValueRepr, -}; -use vm::{GlobalVmState, GlobalVmStateBuilder, VmEnv}; -use {Error, Result, Variants}; - -use value::ValueRepr::{Closure, Data, Float, Function, Int, PartialApplication, String}; - -pub use gc::Traverseable; - -pub struct Execute { - thread: Option, -} - -impl Execute -where - T: Deref, -{ - pub fn new(thread: T) -> Execute { - Execute { - thread: Some(thread), - } - } -} - -impl Future for Execute -where - T: Deref, -{ - type Item = (T, Value); - type Error = Error; - - // Returns `T` so that it can be reused by the caller - fn poll(&mut self) -> Poll<(T, Value), Error> { - let value = { - let mut context = try_ready!( - self.thread - .as_ref() - .expect("cannot poll Execute future after it has succeded") - .resume() - ); - context.stack.pop() - }; - Ok(Async::Ready((self.thread.take().unwrap(), value))) - } -} - -/// Enum signaling a successful or unsuccess ful call to an extern function. -/// If an error occured the error message is expected to be on the top of the stack. -#[derive(Eq, PartialEq)] -#[repr(C)] -pub enum Status { - Ok, - Yield, - Error, -} - -/// A rooted value -#[derive(Clone)] -pub struct RootedValue -where - T: Deref, -{ - vm: T, - value: Value, -} - -impl Deref for RootedValue -where - T: Deref, -{ - type Target = Value; - fn deref(&self) -> &Value { - &self.value - } -} - -impl PartialEq> for RootedValue -where - T: Deref, - U: Deref, -{ - fn eq(&self, other: &RootedValue) -> bool { - self.value == other.value - } -} - -impl Drop for RootedValue -where - T: Deref, -{ - fn drop(&mut self) { - // TODO not safe if the root changes order of being dropped with another root - self.vm.rooted_values.write().unwrap().pop(); - } -} - -impl fmt::Debug for RootedValue -where - T: Deref, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.value) - } -} - -impl RootedValue -where - T: Deref, -{ - pub fn get_variant(&self) -> Variants { - unsafe { Variants::new(&self.value) } - } - - pub fn get_value(&self) -> Value { - self.value.clone() - } - - pub fn vm(&self) -> &Thread { - &self.vm - } - - pub fn clone_vm(&self) -> T - where - T: Clone, - { - self.vm.clone() - } - - /// looks up the field at the given offset - pub fn get<'vm>(&'vm self, index: usize) -> Option> - where - T: VmRoot<'vm>, - { - match self.get_variant().as_ref() { - ValueRef::Data(ref v) => v - .get_variant(index) - .map(|value| self.vm.root_value(value.get_value())), - _ => None, - } - } - - /// looks up the record field with the given name - pub fn get_field<'vm>(&'vm self, name: &str) -> Option> - where - T: VmRoot<'vm>, - { - match self.get_variant().as_ref() { - ValueRef::Data(ref v) => v - .lookup_field(&*self.vm, name) - .map(|value| self.vm.root_value(value.get_value())), - _ => None, - } - } - - pub fn as_ref(&self) -> RootedValue<&Thread> { - self.vm.root_value(self.value.clone()) - } -} - -impl<'vm> RootedValue<&'vm Thread> { - pub fn vm_(&self) -> &'vm Thread { - self.vm - } -} - -/// A rooted userdata value -pub struct Root<'vm, T: ?Sized + 'vm> { - roots: &'vm RwLock>>, - ptr: *const T, -} - -impl<'vm, T: ?Sized> Drop for Root<'vm, T> { - fn drop(&mut self) { - // TODO not safe if the root changes order of being dropped with another root - self.roots.write().unwrap().pop(); - } -} - -impl<'vm, T: ?Sized> Deref for Root<'vm, T> { - type Target = T; - fn deref(&self) -> &T { - unsafe { &*self.ptr } - } -} - -/// A rooted string -pub struct RootStr<'vm>(Root<'vm, str>); - -impl<'vm> Deref for RootStr<'vm> { - type Target = str; - fn deref(&self) -> &str { - &self.0 - } -} - -struct Roots<'b> { - vm: GcPtr, - stack: &'b Stack, -} -impl<'b> Traverseable for Roots<'b> { - fn traverse(&self, gc: &mut Gc) { - // Since this vm's stack is already borrowed in self we need to manually mark it to prevent - // it from being traversed normally - gc.mark(self.vm); - self.stack.traverse(gc); - - // Traverse the vm's fields, avoiding the stack which is traversed above - self.vm.traverse_fields_except_stack(gc); - } -} - -impl<'b> ::gc::CollectScope for Roots<'b> { - fn scope(&self, gc: &mut Gc, f: F) - where - F: FnOnce(&mut Gc), - { - // We need to pretend that the threads lives for longer than it does on the stack or we - // can't move the RwLockGuard into the vec. This does end up safe in the end because we - // never leak any lifetimes outside of this function - unsafe { - let locks = self.mark_child_roots(gc); - // Scan `self` sweep `gc` - f(gc); - - // `sweep` all child gcs - for (_, mut context, _) in locks { - context.gc.sweep(); - } - } - } -} - -impl<'b> Roots<'b> { - unsafe fn mark_child_roots( - &self, - gc: &mut Gc, - ) -> Vec<( - RwLockReadGuard>>, - MutexGuard, - GcPtr, - )> { - let mut stack: Vec> = Vec::new(); - let mut locks: Vec<(_, _, GcPtr)> = Vec::new(); - - let child_threads = self.vm.child_threads.read().unwrap(); - for child in &*child_threads { - Vec::push(&mut stack, *child); - } - - while let Some(thread_ptr) = stack.pop() { - if locks.iter().any(|&(_, _, lock_thread)| { - &*thread_ptr as *const Thread == &*lock_thread as *const Thread - }) { - continue; - } - - let thread = mem::transmute::<&Thread, &'static Thread>(&*thread_ptr); - let child_threads = thread.child_threads.read().unwrap(); - for child in &*child_threads { - Vec::push(&mut stack, *child); - } - - let context = thread.context.lock().unwrap(); - - // Since we locked the context we need to scan the thread using `Roots` rather than - // letting it be scanned normally - Roots { - vm: thread_ptr, - stack: &context.stack, - }.traverse(gc); - - Vec::push(&mut locks, (child_threads, context, thread_ptr)); - } - locks - } -} - -// All threads MUST be allocated in the garbage collected heap. This is necessary as a thread -// calling collect need to mark itself if it is on the garbage collected heap and it has no way of -// knowing wheter it is or not. So the only way of allowing it to mark itself is to disallow it to -// be allocated anywhere else. -/// Representation of the virtual machine -#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] -#[cfg_attr(feature = "serde_derive", serde(deserialize_state = "::serialization::DeSeed"))] -#[cfg_attr(feature = "serde_derive", serde(serialize_state = "::serialization::SeSeed"))] -pub struct Thread { - #[cfg_attr(feature = "serde_derive", serde(state_with = "::base::serialization::shared"))] - global_state: Arc, - // The parent of this thread, if it exists must live at least as long as this thread as this - // thread can refer to any value in the parent thread - #[cfg_attr(feature = "serde_derive", serde(state))] - parent: Option, - #[cfg_attr(feature = "serde_derive", serde(skip))] - roots: RwLock>>, - #[cfg_attr(feature = "serde_derive", serde(state))] - rooted_values: RwLock>, - /// All threads which this thread have spawned in turn. Necessary as this thread needs to scan - /// the roots of all its children as well since those may contain references to this threads - /// garbage collected values - #[cfg_attr(feature = "serde_derive", serde(state))] - child_threads: RwLock>>, - #[cfg_attr(feature = "serde_derive", serde(state))] - context: Mutex, - #[cfg_attr(feature = "serde_derive", serde(skip))] - interrupt: AtomicBool, -} - -impl fmt::Debug for Thread { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Thread({:p})", self) - } -} - -impl Userdata for Thread {} - -impl VmType for Thread { - type Type = Self; -} - -impl Traverseable for Thread { - fn traverse(&self, gc: &mut Gc) { - self.traverse_fields_except_stack(gc); - self.context.lock().unwrap().stack.traverse(gc); - } -} - -impl PartialEq for Thread { - fn eq(&self, other: &Thread) -> bool { - self as *const _ == other as *const _ - } -} - -impl VmType for RootedThread { - type Type = Thread; -} - -impl<'vm> Pushable<'vm> for RootedThread { - fn push(self, _vm: &'vm Thread, context: &mut Context) -> Result<()> { - context.stack.push(ValueRepr::Thread(self.0)); - Ok(()) - } -} - -impl<'vm> Getable<'vm> for RootedThread { - fn from_value(_: &'vm Thread, value: Variants) -> Self { - match value.as_ref() { - ValueRef::Thread(thread) => thread.root_thread(), - _ => ice!("ValueRef is not a Thread"), - } - } -} - -/// An instance of `Thread` which is rooted. See the `Thread` type for documentation on interacting -/// with the type. -#[derive(Debug)] -#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] -#[cfg_attr(feature = "serde_derive", serde(deserialize_state = "::serialization::DeSeed"))] -#[cfg_attr(feature = "serde_derive", serde(serialize_state = "::serialization::SeSeed"))] -pub struct RootedThread(#[cfg_attr(feature = "serde_derive", serde(state))] GcPtr); - -impl Drop for RootedThread { - fn drop(&mut self) { - let is_empty = { - let mut roots = self.parent_threads(); - let index = roots - .iter() - .position(|p| &**p as *const Thread == &*self.0 as *const Thread) - .expect("VM ptr"); - roots.swap_remove(index); - roots.is_empty() - }; - if self.parent.is_none() && is_empty { - // The last RootedThread was dropped, there is no way to refer to the global state any - // longer so drop everything - let mut gc_ref = self.0.global_state.gc.lock().unwrap(); - let gc_to_drop = ::std::mem::replace(&mut *gc_ref, Gc::new(Generation::default(), 0)); - // Make sure that the RefMut is dropped before the Gc itself as the RwLock is dropped - // when the Gc is dropped - drop(gc_ref); - drop(gc_to_drop); - } - } -} - -impl Deref for RootedThread { - type Target = Thread; - fn deref(&self) -> &Thread { - &self.0 - } -} - -impl Clone for RootedThread { - fn clone(&self) -> RootedThread { - self.root_thread() - } -} - -impl Traverseable for RootedThread { - fn traverse(&self, gc: &mut Gc) { - self.0.traverse(gc); - } -} - -impl RootedThread { - /// Creates a new virtual machine with an empty global environment - pub fn new() -> RootedThread { - RootedThread::with_global_state(GlobalVmStateBuilder::default().build()) - } - - pub fn with_global_state(mut global_state: GlobalVmState) -> RootedThread { - let thread = Thread { - parent: None, - context: Mutex::new(Context::new( - global_state.gc.get_mut().unwrap().new_child_gc(), - )), - global_state: Arc::new(global_state), - roots: RwLock::new(Vec::new()), - rooted_values: RwLock::new(Vec::new()), - child_threads: RwLock::new(Vec::new()), - interrupt: AtomicBool::new(false), - }; - let mut gc = Gc::new(Generation::default(), usize::MAX); - let vm = gc - .alloc(Move(thread)) - .expect("Not enough memory to allocate thread") - .root_thread(); - *vm.global_state.gc.lock().unwrap() = gc; - // Enter the top level scope - { - let mut context = vm.context.lock().unwrap(); - StackFrame::frame(&mut context.stack, 0, State::Unknown); - } - vm - } - - /// Converts a `RootedThread` into a raw pointer allowing to be passed through a C api. - /// The reference count for the thread is not modified - pub fn into_raw(self) -> *const Thread { - let ptr: *const Thread = &*self.0; - ::std::mem::forget(self); - ptr - } - - /// Converts a raw pointer into a `RootedThread`. - /// The reference count for the thread is not modified so it is up to the caller to ensure that - /// the count is correct. - pub unsafe fn from_raw(ptr: *const Thread) -> RootedThread { - RootedThread(GcPtr::from_raw(ptr)) - } -} - -impl Thread { - /// Spawns a new gluon thread with its own stack and heap but while still sharing the same - /// global environment - pub fn new_thread(&self) -> Result { - let vm = Thread { - global_state: self.global_state.clone(), - parent: Some(self.root_thread()), - context: Mutex::new(Context::new(self.current_context().gc.new_child_gc())), - roots: RwLock::new(Vec::new()), - rooted_values: RwLock::new(Vec::new()), - child_threads: RwLock::new(Vec::new()), - interrupt: AtomicBool::new(false), - }; - // Enter the top level scope - { - let mut context = vm.current_context(); - StackFrame::frame(&mut context.stack, 0, State::Unknown); - } - let ptr = self.context().gc.alloc(Move(vm))?; - - Ok(ptr.root_thread()) - } - - /// Roots `self`, extending the lifetime of this thread until at least the returned - /// `RootedThread` is droppped - pub fn root_thread(&self) -> RootedThread { - unsafe { - let vm = RootedThread(GcPtr::from_raw(self)); - vm.parent_threads().push(vm.0); - vm - } - } - - /// Creates a new global value at `name`. - /// Fails if a global called `name` already exists. - /// - /// # Examples - /// - /// Load the `factorial` rust function into gluon and evaluate `factorial 5` - /// - /// ``` - /// # extern crate gluon; - /// # #[macro_use] extern crate gluon_vm; - /// # use gluon::{new_vm,Compiler}; - /// # use gluon::base::types::Type; - /// fn factorial(x: i32) -> i32 { - /// if x <= 1 { 1 } else { x * factorial(x - 1) } - /// } - /// # fn main() { - /// - /// # if ::std::env::var("GLUON_PATH").is_err() { - /// # ::std::env::set_var("GLUON_PATH", ".."); - /// # } - /// - /// let vm = new_vm(); - /// - /// vm.define_global("factorial", primitive!(1 factorial)).unwrap(); - /// - /// let result = Compiler::new() - /// .run_expr_async::(&vm, "example", "factorial 5") - /// .sync_or_error() - /// .unwrap_or_else(|err| panic!("{}", err)); - /// let expected = (120, Type::int()); - /// - /// assert_eq!(result, expected); - /// # } - /// ``` - /// - #[deprecated(since = "0.7.0", note = "Use `gluon::import::add_extern_module` instead")] - pub fn define_global<'vm, T>(&'vm self, name: &str, value: T) -> Result<()> - where - T: Pushable<'vm> + VmType, - { - let value = { - let mut context = self.context(); - value.push(self, &mut context)?; - context.stack.pop() - }; - self.set_global( - Symbol::from(format!("@{}", name)), - T::make_forall_type(self), - Metadata::default(), - value, - ) - } - - /// Retrieves the global called `name`. - /// - /// # Examples - /// - /// Bind the `(+)` function in gluon's prelude standard library - /// to an `add` function in rust - /// - /// ```rust - /// # extern crate gluon; - /// # use gluon::{new_vm, Compiler, Thread}; - /// # use gluon::vm::api::{FunctionRef, Hole, OpaqueValue}; - /// # fn main() { - /// - /// # if ::std::env::var("GLUON_PATH").is_err() { - /// # ::std::env::set_var("GLUON_PATH", ".."); - /// # } - /// - /// let vm = new_vm(); - /// - /// Compiler::new() - /// .run_expr::>(&vm, "example", - /// r#" import! std.int "#) - /// .unwrap_or_else(|err| panic!("{}", err)); - /// let mut add: FunctionRef i32> = - /// vm.get_global("std.int.num.(+)").unwrap(); - /// let result = add.call(1, 2); - /// assert_eq!(result, Ok(3)); - /// # } - /// ``` - /// - /// # Errors - /// - /// if the global does not exist or it does not have the correct type. - /// - pub fn get_global<'vm, T>(&'vm self, name: &str) -> Result - where - T: Getable<'vm> + VmType, - { - use check::check_signature; - - let expected = T::make_type(self); - - let env = self.get_env(); - let (value, actual) = env.get_binding(name)?; - - // Finally check that type of the returned value is correct - if check_signature(&*env, &expected, &actual) { - unsafe { Ok(T::from_value(self, Variants::new(&value))) } - } else { - Err(Error::WrongType(expected, actual.into_owned())) - } - } - - /// Retrieves type information about the type `name`. Types inside records can be accessed - /// using dot notation (std.prelude.Option) - pub fn find_type_info(&self, name: &str) -> Result> { - let env = self.get_env(); - env.find_type_info(name).map(|alias| alias.into_owned()) - } - - /// Returns the gluon type that was bound to `T` - pub fn get_type(&self) -> Option { - self.global_env().get_type::() - } - - /// Registers the type `T` as being a gluon type called `name` with generic arguments `args` - pub fn register_type(&self, name: &str, args: &[&str]) -> Result { - self.global_env().register_type::(name, args) - } - pub fn register_type_as( - &self, - name: Symbol, - alias: Alias, - id: TypeId, - ) -> Result { - self.global_env().register_type_as(name, alias, id) - } - - /// Locks and retrieves the global environment of the vm - pub fn get_env<'b>(&'b self) -> RwLockReadGuard<'b, VmEnv> { - self.global_env().get_env() - } - - /// Retrieves the macros defined for this vm - pub fn get_macros(&self) -> &MacroEnv { - self.global_env().get_macros() - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn get_event_loop(&self) -> Option<::tokio_core::reactor::Remote> { - self.global_env().get_event_loop() - } - - /// Runs a garbage collection. - pub fn collect(&self) { - let mut context = self.current_context(); - self.with_roots(&mut context, |gc, roots| unsafe { - gc.collect(roots); - }) - } - - /// Pushes a value to the top of the stack - pub fn push<'vm, T>(&'vm self, v: T) -> Result<()> - where - T: Pushable<'vm>, - { - let mut context = self.current_context(); - v.push(self, &mut context) - } - - /// Removes the top value from the stack - pub fn pop(&self) { - self.current_context().stack.pop(); - } - - pub fn set_memory_limit(&self, memory_limit: usize) { - self.current_context().gc.set_memory_limit(memory_limit) - } - - pub fn interrupt(&self) { - self.interrupt.store(true, atomic::Ordering::Relaxed) - } - - pub fn interrupted(&self) -> bool { - self.interrupt.load(atomic::Ordering::Relaxed) - } - - fn current_context(&self) -> OwnedContext { - self.context() - } - - fn traverse_fields_except_stack(&self, gc: &mut Gc) { - self.global_state.traverse(gc); - self.roots.read().unwrap().traverse(gc); - self.rooted_values.read().unwrap().traverse(gc); - self.child_threads.read().unwrap().traverse(gc); - } - - fn parent_threads(&self) -> RwLockWriteGuard>> { - match self.parent { - Some(ref parent) => parent.child_threads.write().unwrap(), - None => self.global_state.generation_0_threads.write().unwrap(), - } - } - - fn with_roots(&self, context: &mut Context, f: F) -> R - where - F: for<'b> FnOnce(&mut Gc, Roots<'b>) -> R, - { - // For this to be safe we require that the received stack is the same one that is in this - // VM - { - let self_context: *const _ = &self.context; - let context: *const _ = context; - assert!(unsafe { - context as usize >= self_context as usize - && context as usize <= self_context.offset(1) as usize - }); - } - let roots = Roots { - vm: unsafe { - // Threads must only be on the garbage collectors heap which makes this safe - GcPtr::from_raw(self) - }, - stack: &context.stack, - }; - f(&mut context.gc, roots) - } -} - -pub trait VmRoot<'a>: Deref + Clone + 'a { - fn root(thread: &'a Thread) -> Self; - - /// Roots a value - fn root_value_with_self(self, value: Value) -> RootedValue { - self.rooted_values.write().unwrap().push(value.clone()); - RootedValue { - vm: self, - value: value, - } - } -} - -impl<'a> VmRoot<'a> for &'a Thread { - fn root(thread: &'a Thread) -> Self { - thread - } -} - -impl<'a> VmRoot<'a> for RootedThread { - fn root(thread: &'a Thread) -> Self { - thread.root_thread() - } -} - -/// Internal functions for interacting with threads. These functions should be considered both -/// unsafe and unstable. -pub trait ThreadInternal -where - for<'a> &'a Self: Deref, -{ - /// Locks and retrives this threads stack - fn context(&self) -> OwnedContext; - - /// Roots a userdata - fn root<'vm, T: Userdata>(&'vm self, v: GcPtr>) -> Option>; - - /// Roots a string - fn root_string<'vm>(&'vm self, ptr: GcStr) -> RootStr<'vm>; - - /// Roots a value - fn root_value<'vm, T>(&'vm self, value: Value) -> RootedValue - where - T: VmRoot<'vm>; - - /// Evaluates a zero argument function (a thunk) - fn call_thunk(&self, closure: GcPtr) -> FutureValue>; - - /// Executes an `IO` action - fn execute_io(&self, value: Value) -> FutureValue>; - - /// Calls a function on the stack. - /// When this function is called it is expected that the function exists at - /// `stack.len() - args - 1` and that the arguments are of the correct type - fn call_function<'b>( - &'b self, - stack: OwnedContext<'b>, - args: VmIndex, - ) -> Result>>>; - - fn resume(&self) -> Result>; - - fn global_env(&self) -> &Arc; - - fn set_global( - &self, - name: Symbol, - typ: ArcType, - metadata: Metadata, - value: Value, - ) -> Result<()>; - - /// `owner` is theread that owns `value` which is not necessarily the same as `self` - fn deep_clone_value(&self, owner: &Thread, value: Value) -> Result; - - fn can_share_values_with(&self, gc: &mut Gc, other: &Thread) -> bool; -} - -impl ThreadInternal for Thread { - fn context(&self) -> OwnedContext { - OwnedContext { - thread: self, - context: self.context.lock().unwrap(), - } - } - /// Roots a userdata - fn root<'vm, T: Userdata>(&'vm self, v: GcPtr>) -> Option> { - v.downcast_ref::().map(|ptr| { - self.roots.write().unwrap().push(v.as_traverseable()); - Root { - roots: &self.roots, - ptr: ptr, - } - }) - } - - /// Roots a string - fn root_string<'vm>(&'vm self, ptr: GcStr) -> RootStr<'vm> { - self.roots - .write() - .unwrap() - .push(ptr.into_inner().as_traverseable()); - RootStr(Root { - roots: &self.roots, - ptr: &*ptr, - }) - } - - /// Roots a value - fn root_value<'vm, T>(&'vm self, value: Value) -> RootedValue - where - T: VmRoot<'vm>, - { - self.rooted_values.write().unwrap().push(value.clone()); - RootedValue { - vm: T::root(self), - value: value, - } - } - - fn call_thunk(&self, closure: GcPtr) -> FutureValue> { - let mut context = self.current_context(); - context.stack.push(Closure(closure)); - context.borrow_mut().enter_scope(0, State::Closure(closure)); - let async = try_future!(context.execute(false)); - match async { - Async::Ready(context) => FutureValue::Value(Ok((self, context.unwrap().stack.pop()))), - Async::NotReady => FutureValue::Future(Execute::new(self)), - } - } - - /// Calls a module, allowed to to run IO expressions - fn execute_io(&self, value: Value) -> FutureValue> { - debug!("Run IO {:?}", value); - let mut context = self.context(); - // Dummy value to fill the place of the function for TailCall - context.stack.push(Int(0)); - - context.stack.push(value); - context.stack.push(Int(0)); - - context.borrow_mut().enter_scope(2, State::Unknown); - context = match try_future!(self.call_function(context, 1)) { - Async::Ready(context) => context.expect("call_module to have the stack remaining"), - Async::NotReady => return FutureValue::Future(Execute::new(self)), - }; - let result = context.stack.pop(); - { - let mut context = context.borrow_mut(); - while context.stack.len() > 0 { - context.stack.pop(); - } - } - let _ = context.exit_scope(); - FutureValue::Value(Ok((self, result))) - } - - /// Calls a function on the stack. - /// When this function is called it is expected that the function exists at - /// `stack.len() - args - 1` and that the arguments are of the correct type - fn call_function<'b>( - &'b self, - mut context: OwnedContext<'b>, - args: VmIndex, - ) -> Result>>> { - context.borrow_mut().do_call(args)?; - context.execute(false) - } - - fn resume(&self) -> Result> { - let mut context = self.current_context(); - if context.stack.get_frames().len() == 1 { - // Only the top level frame left means that the thread has finished - return Err(Error::Dead); - } - context = try_ready!(context.execute(true)).unwrap(); - Ok(Async::Ready(context)) - } - - fn global_env(&self) -> &Arc { - &self.global_state - } - - fn set_global( - &self, - name: Symbol, - typ: ArcType, - metadata: Metadata, - value: Value, - ) -> Result<()> { - let value = ::value::Cloner::new(self, &mut self.global_env().gc.lock().unwrap()) - .deep_clone(&value)?; - self.global_env().set_global(name, typ, metadata, value) - } - - fn deep_clone_value(&self, owner: &Thread, value: Value) -> Result { - let mut context = self.current_context(); - let full_clone = !self.can_share_values_with(&mut context.gc, owner); - let mut cloner = ::value::Cloner::new(self, &mut context.gc); - if full_clone { - cloner.force_full_clone(); - } - cloner.deep_clone(&value) - } - - fn can_share_values_with(&self, gc: &mut Gc, other: &Thread) -> bool { - if self as *const Thread == other as *const Thread { - return true; - } - // If the threads do not share the same global state then they are disjoint and can't share - // values - if &*self.global_state as *const GlobalVmState - != &*other.global_state as *const GlobalVmState - { - return false; - } - // Otherwise the threads might be able to share values but only if they are on the same - // of the generation tree (see src/gc.rs) - // Search from the thread which MAY be a child to the parent. If `parent` could not be - // found then the threads must be in different branches of the tree - let self_gen = gc.generation(); - let other_gen = other.context.lock().unwrap().gc.generation(); - let (parent, mut child) = if self_gen.is_parent_of(other_gen) { - (self, other) - } else { - (other, self) - }; - while let Some(ref next) = child.parent { - if &**next as *const Thread == parent as *const Thread { - return true; - } - child = next; - } - false - } -} - -pub type HookFn = Box Result> + Send + Sync>; - -pub struct DebugInfo<'a> { - stack: &'a Stack, - state: HookFlags, -} - -pub struct StackInfo<'a> { - info: &'a DebugInfo<'a>, - index: usize, -} - -impl<'a> DebugInfo<'a> { - /// Returns the reason for the hook being called - pub fn state(&self) -> HookFlags { - self.state - } - - /// Returns a struct which can be queried about information about the stack - /// at a specific level where `0` is the currently executing frame. - pub fn stack_info(&self, level: usize) -> Option { - let frames = self.stack.get_frames(); - if level < frames.len() { - Some(StackInfo { - info: self, - index: frames.len() - level - 1, - }) - } else { - None - } - } - - pub fn stack_info_len(&self) -> usize { - self.stack.get_frames().len() - } -} - -impl<'a> StackInfo<'a> { - fn frame(&self) -> &Frame { - &self.info.stack.get_frames()[self.index] - } - - // For frames except the top we subtract one to account for the `Call` instruction adding one - fn instruction_index(&self) -> usize { - if self.info.stack.get_frames().len() - 1 == self.index { - self.frame().instruction_index - } else { - self.frame().instruction_index - 1 - } - } - - /// Returns the line which create the current instruction of this frame - pub fn line(&self) -> Option { - let frame = self.frame(); - match frame.state { - State::Closure(ref closure) => closure - .function - .debug_info - .source_map - .line(self.instruction_index()), - _ => None, - } - } - - /// Returns the name of the source which defined the funtion executing at this frame - pub fn source_name(&self) -> &str { - match self.frame().state { - State::Closure(ref closure) => &closure.function.debug_info.source_name, - _ => "", - } - } - - /// Returns the name of the function executing at this frame - pub fn function_name(&self) -> Option<&str> { - match self.frame().state { - State::Unknown | State::Lock | State::Excess => None, - State::Closure(ref closure) => Some(closure.function.name.declared_name()), - State::Extern(ref function) => Some(function.id.declared_name()), - } - } - - /// Returns an iterator over all locals available at the current executing instruction - pub fn locals(&self) -> LocalIter { - let frame = self.frame(); - match frame.state { - State::Closure(ref closure) => closure - .function - .debug_info - .local_map - .locals(self.instruction_index()), - _ => LocalIter::empty(), - } - } - - /// Returns a slice with information about the values bound to this closure - pub fn upvars(&self) -> &[UpvarInfo] { - match self.frame().state { - State::Closure(ref closure) => &closure.function.debug_info.upvars, - _ => ice!("Attempted to access upvar in non closure function"), - } - } -} - -bitflags! { - #[derive(Default)] - pub struct HookFlags: u8 { - /// Call the hook when execution moves to a new line - const LINE_FLAG = 0b01; - /// Call the hook when a function is called - const CALL_FLAG = 0b10; - } -} - -#[derive(Default)] -struct Hook { - function: Option, - flags: HookFlags, - // The index of the last executed instruction - previous_instruction_index: usize, -} - -#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] -#[cfg_attr(feature = "serde_derive", serde(deserialize_state = "::serialization::DeSeed"))] -#[cfg_attr(feature = "serde_derive", serde(serialize_state = "::serialization::SeSeed"))] -pub struct Context { - // FIXME It is dangerous to write to gc and stack - #[cfg_attr(feature = "serde_derive", serde(state))] - pub stack: Stack, - #[cfg_attr(feature = "serde_derive", serde(state))] - pub gc: Gc, - #[cfg_attr(feature = "serde_derive", serde(skip))] - hook: Hook, - max_stack_size: VmIndex, - - /// Stack of polling functions used for extern functions returning futures - #[cfg_attr(feature = "serde_derive", serde(skip))] - poll_fns: Vec<( - Option, - Box FnMut(&'vm Thread) -> Result>> + Send>, - )>, -} - -impl Context { - fn new(gc: Gc) -> Context { - Context { - gc: gc, - stack: Stack::new(), - hook: Hook { - function: None, - flags: HookFlags::empty(), - previous_instruction_index: usize::max_value(), - }, - max_stack_size: VmIndex::max_value(), - poll_fns: Vec::new(), - } - } - - pub fn new_data(&mut self, thread: &Thread, tag: VmTag, fields: &[Value]) -> Result { - self.alloc_with( - thread, - Def { - tag: tag, - elems: fields, - }, - ).map(ValueRepr::Data) - .map(Value::from) - } - - pub fn alloc_with(&mut self, thread: &Thread, data: D) -> Result> - where - D: DataDef + Traverseable, - D::Value: Sized + Any, - { - alloc(&mut self.gc, thread, &self.stack, data) - } - - pub fn alloc_ignore_limit(&mut self, data: D) -> GcPtr - where - D: DataDef + Traverseable, - D::Value: Sized + Any, - { - self.gc.alloc_ignore_limit(data) - } - - pub fn set_hook(&mut self, hook: Option) -> Option { - mem::replace(&mut self.hook.function, hook) - } - - pub fn set_hook_mask(&mut self, flags: HookFlags) { - self.hook.flags = flags; - } - - pub fn set_max_stack_size(&mut self, limit: VmIndex) { - self.max_stack_size = limit; - } - - /// "Returns a future", letting the virtual machine know that `future` must be resolved to - /// produce the actual value. - /// - /// # Safety - /// - /// This function is unsafe because the `vm` lifetime must not outlive the lifetime of the - /// `Thread` - pub unsafe fn return_future<'vm, F>(&mut self, mut future: F, lock: Lock) - where - F: Future + Send + 'static, - F::Item: Pushable<'vm>, - { - use std::mem::transmute; - - let lock = if self.poll_fns.is_empty() { - self.stack.release_lock(lock); - None - } else { - Some(lock) - }; - - self.poll_fns.push(( - lock, - Box::new(move |vm| { - let value = try_ready!(future.poll()); - - let mut context = vm.current_context(); - let vm = transmute::<&Thread, &'vm Thread>(vm); - value.push(vm, &mut context).map(|()| Async::Ready(context)) - }), - )); - } -} - -impl<'b> OwnedContext<'b> { - pub fn alloc(&mut self, data: D) -> Result> - where - D: DataDef + Traverseable, - D::Value: Sized + Any, - { - let Context { - ref mut gc, - ref stack, - .. - } = **self; - alloc(gc, self.thread, &stack, data) - } - - pub fn debug_info(&self) -> DebugInfo { - DebugInfo { - stack: &self.stack, - state: HookFlags::empty(), - } - } -} - -pub fn alloc(gc: &mut Gc, thread: &Thread, stack: &Stack, def: D) -> Result> -where - D: DataDef + Traverseable, - D::Value: Sized + Any, -{ - let roots = Roots { - vm: unsafe { - // Threads must only be on the garbage collectors heap which makes this safe - GcPtr::from_raw(thread) - }, - stack: stack, - }; - unsafe { gc.alloc_and_collect(roots, def) } -} - -pub struct OwnedContext<'b> { - thread: &'b Thread, - context: MutexGuard<'b, Context>, -} - -impl<'b> Deref for OwnedContext<'b> { - type Target = Context; - fn deref(&self) -> &Context { - &self.context - } -} - -impl<'b> DerefMut for OwnedContext<'b> { - fn deref_mut(&mut self) -> &mut Context { - &mut self.context - } -} - -const INITIAL_CALL: usize = 0; -const POLL_CALL: usize = 1; -const IN_POLL: usize = 2; - -impl<'b> OwnedContext<'b> { - fn exit_scope(mut self) -> StdResult, ()> { - let exists = StackFrame::current(&mut self.stack).exit_scope().is_ok(); - if exists { - Ok(self) - } else { - Err(()) - } - } - - fn execute(self, polled: bool) -> Result>>> { - let mut maybe_context = Some(self); - while let Some(mut context) = maybe_context { - if context.thread.interrupted() { - return Err(Error::Interrupted); - } - debug!("STACK\n{:?}", context.stack.get_frames()); - let state = context.borrow_mut().stack.frame.state; - - let instruction_index = context.borrow_mut().stack.frame.instruction_index; - if instruction_index == 0 && context.hook.flags.contains(HookFlags::CALL_FLAG) { - match state { - State::Extern(_) | State::Closure(_) => { - let thread = context.thread; - let context = &mut *context; - if let Some(ref mut hook) = context.hook.function { - let info = DebugInfo { - stack: &context.stack, - state: HookFlags::CALL_FLAG, - }; - try_ready!(hook(thread, info)) - } - } - _ => (), - } - } - - maybe_context = match state { - State::Lock | State::Unknown => return Ok(Async::Ready(Some(context))), - State::Excess => context.exit_scope().ok(), - State::Extern(ext) => { - let instruction_index = context.borrow_mut().stack.frame.instruction_index; - if instruction_index == IN_POLL { - return Ok(Async::Ready(Some(context))); - } - context.borrow_mut().stack.frame.instruction_index = POLL_CALL; - Some(try_ready!(context.execute_function( - instruction_index == INITIAL_CALL, - &ext, - polled, - ))) - } - State::Closure(closure) => { - let max_stack_size = context.max_stack_size; - // Tail calls into extern functions at the top level will drop the last - // stackframe so just return immedietly - enum State { - Exists, - DoesNotExist, - ReturnContext, - } - let state = { - let mut context = context.borrow_mut(); - - let instruction_index = context.stack.frame.instruction_index; - let function_size = closure.function.max_stack_size; - - // Before entering a function check that the stack cannot exceed `max_stack_size` - if instruction_index == 0 - && context.stack.stack.len() + function_size > max_stack_size - { - return Err(Error::StackOverflow(max_stack_size)); - } - - if context.stack.stack.get_frames().len() == 0 { - State::ReturnContext - } else { - info!( - "Continue with {}\nAt: {}/{}\n{:?}", - closure.function.name, - instruction_index, - closure.function.instructions.len(), - &context.stack[..] - ); - - let new_context = try_ready!(context.execute_( - instruction_index, - &closure.function.instructions, - &closure.function, - )); - if new_context.is_some() { - State::Exists - } else { - State::DoesNotExist - } - } - }; - match state { - State::Exists => Some(context), - State::DoesNotExist => None, - State::ReturnContext => return Ok(Async::Ready(Some(context))), - } - } - }; - } - Ok(Async::Ready(maybe_context)) - } - - fn execute_function( - mut self, - initial_call: bool, - function: &ExternFunction, - polled: bool, - ) -> Result>> { - info!( - "CALL EXTERN {} {:?}", - function.id, - &self.stack.current_frame()[..], - ); - - let mut status = Status::Ok; - if initial_call { - // Make sure that the stack is not borrowed during the external function call - // Necessary since we do not know what will happen during the function call - let thread = self.thread; - drop(self); - status = (function.function)(thread); - - if status == Status::Yield { - return Ok(Async::NotReady); - } - - self = thread.current_context(); - - if status == Status::Error { - return match self.stack.pop().get_repr() { - String(s) => Err(Error::Panic(s.to_string(), Some(self.stack.stacktrace(0)))), - _ => Err(Error::Message(format!( - "Unexpected error calling function `{}`", - function.id - ))), - }; - } - - // The `poll_fn` at the top may be for a stack frame at a lower level, return to the - // state loop to ensure that we are executing the frame at the top of the stack - if !self.poll_fns.is_empty() { - return Ok(Async::Ready(self)); - } - } - while let Some((lock, mut poll_fn)) = self.poll_fns.pop() { - // We can only poll the future if the code is currently executing in a future - if !polled { - self.poll_fns.push((lock, poll_fn)); - return Ok(Async::NotReady); - } - - let frame_offset = self.stack.get_frames().len() - 1; - if self.poll_fns.is_empty() { - self.stack.get_frames_mut()[frame_offset].instruction_index = IN_POLL; - } - let thread = self.thread; - drop(self); - // Poll the future that was returned from the initial call to this extern function - match poll_fn(thread)? { - Async::Ready(context) => { - self = context; - if let Some(lock) = lock { - self.stack.release_lock(lock); - } - self.borrow_mut().stack.frame.instruction_index = POLL_CALL; - return Ok(Async::Ready(self)); - } - Async::NotReady => { - self = thread.current_context(); - self.stack.get_frames_mut()[frame_offset].instruction_index = POLL_CALL; - // Restore `poll_fn` so it can be polled again - self.poll_fns.push((lock, poll_fn)); - return Ok(Async::NotReady); - } - } - } - - // The function call is done at this point so remove any extra values from the frame and - // return the value at the top of the stack - let result = self.stack.pop(); - { - let mut stack = self.stack.current_frame(); - while stack.len() > 0 { - debug!("{} {:?}", stack.len(), &*stack); - stack.pop(); - } - if !(match stack.frame.state { - State::Extern(ref e) => e.id == function.id, - _ => false, - }) { - "asd".to_string(); - } - debug_assert!( - match stack.frame.state { - State::Extern(ref e) => e.id == function.id, - _ => false, - }, - "Attempted to pop {:?} but {} was expected", - stack.frame, - function.id - ) - } - self = self.exit_scope().map_err(|_| { - Error::Message(StdString::from("Poped the last frame in execute_function")) - })?; - self.stack.pop(); // Pop function - self.stack.push(result); - - info!( - "EXIT EXTERN {} {:?}", - function.id, - &self.stack.current_frame()[..] - ); - - match status { - Status::Ok => Ok(Async::Ready(self)), - Status::Yield => Ok(Async::NotReady), - Status::Error => match self.stack.pop().get_repr() { - String(s) => Err(Error::Panic(s.to_string(), Some(self.stack.stacktrace(0)))), - _ => Err(Error::Message(format!( - "Unexpected error calling function `{}`", - function.id - ))), - }, - } - } - - fn borrow_mut(&mut self) -> ExecuteContext { - let context = &mut **self; - ExecuteContext { - thread: self.thread, - gc: &mut context.gc, - stack: StackFrame::current(&mut context.stack), - hook: &mut context.hook, - } - } -} - -struct ExecuteContext<'b> { - thread: &'b Thread, - stack: StackFrame<'b>, - gc: &'b mut Gc, - hook: &'b mut Hook, -} - -impl<'b> ExecuteContext<'b> { - fn enter_scope(&mut self, args: VmIndex, state: State) { - self.stack.enter_scope(args, state); - self.hook.previous_instruction_index = usize::max_value(); - } - - fn exit_scope(&mut self) -> StdResult<(), ()> { - match self.stack.exit_scope() { - Ok(_) => { - if self.hook.flags.bits() != 0 { - // Subtract 1 to compensate for the `Call` instruction adding one earlier - // ensuring that the line hook runs after function calls - self.hook.previous_instruction_index = - self.stack.frame.instruction_index.saturating_sub(1); - } - Ok(()) - } - Err(_) => Err(()), - } - } - - fn execute_callable(&mut self, function: &Callable, excess: bool) -> Result<()> { - match *function { - Callable::Closure(closure) => { - self.enter_scope(closure.function.args, State::Closure(closure)); - self.stack.frame.excess = excess; - Ok(()) - } - Callable::Extern(ref ext) => { - assert!(self.stack.len() >= ext.args + 1); - let function_index = self.stack.len() - ext.args - 1; - debug!("------- {} {:?}", function_index, &self.stack[..]); - self.enter_scope(ext.args, State::Extern(*ext)); - Ok(()) - } - } - } - - fn call_function_with_upvars( - &mut self, - args: VmIndex, - required_args: VmIndex, - callable: Callable, - ) -> Result<()> { - debug!("cmp {} {} {:?} {:?}", args, required_args, callable, { - let function_index = self.stack.len() - 1 - args; - &(*self.stack)[(function_index + 1) as usize..] - }); - match args.cmp(&required_args) { - Ordering::Equal => self.execute_callable(&callable, false), - Ordering::Less => { - let app = { - let fields = &self.stack[self.stack.len() - args..]; - let def = PartialApplicationDataDef(callable, fields); - PartialApplication(alloc(&mut self.gc, self.thread, &self.stack.stack, def)?) - }; - for _ in 0..(args + 1) { - self.stack.pop(); - } - self.stack.push(app); - Ok(()) - } - Ordering::Greater => { - let excess_args = args - required_args; - let d = { - let fields = &self.stack[self.stack.len() - excess_args..]; - alloc( - &mut self.gc, - self.thread, - &self.stack.stack, - Def { - tag: 0, - elems: fields, - }, - )? - }; - for _ in 0..excess_args { - self.stack.pop(); - } - // Insert the excess args before the actual closure so it does not get - // collected - let offset = self.stack.len() - required_args - 1; - self.stack.insert_slice(offset, &[Value::from(Data(d))]); - debug!( - "xxxxxx {:?}\n{:?}", - &(*self.stack)[..], - self.stack.stack.get_frames() - ); - self.execute_callable(&callable, true) - } - } - } - - fn do_call(&mut self, args: VmIndex) -> Result<()> { - let function_index = self.stack.len() - 1 - args; - info!( - "Do call {:?} {:?}", - self.stack[function_index], - &(*self.stack)[(function_index + 1) as usize..] - ); - match self.stack[function_index].clone().get_repr() { - Function(ref f) => { - let callable = Callable::Extern(f.clone()); - self.call_function_with_upvars(args, f.args, callable) - } - Closure(ref closure) => { - let callable = Callable::Closure(closure.clone()); - self.call_function_with_upvars(args, closure.function.args, callable) - } - PartialApplication(app) => { - let total_args = app.args.len() as VmIndex + args; - let offset = self.stack.len() - args; - self.stack.insert_slice(offset, &app.args); - self.call_function_with_upvars(total_args, app.function.args(), app.function) - } - x => Err(Error::Message(format!("Cannot call {:?}", x))), - } - } - - fn execute_( - &mut self, - mut index: usize, - instructions: &[Instruction], - function: &BytecodeFunction, - ) -> Result>> { - { - debug!( - ">>>\nEnter frame {}: {:?}\n{:?}", - function.name, - &self.stack[..], - self.stack.frame - ); - } - while let Some(&instr) = instructions.get(index) { - debug_instruction(&self.stack, index, instr); - - if self.hook.flags.contains(HookFlags::LINE_FLAG) { - if let Some(ref mut hook) = self.hook.function { - let current_line = function.debug_info.source_map.line(index); - let previous_line = function - .debug_info - .source_map - .line(self.hook.previous_instruction_index); - self.hook.previous_instruction_index = index; - if current_line != previous_line { - self.stack.frame.instruction_index = index; - self.stack.store_frame(); - let info = DebugInfo { - stack: &self.stack.stack, - state: HookFlags::LINE_FLAG, - }; - try_ready!(hook(self.thread, info)) - } - } - } - - match instr { - Push(i) => { - let v = self.stack[i].clone(); - self.stack.push(v); - } - PushInt(i) => { - self.stack.push(Int(i)); - } - PushByte(b) => { - self.stack.push(ValueRepr::Byte(b)); - } - PushString(string_index) => { - self.stack - .push(String(function.strings[string_index as usize].inner())); - } - PushFloat(f) => self.stack.push(Float(f)), - Call(args) => { - self.stack.frame.instruction_index = index + 1; - return self.do_call(args).map(|x| Async::Ready(Some(x))); - } - TailCall(mut args) => { - let mut amount = self.stack.len() - args; - if self.stack.frame.excess { - amount += 1; - match self.stack.excess_args() { - Some(excess) => { - debug!("TailCall: Push excess args {:?}", excess.fields); - for value in &excess.fields { - self.stack.push(value); - } - args += excess.fields.len() as VmIndex; - } - None => ice!("Expected excess args"), - } - } - debug_assert!( - match self.stack.frame.state { - State::Closure(ref c) => c.function.name == function.name, - _ => false, - }, - "Attempted to pop {:?} but `{}` was expected", - self.stack.frame.state, - function.name - ); - match self.exit_scope() { - Ok(_) => (), - Err(_) => { - self.enter_scope(args + amount + 1, State::Excess); - } - }; - info!( - "Clearing {} {} {:?}", - self.stack.len(), - amount, - &self.stack[..] - ); - let end = self.stack.len() - args - 1; - self.stack.remove_range(end - amount, end); - debug!("{:?}", &self.stack[..]); - return self.do_call(args).map(|x| Async::Ready(Some(x))); - } - Construct { tag, args } => { - let d = { - if args == 0 { - ValueRepr::Tag(tag) - } else { - let fields = &self.stack[self.stack.len() - args..]; - Data(alloc( - &mut self.gc, - self.thread, - &self.stack.stack, - Def { - tag: tag, - elems: fields, - }, - )?) - } - }; - for _ in 0..args { - self.stack.pop(); - } - self.stack.push(d); - } - ConstructRecord { record, args } => { - let d = { - if args == 0 { - ValueRepr::Tag(0) - } else { - let fields = &self.stack[self.stack.len() - args..]; - unsafe { - let roots = Roots { - vm: GcPtr::from_raw(self.thread), - stack: &self.stack.stack, - }; - let field_names = &function.records[record as usize]; - Data(self.gc.alloc_and_collect( - roots, - RecordDef { - elems: fields, - fields: field_names, - }, - )?) - } - } - }; - for _ in 0..args { - self.stack.pop(); - } - self.stack.push(d); - } - ConstructArray(args) => { - let d = { - let fields = &self.stack[self.stack.len() - args..]; - alloc( - &mut self.gc, - self.thread, - &self.stack.stack, - ::value::ArrayDef(fields), - )? - }; - for _ in 0..args { - self.stack.pop(); - } - self.stack.push(ValueRepr::Array(d)); - } - GetOffset(i) => match self.stack.pop().get_repr() { - Data(data) => { - let v = &data.fields[i as usize]; - self.stack.push(v); - } - x => return Err(Error::Message(format!("GetOffset on {:?}", x))), - }, - GetField(i) => { - let field = function.strings[i as usize]; - match self.stack.pop().get_repr() { - Data(data) => { - let v = data.get_field(field).unwrap_or_else(|| { - error!("{}", self.stack.stack.stacktrace(0)); - ice!("Field `{}` does not exist", field) - }); - self.stack.push(v); - } - x => { - return Err(Error::Message(format!("GetField on {:?}", x))); - } - } - } - TestTag(tag) => { - let data_tag = match self.stack.top().get_repr() { - Data(ref data) => data.tag(), - ValueRepr::Tag(tag) => tag, - _ => { - return Err(Error::Message( - "Op TestTag called on non data type".to_string(), - )) - } - }; - self.stack - .push(ValueRepr::Tag(if data_tag == tag { 1 } else { 0 })); - } - Split => { - match self.stack.pop().get_repr() { - Data(data) => for field in &data.fields { - self.stack.push(field); - }, - // Zero argument variant - ValueRepr::Tag(_) => (), - _ => { - return Err(Error::Message( - "Op Split called on non data type".to_string(), - )) - } - } - } - Jump(i) => { - index = i as usize; - continue; - } - CJump(i) => match self.stack.pop().get_repr() { - ValueRepr::Tag(0) => (), - _ => { - index = i as usize; - continue; - } - }, - Pop(n) => for _ in 0..n { - self.stack.pop(); - }, - Slide(n) => { - debug!("{:?}", &self.stack[..]); - let v = self.stack.pop(); - for _ in 0..n { - self.stack.pop(); - } - self.stack.push(v); - } - MakeClosure { - function_index, - upvars, - } => { - let closure = { - let args = &self.stack[self.stack.len() - upvars..]; - let func = function.inner_functions[function_index as usize]; - Closure(alloc( - &mut self.gc, - self.thread, - &self.stack.stack, - ClosureDataDef(func, args), - )?) - }; - for _ in 0..upvars { - self.stack.pop(); - } - self.stack.push(closure); - } - NewClosure { - function_index, - upvars, - } => { - let closure = { - // Use dummy variables until it is filled - let func = function.inner_functions[function_index as usize]; - Closure(alloc( - &mut self.gc, - self.thread, - &self.stack.stack, - ClosureInitDef(func, upvars as usize), - )?) - }; - self.stack.push(closure); - } - CloseClosure(n) => { - let i = self.stack.len() - n - 1; - match self.stack[i].get_repr() { - Closure(mut closure) => { - // Unique access should be safe as this closure should not be shared as - // it has just been allocated and havent even had its upvars set yet - // (which is done here). - unsafe { - for var in closure.as_mut().upvars.iter_mut().rev() { - *var = self.stack.pop(); - } - } - self.stack.pop(); //Remove the closure - } - x => ice!("Expected closure, got {:?}", x), - } - } - PushUpVar(i) => { - let v = self.stack.get_upvar(i).clone(); - self.stack.push(v); - } - AddInt => binop_int(self.thread, &mut self.stack, VmInt::add), - SubtractInt => binop_int(self.thread, &mut self.stack, VmInt::sub), - MultiplyInt => binop_int(self.thread, &mut self.stack, VmInt::mul), - DivideInt => binop_int(self.thread, &mut self.stack, VmInt::div), - IntLT => binop_bool(self.thread, &mut self.stack, |l: VmInt, r| l < r), - IntEQ => binop_bool(self.thread, &mut self.stack, |l: VmInt, r| l == r), - - AddByte => binop_byte(self.thread, &mut self.stack, u8::add), - SubtractByte => binop_byte(self.thread, &mut self.stack, u8::sub), - MultiplyByte => binop_byte(self.thread, &mut self.stack, u8::mul), - DivideByte => binop_byte(self.thread, &mut self.stack, u8::div), - ByteLT => binop_bool(self.thread, &mut self.stack, |l: u8, r| l < r), - ByteEQ => binop_bool(self.thread, &mut self.stack, |l: u8, r| l == r), - - AddFloat => binop_f64(self.thread, &mut self.stack, f64::add), - SubtractFloat => binop_f64(self.thread, &mut self.stack, f64::sub), - MultiplyFloat => binop_f64(self.thread, &mut self.stack, f64::mul), - DivideFloat => binop_f64(self.thread, &mut self.stack, f64::div), - FloatLT => binop_bool(self.thread, &mut self.stack, |l: f64, r| l < r), - FloatEQ => binop_bool(self.thread, &mut self.stack, |l: f64, r| l == r), - } - index += 1; - } - let result = self.stack.top().clone(); - debug!("Return {:?}", result); - let len = self.stack.len(); - let frame_has_excess = self.stack.frame.excess; - - // We might not get access to the frame above the current as it could be locked - debug_assert!( - match self.stack.frame.state { - State::Closure(ref c) => c.function.name == function.name, - _ => false, - }, - "Attempted to pop {:?} but `{}` was expected", - self.stack.frame.state, - function.name - ); - let stack_exists = self.exit_scope().is_ok(); - - self.stack.pop(); - for _ in 0..len { - self.stack.pop(); - } - if frame_has_excess { - // If the function that just finished had extra arguments we need to call the result of - // the call with the extra arguments - match self.stack.pop().get_repr() { - Data(excess) => { - self.enter_scope(0, State::Excess); - debug!("Push excess args {:?}", &excess.fields); - self.stack.push(result); - for value in &excess.fields { - self.stack.push(value); - } - self.do_call(excess.fields.len() as VmIndex) - .map(|x| Async::Ready(Some(x))) - } - x => ice!("Expected excess arguments found {:?}", x), - } - } else { - self.stack.push(result); - Ok(Async::Ready(if stack_exists { Some(()) } else { None })) - } - } -} - -#[inline] -fn binop_int<'b, F, T>(vm: &'b Thread, stack: &mut StackFrame<'b>, f: F) -where - F: FnOnce(T, T) -> VmInt, - T: Getable<'b> + fmt::Debug, -{ - binop(vm, stack, |l, r| ValueRepr::Int(f(l, r))) -} - -#[inline] -fn binop_f64<'b, F, T>(vm: &'b Thread, stack: &mut StackFrame<'b>, f: F) -where - F: FnOnce(T, T) -> f64, - T: Getable<'b> + fmt::Debug, -{ - binop(vm, stack, |l, r| ValueRepr::Float(f(l, r))) -} - -#[inline] -fn binop_byte<'b, F, T>(vm: &'b Thread, stack: &mut StackFrame<'b>, f: F) -where - F: FnOnce(T, T) -> u8, - T: Getable<'b> + fmt::Debug, -{ - binop(vm, stack, |l, r| ValueRepr::Byte(f(l, r))) -} - -#[inline] -fn binop_bool<'b, F, T>(vm: &'b Thread, stack: &mut StackFrame<'b>, f: F) -where - F: FnOnce(T, T) -> bool, - T: Getable<'b> + fmt::Debug, -{ - binop(vm, stack, |l, r| { - ValueRepr::Tag(if f(l, r) { 1 } else { 0 }) - }) -} - -#[inline] -fn binop<'b, F, T>(vm: &'b Thread, stack: &mut StackFrame<'b>, f: F) -where - F: FnOnce(T, T) -> ValueRepr, - T: Getable<'b> + fmt::Debug, -{ - let (l, r) = { - let r = stack.get_variant(stack.len() - 1).unwrap(); - let l = stack.get_variant(stack.len() - 2).unwrap(); - (T::from_value(vm, l), T::from_value(vm, r)) - }; - let result = f(l, r); - stack.pop(); - stack.pop(); - stack.stack.push(result); -} - -fn debug_instruction(stack: &StackFrame, index: usize, instr: Instruction) { - debug!( - "{:?}: {:?} -> {:?} {:?}", - index, - instr, - stack.len(), - match instr { - Push(i) => { - let x = stack.get(i as usize).cloned(); - if x.is_none() { - debug!("{:?}", &stack[..]) - } - x - } - NewClosure { .. } | MakeClosure { .. } => Some(Value::from(Int(stack.len() as isize))), - _ => None, - } - ); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn send_vm() { - fn send(_: T) {} - send(RootedThread::new()); - } -} +//! The thread/vm type +use std::any::{Any, TypeId}; +use std::cmp::Ordering; +use std::fmt; +use std::mem; +use std::ops::{Add, Deref, DerefMut, Div, Mul, Sub}; +use std::result::Result as StdResult; +use std::string::String as StdString; +use std::sync::atomic::{self, AtomicBool}; +use std::sync::Arc; +use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use std::usize; + +use future::FutureValue; +use futures::{Async, Future, Poll}; + +use base::metadata::Metadata; +use base::pos::Line; +use base::symbol::Symbol; +use base::types::{self, Alias, ArcType}; + +use api::{Getable, Pushable, ValueRef, VmType}; +use compiler::UpvarInfo; +use gc::{DataDef, Gc, GcPtr, Generation, Move}; +use macros::MacroEnv; +use source_map::LocalIter; +use stack::{Frame, Lock, Stack, StackFrame, State}; +use types::*; +use value::{ + BytecodeFunction, Callable, ClosureData, ClosureDataDef, ClosureInitDef, Def, ExternFunction, + GcStr, PartialApplicationDataDef, RecordDef, Userdata, Value, ValueRepr, +}; +use vm::{GlobalVmState, GlobalVmStateBuilder, VmEnv}; +use {Error, Result, Variants}; + +use value::ValueRepr::{Closure, Data, Float, Function, Int, PartialApplication, String}; + +pub use gc::Traverseable; + +pub struct Execute { + thread: Option, +} + +impl Execute +where + T: Deref, +{ + pub fn new(thread: T) -> Execute { + Execute { + thread: Some(thread), + } + } +} + +impl Future for Execute +where + T: Deref, +{ + type Item = (T, Value); + type Error = Error; + + // Returns `T` so that it can be reused by the caller + fn poll(&mut self) -> Poll<(T, Value), Error> { + let value = { + let mut context = try_ready!( + self.thread + .as_ref() + .expect("cannot poll Execute future after it has succeded") + .resume() + ); + context.stack.pop() + }; + Ok(Async::Ready((self.thread.take().unwrap(), value))) + } +} + +/// Enum signaling a successful or unsuccess ful call to an extern function. +/// If an error occured the error message is expected to be on the top of the stack. +#[derive(Eq, PartialEq)] +#[repr(C)] +pub enum Status { + Ok, + Yield, + Error, +} + +/// A rooted value +#[derive(Clone)] +pub struct RootedValue +where + T: Deref, +{ + vm: T, + value: Value, +} + +impl Deref for RootedValue +where + T: Deref, +{ + type Target = Value; + fn deref(&self) -> &Value { + &self.value + } +} + +impl PartialEq> for RootedValue +where + T: Deref, + U: Deref, +{ + fn eq(&self, other: &RootedValue) -> bool { + self.value == other.value + } +} + +impl Drop for RootedValue +where + T: Deref, +{ + fn drop(&mut self) { + // TODO not safe if the root changes order of being dropped with another root + self.vm.rooted_values.write().unwrap().pop(); + } +} + +impl fmt::Debug for RootedValue +where + T: Deref, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.value) + } +} + +impl RootedValue +where + T: Deref, +{ + pub fn get_variant(&self) -> Variants { + unsafe { Variants::new(&self.value) } + } + + pub fn get_value(&self) -> Value { + self.value.clone() + } + + pub fn vm(&self) -> &Thread { + &self.vm + } + + pub fn clone_vm(&self) -> T + where + T: Clone, + { + self.vm.clone() + } + + /// looks up the field at the given offset + pub fn get<'vm>(&'vm self, index: usize) -> Option> + where + T: VmRoot<'vm>, + { + match self.get_variant().as_ref() { + ValueRef::Data(ref v) => v + .get_variant(index) + .map(|value| self.vm.root_value(value.get_value())), + _ => None, + } + } + + /// looks up the record field with the given name + pub fn get_field<'vm>(&'vm self, name: &str) -> Option> + where + T: VmRoot<'vm>, + { + match self.get_variant().as_ref() { + ValueRef::Data(ref v) => v + .lookup_field(&*self.vm, name) + .map(|value| self.vm.root_value(value.get_value())), + _ => None, + } + } + + pub fn as_ref(&self) -> RootedValue<&Thread> { + self.vm.root_value(self.value.clone()) + } +} + +impl<'vm> RootedValue<&'vm Thread> { + pub fn vm_(&self) -> &'vm Thread { + self.vm + } +} + +/// A rooted userdata value +pub struct Root<'vm, T: ?Sized + 'vm> { + roots: &'vm RwLock>>, + ptr: *const T, +} + +impl<'vm, T: ?Sized> Drop for Root<'vm, T> { + fn drop(&mut self) { + // TODO not safe if the root changes order of being dropped with another root + self.roots.write().unwrap().pop(); + } +} + +impl<'vm, T: ?Sized> Deref for Root<'vm, T> { + type Target = T; + fn deref(&self) -> &T { + unsafe { &*self.ptr } + } +} + +/// A rooted string +pub struct RootStr<'vm>(Root<'vm, str>); + +impl<'vm> Deref for RootStr<'vm> { + type Target = str; + fn deref(&self) -> &str { + &self.0 + } +} + +struct Roots<'b> { + vm: GcPtr, + stack: &'b Stack, +} +impl<'b> Traverseable for Roots<'b> { + fn traverse(&self, gc: &mut Gc) { + // Since this vm's stack is already borrowed in self we need to manually mark it to prevent + // it from being traversed normally + gc.mark(self.vm); + self.stack.traverse(gc); + + // Traverse the vm's fields, avoiding the stack which is traversed above + self.vm.traverse_fields_except_stack(gc); + } +} + +impl<'b> ::gc::CollectScope for Roots<'b> { + fn scope(&self, gc: &mut Gc, f: F) + where + F: FnOnce(&mut Gc), + { + // We need to pretend that the threads lives for longer than it does on the stack or we + // can't move the RwLockGuard into the vec. This does end up safe in the end because we + // never leak any lifetimes outside of this function + unsafe { + let locks = self.mark_child_roots(gc); + // Scan `self` sweep `gc` + f(gc); + + // `sweep` all child gcs + for (_, mut context, _) in locks { + context.gc.sweep(); + } + } + } +} + +impl<'b> Roots<'b> { + unsafe fn mark_child_roots( + &self, + gc: &mut Gc, + ) -> Vec<( + RwLockReadGuard>>, + MutexGuard, + GcPtr, + )> { + let mut stack: Vec> = Vec::new(); + let mut locks: Vec<(_, _, GcPtr)> = Vec::new(); + + let child_threads = self.vm.child_threads.read().unwrap(); + for child in &*child_threads { + Vec::push(&mut stack, *child); + } + + while let Some(thread_ptr) = stack.pop() { + if locks.iter().any(|&(_, _, lock_thread)| { + &*thread_ptr as *const Thread == &*lock_thread as *const Thread + }) { + continue; + } + + let thread = mem::transmute::<&Thread, &'static Thread>(&*thread_ptr); + let child_threads = thread.child_threads.read().unwrap(); + for child in &*child_threads { + Vec::push(&mut stack, *child); + } + + let context = thread.context.lock().unwrap(); + + // Since we locked the context we need to scan the thread using `Roots` rather than + // letting it be scanned normally + Roots { + vm: thread_ptr, + stack: &context.stack, + }.traverse(gc); + + Vec::push(&mut locks, (child_threads, context, thread_ptr)); + } + locks + } +} + +// All threads MUST be allocated in the garbage collected heap. This is necessary as a thread +// calling collect need to mark itself if it is on the garbage collected heap and it has no way of +// knowing wheter it is or not. So the only way of allowing it to mark itself is to disallow it to +// be allocated anywhere else. +/// Representation of the virtual machine +#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] +#[cfg_attr(feature = "serde_derive", serde(deserialize_state = "::serialization::DeSeed"))] +#[cfg_attr(feature = "serde_derive", serde(serialize_state = "::serialization::SeSeed"))] +pub struct Thread { + #[cfg_attr(feature = "serde_derive", serde(state_with = "::base::serialization::shared"))] + global_state: Arc, + // The parent of this thread, if it exists must live at least as long as this thread as this + // thread can refer to any value in the parent thread + #[cfg_attr(feature = "serde_derive", serde(state))] + parent: Option, + #[cfg_attr(feature = "serde_derive", serde(skip))] + roots: RwLock>>, + #[cfg_attr(feature = "serde_derive", serde(state))] + rooted_values: RwLock>, + /// All threads which this thread have spawned in turn. Necessary as this thread needs to scan + /// the roots of all its children as well since those may contain references to this threads + /// garbage collected values + #[cfg_attr(feature = "serde_derive", serde(state))] + child_threads: RwLock>>, + #[cfg_attr(feature = "serde_derive", serde(state))] + context: Mutex, + #[cfg_attr(feature = "serde_derive", serde(skip))] + interrupt: AtomicBool, +} + +impl fmt::Debug for Thread { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Thread({:p})", self) + } +} + +impl Userdata for Thread {} + +impl VmType for Thread { + type Type = Self; +} + +impl Traverseable for Thread { + fn traverse(&self, gc: &mut Gc) { + self.traverse_fields_except_stack(gc); + self.context.lock().unwrap().stack.traverse(gc); + } +} + +impl PartialEq for Thread { + fn eq(&self, other: &Thread) -> bool { + self as *const _ == other as *const _ + } +} + +impl VmType for RootedThread { + type Type = Thread; +} + +impl<'vm> Pushable<'vm> for RootedThread { + fn push(self, _vm: &'vm Thread, context: &mut Context) -> Result<()> { + context.stack.push(ValueRepr::Thread(self.0)); + Ok(()) + } +} + +impl<'vm, 'value> Getable<'vm, 'value> for RootedThread { + fn from_value(_: &'vm Thread, value: Variants<'value>) -> Self { + match value.as_ref() { + ValueRef::Thread(thread) => thread.root_thread(), + _ => ice!("ValueRef is not a Thread"), + } + } +} + +/// An instance of `Thread` which is rooted. See the `Thread` type for documentation on interacting +/// with the type. +#[derive(Debug)] +#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] +#[cfg_attr(feature = "serde_derive", serde(deserialize_state = "::serialization::DeSeed"))] +#[cfg_attr(feature = "serde_derive", serde(serialize_state = "::serialization::SeSeed"))] +pub struct RootedThread(#[cfg_attr(feature = "serde_derive", serde(state))] GcPtr); + +impl Drop for RootedThread { + fn drop(&mut self) { + let is_empty = { + let mut roots = self.parent_threads(); + let index = roots + .iter() + .position(|p| &**p as *const Thread == &*self.0 as *const Thread) + .expect("VM ptr"); + roots.swap_remove(index); + roots.is_empty() + }; + if self.parent.is_none() && is_empty { + // The last RootedThread was dropped, there is no way to refer to the global state any + // longer so drop everything + let mut gc_ref = self.0.global_state.gc.lock().unwrap(); + let gc_to_drop = ::std::mem::replace(&mut *gc_ref, Gc::new(Generation::default(), 0)); + // Make sure that the RefMut is dropped before the Gc itself as the RwLock is dropped + // when the Gc is dropped + drop(gc_ref); + drop(gc_to_drop); + } + } +} + +impl Deref for RootedThread { + type Target = Thread; + fn deref(&self) -> &Thread { + &self.0 + } +} + +impl Clone for RootedThread { + fn clone(&self) -> RootedThread { + self.root_thread() + } +} + +impl Traverseable for RootedThread { + fn traverse(&self, gc: &mut Gc) { + self.0.traverse(gc); + } +} + +impl RootedThread { + /// Creates a new virtual machine with an empty global environment + pub fn new() -> RootedThread { + RootedThread::with_global_state(GlobalVmStateBuilder::default().build()) + } + + pub fn with_global_state(mut global_state: GlobalVmState) -> RootedThread { + let thread = Thread { + parent: None, + context: Mutex::new(Context::new( + global_state.gc.get_mut().unwrap().new_child_gc(), + )), + global_state: Arc::new(global_state), + roots: RwLock::new(Vec::new()), + rooted_values: RwLock::new(Vec::new()), + child_threads: RwLock::new(Vec::new()), + interrupt: AtomicBool::new(false), + }; + let mut gc = Gc::new(Generation::default(), usize::MAX); + let vm = gc + .alloc(Move(thread)) + .expect("Not enough memory to allocate thread") + .root_thread(); + *vm.global_state.gc.lock().unwrap() = gc; + // Enter the top level scope + { + let mut context = vm.context.lock().unwrap(); + StackFrame::frame(&mut context.stack, 0, State::Unknown); + } + vm + } + + /// Converts a `RootedThread` into a raw pointer allowing to be passed through a C api. + /// The reference count for the thread is not modified + pub fn into_raw(self) -> *const Thread { + let ptr: *const Thread = &*self.0; + ::std::mem::forget(self); + ptr + } + + /// Converts a raw pointer into a `RootedThread`. + /// The reference count for the thread is not modified so it is up to the caller to ensure that + /// the count is correct. + pub unsafe fn from_raw(ptr: *const Thread) -> RootedThread { + RootedThread(GcPtr::from_raw(ptr)) + } +} + +impl Thread { + /// Spawns a new gluon thread with its own stack and heap but while still sharing the same + /// global environment + pub fn new_thread(&self) -> Result { + let vm = Thread { + global_state: self.global_state.clone(), + parent: Some(self.root_thread()), + context: Mutex::new(Context::new(self.current_context().gc.new_child_gc())), + roots: RwLock::new(Vec::new()), + rooted_values: RwLock::new(Vec::new()), + child_threads: RwLock::new(Vec::new()), + interrupt: AtomicBool::new(false), + }; + // Enter the top level scope + { + let mut context = vm.current_context(); + StackFrame::frame(&mut context.stack, 0, State::Unknown); + } + let ptr = self.context().gc.alloc(Move(vm))?; + + Ok(ptr.root_thread()) + } + + /// Roots `self`, extending the lifetime of this thread until at least the returned + /// `RootedThread` is droppped + pub fn root_thread(&self) -> RootedThread { + unsafe { + let vm = RootedThread(GcPtr::from_raw(self)); + vm.parent_threads().push(vm.0); + vm + } + } + + /// Creates a new global value at `name`. + /// Fails if a global called `name` already exists. + /// + /// # Examples + /// + /// Load the `factorial` rust function into gluon and evaluate `factorial 5` + /// + /// ``` + /// # extern crate gluon; + /// # #[macro_use] extern crate gluon_vm; + /// # use gluon::{new_vm,Compiler}; + /// # use gluon::base::types::Type; + /// fn factorial(x: i32) -> i32 { + /// if x <= 1 { 1 } else { x * factorial(x - 1) } + /// } + /// # fn main() { + /// + /// # if ::std::env::var("GLUON_PATH").is_err() { + /// # ::std::env::set_var("GLUON_PATH", ".."); + /// # } + /// + /// let vm = new_vm(); + /// + /// vm.define_global("factorial", primitive!(1 factorial)).unwrap(); + /// + /// let result = Compiler::new() + /// .run_expr_async::(&vm, "example", "factorial 5") + /// .sync_or_error() + /// .unwrap_or_else(|err| panic!("{}", err)); + /// let expected = (120, Type::int()); + /// + /// assert_eq!(result, expected); + /// # } + /// ``` + /// + #[deprecated(since = "0.7.0", note = "Use `gluon::import::add_extern_module` instead")] + pub fn define_global<'vm, T>(&'vm self, name: &str, value: T) -> Result<()> + where + T: Pushable<'vm> + VmType, + { + let value = { + let mut context = self.context(); + value.push(self, &mut context)?; + context.stack.pop() + }; + self.set_global( + Symbol::from(format!("@{}", name)), + T::make_forall_type(self), + Metadata::default(), + value, + ) + } + + /// Retrieves the global called `name`. + /// + /// # Examples + /// + /// Bind the `(+)` function in gluon's prelude standard library + /// to an `add` function in rust + /// + /// ```rust + /// # extern crate gluon; + /// # use gluon::{new_vm, Compiler, Thread}; + /// # use gluon::vm::api::{FunctionRef, Hole, OpaqueValue}; + /// # fn main() { + /// + /// # if ::std::env::var("GLUON_PATH").is_err() { + /// # ::std::env::set_var("GLUON_PATH", ".."); + /// # } + /// + /// let vm = new_vm(); + /// + /// Compiler::new() + /// .run_expr::>(&vm, "example", + /// r#" import! std.int "#) + /// .unwrap_or_else(|err| panic!("{}", err)); + /// let mut add: FunctionRef i32> = + /// vm.get_global("std.int.num.(+)").unwrap(); + /// let result = add.call(1, 2); + /// assert_eq!(result, Ok(3)); + /// # } + /// ``` + /// + /// # Errors + /// + /// if the global does not exist or it does not have the correct type. + /// + pub fn get_global<'vm, T>(&'vm self, name: &str) -> Result + where + T: for<'value> Getable<'vm, 'value> + VmType, + { + use check::check_signature; + + let expected = T::make_type(self); + + let env = self.get_env(); + let (value, actual) = env.get_binding(name)?; + + // Finally check that type of the returned value is correct + if check_signature(&*env, &expected, &actual) { + Ok(T::from_value(self, value)) + } else { + Err(Error::WrongType(expected, actual.into_owned())) + } + } + + /// Retrieves type information about the type `name`. Types inside records can be accessed + /// using dot notation (std.prelude.Option) + pub fn find_type_info(&self, name: &str) -> Result> { + let env = self.get_env(); + env.find_type_info(name).map(|alias| alias.into_owned()) + } + + /// Returns the gluon type that was bound to `T` + pub fn get_type(&self) -> Option { + self.global_env().get_type::() + } + + /// Registers the type `T` as being a gluon type called `name` with generic arguments `args` + pub fn register_type(&self, name: &str, args: &[&str]) -> Result { + self.global_env().register_type::(name, args) + } + pub fn register_type_as( + &self, + name: Symbol, + alias: Alias, + id: TypeId, + ) -> Result { + self.global_env().register_type_as(name, alias, id) + } + + /// Locks and retrieves the global environment of the vm + pub fn get_env<'b>(&'b self) -> RwLockReadGuard<'b, VmEnv> { + self.global_env().get_env() + } + + /// Retrieves the macros defined for this vm + pub fn get_macros(&self) -> &MacroEnv { + self.global_env().get_macros() + } + + #[cfg(not(target_arch = "wasm32"))] + pub fn get_event_loop(&self) -> Option<::tokio_core::reactor::Remote> { + self.global_env().get_event_loop() + } + + /// Runs a garbage collection. + pub fn collect(&self) { + let mut context = self.current_context(); + self.with_roots(&mut context, |gc, roots| unsafe { + gc.collect(roots); + }) + } + + /// Pushes a value to the top of the stack + pub fn push<'vm, T>(&'vm self, v: T) -> Result<()> + where + T: Pushable<'vm>, + { + let mut context = self.current_context(); + v.push(self, &mut context) + } + + /// Removes the top value from the stack + pub fn pop(&self) { + self.current_context().stack.pop(); + } + + pub fn set_memory_limit(&self, memory_limit: usize) { + self.current_context().gc.set_memory_limit(memory_limit) + } + + pub fn interrupt(&self) { + self.interrupt.store(true, atomic::Ordering::Relaxed) + } + + pub fn interrupted(&self) -> bool { + self.interrupt.load(atomic::Ordering::Relaxed) + } + + fn current_context(&self) -> OwnedContext { + self.context() + } + + fn traverse_fields_except_stack(&self, gc: &mut Gc) { + self.global_state.traverse(gc); + self.roots.read().unwrap().traverse(gc); + self.rooted_values.read().unwrap().traverse(gc); + self.child_threads.read().unwrap().traverse(gc); + } + + fn parent_threads(&self) -> RwLockWriteGuard>> { + match self.parent { + Some(ref parent) => parent.child_threads.write().unwrap(), + None => self.global_state.generation_0_threads.write().unwrap(), + } + } + + fn with_roots(&self, context: &mut Context, f: F) -> R + where + F: for<'b> FnOnce(&mut Gc, Roots<'b>) -> R, + { + // For this to be safe we require that the received stack is the same one that is in this + // VM + { + let self_context: *const _ = &self.context; + let context: *const _ = context; + assert!(unsafe { + context as usize >= self_context as usize + && context as usize <= self_context.offset(1) as usize + }); + } + let roots = Roots { + vm: unsafe { + // Threads must only be on the garbage collectors heap which makes this safe + GcPtr::from_raw(self) + }, + stack: &context.stack, + }; + f(&mut context.gc, roots) + } +} + +pub trait VmRoot<'a>: Deref + Clone + 'a { + fn root(thread: &'a Thread) -> Self; + + /// Roots a value + fn root_value_with_self(self, value: Value) -> RootedValue { + self.rooted_values.write().unwrap().push(value.clone()); + RootedValue { + vm: self, + value: value, + } + } +} + +impl<'a> VmRoot<'a> for &'a Thread { + fn root(thread: &'a Thread) -> Self { + thread + } +} + +impl<'a> VmRoot<'a> for RootedThread { + fn root(thread: &'a Thread) -> Self { + thread.root_thread() + } +} + +/// Internal functions for interacting with threads. These functions should be considered both +/// unsafe and unstable. +pub trait ThreadInternal +where + for<'a> &'a Self: Deref, +{ + /// Locks and retrives this threads stack + fn context(&self) -> OwnedContext; + + /// Roots a userdata + fn root<'vm, T: Userdata>(&'vm self, v: GcPtr>) -> Option>; + + /// Roots a string + fn root_string<'vm>(&'vm self, ptr: GcStr) -> RootStr<'vm>; + + /// Roots a value + fn root_value<'vm, T>(&'vm self, value: Value) -> RootedValue + where + T: VmRoot<'vm>; + + /// Evaluates a zero argument function (a thunk) + fn call_thunk(&self, closure: GcPtr) -> FutureValue>; + + /// Executes an `IO` action + fn execute_io(&self, value: Value) -> FutureValue>; + + /// Calls a function on the stack. + /// When this function is called it is expected that the function exists at + /// `stack.len() - args - 1` and that the arguments are of the correct type + fn call_function<'b>( + &'b self, + stack: OwnedContext<'b>, + args: VmIndex, + ) -> Result>>>; + + fn resume(&self) -> Result>; + + fn global_env(&self) -> &Arc; + + fn set_global( + &self, + name: Symbol, + typ: ArcType, + metadata: Metadata, + value: Value, + ) -> Result<()>; + + /// `owner` is theread that owns `value` which is not necessarily the same as `self` + fn deep_clone_value(&self, owner: &Thread, value: Value) -> Result; + + fn can_share_values_with(&self, gc: &mut Gc, other: &Thread) -> bool; +} + +impl ThreadInternal for Thread { + fn context(&self) -> OwnedContext { + OwnedContext { + thread: self, + context: self.context.lock().unwrap(), + } + } + /// Roots a userdata + fn root<'vm, T: Userdata>(&'vm self, v: GcPtr>) -> Option> { + v.downcast_ref::().map(|ptr| { + self.roots.write().unwrap().push(v.as_traverseable()); + Root { + roots: &self.roots, + ptr: ptr, + } + }) + } + + /// Roots a string + fn root_string<'vm>(&'vm self, ptr: GcStr) -> RootStr<'vm> { + self.roots + .write() + .unwrap() + .push(ptr.into_inner().as_traverseable()); + RootStr(Root { + roots: &self.roots, + ptr: &*ptr, + }) + } + + /// Roots a value + fn root_value<'vm, T>(&'vm self, value: Value) -> RootedValue + where + T: VmRoot<'vm>, + { + self.rooted_values.write().unwrap().push(value.clone()); + RootedValue { + vm: T::root(self), + value: value, + } + } + + fn call_thunk(&self, closure: GcPtr) -> FutureValue> { + let mut context = self.current_context(); + context.stack.push(Closure(closure)); + context.borrow_mut().enter_scope(0, State::Closure(closure)); + let async = try_future!(context.execute(false)); + match async { + Async::Ready(context) => FutureValue::Value(Ok((self, context.unwrap().stack.pop()))), + Async::NotReady => FutureValue::Future(Execute::new(self)), + } + } + + /// Calls a module, allowed to to run IO expressions + fn execute_io(&self, value: Value) -> FutureValue> { + debug!("Run IO {:?}", value); + let mut context = self.context(); + // Dummy value to fill the place of the function for TailCall + context.stack.push(Int(0)); + + context.stack.push(value); + context.stack.push(Int(0)); + + context.borrow_mut().enter_scope(2, State::Unknown); + context = match try_future!(self.call_function(context, 1)) { + Async::Ready(context) => context.expect("call_module to have the stack remaining"), + Async::NotReady => return FutureValue::Future(Execute::new(self)), + }; + let result = context.stack.pop(); + { + let mut context = context.borrow_mut(); + while context.stack.len() > 0 { + context.stack.pop(); + } + } + let _ = context.exit_scope(); + FutureValue::Value(Ok((self, result))) + } + + /// Calls a function on the stack. + /// When this function is called it is expected that the function exists at + /// `stack.len() - args - 1` and that the arguments are of the correct type + fn call_function<'b>( + &'b self, + mut context: OwnedContext<'b>, + args: VmIndex, + ) -> Result>>> { + context.borrow_mut().do_call(args)?; + context.execute(false) + } + + fn resume(&self) -> Result> { + let mut context = self.current_context(); + if context.stack.get_frames().len() == 1 { + // Only the top level frame left means that the thread has finished + return Err(Error::Dead); + } + context = try_ready!(context.execute(true)).unwrap(); + Ok(Async::Ready(context)) + } + + fn global_env(&self) -> &Arc { + &self.global_state + } + + fn set_global( + &self, + name: Symbol, + typ: ArcType, + metadata: Metadata, + value: Value, + ) -> Result<()> { + let value = ::value::Cloner::new(self, &mut self.global_env().gc.lock().unwrap()) + .deep_clone(&value)?; + self.global_env().set_global(name, typ, metadata, value) + } + + fn deep_clone_value(&self, owner: &Thread, value: Value) -> Result { + let mut context = self.current_context(); + let full_clone = !self.can_share_values_with(&mut context.gc, owner); + let mut cloner = ::value::Cloner::new(self, &mut context.gc); + if full_clone { + cloner.force_full_clone(); + } + cloner.deep_clone(&value) + } + + fn can_share_values_with(&self, gc: &mut Gc, other: &Thread) -> bool { + if self as *const Thread == other as *const Thread { + return true; + } + // If the threads do not share the same global state then they are disjoint and can't share + // values + if &*self.global_state as *const GlobalVmState + != &*other.global_state as *const GlobalVmState + { + return false; + } + // Otherwise the threads might be able to share values but only if they are on the same + // of the generation tree (see src/gc.rs) + // Search from the thread which MAY be a child to the parent. If `parent` could not be + // found then the threads must be in different branches of the tree + let self_gen = gc.generation(); + let other_gen = other.context.lock().unwrap().gc.generation(); + let (parent, mut child) = if self_gen.is_parent_of(other_gen) { + (self, other) + } else { + (other, self) + }; + while let Some(ref next) = child.parent { + if &**next as *const Thread == parent as *const Thread { + return true; + } + child = next; + } + false + } +} + +pub type HookFn = Box Result> + Send + Sync>; + +pub struct DebugInfo<'a> { + stack: &'a Stack, + state: HookFlags, +} + +pub struct StackInfo<'a> { + info: &'a DebugInfo<'a>, + index: usize, +} + +impl<'a> DebugInfo<'a> { + /// Returns the reason for the hook being called + pub fn state(&self) -> HookFlags { + self.state + } + + /// Returns a struct which can be queried about information about the stack + /// at a specific level where `0` is the currently executing frame. + pub fn stack_info(&self, level: usize) -> Option { + let frames = self.stack.get_frames(); + if level < frames.len() { + Some(StackInfo { + info: self, + index: frames.len() - level - 1, + }) + } else { + None + } + } + + pub fn stack_info_len(&self) -> usize { + self.stack.get_frames().len() + } +} + +impl<'a> StackInfo<'a> { + fn frame(&self) -> &Frame { + &self.info.stack.get_frames()[self.index] + } + + // For frames except the top we subtract one to account for the `Call` instruction adding one + fn instruction_index(&self) -> usize { + if self.info.stack.get_frames().len() - 1 == self.index { + self.frame().instruction_index + } else { + self.frame().instruction_index - 1 + } + } + + /// Returns the line which create the current instruction of this frame + pub fn line(&self) -> Option { + let frame = self.frame(); + match frame.state { + State::Closure(ref closure) => closure + .function + .debug_info + .source_map + .line(self.instruction_index()), + _ => None, + } + } + + /// Returns the name of the source which defined the funtion executing at this frame + pub fn source_name(&self) -> &str { + match self.frame().state { + State::Closure(ref closure) => &closure.function.debug_info.source_name, + _ => "", + } + } + + /// Returns the name of the function executing at this frame + pub fn function_name(&self) -> Option<&str> { + match self.frame().state { + State::Unknown | State::Lock | State::Excess => None, + State::Closure(ref closure) => Some(closure.function.name.declared_name()), + State::Extern(ref function) => Some(function.id.declared_name()), + } + } + + /// Returns an iterator over all locals available at the current executing instruction + pub fn locals(&self) -> LocalIter { + let frame = self.frame(); + match frame.state { + State::Closure(ref closure) => closure + .function + .debug_info + .local_map + .locals(self.instruction_index()), + _ => LocalIter::empty(), + } + } + + /// Returns a slice with information about the values bound to this closure + pub fn upvars(&self) -> &[UpvarInfo] { + match self.frame().state { + State::Closure(ref closure) => &closure.function.debug_info.upvars, + _ => ice!("Attempted to access upvar in non closure function"), + } + } +} + +bitflags! { + #[derive(Default)] + pub struct HookFlags: u8 { + /// Call the hook when execution moves to a new line + const LINE_FLAG = 0b01; + /// Call the hook when a function is called + const CALL_FLAG = 0b10; + } +} + +#[derive(Default)] +struct Hook { + function: Option, + flags: HookFlags, + // The index of the last executed instruction + previous_instruction_index: usize, +} + +#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] +#[cfg_attr(feature = "serde_derive", serde(deserialize_state = "::serialization::DeSeed"))] +#[cfg_attr(feature = "serde_derive", serde(serialize_state = "::serialization::SeSeed"))] +pub struct Context { + // FIXME It is dangerous to write to gc and stack + #[cfg_attr(feature = "serde_derive", serde(state))] + pub stack: Stack, + #[cfg_attr(feature = "serde_derive", serde(state))] + pub gc: Gc, + #[cfg_attr(feature = "serde_derive", serde(skip))] + hook: Hook, + max_stack_size: VmIndex, + + /// Stack of polling functions used for extern functions returning futures + #[cfg_attr(feature = "serde_derive", serde(skip))] + poll_fns: Vec<( + Option, + Box FnMut(&'vm Thread) -> Result>> + Send>, + )>, +} + +impl Context { + fn new(gc: Gc) -> Context { + Context { + gc: gc, + stack: Stack::new(), + hook: Hook { + function: None, + flags: HookFlags::empty(), + previous_instruction_index: usize::max_value(), + }, + max_stack_size: VmIndex::max_value(), + poll_fns: Vec::new(), + } + } + + pub fn new_data(&mut self, thread: &Thread, tag: VmTag, fields: &[Value]) -> Result { + self.alloc_with( + thread, + Def { + tag: tag, + elems: fields, + }, + ).map(ValueRepr::Data) + .map(Value::from) + } + + pub fn alloc_with(&mut self, thread: &Thread, data: D) -> Result> + where + D: DataDef + Traverseable, + D::Value: Sized + Any, + { + alloc(&mut self.gc, thread, &self.stack, data) + } + + pub fn alloc_ignore_limit(&mut self, data: D) -> GcPtr + where + D: DataDef + Traverseable, + D::Value: Sized + Any, + { + self.gc.alloc_ignore_limit(data) + } + + pub fn set_hook(&mut self, hook: Option) -> Option { + mem::replace(&mut self.hook.function, hook) + } + + pub fn set_hook_mask(&mut self, flags: HookFlags) { + self.hook.flags = flags; + } + + pub fn set_max_stack_size(&mut self, limit: VmIndex) { + self.max_stack_size = limit; + } + + /// "Returns a future", letting the virtual machine know that `future` must be resolved to + /// produce the actual value. + /// + /// # Safety + /// + /// This function is unsafe because the `vm` lifetime must not outlive the lifetime of the + /// `Thread` + pub unsafe fn return_future<'vm, F>(&mut self, mut future: F, lock: Lock) + where + F: Future + Send + 'static, + F::Item: Pushable<'vm>, + { + use std::mem::transmute; + + let lock = if self.poll_fns.is_empty() { + self.stack.release_lock(lock); + None + } else { + Some(lock) + }; + + self.poll_fns.push(( + lock, + Box::new(move |vm| { + let value = try_ready!(future.poll()); + + let mut context = vm.current_context(); + let vm = transmute::<&Thread, &'vm Thread>(vm); + value.push(vm, &mut context).map(|()| Async::Ready(context)) + }), + )); + } +} + +impl<'b> OwnedContext<'b> { + pub fn alloc(&mut self, data: D) -> Result> + where + D: DataDef + Traverseable, + D::Value: Sized + Any, + { + let Context { + ref mut gc, + ref stack, + .. + } = **self; + alloc(gc, self.thread, &stack, data) + } + + pub fn debug_info(&self) -> DebugInfo { + DebugInfo { + stack: &self.stack, + state: HookFlags::empty(), + } + } +} + +pub fn alloc(gc: &mut Gc, thread: &Thread, stack: &Stack, def: D) -> Result> +where + D: DataDef + Traverseable, + D::Value: Sized + Any, +{ + let roots = Roots { + vm: unsafe { + // Threads must only be on the garbage collectors heap which makes this safe + GcPtr::from_raw(thread) + }, + stack: stack, + }; + unsafe { gc.alloc_and_collect(roots, def) } +} + +pub struct OwnedContext<'b> { + thread: &'b Thread, + context: MutexGuard<'b, Context>, +} + +impl<'b> Deref for OwnedContext<'b> { + type Target = Context; + fn deref(&self) -> &Context { + &self.context + } +} + +impl<'b> DerefMut for OwnedContext<'b> { + fn deref_mut(&mut self) -> &mut Context { + &mut self.context + } +} + +const INITIAL_CALL: usize = 0; +const POLL_CALL: usize = 1; +const IN_POLL: usize = 2; + +impl<'b> OwnedContext<'b> { + fn exit_scope(mut self) -> StdResult, ()> { + let exists = StackFrame::current(&mut self.stack).exit_scope().is_ok(); + if exists { + Ok(self) + } else { + Err(()) + } + } + + fn execute(self, polled: bool) -> Result>>> { + let mut maybe_context = Some(self); + while let Some(mut context) = maybe_context { + if context.thread.interrupted() { + return Err(Error::Interrupted); + } + debug!("STACK\n{:?}", context.stack.get_frames()); + let state = context.borrow_mut().stack.frame.state; + + let instruction_index = context.borrow_mut().stack.frame.instruction_index; + if instruction_index == 0 && context.hook.flags.contains(HookFlags::CALL_FLAG) { + match state { + State::Extern(_) | State::Closure(_) => { + let thread = context.thread; + let context = &mut *context; + if let Some(ref mut hook) = context.hook.function { + let info = DebugInfo { + stack: &context.stack, + state: HookFlags::CALL_FLAG, + }; + try_ready!(hook(thread, info)) + } + } + _ => (), + } + } + + maybe_context = match state { + State::Lock | State::Unknown => return Ok(Async::Ready(Some(context))), + State::Excess => context.exit_scope().ok(), + State::Extern(ext) => { + let instruction_index = context.borrow_mut().stack.frame.instruction_index; + if instruction_index == IN_POLL { + return Ok(Async::Ready(Some(context))); + } + context.borrow_mut().stack.frame.instruction_index = POLL_CALL; + Some(try_ready!(context.execute_function( + instruction_index == INITIAL_CALL, + &ext, + polled, + ))) + } + State::Closure(closure) => { + let max_stack_size = context.max_stack_size; + // Tail calls into extern functions at the top level will drop the last + // stackframe so just return immedietly + enum State { + Exists, + DoesNotExist, + ReturnContext, + } + let state = { + let mut context = context.borrow_mut(); + + let instruction_index = context.stack.frame.instruction_index; + let function_size = closure.function.max_stack_size; + + // Before entering a function check that the stack cannot exceed `max_stack_size` + if instruction_index == 0 + && context.stack.stack.len() + function_size > max_stack_size + { + return Err(Error::StackOverflow(max_stack_size)); + } + + if context.stack.stack.get_frames().len() == 0 { + State::ReturnContext + } else { + info!( + "Continue with {}\nAt: {}/{}\n{:?}", + closure.function.name, + instruction_index, + closure.function.instructions.len(), + &context.stack[..] + ); + + let new_context = try_ready!(context.execute_( + instruction_index, + &closure.function.instructions, + &closure.function, + )); + if new_context.is_some() { + State::Exists + } else { + State::DoesNotExist + } + } + }; + match state { + State::Exists => Some(context), + State::DoesNotExist => None, + State::ReturnContext => return Ok(Async::Ready(Some(context))), + } + } + }; + } + Ok(Async::Ready(maybe_context)) + } + + fn execute_function( + mut self, + initial_call: bool, + function: &ExternFunction, + polled: bool, + ) -> Result>> { + info!( + "CALL EXTERN {} {:?}", + function.id, + &self.stack.current_frame()[..], + ); + + let mut status = Status::Ok; + if initial_call { + // Make sure that the stack is not borrowed during the external function call + // Necessary since we do not know what will happen during the function call + let thread = self.thread; + drop(self); + status = (function.function)(thread); + + if status == Status::Yield { + return Ok(Async::NotReady); + } + + self = thread.current_context(); + + if status == Status::Error { + return match self.stack.pop().get_repr() { + String(s) => Err(Error::Panic(s.to_string(), Some(self.stack.stacktrace(0)))), + _ => Err(Error::Message(format!( + "Unexpected error calling function `{}`", + function.id + ))), + }; + } + + // The `poll_fn` at the top may be for a stack frame at a lower level, return to the + // state loop to ensure that we are executing the frame at the top of the stack + if !self.poll_fns.is_empty() { + return Ok(Async::Ready(self)); + } + } + while let Some((lock, mut poll_fn)) = self.poll_fns.pop() { + // We can only poll the future if the code is currently executing in a future + if !polled { + self.poll_fns.push((lock, poll_fn)); + return Ok(Async::NotReady); + } + + let frame_offset = self.stack.get_frames().len() - 1; + if self.poll_fns.is_empty() { + self.stack.get_frames_mut()[frame_offset].instruction_index = IN_POLL; + } + let thread = self.thread; + drop(self); + // Poll the future that was returned from the initial call to this extern function + match poll_fn(thread)? { + Async::Ready(context) => { + self = context; + if let Some(lock) = lock { + self.stack.release_lock(lock); + } + self.borrow_mut().stack.frame.instruction_index = POLL_CALL; + return Ok(Async::Ready(self)); + } + Async::NotReady => { + self = thread.current_context(); + self.stack.get_frames_mut()[frame_offset].instruction_index = POLL_CALL; + // Restore `poll_fn` so it can be polled again + self.poll_fns.push((lock, poll_fn)); + return Ok(Async::NotReady); + } + } + } + + // The function call is done at this point so remove any extra values from the frame and + // return the value at the top of the stack + let result = self.stack.pop(); + { + let mut stack = self.stack.current_frame(); + while stack.len() > 0 { + debug!("{} {:?}", stack.len(), &*stack); + stack.pop(); + } + if !(match stack.frame.state { + State::Extern(ref e) => e.id == function.id, + _ => false, + }) { + "asd".to_string(); + } + debug_assert!( + match stack.frame.state { + State::Extern(ref e) => e.id == function.id, + _ => false, + }, + "Attempted to pop {:?} but {} was expected", + stack.frame, + function.id + ) + } + self = self.exit_scope().map_err(|_| { + Error::Message(StdString::from("Poped the last frame in execute_function")) + })?; + self.stack.pop(); // Pop function + self.stack.push(result); + + info!( + "EXIT EXTERN {} {:?}", + function.id, + &self.stack.current_frame()[..] + ); + + match status { + Status::Ok => Ok(Async::Ready(self)), + Status::Yield => Ok(Async::NotReady), + Status::Error => match self.stack.pop().get_repr() { + String(s) => Err(Error::Panic(s.to_string(), Some(self.stack.stacktrace(0)))), + _ => Err(Error::Message(format!( + "Unexpected error calling function `{}`", + function.id + ))), + }, + } + } + + fn borrow_mut(&mut self) -> ExecuteContext { + let context = &mut **self; + ExecuteContext { + thread: self.thread, + gc: &mut context.gc, + stack: StackFrame::current(&mut context.stack), + hook: &mut context.hook, + } + } +} + +struct ExecuteContext<'b> { + thread: &'b Thread, + stack: StackFrame<'b>, + gc: &'b mut Gc, + hook: &'b mut Hook, +} + +impl<'b> ExecuteContext<'b> { + fn enter_scope(&mut self, args: VmIndex, state: State) { + self.stack.enter_scope(args, state); + self.hook.previous_instruction_index = usize::max_value(); + } + + fn exit_scope(&mut self) -> StdResult<(), ()> { + match self.stack.exit_scope() { + Ok(_) => { + if self.hook.flags.bits() != 0 { + // Subtract 1 to compensate for the `Call` instruction adding one earlier + // ensuring that the line hook runs after function calls + self.hook.previous_instruction_index = + self.stack.frame.instruction_index.saturating_sub(1); + } + Ok(()) + } + Err(_) => Err(()), + } + } + + fn execute_callable(&mut self, function: &Callable, excess: bool) -> Result<()> { + match *function { + Callable::Closure(closure) => { + self.enter_scope(closure.function.args, State::Closure(closure)); + self.stack.frame.excess = excess; + Ok(()) + } + Callable::Extern(ref ext) => { + assert!(self.stack.len() >= ext.args + 1); + let function_index = self.stack.len() - ext.args - 1; + debug!("------- {} {:?}", function_index, &self.stack[..]); + self.enter_scope(ext.args, State::Extern(*ext)); + Ok(()) + } + } + } + + fn call_function_with_upvars( + &mut self, + args: VmIndex, + required_args: VmIndex, + callable: Callable, + ) -> Result<()> { + debug!("cmp {} {} {:?} {:?}", args, required_args, callable, { + let function_index = self.stack.len() - 1 - args; + &(*self.stack)[(function_index + 1) as usize..] + }); + match args.cmp(&required_args) { + Ordering::Equal => self.execute_callable(&callable, false), + Ordering::Less => { + let app = { + let fields = &self.stack[self.stack.len() - args..]; + let def = PartialApplicationDataDef(callable, fields); + PartialApplication(alloc(&mut self.gc, self.thread, &self.stack.stack, def)?) + }; + for _ in 0..(args + 1) { + self.stack.pop(); + } + self.stack.push(app); + Ok(()) + } + Ordering::Greater => { + let excess_args = args - required_args; + let d = { + let fields = &self.stack[self.stack.len() - excess_args..]; + alloc( + &mut self.gc, + self.thread, + &self.stack.stack, + Def { + tag: 0, + elems: fields, + }, + )? + }; + for _ in 0..excess_args { + self.stack.pop(); + } + // Insert the excess args before the actual closure so it does not get + // collected + let offset = self.stack.len() - required_args - 1; + self.stack.insert_slice(offset, &[Value::from(Data(d))]); + debug!( + "xxxxxx {:?}\n{:?}", + &(*self.stack)[..], + self.stack.stack.get_frames() + ); + self.execute_callable(&callable, true) + } + } + } + + fn do_call(&mut self, args: VmIndex) -> Result<()> { + let function_index = self.stack.len() - 1 - args; + info!( + "Do call {:?} {:?}", + self.stack[function_index], + &(*self.stack)[(function_index + 1) as usize..] + ); + match self.stack[function_index].clone().get_repr() { + Function(ref f) => { + let callable = Callable::Extern(f.clone()); + self.call_function_with_upvars(args, f.args, callable) + } + Closure(ref closure) => { + let callable = Callable::Closure(closure.clone()); + self.call_function_with_upvars(args, closure.function.args, callable) + } + PartialApplication(app) => { + let total_args = app.args.len() as VmIndex + args; + let offset = self.stack.len() - args; + self.stack.insert_slice(offset, &app.args); + self.call_function_with_upvars(total_args, app.function.args(), app.function) + } + x => Err(Error::Message(format!("Cannot call {:?}", x))), + } + } + + fn execute_( + &mut self, + mut index: usize, + instructions: &[Instruction], + function: &BytecodeFunction, + ) -> Result>> { + { + debug!( + ">>>\nEnter frame {}: {:?}\n{:?}", + function.name, + &self.stack[..], + self.stack.frame + ); + } + while let Some(&instr) = instructions.get(index) { + debug_instruction(&self.stack, index, instr); + + if self.hook.flags.contains(HookFlags::LINE_FLAG) { + if let Some(ref mut hook) = self.hook.function { + let current_line = function.debug_info.source_map.line(index); + let previous_line = function + .debug_info + .source_map + .line(self.hook.previous_instruction_index); + self.hook.previous_instruction_index = index; + if current_line != previous_line { + self.stack.frame.instruction_index = index; + self.stack.store_frame(); + let info = DebugInfo { + stack: &self.stack.stack, + state: HookFlags::LINE_FLAG, + }; + try_ready!(hook(self.thread, info)) + } + } + } + + match instr { + Push(i) => { + let v = self.stack[i].clone(); + self.stack.push(v); + } + PushInt(i) => { + self.stack.push(Int(i)); + } + PushByte(b) => { + self.stack.push(ValueRepr::Byte(b)); + } + PushString(string_index) => { + self.stack + .push(String(function.strings[string_index as usize].inner())); + } + PushFloat(f) => self.stack.push(Float(f)), + Call(args) => { + self.stack.frame.instruction_index = index + 1; + return self.do_call(args).map(|x| Async::Ready(Some(x))); + } + TailCall(mut args) => { + let mut amount = self.stack.len() - args; + if self.stack.frame.excess { + amount += 1; + match self.stack.excess_args() { + Some(excess) => { + debug!("TailCall: Push excess args {:?}", excess.fields); + for value in &excess.fields { + self.stack.push(value); + } + args += excess.fields.len() as VmIndex; + } + None => ice!("Expected excess args"), + } + } + debug_assert!( + match self.stack.frame.state { + State::Closure(ref c) => c.function.name == function.name, + _ => false, + }, + "Attempted to pop {:?} but `{}` was expected", + self.stack.frame.state, + function.name + ); + match self.exit_scope() { + Ok(_) => (), + Err(_) => { + self.enter_scope(args + amount + 1, State::Excess); + } + }; + info!( + "Clearing {} {} {:?}", + self.stack.len(), + amount, + &self.stack[..] + ); + let end = self.stack.len() - args - 1; + self.stack.remove_range(end - amount, end); + debug!("{:?}", &self.stack[..]); + return self.do_call(args).map(|x| Async::Ready(Some(x))); + } + Construct { tag, args } => { + let d = { + if args == 0 { + ValueRepr::Tag(tag) + } else { + let fields = &self.stack[self.stack.len() - args..]; + Data(alloc( + &mut self.gc, + self.thread, + &self.stack.stack, + Def { + tag: tag, + elems: fields, + }, + )?) + } + }; + for _ in 0..args { + self.stack.pop(); + } + self.stack.push(d); + } + ConstructRecord { record, args } => { + let d = { + if args == 0 { + ValueRepr::Tag(0) + } else { + let fields = &self.stack[self.stack.len() - args..]; + unsafe { + let roots = Roots { + vm: GcPtr::from_raw(self.thread), + stack: &self.stack.stack, + }; + let field_names = &function.records[record as usize]; + Data(self.gc.alloc_and_collect( + roots, + RecordDef { + elems: fields, + fields: field_names, + }, + )?) + } + } + }; + for _ in 0..args { + self.stack.pop(); + } + self.stack.push(d); + } + ConstructArray(args) => { + let d = { + let fields = &self.stack[self.stack.len() - args..]; + alloc( + &mut self.gc, + self.thread, + &self.stack.stack, + ::value::ArrayDef(fields), + )? + }; + for _ in 0..args { + self.stack.pop(); + } + self.stack.push(ValueRepr::Array(d)); + } + GetOffset(i) => match self.stack.pop().get_repr() { + Data(data) => { + let v = &data.fields[i as usize]; + self.stack.push(v); + } + x => return Err(Error::Message(format!("GetOffset on {:?}", x))), + }, + GetField(i) => { + let field = function.strings[i as usize]; + match self.stack.pop().get_repr() { + Data(data) => { + let v = data.get_field(field).unwrap_or_else(|| { + error!("{}", self.stack.stack.stacktrace(0)); + ice!("Field `{}` does not exist", field) + }); + self.stack.push(v); + } + x => { + return Err(Error::Message(format!("GetField on {:?}", x))); + } + } + } + TestTag(tag) => { + let data_tag = match self.stack.top().get_repr() { + Data(ref data) => data.tag(), + ValueRepr::Tag(tag) => tag, + _ => { + return Err(Error::Message( + "Op TestTag called on non data type".to_string(), + )) + } + }; + self.stack + .push(ValueRepr::Tag(if data_tag == tag { 1 } else { 0 })); + } + Split => { + match self.stack.pop().get_repr() { + Data(data) => for field in &data.fields { + self.stack.push(field); + }, + // Zero argument variant + ValueRepr::Tag(_) => (), + _ => { + return Err(Error::Message( + "Op Split called on non data type".to_string(), + )) + } + } + } + Jump(i) => { + index = i as usize; + continue; + } + CJump(i) => match self.stack.pop().get_repr() { + ValueRepr::Tag(0) => (), + _ => { + index = i as usize; + continue; + } + }, + Pop(n) => for _ in 0..n { + self.stack.pop(); + }, + Slide(n) => { + debug!("{:?}", &self.stack[..]); + let v = self.stack.pop(); + for _ in 0..n { + self.stack.pop(); + } + self.stack.push(v); + } + MakeClosure { + function_index, + upvars, + } => { + let closure = { + let args = &self.stack[self.stack.len() - upvars..]; + let func = function.inner_functions[function_index as usize]; + Closure(alloc( + &mut self.gc, + self.thread, + &self.stack.stack, + ClosureDataDef(func, args), + )?) + }; + for _ in 0..upvars { + self.stack.pop(); + } + self.stack.push(closure); + } + NewClosure { + function_index, + upvars, + } => { + let closure = { + // Use dummy variables until it is filled + let func = function.inner_functions[function_index as usize]; + Closure(alloc( + &mut self.gc, + self.thread, + &self.stack.stack, + ClosureInitDef(func, upvars as usize), + )?) + }; + self.stack.push(closure); + } + CloseClosure(n) => { + let i = self.stack.len() - n - 1; + match self.stack[i].get_repr() { + Closure(mut closure) => { + // Unique access should be safe as this closure should not be shared as + // it has just been allocated and havent even had its upvars set yet + // (which is done here). + unsafe { + for var in closure.as_mut().upvars.iter_mut().rev() { + *var = self.stack.pop(); + } + } + self.stack.pop(); //Remove the closure + } + x => ice!("Expected closure, got {:?}", x), + } + } + PushUpVar(i) => { + let v = self.stack.get_upvar(i).clone(); + self.stack.push(v); + } + AddInt => binop_int(self.thread, &mut self.stack, VmInt::add), + SubtractInt => binop_int(self.thread, &mut self.stack, VmInt::sub), + MultiplyInt => binop_int(self.thread, &mut self.stack, VmInt::mul), + DivideInt => binop_int(self.thread, &mut self.stack, VmInt::div), + IntLT => binop_bool(self.thread, &mut self.stack, |l: VmInt, r| l < r), + IntEQ => binop_bool(self.thread, &mut self.stack, |l: VmInt, r| l == r), + + AddByte => binop_byte(self.thread, &mut self.stack, u8::add), + SubtractByte => binop_byte(self.thread, &mut self.stack, u8::sub), + MultiplyByte => binop_byte(self.thread, &mut self.stack, u8::mul), + DivideByte => binop_byte(self.thread, &mut self.stack, u8::div), + ByteLT => binop_bool(self.thread, &mut self.stack, |l: u8, r| l < r), + ByteEQ => binop_bool(self.thread, &mut self.stack, |l: u8, r| l == r), + + AddFloat => binop_f64(self.thread, &mut self.stack, f64::add), + SubtractFloat => binop_f64(self.thread, &mut self.stack, f64::sub), + MultiplyFloat => binop_f64(self.thread, &mut self.stack, f64::mul), + DivideFloat => binop_f64(self.thread, &mut self.stack, f64::div), + FloatLT => binop_bool(self.thread, &mut self.stack, |l: f64, r| l < r), + FloatEQ => binop_bool(self.thread, &mut self.stack, |l: f64, r| l == r), + } + index += 1; + } + let result = self.stack.top().clone(); + debug!("Return {:?}", result); + let len = self.stack.len(); + let frame_has_excess = self.stack.frame.excess; + + // We might not get access to the frame above the current as it could be locked + debug_assert!( + match self.stack.frame.state { + State::Closure(ref c) => c.function.name == function.name, + _ => false, + }, + "Attempted to pop {:?} but `{}` was expected", + self.stack.frame.state, + function.name + ); + let stack_exists = self.exit_scope().is_ok(); + + self.stack.pop(); + for _ in 0..len { + self.stack.pop(); + } + if frame_has_excess { + // If the function that just finished had extra arguments we need to call the result of + // the call with the extra arguments + match self.stack.pop().get_repr() { + Data(excess) => { + self.enter_scope(0, State::Excess); + debug!("Push excess args {:?}", &excess.fields); + self.stack.push(result); + for value in &excess.fields { + self.stack.push(value); + } + self.do_call(excess.fields.len() as VmIndex) + .map(|x| Async::Ready(Some(x))) + } + x => ice!("Expected excess arguments found {:?}", x), + } + } else { + self.stack.push(result); + Ok(Async::Ready(if stack_exists { Some(()) } else { None })) + } + } +} + +#[inline] +fn binop_int<'b, 'c, F, T>(vm: &'b Thread, stack: &'b mut StackFrame<'c>, f: F) +where + F: FnOnce(T, T) -> VmInt, + T: for<'d, 'e> Getable<'d, 'e> + fmt::Debug, +{ + binop(vm, stack, |l, r| ValueRepr::Int(f(l, r))) +} + +#[inline] +fn binop_f64<'b, 'c, F, T>(vm: &'b Thread, stack: &'b mut StackFrame<'c>, f: F) +where + F: FnOnce(T, T) -> f64, + T: for<'d, 'e> Getable<'d, 'e> + fmt::Debug, +{ + binop(vm, stack, |l, r| ValueRepr::Float(f(l, r))) +} + +#[inline] +fn binop_byte<'b, 'c, F, T>(vm: &'b Thread, stack: &'b mut StackFrame<'c>, f: F) +where + F: FnOnce(T, T) -> u8, + T: for<'d, 'e> Getable<'d, 'e> + fmt::Debug, +{ + binop(vm, stack, |l, r| ValueRepr::Byte(f(l, r))) +} + +#[inline] +fn binop_bool<'b, 'c, F, T>(vm: &'b Thread, stack: &'b mut StackFrame<'c>, f: F) +where + F: FnOnce(T, T) -> bool, + T: for<'d, 'e> Getable<'d, 'e> + fmt::Debug, +{ + binop(vm, stack, |l, r| { + ValueRepr::Tag(if f(l, r) { 1 } else { 0 }) + }) +} + +#[inline] +fn binop<'b, 'c, F, T>(vm: &'b Thread, stack: &'b mut StackFrame<'c>, f: F) +where + F: FnOnce(T, T) -> ValueRepr, + T: for<'d, 'e> Getable<'d, 'e> + fmt::Debug, +{ + let (l, r) = { + let r = stack.get_variant(stack.len() - 1).unwrap(); + let l = stack.get_variant(stack.len() - 2).unwrap(); + (T::from_value(vm, l), T::from_value(vm, r)) + }; + let result = f(l, r); + stack.pop(); + stack.pop(); + stack.stack.push(result); +} + +fn debug_instruction(stack: &StackFrame, index: usize, instr: Instruction) { + debug!( + "{:?}: {:?} -> {:?} {:?}", + index, + instr, + stack.len(), + match instr { + Push(i) => { + let x = stack.get(i as usize).cloned(); + if x.is_none() { + debug!("{:?}", &stack[..]) + } + x + } + NewClosure { .. } | MakeClosure { .. } => Some(Value::from(Int(stack.len() as isize))), + _ => None, + } + ); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn send_vm() { + fn send(_: T) {} + send(RootedThread::new()); + } +} diff --git a/vm/src/vm.rs b/vm/src/vm.rs index f68a6dcc4d..d694b0ea6e 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -290,7 +290,7 @@ impl VmEnv { Some((remaining_fields, global)) } - pub fn get_binding(&self, name: &str) -> Result<(Value, Cow)> { + pub fn get_binding(&self, name: &str) -> Result<(Variants, Cow)> { use base::resolve; let (remaining_fields, global) = self @@ -299,7 +299,10 @@ impl VmEnv { if remaining_fields.as_str().is_empty() { // No fields left - return Ok((global.value.clone(), Cow::Borrowed(&global.typ))); + return Ok(( + unsafe { Variants::new(&global.value) }, + Cow::Borrowed(&global.typ), + )); } let mut typ = Cow::Borrowed(&global.typ); @@ -337,7 +340,7 @@ impl VmEnv { typ = next_type .ok_or_else(move || Error::UndefinedField(typ.into_owned(), field_name.into()))?; } - Ok((value.get_value(), typ)) + Ok((value, typ)) } pub fn get_metadata(&self, name_str: &str) -> Result<&Metadata> {