<commit_before>use std::io::{self, Read, Write};
use std::ops::{Add, AddAssign};
use std::str;
use std::thread;
use std::time::Duration;

use mio;
use mio::tcp::{TcpListener, TcpStream};

use cpu::BreakReason;
use dbgcore;
use hwcore::Message;
use msgs;
use utils;

error_chain! {
    foreign_links {
        Io(io::Error);
        Hex(::std::num::ParseIntError);
    }
    errors {
        Parse {
            description("could not find next element to parse")
        }
        NoResponse {
            description("client should not expect a response")
        }
    }
}

fn parse_next<T, I: Iterator<Item=T>>(it: &mut I) -> Result<T> {
    it.next().ok_or(ErrorKind::Parse.into())
}

fn parse_next_hex<'a, I: Iterator<Item=&'a str>>(it: &mut I) -> Result<u32> {
    Ok(utils::from_hex(parse_next(it)?)?)
}

fn cmd_step(ctx: &mut GdbCtx) -> Result<String> {
    ctx.dbg.hw().step();
    let break_data = BreakData::new(BreakReason::LimitReached, ctx.dbg);
    let signal = break_data.to_signal();
    *ctx.last_halt = break_data;
    Ok(signal)
}

fn cmd_continue(ctx: &mut GdbCtx) -> Result<String> {
    ctx.dbg.resume();
    bail!(ErrorKind::NoResponse)
}

struct BreakData {
    reason: BreakReason,
    r15: u32,
    r13: u32
}

impl BreakData {
    fn new(reason: BreakReason, dbg: &mut dbgcore::DbgContext) -> BreakData {
        let hw = dbg.hw();
        BreakData {
            reason: reason,
            r15: hw.pause_addr(),
            r13: hw.read_reg(13),
        }
    }

    fn to_signal(&self) -> String {
        let reason_str = match self.reason {
            BreakReason::Breakpoint => format!(";{}:", "swbreak"),
            _ => String::new(),
        };
        format!("T05{:02X}:{:08X};{:02X}:{:08X}{};", 15, self.r15.swap_bytes(),
                                                     13, self.r13.swap_bytes(),
                                                     reason_str)
    }
}

fn handle_gdb_cmd_q(cmd: &str, _ctx: &mut GdbCtx) -> Result<String> {
    let mut s = cmd.splitn(2, ':');
    let ty = parse_next(&mut s)?;
    let mut out = String::new();
    match ty {
        "Supported" => {
            out += "PacketSize=400;BreakpointCommands+;swbreak+";
        }
        _ => warn!("GDB client tried to run unsupported `q` command {}", ty)
    }
    Ok(out)
}

fn handle_gdb_cmd_v(cmd: &str, ctx: &mut GdbCtx) -> Result<String> {
    let mut s = cmd.splitn(2, |c| c == ',' || c == ':' || c == ';');
    let ty = parse_next(&mut s)?;
    let mut out = String::new();
    match ty {
        "Cont" => {
            let params = parse_next(&mut s)?;
            let threads = params.split(';');
            for thread in threads {
                let mut thread_data = thread.split(':');
                let action = parse_next(&mut thread_data)?;
                let thread_name = thread_data.next();
                if let Some(name) = thread_name {
                    assert!(name == "-1");
                }
                match action {
                    "c" => return cmd_continue(ctx),
                    "s" => return cmd_step(ctx),
                    _ => warn!("GDB client tried to run unsupported `vCont` action {}", action)
                }
            }
        }
        "Cont?" => {
            let supported = ["c", "s"];
            out += "vCont";
            for ty in supported.iter() {
                out += ";";
                out += ty;
            }
        }
        _ => warn!("GDB client tried to run unsupported `v` command {}", ty)
    }
    Ok(out)
}

fn handle_gdb_cmd(cmd: &str, ctx: &mut GdbCtx) -> Result<String> {
    let ty = parse_next(&mut cmd.chars())?;
    let params = &cmd[1..];
    let mut out = String::new();
    ctx.dbg.pause();
    match ty {
        'g' => {
            let hw = ctx.dbg.hw();
            for reg in 0..15 {
                out += &format!("{:08X}", hw.read_reg(reg).swap_bytes());
            }
            out += &format!("{:08X}", hw.pause_addr().swap_bytes());
            out += &format!("{:08X}", hw.read_cpsr().swap_bytes());
        }
        'G' => {
            let mut hw = ctx.dbg.hw();
            let nth_val = |index: usize| -> Result<u32> {
                let param_reg_range = 8*index..8*(index+1);
                let val = utils::from_hex(&params[param_reg_range])?;
                Ok(val.swap_bytes())
            };

            for reg in 0..15 {
                hw.write_reg(reg, nth_val(reg)?);
            }
            // register at 15: PC
            hw.branch_to(nth_val(15)?);
            // register at 16: CPSR
            hw.write_cpsr(nth_val(16)?);
            out += "OK";
        }
        'm' => {
            let hw = ctx.dbg.hw();
            let mut params = params.split(',');
            let addr = parse_next_hex(&mut params)?;
            let size = parse_next_hex(&mut params)?;
            let mut buf = [0u8];
            for b in 0..size {
                hw.read_mem(addr+b, &mut buf);
                out += &format!("{:02X}", buf[0]);
            }
        }
        'M' => {
            let mut hw = ctx.dbg.hw();
            let mut params = params.split(|c| c == ',' || c == ':');
            let addr = parse_next_hex(&mut params)?;
            let size = parse_next_hex(&mut params)?;
            let data = parse_next(&mut params)?;
            for b in 0..size {
                let data_byte_range = 2*(b as usize)..2*((b as usize)+1);
                let byte = utils::from_hex(&data[data_byte_range])? as u8;
                hw.write_mem(addr+b, &[byte]);
            }
            out += "OK";
        }
        'p' => {
            let hw = ctx.dbg.hw();
            let reg = utils::from_hex(&params)? as usize;
            let regval = match reg {
                0 ... 14 => hw.read_reg(reg),
                15 => hw.pause_addr(),
                25 => hw.read_cpsr(),
                n => panic!("GDB requested bad register value {}", n)
            };
            out += &format!("{:08X}", regval.swap_bytes());
        }
        'q' => {
            return handle_gdb_cmd_q(params, ctx);
        }
        's' => {
            return cmd_step(ctx);
        }
        'c' => {
            return cmd_continue(ctx);
        }
        'v' => {
            return handle_gdb_cmd_v(params, ctx);
        }
        'z' | 'Z' => {
            let mut hw = ctx.dbg.hw();
            let mut params = params.split(',');
            let brk_ty = parse_next(&mut params)?;
            let addr = parse_next_hex(&mut params)?;
            let _kind = parse_next(&mut params)?;
            assert!(brk_ty == "0");
            if ty == 'Z' {
                hw.set_breakpoint(addr);
            } else {
                hw.del_breakpoint(addr);
            }
            out += "OK";
        }
        '?' => {
            out += &ctx.last_halt.to_signal();
        }
        x => {
            warn!("GDB client tried to run unsupported command {}", x);
        }
    }
    Ok(out)
}

#[derive(Clone, Copy)]
struct Checksum(pub u32);
impl Add<u8> for Checksum {
    type Output = Checksum;
    fn add(self, b: u8) -> Checksum {
        Checksum((self.0 + (b as u32)) % 256)
    }
}
impl AddAssign<u8> for Checksum {
    fn add_assign(&mut self, b: u8) {
        self.0 = (*self + b).0;
    }
}

enum PacketType {
    Command(String),
    CtrlC,
    AckOk,
    AckErr,
    EndOfPacket,
    Malformed,
}

fn load_packet<I: Iterator<Item = u8>>(it: &mut I) -> PacketType {
    let mut it = it.skip_while(|b| *b != 0x03 && *b != b'$' && *b != b'-' && *b != b'+');
    match it.next() {
        Some(0x3) => return PacketType::CtrlC,
        Some(b'$') => {}
        Some(b'+') => return PacketType::AckOk,
        Some(b'-') => return PacketType::AckErr,
        None => return PacketType::EndOfPacket,
        _ => return PacketType::Malformed
    }

    let mut string = String::new();
    let mut checksum = Checksum(0);
    for b in it.by_ref().take_while(|b| *b != b'#') {
        string.push(b as char);
        checksum += b;
    }

    if let (Some(top), Some(bot)) = (it.next(), it.next()) {
        let packet_checksum = str::from_utf8(&[top, bot]).ok()
            .and_then(|s| utils::from_hex(s).ok());
        if Some(checksum.0) == packet_checksum {
            return PacketType::Command(string)
        }
    }
    return PacketType::Malformed
}

