<commit_before>//! High layer providing automatic marshalling of Rust closures
//! as C function pointers.
//!
//! The main facility here is given by the structs
//! ClosureN and
//! ClosureMutN, for natural numbers *`N`*
//! from `0` to `12` (as of
//! now). These represent C closures of *`N`* arguments, which can be
//! used to turn Rust lambdas (or in generally, anything that implements
//! `Fn` or `FnMut`) into ordinary C function pointers. For example, a
//! Rust value of type `Fn(u32, u32) -> u64` can be turned into a
//! closure of type `Closure2<u32, u32, u64>` using
//! [`Closure2::new`](struct.Closure2.html#method.new). Then a C
//! function pointer of type `extern "C" fn(u32, u32) -> u64` can be
//! borrowed from the closure and passed to C.
//!
//! The above usage case eliminates much of the boilerplate involved in
//! creating a closure as compared to the `middle` and `low` layers, but
//! at the price of flexibility. Some flexibility can be recovered by
//! manually constructing and configuring a CIF (*e.g.,* a
//! [`Cif2`](struct.Cif2.html)) and then creating the closure with
//! [`Closure2::new_with_cif`](struct.Closure2.html#method.new_with_cif).
//!
//! # Example
//!
//! Here we use [`ClosureMut1`](struct.ClosureMut1.html), which is the type
//! for creating mutable closures of one argument. We use it to turn a
//! Rust lambda into a C function pointer.
//!
//! ```
//! use libffi::high::ClosureMut1;
//!
//! let mut x = 0u64;
//! let mut f = |y: u32| { x += y as u64; x };
//!
//! let closure = ClosureMut1::new(&mut f);
//! let counter = closure.code_ptr();
//!
//! assert_eq!(5, counter(5));
//! assert_eq!(6, counter(1));
//! assert_eq!(8, counter(2));
//! ```
//!
//! Note that in the above example, `counter` is an ordinary C function
//! pointer of type `extern "C" fn(u64) -> u64`.
pub use middle::{FfiAbi, FFI_DEFAULT_ABI};

pub mod types;
pub use self::types::{Type, CType};

