<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*!
UART configuration.
*/

use core::intrinsics::abort;

use drivers::chario::CharIO;
use hal::uart;

#[path="../../util/wait_for.rs"] mod wait_for;

/// Available UART peripherals.
#[allow(missing_doc)]
pub enum UARTPeripheral {
  UART0,
  UART1,
  UART2,
}

/// Structure describing a UART instance.
pub struct UART {
  reg: &'static reg::UART,
}

/// Stop bits configuration.
/// K20 UART only supports one stop bit.
pub enum StopBit {
  /// Single stop bit.
  StopBit1bit  = 0,
}

impl StopBit {
  /// Convert from number to StopBit.
  pub fn from_u8(val: u8) -> StopBit {
    match val {
      1 => StopBit1bit,
      _ => unsafe { abort() },
    }
  }
}

impl UARTPeripheral {
  fn reg(self) -> &'static reg::UART {
    match self {
      UART0 => &reg::UART0,
      UART1 => &reg::UART1,
      UART2 => &reg::UART2,
    }
  }
}

impl UART {
  /// Returns platform-specific UART object that implements CharIO trait.
  pub fn new(peripheral: UARTPeripheral, baudrate:  u32, word_len: u8,
      parity: uart::Parity, stop_bits: u8) -> UART {
    let uart = UART {
      reg: peripheral.reg()
    };
    uart.set_baud_rate(baudrate);
    uart.set_mode(reg::UART_c1_m::from_u8(word_len), parity, StopBit::from_u8(stop_bits));
    uart.set_fifo_enabled(true);

    uart
  }

  #[no_stack_check]
  fn uart_clock(&self) -> u32 {
    48000000 // FIXME(bgamari): Use peripheral clocks
  }

  #[no_stack_check]
  fn set_baud_rate(&self, baud_rate: u32) {
    let sbr: u32 = self.uart_clock() / 16 / baud_rate;
    let brfa: u32 = (2 * self.uart_clock() / baud_rate) % 32;
    (*self.reg).bdh.set_sbr((sbr >> 8) as u8);
    (*self.reg).bdl.set_sbr((sbr & 0xff) as u8);
    (*self.reg).c4.set_brfa(brfa as u8);
  }

  #[no_stack_check]
  fn set_mode(&self, word_len: reg::UART_c1_m, parity: uart::Parity,
              _stop_bits: StopBit) {
    let mut c1 = (*self.reg).c1.set_m(word_len);
    match parity {
      uart::Disabled => {c1.set_pe(false);}
      uart::Odd      => {c1.set_pe(true).set_pt(reg::Odd);}
      uart::Even     => {c1.set_pe(true).set_pt(reg::Even);}
      uart::Forced1  => unsafe { abort() },
      uart::Forced0  => unsafe { abort() },
    };
  }

  fn set_fifo_enabled(&self, enabled: bool) {
    (*(self.reg)).pfifo.set_rxfe(enabled).set_txfe(enabled);
  }
}

impl CharIO for UART {
  fn putc(&self, value: char) {
    wait_for!(self.reg.s1.tdre());
    self.reg.d.set_re(value as u8);
  }
}

/// Register definitions
pub mod reg {
  use util::volatile_cell::VolatileCell;
  use core::ops::Drop;
  use core::intrinsics::abort;

  ioregs!(UART = {
    0x0    => reg8 bdh {  //! baud rate high
      0..4  => sbr,       //= baud rate (high 5 bits)
      6     => rxedgie,   //= RxD input active edge interrupt enable
      7     => lbkdie,    //= LIN break detect interrupt enable
    },

    0x1    => reg8 bdl {  //! baud rate low
      0..7  => sbr,       //= baud rate (low 8 bits)
    }

    0x2    => reg8 c1 {   //! Control register 1
      0     => pt {       //! parity type
        0x0 => Even,    //=   even parity
        0x1 => Odd,     //=   odd parity
      }
      1     => pe,        //= parity enable
      2     => ilt {      //! idle line type select
        0x0 => AfterStart, //= idle character bit count starts after start bit
        0x1 => AfterStop,  //= idle character bit count starts after stop bit
      }
      3     => wake {     //! receiver wakeup method select
        0x0 => IdleLineWakeup,
        0x1 => AddressMarkWakeup,
      }
      4     => m {        //! bit width mode select
        0x0 => DataBits8, //= start + 8 data bits + stop
        0x1 => DataBits9, //= start + 9 data bits + stop
      }
      5     => rsrc {     //! receiver source select
        0x0 => RxLoopback, //= select internal loop-back mode
        0x1 => SingleWire, //= single-wire UART mode
      }
      6     => uartswai, //= should UART stop in Wait mode
      7     => loops,    //= loop mode enable
    },

    0x3    => reg8 c2 {  //! Control register 2
      0     => sbk,      //= send break
      1     => rwu,      //= receiver wakeup control
      2     => re,       //= receiver enable
      3     => te,       //= transmitter enable
      4     => ilie,     //= idle line interrupt enable
      5     => rie,      //= receiver full interrupt enable
      6     => tcie,     //= transmission complete interrupt enable
      7     => tie,      //= transmitter interrupt enable
    },

    0x4    => reg8 s1 {  //! Status register 1
      0     => pf,       //= parity error flag
      1     => fe,       //= framing error flag
      2     => nf,       //= noise flag
      3     => or,       //= receiver overrun  flag
      4     => idle,     //= idle line flag
      5     => rdrf,     //= receive data register full flag
      6     => tc,       //= transmit complete flag
      7     => tdre,     //= transmit data register empty flag
    },

    0x5    => reg8 s2 {  //! Status register 2
      0     => raf: ro,  //= reciever active flag
      1     => lbkde,    //= LIN break detection enable
      2     => brk13,    //= break transmit character length
      3     => rwuid,    //= receive wakeup idle detect
      4     => rxinv,    //= receive data inversion
      5     => msbf,     //= most significant bit first
      6     => rxedgif,  //= RxD pin active edge interrupt flag
      7     => lbkdif,   //= LIN break detect interrupt flag
    },

    0x6    => reg8 c3 {  //! Control register 3
      0     => peie,     //= parity error interrupt enable
      1     => feie,     //= framing error interrupt enable
      2     => neie,     //= noise error interrupt enable
      3     => orie,     //= overrun error interrupt enable
      4     => txinv,    //= transmit data inversion
      5     => txdir,    //= transmitter pin data direction in single-wire mode
      6     => t8,       //= transmit bit 8
      7     => r8: ro,   //= receieved bit 8
    },

    0x7    => reg8 d {   //! Data register
      0..7  => re,       //= reads return the contents of the receive data register,
                         //= writes go to the transmit data register.
    },

    0x8    => reg8 ma1 { //! Match address register 1
      0..7  => ma,       //= match address
    },

    0x9    => reg8 ma2 { //! Match address register 2
      0..7  => ma,       //= match address
    },

    0xa    => reg8 c4 {  //! Control register 4
      0..4  => brfa,     //= baud rate fine adjust
      5     => m10,      //= 10-bit mode select
      6     => maen2,    //= match address 2 enable
      7     => maen1,    //= match address 1 enable
    },

    0xb    => reg8 c5 {  //! Control register 5
      5     => rdmas,    //= receiver full DMA select
      7     => tdmas,    //= transmitter DMA select
    },

    0xc    => reg8 ed {  //! Extended data register
      6     => paritye: ro, //= The current received data word has a parity error
      7     => noisy: ro,   //= The current received data word was received with noise
    },

    0xd    => reg8 modem { //! Modem register
      0     => txctse,   //= transmitter clear-to-send enable
      1     => txrese,   //= transmitter request-to-send enable
      2     => txrtspol, //= transmitter request-to-send polarity
      3     => rxrtse,   //= receiver request-to-send polarity
    },

    0xe    => reg8 ir {  //! Infrared register
      0..1  => tnp {     //! transmitter narrow pulse
        0  => PULSE_3_16,
        1  => PULSE_1_16,
        2  => PULSE_1_32,
        3  => PULSE_1_4,
      },
      2     => iren,     //= infrared enable
    },

    0x10   =>  reg8 pfifo { //! FIFO parameters
      0..2  => rxfifosize: ro, //= receive FIFO buffer depth
      3     => rxfe,           //= receive FIFO enable
      4..6  => txfifosize: ro, //= transmit FIFO buffer depth
      7     => txfe,           //= transmit FIFO enable
    },

    0x11   => reg8 cfifo { //! FIFO control
      0     => rxufe,        //= receive FIFO underflow interrupt enable
      1     => txofe,        //= transmit FIFO overflow interrupt enable
      2     => rxofe,        //= receive FIFO overflow interrupt enable
      6     => rxflush: wo,  //= flush receive FIFO
      7     => txflush: wo,  //= flush transmit FIFO
    },

    0x12   => reg8 sfifo { //! FIFO status
      0     => rxuf,         //= recieve FIFO underflow flag
      1     => txof,         //= transmit FIFO overflow flag
      2     => rxof,         //= recieve FIFO overflow flag
      6     => rxempt: ro,   //= recieve FIFO empty flag
      7     => txempt: ro,   //= transmit FIFO empty flag
    },

    0x13   => reg8 twfifo { //! Transmit FIFO watermark
      0..7  => txwater,      //= number of data words to transmit before generating
                             //= an interrupt or DMA request
    },

    0x14   => reg8 tcfifo { //! Transmit FIFO count
      0..7  => txcount,      //= number of data words in the transmit FIFO buffer.
    }

    0x15   => reg8 rwfifo { //! Receive FIFO watermark
      0..7  => rxwater,      //= number of data words to receive before generating
                             //= an interrupt or DMA request
    },

    0x16   => reg8 rcfifo { //! Receive FIFO count
      0..7  => rxcount,      //= number of data words in the receive FIFO buffer.
    },

    // FIXME(bgamari): Specialized registers omitted
  })

