<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn hydrogen_start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    println!("Test from Rust");

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };
    trace!("Address: ");

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { kill_tx = &mut k_tx.clone(); }

    // Writer thread's channel
    let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    unsafe { writer_tx = &mut w_tx.clone(); }

    debug!("Attempting connect to: {}", host_address);

    let result = TcpStream::connect(host_address);
    if result.is_err() {
        error!("Error connecting to {} - {}", host_address, result.unwrap_err());
        return -1 as c_int;
    }
    debug!("Connected");
    on_connect_handler();

    let stream = result.unwrap();
    let client = Bstream::new(stream);
    let r_client = client.clone();
    let w_client = client.clone();

    // Start the reader thread
    thread::Builder::new()
        .name("ReaderThread".to_string())
        .spawn(move||{
            reader_thread(r_client, data_handler)
        }).unwrap();

    // Start the writer thread
    thread::Builder::new()
        .name("WriterThread".to_string())
        .spawn(move||{
            writer_thread(writer_rx, w_client)
        }).unwrap();

    // Wait for the kill signal
    match kill_rx.recv() {
        Ok(_) => { }
        Err(e) => {
            error!("Error on kill channel: {}", e);
            on_disconnect_handler();
            return -1 as c_int;
        }
    };
    on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn hydrogen_kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<commit_msg>Client logging tests<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn hydrogen_start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    println!("Rust - stdout test...");

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { kill_tx = &mut k_tx.clone(); }

    // Writer thread's channel
    let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    unsafe { writer_tx = &mut w_tx.clone(); }

    debug!("Attempting connect to: {}", host_address);

    let result = TcpStream::connect(host_address);
    if result.is_err() {
        error!("Error connecting to {} - {}", host_address, result.unwrap_err());
        return -1 as c_int;
    }
    debug!("Connected");
    on_connect_handler();

    let stream = result.unwrap();
    let client = Bstream::new(stream);
    let r_client = client.clone();
    let w_client = client.clone();

    // Start the reader thread
    thread::Builder::new()
        .name("ReaderThread".to_string())
        .spawn(move||{
            reader_thread(r_client, data_handler)
        }).unwrap();

    // Start the writer thread
    thread::Builder::new()
        .name("WriterThread".to_string())
        .spawn(move||{
            writer_thread(writer_rx, w_client)
        }).unwrap();

    // Wait for the kill signal
    match kill_rx.recv() {
        Ok(_) => { }
        Err(e) => {
            error!("Error on kill channel: {}", e);
            on_disconnect_handler();
            return -1 as c_int;
        }
    };
    on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn hydrogen_kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<|endoftext|>


<commit_before>/// TODO: add markdown columns
/// Relocation computations
/// R_X86_64_NONE 0 none none
/// R_X86_64_64 1 word64 S + A
/// R_X86_64_PC32 2 word32 S + A - P
/// R_X86_64_GOT32 3 word32 G + A
/// R_X86_64_PLT32 4 word32 L + A - P
/// R_X86_64_COPY 5 none none
/// R_X86_64_GLOB_DAT 6 word64 S
/// R_X86_64_JUMP_SLOT 7 word64 S
/// R_X86_64_RELATIVE 8 word64 B + A
/// R_X86_64_GOTPCREL 9 word32 G + GOT + A - P
/// R_X86_64_32 10 word32 S + A
/// R_X86_64_32S 11 word32 S + A
/// R_X86_64_16 12 word16 S + A
/// R_X86_64_PC16 13 word16 S + A - P
/// R_X86_64_8 14 word8 S + A
/// R_X86_64_PC8 15 word8 S + A - P
/// R_X86_64_DTPMOD64 16 word64
/// R_X86_64_DTPOFF64 17 word64
/// R_X86_64_TPOFF64 18 word64
/// R_X86_64_TLSGD 19 word32
/// R_X86_64_TLSLD 20 word32
/// R_X86_64_DTPOFF32 21 word32
/// R_X86_64_GOTTPOFF 22 word32
/// R_X86_64_TPOFF32 23 word32
/// R_X86_64_PC64 24 word64 S + A - P
/// R_X86_64_GOTOFF64 25 word64 S + A - GOT
/// R_X86_64_GOTPC32 26 word32 GOT + A - P
/// R_X86_64_SIZE32 32 word32 Z + A
/// R_X86_64_SIZE64 33 word64 Z + A
/// R_X86_64_GOTPC32_TLSDESC 34 word32
/// R_X86_64_TLSDESC_CALL 35 none
/// R_X86_64_TLSDESC 36 word64×2
/// R_X86_64_IRELATIVE 37 word64 indirect (B + A)
///
/// TLS information is at http://people.redhat.com/aoliva/writeups/TLS/RFC-TLSDESC-x86.txt
/// R_X86_64_IRELATIVE is similar to R_X86_64_RELATIVE except that
/// the value used in this relocation is the program address returned by the function,
/// which takes no arguments, at the address of the result of the corresponding
/// R_X86_64_RELATIVE relocation.
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom::Start;
use std::io;
use std::fmt;
use std::slice;

pub const R_X86_64_NONE: u64 = 0; // No reloc
pub const R_X86_64_64: u64 = 1; // Direct 64 bit
pub const R_X86_64_PC32: u64 = 2; // PC relative 32 bit signed
pub const R_X86_64_GOT32: u64 = 3; // 32 bit GOT entry
pub const R_X86_64_PLT32: u64 = 4; // 32 bit PLT address
pub const R_X86_64_COPY: u64 = 5; // Copy symbol at runtime
pub const R_X86_64_GLOB_DAT: u64 = 6; // Create GOT entry
pub const R_X86_64_JUMP_SLOT: u64 = 7; // Create PLT entry
pub const R_X86_64_RELATIVE: u64 = 8; // Adjust by program base
pub const R_X86_64_GOTPCREL: u64 = 9; // 32 bit signed PC relative offset to GOT
pub const R_X86_64_32: u64 = 10; // Direct 32 bit zero extended
pub const R_X86_64_32S: u64 = 11; // Direct 32 bit sign extended
pub const R_X86_64_16: u64 = 12; // Direct 16 bit zero extended
pub const R_X86_64_PC16: u64 = 13; // 16 bit sign extended pc relative
pub const R_X86_64_8: u64 = 14; // Direct 8 bit sign extended
pub const R_X86_64_PC8: u64 = 15; // 8 bit sign extended pc relative
pub const R_X86_64_DTPMOD64: u64 = 16; // ID of module containing symbol
pub const R_X86_64_DTPOFF64: u64 = 17; // Offset in module's TLS block
pub const R_X86_64_TPOFF64: u64 = 18; // Offset in initial TLS block
pub const R_X86_64_TLSGD: u64 = 19; // 32 bit signed PC relative offset to two GOT entries for GD symbol
pub const R_X86_64_TLSLD: u64 = 20; // 32 bit signed PC relative offset to two GOT entries for LD symbol
pub const R_X86_64_DTPOFF32: u64 = 21; // Offset in TLS block
pub const R_X86_64_GOTTPOFF: u64 = 22; // 32 bit signed PC relative offset to GOT entry for IE symbol
pub const R_X86_64_TPOFF32: u64 = 23; // Offset in initial TLS block
pub const R_X86_64_PC64: u64 = 24; // PC relative 64 bit
pub const R_X86_64_GOTOFF64: u64 = 25; // 64 bit offset to GOT
pub const R_X86_64_GOTPC32: u64 = 26; // 32 bit signed pc relative offset to GOT
pub const R_X86_64_GOT64: u64 = 27; // 64-bit GOT entry offset
pub const R_X86_64_GOTPCREL64: u64 = 28; // 64-bit PC relative offset to GOT entry
pub const R_X86_64_GOTPC64: u64 = 29; // 64-bit PC relative offset to GOT
pub const R_X86_64_GOTPLT64: u64 = 30; // like GOT64, says PLT entry needed
pub const R_X86_64_PLTOFF64: u64 = 31; // 64-bit GOT relative offset to PLT entry
pub const R_X86_64_SIZE32: u64 = 32; // Size of symbol plus 32-bit addend
pub const R_X86_64_SIZE64: u64 = 33; // Size of symbol plus 64-bit addend
pub const R_X86_64_GOTPC32_TLSDESC: u64 = 34; // GOT offset for TLS descriptor.
pub const R_X86_64_TLSDESC_CALL: u64 = 35; // Marker for call through TLS descriptor.
pub const R_X86_64_TLSDESC: u64 = 36; // TLS descriptor.
pub const R_X86_64_IRELATIVE: u64 = 37; // Adjust indirectly by program base
pub const R_X86_64_RELATIVE64: u64 = 38; // 64-bit adjust by program base
pub const R_X86_64_NUM: u64 = 39;