fn write_gdb_packet(data: &str, stream: &mut TcpStream) -> Result<()> {
    let checksum = data.bytes().fold(Checksum(0), |checksum, b| checksum + b);

    trace!("Replying with GDB packet: ${}#{:02X}", data, checksum.0);
    write!(stream, "${}#{:02X}", data, checksum.0)?;
    stream.flush()?;
    Ok(())
}

fn handle_gdb_packet(data: &[u8], stream: &mut TcpStream, ctx: &mut GdbCtx) -> Result<()> {
    trace!("Recieving GDB packet: {}", str::from_utf8(data).unwrap());
    let mut it = data.iter().cloned();
    loop {
        match load_packet(&mut it) {
            PacketType::Command(cmd) => {
                stream.write(b"+")?;
                stream.flush()?;
                match handle_gdb_cmd(&cmd, ctx) {
                    Ok(out) => write_gdb_packet(&out, stream)?,
                    Err(e) => {
                        if let &ErrorKind::NoResponse = e.kind() {}
                        else { return Err(e) }
                    }
                }

            }
            PacketType::CtrlC => {
                ctx.dbg.pause();
                trace!("Recieved GDB packet with CTRL-C signal!");
            }
            PacketType::AckOk => {},
            PacketType::AckErr => error!("GDB client replied with error packet!"),
            PacketType::EndOfPacket => {
                return Ok(())
            }
            PacketType::Malformed => {
                trace!("Recieved malformed data {:?}", data);
                stream.write(b"-")?;
                stream.flush()?;
                return Ok(())
            }
        }
    }
}

struct GdbCtx<'a, 'b: 'a> {
    dbg: &'a mut dbgcore::DbgContext<'b>,
    last_halt: &'a mut BreakData,
}

const TOKEN_LISTENER: mio::Token = mio::Token(1024);
const TOKEN_CLIENT: mio::Token = mio::Token(1025);

pub struct GdbStub {
    debugger: dbgcore::DbgCore,
    gdb_thread: Option<thread::JoinHandle<msgs::Client<Message>>>
}

impl GdbStub {
    pub fn new(msg_client: msgs::Client<Message>, debugger: dbgcore::DbgCore) -> GdbStub {
        let mut stub = GdbStub {
            debugger: debugger,
            gdb_thread: None
        };
        stub.start(msg_client);
        stub
    }

    pub fn start(&mut self, msg_client: msgs::Client<Message>) {
        let mut debugger = self.debugger.clone();
        self.gdb_thread = Some(thread::Builder::new().name("GDBStub".to_owned()).spawn(move || {
            use mio::Events;

            let poll = mio::Poll::new()
                .expect("Could not create mio polling instance!");
            let listener = TcpListener::bind(&"127.0.0.1:4567".parse().unwrap())
                .expect("Could not bind TcpListener to port!");

            poll.register(&listener, TOKEN_LISTENER, mio::Ready::readable(), mio::PollOpt::edge())
                .expect("Could not register TcpListener to mio!");

            let mut connection = Connection {
                listener: &listener,
                poll: poll,
                socket: None,
            };

            let mut events = Events::with_capacity(1024);

            info!("Starting GDB stub on port 4567...");

            let mut last_halt = BreakData::new(BreakReason::Trapped, &mut debugger.ctx());
            't: loop {
                connection.poll.poll(&mut events, Some(Duration::from_millis(100)))
                    .expect("Could not poll for network events!");

                let mut ctx = GdbCtx {
                    dbg: &mut debugger.ctx(),
                    last_halt: &mut last_halt
                };

                for event in &events {
                    handle_event(&event, &mut connection, |buf, stream| {
                        handle_gdb_packet(buf, stream, &mut ctx).unwrap();
                    });
                }

                for msg in msg_client.try_iter() {
                    match msg {
                        Message::Quit => break 't,
                        Message::Arm9Halted(reason) => {
                            if let Some(ref mut stream) = connection.socket {
                                let break_data = BreakData::new(reason, ctx.dbg);
                                write_gdb_packet(&break_data.to_signal(), stream).unwrap();
                            }
                        }
                        _ => {}
                    }
                }
            }
            msg_client
        }).unwrap())
    }

    pub fn wait(&mut self) {
        if let Some(t) = self.gdb_thread.take() {
            t.join().unwrap();
        }
    }
}

struct Connection<'a> {
    listener: &'a TcpListener,
    poll: mio::Poll,
    socket: Option<TcpStream>,
}

fn handle_event<F>(event: &mio::Event, connection: &mut Connection, mut client_responder: F)
        where F: FnMut(&[u8], &mut TcpStream) {

    let mut buf = [0u8; 1024];
    match event.token() {
        TOKEN_LISTENER => {
            match connection.listener.accept() {
                Ok((socket, _)) => {
                    info!("GDB stub accepting connection");
                    connection.poll.register(&socket, TOKEN_CLIENT, mio::Ready::readable(),
                                             mio::PollOpt::edge())
                        .expect("Could not register TCP client to mio!");
                    connection.socket = Some(socket);
                }
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                    return; // Socket is not ready anymore, stop accepting
                }
                e => panic!("GDB stub IO error! {:?}", e)
            }
        }
        TOKEN_CLIENT => for _ in 0..128 {
            match connection.socket.as_mut().unwrap().read(&mut buf) {
                Ok(0) => {
                    connection.socket = None;
                    break;
                }
                Ok(l) => client_responder(&buf[..l], connection.socket.as_mut().unwrap()),
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                    continue; // Socket is not ready anymore, stop reading
                }
                e => panic!("GDB stub IO error! {:?}", e), // Unexpected error
            }
        },
        _ => unimplemented!()
    }
}<commit_msg>gdb stub: stub out 'H' command<commit_after>use std::io::{self, Read, Write};
use std::ops::{Add, AddAssign};
use std::str;
use std::thread;
use std::time::Duration;

use mio;
use mio::tcp::{TcpListener, TcpStream};

use cpu::BreakReason;
use dbgcore;
use hwcore::Message;
use msgs;
use utils;

error_chain! {
    foreign_links {
        Io(io::Error);
        Hex(::std::num::ParseIntError);
    }
    errors {
        Parse {
            description("could not find next element to parse")
        }
        NoResponse {
            description("client should not expect a response")
        }
    }
}

fn parse_next<T, I: Iterator<Item=T>>(it: &mut I) -> Result<T> {
    it.next().ok_or(ErrorKind::Parse.into())
}

fn parse_next_hex<'a, I: Iterator<Item=&'a str>>(it: &mut I) -> Result<u32> {
    Ok(utils::from_hex(parse_next(it)?)?)
}

fn cmd_step(ctx: &mut GdbCtx) -> Result<String> {
    ctx.dbg.hw().step();
    let break_data = BreakData::new(BreakReason::LimitReached, ctx.dbg);
    let signal = break_data.to_signal();
    *ctx.last_halt = break_data;
    Ok(signal)
}

fn cmd_continue(ctx: &mut GdbCtx) -> Result<String> {
    ctx.dbg.resume();
    bail!(ErrorKind::NoResponse)
}

struct BreakData {
    reason: BreakReason,
    r15: u32,
    r13: u32
}

impl BreakData {
    fn new(reason: BreakReason, dbg: &mut dbgcore::DbgContext) -> BreakData {
        let hw = dbg.hw();
        BreakData {
            reason: reason,
            r15: hw.pause_addr(),
            r13: hw.read_reg(13),
        }
    }

    fn to_signal(&self) -> String {
        let reason_str = match self.reason {
            BreakReason::Breakpoint => format!(";{}:", "swbreak"),
            _ => String::new(),
        };
        format!("T05{:02X}:{:08X};{:02X}:{:08X}{};", 15, self.r15.swap_bytes(),
                                                     13, self.r13.swap_bytes(),
                                                     reason_str)
    }
}