  impl UART_c1_m {
    /// UART data word length flag value from bit count
    pub fn from_u8(val: u8) -> UART_c1_m {
      match val {
        8 => DataBits8,
        9 => DataBits9,
        _ => unsafe { abort() },
      }
    }
  }

  extern {
    #[link_name="k20_iomem_UART0"] pub static UART0: UART;
    #[link_name="k20_iomem_UART1"] pub static UART1: UART;
    #[link_name="k20_iomem_UART2"] pub static UART2: UART;
  }
}
<commit_msg>k20::uart: Enable transmission/reception in set_mode<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*!
UART configuration.
*/

use core::intrinsics::abort;

use drivers::chario::CharIO;
use hal::uart;

#[path="../../util/wait_for.rs"] mod wait_for;

/// Available UART peripherals.
#[allow(missing_doc)]
pub enum UARTPeripheral {
  UART0,
  UART1,
  UART2,
}

/// Structure describing a UART instance.
pub struct UART {
  reg: &'static reg::UART,
}

/// Stop bits configuration.
/// K20 UART only supports one stop bit.
pub enum StopBit {
  /// Single stop bit.
  StopBit1bit  = 0,
}

impl StopBit {
  /// Convert from number to StopBit.
  pub fn from_u8(val: u8) -> StopBit {
    match val {
      1 => StopBit1bit,
      _ => unsafe { abort() },
    }
  }
}

impl UARTPeripheral {
  fn reg(self) -> &'static reg::UART {
    match self {
      UART0 => &reg::UART0,
      UART1 => &reg::UART1,
      UART2 => &reg::UART2,
    }
  }
}

impl UART {
  /// Returns platform-specific UART object that implements CharIO trait.
  pub fn new(peripheral: UARTPeripheral, baudrate:  u32, word_len: u8,
      parity: uart::Parity, stop_bits: u8) -> UART {
    let uart = UART {
      reg: peripheral.reg()
    };
    uart.set_baud_rate(baudrate);
    uart.set_mode(reg::UART_c1_m::from_u8(word_len), parity, StopBit::from_u8(stop_bits));
    uart.set_fifo_enabled(true);

    uart
  }

  #[no_stack_check]
  fn uart_clock(&self) -> u32 {
    48000000 // FIXME(bgamari): Use peripheral clocks
  }

  #[no_stack_check]
  fn set_baud_rate(&self, baud_rate: u32) {
    let sbr: u32 = self.uart_clock() / 16 / baud_rate;
    let brfa: u32 = (2 * self.uart_clock() / baud_rate) % 32;
    (*self.reg).bdh.set_sbr((sbr >> 8) as u8);
    (*self.reg).bdl.set_sbr((sbr & 0xff) as u8);
    (*self.reg).c4.set_brfa(brfa as u8);
  }

  #[no_stack_check]
  fn set_mode(&self, word_len: reg::UART_c1_m, parity: uart::Parity,
              _stop_bits: StopBit) {
    let mut c1 = (*self.reg).c1.set_m(word_len);
    match parity {
      uart::Disabled => {c1.set_pe(false);}
      uart::Odd      => {c1.set_pe(true).set_pt(reg::Odd);}
      uart::Even     => {c1.set_pe(true).set_pt(reg::Even);}
      uart::Forced1  => unsafe { abort() },
      uart::Forced0  => unsafe { abort() },
    };
    (*self.reg).c2.set_te(true).set_re(true);
  }

  fn set_fifo_enabled(&self, enabled: bool) {
    (*(self.reg)).pfifo.set_rxfe(enabled).set_txfe(enabled);
  }
}

impl CharIO for UART {
  fn putc(&self, value: char) {
    wait_for!(self.reg.s1.tdre());
    self.reg.d.set_re(value as u8);
  }
}

/// Register definitions
pub mod reg {
  use util::volatile_cell::VolatileCell;
  use core::ops::Drop;
  use core::intrinsics::abort;