#[inline]
pub fn type_to_str(typ: u64) -> &'static str {
    match typ {
        R_X86_64_NONE => "NONE",
        R_X86_64_64 => "64",
        R_X86_64_PC32 => "PC32",
        R_X86_64_GOT32 => "GOT32",
        R_X86_64_PLT32 => "PLT32",
        R_X86_64_COPY => "COPY",
        R_X86_64_GLOB_DAT => "GLOB_DAT",
        R_X86_64_JUMP_SLOT => "JUMP_SLOT",
        R_X86_64_RELATIVE => "RELATIVE",
        R_X86_64_GOTPCREL => "GOTPCREL",
        R_X86_64_32 => "32",
        R_X86_64_32S => "32S",
        R_X86_64_16 => "16",
        R_X86_64_PC16 => "PC16",
        R_X86_64_8 => "8",
        R_X86_64_PC8 => "PC8",
        R_X86_64_DTPMOD64 => "DTPMOD64",
        R_X86_64_DTPOFF64 => "DTPOFF64",
        R_X86_64_TPOFF64 => "TPOFF64",
        R_X86_64_TLSGD => "TLSGD",
        R_X86_64_TLSLD => "TLSLD",
        R_X86_64_DTPOFF32 => "DTPOFF32",
        R_X86_64_GOTTPOFF => "GOTTPOFF",
        R_X86_64_TPOFF32 => "TPOFF32",
        R_X86_64_PC64 => "PC64",
        R_X86_64_GOTOFF64 => "GOTOFF64",
        R_X86_64_GOTPC32 => "GOTPC32",
        R_X86_64_GOT64 => "GOT64",
        R_X86_64_GOTPCREL64 => "GOTPCREL64",
        R_X86_64_GOTPC64 => "GOTPC64",
        R_X86_64_GOTPLT64 => "GOTPLT64",
        R_X86_64_PLTOFF64 => "PLTOFF64",
        R_X86_64_SIZE32 => "SIZE32",
        R_X86_64_SIZE64 => "SIZE64",
        R_X86_64_GOTPC32_TLSDESC => "GOTPC32_TLSDESC",
        R_X86_64_TLSDESC_CALL => "TLSDESC_CALL",
        R_X86_64_TLSDESC => "TLSDESC",
        R_X86_64_IRELATIVE => "IRELATIVE",
        R_X86_64_RELATIVE64 => "RELATIVE64",
        _ => "UNKNOWN_RELA_TYPE",
    }
}

#[repr(C)]
#[derive(Clone, PartialEq)]
pub struct Rela {
    pub r_offset: u64, // Address
    pub r_info: u64, // Relocation type and symbol index
    pub r_addend: i64, // Addend
}

pub const SIZEOF_RELA: usize = 8 + 8 + 8;

#[inline(always)]
pub fn r_sym(info: u64) -> u64 {
    info >> 32
}

#[inline(always)]
pub fn r_type(info: u64) -> u64 {
    info & 0xffffffff
}

#[inline(always)]
pub fn r_info(sym: u64, typ: u64) -> u64 {
    (sym << 32) + typ
}

impl fmt::Debug for Rela {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let sym = r_sym(self.r_info);
        let typ = r_type(self.r_info);
        write!(f,
               "r_offset: {:x} {} @ {} r_addend: {:x}",
               self.r_offset,
               type_to_str(typ),
               sym,
               self.r_addend)
    }
}

/// Gets the rela entries given a rela u64, the size of the rela section in the binary, the size of a rela entry, and a count of how many.
/// Assumes the pointer is valid and can safely return a slice of memory pointing to the relas because:
/// 1. `rela` points to memory received from the kernel (i.e., it loaded the executable), _or_
/// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory.
pub unsafe fn from_raw<'a>(rela: u64, relasz: usize, relaent: usize, relacount: usize) -> &'a [Rela] {
    // TODO: validate relaent, using relacount
    if relaent == 0 {
        &[]
    } else {
        let count = (relasz / relaent) as usize;
        slice::from_raw_parts(rela as *const Rela, count)
    }
}

pub fn from_fd(fd: &mut File, offset: usize, count: usize) -> io::Result<Vec<Rela>> {
    let mut bytes = vec![0u8; count * SIZEOF_RELA];
    try!(fd.seek(Start(offset as u64)));
    try!(fd.read(&mut bytes));
    let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *mut Rela, count) };
    let mut res = Vec::with_capacity(count);
    res.extend_from_slice(bytes);
    res.dedup();
    Ok(res)
}

pub unsafe fn from_raw_plt<'a>(jmprel: u64, pltrelsz: usize) -> &'a [Rela] {
    slice::from_raw_parts(jmprel as *const Rela, pltrelsz / SIZEOF_RELA)
}
<commit_msg>elf: change from rela:from_raw to use usize first arg<commit_after>/// TODO: add markdown columns
/// Relocation computations
/// R_X86_64_NONE 0 none none
/// R_X86_64_64 1 word64 S + A
/// R_X86_64_PC32 2 word32 S + A - P
/// R_X86_64_GOT32 3 word32 G + A
/// R_X86_64_PLT32 4 word32 L + A - P
/// R_X86_64_COPY 5 none none
/// R_X86_64_GLOB_DAT 6 word64 S
/// R_X86_64_JUMP_SLOT 7 word64 S
/// R_X86_64_RELATIVE 8 word64 B + A
/// R_X86_64_GOTPCREL 9 word32 G + GOT + A - P
/// R_X86_64_32 10 word32 S + A
/// R_X86_64_32S 11 word32 S + A
/// R_X86_64_16 12 word16 S + A
/// R_X86_64_PC16 13 word16 S + A - P
/// R_X86_64_8 14 word8 S + A
/// R_X86_64_PC8 15 word8 S + A - P
/// R_X86_64_DTPMOD64 16 word64
/// R_X86_64_DTPOFF64 17 word64
/// R_X86_64_TPOFF64 18 word64
/// R_X86_64_TLSGD 19 word32
/// R_X86_64_TLSLD 20 word32
/// R_X86_64_DTPOFF32 21 word32
/// R_X86_64_GOTTPOFF 22 word32
/// R_X86_64_TPOFF32 23 word32
/// R_X86_64_PC64 24 word64 S + A - P
/// R_X86_64_GOTOFF64 25 word64 S + A - GOT
/// R_X86_64_GOTPC32 26 word32 GOT + A - P
/// R_X86_64_SIZE32 32 word32 Z + A
/// R_X86_64_SIZE64 33 word64 Z + A
/// R_X86_64_GOTPC32_TLSDESC 34 word32
/// R_X86_64_TLSDESC_CALL 35 none
/// R_X86_64_TLSDESC 36 word64×2
/// R_X86_64_IRELATIVE 37 word64 indirect (B + A)
///
/// TLS information is at http://people.redhat.com/aoliva/writeups/TLS/RFC-TLSDESC-x86.txt
/// R_X86_64_IRELATIVE is similar to R_X86_64_RELATIVE except that
/// the value used in this relocation is the program address returned by the function,
/// which takes no arguments, at the address of the result of the corresponding
/// R_X86_64_RELATIVE relocation.
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom::Start;
use std::io;
use std::fmt;
use std::slice;

pub const R_X86_64_NONE: u64 = 0; // No reloc
pub const R_X86_64_64: u64 = 1; // Direct 64 bit
pub const R_X86_64_PC32: u64 = 2; // PC relative 32 bit signed
pub const R_X86_64_GOT32: u64 = 3; // 32 bit GOT entry
pub const R_X86_64_PLT32: u64 = 4; // 32 bit PLT address
pub const R_X86_64_COPY: u64 = 5; // Copy symbol at runtime
pub const R_X86_64_GLOB_DAT: u64 = 6; // Create GOT entry
pub const R_X86_64_JUMP_SLOT: u64 = 7; // Create PLT entry
pub const R_X86_64_RELATIVE: u64 = 8; // Adjust by program base
pub const R_X86_64_GOTPCREL: u64 = 9; // 32 bit signed PC relative offset to GOT
pub const R_X86_64_32: u64 = 10; // Direct 32 bit zero extended
pub const R_X86_64_32S: u64 = 11; // Direct 32 bit sign extended
pub const R_X86_64_16: u64 = 12; // Direct 16 bit zero extended
pub const R_X86_64_PC16: u64 = 13; // 16 bit sign extended pc relative
pub const R_X86_64_8: u64 = 14; // Direct 8 bit sign extended
pub const R_X86_64_PC8: u64 = 15; // 8 bit sign extended pc relative
pub const R_X86_64_DTPMOD64: u64 = 16; // ID of module containing symbol
pub const R_X86_64_DTPOFF64: u64 = 17; // Offset in module's TLS block
pub const R_X86_64_TPOFF64: u64 = 18; // Offset in initial TLS block
pub const R_X86_64_TLSGD: u64 = 19; // 32 bit signed PC relative offset to two GOT entries for GD symbol
pub const R_X86_64_TLSLD: u64 = 20; // 32 bit signed PC relative offset to two GOT entries for LD symbol
pub const R_X86_64_DTPOFF32: u64 = 21; // Offset in TLS block
pub const R_X86_64_GOTTPOFF: u64 = 22; // 32 bit signed PC relative offset to GOT entry for IE symbol
pub const R_X86_64_TPOFF32: u64 = 23; // Offset in initial TLS block
pub const R_X86_64_PC64: u64 = 24; // PC relative 64 bit
pub const R_X86_64_GOTOFF64: u64 = 25; // 64 bit offset to GOT
pub const R_X86_64_GOTPC32: u64 = 26; // 32 bit signed pc relative offset to GOT
pub const R_X86_64_GOT64: u64 = 27; // 64-bit GOT entry offset
pub const R_X86_64_GOTPCREL64: u64 = 28; // 64-bit PC relative offset to GOT entry
pub const R_X86_64_GOTPC64: u64 = 29; // 64-bit PC relative offset to GOT
pub const R_X86_64_GOTPLT64: u64 = 30; // like GOT64, says PLT entry needed
pub const R_X86_64_PLTOFF64: u64 = 31; // 64-bit GOT relative offset to PLT entry
pub const R_X86_64_SIZE32: u64 = 32; // Size of symbol plus 32-bit addend
pub const R_X86_64_SIZE64: u64 = 33; // Size of symbol plus 64-bit addend
pub const R_X86_64_GOTPC32_TLSDESC: u64 = 34; // GOT offset for TLS descriptor.
pub const R_X86_64_TLSDESC_CALL: u64 = 35; // Marker for call through TLS descriptor.
pub const R_X86_64_TLSDESC: u64 = 36; // TLS descriptor.
pub const R_X86_64_IRELATIVE: u64 = 37; // Adjust indirectly by program base
pub const R_X86_64_RELATIVE64: u64 = 38; // 64-bit adjust by program base
pub const R_X86_64_NUM: u64 = 39;