fn handle_gdb_cmd_q(cmd: &str, _ctx: &mut GdbCtx) -> Result<String> {
    let mut s = cmd.splitn(2, ':');
    let ty = parse_next(&mut s)?;
    let mut out = String::new();
    match ty {
        "Supported" => {
            out += "PacketSize=400;BreakpointCommands+;swbreak+";
        }
        _ => warn!("GDB client tried to run unsupported `q` command {}", ty)
    }
    Ok(out)
}

fn handle_gdb_cmd_v(cmd: &str, ctx: &mut GdbCtx) -> Result<String> {
    let mut s = cmd.splitn(2, |c| c == ',' || c == ':' || c == ';');
    let ty = parse_next(&mut s)?;
    let mut out = String::new();
    match ty {
        "Cont" => {
            let params = parse_next(&mut s)?;
            let threads = params.split(';');
            for thread in threads {
                let mut thread_data = thread.split(':');
                let action = parse_next(&mut thread_data)?;
                let thread_name = thread_data.next();
                if let Some(name) = thread_name {
                    assert!(name == "-1");
                }
                match action {
                    "c" => return cmd_continue(ctx),
                    "s" => return cmd_step(ctx),
                    _ => warn!("GDB client tried to run unsupported `vCont` action {}", action)
                }
            }
        }
        "Cont?" => {
            let supported = ["c", "s"];
            out += "vCont";
            for ty in supported.iter() {
                out += ";";
                out += ty;
            }
        }
        _ => warn!("GDB client tried to run unsupported `v` command {}", ty)
    }
    Ok(out)
}

fn handle_gdb_cmd(cmd: &str, ctx: &mut GdbCtx) -> Result<String> {
    let ty = parse_next(&mut cmd.chars())?;
    let params = &cmd[1..];
    let mut out = String::new();
    ctx.dbg.pause();
    match ty {
        'g' => {
            let hw = ctx.dbg.hw();
            for reg in 0..15 {
                out += &format!("{:08X}", hw.read_reg(reg).swap_bytes());
            }
            out += &format!("{:08X}", hw.pause_addr().swap_bytes());
            out += &format!("{:08X}", hw.read_cpsr().swap_bytes());
        }
        'G' => {
            let mut hw = ctx.dbg.hw();
            let nth_val = |index: usize| -> Result<u32> {
                let param_reg_range = 8*index..8*(index+1);
                let val = utils::from_hex(&params[param_reg_range])?;
                Ok(val.swap_bytes())
            };

            for reg in 0..15 {
                hw.write_reg(reg, nth_val(reg)?);
            }
            // register at 15: PC
            hw.branch_to(nth_val(15)?);
            // register at 16: CPSR
            hw.write_cpsr(nth_val(16)?);
            out += "OK";
        }
        'H' => {
            out += "OK";
        }
        'm' => {
            let hw = ctx.dbg.hw();
            let mut params = params.split(',');
            let addr = parse_next_hex(&mut params)?;
            let size = parse_next_hex(&mut params)?;
            let mut buf = [0u8];
            for b in 0..size {
                hw.read_mem(addr+b, &mut buf);
                out += &format!("{:02X}", buf[0]);
            }
        }
        'M' => {
            let mut hw = ctx.dbg.hw();
            let mut params = params.split(|c| c == ',' || c == ':');
            let addr = parse_next_hex(&mut params)?;
            let size = parse_next_hex(&mut params)?;
            let data = parse_next(&mut params)?;
            for b in 0..size {
                let data_byte_range = 2*(b as usize)..2*((b as usize)+1);
                let byte = utils::from_hex(&data[data_byte_range])? as u8;
                hw.write_mem(addr+b, &[byte]);
            }
            out += "OK";
        }
        'p' => {
            let hw = ctx.dbg.hw();
            let reg = utils::from_hex(&params)? as usize;
            let regval = match reg {
                0 ... 14 => hw.read_reg(reg),
                15 => hw.pause_addr(),
                25 => hw.read_cpsr(),
                n => panic!("GDB requested bad register value {}", n)
            };
            out += &format!("{:08X}", regval.swap_bytes());
        }
        'q' => {
            return handle_gdb_cmd_q(params, ctx);
        }
        's' => {
            return cmd_step(ctx);
        }
        'c' => {
            return cmd_continue(ctx);
        }
        'v' => {
            return handle_gdb_cmd_v(params, ctx);
        }
        'z' | 'Z' => {
            let mut hw = ctx.dbg.hw();
            let mut params = params.split(',');
            let brk_ty = parse_next(&mut params)?;
            let addr = parse_next_hex(&mut params)?;
            let _kind = parse_next(&mut params)?;
            assert!(brk_ty == "0");
            if ty == 'Z' {
                hw.set_breakpoint(addr);
            } else {
                hw.del_breakpoint(addr);
            }
            out += "OK";
        }
        '?' => {
            out += &ctx.last_halt.to_signal();
        }
        x => {
            warn!("GDB client tried to run unsupported command {}", x);
        }
    }
    Ok(out)
}

#[derive(Clone, Copy)]
struct Checksum(pub u32);
impl Add<u8> for Checksum {
    type Output = Checksum;
    fn add(self, b: u8) -> Checksum {
        Checksum((self.0 + (b as u32)) % 256)
    }
}
impl AddAssign<u8> for Checksum {
    fn add_assign(&mut self, b: u8) {
        self.0 = (*self + b).0;
    }
}

enum PacketType {
    Command(String),
    CtrlC,
    AckOk,
    AckErr,
    EndOfPacket,
    Malformed,
}

fn load_packet<I: Iterator<Item = u8>>(it: &mut I) -> PacketType {
    let mut it = it.skip_while(|b| *b != 0x03 && *b != b'$' && *b != b'-' && *b != b'+');
    match it.next() {
        Some(0x3) => return PacketType::CtrlC,
        Some(b'$') => {}
        Some(b'+') => return PacketType::AckOk,
        Some(b'-') => return PacketType::AckErr,
        None => return PacketType::EndOfPacket,
        _ => return PacketType::Malformed
    }

    let mut string = String::new();
    let mut checksum = Checksum(0);
    for b in it.by_ref().take_while(|b| *b != b'#') {
        string.push(b as char);
        checksum += b;
    }

    if let (Some(top), Some(bot)) = (it.next(), it.next()) {
        let packet_checksum = str::from_utf8(&[top, bot]).ok()
            .and_then(|s| utils::from_hex(s).ok());
        if Some(checksum.0) == packet_checksum {
            return PacketType::Command(string)
        }
    }
    return PacketType::Malformed
}

fn write_gdb_packet(data: &str, stream: &mut TcpStream) -> Result<()> {
    let checksum = data.bytes().fold(Checksum(0), |checksum, b| checksum + b);

    trace!("Replying with GDB packet: ${}#{:02X}", data, checksum.0);
    write!(stream, "${}#{:02X}", data, checksum.0)?;
    stream.flush()?;
    Ok(())
}

fn handle_gdb_packet(data: &[u8], stream: &mut TcpStream, ctx: &mut GdbCtx) -> Result<()> {
    trace!("Recieving GDB packet: {}", str::from_utf8(data).unwrap());
    let mut it = data.iter().cloned();
    loop {
        match load_packet(&mut it) {
            PacketType::Command(cmd) => {
                stream.write(b"+")?;
                stream.flush()?;
                match handle_gdb_cmd(&cmd, ctx) {
                    Ok(out) => write_gdb_packet(&out, stream)?,
                    Err(e) => {
                        if let &ErrorKind::NoResponse = e.kind() {}
                        else { return Err(e) }
                    }
                }

            }
            PacketType::CtrlC => {
                ctx.dbg.pause();
                trace!("Recieved GDB packet with CTRL-C signal!");
            }
            PacketType::AckOk => {},
            PacketType::AckErr => error!("GDB client replied with error packet!"),
            PacketType::EndOfPacket => {
                return Ok(())
            }
            PacketType::Malformed => {
                trace!("Recieved malformed data {:?}", data);
                stream.write(b"-")?;
                stream.flush()?;
                return Ok(())
            }
        }
    }
}

struct GdbCtx<'a, 'b: 'a> {
    dbg: &'a mut dbgcore::DbgContext<'b>,
    last_halt: &'a mut BreakData,
}

