<commit_before>use std::io::Read;

use mio::*;
use mio::net::TcpStream;
use nom::Err;
use rusty_ulid::Ulid;
use SessionResult;
use Readiness;
use protocol::ProtocolResult;
use socket::{SocketHandler, SocketResult};
use SessionMetrics;
use protocol::pipe::Pipe;
use pool::Checkout;
use super::parser::parse_v2_header;
use super::header::ProxyAddr;
use Protocol;
use sozu_command::ready::Ready;

#[derive(Clone,Copy)]
pub enum HeaderLen {
  V4,
  V6,
  Unix
}

pub struct ExpectProxyProtocol<Front:SocketHandler> {
  pub frontend:       Front,
  pub frontend_token: Token,
  pub request_id:     Ulid,
  pub buf:            [u8; 232],
  pub index:          usize,
  pub header_len:     HeaderLen,
  pub readiness:      Readiness,
  pub addresses:      Option<ProxyAddr>,
}

impl <Front:SocketHandler + Read>ExpectProxyProtocol<Front> {
  pub fn new(frontend: Front, frontend_token: Token, request_id: Ulid) -> Self {
    ExpectProxyProtocol {
      frontend,
      frontend_token,
      request_id,
      buf: [0; 232],
      index: 0,
      header_len: HeaderLen::V4,
      readiness: Readiness {
        interest: Ready::readable(),
        event: Ready::empty(),
      },
      addresses: None,
    }
  }

  pub fn readable(&mut self, metrics: &mut SessionMetrics) -> (ProtocolResult, SessionResult) {
    let total_len = match self.header_len {
      HeaderLen::V4   => 28,
      HeaderLen::V6   => 52,
      HeaderLen::Unix => 232,
    };

    let (sz, res) = self.frontend.socket_read(&mut self.buf[self.index..total_len]);
    trace!("FRONT proxy protocol [{:?}]: read {} bytes and res={:?}, index = {}, total_len = {}",
      self.frontend_token, sz, res, self.index, total_len);

    if sz > 0 {
      self.index += sz;

      count!("bytes_in", sz as i64);
      metrics.bin += sz;

      if self.index == self.buf.len() {
        self.readiness.interest.remove(Ready::readable());
      }
    } else {
      self.readiness.event.remove(Ready::readable());
    }

    if res == SocketResult::Error {
      error!("[{:?}] (expect proxy) front socket error, closing the connection(read {}, wrote {})", self.frontend_token, metrics.bin, metrics.bout);
      metrics.service_stop();
      incr!("proxy_protocol.errors");
      self.readiness.reset();
      return (ProtocolResult::Continue, SessionResult::CloseSession);
    }

    if res == SocketResult::WouldBlock {
      self.readiness.event.remove(Ready::readable());
    }

    match parse_v2_header(&self.buf[..self.index]) {
      Ok((rest, header)) => {
        trace!("got expect header: {:?}, rest.len() = {}", header, rest.len());
        self.addresses = Some(header.addr);
        (ProtocolResult::Upgrade, SessionResult::Continue)
      },
      Err(Err::Incomplete(_)) => {
        match self.header_len {
          HeaderLen::V4 => if self.index == 28 {
            self.header_len = HeaderLen::V6;
          },
          HeaderLen::V6 => if self.index == 52 {
            self.header_len = HeaderLen::Unix;
          },
          HeaderLen::Unix => if self.index == 232 {
            error!("[{:?}] front socket parse error, closing the connection", self.frontend_token);
            metrics.service_stop();
            incr!("proxy_protocol.errors");
            self.readiness.reset();
            return (ProtocolResult::Continue, SessionResult::CloseSession)
          }
        };
        (ProtocolResult::Continue, SessionResult::Continue)
      },
      Err(e) => {
        error!("[{:?}] front socket parse error, closing the connection: {:?}", self.frontend_token, e);
        metrics.service_stop();
        incr!("proxy_protocol.errors");
        self.readiness.reset();
        (ProtocolResult::Continue, SessionResult::CloseSession)
      }
    }
  }

  pub fn front_socket(&self) -> &TcpStream {
    self.frontend.socket_ref()
  }

  pub fn front_socket_mut(&mut self) -> &mut TcpStream {
    self.frontend.socket_mut()
  }

  pub fn readiness(&mut self) -> &mut Readiness {
    &mut self.readiness
  }

  pub fn into_pipe(self, front_buf: Checkout, back_buf: Checkout,
    backend_socket: Option<TcpStream>, backend_token: Option<Token>) -> Pipe<Front> {

    let addr = self.front_socket().peer_addr().ok();

    let mut pipe = Pipe::new(
      self.frontend,
      self.frontend_token,
      self.request_id,
      None,
      None,
      None,
      backend_socket,
      front_buf,
      back_buf,
      addr,
      Protocol::TCP
    );

    pipe.front_readiness.event = self.readiness.event;

    if let Some(backend_token) = backend_token {
      pipe.set_back_token(backend_token);
    }

    pipe
  }
}

#[cfg(test)]
mod expect_test {

  use super::*;
  use std::{sync::{Arc, Barrier}, thread::{self, JoinHandle}, net::{SocketAddr, IpAddr, Ipv4Addr}};
  use mio::net::TcpListener;
  use std::net::{TcpStream as StdTcpStream};
  use std::io::Write;
  use rusty_ulid::Ulid;

  use protocol::proxy_protocol::header::*;

  // Flow diagram of the test below
  //                [connect]   [send proxy protocol]
  //upfront proxy  ----------------------X
  //              /     |           |
  //  sozu     ---------v-----------v----X
  #[test]
  fn middleware_should_receive_proxy_protocol_header_from_an_upfront_middleware() {
    setup_test_logger!();
    let middleware_addr: SocketAddr = "127.0.0.1:3500".parse().expect("parse address error");
    let barrier = Arc::new(Barrier::new(2));

    let upfront = start_upfront_middleware(middleware_addr.clone(), barrier.clone());
    start_middleware(middleware_addr, barrier);

    upfront.join().expect("should join");
  }

  // Accept connection from an upfront proxy and expect to read a proxy protocol header in this stream.
  fn start_middleware(middleware_addr: SocketAddr, barrier: Arc<Barrier>) {
    let upfront_middleware_conn_listener = TcpListener::bind(middleware_addr).expect("could not accept upfront middleware connection");
    let session_stream;
    barrier.wait();

    // mio::TcpListener use a nonblocking mode so we have to loop on accept
    loop {
      if let Ok((stream, _addr)) = upfront_middleware_conn_listener.accept() {
        session_stream = stream;
        break;
      }
    }

    let mut session_metrics = SessionMetrics::new(None);
    let mut expect_pp = ExpectProxyProtocol::new(session_stream, Token(0),
      Ulid::generate());

    let mut res = (ProtocolResult::Continue, SessionResult::Continue);
    while res == (ProtocolResult::Continue, SessionResult::Continue) {
      res = expect_pp.readable(&mut session_metrics);
    }

    if res != (ProtocolResult::Upgrade, SessionResult::Continue) {
      panic!("Should receive a complete proxy protocol header, res = {:?}", res);
    };
  }

  // Connect to the next middleware and send a proxy protocol header
  fn start_upfront_middleware(next_middleware_addr: SocketAddr, barrier: Arc<Barrier>) -> JoinHandle<()> {
    thread::spawn(move|| {
      let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
      let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
      let proxy_protocol = HeaderV2::new(Command::Local, src_addr, dst_addr).into_bytes();

      barrier.wait();
      match StdTcpStream::connect(&next_middleware_addr) {
        Ok(mut stream) => {
          stream.write(&proxy_protocol).unwrap();
        },
        Err(e) => panic!("could not connect to the next middleware: {}", e),
      };
    })
  }
}
<commit_msg>more logs for expect proxy protocol errors<commit_after>use std::io::Read;

use mio::*;
use mio::net::TcpStream;
use nom::{Err, HexDisplay};
use rusty_ulid::Ulid;
use SessionResult;
use Readiness;
use protocol::ProtocolResult;
use socket::{SocketHandler, SocketResult};
use SessionMetrics;
use protocol::pipe::Pipe;
use pool::Checkout;
use super::parser::parse_v2_header;
use super::header::ProxyAddr;
use Protocol;
use sozu_command::ready::Ready;

#[derive(Clone,Copy)]
pub enum HeaderLen {
  V4,
  V6,
  Unix
}

pub struct ExpectProxyProtocol<Front:SocketHandler> {
  pub frontend:       Front,
  pub frontend_token: Token,
  pub request_id:     Ulid,
  pub buf:            [u8; 232],
  pub index:          usize,
  pub header_len:     HeaderLen,
  pub readiness:      Readiness,
  pub addresses:      Option<ProxyAddr>,
}