#[inline]
pub fn type_to_str(typ: u64) -> &'static str {
    match typ {
        R_X86_64_NONE => "NONE",
        R_X86_64_64 => "64",
        R_X86_64_PC32 => "PC32",
        R_X86_64_GOT32 => "GOT32",
        R_X86_64_PLT32 => "PLT32",
        R_X86_64_COPY => "COPY",
        R_X86_64_GLOB_DAT => "GLOB_DAT",
        R_X86_64_JUMP_SLOT => "JUMP_SLOT",
        R_X86_64_RELATIVE => "RELATIVE",
        R_X86_64_GOTPCREL => "GOTPCREL",
        R_X86_64_32 => "32",
        R_X86_64_32S => "32S",
        R_X86_64_16 => "16",
        R_X86_64_PC16 => "PC16",
        R_X86_64_8 => "8",
        R_X86_64_PC8 => "PC8",
        R_X86_64_DTPMOD64 => "DTPMOD64",
        R_X86_64_DTPOFF64 => "DTPOFF64",
        R_X86_64_TPOFF64 => "TPOFF64",
        R_X86_64_TLSGD => "TLSGD",
        R_X86_64_TLSLD => "TLSLD",
        R_X86_64_DTPOFF32 => "DTPOFF32",
        R_X86_64_GOTTPOFF => "GOTTPOFF",
        R_X86_64_TPOFF32 => "TPOFF32",
        R_X86_64_PC64 => "PC64",
        R_X86_64_GOTOFF64 => "GOTOFF64",
        R_X86_64_GOTPC32 => "GOTPC32",
        R_X86_64_GOT64 => "GOT64",
        R_X86_64_GOTPCREL64 => "GOTPCREL64",
        R_X86_64_GOTPC64 => "GOTPC64",
        R_X86_64_GOTPLT64 => "GOTPLT64",
        R_X86_64_PLTOFF64 => "PLTOFF64",
        R_X86_64_SIZE32 => "SIZE32",
        R_X86_64_SIZE64 => "SIZE64",
        R_X86_64_GOTPC32_TLSDESC => "GOTPC32_TLSDESC",
        R_X86_64_TLSDESC_CALL => "TLSDESC_CALL",
        R_X86_64_TLSDESC => "TLSDESC",
        R_X86_64_IRELATIVE => "IRELATIVE",
        R_X86_64_RELATIVE64 => "RELATIVE64",
        _ => "UNKNOWN_RELA_TYPE",
    }
}

#[repr(C)]
#[derive(Clone, PartialEq)]
pub struct Rela {
    pub r_offset: u64, // Address
    pub r_info: u64, // Relocation type and symbol index
    pub r_addend: i64, // Addend
}

pub const SIZEOF_RELA: usize = 8 + 8 + 8;

#[inline(always)]
pub fn r_sym(info: u64) -> u64 {
    info >> 32
}

#[inline(always)]
pub fn r_type(info: u64) -> u64 {
    info & 0xffffffff
}

#[inline(always)]
pub fn r_info(sym: u64, typ: u64) -> u64 {
    (sym << 32) + typ
}

impl fmt::Debug for Rela {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let sym = r_sym(self.r_info);
        let typ = r_type(self.r_info);
        write!(f,
               "r_offset: {:x} {} @ {} r_addend: {:x}",
               self.r_offset,
               type_to_str(typ),
               sym,
               self.r_addend)
    }
}

/// Gets the rela entries given a rela u64, the size of the rela section in the binary, the size of a rela entry, and a count of how many.
/// Assumes the pointer is valid and can safely return a slice of memory pointing to the relas because:
/// 1. `rela` points to memory received from the kernel (i.e., it loaded the executable), _or_
/// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory.
pub unsafe fn from_raw<'a>(rela: usize, relasz: usize, relaent: usize, relacount: usize) -> &'a [Rela] {
    // TODO: validate relaent, using relacount
    if relaent == 0 {
        &[]
    } else {
        let count = (relasz / relaent) as usize;
        slice::from_raw_parts(rela as *const Rela, count)
    }
}

pub fn from_fd(fd: &mut File, offset: usize, count: usize) -> io::Result<Vec<Rela>> {
    let mut bytes = vec![0u8; count * SIZEOF_RELA];
    try!(fd.seek(Start(offset as u64)));
    try!(fd.read(&mut bytes));
    let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *mut Rela, count) };
    let mut res = Vec::with_capacity(count);
    res.extend_from_slice(bytes);
    res.dedup();
    Ok(res)
}

pub unsafe fn from_raw_plt<'a>(jmprel: u64, pltrelsz: usize) -> &'a [Rela] {
    slice::from_raw_parts(jmprel as *const Rela, pltrelsz / SIZEOF_RELA)
}
<|endoftext|>


<commit_before>use co::{Coroutine, CoResult};

pub enum CoChain<F, L> {
    Former(F, L),
    Latter(L),
}

impl<F, L, Input> Coroutine<Input> for CoChain<F, L>
    where F: Coroutine<Input, Return = Input>,
          L: Coroutine<Input, Yield = <F as Coroutine<Input>>::Yield>
{
    type Yield = <F as Coroutine<Input>>::Yield;
    type Return = <L as Coroutine<Input>>::Return;

    fn next(self, i: Input) -> CoResult<Self::Yield, Self, Self::Return> {
        match self {
            CoChain::Former(fmr, ltr) => {
                match fmr.next(i) {
                    CoResult::Yield(y, fmr) => CoResult::Yield(y, fmr.chain(ltr)),
                    CoResult::Return(retf) => CoChain::Latter(ltr).next(retf),
                }
            }
            CoChain::Latter(ltr) => {
                match ltr.next(i) {
                    CoResult::Yield(y, ltr) => CoResult::Yield(y, CoChain::Latter(ltr)),
                    CoResult::Return(retl) => CoResult::Return(retl),
                }
            }
        }
    }
}

pub trait Chain<L, Input>
    where L: Coroutine<Input>,
          Self: Sized + Coroutine<Input, Return = Input>
{
    fn chain(self, l: L) -> CoChain<Self, L> {
        CoChain::Former(self, l)
    }
}

impl<F, L, Input> Chain<L, Input> for F
    where F: Coroutine<Input, Return = Input>,
          L: Coroutine<Input>
{
}
<commit_msg>Relax constraints on Chain and add tests<commit_after>use co::{Coroutine, CoResult};

pub enum CoChain<F, L> {
    Former(F, L),
    Latter(L),
}

impl<F, L, Input> Coroutine<Input> for CoChain<F, L>
    where F: Coroutine<Input>,
          L: Coroutine<Input>,
          Input: From<<F as Coroutine<Input>>::Return>,
          <F as Coroutine<Input>>::Yield: From<<L as Coroutine<Input>>::Yield>
{
    type Yield = <F as Coroutine<Input>>::Yield;
    type Return = <L as Coroutine<Input>>::Return;

    fn next(self, i: Input) -> CoResult<Self::Yield, Self, Self::Return> {
        match self {
            CoChain::Former(fmr, ltr) => {
                match fmr.next(i) {
                    CoResult::Yield(y, fmr) => CoResult::Yield(y, fmr.chain(ltr)),
                    CoResult::Return(retf) => CoChain::Latter(ltr).next(retf.into()),
                }
            }
            CoChain::Latter(ltr) => {
                match ltr.next(i) {
                    CoResult::Yield(y, ltr) => CoResult::Yield(y.into(), CoChain::Latter(ltr)),
                    CoResult::Return(retl) => CoResult::Return(retl),
                }
            }
        }
    }
}

pub trait Chain<L, Input> where Self: Sized
{
    fn chain(self, l: L) -> CoChain<Self, L> {
        CoChain::Former(self, l)
    }
}