const TOKEN_LISTENER: mio::Token = mio::Token(1024);
const TOKEN_CLIENT: mio::Token = mio::Token(1025);

pub struct GdbStub {
    debugger: dbgcore::DbgCore,
    gdb_thread: Option<thread::JoinHandle<msgs::Client<Message>>>
}

impl GdbStub {
    pub fn new(msg_client: msgs::Client<Message>, debugger: dbgcore::DbgCore) -> GdbStub {
        let mut stub = GdbStub {
            debugger: debugger,
            gdb_thread: None
        };
        stub.start(msg_client);
        stub
    }

    pub fn start(&mut self, msg_client: msgs::Client<Message>) {
        let mut debugger = self.debugger.clone();
        self.gdb_thread = Some(thread::Builder::new().name("GDBStub".to_owned()).spawn(move || {
            use mio::Events;

            let poll = mio::Poll::new()
                .expect("Could not create mio polling instance!");
            let listener = TcpListener::bind(&"127.0.0.1:4567".parse().unwrap())
                .expect("Could not bind TcpListener to port!");

            poll.register(&listener, TOKEN_LISTENER, mio::Ready::readable(), mio::PollOpt::edge())
                .expect("Could not register TcpListener to mio!");

            let mut connection = Connection {
                listener: &listener,
                poll: poll,
                socket: None,
            };

            let mut events = Events::with_capacity(1024);

            info!("Starting GDB stub on port 4567...");

            let mut last_halt = BreakData::new(BreakReason::Trapped, &mut debugger.ctx());
            't: loop {
                connection.poll.poll(&mut events, Some(Duration::from_millis(100)))
                    .expect("Could not poll for network events!");

                let mut ctx = GdbCtx {
                    dbg: &mut debugger.ctx(),
                    last_halt: &mut last_halt
                };

                for event in &events {
                    handle_event(&event, &mut connection, |buf, stream| {
                        handle_gdb_packet(buf, stream, &mut ctx).unwrap();
                    });
                }

                for msg in msg_client.try_iter() {
                    match msg {
                        Message::Quit => break 't,
                        Message::Arm9Halted(reason) => {
                            if let Some(ref mut stream) = connection.socket {
                                let break_data = BreakData::new(reason, ctx.dbg);
                                write_gdb_packet(&break_data.to_signal(), stream).unwrap();
                            }
                        }
                        _ => {}
                    }
                }
            }
            msg_client
        }).unwrap())
    }

    pub fn wait(&mut self) {
        if let Some(t) = self.gdb_thread.take() {
            t.join().unwrap();
        }
    }
}

struct Connection<'a> {
    listener: &'a TcpListener,
    poll: mio::Poll,
    socket: Option<TcpStream>,
}

fn handle_event<F>(event: &mio::Event, connection: &mut Connection, mut client_responder: F)
        where F: FnMut(&[u8], &mut TcpStream) {

    let mut buf = [0u8; 1024];
    match event.token() {
        TOKEN_LISTENER => {
            match connection.listener.accept() {
                Ok((socket, _)) => {
                    info!("GDB stub accepting connection");
                    connection.poll.register(&socket, TOKEN_CLIENT, mio::Ready::readable(),
                                             mio::PollOpt::edge())
                        .expect("Could not register TCP client to mio!");
                    connection.socket = Some(socket);
                }
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                    return; // Socket is not ready anymore, stop accepting
                }
                e => panic!("GDB stub IO error! {:?}", e)
            }
        }
        TOKEN_CLIENT => for _ in 0..128 {
            match connection.socket.as_mut().unwrap().read(&mut buf) {
                Ok(0) => {
                    connection.socket = None;
                    break;
                }
                Ok(l) => client_responder(&buf[..l], connection.socket.as_mut().unwrap()),
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                    continue; // Socket is not ready anymore, stop reading
                }
                e => panic!("GDB stub IO error! {:?}", e), // Unexpected error
            }
        },
        _ => unimplemented!()
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here
use hir_def::{
    child_by_source::ChildBySource, dyn_map::DynMap, keys, nameres::ModuleSource, AstItemDef,
    EnumVariantId, GenericDefId, LocationCtx, ModuleId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
use ra_syntax::{
    ast::{self, AstNode, NameOwner},
    match_ast, SyntaxNode,
};

use crate::{
    db::{AstDatabase, DefDatabase, HirDatabase},
    Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
    MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union,
};

pub trait FromSource: Sized {
    type Ast;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self>;
}

impl FromSource for Struct {
    type Ast = ast::StructDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Struct { id })
    }
}
impl FromSource for Union {
    type Ast = ast::UnionDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Union { id })
    }
}
impl FromSource for Enum {
    type Ast = ast::EnumDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Enum { id })
    }
}
impl FromSource for Trait {
    type Ast = ast::TraitDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Trait { id })
    }
}
impl FromSource for Function {
    type Ast = ast::FnDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::FUNCTION]
            .get(&src)
            .copied()
            .map(Function::from)
    }
}

impl FromSource for Const {
    type Ast = ast::ConstDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::CONST]
            .get(&src)
            .copied()
            .map(Const::from)
    }
}
impl FromSource for Static {
    type Ast = ast::StaticDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::STATIC]
            .get(&src)
            .copied()
            .map(Static::from)
    }
}

impl FromSource for TypeAlias {
    type Ast = ast::TypeAliasDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)
            [keys::TYPE_ALIAS]
            .get(&src)
            .copied()
            .map(TypeAlias::from)
    }
}

impl FromSource for MacroDef {
    type Ast = ast::MacroCall;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let kind = MacroDefKind::Declarative;

        let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
        let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
        let krate = Some(module.krate().crate_id());

        let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));

        let id: MacroDefId = MacroDefId { krate, ast_id, kind };
        Some(MacroDef { id })
    }
}

impl FromSource for ImplBlock {
    type Ast = ast::ImplBlock;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(ImplBlock { id })
    }
}

impl FromSource for EnumVariant {
    type Ast = ast::EnumVariant;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let parent_enum = src.value.parent_enum();
        let src_enum = InFile { file_id: src.file_id, value: parent_enum };
        let parent_enum = Enum::from_source(db, src_enum)?;
        parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
            .get(&src)
            .copied()
            .map(EnumVariant::from)
    }
}

impl FromSource for StructField {
    type Ast = FieldSource;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let src = src.as_ref();

        // FIXME this is buggy
        let variant_id: VariantId = match src.value {
            FieldSource::Named(field) => {
                let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = Struct::from_source(db, src)?;
                def.id.into()
            }
            FieldSource::Pos(field) => {
                let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = EnumVariant::from_source(db, src)?;
                EnumVariantId::from(def).into()
            }
        };

        let dyn_map = variant_id.child_by_source(db);
        match src.value {
            FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
            FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
        }
        .copied()
        .map(StructField::from)
    }
}

impl Local {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = db.body_with_source_map(parent.into());
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent, pat_id })
    }
}

impl TypeParam {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?;
        Some(TypeParam { id })
    }
}

impl Module {
    pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
        let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);

        let parent_module = match parent_declaration {
            Some(parent_declaration) => {
                let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
                Module::from_declaration(db, src_parent)
            }
            None => {
                let source_file = db.parse(src.file_id.original_file(db)).tree();
                let src_parent =
                    InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
                Module::from_definition(db, src_parent)
            }
        }?;

        let child_name = src.value.name()?;
        parent_module.child(db, &child_name.as_name())
    }

    pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
        match src.value {
            ModuleSource::Module(ref module) => {
                assert!(!module.has_semi());
                return Module::from_declaration(
                    db,
                    InFile { file_id: src.file_id, value: module.clone() },
                );
            }
            ModuleSource::SourceFile(_) => (),
        };

        let original_file = src.file_id.original_file(db);

        let (krate, local_id) = db.relevant_crates(original_file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(original_file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }
}

fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: InFile<N>) -> Option<DEF>
where
    N: AstNode,
    DEF: AstItemDef<N>,
{
    let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
    let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
    let ctx = LocationCtx::new(db, module.id, src.file_id);
    let items = db.ast_id_map(src.file_id);
    let item_id = items.ast_id(&src.value);
    Some(DEF::from_ast_id(ctx, item_id))
}