macro_rules! define_closure_mod {
    (
        $module:ident
        $cif:ident $callback:ident $callback_mut:ident
                   $closure:ident  $closure_mut:ident ;
                   $( $T:ident )*
    )
        =>
    {
        /// CIF and closure types organized by function arity.
        pub mod $module {
            use std::marker::PhantomData;
            use std::{mem, ptr};

            use super::*;
            use middle;

            /// A typed CIF, which statically tracks argument and result types.
            pub struct $cif<$( $T, )* R> {
                untyped: middle::Cif,
                _marker: PhantomData<fn($( $T, )*) -> R>,
            }

            impl<$( $T, )* R> $cif<$( $T, )* R> {
                /// Creates a new statically-typed CIF with the given argument
                /// and result types.
                #[allow(non_snake_case)]
                pub fn new($( $T: Type<$T>, )* result: Type<R>) -> Self {
                    let cif = middle::Cif::new(
                        vec![$( $T.into_middle() ),*].into_iter(),
                        result.into_middle());
                    $cif { untyped: cif, _marker: PhantomData }
                }

                /// Sets the CIF to use the given calling convention.
                pub fn set_abi(&mut self, abi: FfiAbi) {
                    self.untyped.set_abi(abi);
                }
            }

            impl<$( $T: CType, )* R: CType> $cif<$( $T, )* R> {
                /// Creates a new statically-typed CIF by reifying the
                /// argument types as `Type<T>`s.
                pub fn reify() -> Self {
                    Self::new($( $T::reify(), )* R::reify())
                }
            }

            // We use tuples of pointers to describe the arguments, and we
            // extract them by pattern matching. This assumes that a tuple
            // of pointers will be laid out packed and in order. This seems
            // to hold true right now, and I can’t think of a reason why it
            // wouldn’t be that way, but technically it may be undefined
            // behavior.

            /// The type of function called from an immutable, typed closure.
            pub type $callback
                = extern "C" fn(cif:      &::low::ffi_cif,
                                result:   &mut R,
                                args:     &($( &$T, )*),
                                userdata: &U);

            /// An immutable, typed closure with the given argument and result
            /// types.
            pub struct $closure<'a, $( $T, )* R> {
                untyped: middle::Closure<'a>,
                _marker: PhantomData<fn($( $T, )*) -> R>,
            }

            impl<'a, $($T: Clone + CType,)* R: CType>
                $closure<'a, $($T,)* R>
            {
                /// Constructs a typed closure callable from C from a
                /// Rust closure.
                pub fn new<Callback>(callback: &'a Callback) -> Self
                    where Callback: Fn($( $T, )*) -> R + 'a
                {
                    Self::new_with_cif($cif::reify(), callback)
                }
            }

            impl<'a, $( $T, )* R> $closure<'a, $( $T, )* R> {
                /// Gets the C code pointer that is used to invoke the
                /// closure.
                pub fn code_ptr(&self) -> &extern "C" fn($( $T, )*) -> R {
                    unsafe {
                        mem::transmute(self.untyped.code_ptr())
                    }
                }

                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function, a callback for the function to call, and
                /// userdata to pass to the callback.
                pub fn from_parts(cif: $cif<$( $T, )* R>,
                                     callback: $callback,
                                     userdata: &'a U) -> Self
                {
                    let callback: middle::Callback
                        = unsafe { mem::transmute(callback) };
                    let closure
                        = middle::Closure::new(cif.untyped,
                                               callback,
                                               userdata);
                    $closure {
                        untyped: closure,
                        _marker: PhantomData,
                    }
                }
            }

            impl<'a, $( $T: Clone, )* R> $closure<'a, $( $T, )* R> {
                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function and the Rust closure to call.
                pub fn new_with_cif<Callback>(cif: $cif<$( $T, )* R>,
                                              callback: &'a Callback) -> Self
                    where Callback: Fn($( $T, )*) -> R + 'a
                {
                    Self::from_parts(cif,
                                     Self::static_callback,
                                     callback)
                }

                #[allow(non_snake_case)]
                extern "C" fn static_callback<Callback>
                    (_cif:     &::low::ffi_cif,
                     result:   &mut R,
                     &($( $T, )*):
                               &($( &$T, )*),
                     userdata: &Callback)
                  where Callback: Fn($( $T, )*) -> R + 'a
                {
                    unsafe {
                        ptr::write(result, userdata($( $T.clone(), )*));
                    }
                }
            }

            /// The type of function called from a mutable, typed closure.
            pub type $callback_mut
                = extern "C" fn(cif:      &::low::ffi_cif,
                                result:   &mut R,
                                args:     &($( &$T, )*),
                                userdata: &mut U);

            /// A mutable, typed closure with the given argument and
            /// result types.
            pub struct $closure_mut<'a, $( $T, )* R> {
                untyped: middle::Closure<'a>,
                _marker: PhantomData<fn($( $T, )*) -> R>,
            }

            impl<'a, $($T: Clone + CType,)* R: CType>
                $closure_mut<'a, $($T,)* R>
            {
                /// Constructs a typed closure callable from C from a
                /// Rust closure.
                pub fn new<Callback>(callback: &'a mut Callback) -> Self
                    where Callback: FnMut($( $T, )*) -> R + 'a
                {
                    Self::new_with_cif($cif::reify(), callback)
                }
            }

            impl<'a, $( $T, )* R> $closure_mut<'a, $( $T, )* R> {
                /// Gets the C code pointer that is used to invoke the
                /// closure.
                pub fn code_ptr(&self) -> &extern "C" fn($( $T, )*) -> R {
                    unsafe {
                        mem::transmute(self.untyped.code_ptr())
                    }
                }

                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function, a callback for the function to call, and
                /// userdata to pass to the callback.
                pub fn from_parts(cif:      $cif<$( $T, )* R>,
                                     callback: $callback_mut,
                                     userdata: &'a mut U) -> Self
                {
                    let callback: middle::CallbackMut
                        = unsafe { mem::transmute(callback) };
                    let closure
                        = middle::Closure::new_mut(cif.untyped,
                                                   callback,
                                                   userdata);
                    $closure_mut {
                        untyped: closure,
                        _marker: PhantomData,
                    }
                }
            }

            impl<'a, $( $T: Clone, )* R> $closure_mut<'a, $( $T, )* R> {
                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function and the Rust closure to call.
                pub fn new_with_cif<Callback>(cif: $cif<$( $T, )* R>,
                                              callback: &'a mut Callback)
                                              -> Self
                    where Callback: FnMut($( $T, )*) -> R + 'a
                {
                    Self::from_parts(cif,
                                     Self::static_callback,
                                     callback)
                }

                #[allow(non_snake_case)]
                extern "C" fn static_callback<Callback>
                    (_cif:     &::low::ffi_cif,
                     result:   &mut R,
                     &($( $T, )*):
                               &($( &$T, )*),
                     userdata: &mut Callback)
                  where Callback: FnMut($( $T, )*) -> R + 'a
                {
                    unsafe {
                        ptr::write(result, userdata($( $T.clone(), )*));
                    }
                }
            }
        }
        pub use self::$module::*;
    }
}

define_closure_mod!(arity0 Cif0 Callback0 CallbackMut0 Closure0 ClosureMut0;
                   );
define_closure_mod!(arity1 Cif1 Callback1 CallbackMut1 Closure1 ClosureMut1;
                    A);
define_closure_mod!(arity2 Cif2 Callback2 CallbackMut2 Closure2 ClosureMut2;
                    A B);
define_closure_mod!(arity3 Cif3 Callback3 CallbackMut3 Closure3 ClosureMut3;
                    A B C);
define_closure_mod!(arity4 Cif4 Callback4 CallbackMut4 Closure4 ClosureMut4;
                    A B C D);
define_closure_mod!(arity5 Cif5 Callback5 CallbackMut5 Closure5 ClosureMut5;
                    A B C D E);
define_closure_mod!(arity6 Cif6 Callback6 CallbackMut6 Closure6 ClosureMut6;
                    A B C D E F);
define_closure_mod!(arity7 Cif7 Callback7 CallbackMut7 Closure7 ClosureMut7;
                    A B C D E F G);
define_closure_mod!(arity8 Cif8 Callback8 CallbackMut8 Closure8 ClosureMut8;
                    A B C D E F G H);
define_closure_mod!(arity9 Cif9 Callback9 CallbackMut9 Closure9 ClosureMut9;
                    A B C D E F G H I);
define_closure_mod!(arity10 Cif10 Callback10 CallbackMut10 Closure10 ClosureMut10;
                    A B C D E F G H I J);
define_closure_mod!(arity11 Cif11 Callback11 CallbackMut11 Closure11 ClosureMut11;
                    A B C D E F G H I J K);
define_closure_mod!(arity12 Cif12 Callback12 CallbackMut12 Closure12 ClosureMut12;
                    A B C D E F G H I J K L);

#[cfg(test)]
mod test {
    use super::*;
    use super::types::*;

    #[test]
    fn new_with_cif() {
        let x: u64 = 1;
        let f = |y: u64, z: u64| x + y + z;

        let type_   = u64::reify();
        let cif     = Cif2::new(type_.clone(), type_.clone(), type_.clone());
        let closure = Closure2::new_with_cif(cif, &f);

        assert_eq!(12, closure.code_ptr()(5, 6));
    }

    #[test]
    fn new_with_cif_mut() {
        let mut x: u64 = 0;
        let mut f = |y: u64| { x += y; x };

        let type_   = u64::reify();
        let cif     = Cif1::new(type_.clone(), type_.clone());
        let closure = ClosureMut1::new_with_cif(cif, &mut f);

        let counter = closure.code_ptr();

        assert_eq!(5, counter(5));
        assert_eq!(6, counter(1));
        assert_eq!(8, counter(2));
    }

    #[test]
    fn new() {
        let x: u64 = 1;
        let f = |y: u64, z: u64| x + y + z;

        let closure = Closure2::new(&f);

        assert_eq!(12, closure.code_ptr()(5, 6));
    }

    #[test]
    fn new_mut() {
        let mut x: u64 = 0;
        let mut f = |y: u32| { x += y as u64; x };

        let closure = ClosureMut1::new(&mut f);
        let counter = closure.code_ptr();

        assert_eq!(5, counter(5));
        assert_eq!(6, counter(1));
        assert_eq!(8, counter(2));
    }
}
<commit_msg>Tests pass again.<commit_after>//! High layer providing automatic marshalling of Rust closures
//! as C function pointers.
//!
//! The main facility here is given by the structs
//! ClosureN and
//! ClosureMutN, for natural numbers *`N`*
//! from `0` to `12` (as of
//! now). These represent C closures of *`N`* arguments, which can be
//! used to turn Rust lambdas (or in generally, anything that implements
//! `Fn` or `FnMut`) into ordinary C function pointers. For example, a
//! Rust value of type `Fn(u32, u32) -> u64` can be turned into a
//! closure of type `Closure2<u32, u32, u64>` using
//! [`Closure2::new`](struct.Closure2.html#method.new). Then a C
//! function pointer of type `extern "C" fn(u32, u32) -> u64` can be
//! borrowed from the closure and passed to C.
//!
//! The above usage case eliminates much of the boilerplate involved in
//! creating a closure as compared to the `middle` and `low` layers, but
//! at the price of flexibility. Some flexibility can be recovered by
//! manually constructing and configuring a CIF (*e.g.,* a
//! [`Cif2`](struct.Cif2.html)) and then creating the closure with
//! [`Closure2::new_with_cif`](struct.Closure2.html#method.new_with_cif).
//!
//! # Example
//!
//! Here we use [`ClosureMut1`](struct.ClosureMut1.html), which is the type
//! for creating mutable closures of one argument. We use it to turn a
//! Rust lambda into a C function pointer.
//!
//! ```
//! use libffi::high::ClosureMut1;
//!
//! let mut x = 0u64;
//! let mut f = |y: u32| { x += y as u64; x };
//!
//! let closure = ClosureMut1::new(&mut f);
//! let counter = closure.code_ptr();
//!
//! assert_eq!(5, counter(5));
//! assert_eq!(6, counter(1));
//! assert_eq!(8, counter(2));
//! ```
//!
//! Note that in the above example, `counter` is an ordinary C function
//! pointer of type `extern "C" fn(u64) -> u64`.
pub use middle::{FfiAbi, FFI_DEFAULT_ABI};

pub mod types;
pub use self::types::{Type, CType};

macro_rules! define_closure_mod {
    (
        $module:ident
        $cif:ident $callback:ident $callback_mut:ident
                   $closure:ident  $closure_mut:ident ;
                   $( $T:ident )*
    )
        =>
    {
        /// CIF and closure types organized by function arity.
        pub mod $module {
            use std::marker::PhantomData;
            use std::{mem, ptr};

            use super::*;
            use middle;

            /// A typed CIF, which statically tracks argument and result types.
            pub struct $cif<$( $T, )* R> {
                untyped: middle::Cif,
                _marker: PhantomData<fn($( $T, )*) -> R>,
            }

            impl<$( $T, )* R> $cif<$( $T, )* R> {
                /// Creates a new statically-typed CIF with the given argument
                /// and result types.
                #[allow(non_snake_case)]
                pub fn new($( $T: Type<$T>, )* result: Type<R>) -> Self {
                    let cif = middle::Cif::new(
                        vec![$( $T.into_middle() ),*].into_iter(),
                        result.into_middle());
                    $cif { untyped: cif, _marker: PhantomData }
                }

                /// Sets the CIF to use the given calling convention.
                pub fn set_abi(&mut self, abi: FfiAbi) {
                    self.untyped.set_abi(abi);
                }
            }

            impl<$( $T: CType, )* R: CType> $cif<$( $T, )* R> {
                /// Creates a new statically-typed CIF by reifying the
                /// argument types as `Type<T>`s.
                pub fn reify() -> Self {
                    Self::new($( $T::reify(), )* R::reify())
                }
            }

            // We use tuples of pointers to describe the arguments, and we
            // extract them by pattern matching. This assumes that a tuple
            // of pointers will be laid out packed and in order. This seems
            // to hold true right now, and I can’t think of a reason why it
            // wouldn’t be that way, but technically it may be undefined
            // behavior.

            /// The type of function called from an immutable, typed closure.
            pub type $callback
                = extern "C" fn(cif:      &::low::ffi_cif,
                                result:   &mut R,
                                args:     &($( &$T, )*),
                                userdata: &U);

            /// An immutable, typed closure with the given argument and result
            /// types.
            pub struct $closure<'a, $( $T, )* R> {
                untyped: middle::Closure<'a>,
                _marker: PhantomData<fn($( $T, )*) -> R>,
            }

            impl<'a, $($T: Clone + CType,)* R: CType>
                $closure<'a, $($T,)* R>
            {
                /// Constructs a typed closure callable from C from a
                /// Rust closure.
                pub fn new<Callback>(callback: &'a Callback) -> Self
                    where Callback: Fn($( $T, )*) -> R + 'a
                {
                    Self::new_with_cif($cif::reify(), callback)
                }
            }

            impl<'a, $( $T, )* R> $closure<'a, $( $T, )* R> {
                /// Gets the C code pointer that is used to invoke the
                /// closure.
                pub fn code_ptr(&self) -> &extern "C" fn($( $T, )*) -> R {
                    unsafe {
                        mem::transmute(self.untyped.code_ptr())
                    }
                }

                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function, a callback for the function to call, and
                /// userdata to pass to the callback.
                pub fn from_parts(cif: $cif<$( $T, )* R>,
                                     callback: $callback,
                                     userdata: &'a U) -> Self
                {
                    let callback: middle::Callback
                        = unsafe { mem::transmute(callback) };
                    let closure
                        = middle::Closure::new(cif.untyped,
                                               callback,
                                               userdata);
                    $closure {
                        untyped: closure,
                        _marker: PhantomData,
                    }
                }
            }

            impl<'a, $( $T: Clone, )* R> $closure<'a, $( $T, )* R> {
                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function and the Rust closure to call.
                pub fn new_with_cif<Callback>(cif: $cif<$( $T, )* R>,
                                              callback: &'a Callback) -> Self
                    where Callback: Fn($( $T, )*) -> R + 'a
                {
                    Self::from_parts(cif,
                                     Self::static_callback,
                                     callback)
                }

                #[allow(non_snake_case)]
                extern "C" fn static_callback<Callback>
                    (_cif:     &::low::ffi_cif,
                     result:   &mut R,
                     &($( $T, )*):
                               &($( &$T, )*),
                     userdata: &Callback)
                  where Callback: Fn($( $T, )*) -> R + 'a
                {
                    unsafe {
                        ptr::write(result, userdata($( $T.clone(), )*));
                    }
                }
            }

            /// The type of function called from a mutable, typed closure.
            pub type $callback_mut
                = extern "C" fn(cif:      &::low::ffi_cif,
                                result:   &mut R,
                                args:     &($( &$T, )*),
                                userdata: &mut U);

            /// A mutable, typed closure with the given argument and
            /// result types.
            pub struct $closure_mut<'a, $( $T, )* R> {
                untyped: middle::Closure<'a>,
                _marker: PhantomData<fn($( $T, )*) -> R>,
            }

            impl<'a, $($T: Clone + CType,)* R: CType>
                $closure_mut<'a, $($T,)* R>
            {
                /// Constructs a typed closure callable from C from a
                /// Rust closure.
                pub fn new<Callback>(callback: &'a mut Callback) -> Self
                    where Callback: FnMut($( $T, )*) -> R + 'a
                {
                    Self::new_with_cif($cif::reify(), callback)
                }
            }

            impl<'a, $( $T, )* R> $closure_mut<'a, $( $T, )* R> {
                /// Gets the C code pointer that is used to invoke the
                /// closure.
                pub fn code_ptr(&self) -> &extern "C" fn($( $T, )*) -> R {
                    unsafe {
                        mem::transmute(self.untyped.code_ptr())
                    }
                }

                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function, a callback for the function to call, and
                /// userdata to pass to the callback.
                pub fn from_parts(cif:      $cif<$( $T, )* R>,
                                     callback: $callback_mut,
                                     userdata: &'a mut U) -> Self
                {
                    let callback: middle::CallbackMut
                        = unsafe { mem::transmute(callback) };
                    let closure
                        = middle::Closure::new_mut(cif.untyped,
                                                   callback,
                                                   userdata);
                    $closure_mut {
                        untyped: closure,
                        _marker: PhantomData,
                    }
                }
            }

            impl<'a, $( $T: Clone, )* R> $closure_mut<'a, $( $T, )* R> {
                /// Constructs a typed closure callable from C from a CIF
                /// describing the calling convention for the resulting
                /// function and the Rust closure to call.
                pub fn new_with_cif<Callback>(cif: $cif<$( $T, )* R>,
                                              callback: &'a mut Callback)
                                              -> Self
                    where Callback: FnMut($( $T, )*) -> R + 'a
                {
                    Self::from_parts(cif,
                                     Self::static_callback,
                                     callback)
                }

                #[allow(non_snake_case)]
                extern "C" fn static_callback<Callback>
                    (_cif:     &::low::ffi_cif,
                     result:   &mut R,
                     &($( $T, )*):
                               &($( &$T, )*),
                     userdata: &mut Callback)
                  where Callback: FnMut($( $T, )*) -> R + 'a
                {
                    unsafe {
                        ptr::write(result, userdata($( $T.clone(), )*));
                    }
                }
            }
        }
        pub use self::$module::*;
    }
}

define_closure_mod!(arity0 Cif0 Callback0 CallbackMut0 Closure0 ClosureMut0;
                   );
define_closure_mod!(arity1 Cif1 Callback1 CallbackMut1 Closure1 ClosureMut1;
                    A);
define_closure_mod!(arity2 Cif2 Callback2 CallbackMut2 Closure2 ClosureMut2;
                    A B);
define_closure_mod!(arity3 Cif3 Callback3 CallbackMut3 Closure3 ClosureMut3;
                    A B C);
define_closure_mod!(arity4 Cif4 Callback4 CallbackMut4 Closure4 ClosureMut4;
                    A B C D);
define_closure_mod!(arity5 Cif5 Callback5 CallbackMut5 Closure5 ClosureMut5;
                    A B C D E);
define_closure_mod!(arity6 Cif6 Callback6 CallbackMut6 Closure6 ClosureMut6;
                    A B C D E F);
define_closure_mod!(arity7 Cif7 Callback7 CallbackMut7 Closure7 ClosureMut7;
                    A B C D E F G);
define_closure_mod!(arity8 Cif8 Callback8 CallbackMut8 Closure8 ClosureMut8;
                    A B C D E F G H);
define_closure_mod!(arity9 Cif9 Callback9 CallbackMut9 Closure9 ClosureMut9;
                    A B C D E F G H I);
define_closure_mod!(arity10 Cif10 Callback10 CallbackMut10 Closure10 ClosureMut10;
                    A B C D E F G H I J);
define_closure_mod!(arity11 Cif11 Callback11 CallbackMut11 Closure11 ClosureMut11;
                    A B C D E F G H I J K);
define_closure_mod!(arity12 Cif12 Callback12 CallbackMut12 Closure12 ClosureMut12;
                    A B C D E F G H I J K L);

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn new_with_cif() {
        let x: u64 = 1;
        let f = |y: u64, z: u64| x + y + z;

        let type_   = u64::reify();
        let cif     = Cif2::new(type_.clone(), type_.clone(), type_.clone());
        let closure = Closure2::new_with_cif(cif, &f);

        assert_eq!(12, closure.code_ptr()(5, 6));
    }

    #[test]
    fn new_with_cif_mut() {
        let mut x: u64 = 0;
        let mut f = |y: u64| { x += y; x };

        let type_   = u64::reify();
        let cif     = Cif1::new(type_.clone(), type_.clone());
        let closure = ClosureMut1::new_with_cif(cif, &mut f);

        let counter = closure.code_ptr();

        assert_eq!(5, counter(5));
        assert_eq!(6, counter(1));
        assert_eq!(8, counter(2));
    }

    #[test]
    fn new() {
        let x: u64 = 1;
        let f = |y: u64, z: u64| x + y + z;

        let closure = Closure2::new(&f);

        assert_eq!(12, closure.code_ptr()(5, 6));
    }

    #[test]
    fn new_mut() {
        let mut x: u64 = 0;
        let mut f = |y: u32| { x += y as u64; x };

        let closure = ClosureMut1::new(&mut f);
        let counter = closure.code_ptr();

        assert_eq!(5, counter(5));
        assert_eq!(6, counter(1));
        assert_eq!(8, counter(2));
    }
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

use error_chain::ChainedError;
use libc::*;
use gl;
use gl::types::*;
use std::cmp;
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::sync::{Once, ONCE_INIT, RwLock};

use command;
use dl;
use encode;
use errors::*;
use function::Function;
use sdl;

lazy_static!{
    pub static ref POINTERS: RwLock<Pointers> = RwLock::new(Pointers::default());
    static ref CAPTURING: RwLock<bool> = RwLock::new(false);
}

#[derive(Debug, Default)]
pub struct Pointers {
    RunListenServer: Function<unsafe extern "C" fn(*mut c_void,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_void,
                                                   *mut c_void)
                                                   -> c_int>,

    Cmd_AddCommand: Function<unsafe extern "C" fn(*const c_char, *mut c_void)>,
    Cmd_Argc: Function<unsafe extern "C" fn() -> c_int>,
    Cmd_Argv: Function<unsafe extern "C" fn(c_int) -> *const c_char>,
    Con_Printf: Function<unsafe extern "C" fn(*const c_char)>,
    Memory_Init: Function<unsafe extern "C" fn(*mut c_void, c_int)>,
    GL_EndRendering: Function<unsafe extern "C" fn()>,
    VideoMode_GetCurrentVideoMode: Function<unsafe extern "C" fn(*mut c_int,
                                                                 *mut c_int,
                                                                 *mut c_int)>,
    VideoMode_IsWindowed: Function<unsafe extern "C" fn() -> c_int>,

    window_rect: Option<*mut RECT>,
    s_BackBufferFBO: Option<*mut FBO_Container_t>,
}

// TODO: think about how to deal with unsafety here.
// The compiler complaining about *mut not being Send/Sync is perfectly valid here.
// Our safe RwLock cannot guarantee there isn't some game thread accessing the values
// at the same time. Although reading/writing to a pointer is already unsafe?
unsafe impl Send for Pointers {}
unsafe impl Sync for Pointers {}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct RECT {
    left: c_int,
    right: c_int,
    top: c_int,
    bottom: c_int,
}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct FBO_Container_t {
    s_hBackBufferFBO: GLuint,
    s_hBackBufferCB: GLuint,
    s_hBackBufferDB: GLuint,
    s_hBackBufferTex: GLuint,
}

/// The "main" function of hw.so, called inside `CEngineAPI::Run()`.
///
/// The game runs within this function and shortly after it exits hw.so is unloaded.
/// Note: `_restart` also causes this function to exit, in this case the launcher
/// unloads and reloads hw.so and this function is called again as if it was a fresh start.
#[export_name = "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_"]
pub unsafe extern "C" fn RunListenServer(instance: *mut c_void,
                                         basedir: *mut c_char,
                                         cmdline: *mut c_char,
                                         postRestartCmdLineArgs: *mut c_char,
                                         launcherFactory: *mut c_void,
                                         filesystemFactory: *mut c_void)
                                         -> c_int {
    // hw.so just loaded, either for the first time or potentially at a different address.
    // Refresh all pointers.
    if let Err(ref e) = refresh_pointers().chain_err(|| "error refreshing pointers") {
        panic!("{}", e.display());
    }

    // Initialize the encoding.
    {
        static INIT: Once = ONCE_INIT;
        INIT.call_once(|| if let Err(ref e) = encode::initialize()
                              .chain_err(|| "error initializing encoding") {
                           panic!("{}", e.display());
                       });
    }

    let rv = real!(RunListenServer)(instance,
                                    basedir,
                                    cmdline,
                                    postRestartCmdLineArgs,
                                    launcherFactory,
                                    filesystemFactory);

    // Since hw.so is getting unloaded, reset all pointers.
    reset_pointers();

    rv
}

/// Initializes the hunk memory.
///
/// After the hunk memory has been initialized we can register console commands and variables.
/// It's also a good place to get OpenGL function pointers.
#[no_mangle]
pub unsafe extern "C" fn Memory_Init(buf: *mut c_void, size: c_int) {
    real!(Memory_Init)(buf, size);

    register_cvars_and_commands();

    gl::ReadPixels::load_with(|s| sdl::get_proc_address(s) as _);
    con_print(&format!("gl::ReadPixels::is_loaded() = {}", gl::ReadPixels::is_loaded()));
}

/// Blits pixels from the framebuffer to screen and flips.
///
/// If framebuffers aren't used, simply flips the screen.
#[no_mangle]
pub unsafe extern "C" fn GL_EndRendering() {
    let (w, h) = get_resolution();
    let buf = encode::get_buffer((w, h));

    if *CAPTURING.read().unwrap() {
        gl::ReadPixels(0, 0, w as GLsizei, h as GLsizei,
                       gl::RGBA, gl::UNSIGNED_BYTE,
                       buf.as_mut_ptr() as _);
    }

    real!(GL_EndRendering)();

    // TODO: check if we're called from SCR_UpdateScreen().

    // println!("s_BackBufferFBO: {:?}", *POINTERS.read().unwrap().s_BackBufferFBO.unwrap());
}

/// Obtains and stores all necessary function and variable addresses.
fn refresh_pointers() -> Result<()> {
    let hw = dl::open("hw.so", RTLD_NOW | RTLD_NOLOAD)
        .chain_err(|| "couldn't load hw.so")?;

    let mut pointers = POINTERS.write().unwrap();

    unsafe {
        find!(pointers,
              hw,
              RunListenServer,
              "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_");
        find!(pointers, hw, Cmd_AddCommand, "Cmd_AddCommand");
        find!(pointers, hw, Cmd_Argc, "Cmd_Argc");
        find!(pointers, hw, Cmd_Argv, "Cmd_Argv");
        find!(pointers, hw, Con_Printf, "Con_Printf");
        find!(pointers, hw, Memory_Init, "Memory_Init");
        find!(pointers, hw, GL_EndRendering, "GL_EndRendering");
        find!(pointers, hw, VideoMode_GetCurrentVideoMode, "VideoMode_GetCurrentVideoMode");
        find!(pointers, hw, VideoMode_IsWindowed, "VideoMode_IsWindowed");

        pointers.window_rect = Some(hw.sym("window_rect")
                .chain_err(|| "couldn't find window_rect")? as _);
        pointers.s_BackBufferFBO = Some(hw.sym("s_BackBufferFBO")
                .chain_err(|| "couldn't find s_BackBufferFBO")? as _);
    }

    Ok(())
}

/// Resets all pointers to their default values.
fn reset_pointers() {
    *POINTERS.write().unwrap() = Pointers::default();
}

/// Registers console commands and variables.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_cvars_and_commands() {
    for cmd in command::COMMANDS.read().unwrap().iter() {
        register_command(cmd.name(), cmd.callback());
    }
}

/// Registers a console command.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_command(name: &'static [u8], callback: unsafe extern "C" fn()) {
    real!(Cmd_AddCommand)(name as *const _ as *const _, callback as *mut c_void);
}

/// Prints the given string to the game console.
///
/// `string` must not contain null bytes.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn con_print(string: &str) {
    real!(Con_Printf)(CString::new(string.replace('%', "%%"))
                          .expect("string cannot contain null bytes")
                          .as_ptr())
}

/// Gets the console command argument count.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argc() -> u32 {
    let argc = real!(Cmd_Argc)();
    cmp::max(0, argc) as u32
}

/// Gets a console command argument.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argv(index: u32) -> String {
    let index = cmp::min(index, i32::max_value() as u32) as i32;
    let arg = real!(Cmd_Argv)(index);
    CStr::from_ptr(arg).to_string_lossy().into_owned()
}

/// Returns the current game resolution.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn get_resolution() -> (u32, u32) {
    let mut width;
    let mut height;

    if real!(VideoMode_IsWindowed)() != 0 {
        let window_rect = *POINTERS.read().unwrap().window_rect.unwrap();
        width = window_rect.right - window_rect.left;
        height = window_rect.bottom - window_rect.top;
    } else {
        width = mem::uninitialized();
        height = mem::uninitialized();
        real!(VideoMode_GetCurrentVideoMode)(&mut width, &mut height, ptr::null_mut());
    }

    width = cmp::max(0, width);
    height = cmp::max(0, height);

    (width as u32, height as u32)
}

command!(cap_test, |engine| {
    let args = engine.args();

    let mut buf = String::new();
    buf.push_str(&format!("Args len: {}\n", args.len()));

    for arg in args {
        buf.push_str(&arg);
        buf.push('\n');
    }

    engine.con_print(&buf);
});

command!(cap_another_test, |engine| {
    engine.con_print("Hello! %s %d %\n");
});

command!(cap_start, |engine| {
    *CAPTURING.write().unwrap() = true;
});

command!(cap_stop, |engine| {
    *CAPTURING.write().unwrap() = false;
});
<commit_msg>Fix warnings<commit_after>#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

use error_chain::ChainedError;
use libc::*;
use gl;
use gl::types::*;
use std::cmp;
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::sync::{Once, ONCE_INIT, RwLock};

use command;
use dl;
use encode;
use errors::*;
use function::Function;
use sdl;

lazy_static!{
    pub static ref POINTERS: RwLock<Pointers> = RwLock::new(Pointers::default());
    static ref CAPTURING: RwLock<bool> = RwLock::new(false);
}

#[derive(Debug, Default)]
pub struct Pointers {
    RunListenServer: Function<unsafe extern "C" fn(*mut c_void,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_void,
                                                   *mut c_void)
                                                   -> c_int>,

    Cmd_AddCommand: Function<unsafe extern "C" fn(*const c_char, *mut c_void)>,
    Cmd_Argc: Function<unsafe extern "C" fn() -> c_int>,
    Cmd_Argv: Function<unsafe extern "C" fn(c_int) -> *const c_char>,
    Con_Printf: Function<unsafe extern "C" fn(*const c_char)>,
    Memory_Init: Function<unsafe extern "C" fn(*mut c_void, c_int)>,
    GL_EndRendering: Function<unsafe extern "C" fn()>,
    VideoMode_GetCurrentVideoMode: Function<unsafe extern "C" fn(*mut c_int,
                                                                 *mut c_int,
                                                                 *mut c_int)>,
    VideoMode_IsWindowed: Function<unsafe extern "C" fn() -> c_int>,

    window_rect: Option<*mut RECT>,
    s_BackBufferFBO: Option<*mut FBO_Container_t>,
}

// TODO: think about how to deal with unsafety here.
// The compiler complaining about *mut not being Send/Sync is perfectly valid here.
// Our safe RwLock cannot guarantee there isn't some game thread accessing the values
// at the same time. Although reading/writing to a pointer is already unsafe?
unsafe impl Send for Pointers {}
unsafe impl Sync for Pointers {}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct RECT {
    left: c_int,
    right: c_int,
    top: c_int,
    bottom: c_int,
}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct FBO_Container_t {
    s_hBackBufferFBO: GLuint,
    s_hBackBufferCB: GLuint,
    s_hBackBufferDB: GLuint,
    s_hBackBufferTex: GLuint,
}

/// The "main" function of hw.so, called inside `CEngineAPI::Run()`.
///
/// The game runs within this function and shortly after it exits hw.so is unloaded.
/// Note: `_restart` also causes this function to exit, in this case the launcher
/// unloads and reloads hw.so and this function is called again as if it was a fresh start.
#[export_name = "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_"]
pub unsafe extern "C" fn RunListenServer(instance: *mut c_void,
                                         basedir: *mut c_char,
                                         cmdline: *mut c_char,
                                         postRestartCmdLineArgs: *mut c_char,
                                         launcherFactory: *mut c_void,
                                         filesystemFactory: *mut c_void)
                                         -> c_int {
    // hw.so just loaded, either for the first time or potentially at a different address.
    // Refresh all pointers.
    if let Err(ref e) = refresh_pointers().chain_err(|| "error refreshing pointers") {
        panic!("{}", e.display());
    }

    // Initialize the encoding.
    {
        static INIT: Once = ONCE_INIT;
        INIT.call_once(|| if let Err(ref e) = encode::initialize()
                              .chain_err(|| "error initializing encoding") {
                           panic!("{}", e.display());
                       });
    }

    let rv = real!(RunListenServer)(instance,
                                    basedir,
                                    cmdline,
                                    postRestartCmdLineArgs,
                                    launcherFactory,
                                    filesystemFactory);

    // Since hw.so is getting unloaded, reset all pointers.
    reset_pointers();

    rv
}

/// Initializes the hunk memory.
///
/// After the hunk memory has been initialized we can register console commands and variables.
/// It's also a good place to get OpenGL function pointers.
#[no_mangle]
pub unsafe extern "C" fn Memory_Init(buf: *mut c_void, size: c_int) {
    real!(Memory_Init)(buf, size);

    register_cvars_and_commands();

    gl::ReadPixels::load_with(|s| sdl::get_proc_address(s) as _);
    con_print(&format!("gl::ReadPixels::is_loaded() = {}", gl::ReadPixels::is_loaded()));
}

/// Blits pixels from the framebuffer to screen and flips.
///
/// If framebuffers aren't used, simply flips the screen.
#[no_mangle]
pub unsafe extern "C" fn GL_EndRendering() {
    let (w, h) = get_resolution();
    let buf = encode::get_buffer((w, h));

    if *CAPTURING.read().unwrap() {
        gl::ReadPixels(0, 0, w as GLsizei, h as GLsizei,
                       gl::RGBA, gl::UNSIGNED_BYTE,
                       buf.as_mut_ptr() as _);
    }

    real!(GL_EndRendering)();

    // TODO: check if we're called from SCR_UpdateScreen().

    // println!("s_BackBufferFBO: {:?}", *POINTERS.read().unwrap().s_BackBufferFBO.unwrap());
}

/// Obtains and stores all necessary function and variable addresses.
fn refresh_pointers() -> Result<()> {
    let hw = dl::open("hw.so", RTLD_NOW | RTLD_NOLOAD)
        .chain_err(|| "couldn't load hw.so")?;

    let mut pointers = POINTERS.write().unwrap();

    unsafe {
        find!(pointers,
              hw,
              RunListenServer,
              "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_");
        find!(pointers, hw, Cmd_AddCommand, "Cmd_AddCommand");
        find!(pointers, hw, Cmd_Argc, "Cmd_Argc");
        find!(pointers, hw, Cmd_Argv, "Cmd_Argv");
        find!(pointers, hw, Con_Printf, "Con_Printf");
        find!(pointers, hw, Memory_Init, "Memory_Init");
        find!(pointers, hw, GL_EndRendering, "GL_EndRendering");
        find!(pointers, hw, VideoMode_GetCurrentVideoMode, "VideoMode_GetCurrentVideoMode");
        find!(pointers, hw, VideoMode_IsWindowed, "VideoMode_IsWindowed");

        pointers.window_rect = Some(hw.sym("window_rect")
                .chain_err(|| "couldn't find window_rect")? as _);
        pointers.s_BackBufferFBO = Some(hw.sym("s_BackBufferFBO")
                .chain_err(|| "couldn't find s_BackBufferFBO")? as _);
    }

    Ok(())
}

/// Resets all pointers to their default values.
fn reset_pointers() {
    *POINTERS.write().unwrap() = Pointers::default();
}

/// Registers console commands and variables.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_cvars_and_commands() {
    for cmd in command::COMMANDS.read().unwrap().iter() {
        register_command(cmd.name(), cmd.callback());
    }
}

/// Registers a console command.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_command(name: &'static [u8], callback: unsafe extern "C" fn()) {
    real!(Cmd_AddCommand)(name as *const _ as *const _, callback as *mut c_void);
}