impl<F, L, Input> Chain<L, Input> for F
    where F: Sized + Coroutine<Input>,
          L: Coroutine<Input>,
          Input: From<<F as Coroutine<Input>>::Return>,
{
}

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

    struct Counter<T> {
        i: T,
        lim: T,
    }

    impl Coroutine<()> for Counter<i64> {
        type Yield = i64;
        type Return = ();

        fn next(self, _: ()) -> CoResult<Self::Yield, Self, Self::Return> {
            if self.i < self.lim {
                CoResult::Yield(self.i,
                                Counter {
                                    i: self.i + 1,
                                    lim: self.lim,
                                })
            } else {
                CoResult::Return(())
            }
        }
    }

    #[test]
    fn chain_integers() {
        let first = Counter::<i64> { i: 1, lim: 9 };
        let second = Counter::<i64> { i: 1, lim: 3 };

        let chain = first.chain(second);
        let msg = "This is the end";
        let mut elem = 1;
        let message = each!(chain => i in {
            assert_eq!(i, elem);
            elem = if elem == 8 { 1 } else { elem + 1 };
        } then {
            msg
        });
        assert_eq!(elem, 3);
        assert_eq!(message, msg);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;
use std::sync::Arc;
use style::computed_values::display;
use style::dom::TRestyleDamage;
use style::properties::ServoComputedValues;

bitflags! {
    #[doc = "Individual layout actions that may be necessary after restyling."]
    pub flags RestyleDamage: u8 {
        #[doc = "Repaint the node itself."]
        #[doc = "Currently unused; need to decide how this propagates."]
        const REPAINT = 0x01,

        #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
        #[doc = "Propagates down the flow tree because the computation is bottom-up."]
        const STORE_OVERFLOW = 0x02,

        #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
        #[doc = "Propagates down the flow tree because the computation is"]
        #[doc = "bottom-up."]
        const BUBBLE_ISIZES = 0x04,

        #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
                 into account. \
                 Propagates up the flow tree because the computation is top-down."]
        const REFLOW_OUT_OF_FLOW = 0x08,

        #[doc = "Recompute actual inline_sizes and block_sizes."]
        #[doc = "Propagates up the flow tree because the computation is"]
        #[doc = "top-down."]
        const REFLOW = 0x10,

        #[doc = "Re-resolve generated content. \
                 Propagates up the flow tree because the computation is inorder."]
        const RESOLVE_GENERATED_CONTENT = 0x20,

        #[doc = "The entire flow needs to be reconstructed."]
        const RECONSTRUCT_FLOW = 0x40
    }
}

impl TRestyleDamage for RestyleDamage {
    /// For Servo the style source is always the computed values.
    type PreExistingComputedValues = Arc<ServoComputedValues>;

    fn empty() -> Self {
        RestyleDamage::empty()
    }

    fn compute(old: &Arc<ServoComputedValues>,
               new: &Arc<ServoComputedValues>) -> RestyleDamage {
        compute_damage(old, new)
    }

    /// Returns a bitmask that represents a flow that needs to be rebuilt and
    /// reflowed.
    ///
    /// Use this instead of `RestyleDamage::all()` because
    /// `RestyleDamage::all()` will result in unnecessary sequential resolution
    /// of generated content.
    fn rebuild_and_reflow() -> RestyleDamage {
        REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
    }
}

impl RestyleDamage {
    /// Supposing a flow has the given `position` property and this damage,
    /// returns the damage that we should add to the *parent* of this flow.
    pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
        if child_is_absolutely_positioned {
            self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT)
        } else {
            self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
                    RESOLVE_GENERATED_CONTENT)
        }
    }

    /// Supposing the *parent* of a flow with the given `position` property has this damage,
    /// returns the damage that we should add to this flow.
    pub fn damage_for_child(self,
                            parent_is_absolutely_positioned: bool,
                            child_is_absolutely_positioned: bool)
                            -> RestyleDamage {
        match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
            (false, true) => {
                // Absolute children are out-of-flow and therefore insulated from changes.
                //
                // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
                self & REPAINT
            }
            (true, false) => {
                // Changing the position of an absolutely-positioned block requires us to reflow
                // its kids.
                if self.contains(REFLOW_OUT_OF_FLOW) {
                    self | REFLOW
                } else {
                    self
                }
            }
            _ => {
                // TODO(pcwalton): Take floatedness into account.
                self & (REPAINT | REFLOW)
            }
        }
    }
}

impl fmt::Display for RestyleDamage {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut first_elem = true;

        let to_iter =
            [ (REPAINT, "Repaint")
            , (STORE_OVERFLOW, "StoreOverflow")
            , (BUBBLE_ISIZES, "BubbleISizes")
            , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
            , (REFLOW, "Reflow")
            , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
            , (RECONSTRUCT_FLOW, "ReconstructFlow")
            ];

        for &(damage, damage_str) in &to_iter {
            if self.contains(damage) {
                if !first_elem { try!(write!(f, " | ")); }
                try!(write!(f, "{}", damage_str));
                first_elem = false;
            }
        }

        if first_elem {
            try!(write!(f, "NoDamage"));
        }

        Ok(())
    }
}

// NB: We need the braces inside the RHS due to Rust #8012.  This particular
// version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications.
macro_rules! add_if_not_equal(
    ($old:ident, $new:ident, $damage:ident,
     [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
        if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
            $damage.insert($($effect)|*);
            true
        } else {
            false
        }
    })
);

fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> RestyleDamage {
    let mut damage = RestyleDamage::empty();

    // This should check every CSS property, as enumerated in the fields of
    // http://doc.servo.org/style/properties/struct.ServoComputedValues.html

    // FIXME: Test somehow that every property is included.

    add_if_not_equal!(old, new, damage,
                      [
                          REPAINT,
                          STORE_OVERFLOW,
                          BUBBLE_ISIZES,
                          REFLOW_OUT_OF_FLOW,
                          REFLOW,
                          RECONSTRUCT_FLOW
                      ], [
        get_box.float, get_box.display, get_box.position, get_counters.content,
        get_counters.counter_reset, get_counters.counter_increment,
        get_inheritedbox._servo_under_display_none,
        get_list.quotes, get_list.list_style_type,

        // If these text or font properties change, we need to reconstruct the flow so that
        // text shaping is re-run.
        get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
        get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
        get_inheritedtext.overflow_wrap, get_inheritedtext.text_justify,
        get_inheritedtext.white_space, get_inheritedtext.word_break, get_text.text_overflow,
        get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
        get_font.font_size, get_font.font_stretch,
        get_inheritedbox.direction, get_inheritedbox.writing_mode,
        get_inheritedbox.text_orientation,
        get_text.text_decoration, get_text.unicode_bidi,
        get_inheritedtable.empty_cells, get_inheritedtable.caption_side,
        get_column.column_width, get_column.column_count
    ]) || (new.get_box().display == display::T::inline &&
           add_if_not_equal!(old, new, damage,
                             [REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW,
                              RECONSTRUCT_FLOW], [
        // For inline boxes only, border/padding styles are used in flow construction (to decide
        // whether to create fragments for empty flows).
        get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left
    ])) || add_if_not_equal!(old, new, damage,
                            [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ],
        [get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_margin.margin_top, get_margin.margin_right,
        get_margin.margin_bottom, get_margin.margin_left,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left,
        get_position.width, get_position.height,
        get_inheritedtext.line_height,
        get_inheritedtext.text_align, get_inheritedtext.text_indent,
        get_table.table_layout,
        get_inheritedtable.border_collapse,
        get_inheritedtable.border_spacing,
        get_column.column_gap,
        get_position.flex_direction,
        get_position.flex_wrap,
        get_position.justify_content,
        get_position.align_items,
        get_position.align_content,
        get_position.order,
        get_position.flex_basis,
        get_position.flex_grow,
        get_position.flex_shrink,
        get_position.align_self
    ]) || add_if_not_equal!(old, new, damage,
                            [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [
        get_position.top, get_position.left,
        get_position.right, get_position.bottom
    ]) || add_if_not_equal!(old, new, damage,
                            [ REPAINT ], [
        get_color.color, get_background.background_color,
        get_background.background_image, get_background.background_position,
        get_background.background_repeat, get_background.background_attachment,
        get_background.background_clip, get_background.background_origin,
        get_background.background_size,
        get_border.border_top_color, get_border.border_right_color,
        get_border.border_bottom_color, get_border.border_left_color,
        get_border.border_top_style, get_border.border_right_style,
        get_border.border_bottom_style, get_border.border_left_style,
        get_border.border_top_left_radius, get_border.border_top_right_radius,
        get_border.border_bottom_left_radius, get_border.border_bottom_right_radius,
        get_position.z_index, get_box._servo_overflow_clip_box,
        get_inheritedtext._servo_text_decorations_in_effect,
        get_pointing.cursor, get_pointing.pointer_events,
        get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter,
        get_effects.transform, get_effects.backface_visibility, get_effects.transform_style,
        get_effects.transform_origin, get_effects.perspective, get_effects.perspective_origin,
        get_effects.mix_blend_mode, get_inheritedbox.image_rendering,

        // Note: May require REFLOW et al. if `visibility: collapse` is implemented.
        get_inheritedbox.visibility
    ]);

    // If the layer requirements of this flow have changed due to the value
    // of the transform, then reflow is required to rebuild the layers.
    if old.transform_requires_layer() != new.transform_requires_layer() {
        damage.insert(RestyleDamage::rebuild_and_reflow());
    }

    damage
}
<commit_msg>calculate damage for opacity change<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;
use std::sync::Arc;
use style::computed_values::display;
use style::dom::TRestyleDamage;
use style::properties::ServoComputedValues;

bitflags! {
    #[doc = "Individual layout actions that may be necessary after restyling."]
    pub flags RestyleDamage: u8 {
        #[doc = "Repaint the node itself."]
        #[doc = "Currently unused; need to decide how this propagates."]
        const REPAINT = 0x01,

        #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
        #[doc = "Propagates down the flow tree because the computation is bottom-up."]
        const STORE_OVERFLOW = 0x02,

        #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
        #[doc = "Propagates down the flow tree because the computation is"]
        #[doc = "bottom-up."]
        const BUBBLE_ISIZES = 0x04,

        #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
                 into account. \
                 Propagates up the flow tree because the computation is top-down."]
        const REFLOW_OUT_OF_FLOW = 0x08,

        #[doc = "Recompute actual inline_sizes and block_sizes."]
        #[doc = "Propagates up the flow tree because the computation is"]
        #[doc = "top-down."]
        const REFLOW = 0x10,

        #[doc = "Re-resolve generated content. \
                 Propagates up the flow tree because the computation is inorder."]
        const RESOLVE_GENERATED_CONTENT = 0x20,

        #[doc = "The entire flow needs to be reconstructed."]
        const RECONSTRUCT_FLOW = 0x40
    }
}

impl TRestyleDamage for RestyleDamage {
    /// For Servo the style source is always the computed values.
    type PreExistingComputedValues = Arc<ServoComputedValues>;

    fn empty() -> Self {
        RestyleDamage::empty()
    }

    fn compute(old: &Arc<ServoComputedValues>,
               new: &Arc<ServoComputedValues>) -> RestyleDamage {
        compute_damage(old, new)
    }

    /// Returns a bitmask that represents a flow that needs to be rebuilt and
    /// reflowed.
    ///
    /// Use this instead of `RestyleDamage::all()` because
    /// `RestyleDamage::all()` will result in unnecessary sequential resolution
    /// of generated content.
    fn rebuild_and_reflow() -> RestyleDamage {
        REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
    }
}

impl RestyleDamage {
    /// Supposing a flow has the given `position` property and this damage,
    /// returns the damage that we should add to the *parent* of this flow.
    pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
        if child_is_absolutely_positioned {
            self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT)
        } else {
            self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
                    RESOLVE_GENERATED_CONTENT)
        }
    }

    /// Supposing the *parent* of a flow with the given `position` property has this damage,
    /// returns the damage that we should add to this flow.
    pub fn damage_for_child(self,
                            parent_is_absolutely_positioned: bool,
                            child_is_absolutely_positioned: bool)
                            -> RestyleDamage {
        match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
            (false, true) => {
                // Absolute children are out-of-flow and therefore insulated from changes.
                //
                // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
                self & REPAINT
            }
            (true, false) => {
                // Changing the position of an absolutely-positioned block requires us to reflow
                // its kids.
                if self.contains(REFLOW_OUT_OF_FLOW) {
                    self | REFLOW
                } else {
                    self
                }
            }
            _ => {
                // TODO(pcwalton): Take floatedness into account.
                self & (REPAINT | REFLOW)
            }
        }
    }
}