  ioregs!(UART = {
    0x0    => reg8 bdh {  //! baud rate high
      0..4  => sbr,       //= baud rate (high 5 bits)
      6     => rxedgie,   //= RxD input active edge interrupt enable
      7     => lbkdie,    //= LIN break detect interrupt enable
    },

    0x1    => reg8 bdl {  //! baud rate low
      0..7  => sbr,       //= baud rate (low 8 bits)
    }

    0x2    => reg8 c1 {   //! Control register 1
      0     => pt {       //! parity type
        0x0 => Even,    //=   even parity
        0x1 => Odd,     //=   odd parity
      }
      1     => pe,        //= parity enable
      2     => ilt {      //! idle line type select
        0x0 => AfterStart, //= idle character bit count starts after start bit
        0x1 => AfterStop,  //= idle character bit count starts after stop bit
      }
      3     => wake {     //! receiver wakeup method select
        0x0 => IdleLineWakeup,
        0x1 => AddressMarkWakeup,
      }
      4     => m {        //! bit width mode select
        0x0 => DataBits8, //= start + 8 data bits + stop
        0x1 => DataBits9, //= start + 9 data bits + stop
      }
      5     => rsrc {     //! receiver source select
        0x0 => RxLoopback, //= select internal loop-back mode
        0x1 => SingleWire, //= single-wire UART mode
      }
      6     => uartswai, //= should UART stop in Wait mode
      7     => loops,    //= loop mode enable
    },

    0x3    => reg8 c2 {  //! Control register 2
      0     => sbk,      //= send break
      1     => rwu,      //= receiver wakeup control
      2     => re,       //= receiver enable
      3     => te,       //= transmitter enable
      4     => ilie,     //= idle line interrupt enable
      5     => rie,      //= receiver full interrupt enable
      6     => tcie,     //= transmission complete interrupt enable
      7     => tie,      //= transmitter interrupt enable
    },

    0x4    => reg8 s1 {  //! Status register 1
      0     => pf,       //= parity error flag
      1     => fe,       //= framing error flag
      2     => nf,       //= noise flag
      3     => or,       //= receiver overrun  flag
      4     => idle,     //= idle line flag
      5     => rdrf,     //= receive data register full flag
      6     => tc,       //= transmit complete flag
      7     => tdre,     //= transmit data register empty flag
    },

    0x5    => reg8 s2 {  //! Status register 2
      0     => raf: ro,  //= reciever active flag
      1     => lbkde,    //= LIN break detection enable
      2     => brk13,    //= break transmit character length
      3     => rwuid,    //= receive wakeup idle detect
      4     => rxinv,    //= receive data inversion
      5     => msbf,     //= most significant bit first
      6     => rxedgif,  //= RxD pin active edge interrupt flag
      7     => lbkdif,   //= LIN break detect interrupt flag
    },

    0x6    => reg8 c3 {  //! Control register 3
      0     => peie,     //= parity error interrupt enable
      1     => feie,     //= framing error interrupt enable
      2     => neie,     //= noise error interrupt enable
      3     => orie,     //= overrun error interrupt enable
      4     => txinv,    //= transmit data inversion
      5     => txdir,    //= transmitter pin data direction in single-wire mode
      6     => t8,       //= transmit bit 8
      7     => r8: ro,   //= receieved bit 8
    },

    0x7    => reg8 d {   //! Data register
      0..7  => re,       //= reads return the contents of the receive data register,
                         //= writes go to the transmit data register.
    },

    0x8    => reg8 ma1 { //! Match address register 1
      0..7  => ma,       //= match address
    },

    0x9    => reg8 ma2 { //! Match address register 2
      0..7  => ma,       //= match address
    },

    0xa    => reg8 c4 {  //! Control register 4
      0..4  => brfa,     //= baud rate fine adjust
      5     => m10,      //= 10-bit mode select
      6     => maen2,    //= match address 2 enable
      7     => maen1,    //= match address 1 enable
    },

    0xb    => reg8 c5 {  //! Control register 5
      5     => rdmas,    //= receiver full DMA select
      7     => tdmas,    //= transmitter DMA select
    },

    0xc    => reg8 ed {  //! Extended data register
      6     => paritye: ro, //= The current received data word has a parity error
      7     => noisy: ro,   //= The current received data word was received with noise
    },

    0xd    => reg8 modem { //! Modem register
      0     => txctse,   //= transmitter clear-to-send enable
      1     => txrese,   //= transmitter request-to-send enable
      2     => txrtspol, //= transmitter request-to-send polarity
      3     => rxrtse,   //= receiver request-to-send polarity
    },

    0xe    => reg8 ir {  //! Infrared register
      0..1  => tnp {     //! transmitter narrow pulse
        0  => PULSE_3_16,
        1  => PULSE_1_16,
        2  => PULSE_1_32,
        3  => PULSE_1_4,
      },
      2     => iren,     //= infrared enable
    },

    0x10   =>  reg8 pfifo { //! FIFO parameters
      0..2  => rxfifosize: ro, //= receive FIFO buffer depth
      3     => rxfe,           //= receive FIFO enable
      4..6  => txfifosize: ro, //= transmit FIFO buffer depth
      7     => txfe,           //= transmit FIFO enable
    },

    0x11   => reg8 cfifo { //! FIFO control
      0     => rxufe,        //= receive FIFO underflow interrupt enable
      1     => txofe,        //= transmit FIFO overflow interrupt enable
      2     => rxofe,        //= receive FIFO overflow interrupt enable
      6     => rxflush: wo,  //= flush receive FIFO
      7     => txflush: wo,  //= flush transmit FIFO
    },

    0x12   => reg8 sfifo { //! FIFO status
      0     => rxuf,         //= recieve FIFO underflow flag
      1     => txof,         //= transmit FIFO overflow flag
      2     => rxof,         //= recieve FIFO overflow flag
      6     => rxempt: ro,   //= recieve FIFO empty flag
      7     => txempt: ro,   //= transmit FIFO empty flag
    },

    0x13   => reg8 twfifo { //! Transmit FIFO watermark
      0..7  => txwater,      //= number of data words to transmit before generating
                             //= an interrupt or DMA request
    },

    0x14   => reg8 tcfifo { //! Transmit FIFO count
      0..7  => txcount,      //= number of data words in the transmit FIFO buffer.
    }

    0x15   => reg8 rwfifo { //! Receive FIFO watermark
      0..7  => rxwater,      //= number of data words to receive before generating
                             //= an interrupt or DMA request
    },

    0x16   => reg8 rcfifo { //! Receive FIFO count
      0..7  => rxcount,      //= number of data words in the receive FIFO buffer.
    },

    // FIXME(bgamari): Specialized registers omitted
  })

  impl UART_c1_m {
    /// UART data word length flag value from bit count
    pub fn from_u8(val: u8) -> UART_c1_m {
      match val {
        8 => DataBits8,
        9 => DataBits9,
        _ => unsafe { abort() },
      }
    }
  }

  extern {
    #[link_name="k20_iomem_UART0"] pub static UART0: UART;
    #[link_name="k20_iomem_UART1"] pub static UART1: UART;
    #[link_name="k20_iomem_UART2"] pub static UART2: UART;
  }
}
<|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 ipc_channel::ipc;
use msg::constellation_msg::PipelineId;
use net::resource_thread::new_core_resource_thread;
use net_traits::{CoreResourceMsg, LoadConsumer, LoadContext, LoadData};
use net_traits::{LoadOrigin, NetworkError, ProgressMsg, ReferrerPolicy};
use net_traits::hosts::{host_replacement, parse_hostsfile};
use profile_traits::time::ProfilerChan;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::mpsc::channel;

fn ip(s: &str) -> IpAddr {
    s.parse().unwrap()
}

struct ResourceTest;

impl LoadOrigin for ResourceTest {
    fn referrer_url(&self) -> Option<ServoUrl> {
        None
    }
    fn referrer_policy(&self) -> Option<ReferrerPolicy> {
        None
    }
    fn pipeline_id(&self) -> Option<PipelineId> {
        None
    }
}

#[test]
fn test_exit() {
    let (tx, _rx) = ipc::channel().unwrap();
    let (sender, receiver) = ipc::channel().unwrap();
    let (resource_thread, _) = new_core_resource_thread(
        "".into(), None, ProfilerChan(tx), None);
    resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap();
    receiver.recv().unwrap();
}