/// Prints the given string to the game console.
///
/// `string` must not contain null bytes.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn con_print(string: &str) {
    real!(Con_Printf)(CString::new(string.replace('%', "%%"))
                          .expect("string cannot contain null bytes")
                          .as_ptr())
}

/// Gets the console command argument count.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argc() -> u32 {
    let argc = real!(Cmd_Argc)();
    cmp::max(0, argc) as u32
}

/// Gets a console command argument.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argv(index: u32) -> String {
    let index = cmp::min(index, i32::max_value() as u32) as i32;
    let arg = real!(Cmd_Argv)(index);
    CStr::from_ptr(arg).to_string_lossy().into_owned()
}

/// Returns the current game resolution.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn get_resolution() -> (u32, u32) {
    let mut width;
    let mut height;

    if real!(VideoMode_IsWindowed)() != 0 {
        let window_rect = *POINTERS.read().unwrap().window_rect.unwrap();
        width = window_rect.right - window_rect.left;
        height = window_rect.bottom - window_rect.top;
    } else {
        width = mem::uninitialized();
        height = mem::uninitialized();
        real!(VideoMode_GetCurrentVideoMode)(&mut width, &mut height, ptr::null_mut());
    }

    width = cmp::max(0, width);
    height = cmp::max(0, height);

    (width as u32, height as u32)
}