enum Container {
    Trait(Trait),
    ImplBlock(ImplBlock),
    Module(Module),
}

impl Container {
    fn find(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<Container> {
        // FIXME: this doesn't try to handle nested declarations
        for container in src.value.ancestors() {
            let res = match_ast! {
                match container {
                    ast::TraitDef(it) => {
                        let c = Trait::from_source(db, src.with_value(it))?;
                        Container::Trait(c)
                    },
                    ast::ImplBlock(it) => {
                        let c = ImplBlock::from_source(db, src.with_value(it))?;
                        Container::ImplBlock(c)
                     },
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let module_source = ModuleSource::from_child_node(db, src);
        let c = Module::from_definition(db, src.with_value(module_source))?;
        Some(Container::Module(c))
    }
}

impl ChildBySource for Container {
    fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
        match self {
            Container::Trait(it) => it.id.child_by_source(db),
            Container::ImplBlock(it) => it.id.child_by_source(db),
            Container::Module(it) => it.id.child_by_source(db),
        }
    }
}
<commit_msg>Discover type parameters in more cases<commit_after>//! FIXME: write short doc here
use hir_def::{
    child_by_source::ChildBySource, dyn_map::DynMap, keys, nameres::ModuleSource, AstItemDef,
    EnumVariantId, GenericDefId, LocationCtx, ModuleId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
use ra_syntax::{
    ast::{self, AstNode, NameOwner},
    match_ast, SyntaxNode,
};

use crate::{
    db::{AstDatabase, DefDatabase, HirDatabase},
    Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
    MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union,
};

pub trait FromSource: Sized {
    type Ast;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self>;
}

impl FromSource for Struct {
    type Ast = ast::StructDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Struct { id })
    }
}
impl FromSource for Union {
    type Ast = ast::UnionDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Union { id })
    }
}
impl FromSource for Enum {
    type Ast = ast::EnumDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Enum { id })
    }
}
impl FromSource for Trait {
    type Ast = ast::TraitDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(Trait { id })
    }
}
impl FromSource for Function {
    type Ast = ast::FnDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::FUNCTION]
            .get(&src)
            .copied()
            .map(Function::from)
    }
}

impl FromSource for Const {
    type Ast = ast::ConstDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::CONST]
            .get(&src)
            .copied()
            .map(Const::from)
    }
}
impl FromSource for Static {
    type Ast = ast::StaticDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)[keys::STATIC]
            .get(&src)
            .copied()
            .map(Static::from)
    }
}

impl FromSource for TypeAlias {
    type Ast = ast::TypeAliasDef;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        Container::find(db, src.as_ref().map(|it| it.syntax()))?.child_by_source(db)
            [keys::TYPE_ALIAS]
            .get(&src)
            .copied()
            .map(TypeAlias::from)
    }
}

impl FromSource for MacroDef {
    type Ast = ast::MacroCall;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let kind = MacroDefKind::Declarative;

        let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
        let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
        let krate = Some(module.krate().crate_id());

        let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));

        let id: MacroDefId = MacroDefId { krate, ast_id, kind };
        Some(MacroDef { id })
    }
}

impl FromSource for ImplBlock {
    type Ast = ast::ImplBlock;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let id = from_source(db, src)?;
        Some(ImplBlock { id })
    }
}

impl FromSource for EnumVariant {
    type Ast = ast::EnumVariant;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let parent_enum = src.value.parent_enum();
        let src_enum = InFile { file_id: src.file_id, value: parent_enum };
        let parent_enum = Enum::from_source(db, src_enum)?;
        parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
            .get(&src)
            .copied()
            .map(EnumVariant::from)
    }
}

impl FromSource for StructField {
    type Ast = FieldSource;
    fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
        let src = src.as_ref();

        // FIXME this is buggy
        let variant_id: VariantId = match src.value {
            FieldSource::Named(field) => {
                let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = Struct::from_source(db, src)?;
                def.id.into()
            }
            FieldSource::Pos(field) => {
                let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = EnumVariant::from_source(db, src)?;
                EnumVariantId::from(def).into()
            }
        };

        let dyn_map = variant_id.child_by_source(db);
        match src.value {
            FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
            FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
        }
        .copied()
        .map(StructField::from)
    }
}

impl Local {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = db.body_with_source_map(parent.into());
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent, pat_id })
    }
}

impl TypeParam {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::StructDef(value) => { Struct::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::EnumDef(value) => { Enum::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::TraitDef(value) => { Trait::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::TypeAliasDef(value) => { TypeAlias::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::ImplBlock(value) => { ImplBlock::from_source(db, InFile { value, file_id})?.id.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?;
        Some(TypeParam { id })
    }
}

impl Module {
    pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
        let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);

        let parent_module = match parent_declaration {
            Some(parent_declaration) => {
                let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
                Module::from_declaration(db, src_parent)
            }
            None => {
                let source_file = db.parse(src.file_id.original_file(db)).tree();
                let src_parent =
                    InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
                Module::from_definition(db, src_parent)
            }
        }?;

        let child_name = src.value.name()?;
        parent_module.child(db, &child_name.as_name())
    }

    pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
        match src.value {
            ModuleSource::Module(ref module) => {
                assert!(!module.has_semi());
                return Module::from_declaration(
                    db,
                    InFile { file_id: src.file_id, value: module.clone() },
                );
            }
            ModuleSource::SourceFile(_) => (),
        };

        let original_file = src.file_id.original_file(db);