#[test]
fn test_parse_hostsfile() {
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com\n127.0.0.2 servo.test.server";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_parse_malformed_hostsfile() {
    let mock_hosts_file_content = "malformed file\n127.0.0.1 foo.bar.com\nservo.test.server 127.0.0.1";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(1, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_line_comment() {
    let mock_hosts_file_content = "# this is a line comment\n127.0.0.1 foo.bar.com\n# anothercomment";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(1, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_end_of_line_comment() {
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com # line ending comment\n127.0.0.2 servo.test.server #comment";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_parse_hostsfile_with_2_hostnames_for_1_address() {
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com baz.bar.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("baz.bar.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_4_hostnames_for_1_address() {
    let mock_hosts_file_content = "127.0.0.1 moz.foo.com moz.bar.com moz.baz.com moz.moz.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(4, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.foo.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.bar.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.baz.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.moz.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_tabs_instead_spaces() {
    let mock_hosts_file_content = "127.0.0.1\tfoo.bar.com\n127.0.0.2\tservo.test.server";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_parse_hostsfile_with_valid_ipv4_addresses()
{
    let mock_hosts_file_content =
        "255.255.255.255 foo.bar.com\n169.0.1.201 servo.test.server\n192.168.5.0 servo.foo.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(3, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_invalid_ipv4_addresses()
{
    let mock_hosts_file_content = "256.255.255.255 foo.bar.com\n169.0.1000.201 servo.test.server \
                                   \n192.168.5.500 servo.foo.com\n192.abc.100.2 test.servo.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(0, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_valid_ipv6_addresses()
{
    let mock_hosts_file_content = "2001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
                                   2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n\
                                   2001:db8::ff00:42:8329 foo.moz.com moz.moz.com\n\
                                   0000:0000:0000:0000:0000:0000:0000:0001 bar.moz.com\n\
                                   ::1 foo.bar.baz baz.foo.com\n\
                                   2001:0DB8:85A3:0042:1000:8A2E:0370:7334 baz.bar.moz\n\
                                   :: unspecified.moz.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(9, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_invalid_ipv6_addresses()
{
    let mock_hosts_file_content = "12001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
                                   2001:zdb8:0:0:0:gg00:42:t329 moz.foo.com\n\
                                   2002:0DB8:85A3:0042:1000:8A2E:0370:7334/1289 baz3.bar.moz";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(0, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_end_of_line_whitespace()
{
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com \n\
                                   2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n \
                                   127.0.0.2 servo.test.server ";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(3, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("2001:db8:0:0:0:ff00:42:8329"), *hosts_table.get("moz.foo.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_replace_hosts() {
    let mut host_table = HashMap::new();
    host_table.insert("foo.bar.com".to_owned(), ip("127.0.0.1"));
    host_table.insert("servo.test.server".to_owned(), ip("127.0.0.2"));

    let url = ServoUrl::parse("http://foo.bar.com:8000/foo").unwrap();
    assert_eq!(host_replacement(&host_table, &url).host_str().unwrap(), "127.0.0.1");

    let url = ServoUrl::parse("http://servo.test.server").unwrap();
    assert_eq!(host_replacement(&host_table, &url).host_str().unwrap(), "127.0.0.2");

    let url = ServoUrl::parse("http://a.foo.bar.com").unwrap();
    assert_eq!(host_replacement(&host_table, &url).host_str().unwrap(), "a.foo.bar.com");
}

#[test]
fn test_cancelled_listener() {
    use std::io::Write;
    use std::net::TcpListener;
    use std::thread;

    // http_loader always checks for headers in the response
    let header = vec!["HTTP/1.1 200 OK",
                      "Server: test-server",
                      "Content-Type: text/plain",
                      "\r\n"];
    let body = vec!["Yay!", "We're doomed!"];

    // Setup a TCP server to which requests are made
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let port = listener.local_addr().unwrap().port();
    let (body_sender, body_receiver) = channel();
    thread::spawn(move || {
        if let Ok((mut stream, _)) = listener.accept() {
            // immediately stream the headers once the connection has been established
            let _ = stream.write(header.join("\r\n").as_bytes());
            // wait for the main thread to send the body, so as to ensure that we're
            // doing everything sequentially
            let body_vec: Vec<&str> = body_receiver.recv().unwrap();
            let _ = stream.write(body_vec.join("\r\n").as_bytes());
        }
    });

    let (tx, _rx) = ipc::channel().unwrap();
    let (exit_sender, exit_receiver) = ipc::channel().unwrap();
    let (resource_thread, _) = new_core_resource_thread(
        "".into(), None, ProfilerChan(tx), None);
    let (sender, receiver) = ipc::channel().unwrap();
    let (id_sender, id_receiver) = ipc::channel().unwrap();
    let (sync_sender, sync_receiver) = ipc::channel().unwrap();
    let url = ServoUrl::parse(&format!("http://127.0.0.1:{}", port)).unwrap();

    resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, &ResourceTest),
                                        LoadConsumer::Channel(sender),
                                        Some(id_sender))).unwrap();
    // get the `ResourceId` and send a cancel message, which should stop the loading loop
    let res_id = id_receiver.recv().unwrap();
    resource_thread.send(CoreResourceMsg::Cancel(res_id)).unwrap();
    // synchronize with the resource_thread loop, so that we don't simply send everything at once!
    resource_thread.send(CoreResourceMsg::Synchronize(sync_sender)).unwrap();
    let _ = sync_receiver.recv();
    // now, let's send the body, because the connection is still active and data would be loaded
    // (but, the loading has been cancelled)
    let _ = body_sender.send(body);
    let response = receiver.recv().unwrap();
    assert_eq!(response.progress_port.recv().unwrap(),
               ProgressMsg::Done(Err(NetworkError::LoadCancelled)));
    resource_thread.send(CoreResourceMsg::Exit(exit_sender)).unwrap();
    exit_receiver.recv().unwrap();
}
<commit_msg>Remove a test for the CoreResourceMsg::Cancel message.<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 ipc_channel::ipc;
use net::resource_thread::new_core_resource_thread;
use net_traits::CoreResourceMsg;
use net_traits::hosts::{host_replacement, parse_hostsfile};
use profile_traits::time::ProfilerChan;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::net::IpAddr;

fn ip(s: &str) -> IpAddr {
    s.parse().unwrap()
}

#[test]
fn test_exit() {
    let (tx, _rx) = ipc::channel().unwrap();
    let (sender, receiver) = ipc::channel().unwrap();
    let (resource_thread, _) = new_core_resource_thread(
        "".into(), None, ProfilerChan(tx), None);
    resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap();
    receiver.recv().unwrap();
}

#[test]
fn test_parse_hostsfile() {
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com\n127.0.0.2 servo.test.server";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_parse_malformed_hostsfile() {
    let mock_hosts_file_content = "malformed file\n127.0.0.1 foo.bar.com\nservo.test.server 127.0.0.1";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(1, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_line_comment() {
    let mock_hosts_file_content = "# this is a line comment\n127.0.0.1 foo.bar.com\n# anothercomment";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(1, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_end_of_line_comment() {
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com # line ending comment\n127.0.0.2 servo.test.server #comment";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_parse_hostsfile_with_2_hostnames_for_1_address() {
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com baz.bar.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("baz.bar.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_4_hostnames_for_1_address() {
    let mock_hosts_file_content = "127.0.0.1 moz.foo.com moz.bar.com moz.baz.com moz.moz.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(4, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.foo.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.bar.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.baz.com").unwrap());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("moz.moz.com").unwrap());
}

#[test]
fn test_parse_hostsfile_with_tabs_instead_spaces() {
    let mock_hosts_file_content = "127.0.0.1\tfoo.bar.com\n127.0.0.2\tservo.test.server";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(2, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_parse_hostsfile_with_valid_ipv4_addresses()
{
    let mock_hosts_file_content =
        "255.255.255.255 foo.bar.com\n169.0.1.201 servo.test.server\n192.168.5.0 servo.foo.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(3, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_invalid_ipv4_addresses()
{
    let mock_hosts_file_content = "256.255.255.255 foo.bar.com\n169.0.1000.201 servo.test.server \
                                   \n192.168.5.500 servo.foo.com\n192.abc.100.2 test.servo.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(0, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_valid_ipv6_addresses()
{
    let mock_hosts_file_content = "2001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
                                   2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n\
                                   2001:db8::ff00:42:8329 foo.moz.com moz.moz.com\n\
                                   0000:0000:0000:0000:0000:0000:0000:0001 bar.moz.com\n\
                                   ::1 foo.bar.baz baz.foo.com\n\
                                   2001:0DB8:85A3:0042:1000:8A2E:0370:7334 baz.bar.moz\n\
                                   :: unspecified.moz.com";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(9, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_invalid_ipv6_addresses()
{
    let mock_hosts_file_content = "12001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
                                   2001:zdb8:0:0:0:gg00:42:t329 moz.foo.com\n\
                                   2002:0DB8:85A3:0042:1000:8A2E:0370:7334/1289 baz3.bar.moz";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(0, hosts_table.len());
}

#[test]
fn test_parse_hostsfile_with_end_of_line_whitespace()
{
    let mock_hosts_file_content = "127.0.0.1 foo.bar.com \n\
                                   2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n \
                                   127.0.0.2 servo.test.server ";
    let hosts_table = parse_hostsfile(mock_hosts_file_content);
    assert_eq!(3, hosts_table.len());
    assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
    assert_eq!(ip("2001:db8:0:0:0:ff00:42:8329"), *hosts_table.get("moz.foo.com").unwrap());
    assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
}

#[test]
fn test_replace_hosts() {
    let mut host_table = HashMap::new();
    host_table.insert("foo.bar.com".to_owned(), ip("127.0.0.1"));
    host_table.insert("servo.test.server".to_owned(), ip("127.0.0.2"));

    let url = ServoUrl::parse("http://foo.bar.com:8000/foo").unwrap();
    assert_eq!(host_replacement(&host_table, &url).host_str().unwrap(), "127.0.0.1");

    let url = ServoUrl::parse("http://servo.test.server").unwrap();
    assert_eq!(host_replacement(&host_table, &url).host_str().unwrap(), "127.0.0.2");

    let url = ServoUrl::parse("http://a.foo.bar.com").unwrap();
    assert_eq!(host_replacement(&host_table, &url).host_str().unwrap(), "a.foo.bar.com");
}
<|endoftext|>


<commit_before>use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{
    Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind
};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(visitor.mbcx.mir);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
}

impl GatherUsedMutsVisitor<'_, '_, '_, '_> {
    fn remove_never_initialized_mut_locals(&mut self, into: &Place) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        if let Some(local) = into.base_local() {
            debug!(
                "visit_statement: statement={:?} local={:?} \
                 never_initialized_mut_locals={:?}",
                statement, local, self.never_initialized_mut_locals
            );
            let _ = self.never_initialized_mut_locals.remove(&local);
        }
    }
}

impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
    fn visit_terminator_kind(
        &mut self,
        kind: &TerminatorKind<'tcx>,
        _location: Location,
    ) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(&into);
            },
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(&location);
            },
            _ => {},
        }
    }

    fn visit_statement(
        &mut self,
        statement: &Statement<'tcx>,
        _location: Location,
    ) {
        match &statement.kind {
            StatementKind::Assign(into, _) => {
                self.remove_never_initialized_mut_locals(into);
            },
            _ => {},
        }
    }

    fn visit_local(
        &mut self,
        local: &Local,
        place_context: PlaceContext,
        location: Location,
    ) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Place::Base(PlaceBase::Local(user_local)) = path.place {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<commit_msg>Move debug statement into statement visitor<commit_after>use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{
    Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind
};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(visitor.mbcx.mir);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
}

impl GatherUsedMutsVisitor<'_, '_, '_, '_> {
    fn remove_never_initialized_mut_locals(&mut self, into: &Place) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        if let Some(local) = into.base_local() {
            let _ = self.never_initialized_mut_locals.remove(&local);
        }
    }
}

impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
    fn visit_terminator_kind(
        &mut self,
        kind: &TerminatorKind<'tcx>,
        _location: Location,
    ) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(&into);
            },
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(&location);
            },
            _ => {},
        }
    }

    fn visit_statement(
        &mut self,
        statement: &Statement<'tcx>,
        _location: Location,
    ) {
        match &statement.kind {
            StatementKind::Assign(into, _) => {
                if let Some(local) = into.base_local() {
                    debug!(
                        "visit_statement: statement={:?} local={:?} \
                         never_initialized_mut_locals={:?}",
                        statement, local, self.never_initialized_mut_locals
                    );
                }
                self.remove_never_initialized_mut_locals(into);
            },
            _ => {},
        }
    }

    fn visit_local(
        &mut self,
        local: &Local,
        place_context: PlaceContext,
        location: Location,
    ) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Place::Base(PlaceBase::Local(user_local)) = path.place {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Branch relaxation and offset computation.
//!
//! # EBB header offsets
//!
//! Before we can generate binary machine code for branch instructions, we need to know the final
//! offsets of all the EBB headers in the function. This information is encoded in the
//! `func.offsets` table.
//!
//! # Branch relaxation
//!
//! Branch relaxation is the process of ensuring that all branches in the function have enough
//! range to encode their destination. It is common to have multiple branch encodings in an ISA.
//! For example, x86 branches can have either an 8-bit or a 32-bit displacement.
//!
//! On RISC architectures, it can happen that conditional branches have a shorter range than
//! unconditional branches:
//!
//! ```cton
//!     brz v1, ebb17
//! ```
//!
//! can be transformed into:
//!
//! ```cton
//!     brnz v1, ebb23
//!     jump ebb17
//! ebb23:
//! ```

use binemit::CodeOffset;
use cursor::{Cursor, FuncCursor};
use ir::{Function, InstructionData, Opcode};
use isa::{EncInfo, TargetIsa};
use iterators::IteratorExtras;
use timing;
use CodegenResult;

/// Relax branches and compute the final layout of EBB headers in `func`.
///
/// Fill in the `func.offsets` table so the function is ready for binary emission.
pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
    let _tt = timing::relax_branches();

    let encinfo = isa.encoding_info();

    // Clear all offsets so we can recognize EBBs that haven't been visited yet.
    func.offsets.clear();
    func.offsets.resize(func.dfg.num_ebbs());

    // Start by inserting fall through instructions.
    fallthroughs(func);

    let mut offset = 0;

    // The relaxation algorithm iterates to convergence.
    let mut go_again = true;
    while go_again {
        go_again = false;
        offset = 0;

        // Visit all instructions in layout order
        let mut cur = FuncCursor::new(func);
        while let Some(ebb) = cur.next_ebb() {
            // Record the offset for `ebb` and make sure we iterate until offsets are stable.
            if cur.func.offsets[ebb] != offset {
                debug_assert!(
                    cur.func.offsets[ebb] < offset,
                    "Code shrinking during relaxation"
                );
                cur.func.offsets[ebb] = offset;
                go_again = true;
            }

            while let Some(inst) = cur.next_inst() {
                let enc = cur.func.encodings[inst];
                let size = encinfo.bytes(enc);

                // See if this might be a branch that is out of range.
                if let Some(range) = encinfo.branch_range(enc) {
                    if let Some(dest) = cur.func.dfg[inst].branch_destination() {
                        let dest_offset = cur.func.offsets[dest];
                        // This could be an out-of-range branch.
                        // Relax it unless the destination offset has not been computed yet.
                        if !range.contains(offset, dest_offset)
                            && (dest_offset != 0 || Some(dest) == cur.func.layout.entry_block())
                        {
                            offset += relax_branch(&mut cur, offset, dest_offset, &encinfo, isa);
                            continue;
                        }
                    }
                }

                offset += size;
            }
        }
    }

    Ok(offset)
}

/// Convert `jump` instructions to `fallthrough` instructions where possible and verify that any
/// existing `fallthrough` instructions are correct.
fn fallthroughs(func: &mut Function) {
    for (ebb, succ) in func.layout.ebbs().adjacent_pairs() {
        let term = func.layout.last_inst(ebb).expect("EBB has no terminator.");
        if let InstructionData::Jump {
            ref mut opcode,
            destination,
            ..
        } = func.dfg[term]
        {
            match *opcode {
                Opcode::Fallthrough => {
                    // Somebody used a fall-through instruction before the branch relaxation pass.
                    // Make sure it is correct, i.e. the destination is the layout successor.
                    debug_assert_eq!(destination, succ, "Illegal fall-through in {}", ebb)
                }
                Opcode::Jump => {
                    // If this is a jump to the successor EBB, change it to a fall-through.
                    if destination == succ {
                        *opcode = Opcode::Fallthrough;
                        func.encodings[term] = Default::default();
                    }
                }
                _ => {}
            }
        }
    }
}

/// Relax the branch instruction at `pos` so it can cover the range `offset - dest_offset`.
///
/// Return the size of the replacement instructions up to and including the location where `pos` is
/// left.
fn relax_branch(
    cur: &mut FuncCursor,
    offset: CodeOffset,
    dest_offset: CodeOffset,
    encinfo: &EncInfo,
    isa: &TargetIsa,
) -> CodeOffset {
    let inst = cur.current_inst().unwrap();
    dbg!(
        "Relaxing [{}] {} for {:#x}-{:#x} range",
        encinfo.display(cur.func.encodings[inst]),
        cur.func.dfg.display_inst(inst, isa),
        offset,
        dest_offset
    );

    // Pick the first encoding that can handle the branch range.
    let dfg = &cur.func.dfg;
    let ctrl_type = dfg.ctrl_typevar(inst);
    if let Some(enc) = isa.legal_encodings(cur.func, &dfg[inst], ctrl_type)
        .find(|&enc| {
            let range = encinfo.branch_range(enc).expect("Branch with no range");
            if !range.contains(offset, dest_offset) {
                dbg!("  trying [{}]: out of range", encinfo.display(enc));
                false
            } else if encinfo.operand_constraints(enc)
                != encinfo.operand_constraints(cur.func.encodings[inst])
            {
                // Conservatively give up if the encoding has different constraints
                // than the original, so that we don't risk picking a new encoding
                // which the existing operands don't satisfy. We can't check for
                // validity directly because we don't have a RegDiversions active so
                // we don't know which registers are actually in use.
                dbg!("  trying [{}]: constraints differ", encinfo.display(enc));
                false
            } else {
                dbg!("  trying [{}]: OK", encinfo.display(enc));
                true
            }
        }) {
        cur.func.encodings[inst] = enc;
        return encinfo.bytes(enc);
    }

    // Note: On some RISC ISAs, conditional branches have shorter range than unconditional
    // branches, so one way of extending the range of a conditional branch is to invert its
    // condition and make it branch over an unconditional jump which has the larger range.
    //
    // Splitting the EBB is problematic this late because there may be register diversions in
    // effect across the conditional branch, and they can't survive the control flow edge to a new
    // EBB. We have two options for handling that:
    //
    // 1. Set a flag on the new EBB that indicates it wants the preserve the register diversions of
    //    its layout predecessor, or
    // 2. Use an encoding macro for the branch-over-jump pattern so we don't need to split the EBB.
    //
    // It seems that 1. would allow us to share code among RISC ISAs that need this.
    //
    // We can't allow register diversions to survive from the layout predecessor because the layout
    // predecessor could contain kill points for some values that are live in this EBB, and
    // diversions are not automatically cancelled when the live range of a value ends.

    // This assumes solution 2. above:
    panic!("No branch in range for {:#x}-{:#x}", offset, dest_offset);
}
<commit_msg>Keep a comment in sync with the code.<commit_after>//! Branch relaxation and offset computation.
//!
//! # EBB header offsets
//!
//! Before we can generate binary machine code for branch instructions, we need to know the final
//! offsets of all the EBB headers in the function. This information is encoded in the
//! `func.offsets` table.
//!
//! # Branch relaxation
//!
//! Branch relaxation is the process of ensuring that all branches in the function have enough
//! range to encode their destination. It is common to have multiple branch encodings in an ISA.
//! For example, x86 branches can have either an 8-bit or a 32-bit displacement.
//!
//! On RISC architectures, it can happen that conditional branches have a shorter range than
//! unconditional branches:
//!
//! ```cton
//!     brz v1, ebb17
//! ```
//!
//! can be transformed into:
//!
//! ```cton
//!     brnz v1, ebb23
//!     jump ebb17
//! ebb23:
//! ```

use binemit::CodeOffset;
use cursor::{Cursor, FuncCursor};
use ir::{Function, InstructionData, Opcode};
use isa::{EncInfo, TargetIsa};
use iterators::IteratorExtras;
use timing;
use CodegenResult;

/// Relax branches and compute the final layout of EBB headers in `func`.
///
/// Fill in the `func.offsets` table so the function is ready for binary emission.
pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
    let _tt = timing::relax_branches();

    let encinfo = isa.encoding_info();

    // Clear all offsets so we can recognize EBBs that haven't been visited yet.
    func.offsets.clear();
    func.offsets.resize(func.dfg.num_ebbs());

    // Start by inserting fall through instructions.
    fallthroughs(func);

    let mut offset = 0;

    // The relaxation algorithm iterates to convergence.
    let mut go_again = true;
    while go_again {
        go_again = false;
        offset = 0;

        // Visit all instructions in layout order
        let mut cur = FuncCursor::new(func);
        while let Some(ebb) = cur.next_ebb() {
            // Record the offset for `ebb` and make sure we iterate until offsets are stable.
            if cur.func.offsets[ebb] != offset {
                debug_assert!(
                    cur.func.offsets[ebb] < offset,
                    "Code shrinking during relaxation"
                );
                cur.func.offsets[ebb] = offset;
                go_again = true;
            }

            while let Some(inst) = cur.next_inst() {
                let enc = cur.func.encodings[inst];
                let size = encinfo.bytes(enc);

                // See if this might be a branch that is out of range.
                if let Some(range) = encinfo.branch_range(enc) {
                    if let Some(dest) = cur.func.dfg[inst].branch_destination() {
                        let dest_offset = cur.func.offsets[dest];
                        // This could be an out-of-range branch.
                        // Relax it unless the destination offset has not been computed yet.
                        if !range.contains(offset, dest_offset)
                            && (dest_offset != 0 || Some(dest) == cur.func.layout.entry_block())
                        {
                            offset += relax_branch(&mut cur, offset, dest_offset, &encinfo, isa);
                            continue;
                        }
                    }
                }

                offset += size;
            }
        }
    }

    Ok(offset)
}

/// Convert `jump` instructions to `fallthrough` instructions where possible and verify that any
/// existing `fallthrough` instructions are correct.
fn fallthroughs(func: &mut Function) {
    for (ebb, succ) in func.layout.ebbs().adjacent_pairs() {
        let term = func.layout.last_inst(ebb).expect("EBB has no terminator.");
        if let InstructionData::Jump {
            ref mut opcode,
            destination,
            ..
        } = func.dfg[term]
        {
            match *opcode {
                Opcode::Fallthrough => {
                    // Somebody used a fall-through instruction before the branch relaxation pass.
                    // Make sure it is correct, i.e. the destination is the layout successor.
                    debug_assert_eq!(destination, succ, "Illegal fall-through in {}", ebb)
                }
                Opcode::Jump => {
                    // If this is a jump to the successor EBB, change it to a fall-through.
                    if destination == succ {
                        *opcode = Opcode::Fallthrough;
                        func.encodings[term] = Default::default();
                    }
                }
                _ => {}
            }
        }
    }
}

/// Relax the branch instruction at `cur` so it can cover the range `offset - dest_offset`.
///
/// Return the size of the replacement instructions up to and including the location where `cur` is
/// left.
fn relax_branch(
    cur: &mut FuncCursor,
    offset: CodeOffset,
    dest_offset: CodeOffset,
    encinfo: &EncInfo,
    isa: &TargetIsa,
) -> CodeOffset {
    let inst = cur.current_inst().unwrap();
    dbg!(
        "Relaxing [{}] {} for {:#x}-{:#x} range",
        encinfo.display(cur.func.encodings[inst]),
        cur.func.dfg.display_inst(inst, isa),
        offset,
        dest_offset
    );

    // Pick the first encoding that can handle the branch range.
    let dfg = &cur.func.dfg;
    let ctrl_type = dfg.ctrl_typevar(inst);
    if let Some(enc) = isa.legal_encodings(cur.func, &dfg[inst], ctrl_type)
        .find(|&enc| {
            let range = encinfo.branch_range(enc).expect("Branch with no range");
            if !range.contains(offset, dest_offset) {
                dbg!("  trying [{}]: out of range", encinfo.display(enc));
                false
            } else if encinfo.operand_constraints(enc)
                != encinfo.operand_constraints(cur.func.encodings[inst])
            {
                // Conservatively give up if the encoding has different constraints
                // than the original, so that we don't risk picking a new encoding
                // which the existing operands don't satisfy. We can't check for
                // validity directly because we don't have a RegDiversions active so
                // we don't know which registers are actually in use.
                dbg!("  trying [{}]: constraints differ", encinfo.display(enc));
                false
            } else {
                dbg!("  trying [{}]: OK", encinfo.display(enc));
                true
            }
        }) {
        cur.func.encodings[inst] = enc;
        return encinfo.bytes(enc);
    }

    // Note: On some RISC ISAs, conditional branches have shorter range than unconditional
    // branches, so one way of extending the range of a conditional branch is to invert its
    // condition and make it branch over an unconditional jump which has the larger range.
    //
    // Splitting the EBB is problematic this late because there may be register diversions in
    // effect across the conditional branch, and they can't survive the control flow edge to a new
    // EBB. We have two options for handling that:
    //
    // 1. Set a flag on the new EBB that indicates it wants the preserve the register diversions of
    //    its layout predecessor, or
    // 2. Use an encoding macro for the branch-over-jump pattern so we don't need to split the EBB.
    //
    // It seems that 1. would allow us to share code among RISC ISAs that need this.
    //
    // We can't allow register diversions to survive from the layout predecessor because the layout
    // predecessor could contain kill points for some values that are live in this EBB, and
    // diversions are not automatically cancelled when the live range of a value ends.

    // This assumes solution 2. above:
    panic!("No branch in range for {:#x}-{:#x}", offset, dest_offset);
}
<|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/. */

//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes
//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via
//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from
//! escaping.
//!
//! As a security wrapper is only as good as its whitelist, be careful when adding operations to
//! this list. The cardinal rules are:
//!
//! 1. Layout is not allowed to mutate the DOM.
//!
//! 2. Layout is not allowed to see anything with `LayoutDom` in the name, because it could hang
//!    onto these objects and cause use-after-free.
//!
//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you
//! will race and cause spurious thread failure. (Note that I do not believe these races are
//! exploitable, but they'll result in brokenness nonetheless.)
//!
//! Rules of the road for this file:
//!
//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags.
//!
//!   o Instead of `get_attr()`, use `.get_attr_val_for_layout()`.
//!
//!   o Instead of `html_element_in_html_document()`, use
//!     `html_element_in_html_document_for_layout()`.

#![allow(unsafe_code)]

use atomic_refcell::{AtomicRef, AtomicRefMut};
use data::{LayoutData, LayoutDataFlags, StyleAndLayoutData};
use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use script_layout_interface::wrapper_traits::GetLayoutData;
use style::computed_values::content::{self, ContentItem};
use style::dom::{NodeInfo, TNode};
use style::selector_parser::RestyleDamage;

pub trait LayoutNodeLayoutData {
    /// Similar to borrow_data*, but returns the full PersistentLayoutData rather
    /// than only the style::data::ElementData.
    fn borrow_layout_data(&self) -> Option<AtomicRef<LayoutData>>;
    fn mutate_layout_data(&self) -> Option<AtomicRefMut<LayoutData>>;
    fn flow_debug_id(self) -> usize;
}

impl<T: GetLayoutData> LayoutNodeLayoutData for T {
    fn borrow_layout_data(&self) -> Option<AtomicRef<LayoutData>> {
        self.get_raw_data().map(|d| d.layout_data.borrow())
    }

    fn mutate_layout_data(&self) -> Option<AtomicRefMut<LayoutData>> {
        self.get_raw_data().map(|d| d.layout_data.borrow_mut())
    }

    fn flow_debug_id(self) -> usize {
        self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id())
    }
}

pub trait GetRawData {
    fn get_raw_data(&self) -> Option<&StyleAndLayoutData>;
}

impl<T: GetLayoutData> GetRawData for T {
    fn get_raw_data(&self) -> Option<&StyleAndLayoutData> {
        self.get_style_and_layout_data().map(|opaque| {
            let container = opaque.ptr.get() as *mut StyleAndLayoutData;
            unsafe { &*container }
        })
    }
}

pub trait ThreadSafeLayoutNodeHelpers {
    /// Returns the layout data flags for this node.
    fn flags(self) -> LayoutDataFlags;

    /// Adds the given flags to this node.
    fn insert_flags(self, new_flags: LayoutDataFlags);

    /// Removes the given flags from this node.
    fn remove_flags(self, flags: LayoutDataFlags);

    /// If this is a text node, generated content, or a form element, copies out
    /// its content. Otherwise, panics.
    ///
    /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this.
    fn text_content(&self) -> TextContent;

    /// The RestyleDamage from any restyling, or RestyleDamage::rebuild_and_reflow() if this
    /// is the first time layout is visiting this node. We implement this here, rather than
    /// with the rest of the wrapper layer, because we need layout code to determine whether
    /// layout has visited the node.
    fn restyle_damage(self) -> RestyleDamage;
}

impl<T: ThreadSafeLayoutNode> ThreadSafeLayoutNodeHelpers for T {
    fn flags(self) -> LayoutDataFlags {
            self.borrow_layout_data().as_ref().unwrap().flags
    }

    fn insert_flags(self, new_flags: LayoutDataFlags) {
        self.mutate_layout_data().unwrap().flags.insert(new_flags);
    }

    fn remove_flags(self, flags: LayoutDataFlags) {
        self.mutate_layout_data().unwrap().flags.remove(flags);
    }

    fn text_content(&self) -> TextContent {
        if self.get_pseudo_element_type().is_replaced_content() {
            let style = self.as_element().unwrap().resolved_style();

            return match style.as_ref().get_counters().content {
                content::T::Items(ref value) if !value.is_empty() => {
                    TextContent::GeneratedContent((*value).clone())
                }
                _ => TextContent::GeneratedContent(vec![]),
            };
        }

        return TextContent::Text(self.node_text_content());
    }

    fn restyle_damage(self) -> RestyleDamage {
        // We need the underlying node to potentially access the parent in the
        // case of text nodes. This is safe as long as we don't let the parent
        // escape and never access its descendants.
        let mut node = unsafe { self.unsafe_get() };

        // If this is a text node, use the parent element, since that's what
        // controls our style.
        if node.is_text_node() {
            node = node.parent_node().unwrap();
            debug_assert!(node.is_element());
        }

        let damage = {
            let data = node.get_raw_data().unwrap();

            if !data.layout_data.borrow().flags.contains(::data::LayoutDataFlags::HAS_BEEN_TRAVERSED) {
                // We're reflowing a node that was styled for the first time and
                // has never been visited by layout. Return rebuild_and_reflow,
                // because that's what the code expects.
                RestyleDamage::rebuild_and_reflow()
            } else {
                data.style_data.element_data.borrow().damage
            }
        };

        damage
    }

}

pub enum TextContent {
    Text(String),
    GeneratedContent(Vec<ContentItem>),
}

impl TextContent {
    pub fn is_empty(&self) -> bool {
        match *self {
            TextContent::Text(_) => false,
            TextContent::GeneratedContent(ref content) => content.is_empty(),
        }
    }
}
<commit_msg>layout: Fixup indentation of ThreadSafeLayoutNodeHelpers::flags.<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/. */

//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes
//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via
//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from
//! escaping.
//!
//! As a security wrapper is only as good as its whitelist, be careful when adding operations to
//! this list. The cardinal rules are:
//!
//! 1. Layout is not allowed to mutate the DOM.
//!
//! 2. Layout is not allowed to see anything with `LayoutDom` in the name, because it could hang
//!    onto these objects and cause use-after-free.
//!
//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you
//! will race and cause spurious thread failure. (Note that I do not believe these races are
//! exploitable, but they'll result in brokenness nonetheless.)
//!
//! Rules of the road for this file:
//!
//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags.
//!
//!   o Instead of `get_attr()`, use `.get_attr_val_for_layout()`.
//!
//!   o Instead of `html_element_in_html_document()`, use
//!     `html_element_in_html_document_for_layout()`.

#![allow(unsafe_code)]

use atomic_refcell::{AtomicRef, AtomicRefMut};
use data::{LayoutData, LayoutDataFlags, StyleAndLayoutData};
use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use script_layout_interface::wrapper_traits::GetLayoutData;
use style::computed_values::content::{self, ContentItem};
use style::dom::{NodeInfo, TNode};
use style::selector_parser::RestyleDamage;

pub trait LayoutNodeLayoutData {
    /// Similar to borrow_data*, but returns the full PersistentLayoutData rather
    /// than only the style::data::ElementData.
    fn borrow_layout_data(&self) -> Option<AtomicRef<LayoutData>>;
    fn mutate_layout_data(&self) -> Option<AtomicRefMut<LayoutData>>;
    fn flow_debug_id(self) -> usize;
}

impl<T: GetLayoutData> LayoutNodeLayoutData for T {
    fn borrow_layout_data(&self) -> Option<AtomicRef<LayoutData>> {
        self.get_raw_data().map(|d| d.layout_data.borrow())
    }

    fn mutate_layout_data(&self) -> Option<AtomicRefMut<LayoutData>> {
        self.get_raw_data().map(|d| d.layout_data.borrow_mut())
    }

    fn flow_debug_id(self) -> usize {
        self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id())
    }
}

pub trait GetRawData {
    fn get_raw_data(&self) -> Option<&StyleAndLayoutData>;
}

impl<T: GetLayoutData> GetRawData for T {
    fn get_raw_data(&self) -> Option<&StyleAndLayoutData> {
        self.get_style_and_layout_data().map(|opaque| {
            let container = opaque.ptr.get() as *mut StyleAndLayoutData;
            unsafe { &*container }
        })
    }
}

pub trait ThreadSafeLayoutNodeHelpers {
    /// Returns the layout data flags for this node.
    fn flags(self) -> LayoutDataFlags;

    /// Adds the given flags to this node.
    fn insert_flags(self, new_flags: LayoutDataFlags);

    /// Removes the given flags from this node.
    fn remove_flags(self, flags: LayoutDataFlags);

    /// If this is a text node, generated content, or a form element, copies out
    /// its content. Otherwise, panics.
    ///
    /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this.
    fn text_content(&self) -> TextContent;

    /// The RestyleDamage from any restyling, or RestyleDamage::rebuild_and_reflow() if this
    /// is the first time layout is visiting this node. We implement this here, rather than
    /// with the rest of the wrapper layer, because we need layout code to determine whether
    /// layout has visited the node.
    fn restyle_damage(self) -> RestyleDamage;
}

impl<T: ThreadSafeLayoutNode> ThreadSafeLayoutNodeHelpers for T {
    fn flags(self) -> LayoutDataFlags {
        self.borrow_layout_data().as_ref().unwrap().flags
    }

    fn insert_flags(self, new_flags: LayoutDataFlags) {
        self.mutate_layout_data().unwrap().flags.insert(new_flags);
    }

    fn remove_flags(self, flags: LayoutDataFlags) {
        self.mutate_layout_data().unwrap().flags.remove(flags);
    }

    fn text_content(&self) -> TextContent {
        if self.get_pseudo_element_type().is_replaced_content() {
            let style = self.as_element().unwrap().resolved_style();

            return match style.as_ref().get_counters().content {
                content::T::Items(ref value) if !value.is_empty() => {
                    TextContent::GeneratedContent((*value).clone())
                }
                _ => TextContent::GeneratedContent(vec![]),
            };
        }

        return TextContent::Text(self.node_text_content());
    }

    fn restyle_damage(self) -> RestyleDamage {
        // We need the underlying node to potentially access the parent in the
        // case of text nodes. This is safe as long as we don't let the parent
        // escape and never access its descendants.
        let mut node = unsafe { self.unsafe_get() };

        // If this is a text node, use the parent element, since that's what
        // controls our style.
        if node.is_text_node() {
            node = node.parent_node().unwrap();
            debug_assert!(node.is_element());
        }

        let damage = {
            let data = node.get_raw_data().unwrap();

            if !data.layout_data.borrow().flags.contains(::data::LayoutDataFlags::HAS_BEEN_TRAVERSED) {
                // We're reflowing a node that was styled for the first time and
                // has never been visited by layout. Return rebuild_and_reflow,
                // because that's what the code expects.
                RestyleDamage::rebuild_and_reflow()
            } else {
                data.style_data.element_data.borrow().damage
            }
        };

        damage
    }

}

pub enum TextContent {
    Text(String),
    GeneratedContent(Vec<ContentItem>),
}

impl TextContent {
    pub fn is_empty(&self) -> bool {
        match *self {
            TextContent::Text(_) => false,
            TextContent::GeneratedContent(ref content) => content.is_empty(),
        }
    }
}
<|endoftext|>