impl fmt::Display for RestyleDamage {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut first_elem = true;

        let to_iter =
            [ (REPAINT, "Repaint")
            , (STORE_OVERFLOW, "StoreOverflow")
            , (BUBBLE_ISIZES, "BubbleISizes")
            , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
            , (REFLOW, "Reflow")
            , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
            , (RECONSTRUCT_FLOW, "ReconstructFlow")
            ];

        for &(damage, damage_str) in &to_iter {
            if self.contains(damage) {
                if !first_elem { try!(write!(f, " | ")); }
                try!(write!(f, "{}", damage_str));
                first_elem = false;
            }
        }

        if first_elem {
            try!(write!(f, "NoDamage"));
        }

        Ok(())
    }
}

// NB: We need the braces inside the RHS due to Rust #8012.  This particular
// version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications.
macro_rules! add_if_not_equal(
    ($old:ident, $new:ident, $damage:ident,
     [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
        if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
            $damage.insert($($effect)|*);
            true
        } else {
            false
        }
    })
);

fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> RestyleDamage {
    let mut damage = RestyleDamage::empty();

    // This should check every CSS property, as enumerated in the fields of
    // http://doc.servo.org/style/properties/struct.ServoComputedValues.html

    // FIXME: Test somehow that every property is included.

    add_if_not_equal!(old, new, damage,
                      [
                          REPAINT,
                          STORE_OVERFLOW,
                          BUBBLE_ISIZES,
                          REFLOW_OUT_OF_FLOW,
                          REFLOW,
                          RECONSTRUCT_FLOW
                      ], [
        get_box.float, get_box.display, get_box.position, get_counters.content,
        get_counters.counter_reset, get_counters.counter_increment,
        get_inheritedbox._servo_under_display_none,
        get_list.quotes, get_list.list_style_type,

        // If these text or font properties change, we need to reconstruct the flow so that
        // text shaping is re-run.
        get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
        get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
        get_inheritedtext.overflow_wrap, get_inheritedtext.text_justify,
        get_inheritedtext.white_space, get_inheritedtext.word_break, get_text.text_overflow,
        get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
        get_font.font_size, get_font.font_stretch,
        get_inheritedbox.direction, get_inheritedbox.writing_mode,
        get_inheritedbox.text_orientation,
        get_text.text_decoration, get_text.unicode_bidi,
        get_inheritedtable.empty_cells, get_inheritedtable.caption_side,
        get_column.column_width, get_column.column_count
    ]) || (new.get_box().display == display::T::inline &&
           add_if_not_equal!(old, new, damage,
                             [REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW,
                              RECONSTRUCT_FLOW], [
        // For inline boxes only, border/padding styles are used in flow construction (to decide
        // whether to create fragments for empty flows).
        get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left
    ])) || add_if_not_equal!(old, new, damage,
                            [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ],
        [get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_margin.margin_top, get_margin.margin_right,
        get_margin.margin_bottom, get_margin.margin_left,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left,
        get_position.width, get_position.height,
        get_inheritedtext.line_height,
        get_inheritedtext.text_align, get_inheritedtext.text_indent,
        get_table.table_layout,
        get_inheritedtable.border_collapse,
        get_inheritedtable.border_spacing,
        get_column.column_gap,
        get_position.flex_direction,
        get_position.flex_wrap,
        get_position.justify_content,
        get_position.align_items,
        get_position.align_content,
        get_position.order,
        get_position.flex_basis,
        get_position.flex_grow,
        get_position.flex_shrink,
        get_position.align_self
    ]) || add_if_not_equal!(old, new, damage,
                            [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [
        get_position.top, get_position.left,
        get_position.right, get_position.bottom
    ]) || add_if_not_equal!(old, new, damage,
                            [ REPAINT ], [
        get_color.color, get_background.background_color,
        get_background.background_image, get_background.background_position,
        get_background.background_repeat, get_background.background_attachment,
        get_background.background_clip, get_background.background_origin,
        get_background.background_size,
        get_border.border_top_color, get_border.border_right_color,
        get_border.border_bottom_color, get_border.border_left_color,
        get_border.border_top_style, get_border.border_right_style,
        get_border.border_bottom_style, get_border.border_left_style,
        get_border.border_top_left_radius, get_border.border_top_right_radius,
        get_border.border_bottom_left_radius, get_border.border_bottom_right_radius,
        get_position.z_index, get_box._servo_overflow_clip_box,
        get_inheritedtext._servo_text_decorations_in_effect,
        get_pointing.cursor, get_pointing.pointer_events,
        get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter,
        get_effects.transform, get_effects.backface_visibility, get_effects.transform_style,
        get_effects.transform_origin, get_effects.perspective, get_effects.perspective_origin,
        get_effects.mix_blend_mode, get_effects.opacity, get_inheritedbox.image_rendering,

        // Note: May require REFLOW et al. if `visibility: collapse` is implemented.
        get_inheritedbox.visibility
    ]);

    // If the layer requirements of this flow have changed due to the value
    // of the transform, then reflow is required to rebuild the layers.
    if old.transform_requires_layer() != new.transform_requires_layer() {
        damage.insert(RestyleDamage::rebuild_and_reflow());
    }

    damage
}
<|endoftext|>


<commit_before>use queen_attack::*;

#[test]
fn chess_position_on_the_board_is_some() {
    assert!(ChessPosition::new(2, 4).is_some());
}

#[test]
#[ignore]
fn chess_position_off_the_board_is_none() {
    assert!(ChessPosition::new(-1, 2).is_none());

    assert!(ChessPosition::new(8, 2).is_none());

    assert!(ChessPosition::new(5, -1).is_none());

    assert!(ChessPosition::new(5, 8).is_none());
}

#[test]
#[ignore]
fn queen_is_created_with_a_valid_position() {
    Queen::new(ChessPosition::new(2, 4).unwrap());
}

#[test]
#[ignore]
fn queens_that_can_not_attack() {
    let white_queen = Queen::new(ChessPosition::new(2, 4).unwrap());
    let black_queen = Queen::new(ChessPosition::new(6, 6).unwrap());

    assert!(!white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_rank_can_attack() {
    let white_queen = Queen::new(ChessPosition::new(2, 4).unwrap());
    let black_queen = Queen::new(ChessPosition::new(2, 6).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_file_can_attack() {
    let white_queen = Queen::new(ChessPosition::new(4, 5).unwrap());
    let black_queen = Queen::new(ChessPosition::new(3, 5).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_one() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(0, 4).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_two() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(3, 1).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_three() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(1, 1).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_four() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(5, 5).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}
<commit_msg>queen-attack: Add test case to catch an incomplete diagonal check (#1398)<commit_after>use queen_attack::*;

#[test]
fn chess_position_on_the_board_is_some() {
    assert!(ChessPosition::new(2, 4).is_some());
}

#[test]
#[ignore]
fn chess_position_off_the_board_is_none() {
    assert!(ChessPosition::new(-1, 2).is_none());

    assert!(ChessPosition::new(8, 2).is_none());

    assert!(ChessPosition::new(5, -1).is_none());

    assert!(ChessPosition::new(5, 8).is_none());
}

#[test]
#[ignore]
fn queen_is_created_with_a_valid_position() {
    Queen::new(ChessPosition::new(2, 4).unwrap());
}

#[test]
#[ignore]
fn queens_that_can_not_attack() {
    let white_queen = Queen::new(ChessPosition::new(2, 4).unwrap());
    let black_queen = Queen::new(ChessPosition::new(6, 6).unwrap());

    assert!(!white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_rank_can_attack() {
    let white_queen = Queen::new(ChessPosition::new(2, 4).unwrap());
    let black_queen = Queen::new(ChessPosition::new(2, 6).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_file_can_attack() {
    let white_queen = Queen::new(ChessPosition::new(4, 5).unwrap());
    let black_queen = Queen::new(ChessPosition::new(3, 5).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_one() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(0, 4).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_two() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(3, 1).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_three() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(1, 1).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_on_the_same_diagonal_can_attack_four() {
    let white_queen = Queen::new(ChessPosition::new(2, 2).unwrap());
    let black_queen = Queen::new(ChessPosition::new(5, 5).unwrap());

    assert!(white_queen.can_attack(&black_queen));
}

#[test]
#[ignore]
fn queens_that_cannot_attack_with_equal_difference() {
    let white_queen = Queen::new(ChessPosition::new(4, 1).unwrap());
    let black_queen = Queen::new(ChessPosition::new(2, 5).unwrap());

    assert!(!white_queen.can_attack(&black_queen));
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{ BufReader, BufWriter, Read, Write };
use std::path::Path;
use toml::{ to_string as toml_to_string, Value };

use error::PackageError;
use installation::PackageState;
use utils::Hash;

#[derive(Serialize, Deserialize)]
pub struct Cache(Vec<(Hash, PackageState)>);

impl Cache {
    fn read_from<P: AsRef<Path>>(path: P) -> Result<Cache, PackageError> {
        let mut file = File::open(path)?;
        let mut file = BufReader::new(file);
        let mut content = String::new();
        file.read_to_string(&mut content);
        let content = content.trim().parse::<Value>()?;
        Ok(content.try_into::<Self>()?)
    }

    fn write_to<P: AsRef<Path>>(&self, path: P) -> Result<(), PackageError> {
        let mut file = File::open(path)?;
        let mut file = BufWriter::new(file);
        let content = toml_to_string(self)?;
        Ok(file.write_all(content.as_bytes())?)
    }
}<commit_msg>Getting rid of warning<commit_after>use std::fs::File;
use std::io::{ BufReader, BufWriter, Read, Write };
use std::path::Path;
use toml::{ to_string as toml_to_string, Value };

use error::PackageError;
use installation::PackageState;
use utils::Hash;

#[derive(Serialize, Deserialize)]
pub struct Cache(Vec<(Hash, PackageState)>);

impl Cache {
    fn read_from<P: AsRef<Path>>(path: P) -> Result<Cache, PackageError> {
        let file = File::open(path)?;
        let mut file = BufReader::new(file);
        let mut content = String::new();
        file.read_to_string(&mut content);
        let content = content.trim().parse::<Value>()?;
        Ok(content.try_into::<Self>()?)
    }

    fn write_to<P: AsRef<Path>>(&self, path: P) -> Result<(), PackageError> {
        let file = File::open(path)?;
        let mut file = BufWriter::new(file);
        let content = toml_to_string(self)?;
        Ok(file.write_all(content.as_bytes())?)
    }
}<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map;
use std::rc::Rc;

use render::Renderer;
use routing::{DagHandle, Edge, Effect, GraphWatcher, NodeData, NodeHandle};

#[derive(Default)]
pub struct RefRenderer {
    nodes: HashMap<NodeHandle, Node>,
}

struct Node {
    data: MyNodeData,
    inbound: HashSet<Edge>
}

enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(Rc<Effect>),
    /// This node is an instance of another DAG.
    Graph(DagHandle),
    /// Primitive Delay(samples) effect
    Delay(u64),
    /// Primitive Constant(value) effect.
    /// Also serves as a unit step;
    /// Returns the f32 value for t >= 0, else 0.
    Constant(f32),
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// This node is a DAG definition. i.e. it holds the output edges of a DAG.
    DagIO,
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, ch: u8) -> f32 {
        // Try to find the edge that goes to -> (Null, slot=0, ch=ch)
        let root_handle = NodeHandle::toplevel();
        match self.nodes.get(&root_handle) {
            // for an empty graph, yield silence.
            None => 0f32,
            Some(node) => {
                // find all edges to ([Null], slot=0, ch=ch)
                self.sum_input_to_slot(node, time, 0, ch, &Vec::new())
            }
        }
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, edge: &Edge, time: u64, context: &Vec<NodeHandle>) -> f32 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            let mut new_context = context.clone();
            let head = new_context.pop().unwrap();
            // Sum the inputs to the matching slot/ch
            let in_edges = self.nodes[&head].inbound.iter().filter(|in_edge| {
                in_edge.to_slot() == edge.from_slot() && in_edge.to_ch() == edge.from_ch()
            });
            let in_values = in_edges.map(|in_edge| {
                self.get_value(in_edge, time, &new_context)
            });
            in_values.sum()
        } else {
            // Reading from another node within the DAG
            let node = &self.nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref _effect) => unimplemented!(),
                // Output = sum of all edges to Null of the same slot & ch, within the given DAG.
                MyNodeData::Graph(ref dag_handle) => {
                    let mut new_context = context.clone();
                    new_context.push(from);
                    let subdag = &self.nodes[&NodeHandle::new(dag_handle.clone(), None)];
                    let out_edges = subdag.inbound.iter().filter(|out_edge| {
                        out_edge.to_slot() == edge.from_slot() && out_edge.to_ch() == edge.from_ch()
                    });
                    let out_values = out_edges.map(|out_edge| {
                        self.get_value(out_edge, time, &new_context)
                    });
                    out_values.sum()
                }
                // Output = sum of all inputs to slot 0 of the same ch.
                MyNodeData::Delay(ref frames) => {
                    // The only nonzero output is slot=1.
                    if edge.from_slot() != 1 {
                        println!("Warning: attempt to read from Delay slot != 1");
                        0f32
                    } else {
                        match time.checked_sub(*frames) {
                            // t < 0 => no audio
                            None => 0f32,
                            Some(origin_time) => self.sum_input_to_slot(node, origin_time, 0, edge.from_ch(), context)
                        }
                    }
                },
                MyNodeData::Constant(ref value) => value.clone(),
                MyNodeData::Multiply => {
                    // The only nonzero output is slot=1.
                    if edge.from_slot() != 1 {
                        println!("Warning: attempt to read from Multiply slot != 1");
                        0f32
                    } else {
                        // Sum all inputs from slot=0 and slot=2 into two separate
                        // variables, then multiply them.
                        let val_a = self.sum_input_to_slot(node, time, 0, edge.from_ch(), context);
                        let val_b = self.sum_input_to_slot(node, time, 2, edge.from_ch(), context);
                        val_a * val_b
                    }
                },
                _ => panic!("Internal RefRenderer error: illegal node type"),
            }
        }
    }
    /// Return the sum of all inputs into a specific slot/channel of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, node: &Node, time: u64, slot: u32, ch: u8, context: &Vec<NodeHandle>) -> f32 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot && in_edge.to_ch() == ch
        });
        edges_in.map(|edge| self.get_value(edge, time, context)).sum()
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = match *data {
            NodeData::Graph(ref handle) => MyNodeData::Graph(handle.clone()),
            NodeData::Effect(ref effect) => {
                match effect.meta().get_primitive_url() {
                    Some(ref url) => {
                        let mut params: HashMap<_, _> = url.query_pairs().collect();
                        match url.path() {
                            "/Delay" => {
                                let frames: u64 = match params.entry(Cow::from("frames")) {
                                    hash_map::Entry::Occupied(e) => e.remove().parse().unwrap(),
                                    hash_map::Entry::Vacant(_) => 0u64,
                                };
                                // Make sure we consumed all arguments.
                                assert!(params.is_empty());
                                MyNodeData::Delay(frames)
                            },
                            "/Constant" => {
                                let value: f32 = match params.entry(Cow::from("value")) {
                                    hash_map::Entry::Occupied(e) => e.remove().parse().unwrap(),
                                    hash_map::Entry::Vacant(_) => 0f32,
                                };
                                // Make sure we consumed all arguments.
                                assert!(params.is_empty());
                                MyNodeData::Constant(value)
                            },
                            "/Multiply" => MyNodeData::Multiply,
                            _ => panic!("Unrecognized primitive effect: {} (full url: {})", url.path(), url),
                        }
                    }
                    None => MyNodeData::UserNode(effect.clone())
                }
            }
        };
        self.nodes.insert(handle.clone(), Node::new(my_node_data));
        // If the node is part of a new DAG, allocate data so that future edges
        // to null within the DAG can be held.
        self.nodes.entry(NodeHandle::new_dag(handle.dag_handle().clone())).or_insert_with(|| {
            Node::new(MyNodeData::DagIO)
        });
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}
<commit_msg>Constant should only produce output on slot=1<commit_after>use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map;
use std::rc::Rc;

use render::Renderer;
use routing::{DagHandle, Edge, Effect, GraphWatcher, NodeData, NodeHandle};

#[derive(Default)]
pub struct RefRenderer {
    nodes: HashMap<NodeHandle, Node>,
}

struct Node {
    data: MyNodeData,
    inbound: HashSet<Edge>
}

enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(Rc<Effect>),
    /// This node is an instance of another DAG.
    Graph(DagHandle),
    /// Primitive Delay(samples) effect
    Delay(u64),
    /// Primitive Constant(value) effect.
    /// Also serves as a unit step;
    /// Returns the f32 value for t >= 0, else 0.
    Constant(f32),
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// This node is a DAG definition. i.e. it holds the output edges of a DAG.
    DagIO,
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, ch: u8) -> f32 {
        // Try to find the edge that goes to -> (Null, slot=0, ch=ch)
        let root_handle = NodeHandle::toplevel();
        match self.nodes.get(&root_handle) {
            // for an empty graph, yield silence.
            None => 0f32,
            Some(node) => {
                // find all edges to ([Null], slot=0, ch=ch)
                self.sum_input_to_slot(node, time, 0, ch, &Vec::new())
            }
        }
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, edge: &Edge, time: u64, context: &Vec<NodeHandle>) -> f32 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            let mut new_context = context.clone();
            let head = new_context.pop().unwrap();
            // Sum the inputs to the matching slot/ch
            let in_edges = self.nodes[&head].inbound.iter().filter(|in_edge| {
                in_edge.to_slot() == edge.from_slot() && in_edge.to_ch() == edge.from_ch()
            });
            let in_values = in_edges.map(|in_edge| {
                self.get_value(in_edge, time, &new_context)
            });
            in_values.sum()
        } else {
            // Reading from another node within the DAG
            let node = &self.nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref _effect) => unimplemented!(),
                // Output = sum of all edges to Null of the same slot & ch, within the given DAG.
                MyNodeData::Graph(ref dag_handle) => {
                    let mut new_context = context.clone();
                    new_context.push(from);
                    let subdag = &self.nodes[&NodeHandle::new(dag_handle.clone(), None)];
                    let out_edges = subdag.inbound.iter().filter(|out_edge| {
                        out_edge.to_slot() == edge.from_slot() && out_edge.to_ch() == edge.from_ch()
                    });
                    let out_values = out_edges.map(|out_edge| {
                        self.get_value(out_edge, time, &new_context)
                    });
                    out_values.sum()
                }
                // Output = sum of all inputs to slot 0 of the same ch.
                MyNodeData::Delay(ref frames) => {
                    // The only nonzero output is slot=1.
                    if edge.from_slot() != 1 {
                        println!("Warning: attempt to read from Delay slot != 1");
                        0f32
                    } else {
                        match time.checked_sub(*frames) {
                            // t < 0 => no audio
                            None => 0f32,
                            Some(origin_time) => self.sum_input_to_slot(node, origin_time, 0, edge.from_ch(), context)
                        }
                    }
                },
                MyNodeData::Constant(ref value) => {
                    // The only nonzero output is slot=1.
                    if edge.from_slot() != 1 {
                        println!("Warning: attempt to read from Constant slot != 1");
                        0f32
                    } else {
                        *value
                    }
                },
                MyNodeData::Multiply => {
                    // The only nonzero output is slot=1.
                    if edge.from_slot() != 1 {
                        println!("Warning: attempt to read from Multiply slot != 1");
                        0f32
                    } else {
                        // Sum all inputs from slot=0 and slot=2 into two separate
                        // variables, then multiply them.
                        let val_a = self.sum_input_to_slot(node, time, 0, edge.from_ch(), context);
                        let val_b = self.sum_input_to_slot(node, time, 2, edge.from_ch(), context);
                        val_a * val_b
                    }
                },
                _ => panic!("Internal RefRenderer error: illegal node type"),
            }
        }
    }
    /// Return the sum of all inputs into a specific slot/channel of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, node: &Node, time: u64, slot: u32, ch: u8, context: &Vec<NodeHandle>) -> f32 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot && in_edge.to_ch() == ch
        });
        edges_in.map(|edge| self.get_value(edge, time, context)).sum()
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = match *data {
            NodeData::Graph(ref handle) => MyNodeData::Graph(handle.clone()),
            NodeData::Effect(ref effect) => {
                match effect.meta().get_primitive_url() {
                    Some(ref url) => {
                        let mut params: HashMap<_, _> = url.query_pairs().collect();
                        match url.path() {
                            "/Delay" => {
                                let frames: u64 = match params.entry(Cow::from("frames")) {
                                    hash_map::Entry::Occupied(e) => e.remove().parse().unwrap(),
                                    hash_map::Entry::Vacant(_) => 0u64,
                                };
                                // Make sure we consumed all arguments.
                                assert!(params.is_empty());
                                MyNodeData::Delay(frames)
                            },
                            "/Constant" => {
                                let value: f32 = match params.entry(Cow::from("value")) {
                                    hash_map::Entry::Occupied(e) => e.remove().parse().unwrap(),
                                    hash_map::Entry::Vacant(_) => 0f32,
                                };
                                // Make sure we consumed all arguments.
                                assert!(params.is_empty());
                                MyNodeData::Constant(value)
                            },
                            "/Multiply" => MyNodeData::Multiply,
                            _ => panic!("Unrecognized primitive effect: {} (full url: {})", url.path(), url),
                        }
                    }
                    None => MyNodeData::UserNode(effect.clone())
                }
            }
        };
        self.nodes.insert(handle.clone(), Node::new(my_node_data));
        // If the node is part of a new DAG, allocate data so that future edges
        // to null within the DAG can be held.
        self.nodes.entry(NodeHandle::new_dag(handle.dag_handle().clone())).or_insert_with(|| {
            Node::new(MyNodeData::DagIO)
        });
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}
<|endoftext|>