impl <Front:SocketHandler + Read>ExpectProxyProtocol<Front> {
  pub fn new(frontend: Front, frontend_token: Token, request_id: Ulid) -> Self {
    ExpectProxyProtocol {
      frontend,
      frontend_token,
      request_id,
      buf: [0; 232],
      index: 0,
      header_len: HeaderLen::V4,
      readiness: Readiness {
        interest: Ready::readable(),
        event: Ready::empty(),
      },
      addresses: None,
    }
  }

  pub fn readable(&mut self, metrics: &mut SessionMetrics) -> (ProtocolResult, SessionResult) {
    let total_len = match self.header_len {
      HeaderLen::V4   => 28,
      HeaderLen::V6   => 52,
      HeaderLen::Unix => 232,
    };

    let (sz, res) = self.frontend.socket_read(&mut self.buf[self.index..total_len]);
    trace!("FRONT proxy protocol [{:?}]: read {} bytes and res={:?}, index = {}, total_len = {}",
      self.frontend_token, sz, res, self.index, total_len);

    if sz > 0 {
      self.index += sz;

      count!("bytes_in", sz as i64);
      metrics.bin += sz;

      if self.index == self.buf.len() {
        self.readiness.interest.remove(Ready::readable());
      }
    } else {
      self.readiness.event.remove(Ready::readable());
    }

    if res == SocketResult::Error {
      error!("[{:?}] (expect proxy) front socket error, closing the connection(read {}, wrote {})", self.frontend_token, metrics.bin, metrics.bout);
      metrics.service_stop();
      incr!("proxy_protocol.errors");
      self.readiness.reset();
      return (ProtocolResult::Continue, SessionResult::CloseSession);
    }

    if res == SocketResult::WouldBlock {
      self.readiness.event.remove(Ready::readable());
    }

    match parse_v2_header(&self.buf[..self.index]) {
      Ok((rest, header)) => {
        trace!("got expect header: {:?}, rest.len() = {}", header, rest.len());
        self.addresses = Some(header.addr);
        (ProtocolResult::Upgrade, SessionResult::Continue)
      },
      Err(Err::Incomplete(_)) => {
        match self.header_len {
          HeaderLen::V4 => if self.index == 28 {
            self.header_len = HeaderLen::V6;
          },
          HeaderLen::V6 => if self.index == 52 {
            self.header_len = HeaderLen::Unix;
          },
          HeaderLen::Unix => if self.index == 232 {
            error!("[{:?}] front socket parse error, closing the connection", self.frontend_token);
            metrics.service_stop();
            incr!("proxy_protocol.errors");
            self.readiness.reset();
            return (ProtocolResult::Continue, SessionResult::CloseSession)
          }
        };
        (ProtocolResult::Continue, SessionResult::Continue)
      },
      Err(Err::Error(e)) | Err(Err::Failure(e)) => {
        error!("[{:?}] expect proxy protocol front socket parse error, closing the connection:\n{}", self.frontend_token, e.input.to_hex(16));
        metrics.service_stop();
        incr!("proxy_protocol.errors");
        self.readiness.reset();
        (ProtocolResult::Continue, SessionResult::CloseSession)
      }
    }
  }

  pub fn front_socket(&self) -> &TcpStream {
    self.frontend.socket_ref()
  }

  pub fn front_socket_mut(&mut self) -> &mut TcpStream {
    self.frontend.socket_mut()
  }

  pub fn readiness(&mut self) -> &mut Readiness {
    &mut self.readiness
  }

  pub fn into_pipe(self, front_buf: Checkout, back_buf: Checkout,
    backend_socket: Option<TcpStream>, backend_token: Option<Token>) -> Pipe<Front> {

    let addr = self.front_socket().peer_addr().ok();

    let mut pipe = Pipe::new(
      self.frontend,
      self.frontend_token,
      self.request_id,
      None,
      None,
      None,
      backend_socket,
      front_buf,
      back_buf,
      addr,
      Protocol::TCP
    );

    pipe.front_readiness.event = self.readiness.event;

    if let Some(backend_token) = backend_token {
      pipe.set_back_token(backend_token);
    }

    pipe
  }
}

#[cfg(test)]
mod expect_test {

  use super::*;
  use std::{sync::{Arc, Barrier}, thread::{self, JoinHandle}, net::{SocketAddr, IpAddr, Ipv4Addr}};
  use mio::net::TcpListener;
  use std::net::{TcpStream as StdTcpStream};
  use std::io::Write;
  use rusty_ulid::Ulid;

  use protocol::proxy_protocol::header::*;

  // Flow diagram of the test below
  //                [connect]   [send proxy protocol]
  //upfront proxy  ----------------------X
  //              /     |           |
  //  sozu     ---------v-----------v----X
  #[test]
  fn middleware_should_receive_proxy_protocol_header_from_an_upfront_middleware() {
    setup_test_logger!();
    let middleware_addr: SocketAddr = "127.0.0.1:3500".parse().expect("parse address error");
    let barrier = Arc::new(Barrier::new(2));

    let upfront = start_upfront_middleware(middleware_addr.clone(), barrier.clone());
    start_middleware(middleware_addr, barrier);

    upfront.join().expect("should join");
  }

  // Accept connection from an upfront proxy and expect to read a proxy protocol header in this stream.
  fn start_middleware(middleware_addr: SocketAddr, barrier: Arc<Barrier>) {
    let upfront_middleware_conn_listener = TcpListener::bind(middleware_addr).expect("could not accept upfront middleware connection");
    let session_stream;
    barrier.wait();

    // mio::TcpListener use a nonblocking mode so we have to loop on accept
    loop {
      if let Ok((stream, _addr)) = upfront_middleware_conn_listener.accept() {
        session_stream = stream;
        break;
      }
    }

    let mut session_metrics = SessionMetrics::new(None);
    let mut expect_pp = ExpectProxyProtocol::new(session_stream, Token(0),
      Ulid::generate());

    let mut res = (ProtocolResult::Continue, SessionResult::Continue);
    while res == (ProtocolResult::Continue, SessionResult::Continue) {
      res = expect_pp.readable(&mut session_metrics);
    }

    if res != (ProtocolResult::Upgrade, SessionResult::Continue) {
      panic!("Should receive a complete proxy protocol header, res = {:?}", res);
    };
  }

  // Connect to the next middleware and send a proxy protocol header
  fn start_upfront_middleware(next_middleware_addr: SocketAddr, barrier: Arc<Barrier>) -> JoinHandle<()> {
    thread::spawn(move|| {
      let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
      let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
      let proxy_protocol = HeaderV2::new(Command::Local, src_addr, dst_addr).into_bytes();

      barrier.wait();
      match StdTcpStream::connect(&next_middleware_addr) {
        Ok(mut stream) => {
          stream.write(&proxy_protocol).unwrap();
        },
        Err(e) => panic!("could not connect to the next middleware: {}", e),
      };
    })
  }
}
<|endoftext|>


<commit_before>
import std::vec;
import std::term;
import std::io;
import std::option;
import std::option::some;
import std::option::none;

type filename = str;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */
type filemap = @rec(filename name, uint start_pos, mutable vec[uint] lines);

type codemap = @rec(mutable vec[filemap] files);

type loc = rec(filename filename, uint line, uint col);

fn new_codemap() -> codemap {
    let vec[filemap] files = [];
    ret @rec(mutable files=files);
}

fn new_filemap(filename filename, uint start_pos) -> filemap {
    ret @rec(name=filename, start_pos=start_pos, mutable lines=[0u]);
}

fn next_line(filemap file, uint pos) { vec::push[uint](file.lines, pos); }

fn lookup_pos(codemap map, uint pos) -> loc {
    auto a = 0u;
    auto b = vec::len[filemap](map.files);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
    }
    auto f = map.files.(a);
    a = 0u;
    b = vec::len[uint](f.lines);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (f.lines.(m) > pos) { b = m; } else { a = m; }
    }
    ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
}

type span = rec(uint lo, uint hi);

fn span_to_str(&span sp, &codemap cm) -> str {
    auto lo = lookup_pos(cm, sp.lo);
    auto hi = lookup_pos(cm, sp.hi);
    ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
}

fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
                   &codemap cm) {
    auto ss = "<input>:0:0:0:0";
    alt (sp) {
        case (some(?ssp)) { ss = span_to_str(ssp, cm); }
        case (none) { }
    }
    io::stdout().write_str(ss + ": ");
    if (term::color_supported()) {
        term::fg(io::stdout().get_buf_writer(), color);
    }
    io::stdout().write_str(#fmt("%s:", kind));
    if (term::color_supported()) {
        term::reset(io::stdout().get_buf_writer());
    }
    io::stdout().write_str(#fmt(" %s\n", msg));
}

fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "warning", 11u8, cm);
}
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "error", 9u8, cm);
}
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "note", 10u8, cm);
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Hack to print lines when error spans are available.<commit_after>import std::uint;
import std::str;
import std::vec;
import std::term;
import std::io;
import std::option;
import std::option::some;
import std::option::none;

type filename = str;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */
type filemap = @rec(filename name, uint start_pos, mutable vec[uint] lines);

type codemap = @rec(mutable vec[filemap] files);