        let (krate, local_id) = db.relevant_crates(original_file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(original_file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }
}

fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: InFile<N>) -> Option<DEF>
where
    N: AstNode,
    DEF: AstItemDef<N>,
{
    let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
    let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
    let ctx = LocationCtx::new(db, module.id, src.file_id);
    let items = db.ast_id_map(src.file_id);
    let item_id = items.ast_id(&src.value);
    Some(DEF::from_ast_id(ctx, item_id))
}

enum Container {
    Trait(Trait),
    ImplBlock(ImplBlock),
    Module(Module),
}

impl Container {
    fn find(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<Container> {
        // FIXME: this doesn't try to handle nested declarations
        for container in src.value.ancestors() {
            let res = match_ast! {
                match container {
                    ast::TraitDef(it) => {
                        let c = Trait::from_source(db, src.with_value(it))?;
                        Container::Trait(c)
                    },
                    ast::ImplBlock(it) => {
                        let c = ImplBlock::from_source(db, src.with_value(it))?;
                        Container::ImplBlock(c)
                     },
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let module_source = ModuleSource::from_child_node(db, src);
        let c = Module::from_definition(db, src.with_value(module_source))?;
        Some(Container::Module(c))
    }
}

impl ChildBySource for Container {
    fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
        match self {
            Container::Trait(it) => it.id.child_by_source(db),
            Container::ImplBlock(it) => it.id.child_by_source(db),
            Container::Module(it) => it.id.child_by_source(db),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Facilities for sending log message to syslog.
//!
//! Every function exported by this module is thread-safe. Each function will silently fail until
//! `syslog::init()` is called and returns `Ok`.
//!
//! # Examples
//!
//! ```
//! use base::syslog::{error, self, warn};
//!
//! if let Err(e) = syslog::init() {
//!     println!("failed to initiailize syslog: {}", e);
//!     return;
//! }
//! warn!("this is your {} warning", "final");
//! error!("something went horribly wrong: {}", "out of RAMs");
//! ```

pub use super::win::syslog::PlatformSyslog;

// TODO(b/223733375): Enable ignored flaky tests.
#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::{io::Read, os::windows::io::FromRawHandle};

    use log::*;
    use regex::Regex;

    use super::super::{BlockingMode, FramingMode, StreamChannel};
    use crate::descriptor::AsRawDescriptor;
    use crate::syslog::*;

    #[test]
    fn syslogger_verify_line_written() {
        init().unwrap();
        clear_path_log_levels();
        let (mut reader, writer) =
            StreamChannel::pair(BlockingMode::Blocking, FramingMode::Byte).unwrap();

        // Safe because writer is guaranteed to exist, and we forget the StreamChannel that used
        // to own the StreamChannel.
        let writer_file = unsafe { File::from_raw_handle(writer.as_raw_descriptor()) };
        std::mem::forget(writer);

        echo_file(Some(writer_file));
        error!("Test message.");

        // Ensure the message we wrote actually was written to the supplied "file" (which is
        // really a named pipe here).
        let mut buf: [u8; 1024] = [0; 1024];
        let bytes_read = reader.read(&mut buf).unwrap();
        assert!(bytes_read > 0);
        let log_msg = String::from_utf8(buf.to_vec()).unwrap();
        let re = Regex::new(r"^\[.+:ERROR:.+:[0-9]+\] Test message.").unwrap();
        assert!(re.is_match(&log_msg));
    }

    #[test]
    fn syslogger_log_level_filter() {
        init().unwrap();
        clear_path_log_levels();
        set_log_level(Priority::Error);

        let (mut reader, writer) =
            StreamChannel::pair(BlockingMode::Blocking, FramingMode::Byte).unwrap();

        // Safe because writer is guaranteed to exist, and we forget the StreamChannel that used
        // to own the StreamChannel.
        let writer_file = unsafe { File::from_raw_handle(writer.as_raw_descriptor()) };
        std::mem::forget(writer);

        echo_file(Some(writer_file));
        error!("Test message.");
        debug!("Test message.");

        add_path_log_level(file!(), Priority::Debug);
        debug!("Test with file filter.");

        set_log_level(PriorityFilter::ShowAll);

        // Ensure the message we wrote actually was written to the supplied "file" (which is
        // really a named pipe here).
        let mut buf: [u8; 1024] = [0; 1024];
        let bytes_read = reader.read(&mut buf).unwrap();
        assert!(bytes_read > 0);
        let log_msg = String::from_utf8(buf.to_vec()).unwrap();
        let re_error = Regex::new(r"(?m)^\[.*ERROR:.+:[0-9]+\] Test message.").unwrap();
        let re_debug = Regex::new(r"(?m)^\[.*DEBUG:.+:[0-9]+\] Test message.").unwrap();
        let filter_debug =
            Regex::new(r"(?m)^\[.*DEBUG:.+:[0-9]+\] Test with file filter.").unwrap();
        assert!(re_error.is_match(&log_msg));
        assert!(!re_debug.is_match(&log_msg));
        assert!(filter_debug.is_match(&log_msg));
    }
}
<commit_msg>base: fix doc error<commit_after>// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Facilities for sending log message to syslog.
//!
//! Every function exported by this module is thread-safe. Each function will silently fail until
//! `syslog::init()` is called and returns `Ok`.
//!
//! # Examples
//!
//! ```
//! use base::{error, self, warn};
//!
//! if let Err(e) = base::syslog::init() {
//!     println!("failed to initiailize syslog: {}", e);
//!     return;
//! }
//! warn!("this is your {} warning", "final");
//! error!("something went horribly wrong: {}", "out of RAMs");
//! ```

pub use super::win::syslog::PlatformSyslog;

// TODO(b/223733375): Enable ignored flaky tests.
#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::{io::Read, os::windows::io::FromRawHandle};

    use log::*;
    use regex::Regex;

    use super::super::{BlockingMode, FramingMode, StreamChannel};
    use crate::descriptor::AsRawDescriptor;
    use crate::syslog::*;

    #[test]
    fn syslogger_verify_line_written() {
        init().unwrap();
        clear_path_log_levels();
        let (mut reader, writer) =
            StreamChannel::pair(BlockingMode::Blocking, FramingMode::Byte).unwrap();

        // Safe because writer is guaranteed to exist, and we forget the StreamChannel that used
        // to own the StreamChannel.
        let writer_file = unsafe { File::from_raw_handle(writer.as_raw_descriptor()) };
        std::mem::forget(writer);

        echo_file(Some(writer_file));
        error!("Test message.");

        // Ensure the message we wrote actually was written to the supplied "file" (which is
        // really a named pipe here).
        let mut buf: [u8; 1024] = [0; 1024];
        let bytes_read = reader.read(&mut buf).unwrap();
        assert!(bytes_read > 0);
        let log_msg = String::from_utf8(buf.to_vec()).unwrap();
        let re = Regex::new(r"^\[.+:ERROR:.+:[0-9]+\] Test message.").unwrap();
        assert!(re.is_match(&log_msg));
    }

    #[test]
    fn syslogger_log_level_filter() {
        init().unwrap();
        clear_path_log_levels();
        set_log_level(Priority::Error);

        let (mut reader, writer) =
            StreamChannel::pair(BlockingMode::Blocking, FramingMode::Byte).unwrap();

        // Safe because writer is guaranteed to exist, and we forget the StreamChannel that used
        // to own the StreamChannel.
        let writer_file = unsafe { File::from_raw_handle(writer.as_raw_descriptor()) };
        std::mem::forget(writer);

        echo_file(Some(writer_file));
        error!("Test message.");
        debug!("Test message.");

        add_path_log_level(file!(), Priority::Debug);
        debug!("Test with file filter.");

        set_log_level(PriorityFilter::ShowAll);

        // Ensure the message we wrote actually was written to the supplied "file" (which is
        // really a named pipe here).
        let mut buf: [u8; 1024] = [0; 1024];
        let bytes_read = reader.read(&mut buf).unwrap();
        assert!(bytes_read > 0);
        let log_msg = String::from_utf8(buf.to_vec()).unwrap();
        let re_error = Regex::new(r"(?m)^\[.*ERROR:.+:[0-9]+\] Test message.").unwrap();
        let re_debug = Regex::new(r"(?m)^\[.*DEBUG:.+:[0-9]+\] Test message.").unwrap();
        let filter_debug =
            Regex::new(r"(?m)^\[.*DEBUG:.+:[0-9]+\] Test with file filter.").unwrap();
        assert!(re_error.is_match(&log_msg));
        assert!(!re_debug.is_match(&log_msg));
        assert!(filter_debug.is_match(&log_msg));
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::cell::RefCell;

use glib::translate::*;
use glib_sys;

use aspect_ratio::*;
use attributes::Attribute;
use css::CssStyles;
use drawing_ctx::DrawingCtx;
use error::RenderingError;
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use libc;
use node::*;
use parsers::{parse, parse_and_validate, Parse};
use property_bag::{OwnedPropertyBag, PropertyBag};
use state::Overflow;
use viewbox::*;
use viewport::{draw_in_viewport, ClipMode};

pub struct NodeGroup();

impl NodeGroup {
    pub fn new() -> NodeGroup {
        NodeGroup()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            node.draw_children(cascaded, dc, clipping)
        })
    }
}

pub struct NodeDefs();

impl NodeDefs {
    pub fn new() -> NodeDefs {
        NodeDefs()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }
}

pub struct NodeSwitch();

impl NodeSwitch {
    pub fn new() -> NodeSwitch {
        NodeSwitch()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            if let Some(child) = node.children().find(|c| c.get_cond()) {
                dc.draw_node_from_stack(&CascadedValues::new(cascaded, &child), &child, clipping)
            } else {
                Ok(())
            }
        })
    }
}

pub struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x: Cell<Length>,
    y: Cell<Length>,
    w: Cell<Length>,
    h: Cell<Length>,
    vbox: Cell<Option<ViewBox>>,
    pbag: RefCell<Option<OwnedPropertyBag>>,
}

impl NodeSvg {
    pub fn new() -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            x: Cell::new(Length::parse_str("0", LengthDir::Horizontal).unwrap()),
            y: Cell::new(Length::parse_str("0", LengthDir::Vertical).unwrap()),
            w: Cell::new(Length::parse_str("100%", LengthDir::Horizontal).unwrap()),
            h: Cell::new(Length::parse_str("100%", LengthDir::Vertical).unwrap()),
            vbox: Cell::new(None),
            pbag: RefCell::new(None),
        }
    }