<commit_before>use llvm_sys::prelude::*;
use llvm_sys::core as llvm;

pub struct Function {
    pub ptr: LLVMValueRef,
}

impl Function {
    pub fn from_value_ref(p: LLVMValueRef) -> Function {
        Function {
            ptr: p
        }
    }

    pub fn params(&self) -> FunctionParamIter {
        FunctionParamIter {
            arg: self.ptr,
            first: true,
        }
    }

    // TODO: Check if there is an optimization so that we could
    // call func.params().nth(1) and call this function
    pub fn get_param(&self, index: u32) -> Option<LLVMValueRef> {
        let p = unsafe {
            llvm::LLVMGetParam(self.ptr, index)
        };

        if p.is_null() {
            return None;
        } else  {
            return Some(p);
        }
    }

}

pub struct FunctionParamIter {
    arg: LLVMValueRef,
    first: bool,
}

// TODO: Needs testing
impl Iterator for FunctionParamIter {
    type Item = LLVMValueRef;

    fn next(&mut self) -> Option<LLVMValueRef> {
        let res = if self.first {
            unsafe { llvm::LLVMGetFirstParam(self.arg) }
        } else {
            unsafe { llvm::LLVMGetNextParam(self.arg) }
        };

        if res.is_null() {
            None
        } else {
            Some(res)
        }
    }
}
<commit_msg>Function param iterator: update internal pointer<commit_after>use llvm_sys::prelude::*;
use llvm_sys::core as llvm;