type loc = rec(filename filename, uint line, uint col);

fn new_codemap() -> codemap {
    let vec[filemap] files = [];
    ret @rec(mutable files=files);
}

fn new_filemap(filename filename, uint start_pos) -> filemap {
    ret @rec(name=filename, start_pos=start_pos, mutable lines=[0u]);
}

fn next_line(filemap file, uint pos) { vec::push[uint](file.lines, pos); }

fn lookup_pos(codemap map, uint pos) -> loc {
    auto a = 0u;
    auto b = vec::len[filemap](map.files);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
    }
    auto f = map.files.(a);
    a = 0u;
    b = vec::len[uint](f.lines);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (f.lines.(m) > pos) { b = m; } else { a = m; }
    }
    ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
}

type span = rec(uint lo, uint hi);

fn span_to_str(&span sp, &codemap cm) -> str {
    auto lo = lookup_pos(cm, sp.lo);
    auto hi = lookup_pos(cm, sp.hi);
    ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
}

fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
                   &codemap cm) {
    auto ss = "<input>:0:0:0:0";
    let option::t[@file_lines] maybe_lines = none;
    alt (sp) {
        case (some(?ssp)) {
            ss = span_to_str(ssp, cm);
            maybe_lines = some(span_to_lines(ssp, cm));
        }
        case (none) { }
    }
    io::stdout().write_str(ss + ": ");
    if (term::color_supported()) {
        term::fg(io::stdout().get_buf_writer(), color);
    }
    io::stdout().write_str(#fmt("%s:", kind));
    if (term::color_supported()) {
        term::reset(io::stdout().get_buf_writer());
    }
    io::stdout().write_str(#fmt(" %s\n", msg));
    alt (maybe_lines) {
        case (some(?lines)) {
            auto rdr = io::file_reader(lines.name);
            auto file = str::unsafe_from_bytes(rdr.read_whole_stream());
            auto fm = codemap::get_filemap(cm, lines.name);
            for (uint line in lines.lines) {
                io::stdout().write_str(#fmt("%s:%u ", fm.name, line + 1u));
                auto s = codemap::get_line(fm, line as int, file);
                if (!str::ends_with(s, "\n")) {
                    s += "\n";
                }
                io::stdout().write_str(s);
            }
        }
        case (_) {}
    }
}

fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "warning", 11u8, cm);
}
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "error", 9u8, cm);
}
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "note", 10u8, cm);
}

type file_lines = rec(str name, vec[uint] lines);

fn span_to_lines(span sp, codemap::codemap cm) -> @file_lines {
    auto lo = codemap::lookup_pos(cm, sp.lo);
    auto hi = codemap::lookup_pos(cm, sp.hi);
    auto lines = [];
    for each (uint i in uint::range(lo.line - 1u, hi.line as uint)) {
        lines += [i];
    }
    ret @rec(name=lo.filename, lines=lines);
}

fn get_line(filemap fm, int line, &str file) -> str {
    let uint end;
    if ((line as uint) + 1u >= vec::len(fm.lines)) {
        end = str::byte_len(file);
    } else {
        end = fm.lines.(line + 1);
    }
    ret str::slice(file, fm.lines.(line), end);
}

fn get_filemap(codemap cm, str filename) -> filemap {
    for (filemap fm in cm.files) {
        if (fm.name == filename) {
            ret fm;
        }
    }
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
    fail;// ("asking for " + filename + " which we don't know about");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>use crate::{
    builtin_functions::BuiltinFunction,
    compiler::lir::{Chunk, ChunkIndex, Instruction},
};
use itertools::Itertools;
use log::debug;
use std::collections::HashMap;

/// A VM can execute some byte code.
#[derive(Debug)]
pub struct Vm {
    chunks: Vec<Chunk>,
    status: Status,
    ip: ByteCodePointer, // instruction pointer
    stack: Vec<StackEntry>,
    heap: HashMap<ObjectPointer, Object>,
    next_heap_address: ObjectPointer,
}

#[derive(Debug, Clone)]
pub enum Status {
    Running,
    Done(Object),
    Panicked(Object),
}

// TODO: Stack entries point either to instructions or to objects. In the
// future, we should unify both cases.
#[derive(Debug, Clone, Copy)]
pub enum StackEntry {
    ByteCode(ByteCodePointer),
    Object(ObjectPointer),
}
#[derive(Debug, Clone, Copy)]
pub struct ByteCodePointer {
    chunk: ChunkIndex,
    instruction: usize,
}
type ObjectPointer = usize;

// TODO: Later add a reference counter. For now, we're just leaking memory
// because objects are never freed.
#[derive(Debug, Clone)] // TODO: rm Clone
pub struct Object {
    reference_count: usize,
    data: ObjectData,
}
#[derive(Clone, Debug)] // TODO: rm Clone
pub enum ObjectData {
    Int(u64),
    Text(String),
    Symbol(String),
    Struct(HashMap<ObjectPointer, ObjectPointer>),
    Closure {
        // TODO: This could later be just a vector of object pointers, but for
        // now we capture the whole stack.
        captured: Vec<StackEntry>,
        body: ChunkIndex,
    },
}

impl Vm {
    pub fn new(chunks: Vec<Chunk>) -> Self {
        Self {
            chunks: chunks.clone(),
            status: Status::Running,
            ip: ByteCodePointer {
                chunk: chunks.len() - 1,
                instruction: 0,
            },
            stack: vec![],
            heap: HashMap::new(),
            next_heap_address: 1000000,
        }
    }

    pub fn status(&self) -> Status {
        self.status.clone()
    }

    fn get_from_stack(&self, offset: usize) -> StackEntry {
        self.stack[self.stack.len() - 1 - offset as usize].clone()
    }
    fn get_from_heap(&mut self, address: ObjectPointer) -> &mut Object {
        self.heap.get_mut(&address).unwrap()
    }

    fn create_object(&mut self, object: ObjectData) -> ObjectPointer {
        let address = self.next_heap_address;
        self.heap.insert(
            address,
            Object {
                reference_count: 1,
                data: object,
            },
        );
        self.next_heap_address += 1;
        address
    }
    fn free_object(&mut self, address: ObjectPointer) {
        let object = self.heap.remove(&address).unwrap();
        assert_eq!(object.reference_count, 0);
        match object.data {
            ObjectData::Int(_) => {}
            ObjectData::Text(_) => {}
            ObjectData::Symbol(_) => {}
            ObjectData::Struct(entries) => {
                for (key, value) in entries {
                    self.drop(key);
                    self.drop(value);
                }
            }
            ObjectData::Closure { captured, body } => {
                for entry in captured {
                    if let StackEntry::Object(address) = entry {
                        self.drop(address);
                    }
                }
            }
        }
    }

    fn dup(&mut self, address: ObjectPointer) {
        self.get_from_heap(address).reference_count += 1;
    }
    fn drop(&mut self, address: ObjectPointer) {
        let object = self.get_from_heap(address);
        object.reference_count -= 1;
        if object.reference_count == 0 {
            self.free_object(address);
        }
    }