command!(cap_test, |engine| {
    let args = engine.args();

    let mut buf = String::new();
    buf.push_str(&format!("Args len: {}\n", args.len()));

    for arg in args {
        buf.push_str(&arg);
        buf.push('\n');
    }

    engine.con_print(&buf);
});

command!(cap_another_test, |engine| {
    engine.con_print("Hello! %s %d %\n");
});

command!(cap_start, |_engine| {
    *CAPTURING.write().unwrap() = true;
});

command!(cap_stop, |_engine| {
    *CAPTURING.write().unwrap() = false;
});
<|endoftext|>


<commit_before>use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt::{LowerHex, Debug};

use scroll::{self, Pread};
use scroll::ctx::TryFromCtx;
use error;

use pe::section_table;
use pe::utils;
use pe::data_directories;

pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000;
pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000;
pub const IMPORT_RVA_MASK_32: u32 = 0x8fff_ffff;
pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_8fff_ffff;

pub trait Bitfield<'a>: PartialEq + Eq + LowerHex + Debug + TryFromCtx<'a, scroll::Endian, Error=scroll::Error, Size=usize> {
    fn is_ordinal(&self) -> bool;
    fn to_ordinal(&self) -> u16;
    fn to_rva(&self) -> usize;
    fn size_of() -> usize;
    fn is_zero(&self) -> bool;
}

