From a39f730369382cc82ad15234187f7b431964c4ff Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 2 Jun 2017 22:16:23 +0200 Subject: [PATCH 01/18] chore: Move api.rs to make room for more files --- vm/src/{api.rs => api/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename vm/src/{api.rs => api/mod.rs} (100%) diff --git a/vm/src/api.rs b/vm/src/api/mod.rs similarity index 100% rename from vm/src/api.rs rename to vm/src/api/mod.rs From 8318e34168848a31511b8c0ffce8a7f5c1009ee8 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 2 Jun 2017 23:35:26 +0200 Subject: [PATCH 02/18] feat(vm): Allow automatic marshalling from Rust to gluon objects via serde --- Cargo.toml | 2 + vm/Cargo.toml | 2 + vm/src/api/mod.rs | 3 + vm/src/api/serde.rs | 412 ++++++++++++++++++++++++++++++++++++++++++++ vm/src/lib.rs | 4 + 5 files changed, 423 insertions(+) create mode 100644 vm/src/api/serde.rs diff --git a/Cargo.toml b/Cargo.toml index c6442ec46a..2782508e07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,5 +52,7 @@ curl = "0.4.1" [features] default = ["regex"] +serde = ["gluon_vm/serde"] + test = ["gluon_vm/test", "gluon_check/test", "gluon_parser/test"] nightly = ["compiletest_rs"] diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 9849544e71..9b0f9c82f0 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -23,6 +23,7 @@ bitflags = "0.7.0" itertools = "0.6.0" futures = "0.1.0" typed-arena = "1.2.0" +serde = { version = "1.0.0", optional = true } smallvec = "0.2.1" gluon_base = { path = "../base", version = "0.4.2" } # GLUON @@ -37,6 +38,7 @@ gluon = { path = "..", version = "0.4.2" } # GLUON lalrpop-util = "0.13.1" regex = "0.2.0" +serde_derive = "1.0.0" [features] test = ["env_logger", "lalrpop", "gluon_parser"] diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index b6bfe0450f..f48636a21a 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -22,6 +22,9 @@ use futures::{Async, BoxFuture, Future}; pub use value::Userdata; +#[cfg(feature = "serde")] +mod serde; + macro_rules! count { () => { 0 }; ($_e: ident) => { 1 }; diff --git a/vm/src/api/serde.rs b/vm/src/api/serde.rs new file mode 100644 index 0000000000..95c8a8d236 --- /dev/null +++ b/vm/src/api/serde.rs @@ -0,0 +1,412 @@ +extern crate serde; + +use std::fmt; +use std::ops::{Deref, DerefMut}; + +use {Error, Result}; +use api::Pushable; +use thread::{Context, Thread, ThreadInternal}; +use types::{VmTag, VmIndex}; +use value::{Def, Value}; +use self::serde::ser::{self, Serialize}; + +pub fn to_value(thread: &Thread, value: &T) -> Result + where T: Serialize +{ + let mut context = thread.context(); + let mut serializer = Serializer { + thread: thread, + context: &mut context, + }; + value.serialize(&mut serializer)?; + Ok(serializer.context.stack.pop()) +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self + where T: fmt::Display + { + Error::Message(format!("{}", msg)) + } +} + +struct Serializer<'t> { + thread: &'t Thread, + context: &'t mut Context, +} + +impl<'t> Serializer<'t> { + fn to_value(&mut self, value: T) -> Result<()> + where T: Pushable<'t> + { + value.push(self.thread, self.context) + } + + fn alloc(&mut self, tag: VmTag, values: VmIndex) -> Result<()> { + let value = self.context + .gc + .alloc(Def { + tag: tag, + elems: &self.context.stack[self.context.stack.len() - values..], + })?; + for _ in 0..values { + self.context.stack.pop(); + } + self.context.stack.push(Value::Data(value)); + Ok(()) + } +} + +struct RecordSerializer<'s, 'vm: 's> { + serializer: &'s mut Serializer<'vm>, + variant_index: VmTag, + values: VmIndex, +} + +impl<'s, 'vm> Deref for RecordSerializer<'s, 'vm> { + type Target = Serializer<'vm>; + fn deref(&self) -> &Self::Target { + self.serializer + } +} + +impl<'s, 'vm> DerefMut for RecordSerializer<'s, 'vm> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.serializer + } +} + +impl<'s, 'vm> RecordSerializer<'s, 'vm> { + fn new(serializer: &'s mut Serializer<'vm>, variant_index: u32) -> Self { + RecordSerializer { + serializer: serializer, + variant_index: variant_index, + values: 0, + } + } +} + +impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> { + type Ok = (); + + // The error type when some error occurs during serialization. + type Error = Error; + + // Associated types for keeping track of additional state while serializing + // compound data structures like sequences and maps. In this case no + // additional state is required beyond what is already stored in the + // Serializer struct. + type SerializeSeq = RecordSerializer<'a, 'vm>; + type SerializeTuple = RecordSerializer<'a, 'vm>; + type SerializeTupleStruct = RecordSerializer<'a, 'vm>; + type SerializeTupleVariant = RecordSerializer<'a, 'vm>; + type SerializeMap = RecordSerializer<'a, 'vm>; + type SerializeStruct = RecordSerializer<'a, 'vm>; + type SerializeStructVariant = RecordSerializer<'a, 'vm>; + + // Here we go with the simple methods. The following 12 methods receive one + // of the primitive types of the data model and map it to JSON by appending + // into the output string. + fn serialize_bool(self, v: bool) -> Result { + self.to_value(v) + } + + // JSON does not distinguish between different sizes of integers, so all + // signed integers will be serialized the same and all unsigned integers + // will be serialized the same. Other formats, especially compact binary + // formats, may need independent logic for the different sizes. + fn serialize_i8(self, v: i8) -> Result { + self.serialize_i64(v as i64) + } + + fn serialize_i16(self, v: i16) -> Result { + self.serialize_i64(v as i64) + } + + fn serialize_i32(self, v: i32) -> Result { + self.serialize_i64(v as i64) + } + + // Not particularly efficient but this is example code anyway. A more + // performant approach would be to use the `itoa` crate. + fn serialize_i64(self, v: i64) -> Result { + self.to_value(v as isize) + } + + fn serialize_u8(self, v: u8) -> Result { + self.serialize_u64(v as u64) + } + + fn serialize_u16(self, v: u16) -> Result { + self.serialize_u64(v as u64) + } + + fn serialize_u32(self, v: u32) -> Result { + self.serialize_u64(v as u64) + } + + fn serialize_u64(self, v: u64) -> Result { + self.to_value(v as isize) + } + + fn serialize_f32(self, v: f32) -> Result { + self.serialize_f64(v as f64) + } + + fn serialize_f64(self, v: f64) -> Result { + self.to_value(v) + } + + fn serialize_char(self, v: char) -> Result { + self.serialize_str(&v.to_string()) + } + + fn serialize_str(self, v: &str) -> Result { + self.to_value(v) + } + + fn serialize_bytes(self, v: &[u8]) -> Result { + self.to_value(v) + } + + // An absent optional is represented as the JSON `null`. + fn serialize_none(self) -> Result { + self.serialize_unit() + } + + // A present optional is represented as just the contained value. Note that + // this is a lossy representation. For example the values `Some(())` and + // `None` both serialize as just `null`. Unfortunately this is typically + // what people expect when working with JSON. Other formats are encouraged + // to behave more intelligently if possible. + fn serialize_some(self, value: &T) -> Result + where T: ?Sized + Serialize + { + value.serialize(self) + } + + fn serialize_unit(self) -> Result { + self.context.stack.push(Value::Tag(0)); + Ok(()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + self.serialize_unit() + } + + fn serialize_unit_variant(self, + _name: &'static str, + variant_index: u32, + _variant: &'static str) + -> Result { + self.context.stack.push(Value::Tag(variant_index)); + Ok(()) + } + + // As is done here, serializers are encouraged to treat newtype structs as + // insignificant wrappers around the data they contain. + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result + where T: ?Sized + Serialize + { + value.serialize(self) + } + + // Note that newtype variant (and all of the other variant serialization + // methods) refer exclusively to the "externally tagged" enum + // representation. + // + // Serialize this to JSON in externally tagged form as `{ NAME: VALUE }`. + fn serialize_newtype_variant(self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + value: &T) + -> Result + where T: ?Sized + Serialize + { + value.serialize(&mut *self)?; + self.alloc(variant_index, 1) + } + + fn serialize_seq(self, _len: Option) -> Result { + Ok(RecordSerializer::new(self, 0)) + } + + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_struct(self, + _name: &'static str, + len: usize) + -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_variant(self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + _len: usize) + -> Result { + Ok(RecordSerializer::new(self, variant_index)) + } + + fn serialize_map(self, _len: Option) -> Result { + Ok(RecordSerializer::new(self, 0)) + } + + fn serialize_struct(self, _name: &'static str, len: usize) -> Result { + self.serialize_map(Some(len)) + } + + fn serialize_struct_variant(self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + _len: usize) + -> Result { + Ok(RecordSerializer::new(self, variant_index)) + } +} + +impl<'a, 'vm> ser::SerializeSeq for RecordSerializer<'a, 'vm> { + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + self.serializer.alloc(self.variant_index, self.values) + } +} + +impl<'a, 'vm> ser::SerializeTuple for RecordSerializer<'a, 'vm> { + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + self.serializer.alloc(self.variant_index, self.values) + } +} + +impl<'a, 'vm> ser::SerializeTupleStruct for RecordSerializer<'a, 'vm> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + self.serializer.alloc(self.variant_index, self.values) + } +} + +impl<'a, 'vm> ser::SerializeTupleVariant for RecordSerializer<'a, 'vm> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + self.serializer.alloc(self.variant_index, self.values) + } +} + +impl<'a, 'vm> ser::SerializeMap for RecordSerializer<'a, 'vm> { + type Ok = (); + type Error = Error; + + fn serialize_key(&mut self, _key: &T) -> Result<()> + where T: ?Sized + Serialize + { + Ok(()) + } + + fn serialize_value(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + self.serializer.alloc(self.variant_index, self.values) + } +} + +impl<'a, 'vm> ser::SerializeStruct for RecordSerializer<'a, 'vm> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + self.serializer.alloc(self.variant_index, self.values) + } +} + +// Similar to `SerializeTupleVariant`, here the `end` method is responsible for +// closing both of the curly braces opened by `serialize_struct_variant`. +impl<'a, 'vm> ser::SerializeStructVariant for RecordSerializer<'a, 'vm> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + value.serialize(&mut **self)?; + self.values += 1; + Ok(()) + } + + fn end(self) -> Result { + self.serializer.alloc(self.variant_index, self.values) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use thread::{RootedThread, ThreadInternal}; + + #[derive(Serialize)] + struct Test { + test: i32, + string: String, + } + + fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> Value + where T: Pushable<'vm> + { + let mut context = thread.context(); + value.push(thread, &mut context); + context.stack.pop() + } + + #[test] + fn bool() { + let thread = RootedThread::new(); + assert_eq!(to_value(&thread, &true).unwrap(), Value::Tag(1)); + } +} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 31b2c86030..022b553640 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -18,6 +18,10 @@ extern crate pretty; #[macro_use] extern crate futures; +#[cfg(all(feature = "serde", test))] +#[macro_use] +extern crate serde_derive; + #[macro_use] extern crate gluon_base as base; extern crate gluon_check as check; From 95a9761a24634926d882c57bca0bdb8b1bbc7e06 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 2 Jun 2017 23:37:52 +0200 Subject: [PATCH 03/18] refactor(vm): Move api macros to a separate module Reduces the size of the api module and lets the serde module use the macros in tests --- vm/src/api/mac.rs | 219 +++++++++++++++++++++++++++++++++++++++++++++ vm/src/api/mod.rs | 223 +--------------------------------------------- 2 files changed, 222 insertions(+), 220 deletions(-) create mode 100644 vm/src/api/mac.rs diff --git a/vm/src/api/mac.rs b/vm/src/api/mac.rs new file mode 100644 index 0000000000..af5982e36f --- /dev/null +++ b/vm/src/api/mac.rs @@ -0,0 +1,219 @@ + +/// Creates a `GluonFunction` from a function implementing `VMFunction` +/// +/// ```rust +/// #[macro_use] +/// extern crate gluon_vm; +/// fn test(_x: i32, _y: String) -> f64 { +/// panic!() +/// } +/// +/// fn main() { +/// primitive!(2 test); +/// } +/// ``` +#[macro_export] +macro_rules! primitive { + (0 $name: expr) => { + unsafe { + extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { + $crate::api::VmFunction::unpack_and_call( + &($name as fn () -> _), thread) + } + $crate::api::primitive_f(stringify!($name), wrapper, $name as fn () -> _) + } + }; + (1 $name: expr) => { + unsafe { + extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { + $crate::api::VmFunction::unpack_and_call( + &($name as fn (_) -> _), thread) + } + $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_) -> _) + } + }; + (2 $name: expr) => { + unsafe { + extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { + $crate::api::VmFunction::unpack_and_call( + &($name as fn (_, _) -> _), thread) + } + $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _) -> _) + } + }; + (3 $name: expr) => { + unsafe { + extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { + $crate::api::VmFunction::unpack_and_call( + &($name as fn (_, _, _) -> _), thread) + } + $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _) -> _) + } + }; + (4 $name: expr) => { + unsafe { + extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { + $crate::api::VmFunction::unpack_and_call( + &($name as fn (_, _, _, _) -> _), thread) + } + $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _, _) -> _) + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! field_decl_inner { + ($($field: ident),*) => { + $( + #[allow(non_camel_case_types)] + #[derive(Default)] + pub struct $field; + impl $crate::api::record::Field for $field { + fn name() -> &'static str { + stringify!($field) + } + } + )* + } +} + +/// Declares fields useable by the record macros +/// +/// ```rust +/// #[macro_use] +/// extern crate gluon_vm; +/// # fn main() { } +/// +/// field_decl! {x, y} +/// ``` +#[macro_export] +macro_rules! field_decl { + ($($field: ident),*) => { + mod _field { field_decl_inner!($($field),*); } + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! record_no_decl_inner { + () => { () }; + ($field: ident => $value: expr) => { + $crate::api::record::HList((_field::$field, $value), ()) + }; + ($field: ident => $value: expr, $($field_tail: ident => $value_tail: expr),*) => { + $crate::api::record::HList((_field::$field, $value), + record_no_decl_inner!($($field_tail => $value_tail),*)) + }; +} + +/// Macro that creates a record that can be passed to gluon. Reuses already declared fields +/// instead of generating unique ones. +/// +/// ```rust +/// #[macro_use] +/// extern crate gluon_vm; +/// +/// field_decl! {x, y, name} +/// +/// fn main() { +/// record_no_decl!(x => 1, y => 2, name => "Gluon"); +/// } +/// ``` +#[macro_export] +macro_rules! record_no_decl { + ($($field: ident => $value: expr),*) => { + { + $crate::api::Record { + fields: record_no_decl_inner!($($field => $value),*) + } + } + } +} + +/// Macro that creates a record that can be passed to gluon +/// +/// ```rust +/// #[macro_use] +/// extern crate gluon_vm; +/// fn main() { +/// record!(x => 1, y => 2, name => "Gluon"); +/// } +/// ``` +#[macro_export] +macro_rules! record { + ($($field: ident => $value: expr),*) => { + { + field_decl!($($field),*); + record_no_decl!($($field => $value),*) + } + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! record_type_inner { + () => { () }; + ($field: ident => $value: ty) => { + $crate::api::record::HList<(_field::$field, $value), ()> + }; + ($field: ident => $value: ty, $($field_tail: ident => $value_tail: ty),*) => { + $crate::api::record::HList<(_field::$field, $value), + record_type_inner!( $($field_tail => $value_tail),*)> + } +} + +/// Creates a Rust type compatible with the type of `record_no_decl!` +/// +/// ```rust +/// #[macro_use] +/// extern crate gluon_vm; +/// # fn main() { } +/// // Fields used in `record_type!` needs to be forward declared +/// field_decl! {x, y} +/// fn new_vec(x: f64, y: f64) -> record_type!(x => f64, y => f64) { +/// record_no_decl!(x => y, y => y) +/// } +/// ``` +#[macro_export] +macro_rules! record_type { + ($($field: ident => $value: ty),*) => { + $crate::api::Record< + record_type_inner!($($field => $value),*) + > + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! record_p_impl { + () => { () }; + ($field: pat) => { + $crate::api::record::HList((_, $field), ()) + }; + ($field: pat, $($field_tail: pat),*) => { + $crate::api::record::HList((_, $field), + record_p_impl!($($field_tail),*)) + } +} + +/// Creates a pattern which matches on marshalled gluon records +/// +/// ```rust +/// #[macro_use] +/// extern crate gluon_vm; +/// fn main() { +/// match record!(x => 1, y => "y") { +/// record_p!(a, "y") => assert_eq!(a, 1), +/// _ => assert!(false), +/// } +/// } +/// ``` +#[macro_export] +macro_rules! record_p { + ($($field: pat),*) => { + $crate::api::Record { + fields: record_p_impl!($($field),*) + } + } +} diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index f48636a21a..d41e578c37 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -22,8 +22,10 @@ use futures::{Async, BoxFuture, Future}; pub use value::Userdata; +#[macro_use] +pub mod mac; #[cfg(feature = "serde")] -mod serde; +pub mod serde; macro_rules! count { () => { 0 }; @@ -162,68 +164,6 @@ where } } -/// Creates a `GluonFunction` from a function implementing `VMFunction` -/// -/// ```rust -/// #[macro_use] -/// extern crate gluon_vm; -/// fn test(_x: i32, _y: String) -> f64 { -/// panic!() -/// } -/// -/// fn main() { -/// primitive!(2 test); -/// } -/// ``` -#[macro_export] -macro_rules! primitive { - (0 $name: expr) => { - unsafe { - extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { - $crate::api::VmFunction::unpack_and_call( - &($name as fn () -> _), thread) - } - $crate::api::primitive_f(stringify!($name), wrapper, $name as fn () -> _) - } - }; - (1 $name: expr) => { - unsafe { - extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { - $crate::api::VmFunction::unpack_and_call( - &($name as fn (_) -> _), thread) - } - $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_) -> _) - } - }; - (2 $name: expr) => { - unsafe { - extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { - $crate::api::VmFunction::unpack_and_call( - &($name as fn (_, _) -> _), thread) - } - $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _) -> _) - } - }; - (3 $name: expr) => { - unsafe { - extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { - $crate::api::VmFunction::unpack_and_call( - &($name as fn (_, _, _) -> _), thread) - } - $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _) -> _) - } - }; - (4 $name: expr) => { - unsafe { - extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status { - $crate::api::VmFunction::unpack_and_call( - &($name as fn (_, _, _, _) -> _), thread) - } - $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _, _) -> _) - } - }; -} - #[derive(PartialEq)] pub struct Generic(pub Value, PhantomData); @@ -1441,163 +1381,6 @@ pub mod record { } } -#[doc(hidden)] -#[macro_export] -macro_rules! field_decl_inner { - ($($field: ident),*) => { - $( - #[allow(non_camel_case_types)] - #[derive(Default)] - pub struct $field; - impl $crate::api::record::Field for $field { - fn name() -> &'static str { - stringify!($field) - } - } - )* - } -} - -/// Declares fields useable by the record macros -/// -/// ```rust -/// #[macro_use] -/// extern crate gluon_vm; -/// # fn main() { } -/// -/// field_decl! {x, y} -/// ``` -#[macro_export] -macro_rules! field_decl { - ($($field: ident),*) => { - mod _field { field_decl_inner!($($field),*); } - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! record_no_decl_inner { - () => { () }; - ($field: ident => $value: expr) => { - $crate::api::record::HList((_field::$field, $value), ()) - }; - ($field: ident => $value: expr, $($field_tail: ident => $value_tail: expr),*) => { - $crate::api::record::HList((_field::$field, $value), - record_no_decl_inner!($($field_tail => $value_tail),*)) - }; -} - -/// Macro that creates a record that can be passed to gluon. Reuses already declared fields -/// instead of generating unique ones. -/// -/// ```rust -/// #[macro_use] -/// extern crate gluon_vm; -/// -/// field_decl! {x, y, name} -/// -/// fn main() { -/// record_no_decl!(x => 1, y => 2, name => "Gluon"); -/// } -/// ``` -#[macro_export] -macro_rules! record_no_decl { - ($($field: ident => $value: expr),*) => { - { - $crate::api::Record { - fields: record_no_decl_inner!($($field => $value),*) - } - } - } -} - -/// Macro that creates a record that can be passed to gluon -/// -/// ```rust -/// #[macro_use] -/// extern crate gluon_vm; -/// fn main() { -/// record!(x => 1, y => 2, name => "Gluon"); -/// } -/// ``` -#[macro_export] -macro_rules! record { - ($($field: ident => $value: expr),*) => { - { - field_decl!($($field),*); - record_no_decl!($($field => $value),*) - } - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! record_type_inner { - () => { () }; - ($field: ident => $value: ty) => { - $crate::api::record::HList<(_field::$field, $value), ()> - }; - ($field: ident => $value: ty, $($field_tail: ident => $value_tail: ty),*) => { - $crate::api::record::HList<(_field::$field, $value), - record_type_inner!( $($field_tail => $value_tail),*)> - } -} - -/// Creates a Rust type compatible with the type of `record_no_decl!` -/// -/// ```rust -/// #[macro_use] -/// extern crate gluon_vm; -/// # fn main() { } -/// // Fields used in `record_type!` needs to be forward declared -/// field_decl! {x, y} -/// fn new_vec(x: f64, y: f64) -> record_type!(x => f64, y => f64) { -/// record_no_decl!(x => y, y => y) -/// } -/// ``` -#[macro_export] -macro_rules! record_type { - ($($field: ident => $value: ty),*) => { - $crate::api::Record< - record_type_inner!($($field => $value),*) - > - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! record_p_impl { - () => { () }; - ($field: pat) => { - $crate::api::record::HList((_, $field), ()) - }; - ($field: pat, $($field_tail: pat),*) => { - $crate::api::record::HList((_, $field), - record_p_impl!($($field_tail),*)) - } -} - -/// Creates a pattern which matches on marshalled gluon records -/// -/// ```rust -/// #[macro_use] -/// extern crate gluon_vm; -/// fn main() { -/// match record!(x => 1, y => "y") { -/// record_p!(a, "y") => assert_eq!(a, 1), -/// _ => assert!(false), -/// } -/// } -/// ``` -#[macro_export] -macro_rules! record_p { - ($($field: pat),*) => { - $crate::api::Record { - fields: record_p_impl!($($field),*) - } - } -} - impl<'vm, F: VmType> VmType for Primitive { type Type = F::Type; fn make_type(vm: &Thread) -> ArcType { From 58983bd4cd070c8738eae7630d66c4a8bd0784b1 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 2 Jun 2017 23:48:37 +0200 Subject: [PATCH 04/18] test: Add a test for enum serializing --- vm/src/api/serde.rs | 89 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/vm/src/api/serde.rs b/vm/src/api/serde.rs index 95c8a8d236..af5ece9997 100644 --- a/vm/src/api/serde.rs +++ b/vm/src/api/serde.rs @@ -277,7 +277,9 @@ impl<'a, 'vm> ser::SerializeSeq for RecordSerializer<'a, 'vm> { fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize { - value.serialize(&mut **self) + value.serialize(&mut **self)?; + self.values += 1; + Ok(()) } fn end(self) -> Result { @@ -292,7 +294,9 @@ impl<'a, 'vm> ser::SerializeTuple for RecordSerializer<'a, 'vm> { fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize { - value.serialize(&mut **self) + value.serialize(&mut **self)?; + self.values += 1; + Ok(()) } fn end(self) -> Result { @@ -307,7 +311,9 @@ impl<'a, 'vm> ser::SerializeTupleStruct for RecordSerializer<'a, 'vm> { fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize { - value.serialize(&mut **self) + value.serialize(&mut **self)?; + self.values += 1; + Ok(()) } fn end(self) -> Result { @@ -322,7 +328,9 @@ impl<'a, 'vm> ser::SerializeTupleVariant for RecordSerializer<'a, 'vm> { fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize { - value.serialize(&mut **self) + value.serialize(&mut **self)?; + self.values += 1; + Ok(()) } fn end(self) -> Result { @@ -343,7 +351,9 @@ impl<'a, 'vm> ser::SerializeMap for RecordSerializer<'a, 'vm> { fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize { - value.serialize(&mut **self) + value.serialize(&mut **self)?; + self.values += 1; + Ok(()) } fn end(self) -> Result { @@ -358,7 +368,9 @@ impl<'a, 'vm> ser::SerializeStruct for RecordSerializer<'a, 'vm> { fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize { - value.serialize(&mut **self) + value.serialize(&mut **self)?; + self.values += 1; + Ok(()) } fn end(self) -> Result { @@ -390,17 +402,11 @@ mod tests { use super::*; use thread::{RootedThread, ThreadInternal}; - #[derive(Serialize)] - struct Test { - test: i32, - string: String, - } - fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> Value where T: Pushable<'vm> { let mut context = thread.context(); - value.push(thread, &mut context); + value.push(thread, &mut context).unwrap(); context.stack.pop() } @@ -409,4 +415,61 @@ mod tests { let thread = RootedThread::new(); assert_eq!(to_value(&thread, &true).unwrap(), Value::Tag(1)); } + + #[derive(Serialize)] + struct Record { + test: i32, + string: String, + } + + #[test] + fn record() { + let thread = RootedThread::new(); + let actual = to_value(&thread, + &Record { + test: 123, + string: "abc".to_string(), + }) + .unwrap(); + assert_eq!(actual, + make_value(&thread, + record! { + test => 123, + string => "abc" + })); + } + + #[derive(Serialize)] + enum Enum { + A(String), + B(i32), + } + + #[test] + fn enum_() { + let thread = RootedThread::new(); + + let actual = to_value(&thread, &Enum::A("abc".to_string())).unwrap(); + let s = make_value(&thread, "abc"); + assert_eq!(actual, + Value::Data(thread + .context() + .gc + .alloc(Def { + tag: 0, + elems: &[s], + }) + .unwrap())); + + let actual = to_value(&thread, &Enum::B(1232)).unwrap(); + assert_eq!(actual, + Value::Data(thread + .context() + .gc + .alloc(Def { + tag: 1, + elems: &[Value::Int(1232)], + }) + .unwrap())); + } } From 712703e2a16256d142bfd7d6daaf2c71ba542826 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 3 Jun 2017 11:39:43 +0200 Subject: [PATCH 05/18] feat(vm): Add serialization Pushable wrapper --- vm/src/api/serde.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/vm/src/api/serde.rs b/vm/src/api/serde.rs index af5ece9997..b4d556d72e 100644 --- a/vm/src/api/serde.rs +++ b/vm/src/api/serde.rs @@ -11,15 +11,25 @@ use value::{Def, Value}; use self::serde::ser::{self, Serialize}; pub fn to_value(thread: &Thread, value: &T) -> Result - where T: Serialize + where T: ?Sized + Serialize { let mut context = thread.context(); - let mut serializer = Serializer { - thread: thread, - context: &mut context, - }; - value.serialize(&mut serializer)?; - Ok(serializer.context.stack.pop()) + Ser(value).push(thread, &mut context)?; + Ok(context.stack.pop()) +} + +pub struct Ser(pub T); + +impl<'vm, T> Pushable<'vm> for Ser + where T: Serialize +{ + fn push(self, thread: &'vm Thread, context: &mut Context) -> Result<()> { + let mut serializer = Serializer { + thread: thread, + context: context, + }; + self.0.serialize(&mut serializer) + } } impl ser::Error for Error { From 738bb63cbf3d6cabfddc9c4651380ea21e85d7fa Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 11 Jun 2017 15:02:00 +0200 Subject: [PATCH 06/18] Allow Variants to be constructed with an explicit root The `Array` type do not store `Value` directly but instead creates `Value` types on demand. The actual data is still lives as long as the the array itself though so the `Variants` need to reflect that to work in deserialization --- vm/src/api/mod.rs | 87 ++++++++++++-------- vm/src/api/serde.rs | 186 +++++++++++++++++++++++++------------------ vm/src/core/mod.rs | 3 +- vm/src/lib.rs | 8 +- vm/src/primitives.rs | 4 +- vm/src/stack.rs | 10 ++- vm/src/thread.rs | 31 ++++---- vm/src/value.rs | 31 +++++++- 8 files changed, 227 insertions(+), 133 deletions(-) diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index d41e578c37..942b540b8d 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -40,6 +40,7 @@ pub enum ValueRef<'a> { Float(f64), String(&'a str), Data(Data<'a>), + Array(ArrayRef<'a>), Tag(VmTag), Userdata(&'a vm::Userdata), Internal, @@ -102,6 +103,10 @@ impl<'a> Data<'a> { pub fn get(&self, index: usize) -> Option> { self.0.fields.get(index).map(ValueRef::new) } + + pub fn get_variants(&self, index: usize) -> Option> { + unsafe { self.0.fields.get(index).map(|v| Variants::new(v)) } + } } /// Marker type representing a hole @@ -198,7 +203,7 @@ impl<'vm, T: VmType> Pushable<'vm> for Generic { } impl<'vm, T> Getable<'vm> for Generic { fn from_value(_: &'vm Thread, value: Variants) -> Option> { - Some(Generic::from(*value.0)) + Some(Generic::from(value.0)) } } @@ -321,7 +326,7 @@ impl<'vm, T: vm::Userdata> Pushable<'vm> for T { impl<'vm> Getable<'vm> for Value { fn from_value(_vm: &'vm Thread, value: Variants) -> Option { - Some(*value.0) + Some(value.0) } } @@ -675,8 +680,8 @@ where } impl<'s, 'vm, T: Copy + ArrayRepr> Getable<'vm> for &'s [T] { unsafe fn from_value_unsafe(_: &'vm Thread, value: Variants) -> Option { - match *value.0 { - Value::Array(ptr) => ptr.as_slice().map(|s| &*(s as *const _)), + match value.as_ref() { + ValueRef::Array(ptr) => ptr.0.as_slice().map(|s| &*(s as *const _)), _ => None, } } @@ -781,15 +786,15 @@ impl<'vm, T: Pushable<'vm>> Pushable<'vm> for Option { } impl<'vm, T: Getable<'vm>> Getable<'vm> for Option { fn from_value(vm: &'vm Thread, value: Variants) -> Option> { - match *value.0 { - Value::Data(data) => { - if data.tag == 0 { + match value.as_ref() { + ValueRef::Data(data) => { + if data.tag() == 0 { Some(None) } else { - T::from_value(vm, Variants(&data.fields[0])).map(Some) + T::from_value(vm, data.get_variants(0).unwrap()).map(Some) } } - Value::Tag(0) => Some(None), + ValueRef::Tag(0) => Some(None), _ => None, } } @@ -837,11 +842,11 @@ 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) -> Option> { - match *value.0 { - Value::Data(data) => { - match data.tag { - 0 => E::from_value(vm, Variants(&data.fields[0])).map(Err), - 1 => T::from_value(vm, Variants(&data.fields[0])).map(Ok), + match value.as_ref() { + ValueRef::Data(data) => { + match data.tag() { + 0 => E::from_value(vm, data.get_variants(0).unwrap()).map(Err), + 1 => T::from_value(vm, data.get_variants(0).unwrap()).map(Ok), _ => None, } } @@ -1016,13 +1021,30 @@ where impl<'vm, V> Getable<'vm> for OpaqueValue<&'vm Thread, V> { fn from_value(vm: &'vm Thread, value: Variants) -> Option> { - Some(OpaqueValue(vm.root_value(*value.0), PhantomData)) + Some(OpaqueValue(vm.root_value(value.0), PhantomData)) } } impl<'vm, V> Getable<'vm> for OpaqueValue { fn from_value(vm: &'vm Thread, value: Variants) -> Option> { - Some(OpaqueValue(vm.root_value(*value.0), PhantomData)) + Some(OpaqueValue(vm.root_value(value.0), PhantomData)) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct ArrayRef<'vm>(&'vm ValueArray); + +impl<'vm> ArrayRef<'vm> { + pub fn get(&self, index: usize) -> Option { + if index < self.0.len() { + unsafe { Some(Variants::with_root(self.0.get(index), self)) } + } else { + None + } + } + + pub fn iter(&self) -> ::value::VariantIter { + self.0.variant_iter() } } @@ -1047,12 +1069,14 @@ impl<'vm, T> Array<'vm, T> { impl<'vm, T: for<'vm2> Getable<'vm2>> Array<'vm, T> { pub fn get(&self, index: VmInt) -> Option { - match *self.0 { - Value::Array(data) => { - let v = data.get(index as usize); - T::from_value(self.0.vm(), Variants(&v)) + unsafe { + match Variants::new(&self.0).as_ref() { + ValueRef::Array(data) => { + let v = data.get(index as usize).unwrap(); + T::from_value(self.0.vm(), v) + } + _ => None, } - _ => None, } } } @@ -1080,7 +1104,7 @@ where impl<'vm, T> Getable<'vm> for Array<'vm, T> { fn from_value(vm: &'vm Thread, value: Variants) -> Option> { - Some(Array(vm.root_value(*value.0), PhantomData)) + Some(Array(vm.root_value(value.0), PhantomData)) } } @@ -1089,7 +1113,7 @@ impl<'vm, T: Any> VmType for Root<'vm, T> { } impl<'vm, T: vm::Userdata> Getable<'vm> for Root<'vm, T> { fn from_value(vm: &'vm Thread, value: Variants) -> Option> { - match *value.0 { + match value.0 { Value::Userdata(data) => vm.root::(data).map(From::from), _ => None, } @@ -1101,7 +1125,7 @@ impl<'vm> VmType for RootStr<'vm> { } impl<'vm> Getable<'vm> for RootStr<'vm> { fn from_value(vm: &'vm Thread, value: Variants) -> Option> { - match *value.0 { + match value.0 { Value::String(v) => Some(vm.root_string(v)), _ => None, } @@ -1160,9 +1184,10 @@ macro_rules! define_tuple { fn from_value(vm: &'vm Thread, value: Variants) -> Option<($($id),+)> { match value.as_ref() { ValueRef::Data(v) => { + assert!(v.len() == count!($($id),+)); let mut i = 0; let x = ( $( - { let a = $id::from_value(vm, Variants(&v.0.fields[i])); i += 1; a } + { let a = $id::from_value(vm, v.get_variants(i).unwrap()); i += 1; a } ),+ ); match x { ($(Some($id)),+) => Some(( $($id),+ )), @@ -1367,7 +1392,7 @@ pub mod record { T: GetableFieldList<'vm>, { fn from_value(vm: &'vm Thread, value: Variants) -> Option { - match *value.0 { + match value.0 { Value::Data(ref data) => { HList::<(F, A), T>::from_value(vm, &data.fields).map( |fields| { @@ -1539,7 +1564,7 @@ where impl<'vm, F> Getable<'vm> for Function<&'vm Thread, F> { fn from_value(vm: &'vm Thread, value: Variants) -> Option> { Some(Function { - value: vm.root_value(*value.0), + value: vm.root_value(value.0), _marker: PhantomData, }) //TODO not type safe } @@ -1548,7 +1573,7 @@ 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) -> Option { Some(Function { - value: vm.root_value(*value.0), + value: vm.root_value(value.0), _marker: PhantomData, }) //TODO not type safe } @@ -1630,7 +1655,7 @@ where $($args: Getable<'vm> + 'vm,)* let (lock, ($($args,)*)) = { let stack = StackFrame::current(&mut context.stack); $(let $args = { - let x = $args::from_value_unsafe(vm, Variants(&stack[i])) + let x = $args::from_value_unsafe(vm, Variants::new(&stack[i])) .expect(stringify!(Argument $args)); i += 1; x @@ -1678,7 +1703,7 @@ where $($args: Getable<'vm> + 'vm,)* let (lock, ($($args,)*)) = { let stack = StackFrame::current(&mut context.stack); $(let $args = { - let x = $args::from_value_unsafe(vm, Variants(&stack[i])) + let x = $args::from_value_unsafe(vm, Variants::new(&stack[i])) .expect(stringify!(Argument $args)); i += 1; x @@ -1734,7 +1759,7 @@ impl<'vm, T, $($args,)* R> Function R> } fn return_value(vm: &Thread, value: Value) -> Result { - R::from_value(vm, Variants(&value)) + R::from_value(vm, Variants::new(&value)) .ok_or_else(|| { error!("Wrong type {:?}", value); Error::Message("Wrong type".to_string()) diff --git a/vm/src/api/serde.rs b/vm/src/api/serde.rs index b4d556d72e..f431b135e4 100644 --- a/vm/src/api/serde.rs +++ b/vm/src/api/serde.rs @@ -11,7 +11,8 @@ use value::{Def, Value}; use self::serde::ser::{self, Serialize}; pub fn to_value(thread: &Thread, value: &T) -> Result - where T: ?Sized + Serialize +where + T: ?Sized + Serialize, { let mut context = thread.context(); Ser(value).push(thread, &mut context)?; @@ -21,7 +22,8 @@ pub fn to_value(thread: &Thread, value: &T) -> Result pub struct Ser(pub T); impl<'vm, T> Pushable<'vm> for Ser - where T: Serialize +where + T: Serialize, { fn push(self, thread: &'vm Thread, context: &mut Context) -> Result<()> { let mut serializer = Serializer { @@ -34,7 +36,8 @@ impl<'vm, T> Pushable<'vm> for Ser impl ser::Error for Error { fn custom(msg: T) -> Self - where T: fmt::Display + where + T: fmt::Display, { Error::Message(format!("{}", msg)) } @@ -47,18 +50,17 @@ struct Serializer<'t> { impl<'t> Serializer<'t> { fn to_value(&mut self, value: T) -> Result<()> - where T: Pushable<'t> + where + T: Pushable<'t>, { value.push(self.thread, self.context) } fn alloc(&mut self, tag: VmTag, values: VmIndex) -> Result<()> { - let value = self.context - .gc - .alloc(Def { - tag: tag, - elems: &self.context.stack[self.context.stack.len() - values..], - })?; + let value = self.context.gc.alloc(Def { + tag: tag, + elems: &self.context.stack[self.context.stack.len() - values..], + })?; for _ in 0..values { self.context.stack.pop(); } @@ -190,7 +192,8 @@ impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> { // what people expect when working with JSON. Other formats are encouraged // to behave more intelligently if possible. fn serialize_some(self, value: &T) -> Result - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(self) } @@ -204,11 +207,12 @@ impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> { self.serialize_unit() } - fn serialize_unit_variant(self, - _name: &'static str, - variant_index: u32, - _variant: &'static str) - -> Result { + fn serialize_unit_variant( + self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + ) -> Result { self.context.stack.push(Value::Tag(variant_index)); Ok(()) } @@ -216,7 +220,8 @@ impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> { // As is done here, serializers are encouraged to treat newtype structs as // insignificant wrappers around the data they contain. fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(self) } @@ -226,13 +231,15 @@ impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> { // representation. // // Serialize this to JSON in externally tagged form as `{ NAME: VALUE }`. - fn serialize_newtype_variant(self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - value: &T) - -> Result - where T: ?Sized + Serialize + fn serialize_newtype_variant( + self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, { value.serialize(&mut *self)?; self.alloc(variant_index, 1) @@ -246,19 +253,21 @@ impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> { self.serialize_seq(Some(len)) } - fn serialize_tuple_struct(self, - _name: &'static str, - len: usize) - -> Result { + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { self.serialize_seq(Some(len)) } - fn serialize_tuple_variant(self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - _len: usize) - -> Result { + fn serialize_tuple_variant( + self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { Ok(RecordSerializer::new(self, variant_index)) } @@ -270,12 +279,13 @@ impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> { self.serialize_map(Some(len)) } - fn serialize_struct_variant(self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - _len: usize) - -> Result { + fn serialize_struct_variant( + self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { Ok(RecordSerializer::new(self, variant_index)) } } @@ -285,7 +295,8 @@ impl<'a, 'vm> ser::SerializeSeq for RecordSerializer<'a, 'vm> { type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(&mut **self)?; self.values += 1; @@ -302,7 +313,8 @@ impl<'a, 'vm> ser::SerializeTuple for RecordSerializer<'a, 'vm> { type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(&mut **self)?; self.values += 1; @@ -319,7 +331,8 @@ impl<'a, 'vm> ser::SerializeTupleStruct for RecordSerializer<'a, 'vm> { type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(&mut **self)?; self.values += 1; @@ -336,7 +349,8 @@ impl<'a, 'vm> ser::SerializeTupleVariant for RecordSerializer<'a, 'vm> { type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(&mut **self)?; self.values += 1; @@ -353,13 +367,15 @@ impl<'a, 'vm> ser::SerializeMap for RecordSerializer<'a, 'vm> { type Error = Error; fn serialize_key(&mut self, _key: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(&mut **self)?; self.values += 1; @@ -376,7 +392,8 @@ impl<'a, 'vm> ser::SerializeStruct for RecordSerializer<'a, 'vm> { type Error = Error; fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(&mut **self)?; self.values += 1; @@ -395,7 +412,8 @@ impl<'a, 'vm> ser::SerializeStructVariant for RecordSerializer<'a, 'vm> { type Error = Error; fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> - where T: ?Sized + Serialize + where + T: ?Sized + Serialize, { value.serialize(&mut **self)?; self.values += 1; @@ -413,7 +431,8 @@ mod tests { use thread::{RootedThread, ThreadInternal}; fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> Value - where T: Pushable<'vm> + where + T: Pushable<'vm>, { let mut context = thread.context(); value.push(thread, &mut context).unwrap(); @@ -435,18 +454,23 @@ mod tests { #[test] fn record() { let thread = RootedThread::new(); - let actual = to_value(&thread, - &Record { - test: 123, - string: "abc".to_string(), - }) - .unwrap(); - assert_eq!(actual, - make_value(&thread, - record! { + let actual = to_value( + &thread, + &Record { + test: 123, + string: "abc".to_string(), + }, + ).unwrap(); + assert_eq!( + actual, + make_value( + &thread, + record! { test => 123, string => "abc" - })); + }, + ) + ); } #[derive(Serialize)] @@ -461,25 +485,33 @@ mod tests { let actual = to_value(&thread, &Enum::A("abc".to_string())).unwrap(); let s = make_value(&thread, "abc"); - assert_eq!(actual, - Value::Data(thread - .context() - .gc - .alloc(Def { - tag: 0, - elems: &[s], - }) - .unwrap())); + assert_eq!( + actual, + Value::Data( + thread + .context() + .gc + .alloc(Def { + tag: 0, + elems: &[s], + }) + .unwrap(), + ) + ); let actual = to_value(&thread, &Enum::B(1232)).unwrap(); - assert_eq!(actual, - Value::Data(thread - .context() - .gc - .alloc(Def { - tag: 1, - elems: &[Value::Int(1232)], - }) - .unwrap())); + assert_eq!( + actual, + Value::Data( + thread + .context() + .gc + .alloc(Def { + tag: 1, + elems: &[Value::Int(1232)], + }) + .unwrap(), + ) + ); } } diff --git a/vm/src/core/mod.rs b/vm/src/core/mod.rs index 6df5a0be76..035505952a 100644 --- a/vm/src/core/mod.rs +++ b/vm/src/core/mod.rs @@ -377,7 +377,8 @@ impl<'a, 'e> Translator<'a, 'e> { } } ast::Expr::IfElse(ref pred, ref if_true, ref if_false) => { - let alts: SmallVec<[_; 2]> = collect![ + let alts: SmallVec<[_; 2]> = + collect![ Alternative { pattern: Pattern::Constructor(self.bool_constructor(true), vec![]), expr: self.translate_alloc(if_true), diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 022b553640..fb40a69ce2 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -64,13 +64,17 @@ impl<'a> Variants<'a> { /// Creates a new `Variants` value which assumes that `value` is alive for the lifetime of the /// value pub unsafe fn new(value: &Value) -> Variants { - Variants(value) + Variants::with_root(*value, value) + } + + pub unsafe fn with_root(value: Value, _root: &T) -> Variants { + Variants(value, PhantomData) } /// Returns an instance of `ValueRef` which allows users to safely retrieve the interals of a /// value pub fn as_ref(&self) -> ValueRef { - ValueRef::new(self.0) + ValueRef::new(&self.0) } } diff --git a/vm/src/primitives.rs b/vm/src/primitives.rs index 65a229b665..daba936e28 100644 --- a/vm/src/primitives.rs +++ b/vm/src/primitives.rs @@ -80,7 +80,7 @@ fn array_append<'vm>( } }; RuntimeResult::Return( - Getable::from_value(lhs.vm(), Variants(&Value::Array(value))).expect("Array"), + Getable::from_value(lhs.vm(), Variants::new(&Value::Array(value))).expect("Array"), ) } @@ -126,7 +126,7 @@ fn string_append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult { } }; RuntimeResult::Return( - Getable::from_value(vm, Variants(&Value::String(value))).expect("Array"), + Getable::from_value(vm, Variants::new(&Value::String(value))).expect("Array"), ) } diff --git a/vm/src/stack.rs b/vm/src/stack.rs index cd4094e2dc..7402c0900e 100644 --- a/vm/src/stack.rs +++ b/vm/src/stack.rs @@ -213,10 +213,12 @@ impl<'a: 'b, 'b> StackFrame<'b> { } pub fn get_variants(&self, index: VmIndex) -> Option { - if index < self.len() { - Some(Variants(&self[index])) - } else { - None + unsafe { + if index < self.len() { + Some(Variants::new(&self[index])) + } else { + None + } } } diff --git a/vm/src/thread.rs b/vm/src/thread.rs index 7a4215ebd4..53bad969ed 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -461,9 +461,11 @@ impl Thread { // Finally check that type of the returned value is correct let expected = T::make_type(self); if check_signature(&*env, &expected, &actual) { - T::from_value(self, Variants(&value)).ok_or_else(|| { - Error::UndefinedBinding(name.into()) - }) + unsafe { + T::from_value(self, Variants::new(&value)).ok_or_else(|| { + Error::UndefinedBinding(name.into()) + }) + } } else { Err(Error::WrongType(expected, actual.into_owned())) } @@ -1834,19 +1836,18 @@ where F: FnOnce(T, T) -> Value, T: Getable<'b> + fmt::Debug, { - let r = stack.pop(); - let l = stack.pop(); - match ( - T::from_value(vm, Variants(&l)), - T::from_value(vm, Variants(&r)), - ) { - (Some(l), Some(r)) => { - let result = f(l, r); - // pushing numbers should never return an error so unwrap - stack.stack.push(result); + let (l, r) = { + let r = stack.get_variants(stack.len() - 1).unwrap(); + let l = stack.get_variants(stack.len() - 2).unwrap(); + match (T::from_value(vm, l), T::from_value(vm, r)) { + (Some(l), Some(r)) => (l, r), + _ => panic!("{:?} `op` {:?}", l, r), } - _ => panic!("{:?} `op` {:?}", l, r), - } + }; + let result = f(l, r); + stack.pop(); + stack.pop(); + stack.stack.push(result); } fn debug_instruction( diff --git a/vm/src/value.rs b/vm/src/value.rs index 389875286d..d082e8a8ba 100644 --- a/vm/src/value.rs +++ b/vm/src/value.rs @@ -16,7 +16,7 @@ use compiler::DebugInfo; use gc::{Gc, GcPtr, Generation, Traverseable, DataDef, Move, WriteOnly}; use array::Array; use thread::{Thread, Status}; -use {Error, Result}; +use {Error, Result, Variants}; use self::Value::{Int, Float, String, Function, PartialApplication, Closure}; @@ -802,6 +802,28 @@ impl<'a> Iterator for Iter<'a> { } } +pub struct VariantIter<'a> { + array: &'a ValueArray, + index: usize, +} +impl<'a> Iterator for VariantIter<'a> { + type Item = Variants<'a>; + fn next(&mut self) -> Option { + if self.index < self.array.len() { + let value = self.array.get(self.index); + self.index += 1; + Some(unsafe { Variants::with_root(value, self.array) }) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let i = self.array.len() - self.index; + (i, Some(i)) + } +} + impl Traverseable for ValueArray { fn traverse(&self, gc: &mut Gc) { on_array!(*self, |array: &Array<_>| array.traverse(gc)) @@ -839,6 +861,13 @@ impl ValueArray { } } + pub fn variant_iter(&self) -> VariantIter { + VariantIter { + array: self, + index: 0, + } + } + pub fn size_of(repr: Repr, len: usize) -> usize { ::std::mem::size_of::() + repr.size_of() * len } From 6b98e29c76abe30d2c9117de0475eed6d6a4dfe7 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 11 Jun 2017 12:46:46 +0200 Subject: [PATCH 07/18] feat(vm): Add deserialization from gluon values --- vm/Cargo.toml | 2 +- vm/src/api/de.rs | 661 +++++++++++++++++++++++++++++++++++++++++++ vm/src/api/mod.rs | 45 +-- vm/src/api/serde.rs | 4 +- vm/src/lib.rs | 19 +- vm/src/primitives.rs | 22 +- vm/src/value.rs | 1 + 7 files changed, 719 insertions(+), 35 deletions(-) create mode 100644 vm/src/api/de.rs diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 9b0f9c82f0..b88227ffa4 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -41,4 +41,4 @@ regex = "0.2.0" serde_derive = "1.0.0" [features] -test = ["env_logger", "lalrpop", "gluon_parser"] +test = ["env_logger", "lalrpop", "gluon_parser", "serde"] diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs new file mode 100644 index 0000000000..af451fabf6 --- /dev/null +++ b/vm/src/api/de.rs @@ -0,0 +1,661 @@ +use std::fmt; + +use base::resolve; +use base::types::{ArcType, BuiltinType, Type, TypeEnv}; +use base::symbol::Symbol; + +use {Error as VmError, Result, Variants}; +use api::{Getable, ValueRef}; +use thread::Thread; + +use serde::de::{self, DeserializeSeed, Visitor, SeqAccess, MapAccess, EnumAccess, VariantAccess, + IntoDeserializer, Error}; + +impl de::Error for VmError { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + VmError::Message(format!("{}", msg)) + } +} + +#[derive(Clone)] +struct State<'de> { + thread: &'de Thread, + env: &'de TypeEnv, +} +pub struct Deserializer<'de, 't> { + state: State<'de>, + input: Variants<'de>, + typ: &'t ArcType, +} + +impl<'de, 't> Deserializer<'de, 't> { + pub 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, + } + } + + pub 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) + } + + pub 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); + match T::from_value(self.state.thread, self.input) { + Some(c) => { + if expected(&typ) { + visit(c) + } else { + Err(Error::custom("Cant deserialize type")) + } + } + _ => Err(Error::custom("Cant deserialize type")), + } + } +} + + +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(_) => { + if let Type::Record(_) = **resolve::remove_aliases_cow(self.state.env, self.typ) { + self.deserialize_enum("", &[], visitor) + } else { + self.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::Tag(_) => self.deserialize_enum("", &[], visitor), + ValueRef::Userdata(_) | + ValueRef::Internal => Err(Self::Error::custom("Cant deserialize type")), + } + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match (self.input.as_ref(), &**self.typ) { + (ValueRef::Tag(t), &Type::Alias(ref alias)) if alias.name.as_ref() == "Bool" => { + visitor.visit_bool(t != 0) + } + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + // 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Byte(b) => visitor.visit_u8(b), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Float(f) => visitor.visit_f64(f), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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 => Err(Self::Error::custom("Cant deserialize type")), + } + } + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + 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 = match **resolve::remove_aliases_cow(self.state.env, self.typ) { + Type::App(ref func, ref args) if args.len() == 1 => { + match **func { + Type::Alias(ref alias) if alias.name.declared_name() == "Option" => { + args[0].clone() + } + _ => return Err(Self::Error::custom("Expected `Option` type")), + } + } + _ => return Err(Self::Error::custom("Expected `Option` type")), + }; + match self.input.as_ref() { + ValueRef::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_variants(0).unwrap(), + }) + } + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + // In Serde, unit means an anonymous value containing no data. + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.input.as_ref() { + ValueRef::Tag(0) => visitor.visit_unit(), + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + // Unit struct means a named value containing no data. + fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_unit(visitor) + } + + // As is done here, serializers are encouraged to treat newtype structs as + // insignificant wrappers around the data they contain. That means not + // parsing anything other than the contained value. + 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(), + &args[0], + values.iter(), + )) + } + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + // Tuple structs look just like sequences in JSON. + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + // Much like `deserialize_seq` but calls the visitors `visit_map` method + // with a `MapAccess` implementation, rather than the visitor's `visit_seq` + // method with a `SeqAccess` implementation. + 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 = (0..data.len()) + .map(|i| data.get_variants(i).unwrap()) + .zip(typ.row_iter()) + .map(|(value, field)| (value, &field.name, &field.typ)); + visitor.visit_map(MapDeserializer::new(self.state.clone(), iter)) + } + _ => Err(Self::Error::custom("Cant deserialize type")), + } + } + + // Structs look just like maps in JSON. + // + // Notice the `fields` parameter - a "struct" in the Serde data model means + // that the `Deserialize` implementation is required to know what the fields + // are before even looking at the input data. Any key-value pairing in which + // the fields cannot be known ahead of time is probably a map. + 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)) + } + + // An identifier in Serde is the type that identifies a field of a struct or + // the variant of an enum. In JSON, struct fields and enum variants are + // represented as strings. In other formats they may be represented as + // numeric indices. + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + // Like `deserialize_any` but indicates to the `Deserializer` that it makes + // no difference which `Visitor` method is called because the data is + // ignored. + // + // Some deserializers are able to implement this more efficiently than + // `deserialize_any`, for example by rapidly skipping over matched + // delimiters without paying close attention to the data in between. + // + // Some formats are not able to implement this at all. Formats that can + // implement `deserialize_any` and `deserialize_ignored_any` are known as + // self-describing. + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_any(visitor) + } +} + +struct SeqDeserializer<'de, 't, I> { + state: State<'de>, + typ: &'t ArcType, + iter: I, +} + +impl<'de, 't, I> SeqDeserializer<'de, 't, I> { + fn new(state: State<'de>, typ: &'t ArcType, iter: I) -> Self { + SeqDeserializer { + state: state, + typ: typ, + iter: iter, + } + } +} + +// `SeqAccess` is provided to the `Visitor` to give it the ability to iterate +// through elements of the sequence. +impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I> +where + I: Iterator>, +{ + type Error = VmError; + + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: DeserializeSeed<'de>, + { + match self.iter.next() { + Some(value) => { + seed.deserialize(&mut Deserializer { + state: self.state.clone(), + input: value, + typ: self.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, + } + } +} + +// `MapAccess` is provided to the `Visitor` to give it the ability to iterate +// through entries of the map. +impl<'de, 'a, 't, I> MapAccess<'de> for MapDeserializer<'de, 't, I> +where + I: Iterator< + Item = ( + Variants<'de>, + &'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("Cant deserialize type")), + } + } +} + +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()), + ValueRef::Tag(tag) => visitor.visit_u32(tag), + _ => Err(Self::Error::custom("Cant 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(), + ValueRef::Tag(tag) => tag, + _ => return Err(Self::Error::custom("Cant 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("Cant deserialize tag") + })?; + visitor.visit_str(variant.name.as_ref()) + } + _ => return Err(Self::Error::custom("Cant 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 + } +} + +// `EnumAccess` is provided to the `Visitor` to give it the ability to determine +// which variant of the enum is supposed to be deserialized. +// +// Note that all enum deserialization methods in Serde refer exclusively to the +// "externally tagged" enum representation. +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)) + } +} + +// `VariantAccess` is provided to the `Visitor` to give it the ability to see +// the content of the single variant that it decided to deserialize. +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>, + { + 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_map(self.de, visitor) + } +} diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index 942b540b8d..c42fc0d7b4 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -1,5 +1,5 @@ //! The marshalling api -use {Variants, Error, Result}; +use {Variants, Error, Result, forget_lifetime}; use gc::{DataDef, Gc, Traverseable, Move}; use base::symbol::Symbol; use stack::StackFrame; @@ -26,6 +26,8 @@ pub use value::Userdata; pub mod mac; #[cfg(feature = "serde")] pub mod serde; +#[cfg(feature = "serde")] +pub mod de; macro_rules! count { () => { 0 }; @@ -71,18 +73,22 @@ impl<'a> PartialEq for ValueRef<'a> { impl<'a> ValueRef<'a> { pub fn new(value: &'a Value) -> ValueRef<'a> { - match *value { + unsafe { ValueRef::rooted_new(*value) } + } + + pub unsafe fn rooted_new(value: Value) -> ValueRef<'a> { + match value { Value::Byte(i) => ValueRef::Byte(i), Value::Int(i) => ValueRef::Int(i), Value::Float(f) => ValueRef::Float(f), - Value::String(ref s) => ValueRef::String(s), - Value::Data(ref data) => ValueRef::Data(Data(data)), - Value::Userdata(ref data) => ValueRef::Userdata(&***data), + Value::String(s) => ValueRef::String(forget_lifetime(&*s)), + Value::Data(data) => ValueRef::Data(Data(forget_lifetime(&*data))), + Value::Array(array) => ValueRef::Array(ArrayRef(forget_lifetime(&*array))), + Value::Userdata(data) => ValueRef::Userdata(forget_lifetime(&**data)), Value::Tag(tag) => ValueRef::Tag(tag), Value::Thread(_) | Value::Function(_) | Value::Closure(_) | - Value::Array(_) | // FIXME Expose arrays safely Value::PartialApplication(_) => ValueRef::Internal, } } @@ -350,10 +356,6 @@ where } } -unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { - ::std::mem::transmute(x) -} - impl<'vm> Getable<'vm> for &'vm str { fn from_value(_vm: &'vm Thread, value: Variants) -> Option { unsafe { @@ -1043,7 +1045,14 @@ impl<'vm> ArrayRef<'vm> { } } - pub fn iter(&self) -> ::value::VariantIter { + pub fn as_slice(&self) -> Option<&[T]> + where + T: ArrayRepr + Copy, + { + self.0.as_slice() + } + + pub fn iter(&self) -> ::value::VariantIter<'vm> { self.0.variant_iter() } } @@ -1757,13 +1766,15 @@ impl<'vm, T, $($args,)* R> Function R> } }) } - + fn return_value(vm: &Thread, value: Value) -> Result { - R::from_value(vm, Variants::new(&value)) - .ok_or_else(|| { - error!("Wrong type {:?}", value); - Error::Message("Wrong type".to_string()) - }) + unsafe { + R::from_value(vm, Variants::new(&value)) + .ok_or_else(|| { + error!("Wrong type {:?}", value); + Error::Message("Wrong type".to_string()) + }) + } } } diff --git a/vm/src/api/serde.rs b/vm/src/api/serde.rs index f431b135e4..721e77a348 100644 --- a/vm/src/api/serde.rs +++ b/vm/src/api/serde.rs @@ -1,4 +1,3 @@ -extern crate serde; use std::fmt; use std::ops::{Deref, DerefMut}; @@ -8,7 +7,7 @@ use api::Pushable; use thread::{Context, Thread, ThreadInternal}; use types::{VmTag, VmIndex}; use value::{Def, Value}; -use self::serde::ser::{self, Serialize}; +use serde::ser::{self, Serialize}; pub fn to_value(thread: &Thread, value: &T) -> Result where @@ -425,6 +424,7 @@ impl<'a, 'vm> ser::SerializeStructVariant for RecordSerializer<'a, 'vm> { } } + #[cfg(test)] mod tests { use super::*; diff --git a/vm/src/lib.rs b/vm/src/lib.rs index fb40a69ce2..8b78ae9ef1 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -18,6 +18,9 @@ extern crate pretty; #[macro_use] extern crate futures; +#[cfg(feature = "serde")] +#[macro_use] +extern crate serde; #[cfg(all(feature = "serde", test))] #[macro_use] extern crate serde_derive; @@ -51,17 +54,23 @@ mod source_map; mod value; mod vm; +use std::marker::PhantomData; + use api::ValueRef; use value::Value; use types::VmIndex; use base::types::ArcType; use base::symbol::Symbol; -#[derive(Debug)] -pub struct Variants<'a>(&'a Value); +unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { + ::std::mem::transmute(x) +} + +#[derive(Copy, Clone, Debug)] +pub struct Variants<'a>(Value, PhantomData<&'a Value>); impl<'a> Variants<'a> { - /// Creates a new `Variants` value which assumes that `value` is alive for the lifetime of the + /// Creates a new `Variants` value which assumes that `value` is rooted for the lifetime of the /// value pub unsafe fn new(value: &Value) -> Variants { Variants::with_root(*value, value) @@ -73,8 +82,8 @@ impl<'a> Variants<'a> { /// Returns an instance of `ValueRef` which allows users to safely retrieve the interals of a /// value - pub fn as_ref(&self) -> ValueRef { - ValueRef::new(&self.0) + pub fn as_ref(&self) -> ValueRef<'a> { + unsafe { ValueRef::rooted_new(self.0) } } } diff --git a/vm/src/primitives.rs b/vm/src/primitives.rs index daba936e28..3ad888f86a 100644 --- a/vm/src/primitives.rs +++ b/vm/src/primitives.rs @@ -79,9 +79,11 @@ fn array_append<'vm>( Err(err) => return RuntimeResult::Panic(err), } }; - RuntimeResult::Return( - Getable::from_value(lhs.vm(), Variants::new(&Value::Array(value))).expect("Array"), - ) + unsafe { + RuntimeResult::Return( + Getable::from_value(lhs.vm(), Variants::new(&Value::Array(value))).expect("Array"), + ) + } } fn string_append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult { @@ -117,17 +119,17 @@ fn string_append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult { let vm = lhs.vm; let lhs = lhs.value; - let value = unsafe { + unsafe { let mut context = vm.context(); let result = context.alloc(StrAppend { lhs: lhs, rhs: rhs }); - match result { + let value = match result { Ok(x) => GcStr::from_utf8_unchecked(x), Err(err) => return RuntimeResult::Panic(err), - } - }; - RuntimeResult::Return( - Getable::from_value(vm, Variants::new(&Value::String(value))).expect("Array"), - ) + }; + RuntimeResult::Return( + Getable::from_value(vm, Variants::new(&Value::String(value))).expect("Array"), + ) + } } fn string_slice(s: &str, start: usize, end: usize) -> RuntimeResult<&str, String> { diff --git a/vm/src/value.rs b/vm/src/value.rs index d082e8a8ba..ef4fc5e934 100644 --- a/vm/src/value.rs +++ b/vm/src/value.rs @@ -806,6 +806,7 @@ pub struct VariantIter<'a> { array: &'a ValueArray, index: usize, } + impl<'a> Iterator for VariantIter<'a> { type Item = Variants<'a>; fn next(&mut self) -> Option { From c0851e77f5326ec6bc422b71df64abf609afc83f Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 12 Jun 2017 22:48:59 +0200 Subject: [PATCH 08/18] refactor(vm): Rename the serializer module --- vm/src/api/mod.rs | 2 +- vm/src/api/{serde.rs => ser.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename vm/src/api/{serde.rs => ser.rs} (100%) diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index c42fc0d7b4..8898d2a076 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -25,7 +25,7 @@ pub use value::Userdata; #[macro_use] pub mod mac; #[cfg(feature = "serde")] -pub mod serde; +pub mod ser; #[cfg(feature = "serde")] pub mod de; diff --git a/vm/src/api/serde.rs b/vm/src/api/ser.rs similarity index 100% rename from vm/src/api/serde.rs rename to vm/src/api/ser.rs From 0757f58f78876879a3e17cebd71b73cf2ff5c878 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Tue, 13 Jun 2017 22:35:09 +0200 Subject: [PATCH 09/18] test(vm): Add simple deserialization tests --- Cargo.toml | 5 ++-- base/src/resolve.rs | 28 ++++++++++++++++++-- check/src/lib.rs | 7 ++++- tests/de.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++ vm/src/api/de.rs | 55 +++++++++++++++++++++++++++++++------- vm/src/api/ser.rs | 28 ++++++++++---------- 6 files changed, 158 insertions(+), 29 deletions(-) create mode 100644 tests/de.rs diff --git a/Cargo.toml b/Cargo.toml index 2782508e07..1f422d7c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,13 +46,14 @@ skeptic = { version = "0.6", optional = true } collect-mac = "0.1.0" env_logger = "0.3.4" +serde = "1.0.0" +serde_derive = "1.0.0" + hyper = "0.11.0" curl = "0.4.1" [features] default = ["regex"] -serde = ["gluon_vm/serde"] - test = ["gluon_vm/test", "gluon_check/test", "gluon_parser/test"] nightly = ["compiletest_rs"] diff --git a/base/src/resolve.rs b/base/src/resolve.rs index bd37bbd705..4a5c99bbba 100644 --- a/base/src/resolve.rs +++ b/base/src/resolve.rs @@ -29,9 +29,34 @@ pub fn remove_aliases_cow<'t>(env: &TypeEnv, typ: &'t ArcType) -> Cow<'t, ArcTyp } } +pub fn canonical_alias<'t, F>(env: &TypeEnv, typ: &'t ArcType, canonical: F) -> Cow<'t, ArcType> +where + F: Fn(&AliasData) -> bool, +{ + match peek_alias(env, typ) { + Ok(Some(alias)) if canonical(alias) => { + type_of_alias(env, alias, typ.unapplied_args()) + .map(|typ| { + Cow::Owned(canonical_alias(env, &typ, canonical).into_owned()) + }) + .unwrap_or(Cow::Borrowed(typ)) + } + _ => Cow::Borrowed(typ), + } +} + /// Expand `typ` if it is an alias that can be expanded and return the expanded type. /// Returns `None` if the type is not an alias or the alias could not be expanded. pub fn remove_alias(env: &TypeEnv, typ: &ArcType) -> Result, Error> { + Ok(peek_alias(env, typ)?.and_then(|alias| { + type_of_alias(env, alias, typ.unapplied_args()) + })) +} + +pub fn peek_alias<'t>( + env: &'t TypeEnv, + typ: &'t ArcType, +) -> Result>, Error> { let maybe_alias = match **typ { Type::Alias(ref alias) if alias.args.is_empty() => Some(alias), Type::App(ref alias, ref args) => { @@ -53,8 +78,7 @@ pub fn remove_alias(env: &TypeEnv, typ: &ArcType) -> Result, Err })? } }; - - Ok(type_of_alias(env, alias, typ.unapplied_args())) + Ok(Some(alias)) } None => Ok(None), } diff --git a/check/src/lib.rs b/check/src/lib.rs index f4a8beca12..063160b7f2 100644 --- a/check/src/lib.rs +++ b/check/src/lib.rs @@ -36,7 +36,12 @@ pub fn check_signature(env: &TypeEnv, signature: &ArcType, actual: &ArcType) -> let subs = Substitution::new(Kind::typ()); let state = unify_type::State::new(env, &subs); let actual = unify_type::instantiate_generic_variables(&mut FnvMap::default(), &subs, actual); - unify_type::merge_signature(&subs, &mut ScopedMap::new(), 0, state, signature, &actual).is_ok() + let result = + unify_type::merge_signature(&subs, &mut ScopedMap::new(), 0, state, signature, &actual); + if let Err(ref err) = result { + debug!("Check signature error: {}", err); + } + result.is_ok() } #[cfg(test)] diff --git a/tests/de.rs b/tests/de.rs new file mode 100644 index 0000000000..fa2d1d2460 --- /dev/null +++ b/tests/de.rs @@ -0,0 +1,64 @@ +extern crate env_logger; +#[macro_use] +extern crate serde_derive; + +extern crate gluon; +#[macro_use] +extern crate gluon_vm; + +use gluon::base::types::ArcType; +use gluon::vm::api::VmType; +use gluon::vm::api::de::De; +use gluon::vm::thread::Thread; +use gluon::{Compiler, new_vm}; + +#[test] +fn bool() { + let _ = env_logger::init(); + + let thread = new_vm(); + let (De(b), _) = Compiler::new() + .run_expr::>(&thread, "test", "True") + .unwrap_or_else(|err| panic!("{}", err)); + assert_eq!(b, true); +} + +#[derive(Debug, PartialEq, Deserialize)] +struct Record { + test: i32, + string: String, +} + +impl VmType for Record { + type Type = Self; + + fn make_type(thread: &Thread) -> ArcType { + fn type_of(thread: &Thread, _: T) -> ArcType { + T::make_type(thread) + } + type_of( + thread, + record!{ + test => 0, + string => "" + }, + ) + } +} + +#[test] +fn record() { + let _ = env_logger::init(); + + let thread = new_vm(); + let (De(record), _) = Compiler::new() + .run_expr::>(&thread, "test", r#" { test = 1, string = "test" } "#) + .unwrap_or_else(|err| panic!("{}", err)); + assert_eq!( + record, + Record { + test: 1, + string: "test".to_string(), + } + ); +} diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index af451fabf6..0e2aa3f1ef 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -5,11 +5,11 @@ use base::types::{ArcType, BuiltinType, Type, TypeEnv}; use base::symbol::Symbol; use {Error as VmError, Result, Variants}; -use api::{Getable, ValueRef}; -use thread::Thread; +use api::{Getable, ValueRef, VmType}; +use thread::{Thread, ThreadInternal}; -use serde::de::{self, DeserializeSeed, Visitor, SeqAccess, MapAccess, EnumAccess, VariantAccess, - IntoDeserializer, Error}; +use serde::de::{self, DeserializeSeed, DeserializeOwned, Visitor, SeqAccess, MapAccess, + EnumAccess, VariantAccess, IntoDeserializer, Error}; impl de::Error for VmError { fn custom(msg: T) -> Self @@ -20,6 +20,40 @@ impl de::Error for VmError { } } +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) -> Option { + let env = thread.global_env().get_env(); + let typ = T::make_type(thread); + let mut deserializer = Deserializer { + state: State { + thread: thread, + env: &*env, + }, + input: value, + typ: &typ, + }; + T::deserialize(&mut deserializer).map(De).ok() + } +} + + #[derive(Clone)] struct State<'de> { thread: &'de Thread, @@ -112,10 +146,8 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { where V: Visitor<'de>, { - match (self.input.as_ref(), &**self.typ) { - (ValueRef::Tag(t), &Type::Alias(ref alias)) if alias.name.as_ref() == "Bool" => { - visitor.visit_bool(t != 0) - } + match self.input.as_ref() { + ValueRef::Tag(t) => visitor.visit_bool(t != 0), _ => Err(Self::Error::custom("Cant deserialize type")), } } @@ -280,10 +312,13 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { where V: Visitor<'de>, { - let typ = match **resolve::remove_aliases_cow(self.state.env, self.typ) { + let typ = resolve::canonical_alias(self.state.env, self.typ, |alias| { + alias.name.declared_name() == "std.types.Option" + }); + let typ = match **typ { Type::App(ref func, ref args) if args.len() == 1 => { match **func { - Type::Alias(ref alias) if alias.name.declared_name() == "Option" => { + Type::Alias(ref alias) if alias.name.declared_name() == "std.types.Option" => { args[0].clone() } _ => return Err(Self::Error::custom("Expected `Option` type")), diff --git a/vm/src/api/ser.rs b/vm/src/api/ser.rs index 721e77a348..61cb75d4bf 100644 --- a/vm/src/api/ser.rs +++ b/vm/src/api/ser.rs @@ -428,15 +428,15 @@ impl<'a, 'vm> ser::SerializeStructVariant for RecordSerializer<'a, 'vm> { #[cfg(test)] mod tests { use super::*; - use thread::{RootedThread, ThreadInternal}; + use thread::{RootedThread, RootedValue, ThreadInternal}; - fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> Value + fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> RootedValue<&'vm Thread> where T: Pushable<'vm>, { let mut context = thread.context(); value.push(thread, &mut context).unwrap(); - context.stack.pop() + thread.root_value(context.stack.pop()) } #[test] @@ -454,13 +454,13 @@ mod tests { #[test] fn record() { let thread = RootedThread::new(); - let actual = to_value( + let actual = make_value( &thread, - &Record { + Ser(Record { test: 123, string: "abc".to_string(), - }, - ).unwrap(); + }), + ); assert_eq!( actual, make_value( @@ -483,26 +483,26 @@ mod tests { fn enum_() { let thread = RootedThread::new(); - let actual = to_value(&thread, &Enum::A("abc".to_string())).unwrap(); + let actual = make_value(&thread, Ser(Enum::A("abc".to_string()))); let s = make_value(&thread, "abc"); assert_eq!( actual, - Value::Data( + thread.root_value(Value::Data( thread .context() .gc .alloc(Def { tag: 0, - elems: &[s], + elems: &[*s], }) .unwrap(), - ) + )) ); - let actual = to_value(&thread, &Enum::B(1232)).unwrap(); + let actual = make_value(&thread, Ser(Enum::B(1232))); assert_eq!( actual, - Value::Data( + thread.root_value(Value::Data( thread .context() .gc @@ -511,7 +511,7 @@ mod tests { elems: &[Value::Int(1232)], }) .unwrap(), - ) + )) ); } } From 5566b5183e137cde97b4122f707dc049491ea40d Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Tue, 13 Jun 2017 23:38:59 +0200 Subject: [PATCH 10/18] fix(vm): Fix the debug printing of GcStr and ValueArray --- vm/src/value.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/vm/src/value.rs b/vm/src/value.rs index ef4fc5e934..985addc447 100644 --- a/vm/src/value.rs +++ b/vm/src/value.rs @@ -175,12 +175,19 @@ mod gc_str { use super::ValueArray; use gc::{Gc, GcPtr, Generation, Traverseable}; + use std::fmt; use std::str; use std::ops::Deref; - #[derive(Copy, Clone, Debug, PartialEq)] + #[derive(Copy, Clone, PartialEq)] pub struct GcStr(GcPtr); + impl fmt::Debug for GcStr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("GcStr").field(&&**self).finish() + } + } + impl Eq for GcStr {} impl GcStr { @@ -768,12 +775,20 @@ macro_rules! on_array { } -#[derive(Debug)] pub struct ValueArray { repr: Repr, array: Array<()>, } +impl fmt::Debug for ValueArray { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("ValueArray") + .field("repr", &self.repr) + .field("array", on_array!(self, |x| x as &fmt::Debug)) + .finish() + } +} + impl PartialEq for ValueArray { fn eq(&self, other: &ValueArray) -> bool { self.repr == other.repr && self.iter().zip(other.iter()).all(|(l, r)| l == r) From cdf41bae4658e975de76937fd9591cf0d46e7fbb Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Jun 2017 00:02:31 +0200 Subject: [PATCH 11/18] Allow records to be deserialized into even if the gluon record has additional fields --- base/src/resolve.rs | 2 +- base/src/types.rs | 9 +++++++ tests/de.rs | 59 ++++++++++++++++++++++++++++++++++++--------- vm/src/api/de.rs | 52 +++++++++++---------------------------- vm/src/api/mod.rs | 9 +++++++ vm/src/field_map.rs | 1 + vm/src/thread.rs | 15 +++++------- 7 files changed, 87 insertions(+), 60 deletions(-) diff --git a/base/src/resolve.rs b/base/src/resolve.rs index 4a5c99bbba..61fd248eb2 100644 --- a/base/src/resolve.rs +++ b/base/src/resolve.rs @@ -34,7 +34,7 @@ where F: Fn(&AliasData) -> bool, { match peek_alias(env, typ) { - Ok(Some(alias)) if canonical(alias) => { + Ok(Some(alias)) if !canonical(alias) => { type_of_alias(env, alias, typ.unapplied_args()) .map(|typ| { Cow::Owned(canonical_alias(env, &typ, canonical).into_owned()) diff --git a/base/src/types.rs b/base/src/types.rs index 9d9eb82cb1..60614114bd 100644 --- a/base/src/types.rs +++ b/base/src/types.rs @@ -630,6 +630,15 @@ where _ => None, } } + + pub fn is_non_polymorphic_record(&self) -> bool { + match *self { + Type::Record(ref row) | + Type::ExtendRow { rest: ref row, .. } => row.is_non_polymorphic_record(), + Type::EmptyRow => true, + _ => false, + } + } } impl Type diff --git a/tests/de.rs b/tests/de.rs index fa2d1d2460..2df52babc8 100644 --- a/tests/de.rs +++ b/tests/de.rs @@ -3,10 +3,9 @@ extern crate env_logger; extern crate serde_derive; extern crate gluon; -#[macro_use] -extern crate gluon_vm; -use gluon::base::types::ArcType; +use gluon::base::types::{ArcType, Field, Type}; +use gluon::base::symbol::Symbol; use gluon::vm::api::VmType; use gluon::vm::api::de::De; use gluon::vm::thread::Thread; @@ -33,15 +32,19 @@ impl VmType for Record { type Type = Self; fn make_type(thread: &Thread) -> ArcType { - fn type_of(thread: &Thread, _: T) -> ArcType { - T::make_type(thread) - } - type_of( - thread, - record!{ - test => 0, - string => "" - }, + Type::poly_record( + vec![], + vec![ + Field { + name: Symbol::from("test"), + typ: i32::make_type(thread), + }, + Field { + name: Symbol::from("string"), + typ: str::make_type(thread), + }, + ], + Type::hole(), ) } } @@ -62,3 +65,35 @@ fn record() { } ); } + +#[test] +fn option() { + let _ = env_logger::init(); + + let thread = new_vm(); + let (De(opt), _) = Compiler::new() + .run_expr::>>(&thread, "test", r#" Some 1.0 "#) + .unwrap_or_else(|err| panic!("{}", err)); + assert_eq!(opt, Some(1.0)); +} + +#[test] +fn partial_record() { + let _ = env_logger::init(); + + let thread = new_vm(); + let (De(record), _) = Compiler::new() + .run_expr::>( + &thread, + "test", + r#" { test = 1, extra = 1.0, string = "test", } "#, + ) + .unwrap_or_else(|err| panic!("{}", err)); + assert_eq!( + record, + Record { + test: 1, + string: "test".to_string(), + } + ); +} diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index 0e2aa3f1ef..706616c1d4 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -49,7 +49,13 @@ where input: value, typ: &typ, }; - T::deserialize(&mut deserializer).map(De).ok() + T::deserialize(&mut deserializer) + .map(De) + .map_err(|err| { + debug!("{}", err); + err + }) + .ok() } } @@ -339,7 +345,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { } } - // In Serde, unit means an anonymous value containing no data. fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, @@ -350,7 +355,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { } } - // Unit struct means a named value containing no data. fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, @@ -358,9 +362,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { self.deserialize_unit(visitor) } - // As is done here, serializers are encouraged to treat newtype structs as - // insignificant wrappers around the data they contain. That means not - // parsing anything other than the contained value. fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, @@ -392,7 +393,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { self.deserialize_seq(visitor) } - // Tuple structs look just like sequences in JSON. fn deserialize_tuple_struct( self, _name: &'static str, @@ -405,9 +405,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { self.deserialize_seq(visitor) } - // Much like `deserialize_seq` but calls the visitors `visit_map` method - // with a `MapAccess` implementation, rather than the visitor's `visit_seq` - // method with a `SeqAccess` implementation. fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, @@ -415,22 +412,18 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { 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 = (0..data.len()) - .map(|i| data.get_variants(i).unwrap()) - .zip(typ.row_iter()) - .map(|(value, field)| (value, &field.name, &field.typ)); + 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)) } - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom( + format!("Cant deserialize type {}", typ), + )), } } - // Structs look just like maps in JSON. - // - // Notice the `fields` parameter - a "struct" in the Serde data model means - // that the `Deserialize` implementation is required to know what the fields - // are before even looking at the input data. Any key-value pairing in which - // the fields cannot be known ahead of time is probably a map. fn deserialize_struct( self, _name: &'static str, @@ -455,10 +448,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { visitor.visit_enum(Enum::new(self)) } - // An identifier in Serde is the type that identifies a field of a struct or - // the variant of an enum. In JSON, struct fields and enum variants are - // represented as strings. In other formats they may be represented as - // numeric indices. fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, @@ -466,17 +455,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { self.deserialize_str(visitor) } - // Like `deserialize_any` but indicates to the `Deserializer` that it makes - // no difference which `Visitor` method is called because the data is - // ignored. - // - // Some deserializers are able to implement this more efficiently than - // `deserialize_any`, for example by rapidly skipping over matched - // delimiters without paying close attention to the data in between. - // - // Some formats are not able to implement this at all. Formats that can - // implement `deserialize_any` and `deserialize_ignored_any` are known as - // self-describing. fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -501,8 +479,6 @@ impl<'de, 't, I> SeqDeserializer<'de, 't, I> { } } -// `SeqAccess` is provided to the `Visitor` to give it the ability to iterate -// through elements of the sequence. impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I> where I: Iterator>, diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index 8898d2a076..dc0b0b6b31 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -113,6 +113,15 @@ impl<'a> Data<'a> { pub fn get_variants(&self, index: usize) -> Option> { unsafe { self.0.fields.get(index).map(|v| Variants::new(v)) } } + + // Retrieves the field `name` from this record + pub fn lookup_field(&self, thread: &Thread, name: &str) -> Option> { + unsafe { + thread.lookup_field(self.0, name).ok().map(|v| { + Variants::with_root(v, self.0) + }) + } + } } /// Marker type representing a hole diff --git a/vm/src/field_map.rs b/vm/src/field_map.rs index da7a2b49b8..c4a63c9d36 100644 --- a/vm/src/field_map.rs +++ b/vm/src/field_map.rs @@ -3,6 +3,7 @@ use base::fnv::FnvMap; use interner::InternedStr; use types::{VmTag, VmIndex}; +#[derive(Debug)] pub struct FieldMap { /// Maps fields into a tag tags: FnvMap, VmTag>, diff --git a/vm/src/thread.rs b/vm/src/thread.rs index 53bad969ed..a6daba7bf4 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -30,8 +30,8 @@ use source_map::LocalIter; use stack::{Frame, Stack, StackFrame, State}; use types::*; use vm::{GlobalVmState, VmEnv}; -use value::{Value, ClosureData, ClosureInitDef, ClosureDataDef, Def, ExternFunction, GcStr, - BytecodeFunction, Callable, PartialApplicationDataDef, Userdata}; +use value::{Value, ClosureData, ClosureInitDef, ClosureDataDef, DataStruct, Def, ExternFunction, + GcStr, BytecodeFunction, Callable, PartialApplicationDataDef, Userdata}; use value::Value::{Int, Float, String, Data, Function, PartialApplication, Closure}; @@ -643,7 +643,7 @@ where fn can_share_values_with(&self, gc: &mut Gc, other: &Thread) -> bool; - fn lookup_field(&self, value: Value, field: &str) -> Result; + fn lookup_field(&self, data: &DataStruct, field: &str) -> Result; } impl ThreadInternal for Thread { @@ -823,16 +823,13 @@ impl ThreadInternal for Thread { false } - fn lookup_field(&self, value: Value, field: &str) -> Result { + fn lookup_field(&self, data: &DataStruct, field: &str) -> Result { let context = self.context.lock().unwrap(); let str_index = self.global_env().intern(field)?; context .record_map - .get_offset(0, str_index) - .and_then(|index| match value { - Data(data) => data.fields.get(index as usize).cloned(), - _ => None, - }) + .get_offset(data.tag, str_index) + .and_then(|index| data.fields.get(index as usize).cloned()) .ok_or_else(|| { Error::Message(format!( "Internal error: Undefined record field {}", From ed7330d1b318a406ade8993d526464d8e94c99e0 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Jun 2017 20:43:49 +0200 Subject: [PATCH 12/18] feat(vm): Merge the Tag and Data variants of ValueRef `ValueRef` shouldn't expose implementation details such as that --- examples/http.rs | 4 ++-- vm/src/api/de.rs | 9 +++------ vm/src/api/mod.rs | 50 +++++++++++++++++++++++++++++++---------------- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/examples/http.rs b/examples/http.rs index 8809d4a32d..8fca592562 100644 --- a/examples/http.rs +++ b/examples/http.rs @@ -113,8 +113,8 @@ impl<'vm> Getable<'vm> for Wrap { fn from_value(_: &'vm Thread, value: Variants) -> Option { use hyper::StatusCode::*; match value.as_ref() { - ValueRef::Tag(tag) => { - Some(Wrap(match tag { + ValueRef::Data(data) => { + Some(Wrap(match data.tag() { 0 => Ok, 1 => NotFound, 2 => InternalServerError, diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index 706616c1d4..cf0fb074ad 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -142,7 +142,6 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { ValueRef::Float(_) => self.deserialize_f64(visitor), ValueRef::Int(_) => self.deserialize_i64(visitor), ValueRef::String(ref s) => visitor.visit_borrowed_str(s), - ValueRef::Tag(_) => self.deserialize_enum("", &[], visitor), ValueRef::Userdata(_) | ValueRef::Internal => Err(Self::Error::custom("Cant deserialize type")), } @@ -153,7 +152,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { V: Visitor<'de>, { match self.input.as_ref() { - ValueRef::Tag(t) => visitor.visit_bool(t != 0), + ValueRef::Data(data) => visitor.visit_bool(data.tag() != 0), _ => Err(Self::Error::custom("Cant deserialize type")), } } @@ -333,7 +332,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { _ => return Err(Self::Error::custom("Expected `Option` type")), }; match self.input.as_ref() { - ValueRef::Tag(0) => visitor.visit_none(), + 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(), @@ -350,7 +349,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { V: Visitor<'de>, { match self.input.as_ref() { - ValueRef::Tag(0) => visitor.visit_unit(), + ValueRef::Data(data) if data.tag() == 0 => visitor.visit_unit(), _ => Err(Self::Error::custom("Cant deserialize type")), } } @@ -583,7 +582,6 @@ impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { { match self.de.input.as_ref() { ValueRef::Data(data) => visitor.visit_u32(data.tag()), - ValueRef::Tag(tag) => visitor.visit_u32(tag), _ => Err(Self::Error::custom("Cant deserialize tag")), } } @@ -594,7 +592,6 @@ impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { { let tag = match self.de.input.as_ref() { ValueRef::Data(data) => data.tag(), - ValueRef::Tag(tag) => tag, _ => return Err(Self::Error::custom("Cant deserialize tag")), }; let typ = resolve::remove_aliases_cow(self.de.state.env, self.de.typ); diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index dc0b0b6b31..3fe5b85a35 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -43,7 +43,6 @@ pub enum ValueRef<'a> { String(&'a str), Data(Data<'a>), Array(ArrayRef<'a>), - Tag(VmTag), Userdata(&'a vm::Userdata), Internal, } @@ -59,7 +58,6 @@ impl<'a, 'b> PartialEq> for ValueRef<'a> { (&Float(l), &Float(r)) => l == r, (&String(l), &String(r)) => l == r, (&Data(l), &Data(r)) => l == r, - (&Tag(l), &Tag(r)) => l == r, _ => false, } } @@ -82,10 +80,10 @@ impl<'a> ValueRef<'a> { Value::Int(i) => ValueRef::Int(i), Value::Float(f) => ValueRef::Float(f), Value::String(s) => ValueRef::String(forget_lifetime(&*s)), - Value::Data(data) => ValueRef::Data(Data(forget_lifetime(&*data))), + Value::Data(data) => ValueRef::Data(Data(DataInner::Data(forget_lifetime(&*data)))), + Value::Tag(tag) => ValueRef::Data(Data(DataInner::Tag(tag))), Value::Array(array) => ValueRef::Array(ArrayRef(forget_lifetime(&*array))), Value::Userdata(data) => ValueRef::Userdata(forget_lifetime(&**data)), - Value::Tag(tag) => ValueRef::Tag(tag), Value::Thread(_) | Value::Function(_) | Value::Closure(_) | @@ -95,31 +93,52 @@ impl<'a> ValueRef<'a> { } #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Data<'a>(&'a DataStruct); +enum DataInner<'a> { + Tag(VmTag), + Data(&'a DataStruct), +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub struct Data<'a>(DataInner<'a>); impl<'a> Data<'a> { pub fn tag(&self) -> VmTag { - self.0.tag + match self.0 { + DataInner::Tag(tag) => tag, + DataInner::Data(data) => data.tag, + } } pub fn len(&self) -> usize { - self.0.fields.len() + match self.0 { + DataInner::Tag(_) => 0, + DataInner::Data(data) => data.fields.len(), + } } pub fn get(&self, index: usize) -> Option> { - self.0.fields.get(index).map(ValueRef::new) + match self.0 { + DataInner::Tag(_) => None, + DataInner::Data(data) => data.fields.get(index).map(ValueRef::new), + } } pub fn get_variants(&self, index: usize) -> Option> { - unsafe { self.0.fields.get(index).map(|v| Variants::new(v)) } + match self.0 { + DataInner::Tag(_) => None, + DataInner::Data(data) => unsafe { data.fields.get(index).map(|v| Variants::new(v)) }, + } } // Retrieves the field `name` from this record pub fn lookup_field(&self, thread: &Thread, name: &str) -> Option> { - unsafe { - thread.lookup_field(self.0, name).ok().map(|v| { - Variants::with_root(v, self.0) - }) + match self.0 { + DataInner::Tag(_) => None, + DataInner::Data(data) => unsafe { + thread.lookup_field(data, name).ok().map(|v| { + Variants::with_root(v, data) + }) + }, } } } @@ -557,8 +576,7 @@ impl<'vm> Pushable<'vm> for bool { impl<'vm> Getable<'vm> for bool { fn from_value(_: &'vm Thread, value: Variants) -> Option { match value.as_ref() { - ValueRef::Tag(1) => Some(true), - ValueRef::Tag(0) => Some(false), + ValueRef::Data(data) => Some(data.tag() == 1), _ => None, } } @@ -588,7 +606,6 @@ impl<'vm> Getable<'vm> for Ordering { fn from_value(_: &'vm Thread, value: Variants) -> Option { let tag = match value.as_ref() { ValueRef::Data(data) => data.tag(), - ValueRef::Tag(tag) => tag, _ => return None, }; match tag { @@ -805,7 +822,6 @@ impl<'vm, T: Getable<'vm>> Getable<'vm> for Option { T::from_value(vm, data.get_variants(0).unwrap()).map(Some) } } - ValueRef::Tag(0) => Some(None), _ => None, } } From e0be03aa57000c5927302db90a762b2596a16833 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Jun 2017 07:59:22 +0200 Subject: [PATCH 13/18] Deserialize optional fields from gluon values --- tests/de.rs | 70 +++++++++++++++++++++++++++++++++++++++++++-- vm/src/api/de.rs | 72 ++++++++++++++++++++++++++++++----------------- vm/src/api/mod.rs | 4 +++ 3 files changed, 118 insertions(+), 28 deletions(-) diff --git a/tests/de.rs b/tests/de.rs index 2df52babc8..570cc64657 100644 --- a/tests/de.rs +++ b/tests/de.rs @@ -6,8 +6,8 @@ extern crate gluon; use gluon::base::types::{ArcType, Field, Type}; use gluon::base::symbol::Symbol; -use gluon::vm::api::VmType; -use gluon::vm::api::de::De; +use gluon::vm::api::{Getable, Hole, OpaqueValue, VmType}; +use gluon::vm::api::de::{self, De}; use gluon::vm::thread::Thread; use gluon::{Compiler, new_vm}; @@ -97,3 +97,69 @@ fn partial_record() { } ); } + + +#[derive(Debug, PartialEq, Deserialize)] +struct OptionalFieldRecord { + test: Option, +} + +impl VmType for OptionalFieldRecord { + type Type = Self; + + fn make_type(thread: &Thread) -> ArcType { + Type::poly_record( + vec![], + vec![ + Field { + name: Symbol::from("test"), + typ: Option::::make_type(thread), + }, + ], + Type::hole(), + ) + } +} + +#[test] +fn optional_field() { + let _ = env_logger::init(); + + let thread = new_vm(); + + let (value, _) = Compiler::new() + .run_expr::>(&thread, "test", r#" { } "#) + .unwrap_or_else(|err| panic!("{}", err)); + assert_eq!( + De::::from_value(&thread, value.get_variants()).map(|x| x.0), + Some(OptionalFieldRecord { test: None }) + ); + + let (value, _) = Compiler::new() + .run_expr::>(&thread, "test", r#" { test = Some 2 } "#) + .unwrap_or_else(|err| panic!("{}", err)); + assert_eq!( + De::::from_value(&thread, value.get_variants()).map(|x| x.0), + Some(OptionalFieldRecord { test: Some(2) }) + ); + + let (value, _) = Compiler::new() + .run_expr::>(&thread, "test", r#" { test = 1 } "#) + .unwrap_or_else(|err| panic!("{}", err)); + + let typ = Type::poly_record( + vec![], + vec![ + Field { + name: Symbol::from("test"), + typ: i32::make_type(&thread), + }, + ], + Type::hole(), + ); + assert_eq!( + de::from_value(&thread, value.get_variants(), &typ).ok(), + Some(OptionalFieldRecord { test: Some(1) }) + ); + +} diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index cf0fb074ad..6ef3977b6f 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -20,6 +20,7 @@ impl de::Error for VmError { } } +/// `Getable` wrapper which extracts `T` by deserializing it. pub struct De(pub T); impl VmType for De @@ -39,17 +40,8 @@ where T: DeserializeOwned, { fn from_value(thread: &'vm Thread, value: Variants) -> Option { - let env = thread.global_env().get_env(); let typ = T::make_type(thread); - let mut deserializer = Deserializer { - state: State { - thread: thread, - env: &*env, - }, - input: value, - typ: &typ, - }; - T::deserialize(&mut deserializer) + from_value(thread, value, &typ) .map(De) .map_err(|err| { debug!("{}", err); @@ -59,12 +51,29 @@ where } } +/// 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 { + state: State { + thread: thread, + env: &*env, + }, + input: value, + typ: typ, + }; + T::deserialize(&mut deserializer) +} #[derive(Clone)] struct State<'de> { thread: &'de Thread, env: &'de TypeEnv, } + +#[derive(Clone)] pub struct Deserializer<'de, 't> { state: State<'de>, input: Variants<'de>, @@ -133,10 +142,15 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { } ValueRef::Byte(_) => self.deserialize_u8(visitor), ValueRef::Data(_) => { - if let Type::Record(_) = **resolve::remove_aliases_cow(self.state.env, self.typ) { - self.deserialize_enum("", &[], visitor) + 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 { - self.deserialize_map(visitor) + deserializer.deserialize_map(visitor) } } ValueRef::Float(_) => self.deserialize_f64(visitor), @@ -320,27 +334,33 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { let typ = resolve::canonical_alias(self.state.env, self.typ, |alias| { alias.name.declared_name() == "std.types.Option" }); - let typ = match **typ { + 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.declared_name() == "std.types.Option" => { - args[0].clone() + Some(&args[0]) } - _ => return Err(Self::Error::custom("Expected `Option` type")), + _ => None, } } - _ => return Err(Self::Error::custom("Expected `Option` type")), + _ => None, }; - 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_variants(0).unwrap(), - }) + // 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_variants(0).unwrap(), + }) + } + _ => Err(Self::Error::custom("Cant deserialize type")), + } } - _ => Err(Self::Error::custom("Cant deserialize type")), + None => visitor.visit_some(self), } } diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index 3fe5b85a35..cae7b7e9c7 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -1012,6 +1012,10 @@ where *self.0 } + pub fn get_variants(&self) -> Variants { + unsafe { Variants::new(&self.0) } + } + pub fn get_ref(&self) -> ValueRef { ValueRef::new(&self.0) } From b623f3b5b829305bc454b71610ad252f9170a0f0 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Jun 2017 22:25:46 +0200 Subject: [PATCH 14/18] Deserialize variants --- tests/de.rs | 66 +++++++++++++++++++++++++++ vm/src/api/de.rs | 114 ++++++++++++++++++++++++++++------------------- 2 files changed, 133 insertions(+), 47 deletions(-) diff --git a/tests/de.rs b/tests/de.rs index 570cc64657..3fbb19b710 100644 --- a/tests/de.rs +++ b/tests/de.rs @@ -163,3 +163,69 @@ fn optional_field() { ); } + +#[derive(Debug, PartialEq, Deserialize)] +enum Enum { + A(i32), + B { string: String, test: f64 }, + C(i32, i32), +} + +impl VmType for Enum { + type Type = Self; + + fn make_type(thread: &Thread) -> ArcType { + thread.find_type_info("test.Enum").unwrap().into_type() + } +} + +#[test] +fn enum_() { + let _ = env_logger::init(); + + let thread = new_vm(); + Compiler::new() + .implicit_prelude(false) + .load_script( + &thread, + "test", + r#" type Enum = | A Int | B String Float | C Int Int 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)); + + 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, + } + ); + + let (De(enum_), _) = Compiler::new() + .implicit_prelude(false) + .run_expr::>( + &thread, + "test", + r#" let { Enum } = import! "test" in C 0 1 "#, + ) + .unwrap_or_else(|err| panic!("{}", err)); + assert_eq!(enum_, Enum::C(0, 1)); +} diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index 6ef3977b6f..468701e5d9 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -1,7 +1,8 @@ use std::fmt; +use std::marker::PhantomData; use base::resolve; -use base::types::{ArcType, BuiltinType, Type, TypeEnv}; +use base::types::{ArcType, arg_iter, BuiltinType, Type, TypeEnv}; use base::symbol::Symbol; use {Error as VmError, Result, Variants}; @@ -117,10 +118,10 @@ impl<'de, 't> Deserializer<'de, 't> { if expected(&typ) { visit(c) } else { - Err(Error::custom("Cant deserialize type")) + Err(VmError::Message(format!("Unable to deserialize `{}`", self.typ))) } } - _ => Err(Error::custom("Cant deserialize type")), + _ => Err(VmError::Message(format!("Unable to deserialize `{}`", self.typ))), } } } @@ -157,7 +158,9 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { ValueRef::Int(_) => self.deserialize_i64(visitor), ValueRef::String(ref s) => visitor.visit_borrowed_str(s), ValueRef::Userdata(_) | - ValueRef::Internal => Err(Self::Error::custom("Cant deserialize type")), + ValueRef::Internal => { + Err(VmError::Message(format!("Unable to deserialize `{}`", self.typ))) + } } } @@ -167,7 +170,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Data(data) => visitor.visit_bool(data.tag() != 0), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(VmError::Message(format!("Unable to deserialize bool"))), } } @@ -179,7 +182,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_i8(b as i8), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize i8")), } } @@ -189,7 +192,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_i16(b as i16), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize i16")), } } @@ -199,7 +202,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_i32(b as i32), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize i32")), } } @@ -209,7 +212,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_i64(b as i64), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize i64")), } } @@ -219,7 +222,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_u8(b), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize u8")), } } @@ -229,7 +232,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_u16(b as u16), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize u16")), } } @@ -239,7 +242,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_u32(b as u32), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize u32")), } } @@ -249,7 +252,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_u64(b as u64), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize u64")), } } @@ -259,7 +262,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Float(f) => visitor.visit_f32(f as f32), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize f32")), } } @@ -269,7 +272,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Float(f) => visitor.visit_f64(f), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize f64")), } } @@ -283,10 +286,10 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { (ValueRef::Int(c), &Type::Builtin(BuiltinType::Char)) => { match from_u32(c as u32) { Some(c) => visitor.visit_char(c), - None => Err(Self::Error::custom("Cant deserialize type")), + None => Err(Self::Error::custom("Unable to deserialize char")), } } - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize char")), } } @@ -357,7 +360,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { input: data.get_variants(0).unwrap(), }) } - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize type")), } } None => visitor.visit_some(self), @@ -370,7 +373,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Data(data) if data.tag() == 0 => visitor.visit_unit(), - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize type")), } } @@ -395,13 +398,23 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { 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(), - &args[0], - values.iter(), - )) + 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_variants(i).unwrap()) + .zip(arg_iter(&field.typ)); + visitor.visit_seq(SeqDeserializer::new(self.state.clone(), iter)) + } + None => Err(Self::Error::custom("Unable to deserialize type")), + } } - _ => Err(Self::Error::custom("Cant deserialize type")), + _ => Err(Self::Error::custom("Unable to deserialize type")), } } @@ -437,9 +450,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { }); visitor.visit_map(MapDeserializer::new(self.state.clone(), iter)) } - _ => Err(Self::Error::custom( - format!("Cant deserialize type {}", typ), - )), + _ => Err(Self::Error::custom(format!("Unable to deserialize type {}", typ))), } } @@ -484,23 +495,22 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { struct SeqDeserializer<'de, 't, I> { state: State<'de>, - typ: &'t ArcType, iter: I, + _marker: PhantomData<&'t ()>, } impl<'de, 't, I> SeqDeserializer<'de, 't, I> { - fn new(state: State<'de>, typ: &'t ArcType, iter: I) -> Self { + fn new(state: State<'de>, iter: I) -> Self { SeqDeserializer { state: state, - typ: typ, iter: iter, + _marker: PhantomData, } } } impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I> -where - I: Iterator>, + where I: Iterator, &'t ArcType)> { type Error = VmError; @@ -509,12 +519,13 @@ where T: DeserializeSeed<'de>, { match self.iter.next() { - Some(value) => { + Some((value, typ)) => { seed.deserialize(&mut Deserializer { - state: self.state.clone(), - input: value, - typ: self.typ, - }).map(Some) + state: self.state.clone(), + input: value, + typ: typ, + }) + .map(Some) } None => Ok(None), } @@ -578,7 +589,7 @@ where typ: typ, }) } - None => Err(Self::Error::custom("Cant deserialize type")), + None => Err(Self::Error::custom("Unable to deserialize value")), } } } @@ -602,7 +613,7 @@ impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { { match self.de.input.as_ref() { ValueRef::Data(data) => visitor.visit_u32(data.tag()), - _ => Err(Self::Error::custom("Cant deserialize tag")), + _ => Err(Self::Error::custom("Unable to deserialize tag")), } } @@ -612,17 +623,18 @@ impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { { let tag = match self.de.input.as_ref() { ValueRef::Data(data) => data.tag(), - _ => return Err(Self::Error::custom("Cant deserialize 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("Cant deserialize tag") - })?; + 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("Cant deserialize tag")), + _ => return Err(Self::Error::custom("Unable to deserialize tag")), } } @@ -670,7 +682,15 @@ impl<'de, 'a, 't> VariantAccess<'de> for Enum<'a, 'de, 't> { where T: DeserializeSeed<'de>, { - seed.deserialize(self.de) + match self.de.input.as_ref() { + ValueRef::Data(data) if data.len() == 1 => { + seed.deserialize(&mut Deserializer { + input: data.get_variants(0).unwrap(), + ..self.de.clone() + }) + } + _ => Err(VmError::Message(format!("Unable to deserialize bool"))), + } } fn tuple_variant(self, _len: usize, visitor: V) -> Result @@ -684,6 +704,6 @@ impl<'de, 'a, 't> VariantAccess<'de> for Enum<'a, 'de, 't> { where V: Visitor<'de>, { - de::Deserializer::deserialize_map(self.de, visitor) + de::Deserializer::deserialize_seq(self.de, visitor) } } From 6478fffcaeac02b446baf230cf6b01b0d7bf81d7 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 15 Jun 2017 19:01:07 +0200 Subject: [PATCH 15/18] Avoid generating errors from the Deserializer unless necessary The `Deserialize` implementation can emit any errors about type mismatches so we do not need to do it in the `Deserializer`. --- vm/src/api/de.rs | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index 468701e5d9..cc55ae1fed 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -126,7 +126,6 @@ impl<'de, 't> Deserializer<'de, 't> { } } - impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { type Error = VmError; @@ -182,7 +181,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_i8(b as i8), - _ => Err(Self::Error::custom("Unable to deserialize i8")), + _ => self.deserialize_any(visitor), } } @@ -192,7 +191,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_i16(b as i16), - _ => Err(Self::Error::custom("Unable to deserialize i16")), + _ => self.deserialize_any(visitor), } } @@ -202,7 +201,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_i32(b as i32), - _ => Err(Self::Error::custom("Unable to deserialize i32")), + _ => self.deserialize_any(visitor), } } @@ -212,7 +211,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_i64(b as i64), - _ => Err(Self::Error::custom("Unable to deserialize i64")), + _ => self.deserialize_any(visitor), } } @@ -222,7 +221,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_u8(b), - _ => Err(Self::Error::custom("Unable to deserialize u8")), + _ => self.deserialize_any(visitor), } } @@ -232,7 +231,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Byte(b) => visitor.visit_u16(b as u16), - _ => Err(Self::Error::custom("Unable to deserialize u16")), + _ => self.deserialize_any(visitor), } } @@ -242,7 +241,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_u32(b as u32), - _ => Err(Self::Error::custom("Unable to deserialize u32")), + _ => self.deserialize_any(visitor), } } @@ -252,7 +251,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Int(b) => visitor.visit_u64(b as u64), - _ => Err(Self::Error::custom("Unable to deserialize u64")), + _ => self.deserialize_any(visitor), } } @@ -262,7 +261,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Float(f) => visitor.visit_f32(f as f32), - _ => Err(Self::Error::custom("Unable to deserialize f32")), + _ => self.deserialize_any(visitor), } } @@ -272,7 +271,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Float(f) => visitor.visit_f64(f), - _ => Err(Self::Error::custom("Unable to deserialize f64")), + _ => self.deserialize_any(visitor), } } @@ -286,10 +285,10 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { (ValueRef::Int(c), &Type::Builtin(BuiltinType::Char)) => { match from_u32(c as u32) { Some(c) => visitor.visit_char(c), - None => Err(Self::Error::custom("Unable to deserialize char")), + None => self.deserialize_any(visitor), } } - _ => Err(Self::Error::custom("Unable to deserialize char")), + _ => self.deserialize_any(visitor), } } @@ -360,7 +359,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { input: data.get_variants(0).unwrap(), }) } - _ => Err(Self::Error::custom("Unable to deserialize type")), + _ => self.deserialize_any(visitor), } } None => visitor.visit_some(self), @@ -373,7 +372,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { { match self.input.as_ref() { ValueRef::Data(data) if data.tag() == 0 => visitor.visit_unit(), - _ => Err(Self::Error::custom("Unable to deserialize type")), + _ => self.deserialize_any(visitor), } } @@ -411,10 +410,10 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { .zip(arg_iter(&field.typ)); visitor.visit_seq(SeqDeserializer::new(self.state.clone(), iter)) } - None => Err(Self::Error::custom("Unable to deserialize type")), + None => self.deserialize_any(visitor), } } - _ => Err(Self::Error::custom("Unable to deserialize type")), + _ => self.deserialize_any(visitor), } } @@ -450,7 +449,7 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { }); visitor.visit_map(MapDeserializer::new(self.state.clone(), iter)) } - _ => Err(Self::Error::custom(format!("Unable to deserialize type {}", typ))), + _ => self.deserialize_any(visitor), } } @@ -548,8 +547,6 @@ impl<'de, 't, I> MapDeserializer<'de, 't, I> { } } -// `MapAccess` is provided to the `Visitor` to give it the ability to iterate -// through entries of the map. impl<'de, 'a, 't, I> MapAccess<'de> for MapDeserializer<'de, 't, I> where I: Iterator< @@ -652,11 +649,6 @@ impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { } } -// `EnumAccess` is provided to the `Visitor` to give it the ability to determine -// which variant of the enum is supposed to be deserialized. -// -// Note that all enum deserialization methods in Serde refer exclusively to the -// "externally tagged" enum representation. impl<'a, 'de, 't> EnumAccess<'de> for Enum<'a, 'de, 't> { type Error = VmError; type Variant = Self; @@ -669,8 +661,6 @@ impl<'a, 'de, 't> EnumAccess<'de> for Enum<'a, 'de, 't> { } } -// `VariantAccess` is provided to the `Visitor` to give it the ability to see -// the content of the single variant that it decided to deserialize. impl<'de, 'a, 't> VariantAccess<'de> for Enum<'a, 'de, 't> { type Error = VmError; From c60d302fdd9ceb9c3b0dfef644dc784228884f71 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 19 Jun 2017 23:29:10 +0200 Subject: [PATCH 16/18] Set the correct tag when serializing structs Also moves some serialization tests to doc tests. --- tests/de.rs | 43 --------- vm/src/api/de.rs | 193 +++++++++++++++++++++++++++++++++------ vm/src/api/ser.rs | 220 ++++++++++++++++++++++++++++----------------- vm/src/core/mod.rs | 4 +- vm/src/thread.rs | 4 + 5 files changed, 310 insertions(+), 154 deletions(-) diff --git a/tests/de.rs b/tests/de.rs index 3fbb19b710..ef1dcbc065 100644 --- a/tests/de.rs +++ b/tests/de.rs @@ -49,23 +49,6 @@ impl VmType for Record { } } -#[test] -fn record() { - let _ = env_logger::init(); - - let thread = new_vm(); - let (De(record), _) = Compiler::new() - .run_expr::>(&thread, "test", r#" { test = 1, string = "test" } "#) - .unwrap_or_else(|err| panic!("{}", err)); - assert_eq!( - record, - Record { - test: 1, - string: "test".to_string(), - } - ); -} - #[test] fn option() { let _ = env_logger::init(); @@ -193,32 +176,6 @@ fn 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)); - - 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, - } - ); - let (De(enum_), _) = Compiler::new() .implicit_prelude(false) .run_expr::>( diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index cc55ae1fed..85cc4c3637 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -1,3 +1,5 @@ +//! Gluon -> Rust value conversion via the `serde::Deserialize` trait + use std::fmt; use std::marker::PhantomData; @@ -21,7 +23,129 @@ impl de::Error for VmError { } } -/// `Getable` wrapper which extracts `T` by deserializing it. +/** +`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) + } +} + +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; +#[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)] +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() + } +} + +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 @@ -54,7 +178,8 @@ where /// 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 +where + T: DeserializeOwned, { let env = thread.global_env().get_env(); let mut deserializer = Deserializer { @@ -118,10 +243,14 @@ impl<'de, 't> Deserializer<'de, 't> { if expected(&typ) { visit(c) } else { - Err(VmError::Message(format!("Unable to deserialize `{}`", self.typ))) + Err(VmError::Message( + format!("Unable to deserialize `{}`", self.typ), + )) } } - _ => Err(VmError::Message(format!("Unable to deserialize `{}`", self.typ))), + _ => Err(VmError::Message( + format!("Unable to deserialize `{}`", self.typ), + )), } } } @@ -158,7 +287,9 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { ValueRef::String(ref s) => visitor.visit_borrowed_str(s), ValueRef::Userdata(_) | ValueRef::Internal => { - Err(VmError::Message(format!("Unable to deserialize `{}`", self.typ))) + Err(VmError::Message( + format!("Unable to deserialize `{}`", self.typ), + )) } } } @@ -354,10 +485,10 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { 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_variants(0).unwrap(), - }) + state: self.state.clone(), + typ: typ, + input: data.get_variants(0).unwrap(), + }) } _ => self.deserialize_any(visitor), } @@ -397,17 +528,17 @@ impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> { 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]) - }))) + 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_variants(i).unwrap()) - .zip(arg_iter(&field.typ)); + let iter = (0..data.len()).map(|i| data.get_variants(i).unwrap()).zip( + arg_iter(&field.typ), + ); visitor.visit_seq(SeqDeserializer::new(self.state.clone(), iter)) } None => self.deserialize_any(visitor), @@ -509,7 +640,13 @@ impl<'de, 't, I> SeqDeserializer<'de, 't, I> { } impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I> - where I: Iterator, &'t ArcType)> +where + I: Iterator< + Item = ( + Variants<'de>, + &'t ArcType, + ), + >, { type Error = VmError; @@ -520,11 +657,10 @@ impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I> match self.iter.next() { Some((value, typ)) => { seed.deserialize(&mut Deserializer { - state: self.state.clone(), - input: value, - typ: typ, - }) - .map(Some) + state: self.state.clone(), + input: value, + typ: typ, + }).map(Some) } None => Ok(None), } @@ -625,10 +761,9 @@ impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> { 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"))?; + 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")), @@ -675,9 +810,9 @@ impl<'de, 'a, 't> VariantAccess<'de> for Enum<'a, 'de, 't> { match self.de.input.as_ref() { ValueRef::Data(data) if data.len() == 1 => { seed.deserialize(&mut Deserializer { - input: data.get_variants(0).unwrap(), - ..self.de.clone() - }) + input: data.get_variants(0).unwrap(), + ..self.de.clone() + }) } _ => Err(VmError::Message(format!("Unable to deserialize bool"))), } diff --git a/vm/src/api/ser.rs b/vm/src/api/ser.rs index 61cb75d4bf..d07e4fd4da 100644 --- a/vm/src/api/ser.rs +++ b/vm/src/api/ser.rs @@ -1,24 +1,140 @@ +//! Rust -> Gluon value conversion via the `serde::Serialize` trait use std::fmt; use std::ops::{Deref, DerefMut}; +use base::types::ArcType; use {Error, Result}; -use api::Pushable; +use api::{Pushable, VmType}; +use interner::InternedStr; use thread::{Context, Thread, ThreadInternal}; use types::{VmTag, VmIndex}; use value::{Def, Value}; use serde::ser::{self, Serialize}; -pub fn to_value(thread: &Thread, value: &T) -> Result +/* +`Pushable` wrapper which pushes `T` by serializing it. + +## 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::{FunctionRef, VmType}; +use gluon::vm::api::ser::Ser; +# fn main() { + +#[derive(Serialize)] +struct Vec2 { + x: i32, + y: i32, +} + +impl VmType for Vec2 { + type Type = Self; + + fn make_type(thread: &Thread) -> ArcType { + field_decl!{ x, y } + type T = record_type! { + x => i32, + y => i32 + }; + T::make_type(thread) + } +} + +let thread = new_vm(); +let (mut f, _): (FunctionRef) -> i32>, _) = Compiler::new() + .run_expr(&thread, "", r#"let f v: _ -> Int = v.x + v.y in f"#) + .unwrap_or_else(|err| panic!("{}", err)); +let vec = Vec2 { + x: 3, + y: 10 +}; +let result = f.call(Ser(vec)).unwrap_or_else(|err| panic!("{}", err)); +assert_eq!(result, 13); +# } +``` + +## Enum + +``` +#[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::{FunctionRef, VmType}; +use gluon::vm::api::ser::Ser; +# fn main() { + + +#[derive(Serialize)] +enum Enum { + A(i32), + B(String, i32), +} + +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() + } +} + +let thread = new_vm(); + +let expr = r#" +type Enum = | A Int | B String Int + +let f e = + match e with + | A a -> a + | B b c -> c + +{ Enum, f } +"#; +Compiler::new() +# .implicit_prelude(false) + .load_script(&thread, "test", expr) + .unwrap_or_else(|err| panic!("{}", err)); + +let mut f: FunctionRef) -> i32> = thread + .get_global("test.f") + .unwrap_or_else(|err| panic!("{}", err)); + +let result = f.call(Ser(Enum::B("".to_string(), 4))).unwrap_or_else(|err| panic!("{}", err)); +assert_eq!(result, 4); + +# } +``` +*/ +pub struct Ser(pub T); + +impl VmType for Ser where - T: ?Sized + Serialize, + T: VmType, { - let mut context = thread.context(); - Ser(value).push(thread, &mut context)?; - Ok(context.stack.pop()) + type Type = T::Type; + + fn make_type(thread: &Thread) -> ArcType { + T::make_type(thread) + } } -pub struct Ser(pub T); impl<'vm, T> Pushable<'vm> for Ser where @@ -72,6 +188,7 @@ struct RecordSerializer<'s, 'vm: 's> { serializer: &'s mut Serializer<'vm>, variant_index: VmTag, values: VmIndex, + fields: Vec, } impl<'s, 'vm> Deref for RecordSerializer<'s, 'vm> { @@ -93,6 +210,7 @@ impl<'s, 'vm> RecordSerializer<'s, 'vm> { serializer: serializer, variant_index: variant_index, values: 0, + fields: Vec::new(), } } } @@ -390,17 +508,20 @@ impl<'a, 'vm> ser::SerializeStruct for RecordSerializer<'a, 'vm> { type Ok = (); type Error = Error; - fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { + let field = self.thread.global_env().intern(key)?; + self.fields.push(field); value.serialize(&mut **self)?; self.values += 1; Ok(()) } - fn end(self) -> Result { - self.serializer.alloc(self.variant_index, self.values) + fn end(mut self) -> Result { + let tag = self.serializer.context.get_map(&self.fields); + self.serializer.alloc(tag, self.values) } } @@ -430,6 +551,15 @@ mod tests { use super::*; use thread::{RootedThread, RootedValue, ThreadInternal}; + fn to_value(thread: &Thread, value: &T) -> Result + where + T: ?Sized + Serialize, + { + let mut context = thread.context(); + Ser(value).push(thread, &mut context)?; + Ok(context.stack.pop()) + } + fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> RootedValue<&'vm Thread> where T: Pushable<'vm>, @@ -444,74 +574,4 @@ mod tests { let thread = RootedThread::new(); assert_eq!(to_value(&thread, &true).unwrap(), Value::Tag(1)); } - - #[derive(Serialize)] - struct Record { - test: i32, - string: String, - } - - #[test] - fn record() { - let thread = RootedThread::new(); - let actual = make_value( - &thread, - Ser(Record { - test: 123, - string: "abc".to_string(), - }), - ); - assert_eq!( - actual, - make_value( - &thread, - record! { - test => 123, - string => "abc" - }, - ) - ); - } - - #[derive(Serialize)] - enum Enum { - A(String), - B(i32), - } - - #[test] - fn enum_() { - let thread = RootedThread::new(); - - let actual = make_value(&thread, Ser(Enum::A("abc".to_string()))); - let s = make_value(&thread, "abc"); - assert_eq!( - actual, - thread.root_value(Value::Data( - thread - .context() - .gc - .alloc(Def { - tag: 0, - elems: &[*s], - }) - .unwrap(), - )) - ); - - let actual = make_value(&thread, Ser(Enum::B(1232))); - assert_eq!( - actual, - thread.root_value(Value::Data( - thread - .context() - .gc - .alloc(Def { - tag: 1, - elems: &[Value::Int(1232)], - }) - .unwrap(), - )) - ); - } } diff --git a/vm/src/core/mod.rs b/vm/src/core/mod.rs index 035505952a..c18b24765d 100644 --- a/vm/src/core/mod.rs +++ b/vm/src/core/mod.rs @@ -1038,7 +1038,7 @@ impl<'a, 'e> PatternTranslator<'a, 'e> { ast::Pattern::Record { .. } | ast::Pattern::Tuple { .. } => CType::Record, ast::Pattern::Constructor(_, _) => CType::Constructor, - ast::Pattern::Error => panic!("ICE: Error pattern survived typechecking") + ast::Pattern::Error => panic!("ICE: Error pattern survived typechecking"), } } @@ -1145,7 +1145,7 @@ impl<'a, 'e> PatternTranslator<'a, 'e> { } } } - ast::Pattern::Error => () + ast::Pattern::Error => (), } } if record_fields.is_empty() { diff --git a/vm/src/thread.rs b/vm/src/thread.rs index a6daba7bf4..16c437e152 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -1023,6 +1023,10 @@ impl Context { self.max_stack_size = limit; } + pub fn get_map(&mut self, fields: &[InternedStr]) -> VmTag { + self.record_map.get_map(fields) + } + /// "Returns a future", letting the virtual machine know that `future` must be resolved to /// produce the actual value. /// From f16fce4f195ca0abac2ca141c494747433600d3e Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 19 Jun 2017 23:34:02 +0200 Subject: [PATCH 17/18] Hide the serialization functionality that is not necessary for users --- vm/src/api/de.rs | 8 ++++---- vm/src/api/ser.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs index 85cc4c3637..effd7267c6 100644 --- a/vm/src/api/de.rs +++ b/vm/src/api/de.rs @@ -200,14 +200,14 @@ struct State<'de> { } #[derive(Clone)] -pub struct Deserializer<'de, 't> { +struct Deserializer<'de, 't> { state: State<'de>, input: Variants<'de>, typ: &'t ArcType, } impl<'de, 't> Deserializer<'de, 't> { - pub fn from_value( + fn from_value( thread: &'de Thread, env: &'de TypeEnv, input: Variants<'de>, @@ -223,7 +223,7 @@ impl<'de, 't> Deserializer<'de, 't> { } } - pub fn deserialize_builtin(&self, expected: BuiltinType, visit: F) -> Result + fn deserialize_builtin(&self, expected: BuiltinType, visit: F) -> Result where F: FnOnce(T) -> Result, T: Getable<'de>, @@ -231,7 +231,7 @@ impl<'de, 't> Deserializer<'de, 't> { self.deserialize_leaf(|t| *t == Type::Builtin(expected), visit) } - pub fn deserialize_leaf(&self, expected: E, visit: F) -> Result + fn deserialize_leaf(&self, expected: E, visit: F) -> Result where E: FnOnce(&Type) -> bool, F: FnOnce(T) -> Result, diff --git a/vm/src/api/ser.rs b/vm/src/api/ser.rs index d07e4fd4da..1e896c7462 100644 --- a/vm/src/api/ser.rs +++ b/vm/src/api/ser.rs @@ -12,7 +12,7 @@ use types::{VmTag, VmIndex}; use value::{Def, Value}; use serde::ser::{self, Serialize}; -/* +/** `Pushable` wrapper which pushes `T` by serializing it. ## Struct From 91e2614086b8adbbd849640a7fbeb6f4282143fa Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Tue, 20 Jun 2017 00:00:58 +0200 Subject: [PATCH 18/18] Fix warnings --- check/src/completion.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/check/src/completion.rs b/check/src/completion.rs index 267ec8eabd..c917950305 100644 --- a/check/src/completion.rs +++ b/check/src/completion.rs @@ -1,6 +1,5 @@ //! Primitive auto completion and type quering on ASTs -use std::borrow::Borrow; use std::iter::once; use std::cmp::Ordering; @@ -265,7 +264,7 @@ impl<'a> OnFound for GetMetadata<'a> { } } - fn pattern(&mut self, pattern: &SpannedPattern) {} + fn pattern(&mut self, _pattern: &SpannedPattern) {} fn nothing(&mut self) {} @@ -313,7 +312,7 @@ impl<'a, E: TypeEnv> OnFound for MetadataSuggest<'a, E> { } } - fn pattern(&mut self, pattern: &SpannedPattern) {} + fn pattern(&mut self, _pattern: &SpannedPattern) {} fn nothing(&mut self) { self.result = self.suggest