    pub fn run(&mut self, mut num_instructions: u16) {
        assert!(
            matches!(self.status, Status::Running),
            "Called Vm::run on a vm that is not ready to run."
        );
        while matches!(self.status, Status::Running) && num_instructions > 0 {
            num_instructions -= 1;
            let instruction = self.chunks[self.ip.chunk].instructions[self.ip.instruction].clone();
            debug!("Executing instruction: {:?}", &instruction);
            self.ip.instruction += 1;
            self.run_instruction(instruction);

            if self.ip.instruction >= self.chunks[self.ip.chunk].instructions.len() {
                self.status = Status::Done(Object {
                    reference_count: 0,
                    data: ObjectData::Symbol("Nothing".to_string()),
                });
            }
        }
    }
    fn run_instruction(&mut self, instruction: Instruction) {
        match instruction {
            Instruction::CreateInt(int) => {
                let address = self.create_object(ObjectData::Int(int));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateText(text) => {
                let address = self.create_object(ObjectData::Text(text));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateSymbol(symbol) => {
                let address = self.create_object(ObjectData::Symbol(symbol));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateStruct { num_entries } => {
                let mut key_value_addresses = vec![];
                for _ in 0..(2 * num_entries) {
                    match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => panic!("Struct can only contain objects."),
                        StackEntry::Object(address) => key_value_addresses.push(address),
                    }
                }
                let mut entries = HashMap::new();
                for mut key_and_value in &key_value_addresses.into_iter().rev().chunks(2) {
                    let key = key_and_value.next().unwrap();
                    let value = key_and_value.next().unwrap();
                    assert_eq!(key_and_value.next(), None);
                    entries.insert(key, value);
                }
                let address = self.create_object(ObjectData::Struct(entries));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateClosure(chunk_index) => {
                let stack = self.stack.clone();
                for entry in &stack {
                    match entry {
                        StackEntry::ByteCode(_) => {}
                        StackEntry::Object(address) => {
                            self.dup(*address);
                        }
                    }
                }
                let address = self.create_object(ObjectData::Closure {
                    captured: stack,
                    body: chunk_index,
                });
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::PopMultipleBelowTop(n) => {
                let top = self.stack.pop().unwrap();
                for _ in 0..n {
                    if let StackEntry::Object(address) = self.stack.pop().unwrap() {
                        self.drop(address);
                    }
                }
                self.stack.push(top);
            }
            Instruction::PushFromStack(offset) => {
                let entry = self.get_from_stack(offset);
                if let StackEntry::Object(address) = &entry {
                    self.dup(*address);
                }
                self.stack.push(entry);
            }
            Instruction::Call => {
                let closure_address = match self.stack.pop().unwrap() {
                    StackEntry::ByteCode(_) => panic!(),
                    StackEntry::Object(address) => address,
                };
                let (captured, body) = match &self.get_from_heap(closure_address).data {
                    ObjectData::Closure { captured, body } => (captured.clone(), *body),
                    _ => panic!("Can't call non-closure."),
                };
                let num_args = self.chunks[body].num_args;
                let mut args = vec![];
                for _ in 0..num_args {
                    let address = match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => {
                            panic!("You can only pass objects as arguments.")
                        }
                        StackEntry::Object(address) => address,
                    };
                    args.push(address);
                }
                self.stack.push(StackEntry::ByteCode(self.ip));
                self.stack.append(&mut captured.clone());
                for arg in args {
                    self.stack.push(StackEntry::Object(arg));
                }
                self.drop(closure_address);
                self.ip = ByteCodePointer {
                    chunk: body,
                    instruction: 0,
                };
            }
            Instruction::Return => {
                let return_value = self.stack.pop().unwrap();
                // TODO: The closure should take care of removing all captured
                // variables from the stack. That's only possible once we
                // analyze what variables are closed over in the first place.
                // For now, we simply pop until we find a byte code address,
                // which is the address we pushed to remember the caller.
                let caller = loop {
                    match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(address) => break address,
                        StackEntry::Object(_) => {}
                    }
                };
                self.stack.push(return_value);
                self.ip = caller;
            }
            Instruction::Builtin(builtin) => {
                todo!("Implement builtins")
            }
            Instruction::DebugValueEvaluated(_) => {}
            Instruction::Error(_) => {
                // TODO: Fail gracefully.
                panic!("The VM crashed because there was an error in previous compilation stages.")
            }
        }
    }
}
<commit_msg>Panic if there's a compiler error<commit_after>use crate::{
    builtin_functions::BuiltinFunction,
    compiler::lir::{Chunk, ChunkIndex, Instruction},
};
use itertools::Itertools;
use log::debug;
use std::collections::HashMap;

/// A VM can execute some byte code.
#[derive(Debug)]
pub struct Vm {
    chunks: Vec<Chunk>,
    status: Status,
    ip: ByteCodePointer, // instruction pointer
    stack: Vec<StackEntry>,
    heap: HashMap<ObjectPointer, Object>,
    next_heap_address: ObjectPointer,
}

#[derive(Debug, Clone)]
pub enum Status {
    Running,
    Done(Object),
    Panicked(Object),
}

// TODO: Stack entries point either to instructions or to objects. In the
// future, we should unify both cases.
#[derive(Debug, Clone, Copy)]
pub enum StackEntry {
    ByteCode(ByteCodePointer),
    Object(ObjectPointer),
}
#[derive(Debug, Clone, Copy)]
pub struct ByteCodePointer {
    chunk: ChunkIndex,
    instruction: usize,
}
type ObjectPointer = usize;

// TODO: Later add a reference counter. For now, we're just leaking memory
// because objects are never freed.
#[derive(Debug, Clone)] // TODO: rm Clone
pub struct Object {
    reference_count: usize,
    data: ObjectData,
}
#[derive(Clone, Debug)] // TODO: rm Clone
pub enum ObjectData {
    Int(u64),
    Text(String),
    Symbol(String),
    Struct(HashMap<ObjectPointer, ObjectPointer>),
    Closure {
        // TODO: This could later be just a vector of object pointers, but for
        // now we capture the whole stack.
        captured: Vec<StackEntry>,
        body: ChunkIndex,
    },
}

impl Vm {
    pub fn new(chunks: Vec<Chunk>) -> Self {
        Self {
            chunks: chunks.clone(),
            status: Status::Running,
            ip: ByteCodePointer {
                chunk: chunks.len() - 1,
                instruction: 0,
            },
            stack: vec![],
            heap: HashMap::new(),
            next_heap_address: 1000000,
        }
    }

    pub fn status(&self) -> Status {
        self.status.clone()
    }

    fn get_from_stack(&self, offset: usize) -> StackEntry {
        self.stack[self.stack.len() - 1 - offset as usize].clone()
    }
    fn get_from_heap(&mut self, address: ObjectPointer) -> &mut Object {
        self.heap.get_mut(&address).unwrap()
    }

    fn create_object(&mut self, object: ObjectData) -> ObjectPointer {
        let address = self.next_heap_address;
        self.heap.insert(
            address,
            Object {
                reference_count: 1,
                data: object,
            },
        );
        self.next_heap_address += 1;
        address
    }
    fn free_object(&mut self, address: ObjectPointer) {
        let object = self.heap.remove(&address).unwrap();
        assert_eq!(object.reference_count, 0);
        match object.data {
            ObjectData::Int(_) => {}
            ObjectData::Text(_) => {}
            ObjectData::Symbol(_) => {}
            ObjectData::Struct(entries) => {
                for (key, value) in entries {
                    self.drop(key);
                    self.drop(value);
                }
            }
            ObjectData::Closure { captured, body } => {
                for entry in captured {
                    if let StackEntry::Object(address) = entry {
                        self.drop(address);
                    }
                }
            }
        }
    }

    fn dup(&mut self, address: ObjectPointer) {
        self.get_from_heap(address).reference_count += 1;
    }
    fn drop(&mut self, address: ObjectPointer) {
        let object = self.get_from_heap(address);
        object.reference_count -= 1;
        if object.reference_count == 0 {
            self.free_object(address);
        }
    }