impl<'a> Bitfield<'a> for u64 {
    fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64 }
    fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 }
    fn to_rva(&self) -> usize { (self & IMPORT_RVA_MASK_64) as usize }
    fn size_of() -> usize { 8 }
    fn is_zero(&self) -> bool { *self == 0}
}

impl<'a> Bitfield<'a> for u32 {
    fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32 }
    fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 }
    fn to_rva(&self) -> usize { (self & IMPORT_RVA_MASK_32) as usize }
    fn size_of() -> usize { 4 }
    fn is_zero(&self) -> bool { *self == 0}
}

#[derive(Debug, Clone)]
pub struct HintNameTableEntry<'a> {
    pub hint: u16,
    pub name: &'a str,
}

impl<'a> HintNameTableEntry<'a> {
    fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
        let offset = &mut offset;
        let hint = bytes.gread_with(offset, scroll::LE)?;
        let name = bytes.pread::<&'a str>(*offset)?;
        Ok(HintNameTableEntry { hint: hint, name: name })
    }
}

#[derive(Debug, Clone)]
pub enum SyntheticImportLookupTableEntry<'a> {
    OrdinalNumber(u16),
    HintNameTableRVA ((usize, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/
}

pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;

impl<'a> SyntheticImportLookupTableEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable])
                                                                      -> error::Result<ImportLookupTable<'a>> {
        let le = scroll::LE;
        let offset = &mut offset;
        let mut table = Vec::new();
        loop {
            let bitfield: T = bytes.gread_with(offset, le)?;
            if bitfield.is_zero() {
                debug!("imports done");
                break;
            } else {
                let entry = {
                    debug!("bitfield {:#x}", bitfield);
                    use self::SyntheticImportLookupTableEntry::*;
                    if bitfield.is_ordinal() {
                        let ordinal = bitfield.to_ordinal();
                        debug!("importing by ordinal {:#x}", ordinal);
                        OrdinalNumber(ordinal)
                    } else {
                        let rva = bitfield.to_rva();
                        let hentry = {
                            debug!("searching for RVA {:#x}", rva);
                            if let Some(offset) = utils::find_offset(rva as usize, sections) {
                                debug!("offset {:#x}", offset);
                                HintNameTableEntry::parse(bytes, offset)?
                            } else {
                                warn!("Entry {} has bad RVA: {:#x}", table.len(), rva);
                                continue
                            }
                        };
                        HintNameTableRVA ((rva, hentry))
                    }
                };
                table.push(entry);
            }
        }
        Ok(table)
    }
}