    pub fn set_delayed_style(&self, node: &RsvgNode, css_styles: &CssStyles) {
        if let Some(owned_pbag) = self.pbag.borrow().as_ref() {
            let pbag = PropertyBag::from_owned(owned_pbag);
            node.set_style(css_styles, &pbag);
        }
    }
}

impl NodeTrait for NodeSvg {
    fn set_atts(
        &self,
        node: &RsvgNode,
        _: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        let is_inner_svg = node.get_parent().is_some();

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio
                        .set(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::X => {
                    if is_inner_svg {
                        self.x.set(parse("x", value, LengthDir::Horizontal)?);
                    }
                }

                Attribute::Y => {
                    if is_inner_svg {
                        self.y.set(parse("y", value, LengthDir::Vertical)?);
                    }
                }

                Attribute::Width => self.w.set(parse_and_validate(
                    "width",
                    value,
                    LengthDir::Horizontal,
                    Length::check_nonnegative,
                )?),

                Attribute::Height => self.h.set(parse_and_validate(
                    "height",
                    value,
                    LengthDir::Vertical,
                    Length::check_nonnegative,
                )?),

                Attribute::ViewBox => self.vbox.set(parse("viewBox", value, ()).map(Some)?),

                _ => (),
            }
        }

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        *self.pbag.borrow_mut() = Some(pbag.to_owned());

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);
        let nw = self.w.get().normalize(values, &params);
        let nh = self.h.get().normalize(values, &params);

        let do_clip = !values.is_overflow() && node.get_parent().is_some();

        draw_in_viewport(
            nx,
            ny,
            nw,
            nh,
            ClipMode::ClipToViewport,
            do_clip,
            self.vbox.get(),
            self.preserve_aspect_ratio.get(),
            node,
            values,
            draw_ctx.get_cairo_context().get_matrix(),
            draw_ctx,
            clipping,
            &mut |dc| {
                // we don't push a layer because draw_in_viewport() already does it
                node.draw_children(cascaded, dc, clipping)
            },
        )
    }
}

pub struct NodeUse {
    link: RefCell<Option<String>>,
    x: Cell<Length>,
    y: Cell<Length>,
    w: Cell<Option<Length>>,
    h: Cell<Option<Length>>,
}

impl NodeUse {
    pub fn new() -> NodeUse {
        NodeUse {
            link: RefCell::new(None),
            x: Cell::new(Length::default()),
            y: Cell::new(Length::default()),
            w: Cell::new(None),
            h: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeUse {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag<'_>) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::XlinkHref => *self.link.borrow_mut() = Some(value.to_owned()),

                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical)?),

                Attribute::Width => self.w.set(
                    parse_and_validate(
                        "width",
                        value,
                        LengthDir::Horizontal,
                        Length::check_nonnegative,
                    )
                    .map(Some)?,
                ),
                Attribute::Height => self.h.set(
                    parse_and_validate(
                        "height",
                        value,
                        LengthDir::Vertical,
                        Length::check_nonnegative,
                    )
                    .map(Some)?,
                ),

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let link = self.link.borrow();

        if link.is_none() {
            return Ok(());
        }

        let uri = link.as_ref().unwrap();

        let child = if let Some(acquired) = draw_ctx.get_acquired_href(uri) {
            // Here we clone the acquired child, so that we can drop the AcquiredNode as
            // early as possible.  This is so that the child's drawing method will be able
            // to re-acquire the child for other purposes.
            acquired.get().clone()
        } else {
            rsvg_log!(
                "element {} references nonexistent \"{}\"",
                node.get_human_readable_name(),
                uri,
            );
            return Ok(());
        };

        if Node::is_ancestor(child.clone(), node.clone()) {
            // or, if we're <use>'ing ourselves
            return Err(RenderingError::CircularReference);
        }

        draw_ctx.increase_num_elements_rendered_through_use(1);

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);

        // If attributes ‘width’ and/or ‘height’ are not specified,
        // [...] use values of '100%' for these attributes.
        // From https://www.w3.org/TR/SVG/struct.html#UseElement in
        // "If the ‘use’ element references a ‘symbol’ element"

        let nw = self
            .w
            .get()
            .unwrap_or_else(|| Length::parse_str("100%", LengthDir::Horizontal).unwrap())
            .normalize(values, &params);
        let nh = self
            .h
            .get()
            .unwrap_or_else(|| Length::parse_str("100%", LengthDir::Vertical).unwrap())
            .normalize(values, &params);

        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
        if nw.approx_eq_cairo(&0.0) || nh.approx_eq_cairo(&0.0) {
            return Ok(());
        }

        if child.get_type() != NodeType::Symbol {
            let cr = draw_ctx.get_cairo_context();
            cr.translate(nx, ny);

            draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
                dc.draw_node_from_stack(
                    &CascadedValues::new_from_values(&child, values),
                    &child,
                    clipping,
                )
            })
        } else {
            child.with_impl(|symbol: &NodeSymbol| {
                let do_clip = !values.is_overflow()
                    || (values.overflow == Overflow::Visible && child.is_overflow());

                draw_in_viewport(
                    nx,
                    ny,
                    nw,
                    nh,
                    ClipMode::ClipToVbox,
                    do_clip,
                    symbol.vbox.get(),
                    symbol.preserve_aspect_ratio.get(),
                    node,
                    values,
                    draw_ctx.get_cairo_context().get_matrix(),
                    draw_ctx,
                    clipping,
                    &mut |dc| {
                        // We don't push a layer because draw_in_viewport() already does it
                        child.draw_children(
                            &CascadedValues::new_from_values(&child, values),
                            dc,
                            clipping,
                        )
                    },
                )
            })
        }
    }
}

pub struct NodeSymbol {
    preserve_aspect_ratio: Cell<AspectRatio>,
    vbox: Cell<Option<ViewBox>>,
}

impl NodeSymbol {
    pub fn new() -> NodeSymbol {
        NodeSymbol {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            vbox: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeSymbol {
    fn set_atts(
        &self,
        node: &RsvgNode,
        _: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        // symbol element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio
                        .set(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::ViewBox => self.vbox.set(parse("viewBox", value, ()).map(Some)?),

                _ => (),
            }
        }

        Ok(())
    }
}

#[no_mangle]
pub extern "C" fn rsvg_node_svg_get_size(
    raw_node: *const RsvgNode,
    dpi_x: libc::c_double,
    dpi_y: libc::c_double,
    out_width: *mut i32,
    out_height: *mut i32,
) -> glib_sys::gboolean {
    assert!(!raw_node.is_null());
    let node: &RsvgNode = unsafe { &*raw_node };

    assert!(!out_width.is_null());
    assert!(!out_height.is_null());

    node.with_impl(
        |svg: &NodeSvg| match (svg.w.get(), svg.h.get(), svg.vbox.get()) {
            (w, h, Some(vb)) => {
                unsafe {
                    *out_width = w.hand_normalize(dpi_x, vb.0.width, 12.0).round() as i32;
                    *out_height = h.hand_normalize(dpi_y, vb.0.height, 12.0).round() as i32;
                }
                true.to_glib()
            }
            (w, h, None) if w.unit != LengthUnit::Percent && h.unit != LengthUnit::Percent => {
                unsafe {
                    *out_width = w.hand_normalize(dpi_x, 0.0, 12.0).round() as i32;
                    *out_height = h.hand_normalize(dpi_y, 0.0, 12.0).round() as i32;
                }
                true.to_glib()
            }
            (_, _, _) => false.to_glib(),
        },
    )
}
<commit_msg>NodeUse: use Fragment instead of String for the link<commit_after>use std::cell::Cell;
use std::cell::RefCell;

use glib::translate::*;
use glib_sys;

use aspect_ratio::*;
use attributes::Attribute;
use css::CssStyles;
use defs::Fragment;
use drawing_ctx::DrawingCtx;
use error::{AttributeResultExt, RenderingError};
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use libc;
use node::*;
use parsers::{parse, parse_and_validate, Parse};
use property_bag::{OwnedPropertyBag, PropertyBag};
use state::Overflow;
use viewbox::*;
use viewport::{draw_in_viewport, ClipMode};

pub struct NodeGroup();

impl NodeGroup {
    pub fn new() -> NodeGroup {
        NodeGroup()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            node.draw_children(cascaded, dc, clipping)
        })
    }
}

pub struct NodeDefs();

impl NodeDefs {
    pub fn new() -> NodeDefs {
        NodeDefs()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }
}

pub struct NodeSwitch();

impl NodeSwitch {
    pub fn new() -> NodeSwitch {
        NodeSwitch()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            if let Some(child) = node.children().find(|c| c.get_cond()) {
                dc.draw_node_from_stack(&CascadedValues::new(cascaded, &child), &child, clipping)
            } else {
                Ok(())
            }
        })
    }
}