    pub fn run(&mut self, mut num_instructions: u16) {
        assert!(
            matches!(self.status, Status::Running),
            "Called Vm::run on a vm that is not ready to run."
        );
        while matches!(self.status, Status::Running) && num_instructions > 0 {
            num_instructions -= 1;
            let instruction = self.chunks[self.ip.chunk].instructions[self.ip.instruction].clone();
            debug!("Executing instruction: {:?}", &instruction);
            self.ip.instruction += 1;
            self.run_instruction(instruction);

            if self.ip.instruction >= self.chunks[self.ip.chunk].instructions.len() {
                self.status = Status::Done(Object {
                    reference_count: 0,
                    data: ObjectData::Symbol("Nothing".to_string()),
                });
            }
        }
    }
    fn run_instruction(&mut self, instruction: Instruction) {
        match instruction {
            Instruction::CreateInt(int) => {
                let address = self.create_object(ObjectData::Int(int));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateText(text) => {
                let address = self.create_object(ObjectData::Text(text));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateSymbol(symbol) => {
                let address = self.create_object(ObjectData::Symbol(symbol));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateStruct { num_entries } => {
                let mut key_value_addresses = vec![];
                for _ in 0..(2 * num_entries) {
                    match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => panic!("Struct can only contain objects."),
                        StackEntry::Object(address) => key_value_addresses.push(address),
                    }
                }
                let mut entries = HashMap::new();
                for mut key_and_value in &key_value_addresses.into_iter().rev().chunks(2) {
                    let key = key_and_value.next().unwrap();
                    let value = key_and_value.next().unwrap();
                    assert_eq!(key_and_value.next(), None);
                    entries.insert(key, value);
                }
                let address = self.create_object(ObjectData::Struct(entries));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateClosure(chunk_index) => {
                let stack = self.stack.clone();
                for entry in &stack {
                    match entry {
                        StackEntry::ByteCode(_) => {}
                        StackEntry::Object(address) => {
                            self.dup(*address);
                        }
                    }
                }
                let address = self.create_object(ObjectData::Closure {
                    captured: stack,
                    body: chunk_index,
                });
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::PopMultipleBelowTop(n) => {
                let top = self.stack.pop().unwrap();
                for _ in 0..n {
                    if let StackEntry::Object(address) = self.stack.pop().unwrap() {
                        self.drop(address);
                    }
                }
                self.stack.push(top);
            }
            Instruction::PushFromStack(offset) => {
                let entry = self.get_from_stack(offset);
                if let StackEntry::Object(address) = &entry {
                    self.dup(*address);
                }
                self.stack.push(entry);
            }
            Instruction::Call => {
                let closure_address = match self.stack.pop().unwrap() {
                    StackEntry::ByteCode(_) => panic!(),
                    StackEntry::Object(address) => address,
                };
                let (captured, body) = match &self.get_from_heap(closure_address).data {
                    ObjectData::Closure { captured, body } => (captured.clone(), *body),
                    _ => panic!("Can't call non-closure."),
                };
                let num_args = self.chunks[body].num_args;
                let mut args = vec![];
                for _ in 0..num_args {
                    let address = match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => {
                            panic!("You can only pass objects as arguments.")
                        }
                        StackEntry::Object(address) => address,
                    };
                    args.push(address);
                }
                self.stack.push(StackEntry::ByteCode(self.ip));
                self.stack.append(&mut captured.clone());
                for arg in args {
                    self.stack.push(StackEntry::Object(arg));
                }
                self.drop(closure_address);
                self.ip = ByteCodePointer {
                    chunk: body,
                    instruction: 0,
                };
            }
            Instruction::Return => {
                let return_value = self.stack.pop().unwrap();
                // TODO: The closure should take care of removing all captured
                // variables from the stack. That's only possible once we
                // analyze what variables are closed over in the first place.
                // For now, we simply pop until we find a byte code address,
                // which is the address we pushed to remember the caller.
                let caller = loop {
                    match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(address) => break address,
                        StackEntry::Object(_) => {}
                    }
                };
                self.stack.push(return_value);
                self.ip = caller;
            }
            Instruction::Builtin(builtin) => {
                todo!("Implement builtins")
            }
            Instruction::DebugValueEvaluated(_) => {}
            Instruction::Error(hir_id) => {
                self.status = Status::Panicked(Object {
                    reference_count: 0,
                    data: ObjectData::Text(
                        "The VM crashed because there was an error in previous compilation stages."
                            .to_string(),
                    ),
                });
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use codemap::{Pos, Span};
use codemap;

use std::io;
use std::io::stdio::StdWriter;
use std::local_data;
use extra::term;

static BUG_REPORT_URL: &'static str =
    "https://github.com/mozilla/rust/wiki/HOWTO-submit-a-Rust-bug-report";

pub trait Emitter {
    fn emit(&self,
            cmsp: Option<(&codemap::CodeMap, Span)>,
            msg: &str,
            lvl: level);
}

// a span-handler is like a handler but also
// accepts span information for source-location
// reporting.
pub struct SpanHandler {
    handler: @mut Handler,
    cm: @codemap::CodeMap,
}

impl SpanHandler {
    pub fn span_fatal(@self, sp: Span, msg: &str) -> ! {
        self.handler.emit(Some((&*self.cm, sp)), msg, fatal);
        fail!();
    }
    pub fn span_err(@self, sp: Span, msg: &str) {
        self.handler.emit(Some((&*self.cm, sp)), msg, error);
        self.handler.bump_err_count();
    }
    pub fn span_warn(@self, sp: Span, msg: &str) {
        self.handler.emit(Some((&*self.cm, sp)), msg, warning);
    }
    pub fn span_note(@self, sp: Span, msg: &str) {
        self.handler.emit(Some((&*self.cm, sp)), msg, note);
    }
    pub fn span_bug(@self, sp: Span, msg: &str) -> ! {
        self.span_fatal(sp, ice_msg(msg));
    }
    pub fn span_unimpl(@self, sp: Span, msg: &str) -> ! {
        self.span_bug(sp, ~"unimplemented " + msg);
    }
    pub fn handler(@self) -> @mut Handler {
        self.handler
    }
}

// a handler deals with errors; certain errors
// (fatal, bug, unimpl) may cause immediate exit,
// others log errors for later reporting.
pub struct Handler {
    err_count: uint,
    emit: @Emitter,
}

impl Handler {
    pub fn fatal(@mut self, msg: &str) -> ! {
        self.emit.emit(None, msg, fatal);
        fail!();
    }
    pub fn err(@mut self, msg: &str) {
        self.emit.emit(None, msg, error);
        self.bump_err_count();
    }
    pub fn bump_err_count(@mut self) {
        self.err_count += 1u;
    }
    pub fn err_count(@mut self) -> uint {
        self.err_count
    }
    pub fn has_errors(@mut self) -> bool {
        self.err_count > 0u
    }
    pub fn abort_if_errors(@mut self) {
        let s;
        match self.err_count {
          0u => return,
          1u => s = ~"aborting due to previous error",
          _  => {
            s = format!("aborting due to {} previous errors",
                     self.err_count);
          }
        }
        self.fatal(s);
    }
    pub fn warn(@mut self, msg: &str) {
        self.emit.emit(None, msg, warning);
    }
    pub fn note(@mut self, msg: &str) {
        self.emit.emit(None, msg, note);
    }
    pub fn bug(@mut self, msg: &str) -> ! {
        self.fatal(ice_msg(msg));
    }
    pub fn unimpl(@mut self, msg: &str) -> ! {
        self.bug(~"unimplemented " + msg);
    }
    pub fn emit(@mut self,
            cmsp: Option<(&codemap::CodeMap, Span)>,
            msg: &str,
            lvl: level) {
        self.emit.emit(cmsp, msg, lvl);
    }
}

pub fn ice_msg(msg: &str) -> ~str {
    format!("internal compiler error: {}\nThis message reflects a bug in the Rust compiler. \
            \nWe would appreciate a bug report: {}", msg, BUG_REPORT_URL)
}

pub fn mk_span_handler(handler: @mut Handler, cm: @codemap::CodeMap)
                       -> @SpanHandler {
    @SpanHandler {
        handler: handler,
        cm: cm,
    }
}

pub fn mk_handler(emitter: Option<@Emitter>) -> @mut Handler {
    let emit: @Emitter = match emitter {
        Some(e) => e,
        None => @DefaultEmitter as @Emitter
    };

    @mut Handler {
        err_count: 0,
        emit: emit,
    }
}

#[deriving(Eq)]
pub enum level {
    fatal,
    error,
    warning,
    note,
}

fn diagnosticstr(lvl: level) -> ~str {
    match lvl {
        fatal => ~"error",
        error => ~"error",
        warning => ~"warning",
        note => ~"note"
    }
}

fn diagnosticcolor(lvl: level) -> term::color::Color {
    match lvl {
        fatal => term::color::BRIGHT_RED,
        error => term::color::BRIGHT_RED,
        warning => term::color::BRIGHT_YELLOW,
        note => term::color::BRIGHT_GREEN
    }
}

fn print_maybe_styled(msg: &str, color: term::attr::Attr) {
    local_data_key!(tls_terminal: ~Option<term::Terminal<StdWriter>>)

    fn is_stderr_screen() -> bool {
        use std::libc;
        unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
    }
    fn write_pretty<T: Writer>(term: &mut term::Terminal<T>, s: &str, c: term::attr::Attr) {
        term.attr(c);
        term.write(s.as_bytes());
        term.reset();
    }

    if is_stderr_screen() {
        local_data::get_mut(tls_terminal, |term| {
            match term {
                Some(term) => {
                    match **term {
                        Some(ref mut term) => write_pretty(term, msg, color),
                        None => io::stderr().write(msg.as_bytes())
                    }
                }
                None => {
                    let t = ~match term::Terminal::new(io::stderr()) {
                        Ok(mut term) => {
                            write_pretty(&mut term, msg, color);
                            Some(term)
                        }
                        Err(_) => {
                            io::stderr().write(msg.as_bytes());
                            None
                        }
                    };
                    local_data::set(tls_terminal, t);
                }
            }
        });
    } else {
        io::stderr().write(msg.as_bytes());
    }
}

fn print_diagnostic(topic: &str, lvl: level, msg: &str) {
    let mut stderr = io::stderr();

    if !topic.is_empty() {
        write!(&mut stderr as &mut io::Writer, "{} ", topic);
    }

    print_maybe_styled(format!("{}: ", diagnosticstr(lvl)),
                            term::attr::ForegroundColor(diagnosticcolor(lvl)));
    print_maybe_styled(format!("{}\n", msg), term::attr::Bold);
}

pub struct DefaultEmitter;

impl Emitter for DefaultEmitter {
    fn emit(&self,
            cmsp: Option<(&codemap::CodeMap, Span)>,
            msg: &str,
            lvl: level) {
        match cmsp {
            Some((cm, sp)) => {
                let sp = cm.adjust_span(sp);
                let ss = cm.span_to_str(sp);
                let lines = cm.span_to_lines(sp);
                print_diagnostic(ss, lvl, msg);
                highlight_lines(cm, sp, lvl, lines);
                print_macro_backtrace(cm, sp);
            }
            None => print_diagnostic("", lvl, msg),
        }
    }
}

fn highlight_lines(cm: &codemap::CodeMap,
                   sp: Span,
                   lvl: level,
                   lines: &codemap::FileLines) {
    let fm = lines.file;
    let mut err = io::stderr();
    let err = &mut err as &mut io::Writer;

    // arbitrarily only print up to six lines of the error
    let max_lines = 6u;
    let mut elided = false;
    let mut display_lines = lines.lines.as_slice();
    if display_lines.len() > max_lines {
        display_lines = display_lines.slice(0u, max_lines);
        elided = true;
    }
    // Print the offending lines
    for line in display_lines.iter() {
        write!(err, "{}:{} {}\n", fm.name, *line + 1, fm.get_line(*line as int));
    }
    if elided {
        let last_line = display_lines[display_lines.len() - 1u];
        let s = format!("{}:{} ", fm.name, last_line + 1u);
        write!(err, "{0:1$}...\n", "", s.len());
    }

    // FIXME (#3260)
    // If there's one line at fault we can easily point to the problem
    if lines.lines.len() == 1u {
        let lo = cm.lookup_char_pos(sp.lo);
        let mut digits = 0u;
        let mut num = (lines.lines[0] + 1u) / 10u;

        // how many digits must be indent past?
        while num > 0u { num /= 10u; digits += 1u; }

        // indent past |name:## | and the 0-offset column location
        let left = fm.name.len() + digits + lo.col.to_uint() + 3u;
        let mut s = ~"";
        // Skip is the number of characters we need to skip because they are
        // part of the 'filename:line ' part of the previous line.
        let skip = fm.name.len() + digits + 3u;
        skip.times(|| s.push_char(' '));
        let orig = fm.get_line(lines.lines[0] as int);
        for pos in range(0u, left-skip) {
            let curChar = (orig[pos] as char);
            // Whenever a tab occurs on the previous line, we insert one on
            // the error-point-squiggly-line as well (instead of a space).
            // That way the squiggly line will usually appear in the correct
            // position.
            match curChar {
                '\t' => s.push_char('\t'),
                _ => s.push_char(' '),
            };
        }
        write!(err, "{}", s);
        let mut s = ~"^";
        let hi = cm.lookup_char_pos(sp.hi);
        if hi.col != lo.col {
            // the ^ already takes up one space
            let num_squigglies = hi.col.to_uint()-lo.col.to_uint()-1u;
            num_squigglies.times(|| s.push_char('~'));
        }
        print_maybe_styled(s + "\n", term::attr::ForegroundColor(diagnosticcolor(lvl)));
    }
}

fn print_macro_backtrace(cm: &codemap::CodeMap, sp: Span) {
    for ei in sp.expn_info.iter() {
        let ss = ei.callee.span.as_ref().map_default(~"", |span| cm.span_to_str(*span));
        let (pre, post) = match ei.callee.format {
            codemap::MacroAttribute => ("#[", "]"),
            codemap::MacroBang => ("", "!")
        };

        print_diagnostic(ss, note,
                         format!("in expansion of {}{}{}", pre, ei.callee.name, post));
        let ss = cm.span_to_str(ei.call_site);
        print_diagnostic(ss, note, "expansion site");
        print_macro_backtrace(cm, ei.call_site);
    }
}

pub fn expect<T:Clone>(diag: @SpanHandler, opt: Option<T>, msg: || -> ~str)
              -> T {
    match opt {
       Some(ref t) => (*t).clone(),
       None => diag.handler().bug(msg()),
    }
}
<commit_msg>libsyntax: De-`@mut` `HandlerT::err_count`<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use codemap::{Pos, Span};
use codemap;

use std::cell::Cell;
use std::io;
use std::io::stdio::StdWriter;
use std::local_data;
use extra::term;

static BUG_REPORT_URL: &'static str =
    "https://github.com/mozilla/rust/wiki/HOWTO-submit-a-Rust-bug-report";

pub trait Emitter {
    fn emit(&self,
            cmsp: Option<(&codemap::CodeMap, Span)>,
            msg: &str,
            lvl: level);
}

// a span-handler is like a handler but also
// accepts span information for source-location
// reporting.
pub struct SpanHandler {
    handler: @mut Handler,
    cm: @codemap::CodeMap,
}

impl SpanHandler {
    pub fn span_fatal(@self, sp: Span, msg: &str) -> ! {
        self.handler.emit(Some((&*self.cm, sp)), msg, fatal);
        fail!();
    }
    pub fn span_err(@self, sp: Span, msg: &str) {
        self.handler.emit(Some((&*self.cm, sp)), msg, error);
        self.handler.bump_err_count();
    }
    pub fn span_warn(@self, sp: Span, msg: &str) {
        self.handler.emit(Some((&*self.cm, sp)), msg, warning);
    }
    pub fn span_note(@self, sp: Span, msg: &str) {
        self.handler.emit(Some((&*self.cm, sp)), msg, note);
    }
    pub fn span_bug(@self, sp: Span, msg: &str) -> ! {
        self.span_fatal(sp, ice_msg(msg));
    }
    pub fn span_unimpl(@self, sp: Span, msg: &str) -> ! {
        self.span_bug(sp, ~"unimplemented " + msg);
    }
    pub fn handler(@self) -> @mut Handler {
        self.handler
    }
}

// a handler deals with errors; certain errors
// (fatal, bug, unimpl) may cause immediate exit,
// others log errors for later reporting.
pub struct Handler {
    err_count: Cell<uint>,
    emit: @Emitter,
}

impl Handler {
    pub fn fatal(@mut self, msg: &str) -> ! {
        self.emit.emit(None, msg, fatal);
        fail!();
    }
    pub fn err(@mut self, msg: &str) {
        self.emit.emit(None, msg, error);
        self.bump_err_count();
    }
    pub fn bump_err_count(@mut self) {
        self.err_count.set(self.err_count.get() + 1u);
    }
    pub fn err_count(@mut self) -> uint {
        self.err_count.get()
    }
    pub fn has_errors(@mut self) -> bool {
        self.err_count.get()> 0u
    }
    pub fn abort_if_errors(@mut self) {
        let s;
        match self.err_count.get() {
          0u => return,
          1u => s = ~"aborting due to previous error",
          _  => {
            s = format!("aborting due to {} previous errors",
                     self.err_count.get());
          }
        }
        self.fatal(s);
    }
    pub fn warn(@mut self, msg: &str) {
        self.emit.emit(None, msg, warning);
    }
    pub fn note(@mut self, msg: &str) {
        self.emit.emit(None, msg, note);
    }
    pub fn bug(@mut self, msg: &str) -> ! {
        self.fatal(ice_msg(msg));
    }
    pub fn unimpl(@mut self, msg: &str) -> ! {
        self.bug(~"unimplemented " + msg);
    }
    pub fn emit(@mut self,
            cmsp: Option<(&codemap::CodeMap, Span)>,
            msg: &str,
            lvl: level) {
        self.emit.emit(cmsp, msg, lvl);
    }
}

pub fn ice_msg(msg: &str) -> ~str {
    format!("internal compiler error: {}\nThis message reflects a bug in the Rust compiler. \
            \nWe would appreciate a bug report: {}", msg, BUG_REPORT_URL)
}

pub fn mk_span_handler(handler: @mut Handler, cm: @codemap::CodeMap)
                       -> @SpanHandler {
    @SpanHandler {
        handler: handler,
        cm: cm,
    }
}

pub fn mk_handler(emitter: Option<@Emitter>) -> @mut Handler {
    let emit: @Emitter = match emitter {
        Some(e) => e,
        None => @DefaultEmitter as @Emitter
    };

    @mut Handler {
        err_count: Cell::new(0),
        emit: emit,
    }
}

#[deriving(Eq)]
pub enum level {
    fatal,
    error,
    warning,
    note,
}

fn diagnosticstr(lvl: level) -> ~str {
    match lvl {
        fatal => ~"error",
        error => ~"error",
        warning => ~"warning",
        note => ~"note"
    }
}

fn diagnosticcolor(lvl: level) -> term::color::Color {
    match lvl {
        fatal => term::color::BRIGHT_RED,
        error => term::color::BRIGHT_RED,
        warning => term::color::BRIGHT_YELLOW,
        note => term::color::BRIGHT_GREEN
    }
}

fn print_maybe_styled(msg: &str, color: term::attr::Attr) {
    local_data_key!(tls_terminal: ~Option<term::Terminal<StdWriter>>)

    fn is_stderr_screen() -> bool {
        use std::libc;
        unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
    }
    fn write_pretty<T: Writer>(term: &mut term::Terminal<T>, s: &str, c: term::attr::Attr) {
        term.attr(c);
        term.write(s.as_bytes());
        term.reset();
    }

    if is_stderr_screen() {
        local_data::get_mut(tls_terminal, |term| {
            match term {
                Some(term) => {
                    match **term {
                        Some(ref mut term) => write_pretty(term, msg, color),
                        None => io::stderr().write(msg.as_bytes())
                    }
                }
                None => {
                    let t = ~match term::Terminal::new(io::stderr()) {
                        Ok(mut term) => {
                            write_pretty(&mut term, msg, color);
                            Some(term)
                        }
                        Err(_) => {
                            io::stderr().write(msg.as_bytes());
                            None
                        }
                    };
                    local_data::set(tls_terminal, t);
                }
            }
        });
    } else {
        io::stderr().write(msg.as_bytes());
    }
}

fn print_diagnostic(topic: &str, lvl: level, msg: &str) {
    let mut stderr = io::stderr();

    if !topic.is_empty() {
        write!(&mut stderr as &mut io::Writer, "{} ", topic);
    }

    print_maybe_styled(format!("{}: ", diagnosticstr(lvl)),
                            term::attr::ForegroundColor(diagnosticcolor(lvl)));
    print_maybe_styled(format!("{}\n", msg), term::attr::Bold);
}

pub struct DefaultEmitter;

impl Emitter for DefaultEmitter {
    fn emit(&self,
            cmsp: Option<(&codemap::CodeMap, Span)>,
            msg: &str,
            lvl: level) {
        match cmsp {
            Some((cm, sp)) => {
                let sp = cm.adjust_span(sp);
                let ss = cm.span_to_str(sp);
                let lines = cm.span_to_lines(sp);
                print_diagnostic(ss, lvl, msg);
                highlight_lines(cm, sp, lvl, lines);
                print_macro_backtrace(cm, sp);
            }
            None => print_diagnostic("", lvl, msg),
        }
    }
}

fn highlight_lines(cm: &codemap::CodeMap,
                   sp: Span,
                   lvl: level,
                   lines: &codemap::FileLines) {
    let fm = lines.file;
    let mut err = io::stderr();
    let err = &mut err as &mut io::Writer;

    // arbitrarily only print up to six lines of the error
    let max_lines = 6u;
    let mut elided = false;
    let mut display_lines = lines.lines.as_slice();
    if display_lines.len() > max_lines {
        display_lines = display_lines.slice(0u, max_lines);
        elided = true;
    }
    // Print the offending lines
    for line in display_lines.iter() {
        write!(err, "{}:{} {}\n", fm.name, *line + 1, fm.get_line(*line as int));
    }
    if elided {
        let last_line = display_lines[display_lines.len() - 1u];
        let s = format!("{}:{} ", fm.name, last_line + 1u);
        write!(err, "{0:1$}...\n", "", s.len());
    }

    // FIXME (#3260)
    // If there's one line at fault we can easily point to the problem
    if lines.lines.len() == 1u {
        let lo = cm.lookup_char_pos(sp.lo);
        let mut digits = 0u;
        let mut num = (lines.lines[0] + 1u) / 10u;

        // how many digits must be indent past?
        while num > 0u { num /= 10u; digits += 1u; }

        // indent past |name:## | and the 0-offset column location
        let left = fm.name.len() + digits + lo.col.to_uint() + 3u;
        let mut s = ~"";
        // Skip is the number of characters we need to skip because they are
        // part of the 'filename:line ' part of the previous line.
        let skip = fm.name.len() + digits + 3u;
        skip.times(|| s.push_char(' '));
        let orig = fm.get_line(lines.lines[0] as int);
        for pos in range(0u, left-skip) {
            let curChar = (orig[pos] as char);
            // Whenever a tab occurs on the previous line, we insert one on
            // the error-point-squiggly-line as well (instead of a space).
            // That way the squiggly line will usually appear in the correct
            // position.
            match curChar {
                '\t' => s.push_char('\t'),
                _ => s.push_char(' '),
            };
        }
        write!(err, "{}", s);
        let mut s = ~"^";
        let hi = cm.lookup_char_pos(sp.hi);
        if hi.col != lo.col {
            // the ^ already takes up one space
            let num_squigglies = hi.col.to_uint()-lo.col.to_uint()-1u;
            num_squigglies.times(|| s.push_char('~'));
        }
        print_maybe_styled(s + "\n", term::attr::ForegroundColor(diagnosticcolor(lvl)));
    }
}

fn print_macro_backtrace(cm: &codemap::CodeMap, sp: Span) {
    for ei in sp.expn_info.iter() {
        let ss = ei.callee.span.as_ref().map_default(~"", |span| cm.span_to_str(*span));
        let (pre, post) = match ei.callee.format {
            codemap::MacroAttribute => ("#[", "]"),
            codemap::MacroBang => ("", "!")
        };

        print_diagnostic(ss, note,
                         format!("in expansion of {}{}{}", pre, ei.callee.name, post));
        let ss = cm.span_to_str(ei.call_site);
        print_diagnostic(ss, note, "expansion site");
        print_macro_backtrace(cm, ei.call_site);
    }
}

pub fn expect<T:Clone>(diag: @SpanHandler, opt: Option<T>, msg: || -> ~str)
              -> T {
    match opt {
       Some(ref t) => (*t).clone(),
       None => diag.handler().bug(msg()),
    }
}
<|endoftext|>


<commit_before>//! Rendering functionality and abstractions
//!
//! Collection of common traits and implementations
//! to facilitate (possible hardware-accelerated) rendering.
//!
//! Supported rendering apis:
//!
//! - Raw OpenGL ES 2

use std::collections::HashSet;
use std::error::Error;

#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::Damage;
use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};

use crate::backend::SwapBuffersError;
#[cfg(feature = "renderer_gl")]
pub mod gles2;
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
use crate::backend::egl::display::EGLBufferReader;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
    /// Identity transformation (plane is unaltered when applied)
    Normal,
    /// Plane is rotated by 90 degrees
    _90,
    /// Plane is rotated by 180 degrees
    _180,
    /// Plane is rotated by 270 degrees
    _270,
    /// Plane is flipped vertically
    Flipped,
    /// Plane is flipped vertically and rotated by 90 degrees
    Flipped90,
    /// Plane is flipped vertically and rotated by 180 degrees
    Flipped180,
    /// Plane is flipped vertically and rotated by 270 degrees
    Flipped270,
}

impl Transform {
    /// A projection matrix to apply this transformation
    pub fn matrix(&self) -> Matrix3<f32> {
        match self {
            Transform::Normal => Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_90 => Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
        }
    }

    /// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
    ///
    /// Flipping is preserved and 180/Normal transformation are uneffected.
    pub fn invert(&self) -> Transform {
        match self {
            Transform::Normal => Transform::Normal,
            Transform::Flipped => Transform::Flipped,
            Transform::_90 => Transform::_270,
            Transform::_180 => Transform::_180,
            Transform::_270 => Transform::_90,
            Transform::Flipped90 => Transform::Flipped270,
            Transform::Flipped180 => Transform::Flipped180,
            Transform::Flipped270 => Transform::Flipped90,
        }
    }

    /// Transformed size after applying this transformation.
    pub fn transform_size(&self, width: u32, height: u32) -> (u32, u32) {
        if *self == Transform::_90
            || *self == Transform::_270
            || *self == Transform::Flipped90
            || *self == Transform::Flipped270
        {
            (height, width)
        } else {
            (width, height)
        }
    }
}

#[cfg(feature = "wayland-frontend")]
impl From<wayland_server::protocol::wl_output::Transform> for Transform {
    fn from(transform: wayland_server::protocol::wl_output::Transform) -> Transform {
        use wayland_server::protocol::wl_output::Transform::*;
        match transform {
            Normal => Transform::Normal,
            _90 => Transform::_90,
            _180 => Transform::_180,
            _270 => Transform::_270,
            Flipped => Transform::Flipped,
            Flipped90 => Transform::Flipped90,
            Flipped180 => Transform::Flipped180,
            Flipped270 => Transform::Flipped270,
        }
    }
}

/// Abstraction for Renderers, that can render into different targets
pub trait Bind<Target>: Unbind {
    /// Bind a given rendering target, which will contain the rendering results until `unbind` is called.
    fn bind(&mut self, target: Target) -> Result<(), <Self as Renderer>::Error>;
    /// Supported pixel formats for given targets, if applicable.
    fn supported_formats(&self) -> Option<HashSet<crate::backend::allocator::Format>> {
        None
    }
}

/// Functionality to unbind the current rendering target
pub trait Unbind: Renderer {
    /// Unbind the current rendering target.
    ///
    /// May fall back to a default target, if defined by the implementation.
    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error>;
}

/// A two dimensional texture
pub trait Texture {
    /// Size of the texture plane (w x h)
    fn size(&self) -> (u32, u32) {
        (self.width(), self.height())
    }

    /// Width of the texture plane
    fn width(&self) -> u32;
    /// Height of the texture plane
    fn height(&self) -> u32;
}

/// Abstraction of commonly used rendering operations for compositors.
pub trait Renderer {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;

    /// Import a given bitmap into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
    /// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
    ///
    /// This operation needs no bound or default rendering target.
    #[cfg(feature = "image")]
    fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
        &mut self,
        image: &image::ImageBuffer<image::Rgba<u8>, C>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Returns supported formats for shared memory buffers.
    ///
    /// Will always contain At least `Argb8888` and `Xrgb8888`.
    #[cfg(feature = "wayland_frontend")]
    fn shm_formats(&self) -> &[wl_shm::Format] {
        // Mandatory
        &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
    }

    /// Import a given buffer into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
    ///
    /// This operation needs no bound or default rendering target.
    ///
    /// The implementation defines, if the id keeps being valid, if the buffer is released,
    /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
    #[cfg(feature = "wayland_frontend")]
    fn import_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        damage: Option<&Damage>,
        egl: Option<&EGLBufferReader>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
    ///
    /// This function *may* error, if:
    /// - The given dimensions are unsuppored (too large) for this renderer
    /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
    /// - There was a previous `begin`-call, which was not terminated by `finish`.
    /// - This renderer implements `Bind`, no target was bound *and* has no default target.
    /// - (Renderers not implementing `Bind` always have a default target.)
    fn begin(
        &mut self,
        width: u32,
        height: u32,
        transform: Transform,
    ) -> Result<(), <Self as Renderer>::Error>;

    /// Finish a renderering context, previously started by `begin`.
    ///
    /// After this operation is finished the current rendering target contains a sucessfully rendered image.
    /// If the image is immediently shown to the user depends on the target.
    fn finish(&mut self) -> Result<(), SwapBuffersError>;

    /// Clear the complete current target with a single given color.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
    /// Render a texture to the current target using given projection matrix and alpha.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture(
        &mut self,
        texture: &Self::TextureId,
        matrix: Matrix3<f32>,
        alpha: f32,
    ) -> Result<(), Self::Error>;
    /// Render a texture to the current target as a flat 2d-plane at a given
    /// position, applying the given transformation with the given alpha value.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture_at(
        &mut self,
        texture: &Self::TextureId,
        pos: (i32, i32),
        transform: Transform,
        alpha: f32,
    ) -> Result<(), Self::Error> {
        let mut mat = Matrix3::<f32>::identity();

        // position and scale
        let size = texture.size();
        mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
        mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);

        //apply surface transformation
        mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
        if transform == Transform::Normal {
            assert_eq!(mat, mat * transform.invert().matrix());
            assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
        }
        mat = mat * transform.invert().matrix();
        mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));