// get until entry is 0
pub type ImportAddressTable = Vec<usize>;

#[repr(C)]
#[derive(Debug)]
#[derive(Pread, Pwrite, SizeWith)]
pub struct ImportDirectoryEntry {
    pub import_lookup_table_rva: u32,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name_rva: u32,
    pub import_address_table_rva: u32,
}

pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20;

impl ImportDirectoryEntry {
    pub fn is_null (&self) -> bool {
        (self.import_lookup_table_rva == 0) &&
            (self.time_date_stamp == 0) &&
            (self.forwarder_chain == 0) &&
            (self.name_rva == 0) &&
            (self.import_address_table_rva == 0)
    }
}

#[derive(Debug)]
pub struct SyntheticImportDirectoryEntry<'a> {
    pub import_directory_entry: ImportDirectoryEntry,
    /// Computed
    pub name: &'a str,
    /// The import lookup table is a vector of either ordinals, or RVAs + import names
    pub import_lookup_table: Option<ImportLookupTable<'a>>,
    /// Computed
    pub import_address_table: ImportAddressTable,
}

impl<'a> SyntheticImportDirectoryEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable]) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
        const LE: scroll::Endian = scroll::LE;
        let name_rva = import_directory_entry.name_rva;
        let name = utils::try_name(bytes, name_rva as usize, sections)?;
        let import_lookup_table = {
            let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
            debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
            if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections) {
                let import_lookup_table = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_lookup_table_offset, sections)?;
                debug!("Successfully synthesized import lookup table entry: {:#?}", import_lookup_table);
                Some(import_lookup_table)
            } else {
                None
            }
        };

        let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections).ok_or(error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?;
        let mut import_address_table = Vec::new();
        loop {
            let import_address = bytes.gread_with(import_address_table_offset, LE)?;
            if import_address == 0 { break } else { import_address_table.push(import_address); }
        }
        Ok(SyntheticImportDirectoryEntry {
            import_directory_entry: import_directory_entry,
            name: name,
            import_lookup_table: import_lookup_table,
            import_address_table: import_address_table
        })
    }
}

#[derive(Debug)]
/// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from
pub struct ImportData<'a> {
    pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>,
}

impl<'a> ImportData<'a> {
    pub fn parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result<ImportData<'a>> {
        let import_directory_table_rva = dd.virtual_address as usize;
        debug!("import_directory_table_rva {:#x}", import_directory_table_rva);
        let offset = &mut utils::find_offset(import_directory_table_rva, sections).ok_or(error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?;;
        debug!("import data offset {:#x}", offset);
        let mut import_data = Vec::new();
        loop {
            let import_directory_entry: ImportDirectoryEntry = bytes.gread_with(offset, scroll::LE)?;
            debug!("{:#?}", import_directory_entry);
            if import_directory_entry.is_null() {
                break;
            } else {
                let entry = SyntheticImportDirectoryEntry::parse::<T>(bytes, import_directory_entry, sections)?;
                debug!("entry {:#?}", entry);
                import_data.push(entry);
            }
        }
        debug!("finished ImportData");
        Ok(ImportData { import_data: import_data})
    }
}

#[derive(Debug)]
/// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to
pub struct Import<'a> {
    pub name: Cow<'a, str>,
    pub dll: &'a str,
    pub ordinal: u16,
    pub offset: usize,
    pub rva: usize,
    pub size: usize,
}

impl<'a> Import<'a> {
    pub fn parse<T: Bitfield<'a>>(_bytes: &'a [u8], import_data: &ImportData<'a>, _sections: &[section_table::SectionTable]) -> error::Result<Vec<Import<'a>>> {
        let mut imports = Vec::new();
        for data in &import_data.import_data {
            if let Some(ref import_lookup_table) = data.import_lookup_table {
                let dll = data.name;
                let import_base = data.import_directory_entry.import_address_table_rva as usize;
                debug!("Getting imports from {}", &dll);
                for (i, entry) in import_lookup_table.iter().enumerate() {
                    let offset = import_base + (i * T::size_of());
                    use self::SyntheticImportLookupTableEntry::*;
                    let (rva, name, ordinal) =
                        match entry {
                            &HintNameTableRVA ((rva, ref hint_entry)) => {
                                // if hint_entry.name = "" && hint_entry.hint = 0 {
                                //     println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva);
                                // }
                                (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint.clone())
                            },
                            &OrdinalNumber(ordinal) => {
                                let name = format!("ORDINAL {}", ordinal);
                                (0x0, Cow::Owned(name), ordinal)
                            },
                        };
                    let import =
                        Import {
                            name: name,
                            ordinal: ordinal, dll: dll,
                            size: T::size_of(), offset: offset, rva: rva as usize
                        };
                    imports.push(import);
                }
            }
        }
        Ok (imports)
    }
}
<commit_msg>Hint/Name Table RVA is always 32bit. ImportAddressTable is either 32bit or 64bit<commit_after>use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt::{LowerHex, Debug};

use scroll::{self, Pread};
use scroll::ctx::TryFromCtx;
use error;

use pe::section_table;
use pe::utils;
use pe::data_directories;

pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000;
pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000;
pub const IMPORT_RVA_MASK_32: u32 = 0x8fff_ffff;
pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_8fff_ffff;

pub trait Bitfield<'a>: PartialEq + Eq + LowerHex + Debug + TryFromCtx<'a, scroll::Endian, Error=scroll::Error, Size=usize> {
    fn is_ordinal(&self) -> bool;
    fn to_ordinal(&self) -> u16;
    fn to_rva(&self) -> u32;
    fn size_of() -> usize;
    fn is_zero(&self) -> bool;
    fn vec_to_import_address_table(Vec<Self>) -> ImportAddressTable;
}

impl<'a> Bitfield<'a> for u64 {
    fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64 }
    fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 }
    fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_64) as u32 }
    fn size_of() -> usize { 8 }
    fn is_zero(&self) -> bool { *self == 0 }
    fn vec_to_import_address_table(table: Vec<u64>) -> ImportAddressTable { ImportAddressTable::PE64(table) }
}