pub struct Function {
    pub ptr: LLVMValueRef,
}

impl Function {
    pub fn from_value_ref(p: LLVMValueRef) -> Function {
        Function {
            ptr: p
        }
    }

    pub fn params(&self) -> FunctionParamIter {
        FunctionParamIter {
            arg: self.ptr,
            first: true,
        }
    }

    // TODO: Check if there is an optimization so that we could
    // call func.params().nth(1) and call this function
    pub fn get_param(&self, index: u32) -> Option<LLVMValueRef> {
        let p = unsafe {
            llvm::LLVMGetParam(self.ptr, index)
        };

        if p.is_null() {
            return None;
        } else  {
            return Some(p);
        }
    }

}

pub struct FunctionParamIter {
    arg: LLVMValueRef,
    first: bool,
}

// TODO: Needs testing
impl Iterator for FunctionParamIter {
    type Item = LLVMValueRef;

    fn next(&mut self) -> Option<LLVMValueRef> {
        self.arg = if self.first {
            unsafe { llvm::LLVMGetFirstParam(self.arg) }
        } else {
            unsafe { llvm::LLVMGetNextParam(self.arg) }
        };

        if self.arg.is_null() {
            None
        } else {
            Some(self.arg)
        }
    }
}
<|endoftext|>


<commit_before>//!
//! The script interpreter runs scripts directly from their parse tree. This is
//! a fairly slow way to run scripts but comparatively simple to implement.
//!

use std::result::Result;
use serde_json::*;

use silkthread_base::*;

use super::script::*;

///
/// A tool representing a script that will be interepreted
///
pub struct InterpretedScriptTool {
    statements: Vec<Script>
}

///
/// Creates an unquoted version of a string
///
fn unquote_string(string: &str) -> String {
    let chars: Vec<char>    = string.chars().collect();
    let mut result          = String::new();
    let mut index           = 1;
    while index < chars.len()-1 {
        // Push character
        let chr = chars[index];

        match chr {
            '\\' => {
                let quoted = chars[index+1];
                match quoted {
                    'n' => result.push('\n'),
                    'r' => result.push('\r'),
                    't' => result.push('\t'),
                    quoted => result.push(quoted)
                }
            },
            chr => result.push(chr)
        }

        // Next character
        index += 1;
    }

    result
}

///
/// Script evaluation error
///
#[derive(Serialize, Deserialize)]
pub enum ScriptEvaluationError {
    /// Tried to evaluate an expression type that's not implemented yet
    ExpressionNotImplemented,

    /// Tried to look up a tool and it couldn't be found
    ToolNameNotFound,

    /// Found an expression that can't be treated as a tool where a tool name was expected
    ExpressionDoesNotEvaluateToTool
}

///
/// Creates an execution error
///
fn generate_expression_error(error: ScriptEvaluationError, expr: &Expression) -> Value {
    json![{
        "error":                error,
        "failed-expression":    expr
    }]
}