        self.render_texture(texture, mat, alpha)
    }
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
pub fn buffer_dimensions(
    buffer: &wl_buffer::WlBuffer,
    egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<(i32, i32)> {
    if let Some((w, h)) = egl_buffer_reader
        .as_ref()
        .and_then(|x| x.egl_buffer_dimensions(&buffer))
    {
        Some((w, h))
    } else if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl")))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
    use crate::backend::allocator::Buffer;

    if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}
<commit_msg>renderer: import_buffer (sadly) depends on backend_egl<commit_after>//! Rendering functionality and abstractions
//!
//! Collection of common traits and implementations
//! to facilitate (possible hardware-accelerated) rendering.
//!
//! Supported rendering apis:
//!
//! - Raw OpenGL ES 2

use std::collections::HashSet;
use std::error::Error;

#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::Damage;
use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};

use crate::backend::SwapBuffersError;
#[cfg(feature = "renderer_gl")]
pub mod gles2;
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
use crate::backend::egl::display::EGLBufferReader;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
    /// Identity transformation (plane is unaltered when applied)
    Normal,
    /// Plane is rotated by 90 degrees
    _90,
    /// Plane is rotated by 180 degrees
    _180,
    /// Plane is rotated by 270 degrees
    _270,
    /// Plane is flipped vertically
    Flipped,
    /// Plane is flipped vertically and rotated by 90 degrees
    Flipped90,
    /// Plane is flipped vertically and rotated by 180 degrees
    Flipped180,
    /// Plane is flipped vertically and rotated by 270 degrees
    Flipped270,
}