impl<'a> Bitfield<'a> for u32 {
    fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32 }
    fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 }
    fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_32) as u32 }
    fn size_of() -> usize { 4 }
    fn is_zero(&self) -> bool { *self == 0 }
    fn vec_to_import_address_table(table: Vec<u32>) -> ImportAddressTable { ImportAddressTable::PE32(table) }
}

#[derive(Debug, Clone)]
pub struct HintNameTableEntry<'a> {
    pub hint: u16,
    pub name: &'a str,
}

impl<'a> HintNameTableEntry<'a> {
    fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
        let offset = &mut offset;
        let hint = bytes.gread_with(offset, scroll::LE)?;
        let name = bytes.pread::<&'a str>(*offset)?;
        Ok(HintNameTableEntry { hint: hint, name: name })
    }
}

#[derive(Debug, Clone)]
pub enum SyntheticImportLookupTableEntry<'a> {
    OrdinalNumber(u16),
    HintNameTableRVA ((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/
}

pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;

impl<'a> SyntheticImportLookupTableEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable])
                                                                      -> error::Result<ImportLookupTable<'a>> {
        let le = scroll::LE;
        let offset = &mut offset;
        let mut table = Vec::new();
        loop {
            let bitfield: T = bytes.gread_with(offset, le)?;
            if bitfield.is_zero() {
                debug!("imports done");
                break;
            } else {
                let entry = {
                    debug!("bitfield {:#x}", bitfield);
                    use self::SyntheticImportLookupTableEntry::*;
                    if bitfield.is_ordinal() {
                        let ordinal = bitfield.to_ordinal();
                        debug!("importing by ordinal {:#x}", ordinal);
                        OrdinalNumber(ordinal)
                    } else {
                        let rva = bitfield.to_rva();
                        let hentry = {
                            debug!("searching for RVA {:#x}", rva);
                            if let Some(offset) = utils::find_offset(rva as usize, sections) {
                                debug!("offset {:#x}", offset);
                                HintNameTableEntry::parse(bytes, offset)?
                            } else {
                                warn!("Entry {} has bad RVA: {:#x}", table.len(), rva);
                                continue
                            }
                        };
                        HintNameTableRVA ((rva, hentry))
                    }
                };
                table.push(entry);
            }
        }
        Ok(table)
    }
}

// get until entry is 0
#[derive(Debug)]
pub enum ImportAddressTable {
    PE32(Vec<u32>),
    PE64(Vec<u64>)
}

#[repr(C)]
#[derive(Debug)]
#[derive(Pread, Pwrite, SizeWith)]
pub struct ImportDirectoryEntry {
    pub import_lookup_table_rva: u32,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name_rva: u32,
    pub import_address_table_rva: u32,
}

pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20;

impl ImportDirectoryEntry {
    pub fn is_null (&self) -> bool {
        (self.import_lookup_table_rva == 0) &&
            (self.time_date_stamp == 0) &&
            (self.forwarder_chain == 0) &&
            (self.name_rva == 0) &&
            (self.import_address_table_rva == 0)
    }
}

#[derive(Debug)]
pub struct SyntheticImportDirectoryEntry<'a> {
    pub import_directory_entry: ImportDirectoryEntry,
    /// Computed
    pub name: &'a str,
    /// The import lookup table is a vector of either ordinals, or RVAs + import names
    pub import_lookup_table: Option<ImportLookupTable<'a>>,
    /// Computed
    pub import_address_table: ImportAddressTable,
}

impl<'a> SyntheticImportDirectoryEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable]) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
        const LE: scroll::Endian = scroll::LE;
        let name_rva = import_directory_entry.name_rva;
        let name = utils::try_name(bytes, name_rva as usize, sections)?;
        let import_lookup_table = {
            let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
            debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
            if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections) {
                let import_lookup_table = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_lookup_table_offset, sections)?;
                debug!("Successfully synthesized import lookup table entry: {:#?}", import_lookup_table);
                Some(import_lookup_table)
            } else {
                None
            }
        };

        let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections).ok_or(error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?;
        let mut import_address_table = Vec::new();
        loop {
            let import_address: T = bytes.gread_with(import_address_table_offset, LE)?;
            if import_address.is_zero() { break } else { import_address_table.push(import_address); }
        }
        Ok(SyntheticImportDirectoryEntry {
            import_directory_entry: import_directory_entry,
            name: name,
            import_lookup_table: import_lookup_table,
            import_address_table: T::vec_to_import_address_table(import_address_table)
        })
    }
}

#[derive(Debug)]
/// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from
pub struct ImportData<'a> {
    pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>,
}

impl<'a> ImportData<'a> {
    pub fn parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: &data_directories::DataDirectory, sections: &[section_table::SectionTable]) -> error::Result<ImportData<'a>> {
        let import_directory_table_rva = dd.virtual_address as usize;
        debug!("import_directory_table_rva {:#x}", import_directory_table_rva);
        let offset = &mut utils::find_offset(import_directory_table_rva, sections).ok_or(error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?;;
        debug!("import data offset {:#x}", offset);
        let mut import_data = Vec::new();
        loop {
            let import_directory_entry: ImportDirectoryEntry = bytes.gread_with(offset, scroll::LE)?;
            debug!("{:#?}", import_directory_entry);
            if import_directory_entry.is_null() {
                break;
            } else {
                let entry = SyntheticImportDirectoryEntry::parse::<T>(bytes, import_directory_entry, sections)?;
                debug!("entry {:#?}", entry);
                import_data.push(entry);
            }
        }
        debug!("finished ImportData");
        Ok(ImportData { import_data: import_data})
    }
}

#[derive(Debug)]
/// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to
pub struct Import<'a> {
    pub name: Cow<'a, str>,
    pub dll: &'a str,
    pub ordinal: u16,
    pub offset: usize,
    pub rva: usize,
    pub size: usize,
}

