Commit 0611bdce authored by Alberto Ruiz's avatar Alberto Ruiz

Use fields to create implicit private struct instead of explicitly outside the macro

parent 909fe395
......@@ -12,21 +12,15 @@ The AST is defined in `src/ast.rs`. The AST is intended to match the
user's code pretty much verbatim. For example, consider a call like this:
```rust
struct CounterPrivate {
f: Cell<u32>,
}
gobject_gen! {
class Counter {
type InstancePrivate = CounterPrivate;
f: Cell<u32>;
}
impl Counter {
pub fn add(&self, x: u32) -> u32 {
let private = self.get_priv();
let v = private.f.get() + x;
private.f.set(v);
v
self.get_priv().f.set(self.get() + x);
self.get()
}
pub fn get(&self) -> u32 {
......@@ -36,9 +30,8 @@ gobject_gen! {
}
```
First, this has a `CounterPrivate` structure defined *outside* the
macro, so the macro doesn't really know about it. It's just an
artifact of the user's code.
First, `f: Cell<u32>` is a member of the private structure from a GObject
point of view, hence the call to `self.get_priv()` to access it.
Then, there is the actual invocation of the `gobject_gen!` macro. It
has two *items*, a `class` and an `impl`. Even though Rust does not
......@@ -58,12 +51,12 @@ Program {
name: Ident("Counter"),
extends: None,
items: [
ClassItem::InstancePrivate(
InstancePrivateItem {
type_token: Token!(type),
eq_token: Token!(=),
path: Path(CounterPrivate),
semi_token: Token!(;),
ClassItem::PrivateField(
PrivateField {
type: Ident("f"),
sep: Token!(:),
path: Type,
semi: Token!(;),
}
),
],
......@@ -116,7 +109,7 @@ In summary: the macro call that looks like
```
gobject_gen! {
class Counter {
... InstancePrivate definition ...
... PrivateField definitions ...
}
impl Counter {
......@@ -134,8 +127,8 @@ Program {
Class {
name: Ident("Counter"),
items: [
... one InstancePrivateItem referencing
the "CounterPrivate" path ...
... one PrivateField declaring
an f member of type Cell<u32> ...
]
}
),
......
```rust
struct FooPrivate {
...
}
// or #[derive(Default)] above if it works for you
impl Default for FooPrivate {
fn default() -> FooPrivate {
...
}
}
struct FooClassPrivate {
...
}
gnome_class! {
class Foo: Superclass {
type InstancePrivate = FooPrivate; // similar to associated types, "type Foo = Bar;"
type ClassPrivate = FooClassPrivate;
field: Cell<u32>; // this is where we declare the members of the private struct
}
// this defines the class ABI, basically
......
......@@ -3,7 +3,7 @@
use proc_macro2::Term;
use syn::punctuated::Punctuated;
use syn::{Attribute, Lit};
use syn::{Block, FnArg, Ident, Path, ReturnType};
use syn::{Block, FnArg, Ident, Path, ReturnType, Type};
pub struct Program {
pub items: Vec<Item>,
......@@ -58,7 +58,7 @@ pub struct Impl {
}
pub enum ClassItem {
InstancePrivate(InstancePrivateItem),
PrivateField(Field)
}
pub struct ImplItem {
......@@ -81,10 +81,9 @@ pub struct ImplItemMethod {
pub body: Option<Block>,
}
// Mostly copied from syn's ImplItemType
pub struct InstancePrivateItem {
pub type_token: Token!(type),
pub eq_token: Token!(=),
pub path: Path,
pub semi_token: Token!(;),
pub struct Field {
pub name: Ident,
pub colon: Token!(:),
pub ty: Type,
pub semi: Token!(;),
}
......@@ -12,22 +12,7 @@ fn check_class(class: &Class) -> Result<()> {
Ok(check_class_items(class)?)
}
fn check_class_items(class: &Class) -> Result<()> {
Ok(check_private_struct(class)?)
}
fn check_private_struct(class: &Class) -> Result<()> {
let num_instance_private_items = class.items.iter().fold(0, |p, item| match *item {
ClassItem::InstancePrivate(_) => p + 1,
});
if num_instance_private_items > 1 {
bail!(ErrorKind::InstancePrivateError(format!(
"found {} InstancePrivate type declarations",
num_instance_private_items
)));
}
fn check_class_items(_class: &Class) -> Result<()> {
Ok(())
}
......@@ -41,8 +26,6 @@ pub mod tests {
pub fn run() {
checks_empty_class();
checks_class_with_instance_private();
catches_several_instance_private_items();
}
fn checks_empty_class() {
......@@ -57,40 +40,4 @@ pub mod tests {
assert!(check_program(&program).is_ok());
}
fn checks_class_with_instance_private() {
let raw = "class Foo {
type InstancePrivate = FooPrivate;
\
}";
let token_stream = raw.parse::<TokenStream>().unwrap();
let buffer = TokenBuffer::new(token_stream);
let cursor = buffer.begin();
let program = ast::Program::parse(cursor).unwrap().0;
assert!(check_program(&program).is_ok());
}
fn catches_several_instance_private_items() {
let raw = "class Foo {
type InstancePrivate = FooPriv;
\
type InstancePrivate = BarPriv;
}";
let token_stream = raw.parse::<TokenStream>().unwrap();
let buffer = TokenBuffer::new(token_stream);
let cursor = buffer.begin();
let program = ast::Program::parse(cursor).unwrap().0;
match check_program(&program) {
Err(Error(ErrorKind::InstancePrivateError(_), _)) => (),
_ => unreachable!(),
}
}
}
......@@ -14,11 +14,4 @@ error_chain! {
Io(::std::io::Error) #[cfg(unix)];
Parse(ParseError);
}
errors {
InstancePrivateError(msg: String) {
description("zero or one InstancePrivate types expected")
display("at most one InstancePrivate type definitions expected: {}", msg)
}
}
}
......@@ -22,6 +22,7 @@ impl<'ast> ClassContext<'ast> {
let ParentInstance = self.ParentInstance;
let ParentInstanceFfi = self.ParentInstanceFfi;
let PrivateClassName = &self.PrivateClassName;
let PrivateStructName = self.PrivateStructName;
let callback_guard = glib_callback_guard();
let register_instance_private = self.register_instance_private();
......@@ -30,6 +31,7 @@ impl<'ast> ClassContext<'ast> {
let free_instance_private = self.free_instance_private();
let get_type_fn_name = self.instance_get_type_fn_name();
let imp_new_fn_name = self.imp_new_fn_name();
let private_fields = &self.class.private_fields;
let slots = self.slots();
......@@ -154,6 +156,10 @@ impl<'ast> ClassContext<'ast> {
// signal ids
#(#signal_id_names: 0,)*
};
#[derive(Default)]
struct #PrivateStructName {
#(#private_fields,)*
}
// We are inside the "mod imp". We will create function
// implementations for the default handlers for methods and
......
......@@ -308,90 +308,65 @@ impl<'ast> ClassContext<'ast> {
}
pub fn register_instance_private(&self) -> Tokens {
match self.instance_private {
Some(name) => {
let PrivateName = name;
quote_cs! {
// This is an Option<_> so that we can replace its value with None on finalize() to
// release all memory it holds
gobject_ffi::g_type_class_add_private(klass, mem::size_of::<Option<#PrivateName>>());
}
}
let PrivateStructName = self.PrivateStructName;
None => {
quote_cs!{}
}
quote_cs! {
// This is an Option<_> so that we can replace its value with None on finalize() to
// release all memory it holds
gobject_ffi::g_type_class_add_private(klass, mem::size_of::<Option<#PrivateStructName>>());
}
}
pub fn get_priv_fn(&self) -> Tokens {
match self.instance_private {
Some(name) => {
let PrivateName = name;
let InstanceNameFfi = self.InstanceNameFfi;
let get_type_fn_name = self.instance_get_type_fn_name();
quote_cs! {
fn get_priv(&self) -> &#PrivateName {
unsafe {
let private = gobject_ffi::g_type_instance_get_private(
<Self as ToGlibPtr<*mut #InstanceNameFfi>>::to_glib_none(self).0 as *mut gobject_ffi::GTypeInstance,
#get_type_fn_name(),
) as *const Option<#PrivateName>;
(&*private).as_ref().unwrap()
}
}
let PrivateStructName = self.PrivateStructName;
let InstanceNameFfi = self.InstanceNameFfi;
let get_type_fn_name = self.instance_get_type_fn_name();
quote_cs! {
#[allow(dead_code)]
fn get_priv(&self) -> &#PrivateStructName {
unsafe {
let _private = gobject_ffi::g_type_instance_get_private(
<Self as ToGlibPtr<*mut #InstanceNameFfi>>::to_glib_none(self).0 as *mut gobject_ffi::GTypeInstance,
#get_type_fn_name(),
) as *const Option<#PrivateStructName>;
(&*_private).as_ref().unwrap()
}
}
None => quote_cs!{},
}
}
pub fn init_priv_with_default(&self) -> Tokens {
match self.instance_private {
Some(name) => {
let PrivateName = name;
let get_type_fn_name = self.instance_get_type_fn_name();
quote_cs! {
let private = gobject_ffi::g_type_instance_get_private(
obj,
#get_type_fn_name()
) as *mut Option<#PrivateName>;
// Here we initialize the private data. GObject gives it to us all zero-initialized
// but we don't really want to have any Drop impls run here so just overwrite the
// data.
ptr::write(private, Some(<#PrivateName as Default>::default()));
}
}
None => quote_cs!{},
let PrivateStructName = self.PrivateStructName;
let get_type_fn_name = self.instance_get_type_fn_name();
quote_cs! {
let _private = gobject_ffi::g_type_instance_get_private(
obj,
#get_type_fn_name()
) as *mut Option<#PrivateStructName>;
// Here we initialize the private data. GObject gives it to us all zero-initialized
// but we don't really want to have any Drop impls run here so just overwrite the
// data.
ptr::write(_private, Some(<#PrivateStructName as Default>::default()));
}
}
pub fn free_instance_private(&self) -> Tokens {
match self.instance_private {
Some(name) => {
let PrivateName = name;
let get_type_fn_name = self.instance_get_type_fn_name();
quote_cs! {
let private = gobject_ffi::g_type_instance_get_private(
obj as *mut gobject_ffi::GTypeInstance,
#get_type_fn_name(),
) as *mut Option<#PrivateName>;
// Drop contents of private data by replacing its
// Option container with None
let _ = (*private).take();
}
}
None => quote_cs!{},
let PrivateStructName = self.PrivateStructName;
let get_type_fn_name = self.instance_get_type_fn_name();
quote_cs! {
let _private = gobject_ffi::g_type_instance_get_private(
obj as *mut gobject_ffi::GTypeInstance,
#get_type_fn_name(),
) as *mut Option<#PrivateStructName>;
// Drop contents of private data by replacing its
// Option container with None
let _ = (*_private).take();
}
}
}
......@@ -10,6 +10,8 @@ use syn::{Ident, Path};
use errors::*;
use hir::*;
use ast;
mod boilerplate;
mod cstringident;
mod imp;
......@@ -33,7 +35,7 @@ pub fn classes(program: &Program) -> Result<Tokens> {
struct ClassContext<'ast> {
program: &'ast Program<'ast>,
class: &'ast Class<'ast>,
instance_private: Option<&'ast Path>,
PrivateStructName: Ident,
ModuleName: Ident,
InstanceName: &'ast Ident,
InstanceNameFfi: Ident,
......@@ -55,8 +57,6 @@ impl<'ast> ClassContext<'ast> {
// commonly-used symbol names for the class in question, for
// example, "FooClass" out of "Foo".
let instance_private = class.instance_private;
let InstanceName = &class.name;
// If our instance name is "Foo" and we have a suffix "Bar", generates a
......@@ -77,6 +77,7 @@ impl<'ast> ClassContext<'ast> {
};
let InstanceNameFfi = container_name("Ffi");
let PrivateStructName = container_name("Priv");
let ModuleName = container_name("Mod"); // toplevel "InstanceMod" module name
let ClassName = container_name("Class");
let PrivateClassName = container_name("ClassPrivate");
......@@ -93,7 +94,7 @@ impl<'ast> ClassContext<'ast> {
ClassContext {
program,
class,
instance_private,
PrivateStructName,
ModuleName,
InstanceName,
ClassName,
......@@ -459,3 +460,11 @@ impl<'ast> ToTokens for ArgNames<'ast> {
}
}
}
impl ToTokens for ast::Field {
fn to_tokens(&self, tokens: &mut Tokens) {
let name = &self.name;
let ty = &self.ty;
(quote_cs! { #name: #ty }).to_tokens(tokens);
}
}
\ No newline at end of file
......@@ -37,9 +37,10 @@ pub struct Class<'ast> {
pub parent_class_ffi: Tokens, // ffi::ParentClass
pub implements: Vec<Path>, // names of GTypeInterfaces
pub instance_private: Option<&'ast Path>,
// pub class_private: Option<&'ast ast::PrivateStruct>
//
pub private_fields: Vec<&'ast ast::Field>,
// The order of these is important; it's the order of the slots in FooClass
pub slots: Vec<Slot<'ast>>,
// pub n_reserved_slots: usize,
......@@ -184,13 +185,12 @@ impl<'ast> Classes<'ast> {
parent_ffi: tokens_ParentInstanceFfi(ast_class),
parent_class_ffi: tokens_ParentClassFfi(ast_class),
implements: Vec::new(),
instance_private: ast_class
private_fields: ast_class
.items
.iter()
.filter_map(|i| match *i {
ast::ClassItem::InstancePrivate(ref ip) => Some(&ip.path),
})
.next(),
ast::ClassItem::PrivateField(ref field) => Some(field)
}).collect(),
slots: Vec::new(),
overrides: HashMap::new(),
},
......
......@@ -54,27 +54,17 @@ mod parser;
/// extern crate glib; // see "Necessary imports" below on why this is needed
/// use gobject_gen::gobject_gen;
///
/// struct FooPrivate {
/// // ... your instance data here
/// }
///
/// impl Default for FooPrivate {
/// fn default() -> FooPrivate {
/// // initialize your instance data here
/// }
/// }
///
/// gobject_gen! {
/// class Foo {
/// type InstancePrivate = FooPrivate;
/// private_field: Cell<u32>;
/// }
///
/// // Methods and signals;, their order defines the ABI of your class
/// impl Foo {
/// pub fn a_static_method(&self) {
/// // self.get_priv() gives us a &mut FooPrivate - this is how
/// // you access your instance-private data
/// do_something_with(self.get_priv());
/// // self.get_priv() gives us access to the private
// fields declared in class Foo
/// do_something_with_u32(self.get_priv().private_field.get());
/// }
///
/// virtual fn a_virtual_method(&self) {
......@@ -90,21 +80,17 @@ mod parser;
///
/// # Instance-private data
///
/// GObject classes defined through this macro can have an optional
/// instance-private data structure. To specify instance-private data:
/// GObject classes defined through this macro can have instance-private data
/// declared as struct fields inside the class.
///
/// * **Declaration:** Declare a `struct` to contain your data outside
/// of the `gobject_gen!` invocation.
/// * **Declaration:** Declare struct fields inside `class Foo { ... }`
///
/// * **Initialization:** Implement the `Default` trait for your
/// struct, either with `#[derive(Default)]` if those values suit your
/// purposes, or with an `impl Default` if you need custom values.
/// When the generated code needs to initialize the instance-private
/// data, it will do so by calling your struct's `::default()` method.
///
/// * **Bind to the class:** Specify `type InstancePrivate =
/// MyPrivateStruct;` inside the `class` item in the `gobject_gen!`
/// invocation.
/// struct members, either with `#[derive(Default)]` or with an `impl Default`
/// if its missing. Note that you have to do this for each type used across
/// fields. When the generated code needs to initialize the instance-private
/// data, it will do so by calling the `Default::default()` method and assign it
/// to the internally private structure generated by the macro.
///
/// * **Drop:** When the GObject instance gets finalized, your private
/// data will be `drop()`ed. You can provide `impl Drop` for any fields
......@@ -114,38 +100,10 @@ mod parser;
///
/// ```norun
/// #[derive(Default)]
/// struct FooPrivate {
/// // ... your fields here
/// //
/// // They will get initialized per #[derive(Default)]
/// }
///
/// gobject_gen! {
/// class Foo {
/// type InstancePrivate = FooPrivate;
/// ...
/// }
/// }
/// ```
///
/// ## Example: instance-private data with custom values
///
/// ```norun
/// struct FooPrivate {
/// value: Cell<i32>;
/// }
///
/// impl Default for FooPrivate {
/// fn default() -> FooPrivate {
/// FooPrivate {
/// value: Cell::new(42); // note our custom initial value here
/// }
/// }
/// }
///
/// gobject_gen! {
/// class Foo {
/// type InstancePrivate = FooPrivate;
/// field_one: Cell<u32>;
/// field_two: Cell<u16>;
/// ...
/// }
/// }
......
......@@ -71,7 +71,7 @@ impl Synom for ast::Class {
impl Synom for ast::ClassItem {
named!(parse -> Self, alt!(
syn!(ast::InstancePrivateItem) => { |x| ast::ClassItem::InstancePrivate(x) }
syn!(ast::Field) => { |x| ast::ClassItem::PrivateField(x) }
));
fn description() -> Option<&'static str> {
......@@ -79,24 +79,19 @@ impl Synom for ast::ClassItem {
}
}
impl Synom for ast::InstancePrivateItem {
impl Synom for ast::Field {
named!(parse -> Self, do_parse!(
type_: keyword!(type) >>
call!(keyword("InstancePrivate")) >>
eq: punct!(=) >>
path: syn!(Path) >>
semi: punct!(;) >>
(ast::InstancePrivateItem {
type_token: type_,
eq_token: eq,
path: path,
semi_token: semi
name: syn!(Ident) >>
colon: punct!(:) >>
ty: syn!(syn::Type) >>
semi: punct!(;) >>
(ast::Field {
name: name,
colon: colon,
ty: ty,
semi: semi
})
));
fn description() -> Option<&'static str> {
Some("InstancePrivate type")
}
}
impl Synom for ast::Impl {
......@@ -239,11 +234,10 @@ pub mod tests {
pub fn run() {
parses_class_with_no_superclass();
parses_class_with_superclass();
parses_instance_private_item();
parses_class_item();
parses_program();
parses_plain_impl_item();
parses_impl_item_with_trait();
parses_class_with_private_field();
}
fn assert_tokens_equal<T: ToTokens>(x: &T, s: &str) {
......@@ -260,6 +254,17 @@ pub mod tests {
assert!(class.extends.is_none());
}
fn parses_class_with_private_field() {
let raw = "class Foo {
foo : u32;
bar : u32;
baz : u32;
}";
let class = parse_str::<ast::Class>(raw).unwrap();
assert_eq!(class.items.len(), 3);
}
fn parses_class_with_superclass() {
let raw = "class Foo: Bar {}";
let class = parse_str::<ast::Class>(raw).unwrap();
......@@ -268,17 +273,6 @@ pub mod tests {
assert_tokens_equal(&class.extends, "Bar");
}
fn parses_instance_private_item() {
let raw = "type InstancePrivate = FooPrivate;";
let item = parse_str::<ast::ClassItem>(raw).unwrap();
match item {
ast::ClassItem::InstancePrivate(item) => {
assert_tokens_equal(&item.path, "FooPrivate");
}
}
}
fn parses_class_item() {
let raw = "class Foo {}";
let item = parse_str::<ast::Item>(raw).unwrap();
......@@ -291,17 +285,6 @@ pub mod tests {
}
}
fn parses_program() {
let raw = "class Foo {
type InstancePrivate = FooPrivate;
\
}";
let program = parse_str::<ast::Program>(raw).unwrap();
assert!(program.items.len() == 1);
assert!(ast::get_program_classes(&program).len() == 1);
}
fn test_parsing_impl_item(raw: &str, trait_name: Option<&str>, self_name: &str) {
let item = parse_str::<ast::Item>(raw).unwrap();
......
......@@ -9,32 +9,15 @@ use gobject_gen::gobject_gen;
use std::cell::Cell;
struct CounterPrivate {
f: Cell<u32>,
}
impl Default for CounterPrivate {
fn default() -> Self {
CounterPrivate { f: Cell::new(0) }
}
}
gobject_gen! {
class Counter {
type InstancePrivate = CounterPrivate;
f: Cell<u32>;
}
impl Counter {
pub fn add(&self, x: u32) -> u32 {
let private = self.get_priv();
let v = private.f.get() + x;
private.f.set(v);
self.get();
self.foo();
v
}
fn foo(&self) {
self.get_priv().f.set(self.get() + x);
self.get()
}
pub fn get(&self) -> u32 {
......
......@@ -11,7 +11,7 @@ use std::cell::RefCell;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct DropCounter {
counter: Arc<AtomicUsize>,
}
......@@ -34,21 +34,9 @@ impl Drop for DropCounter {
}
}
struct DummyPrivate {
dc: RefCell<DropCounter>,
}
impl Default for DummyPrivate {
fn default() -> Self {
DummyPrivate {
dc: RefCell::new(DropCounter::new()),
}
}
}
gobject_gen! {
class Dummy {
type InstancePrivate = DummyPrivate;
dc: RefCell<DropCounter>;
}
impl Dummy {
......
......@@ -19,19 +19,9 @@ use std::slice;
use glib::object::*;
use glib::translate::*;
struct SignalerPrivate {
val: Cell<u32>,
}
impl Default for SignalerPrivate {
fn default() -> Self {
SignalerPrivate { val: Cell::new(0) }
}