impl Transform {
    /// A projection matrix to apply this transformation
    pub fn matrix(&self) -> Matrix3<f32> {
        match self {
            Transform::Normal => Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_90 => Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
        }
    }

    /// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
    ///
    /// Flipping is preserved and 180/Normal transformation are uneffected.
    pub fn invert(&self) -> Transform {
        match self {
            Transform::Normal => Transform::Normal,
            Transform::Flipped => Transform::Flipped,
            Transform::_90 => Transform::_270,
            Transform::_180 => Transform::_180,
            Transform::_270 => Transform::_90,
            Transform::Flipped90 => Transform::Flipped270,
            Transform::Flipped180 => Transform::Flipped180,
            Transform::Flipped270 => Transform::Flipped90,
        }
    }

    /// Transformed size after applying this transformation.
    pub fn transform_size(&self, width: u32, height: u32) -> (u32, u32) {
        if *self == Transform::_90
            || *self == Transform::_270
            || *self == Transform::Flipped90
            || *self == Transform::Flipped270
        {
            (height, width)
        } else {
            (width, height)
        }
    }
}

#[cfg(feature = "wayland-frontend")]
impl From<wayland_server::protocol::wl_output::Transform> for Transform {
    fn from(transform: wayland_server::protocol::wl_output::Transform) -> Transform {
        use wayland_server::protocol::wl_output::Transform::*;
        match transform {
            Normal => Transform::Normal,
            _90 => Transform::_90,
            _180 => Transform::_180,
            _270 => Transform::_270,
            Flipped => Transform::Flipped,
            Flipped90 => Transform::Flipped90,
            Flipped180 => Transform::Flipped180,
            Flipped270 => Transform::Flipped270,
        }
    }
}