impl<'a> Import<'a> {
    pub fn parse<T: Bitfield<'a>>(_bytes: &'a [u8], import_data: &ImportData<'a>, _sections: &[section_table::SectionTable]) -> error::Result<Vec<Import<'a>>> {
        let mut imports = Vec::new();
        for data in &import_data.import_data {
            if let Some(ref import_lookup_table) = data.import_lookup_table {
                let dll = data.name;
                let import_base = data.import_directory_entry.import_address_table_rva as usize;
                debug!("Getting imports from {}", &dll);
                for (i, entry) in import_lookup_table.iter().enumerate() {
                    let offset = import_base + (i * T::size_of());
                    use self::SyntheticImportLookupTableEntry::*;
                    let (rva, name, ordinal) =
                        match entry {
                            &HintNameTableRVA ((rva, ref hint_entry)) => {
                                // if hint_entry.name = "" && hint_entry.hint = 0 {
                                //     println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva);
                                // }
                                (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint.clone())
                            },
                            &OrdinalNumber(ordinal) => {
                                let name = format!("ORDINAL {}", ordinal);
                                (0x0, Cow::Owned(name), ordinal)
                            },
                        };
                    let import =
                        Import {
                            name: name,
                            ordinal: ordinal, dll: dll,
                            size: T::size_of(), offset: offset, rva: rva as usize
                        };
                    imports.push(import);
                }
            }
        }
        Ok (imports)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;
use std::collections::{HashMap, HashSet};

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction, RPCLog};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction(transaction: &RPCTransaction) -> Transaction {
    if transaction.to.is_empty() {
        Transaction::ContractCreation {
            caller: Address::from_str(&transaction.from).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            init: read_hex(&transaction.input).unwrap(),
        }
    } else {
        Transaction::MessageCall {
            caller: Address::from_str(&transaction.from).unwrap(),
            address: Address::from_str(&transaction.to).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            data: read_hex(&transaction.input).unwrap(),
        }
    }
}

fn from_rpc_log(log: &RPCLog) -> Log {
    let mut topics: Vec<M256> = Vec::new();
    for topic in &log.topics {
        topics.push(M256::from_str(&topic).unwrap());
    }
    Log {
        address: Address::from_str(&log.address).unwrap(),
        data: read_hex(&log.data).unwrap(),
        topics: topics,
    }
}

fn handle_fire(client: &mut GethRPCClient, vm: &mut SeqTransactionVM, last_block_id: usize) {
    let last_block_number = format!("0x{:x}", last_block_id);
    loop {
        match vm.fire() {
            Ok(()) => {
                println!("VM exited with {:?}.", vm.status());
                break;
            },
            Err(RequireError::Account(address)) => {
                println!("Feeding VM account at 0x{:x} ...", address);
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &last_block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &last_block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                if !client.account_exist(&format!("0x{:x}", address), last_block_id) {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                println!("Feeding VM account code at 0x{:x} ...", address);
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                println!("Feeding blockhash with number 0x{:x} ...", number);
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                                          .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn is_miner_or_uncle(address: Address, block: &RPCBlock, client: &mut GethRPCClient) -> bool {
    // Give up balance testing if the address is a miner or an uncle.

    let miner = Address::from_str(&block.miner).unwrap();
    if miner == address {
        return true;
    }
    if block.uncles.len() > 0 {
        for i in 0..block.uncles.len() {
            let uncle = client.get_uncle_by_block_number_and_index(&block.number,
                                                                   &format!("0x{:x}", i));
            let uncle_miner = Address::from_str(&uncle.miner).unwrap();
            if uncle_miner == address {
                return true;
            }
        }
    }

    return false;
}

fn test_block(client: &mut GethRPCClient, number: usize) {
    let block = client.get_block_by_number(format!("0x{:x}", number).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());
    let last_id = number - 1;
    let last_number = format!("0x{:x}", last_id);
    let cur_number = block.number.to_string();
    let block_header = from_rpc_block(&block);

    let mut last_vm: Option<SeqTransactionVM> = None;
    for transaction_hash in &block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = from_rpc_transaction(&client.get_transaction_by_hash(&transaction_hash));
        let receipt = client.get_transaction_receipt(&transaction_hash);

        let mut vm = if last_vm.is_none() {
            SeqTransactionVM::new(transaction, block_header.clone(), &FRONTIER_PATCH)
        } else {
            SeqTransactionVM::with_previous(transaction, block_header.clone(), &FRONTIER_PATCH, last_vm.as_ref().unwrap())
        };

        handle_fire(client, &mut vm, last_id);

        assert!(Gas::from_str(&receipt.gasUsed).unwrap() == vm.real_used_gas());
        assert!(receipt.logs.len() == vm.logs().len());
        for i in 0..receipt.logs.len() {
            assert!(from_rpc_log(&receipt.logs[i]) == vm.logs()[i]);
        }

        last_vm = Some(vm);
    }

    if last_vm.is_some() {
        for account in last_vm.as_ref().unwrap().accounts() {
            match account {
                &Account::Full {
                    address,
                    balance,
                    ref changing_storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let changing_storage: HashMap<M256, M256> = changing_storage.clone().into();
                    for (key, value) in changing_storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::Create {
                    address,
                    balance,
                    ref storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let storage: HashMap<M256, M256> = storage.clone().into();
                    for (key, value) in storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::IncreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() + balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
                &Account::DecreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() - balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
            }
        }
    }
}

fn main() {
    let matches = clap_app!(regtests =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545`.\n* Wait for the chain to sync.\n* Change directory into the `regtests` directory `$ cd regtests`\n* Run this command: `$ RUST_BACKTRACE=1 cargo run --bin regtests -- -r http://127.0.0.1:8545")
        (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r http://127.0.0.1:8545`.")
        (@arg NUMBER: -n --number +takes_value + required "Block number to run this test. Radix is 10. e.g. `-n 49439`.")
    ).get_matches();

    let address = matches.value_of("RPC").unwrap();
    let number = matches.value_of("NUMBER").unwrap();
    let mut client = GethRPCClient::new(address);

    if number.contains("..") {
        let number: Vec<&str> = number.split("..").collect();
        let from = usize::from_str_radix(&number[0], 10).unwrap();
        let to = usize::from_str_radix(&number[1], 10).unwrap();
        for n in from..to {
            test_block(&mut client, n);
        }
    } else {
        let number = usize::from_str_radix(&number, 10).unwrap();
        test_block(&mut client, number);
    }
}
<commit_msg>Problem: mentally converting hex to dec required for getting cur blk<commit_after>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;
use std::collections::{HashMap, HashSet};

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction, RPCLog};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction(transaction: &RPCTransaction) -> Transaction {
    if transaction.to.is_empty() {
        Transaction::ContractCreation {
            caller: Address::from_str(&transaction.from).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            init: read_hex(&transaction.input).unwrap(),
        }
    } else {
        Transaction::MessageCall {
            caller: Address::from_str(&transaction.from).unwrap(),
            address: Address::from_str(&transaction.to).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            data: read_hex(&transaction.input).unwrap(),
        }
    }
}

fn from_rpc_log(log: &RPCLog) -> Log {
    let mut topics: Vec<M256> = Vec::new();
    for topic in &log.topics {
        topics.push(M256::from_str(&topic).unwrap());
    }
    Log {
        address: Address::from_str(&log.address).unwrap(),
        data: read_hex(&log.data).unwrap(),
        topics: topics,
    }
}

fn handle_fire(client: &mut GethRPCClient, vm: &mut SeqTransactionVM, last_block_id: usize) {
    let last_block_number = format!("0x{:x}", last_block_id);
    loop {
        match vm.fire() {
            Ok(()) => {
                println!("VM exited with {:?}.", vm.status());
                break;
            },
            Err(RequireError::Account(address)) => {
                println!("Feeding VM account at 0x{:x} ...", address);
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &last_block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &last_block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                if !client.account_exist(&format!("0x{:x}", address), last_block_id) {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                println!("Feeding VM account code at 0x{:x} ...", address);
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                println!("Feeding blockhash with number 0x{:x} ...", number);
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                                          .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn is_miner_or_uncle(address: Address, block: &RPCBlock, client: &mut GethRPCClient) -> bool {
    // Give up balance testing if the address is a miner or an uncle.

    let miner = Address::from_str(&block.miner).unwrap();
    if miner == address {
        return true;
    }
    if block.uncles.len() > 0 {
        for i in 0..block.uncles.len() {
            let uncle = client.get_uncle_by_block_number_and_index(&block.number,
                                                                   &format!("0x{:x}", i));
            let uncle_miner = Address::from_str(&uncle.miner).unwrap();
            if uncle_miner == address {
                return true;
            }
        }
    }

    return false;
}

fn test_block(client: &mut GethRPCClient, number: usize) {
    let block = client.get_block_by_number(format!("0x{:x}", number).as_str());
    println!("block {} ({}), transaction count: {}", number, block.number, block.transactions.len());
    let last_id = number - 1;
    let last_number = format!("0x{:x}", last_id);
    let cur_number = block.number.to_string();
    let block_header = from_rpc_block(&block);

    let mut last_vm: Option<SeqTransactionVM> = None;
    for transaction_hash in &block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = from_rpc_transaction(&client.get_transaction_by_hash(&transaction_hash));
        let receipt = client.get_transaction_receipt(&transaction_hash);

        let mut vm = if last_vm.is_none() {
            SeqTransactionVM::new(transaction, block_header.clone(), &FRONTIER_PATCH)
        } else {
            SeqTransactionVM::with_previous(transaction, block_header.clone(), &FRONTIER_PATCH, last_vm.as_ref().unwrap())
        };

        handle_fire(client, &mut vm, last_id);

        assert!(Gas::from_str(&receipt.gasUsed).unwrap() == vm.real_used_gas());
        assert!(receipt.logs.len() == vm.logs().len());
        for i in 0..receipt.logs.len() {
            assert!(from_rpc_log(&receipt.logs[i]) == vm.logs()[i]);
        }

        last_vm = Some(vm);
    }

    if last_vm.is_some() {
        for account in last_vm.as_ref().unwrap().accounts() {
            match account {
                &Account::Full {
                    address,
                    balance,
                    ref changing_storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let changing_storage: HashMap<M256, M256> = changing_storage.clone().into();
                    for (key, value) in changing_storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::Create {
                    address,
                    balance,
                    ref storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let storage: HashMap<M256, M256> = storage.clone().into();
                    for (key, value) in storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::IncreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() + balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
                &Account::DecreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() - balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
            }
        }
    }
}

fn main() {
    let matches = clap_app!(regtests =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545`.\n* Wait for the chain to sync.\n* Change directory into the `regtests` directory `$ cd regtests`\n* Run this command: `$ RUST_BACKTRACE=1 cargo run --bin regtests -- -r http://127.0.0.1:8545")
        (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r http://127.0.0.1:8545`.")
        (@arg NUMBER: -n --number +takes_value + required "Block number to run this test. Radix is 10. e.g. `-n 49439`.")
    ).get_matches();

    let address = matches.value_of("RPC").unwrap();
    let number = matches.value_of("NUMBER").unwrap();
    let mut client = GethRPCClient::new(address);

    if number.contains("..") {
        let number: Vec<&str> = number.split("..").collect();
        let from = usize::from_str_radix(&number[0], 10).unwrap();
        let to = usize::from_str_radix(&number[1], 10).unwrap();
        for n in from..to {
            test_block(&mut client, n);
        }
    } else {
        let number = usize::from_str_radix(&number, 10).unwrap();
        test_block(&mut client, number);
    }
}
<|endoftext|>