pub struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x: Cell<Length>,
    y: Cell<Length>,
    w: Cell<Length>,
    h: Cell<Length>,
    vbox: Cell<Option<ViewBox>>,
    pbag: RefCell<Option<OwnedPropertyBag>>,
}

impl NodeSvg {
    pub fn new() -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            x: Cell::new(Length::parse_str("0", LengthDir::Horizontal).unwrap()),
            y: Cell::new(Length::parse_str("0", LengthDir::Vertical).unwrap()),
            w: Cell::new(Length::parse_str("100%", LengthDir::Horizontal).unwrap()),
            h: Cell::new(Length::parse_str("100%", LengthDir::Vertical).unwrap()),
            vbox: Cell::new(None),
            pbag: RefCell::new(None),
        }
    }

    pub fn set_delayed_style(&self, node: &RsvgNode, css_styles: &CssStyles) {
        if let Some(owned_pbag) = self.pbag.borrow().as_ref() {
            let pbag = PropertyBag::from_owned(owned_pbag);
            node.set_style(css_styles, &pbag);
        }
    }
}

impl NodeTrait for NodeSvg {
    fn set_atts(
        &self,
        node: &RsvgNode,
        _: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        let is_inner_svg = node.get_parent().is_some();

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio
                        .set(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::X => {
                    if is_inner_svg {
                        self.x.set(parse("x", value, LengthDir::Horizontal)?);
                    }
                }

                Attribute::Y => {
                    if is_inner_svg {
                        self.y.set(parse("y", value, LengthDir::Vertical)?);
                    }
                }

                Attribute::Width => self.w.set(parse_and_validate(
                    "width",
                    value,
                    LengthDir::Horizontal,
                    Length::check_nonnegative,
                )?),

                Attribute::Height => self.h.set(parse_and_validate(
                    "height",
                    value,
                    LengthDir::Vertical,
                    Length::check_nonnegative,
                )?),

                Attribute::ViewBox => self.vbox.set(parse("viewBox", value, ()).map(Some)?),

                _ => (),
            }
        }

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        *self.pbag.borrow_mut() = Some(pbag.to_owned());

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);
        let nw = self.w.get().normalize(values, &params);
        let nh = self.h.get().normalize(values, &params);

        let do_clip = !values.is_overflow() && node.get_parent().is_some();

        draw_in_viewport(
            nx,
            ny,
            nw,
            nh,
            ClipMode::ClipToViewport,
            do_clip,
            self.vbox.get(),
            self.preserve_aspect_ratio.get(),
            node,
            values,
            draw_ctx.get_cairo_context().get_matrix(),
            draw_ctx,
            clipping,
            &mut |dc| {
                // we don't push a layer because draw_in_viewport() already does it
                node.draw_children(cascaded, dc, clipping)
            },
        )
    }
}

pub struct NodeUse {
    link: RefCell<Option<Fragment>>,
    x: Cell<Length>,
    y: Cell<Length>,
    w: Cell<Option<Length>>,
    h: Cell<Option<Length>>,
}

impl NodeUse {
    pub fn new() -> NodeUse {
        NodeUse {
            link: RefCell::new(None),
            x: Cell::new(Length::default()),
            y: Cell::new(Length::default()),
            w: Cell::new(None),
            h: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeUse {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag<'_>) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::XlinkHref => {
                    *self.link.borrow_mut() =
                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
                }

                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical)?),

                Attribute::Width => self.w.set(
                    parse_and_validate(
                        "width",
                        value,
                        LengthDir::Horizontal,
                        Length::check_nonnegative,
                    )
                    .map(Some)?,
                ),
                Attribute::Height => self.h.set(
                    parse_and_validate(
                        "height",
                        value,
                        LengthDir::Vertical,
                        Length::check_nonnegative,
                    )
                    .map(Some)?,
                ),

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx<'_>,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let link = self.link.borrow();

        if link.is_none() {
            return Ok(());
        }

        let link = link.as_ref().unwrap();

        let child = if let Some(acquired) = draw_ctx.get_acquired_node(link) {
            // Here we clone the acquired child, so that we can drop the AcquiredNode as
            // early as possible.  This is so that the child's drawing method will be able
            // to re-acquire the child for other purposes.
            acquired.get().clone()
        } else {
            rsvg_log!(
                "element {} references nonexistent \"{}\"",
                node.get_human_readable_name(),
                link,
            );
            return Ok(());
        };

        if Node::is_ancestor(child.clone(), node.clone()) {
            // or, if we're <use>'ing ourselves
            return Err(RenderingError::CircularReference);
        }

        draw_ctx.increase_num_elements_rendered_through_use(1);

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);

        // If attributes ‘width’ and/or ‘height’ are not specified,
        // [...] use values of '100%' for these attributes.
        // From https://www.w3.org/TR/SVG/struct.html#UseElement in
        // "If the ‘use’ element references a ‘symbol’ element"

        let nw = self
            .w
            .get()
            .unwrap_or_else(|| Length::parse_str("100%", LengthDir::Horizontal).unwrap())
            .normalize(values, &params);
        let nh = self
            .h
            .get()
            .unwrap_or_else(|| Length::parse_str("100%", LengthDir::Vertical).unwrap())
            .normalize(values, &params);

        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
        if nw.approx_eq_cairo(&0.0) || nh.approx_eq_cairo(&0.0) {
            return Ok(());
        }

        if child.get_type() != NodeType::Symbol {
            let cr = draw_ctx.get_cairo_context();
            cr.translate(nx, ny);

            draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
                dc.draw_node_from_stack(
                    &CascadedValues::new_from_values(&child, values),
                    &child,
                    clipping,
                )
            })
        } else {
            child.with_impl(|symbol: &NodeSymbol| {
                let do_clip = !values.is_overflow()
                    || (values.overflow == Overflow::Visible && child.is_overflow());

                draw_in_viewport(
                    nx,
                    ny,
                    nw,
                    nh,
                    ClipMode::ClipToVbox,
                    do_clip,
                    symbol.vbox.get(),
                    symbol.preserve_aspect_ratio.get(),
                    node,
                    values,
                    draw_ctx.get_cairo_context().get_matrix(),
                    draw_ctx,
                    clipping,
                    &mut |dc| {
                        // We don't push a layer because draw_in_viewport() already does it
                        child.draw_children(
                            &CascadedValues::new_from_values(&child, values),
                            dc,
                            clipping,
                        )
                    },
                )
            })
        }
    }
}

pub struct NodeSymbol {
    preserve_aspect_ratio: Cell<AspectRatio>,
    vbox: Cell<Option<ViewBox>>,
}

impl NodeSymbol {
    pub fn new() -> NodeSymbol {
        NodeSymbol {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            vbox: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeSymbol {
    fn set_atts(
        &self,
        node: &RsvgNode,
        _: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        // symbol element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio
                        .set(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::ViewBox => self.vbox.set(parse("viewBox", value, ()).map(Some)?),

                _ => (),
            }
        }

        Ok(())
    }
}

#[no_mangle]
pub extern "C" fn rsvg_node_svg_get_size(
    raw_node: *const RsvgNode,
    dpi_x: libc::c_double,
    dpi_y: libc::c_double,
    out_width: *mut i32,
    out_height: *mut i32,
) -> glib_sys::gboolean {
    assert!(!raw_node.is_null());
    let node: &RsvgNode = unsafe { &*raw_node };

    assert!(!out_width.is_null());
    assert!(!out_height.is_null());

    node.with_impl(
        |svg: &NodeSvg| match (svg.w.get(), svg.h.get(), svg.vbox.get()) {
            (w, h, Some(vb)) => {
                unsafe {
                    *out_width = w.hand_normalize(dpi_x, vb.0.width, 12.0).round() as i32;
                    *out_height = h.hand_normalize(dpi_y, vb.0.height, 12.0).round() as i32;
                }
                true.to_glib()
            }
            (w, h, None) if w.unit != LengthUnit::Percent && h.unit != LengthUnit::Percent => {
                unsafe {
                    *out_width = w.hand_normalize(dpi_x, 0.0, 12.0).round() as i32;
                    *out_height = h.hand_normalize(dpi_y, 0.0, 12.0).round() as i32;
                }
                true.to_glib()
            }
            (_, _, _) => false.to_glib(),
        },
    )
}
<|endoftext|>