/// Abstraction for Renderers, that can render into different targets
pub trait Bind<Target>: Unbind {
    /// Bind a given rendering target, which will contain the rendering results until `unbind` is called.
    fn bind(&mut self, target: Target) -> Result<(), <Self as Renderer>::Error>;
    /// Supported pixel formats for given targets, if applicable.
    fn supported_formats(&self) -> Option<HashSet<crate::backend::allocator::Format>> {
        None
    }
}

/// Functionality to unbind the current rendering target
pub trait Unbind: Renderer {
    /// Unbind the current rendering target.
    ///
    /// May fall back to a default target, if defined by the implementation.
    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error>;
}

/// A two dimensional texture
pub trait Texture {
    /// Size of the texture plane (w x h)
    fn size(&self) -> (u32, u32) {
        (self.width(), self.height())
    }

    /// Width of the texture plane
    fn width(&self) -> u32;
    /// Height of the texture plane
    fn height(&self) -> u32;
}

/// Abstraction of commonly used rendering operations for compositors.
pub trait Renderer {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;

    /// Import a given bitmap into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
    /// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
    ///
    /// This operation needs no bound or default rendering target.
    #[cfg(feature = "image")]
    fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
        &mut self,
        image: &image::ImageBuffer<image::Rgba<u8>, C>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Returns supported formats for shared memory buffers.
    ///
    /// Will always contain At least `Argb8888` and `Xrgb8888`.
    #[cfg(feature = "wayland_frontend")]
    fn shm_formats(&self) -> &[wl_shm::Format] {
        // Mandatory
        &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
    }

    /// Import a given buffer into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
    ///
    /// This operation needs no bound or default rendering target.
    ///
    /// The implementation defines, if the id keeps being valid, if the buffer is released,
    /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
    #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
    fn import_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        damage: Option<&Damage>,
        egl: Option<&EGLBufferReader>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
    ///
    /// This function *may* error, if:
    /// - The given dimensions are unsuppored (too large) for this renderer
    /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
    /// - There was a previous `begin`-call, which was not terminated by `finish`.
    /// - This renderer implements `Bind`, no target was bound *and* has no default target.
    /// - (Renderers not implementing `Bind` always have a default target.)
    fn begin(
        &mut self,
        width: u32,
        height: u32,
        transform: Transform,
    ) -> Result<(), <Self as Renderer>::Error>;

    /// Finish a renderering context, previously started by `begin`.
    ///
    /// After this operation is finished the current rendering target contains a sucessfully rendered image.
    /// If the image is immediently shown to the user depends on the target.
    fn finish(&mut self) -> Result<(), SwapBuffersError>;

    /// Clear the complete current target with a single given color.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
    /// Render a texture to the current target using given projection matrix and alpha.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture(
        &mut self,
        texture: &Self::TextureId,
        matrix: Matrix3<f32>,
        alpha: f32,
    ) -> Result<(), Self::Error>;
    /// Render a texture to the current target as a flat 2d-plane at a given
    /// position, applying the given transformation with the given alpha value.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture_at(
        &mut self,
        texture: &Self::TextureId,
        pos: (i32, i32),
        transform: Transform,
        alpha: f32,
    ) -> Result<(), Self::Error> {
        let mut mat = Matrix3::<f32>::identity();

        // position and scale
        let size = texture.size();
        mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
        mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);

        //apply surface transformation
        mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
        if transform == Transform::Normal {
            assert_eq!(mat, mat * transform.invert().matrix());
            assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
        }
        mat = mat * transform.invert().matrix();
        mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));

        self.render_texture(texture, mat, alpha)
    }
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
pub fn buffer_dimensions(
    buffer: &wl_buffer::WlBuffer,
    egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<(i32, i32)> {
    if let Some((w, h)) = egl_buffer_reader
        .as_ref()
        .and_then(|x| x.egl_buffer_dimensions(&buffer))
    {
        Some((w, h))
    } else if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl")))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
    use crate::backend::allocator::Buffer;

    if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}
<|endoftext|>