impl InterpretedScriptTool {
    ///
    /// Creates a new interpreted script tool from a set of statements
    ///
    pub fn from_statements(statements: Vec<Script>) -> InterpretedScriptTool {
        InterpretedScriptTool { statements: statements }
    }

    ///
    /// Attempts to evaluate an expression to a tool
    ///
    pub fn evaluate_expression_to_tool(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Box<Tool>, Value> {
        match expression {
            &Expression::Identifier(ref name)   => environment.parent_environment.get_json_tool(&name.matched).map_err(|_| generate_expression_error(ScriptEvaluationError::ToolNameNotFound, expression)),
            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionDoesNotEvaluateToTool, expression))
        }
    }

    ///
    /// Calls an expression representing a tool and calls it with the specified parameters
    ///
    pub fn call_tool(tool_name: &Expression, parameters: Value, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression_to_tool(tool_name, environment).and_then(|tool| {
            // TODO: create environment for the tool to run in

            // Invoke the tool and generate the final result
            tool.invoke_json(parameters, environment.parent_environment)
        })
    }

    ///
    /// Evaluates an 'apply' expression
    ///
    pub fn apply(&(ref tool, ref parameters): &(Expression, Expression), environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression(&parameters, environment).and_then(|parameters| {
            InterpretedScriptTool::call_tool(&tool, parameters, environment)
        })
    }

    ///
    /// Evaluates a single expression
    ///
    pub fn evaluate_expression(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        match expression {
            &Expression::String(ref s)          => Ok(Value::String(unquote_string(&s.matched))),

            &Expression::Identifier(_)          => InterpretedScriptTool::call_tool(expression, Value::Null, environment),
            &Expression::Apply(ref expr)        => InterpretedScriptTool::apply(&*expr, environment),

            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionNotImplemented, expression))
        }
    }

    ///
    /// Evaluates the result of executing a single statement
    ///
    pub fn evaluate_statement(statement: &Script, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        unimplemented!()
    }
}

impl Tool for InterpretedScriptTool {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        // Make the environment that this script will run in
        let mut script_environment = ScriptExecutionEnvironment::new(environment);

        // Execute the script
        let mut result = vec![];
        for statement in self.statements.iter() {
            // Evaluate the next statement
            let next_result = match Self::evaluate_statement(statement, &mut script_environment) {
                Ok(result) => result,

                // Fail immediately if any statement generates an error
                Err(fail) => return Err(fail)
            };

            // The script result is built up from the result of each statement
            // TODO: unless there's something like a return statement?
            result.push(next_result);
        }

        // Script is done
        Ok(Value::Array(result))
    }
}

///
/// Represents an execution environment for a running script
///
pub struct ScriptExecutionEnvironment<'a> {
    /// The environment where tools are drawn from
    parent_environment: &'a Environment
}

impl<'a> ScriptExecutionEnvironment<'a> {
    ///
    /// Creates a new script execution environment
    ///
    pub fn new(parent_environment: &'a Environment) -> ScriptExecutionEnvironment<'a> {
        ScriptExecutionEnvironment { parent_environment: parent_environment }
    }
}

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

    #[test]
    fn can_evaluate_string() {
        let string_expr         = Expression::string("\"Foo\"");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&string_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Foo"))));
    }

    #[test]
    fn can_call_tool() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::call_tool(&tool_expr, Value::Null, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_tool_call() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_apply_expression() {
        let tool_expr           = Expression::Apply(Box::new((Expression::identifier("test"), Expression::string("\"Success\""))));
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|s: String| s)));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }
}
<commit_msg>Add basic statement evaluation to the script interpreter<commit_after>//!
//! The script interpreter runs scripts directly from their parse tree. This is
//! a fairly slow way to run scripts but comparatively simple to implement.
//!

use std::result::Result;
use serde_json::*;

use silkthread_base::*;

use super::script::*;

///
/// A tool representing a script that will be interepreted
///
pub struct InterpretedScriptTool {
    statements: Vec<Script>
}

///
/// Creates an unquoted version of a string
///
fn unquote_string(string: &str) -> String {
    let chars: Vec<char>    = string.chars().collect();
    let mut result          = String::new();
    let mut index           = 1;
    while index < chars.len()-1 {
        // Push character
        let chr = chars[index];

        match chr {
            '\\' => {
                let quoted = chars[index+1];
                match quoted {
                    'n' => result.push('\n'),
                    'r' => result.push('\r'),
                    't' => result.push('\t'),
                    quoted => result.push(quoted)
                }
            },
            chr => result.push(chr)
        }

        // Next character
        index += 1;
    }

    result
}

///
/// Script evaluation error
///
#[derive(Serialize, Deserialize)]
pub enum ScriptEvaluationError {
    /// Tried to evaluate an expression type that's not implemented yet
    ExpressionNotImplemented,

    /// Tried to evaluate a statement type that's not implemented yet
    StatementNotImplemented,

    /// Tried to look up a tool and it couldn't be found
    ToolNameNotFound,

    /// Found an expression that can't be treated as a tool where a tool name was expected
    ExpressionDoesNotEvaluateToTool
}

///
/// Creates an execution error
///
fn generate_script_error(error: ScriptEvaluationError, script: &Script) -> Value {
    json![{
        "error":                error,
        "failed-statement":     script
    }]
}

///
/// Creates an execution error relating to an expression
///
fn generate_expression_error(error: ScriptEvaluationError, expr: &Expression) -> Value {
    json![{
        "error":                error,
        "failed-expression":    expr
    }]
}

impl InterpretedScriptTool {
    ///
    /// Creates a new interpreted script tool from a set of statements
    ///
    pub fn from_statements(statements: Vec<Script>) -> InterpretedScriptTool {
        InterpretedScriptTool { statements: statements }
    }

    ///
    /// Attempts to evaluate an expression to a tool
    ///
    pub fn evaluate_expression_to_tool(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Box<Tool>, Value> {
        match expression {
            &Expression::Identifier(ref name)   => environment.parent_environment.get_json_tool(&name.matched).map_err(|_| generate_expression_error(ScriptEvaluationError::ToolNameNotFound, expression)),
            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionDoesNotEvaluateToTool, expression))
        }
    }

    ///
    /// Calls an expression representing a tool and calls it with the specified parameters
    ///
    pub fn call_tool(tool_name: &Expression, parameters: Value, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression_to_tool(tool_name, environment).and_then(|tool| {
            // TODO: create environment for the tool to run in

            // Invoke the tool and generate the final result
            tool.invoke_json(parameters, environment.parent_environment)
        })
    }

    ///
    /// Evaluates an 'apply' expression
    ///
    pub fn apply(&(ref tool, ref parameters): &(Expression, Expression), environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression(&parameters, environment).and_then(|parameters| {
            InterpretedScriptTool::call_tool(&tool, parameters, environment)
        })
    }

    ///
    /// Evaluates a single expression
    ///
    pub fn evaluate_expression(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        match expression {
            &Expression::String(ref s)          => Ok(Value::String(unquote_string(&s.matched))),

            &Expression::Identifier(_)          => InterpretedScriptTool::call_tool(expression, Value::Null, environment),
            &Expression::Apply(ref expr)        => InterpretedScriptTool::apply(&*expr, environment),

            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionNotImplemented, expression))
        }
    }

    ///
    /// Evaluates the result of executing a single statement
    ///
    pub fn evaluate_statement(statement: &Script, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        match statement {
            &Script::RunCommand(ref expr)   => InterpretedScriptTool::evaluate_expression(expr, environment),

            _                               => Err(generate_script_error(ScriptEvaluationError::StatementNotImplemented, statement))
        }
    }
}

impl Tool for InterpretedScriptTool {
    fn invoke_json(&self, _input: Value, environment: &Environment) -> Result<Value, Value> {
        // Make the environment that this script will run in
        let mut script_environment = ScriptExecutionEnvironment::new(environment);

        // Execute the script
        let mut result = vec![];
        for statement in self.statements.iter() {
            // Evaluate the next statement
            let next_result = match Self::evaluate_statement(statement, &mut script_environment) {
                Ok(result) => result,

                // Fail immediately if any statement generates an error
                Err(fail) => return Err(fail)
            };

            // The script result is built up from the result of each statement
            // TODO: unless there's something like a return statement?
            result.push(next_result);
        }

        // Script is done
        Ok(Value::Array(result))
    }
}

///
/// Represents an execution environment for a running script
///
pub struct ScriptExecutionEnvironment<'a> {
    /// The environment where tools are drawn from
    parent_environment: &'a Environment
}

impl<'a> ScriptExecutionEnvironment<'a> {
    ///
    /// Creates a new script execution environment
    ///
    pub fn new(parent_environment: &'a Environment) -> ScriptExecutionEnvironment<'a> {
        ScriptExecutionEnvironment { parent_environment: parent_environment }
    }
}

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

    #[test]
    fn can_evaluate_string() {
        let string_expr         = Expression::string("\"Foo\"");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&string_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Foo"))));
    }

    #[test]
    fn can_call_tool() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::call_tool(&tool_expr, Value::Null, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_tool_call() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_execute_run_command() {
        let tool_expr           = Script::RunCommand(Expression::identifier("test"));
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_statement(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_apply_expression() {
        let tool_expr           = Expression::Apply(Box::new((Expression::identifier("test"), Expression::string("\"Success\""))));
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|s: String| s)));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }
}
<|endoftext|>