<commit_before>use std::{default, iter, fmt};
use std::collections::LinkedList;

use super::c_simple;
use super::c_simple::{CAST, CASTNode};
use frontend::radeco_containers::RadecoFunction;
use petgraph::graph::{Graph, NodeIndex, EdgeIndex, Edges, EdgeReference};
use petgraph::visit::EdgeRef;
use petgraph::{EdgeDirection, Direction, Directed};

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTNode {
    Action(ActionNode),
    Value(ValueNode),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionNode {
    Assignment,
    Call,
    Return,
    If,
    Goto,
}

// TODO
#[derive(Debug, Clone, PartialEq)]
enum ValueNode {
    /// The string is the name of variable
    Variable(String),
    Constant,
}

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTEdge {
    Action(ActionEdge),
    Value(ValueEdge),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionEdge {
    IfThen,
    IfElse,
    FuncCall,
    FuncRet,
    Normal,
}

#[derive(Debug, Clone, PartialEq)]
enum ValueEdge {
    AssignSrc,
    AssignDst,
    Arg(u8),
    GotoDst,
}

struct SimpleCAST {
    /// Name of function of this AST
    fname: String,
    entry: NodeIndex,
    ast: Graph<SimpleCASTNode, SimpleCASTEdge>,
}

impl SimpleCASTNode {
    fn is_exit(&self) -> bool {
        match self {
            &SimpleCASTNode::Action(ActionNode::Return) => true,
            _ => false,
        }
    }
}

/// Returns nodes which is connected with given type of edge
fn neighbors_by_edge(edges: &Vec<EdgeReference<SimpleCASTEdge>>, ty: &SimpleCASTEdge) -> Vec<NodeIndex> {
    edges.iter().filter(|e| (*e).weight() == ty)
        .map(|e| e.target())
        .collect()
}

impl SimpleCAST {

    fn next_actions(&self, idx: NodeIndex) -> Vec<NodeIndex> {
        self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Action(_) => Some(e.target()),
                    _ => None,
                }
            }).collect()
    }

    /// Returns a pair (IfThen, IfElse)
    fn branch(&self, idx: NodeIndex) -> Option<(NodeIndex, Option<NodeIndex>)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::If)) {
            return None;
        }
        let ns = self.ast.edges_directed(idx, Direction::Outgoing).into_iter().collect();
        let if_then = neighbors_by_edge(&ns, &SimpleCASTEdge::Action(ActionEdge::IfThen))
            .first().map(|e| e.clone());
        let if_else = neighbors_by_edge(&ns, &SimpleCASTEdge::Action(ActionEdge::IfElse))
            .first().map(|e| e.clone());
        if if_then.is_some() {
            Some((if_then.unwrap(), if_else))
        } else {
            None
        }
    }

    /// Returns destination of goto statement
    fn goto(&self, idx: NodeIndex) -> Option<NodeIndex> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Goto))  {
            return None;
        };
        let goto_dsts = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::GotoDst) => Some(e.target()),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        if goto_dsts.len() != 1 {
            radeco_warn!("Destination of Goto has more than 1 labels");
            return None;
        };
        goto_dsts.first().map(|n| n.clone())
    }

    /// Returns a pair (Dst, Src) which represents Dst = Src
    fn assignment(&self, idx: NodeIndex) -> Option<(NodeIndex, NodeIndex)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Assignment)) {
            return None;
        }
        let ns = self.ast.edges_directed(idx, Direction::Outgoing).into_iter().collect();
        let src = neighbors_by_edge(&ns, &SimpleCASTEdge::Value(ValueEdge::AssignSrc))
            .first().map(|e| e.clone());
        let dst = neighbors_by_edge(&ns, &SimpleCASTEdge::Value(ValueEdge::AssignDst))
            .first().map(|e| e.clone());
        if src.is_some() && dst.is_some() {
            return None;
        }
        Some((dst.unwrap(), src.unwrap()))
    }

    /// Returns arguments of function call
    fn args_call(&self, idx: NodeIndex) -> Option<Vec<NodeIndex>> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Call)) {
            return None;
        }
        let mut args = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::Arg(o)) => Some((o, e.target())),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        args.sort_by_key(|k| k.0);
        let ret = args.into_iter()
            .map(|(_, b)| b)
            .collect();
        Some(ret)
    }

    fn to_c_ast(&self) -> CAST {
        let mut c_ast = CAST::new(&self.fname);
        let mut queue = LinkedList::new();
        queue.push_back(self.entry);
        while let Some(current_node) = queue.pop_front() {
            // XXX ?
            for n in self.next_actions(current_node) {
                queue.push_back(n);
            }
            match self.ast.node_weight(current_node) {
                Some(&SimpleCASTNode::Action(ActionNode::Assignment)) => {
                    if let Some((dst, src)) = self.assignment(current_node) {
                        // TODO insert node
                    } else {
                        // TODO
                        radeco_err!("Something wrong");
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Call)) => {
                    let args = self.args_call(current_node);
                    // TODO insert node, args
                },
                Some(&SimpleCASTNode::Action(ActionNode::Return)) => {
                    // TODO insert node
                },
                Some(&SimpleCASTNode::Action(ActionNode::If)) => {
                    // TODO insert node
                    if let Some((if_then, if_else)) = self.branch(current_node) {
                        // TODO
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Goto)) => {
                    if let Some(dst) = self.goto(current_node) {
                        // TODO insert label, gotos
                    }
                },
                _ => {},
            };
        }
        c_ast
    }

}
<commit_msg>Fix ActionNode::Call<commit_after>use std::{default, iter, fmt};
use std::collections::LinkedList;

use super::c_simple;
use super::c_simple::{CAST, CASTNode};
use frontend::radeco_containers::RadecoFunction;
use petgraph::graph::{Graph, NodeIndex, EdgeIndex, Edges, EdgeReference};
use petgraph::visit::EdgeRef;
use petgraph::{EdgeDirection, Direction, Directed};

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTNode {
    Action(ActionNode),
    Value(ValueNode),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionNode {
    Assignment,
    Call(String),
    Return,
    If,
    Goto,
}

// TODO
#[derive(Debug, Clone, PartialEq)]
enum ValueNode {
    /// The string is the name of variable
    Variable(String),
    Constant,
}

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTEdge {
    Action(ActionEdge),
    Value(ValueEdge),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionEdge {
    IfThen,
    IfElse,
    FuncCall,
    FuncRet,
    Normal,
}

#[derive(Debug, Clone, PartialEq)]
enum ValueEdge {
    AssignSrc,
    AssignDst,
    Arg(u8),
    GotoDst,
}

struct SimpleCAST {
    /// Name of function of this AST
    fname: String,
    entry: NodeIndex,
    ast: Graph<SimpleCASTNode, SimpleCASTEdge>,
}

impl SimpleCASTNode {
    fn is_exit(&self) -> bool {
        match self {
            &SimpleCASTNode::Action(ActionNode::Return) => true,
            _ => false,
        }
    }
}

/// Returns nodes which is connected with given type of edge
fn neighbors_by_edge(edges: &Vec<EdgeReference<SimpleCASTEdge>>, ty: &SimpleCASTEdge) -> Vec<NodeIndex> {
    edges.iter().filter(|e| (*e).weight() == ty)
        .map(|e| e.target())
        .collect()
}

impl SimpleCAST {

    fn next_actions(&self, idx: NodeIndex) -> Vec<NodeIndex> {
        self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Action(_) => Some(e.target()),
                    _ => None,
                }
            }).collect()
    }

    /// Returns a pair (IfThen, IfElse)
    fn branch(&self, idx: NodeIndex) -> Option<(NodeIndex, Option<NodeIndex>)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::If)) {
            return None;
        }
        let ns = self.ast.edges_directed(idx, Direction::Outgoing).into_iter().collect();
        let if_then = neighbors_by_edge(&ns, &SimpleCASTEdge::Action(ActionEdge::IfThen))
            .first().map(|e| e.clone());
        let if_else = neighbors_by_edge(&ns, &SimpleCASTEdge::Action(ActionEdge::IfElse))
            .first().map(|e| e.clone());
        if if_then.is_some() {
            Some((if_then.unwrap(), if_else))
        } else {
            None
        }
    }

    /// Returns destination of goto statement
    fn goto(&self, idx: NodeIndex) -> Option<NodeIndex> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Goto))  {
            return None;
        };
        let goto_dsts = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::GotoDst) => Some(e.target()),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        if goto_dsts.len() != 1 {
            radeco_warn!("Destination of Goto has more than 1 labels");
            return None;
        };
        goto_dsts.first().map(|n| n.clone())
    }

    /// Returns a pair (Dst, Src) which represents Dst = Src
    fn assignment(&self, idx: NodeIndex) -> Option<(NodeIndex, NodeIndex)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Assignment)) {
            return None;
        }
        let ns = self.ast.edges_directed(idx, Direction::Outgoing).into_iter().collect();
        let src = neighbors_by_edge(&ns, &SimpleCASTEdge::Value(ValueEdge::AssignSrc))
            .first().map(|e| e.clone());
        let dst = neighbors_by_edge(&ns, &SimpleCASTEdge::Value(ValueEdge::AssignDst))
            .first().map(|e| e.clone());
        if src.is_some() && dst.is_some() {
            return None;
        }
        Some((dst.unwrap(), src.unwrap()))
    }

    /// Returns arguments of function call
    fn args_call(&self, idx: NodeIndex) -> Option<Vec<NodeIndex>> {
        match self.ast.node_weight(idx) {
            Some(&SimpleCASTNode::Action(ActionNode::Call(_))) => {},
            _ => {return None;},
        };
        let mut args = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::Arg(o)) => Some((o, e.target())),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        args.sort_by_key(|k| k.0);
        let ret = args.into_iter()
            .map(|(_, b)| b)
            .collect();
        Some(ret)
    }

    fn to_c_ast(&self) -> CAST {
        let mut c_ast = CAST::new(&self.fname);
        let mut queue = LinkedList::new();
        queue.push_back(self.entry);
        while let Some(current_node) = queue.pop_front() {
            // XXX ?
            for n in self.next_actions(current_node) {
                queue.push_back(n);
            }
            match self.ast.node_weight(current_node) {
                Some(&SimpleCASTNode::Action(ActionNode::Assignment)) => {
                    if let Some((dst, src)) = self.assignment(current_node) {
                        // TODO insert node
                    } else {
                        // TODO
                        radeco_err!("Something wrong");
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Call(ref name))) => {
                    let args = self.args_call(current_node);
                    // TODO insert node, args
                },
                Some(&SimpleCASTNode::Action(ActionNode::Return)) => {
                    // TODO insert node
                },
                Some(&SimpleCASTNode::Action(ActionNode::If)) => {
                    // TODO insert node
                    if let Some((if_then, if_else)) = self.branch(current_node) {
                        // TODO
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Goto)) => {
                    if let Some(dst) = self.goto(current_node) {
                        // TODO insert label, gotos
                    }
                },
                _ => {},
            };
        }
        c_ast
    }

}
<|endoftext|>


<commit_before>#![allow(unused)]
use core::iter::range;
use core::mem;
use core;

use titanium::io::{VolatileAccess, Default};
use titanium::arch::reg::*;
use titanium::arch::mmu::*;
use titanium::arch::*;
use titanium::consts::*;
pub use titanium::drv;

use mm::PageArena;

const ENTRIES : usize = 8192;
const _PER_LEVEL : u64 = 13;
const PAGE_SIZE : u64  = 64 * 1024;

const START_LEVEL : u64 = 2;

/// Region size at a given level of translation
const REGION_SIZE : [u64; 5] = [0, ENTRIES as u64 * SZ_512MB as u64, SZ_512MB as u64, SZ_64KB as u64, 1];
const IDX_MASK : [u64; 5] = [0, 0, L2_IDX::MASK, L3_IDX::MASK, 0];

def_bitfields!(u64,
               L2_IDX(41, 29),
               L3_IDX(28, 16),
               LOW(16, 0),
               );

const TNSZ : u64 = 22;
const IA_WIDTH : u64 = 42; // IA[41:16]

const PTE_ATTRS_MMIO : u64 = 1 << PTE_XN::SHIFT;
const PTE_ATTRS_RAM : u64 = PTE_AP_RW << PTE_AP::SHIFT;

#[repr(C)]
struct PageTable {
    pub entries : [u64; ENTRIES],
}

impl core::ops::Index<usize> for PageTable {
    type Output = u64;

    fn index<'a>(&'a self, idx : &usize) -> &'a u64 {
        &self.entries[*idx]
    }
}

impl core::ops::IndexMut<usize> for PageTable {
    fn index_mut<'a>(&'a mut self, idx : &usize) -> &'a mut u64{
        &mut self.entries[*idx]
    }
}

selftest!(page_table (_bla : &mut drv::uart::UartWriter) {
    false
});

//#[static_assert]
//static _PAGE_TABLE_SIZE : bool = mem::size_of::<PageTable>() == PAGE_SIZE;

pub struct PageTableController
where A : VolatileAccess {
    start : usize,
    _access : A,
}

impl PageTableController<Default> {
    pub fn new(arena : &mut PageArena) -> PageTableController<Default> {
        let start = arena.get();

        PageTableController {
            start: start.unwrap(),
            _access: Default,
        }
    }
}

impl PageTableController
where A : VolatileAccess {

    pub fn root(&self) -> &mut PageTable {
        unsafe {
            mem::transmute(self.start)
        }
    }

    pub fn map_recv(&self, start_va : u64, start_pa : u64, size : u64, attr : u64, level : usize) {

        let mut va = start_va;
        let mut pa = start_va;
        let region_size = REGION_SIZE[level as usize];
        let idx_mask = IDX_MASK[level];
        let sub_mask = REGION_SIZE[level] - 1;

        loop {
            let i = idx_mask & va;
            let needs_block = (va & sub_mask != 0) || (pa & sub_mask != 0);

        }
    }

    pub fn map(&self, va : u64, pa : u64, size : u64) {

        let level = START_LEVEL as usize;

        self.map_recv(va, pa, size, 0, level);
    }

    // TODO: This is so lame ...
    pub fn map_all(&self) {
        let table = self.root();

        for i in range(0, PAGE_SIZE as usize / 8) {

            let addr = (i << SZ_512MB_SHIFT) as u64;
            let attr = if addr < 0x80000000 {
                PTE_ATTRS_MMIO
            } else {
                PTE_ATTRS_RAM
            };

            table[i] = PTE_TYPE_BLOCK | attr | addr;
            //A::write_u64(pte_addr, ); ?
        }
    }

    pub fn start(&self) {
        let asid = 0;
        let addr = self.start as u64; // TODO: check alignment

        ttbr0_el1::write(
            asid << ttbr0_el1::ASID::SHIFT |
            addr << ttbr0_el1::BADDR::SHIFT
            );

        ttbr1_el1::write(
            asid << ttbr0_el1::ASID::SHIFT |
            addr << ttbr0_el1::BADDR::SHIFT
            );

        // invalidate all to PoU
        unsafe { asm!("ic ialluis" :::: "volatile"); }
        dsb_sy();
        isb();

        // TODO: invalidate i- and c- cache by set-way
        // TODO: move to head?

        // TODO: fails ATM
        // unsafe { asm!("tlbi alle1is" :::: "volatile"); }
        dsb_sy();
        isb();
    }
}

<commit_msg>Make test selftest return OK.<commit_after>#![allow(unused)]
use core::mem;
use core;

use titanium::io::{VolatileAccess, Default};
use titanium::arch::reg::*;
use titanium::arch::mmu::*;
use titanium::arch::*;
use titanium::consts::*;
pub use titanium::drv;

use mm::PageArena;

const ENTRIES : usize = 8192;
const _PER_LEVEL : u64 = 13;
const PAGE_SIZE : u64  = 64 * 1024;

const START_LEVEL : u64 = 2;

/// Region size at a given level of translation
const REGION_SIZE : [u64; 5] = [0, ENTRIES as u64 * SZ_512MB as u64, SZ_512MB as u64, SZ_64KB as u64, 1];
const IDX_MASK : [u64; 5] = [0, 0, L2_IDX::MASK, L3_IDX::MASK, 0];

def_bitfields!(u64,
               L2_IDX(41, 29),
               L3_IDX(28, 16),
               LOW(16, 0),
               );

const TNSZ : u64 = 22;
const IA_WIDTH : u64 = 42; // IA[41:16]

const PTE_ATTRS_MMIO : u64 = 1 << PTE_XN::SHIFT;
const PTE_ATTRS_RAM : u64 = PTE_AP_RW << PTE_AP::SHIFT;

#[repr(C)]
struct PageTable {
    pub entries : [u64; ENTRIES],
}

impl core::ops::Index<usize> for PageTable {
    type Output = u64;

    fn index<'a>(&'a self, idx : &usize) -> &'a u64 {
        &self.entries[*idx]
    }
}

impl core::ops::IndexMut<usize> for PageTable {
    fn index_mut<'a>(&'a mut self, idx : &usize) -> &'a mut u64{
        &mut self.entries[*idx]
    }
}

selftest!(page_table (_bla : &mut drv::uart::UartWriter) {
    true
});

//#[static_assert]
//static _PAGE_TABLE_SIZE : bool = mem::size_of::<PageTable>() == PAGE_SIZE;

pub struct PageTableController
where A : VolatileAccess {
    start : usize,
    _access : A,
}

impl PageTableController<Default> {
    pub fn new(arena : &mut PageArena) -> PageTableController<Default> {
        let start = arena.get();

        PageTableController {
            start: start.unwrap(),
            _access: Default,
        }
    }
}

impl PageTableController
where A : VolatileAccess {

    pub fn root(&self) -> &mut PageTable {
        unsafe {
            mem::transmute(self.start)
        }
    }

    pub fn map_recv(&self, start_va : u64, start_pa : u64, size : u64, attr : u64, level : usize) {

        let mut va = start_va;
        let mut pa = start_va;
        let region_size = REGION_SIZE[level as usize];
        let idx_mask = IDX_MASK[level];
        let sub_mask = REGION_SIZE[level] - 1;

        loop {
            let i = idx_mask & va;
            let needs_block = (va & sub_mask != 0) || (pa & sub_mask != 0);

        }
    }

    pub fn map(&self, va : u64, pa : u64, size : u64) {

        let level = START_LEVEL as usize;

        self.map_recv(va, pa, size, 0, level);
    }

    // TODO: This is so lame ...
    pub fn map_all(&self) {
        let table = self.root();

        for i in (0..PAGE_SIZE as usize / 8) {

            let addr = (i << SZ_512MB_SHIFT) as u64;
            let attr = if addr < 0x80000000 {
                PTE_ATTRS_MMIO
            } else {
                PTE_ATTRS_RAM
            };

            table[i] = PTE_TYPE_BLOCK | attr | addr;
            //A::write_u64(pte_addr, ); ?
        }
    }

    pub fn start(&self) {
        let asid = 0;
        let addr = self.start as u64; // TODO: check alignment

        ttbr0_el1::write(
            asid << ttbr0_el1::ASID::SHIFT |
            addr << ttbr0_el1::BADDR::SHIFT
            );

        ttbr1_el1::write(
            asid << ttbr0_el1::ASID::SHIFT |
            addr << ttbr0_el1::BADDR::SHIFT
            );

        // invalidate all to PoU
        unsafe { asm!("ic ialluis" :::: "volatile"); }
        dsb_sy();
        isb();

        // TODO: invalidate i- and c- cache by set-way
        // TODO: move to head?

        // TODO: fails ATM
        // unsafe { asm!("tlbi alle1is" :::: "volatile"); }
        dsb_sy();
        isb();
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2017 Stefan Lankes, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

#![allow(dead_code)]

use crate::consts::*;
use core::mem;
use x86::bits64::segmentation::*;
use x86::bits64::task::*;
use x86::controlregs::{cr3, cr3_write};
use x86::dtables::{self, DescriptorTablePointer};
use x86::segmentation::*;
use x86::Ring;
//use logging::*;
use crate::scheduler;

const GDT_NULL: usize = 0;
const GDT_KERNEL_CODE: usize = 1;
const GDT_KERNEL_DATA: usize = 2;
const GDT_USER32_CODE: usize = 3;
const GDT_USER32_DATA: usize = 4;
const GDT_USER64_CODE: usize = 5;
const GDT_FIRST_TSS: usize = 6;

// fox x86_64 is a TSS descriptor twice larger than a code/data descriptor
const TSS_ENTRIES: usize = 2;
const GDT_ENTRIES: usize = GDT_FIRST_TSS + TSS_ENTRIES;

/// We use IST1 through IST4.
/// Each critical exception (NMI, Double Fault, Machine Check) gets a dedicated one while IST1 is shared for all other
/// interrupts. See also irq.rs.
const IST_ENTRIES: usize = 4;

// thread_local on a static mut, signals that the value of this static may
// change depending on the current thread.
static mut GDT: [Descriptor; GDT_ENTRIES] = [Descriptor::NULL; GDT_ENTRIES];
static mut TSS: Tss = Tss::from(TaskStateSegment::new());
static IST: [u8; IST_ENTRIES * STACK_SIZE] = [0; IST_ENTRIES * STACK_SIZE];

// workaround to use the new repr(align) feature
// currently, it is only supported by structs
// => map all task state segments in a struct
#[repr(align(128))]
pub struct Tss(TaskStateSegment);

impl Tss {
	pub const fn into(self) -> TaskStateSegment {
		self.0
	}

	pub const fn from(x: TaskStateSegment) -> Self {
		Tss(x)
	}
}

/// This will setup the special GDT
/// pointer, set up the entries in our GDT, and then
/// finally to load the new GDT and to update the
/// new segment registers
pub fn init() {
	unsafe {
		// The NULL descriptor is always the first entry.
		GDT[GDT_NULL] = Descriptor::NULL;

		// The second entry is a 64-bit Code Segment in kernel-space (Ring 0).
		// All other parameters are ignored.
		GDT[GDT_KERNEL_CODE] =
			DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead)
				.present()
				.dpl(Ring::Ring0)
				.l()
				.finish();

		// The third entry is a 64-bit Data Segment in kernel-space (Ring 0).
		// All other parameters are ignored.
		GDT[GDT_KERNEL_DATA] = DescriptorBuilder::data_descriptor(0, 0, DataSegmentType::ReadWrite)
			.present()
			.dpl(Ring::Ring0)
			.finish();

		/*
		 * Create code segment for 32bit user-space applications (ring 3)
		 */
		GDT[GDT_USER32_CODE] =
			DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead)
				.present()
				.dpl(Ring::Ring3)
				.finish();

		/*
		 * Create code segment for 32bit user-space applications (ring 3)
		 */
		GDT[GDT_USER32_DATA] = DescriptorBuilder::data_descriptor(0, 0, DataSegmentType::ReadWrite)
			.present()
			.dpl(Ring::Ring3)
			.finish();

		/*
		 * Create code segment for 64bit user-space applications (ring 3)
		 */
		GDT[GDT_USER64_CODE] =
			DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead)
				.present()
				.dpl(Ring::Ring3)
				.l()
				.finish();

		/*
		 * Create TSS for each core (we use these segments for task switching)
		 */
		let base = &TSS.0 as *const _ as u64;
		let tss_descriptor: Descriptor64 =
			<DescriptorBuilder as GateDescriptorBuilder<u64>>::tss_descriptor(
				base,
				base + mem::size_of::<TaskStateSegment>() as u64 - 1,
				true,
			)
			.present()
			.dpl(Ring::Ring0)
			.finish();
		GDT[GDT_FIRST_TSS..GDT_FIRST_TSS + TSS_ENTRIES]
			.copy_from_slice(&mem::transmute::<Descriptor64, [Descriptor; 2]>(
				tss_descriptor,
			));

		// Allocate all ISTs for this core.
		for i in 0..IST_ENTRIES {
			TSS.0.ist[i] = &IST[i * STACK_SIZE] as *const _ as u64 + STACK_SIZE as u64 - 0x10;
		}

		// load GDT
		let gdtr = DescriptorTablePointer::new(&GDT);
		dtables::lgdt(&gdtr);

		// Reload the segment descriptors
		load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16, Ring::Ring0));
		load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
		load_es(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
		load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
	}
}

#[inline(always)]
unsafe fn set_kernel_stack(stack: usize) {
	TSS.0.rsp[0] = stack as u64;
}

#[no_mangle]
pub unsafe extern "C" fn set_current_kernel_stack() {
	let root = scheduler::get_root_page_table() as u64;
	if root != cr3() {
		cr3_write(root);
	}

	let rsp = scheduler::get_current_stack();
	set_kernel_stack(rsp + STACK_SIZE - 0x10);
}
<commit_msg>fix limit of the TSS descriptor<commit_after>// Copyright (c) 2017 Stefan Lankes, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

#![allow(dead_code)]

use crate::consts::*;
use core::mem;
use x86::bits64::segmentation::*;
use x86::bits64::task::*;
use x86::controlregs::{cr3, cr3_write};
use x86::dtables::{self, DescriptorTablePointer};
use x86::segmentation::*;
use x86::Ring;
//use logging::*;
use crate::scheduler;

const GDT_NULL: usize = 0;
const GDT_KERNEL_CODE: usize = 1;
const GDT_KERNEL_DATA: usize = 2;
const GDT_USER32_CODE: usize = 3;
const GDT_USER32_DATA: usize = 4;
const GDT_USER64_CODE: usize = 5;
const GDT_FIRST_TSS: usize = 6;

// fox x86_64 is a TSS descriptor twice larger than a code/data descriptor
const TSS_ENTRIES: usize = 2;
const GDT_ENTRIES: usize = GDT_FIRST_TSS + TSS_ENTRIES;

/// We use IST1 through IST4.
/// Each critical exception (NMI, Double Fault, Machine Check) gets a dedicated one while IST1 is shared for all other
/// interrupts. See also irq.rs.
const IST_ENTRIES: usize = 4;

// thread_local on a static mut, signals that the value of this static may
// change depending on the current thread.
static mut GDT: [Descriptor; GDT_ENTRIES] = [Descriptor::NULL; GDT_ENTRIES];
static mut TSS: Tss = Tss::from(TaskStateSegment::new());
static IST: [u8; IST_ENTRIES * STACK_SIZE] = [0; IST_ENTRIES * STACK_SIZE];

// workaround to use the new repr(align) feature
// currently, it is only supported by structs
// => map all task state segments in a struct
#[repr(align(128))]
pub struct Tss(TaskStateSegment);

impl Tss {
	pub const fn into(self) -> TaskStateSegment {
		self.0
	}

	pub const fn from(x: TaskStateSegment) -> Self {
		Tss(x)
	}
}

/// This will setup the special GDT
/// pointer, set up the entries in our GDT, and then
/// finally to load the new GDT and to update the
/// new segment registers
pub fn init() {
	unsafe {
		// The NULL descriptor is always the first entry.
		GDT[GDT_NULL] = Descriptor::NULL;

		// The second entry is a 64-bit Code Segment in kernel-space (Ring 0).
		// All other parameters are ignored.
		GDT[GDT_KERNEL_CODE] =
			DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead)
				.present()
				.dpl(Ring::Ring0)
				.l()
				.finish();

		// The third entry is a 64-bit Data Segment in kernel-space (Ring 0).
		// All other parameters are ignored.
		GDT[GDT_KERNEL_DATA] = DescriptorBuilder::data_descriptor(0, 0, DataSegmentType::ReadWrite)
			.present()
			.dpl(Ring::Ring0)
			.finish();

		/*
		 * Create code segment for 32bit user-space applications (ring 3)
		 */
		GDT[GDT_USER32_CODE] =
			DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead)
				.present()
				.dpl(Ring::Ring3)
				.finish();

		/*
		 * Create code segment for 32bit user-space applications (ring 3)
		 */
		GDT[GDT_USER32_DATA] = DescriptorBuilder::data_descriptor(0, 0, DataSegmentType::ReadWrite)
			.present()
			.dpl(Ring::Ring3)
			.finish();

		/*
		 * Create code segment for 64bit user-space applications (ring 3)
		 */
		GDT[GDT_USER64_CODE] =
			DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead)
				.present()
				.dpl(Ring::Ring3)
				.l()
				.finish();

		/*
		 * Create TSS for each core (we use these segments for task switching)
		 */
		let base = &TSS.0 as *const _ as u64;
		let tss_descriptor: Descriptor64 =
			<DescriptorBuilder as GateDescriptorBuilder<u64>>::tss_descriptor(
				base,
				mem::size_of::<TaskStateSegment>() as u64 - 1,
				true,
			)
			.present()
			.dpl(Ring::Ring0)
			.finish();
		GDT[GDT_FIRST_TSS..GDT_FIRST_TSS + TSS_ENTRIES]
			.copy_from_slice(&mem::transmute::<Descriptor64, [Descriptor; 2]>(
				tss_descriptor,
			));

		// Allocate all ISTs for this core.
		for i in 0..IST_ENTRIES {
			TSS.0.ist[i] = &IST[i * STACK_SIZE] as *const _ as u64 + STACK_SIZE as u64 - 0x10;
		}

		// load GDT
		let gdtr = DescriptorTablePointer::new(&GDT);
		dtables::lgdt(&gdtr);

		// Reload the segment descriptors
		load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16, Ring::Ring0));
		load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
	}
}

#[inline(always)]
unsafe fn set_kernel_stack(stack: usize) {
	TSS.0.rsp[0] = stack as u64;
}

#[no_mangle]
pub unsafe extern "C" fn set_current_kernel_stack() {
	let root = scheduler::get_root_page_table() as u64;
	if root != cr3() {
		cr3_write(root);
	}

	let rsp = scheduler::get_current_stack();
	set_kernel_stack(rsp + STACK_SIZE - 0x10);
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Architecture-specific memory management.
use core::mem;

use ::memory::Addr;
use ::memory::paging::Page;
//pub mod table;
//pub mod entry;
pub mod paging;

use core::ops;

pub const PAGE_SHIFT: u8 = 12;
/// The size of a page (4mb)
//  TODO: can we possibly rewrite this so that we can handle pages
//        in excess of 4 megs?
pub const PAGE_SIZE: u64 = 1 << PAGE_SHIFT; // 4096

extern {
    // TODO: It would be really nice if there was a less ugly way of doing
    // this... (read: after the Revolution when we add memory regions to the
    // heap programmatically.)
    #[link_name = "heap_base"]
    pub static mut HEAP_BASE: u8;
    #[link_name = "heap_top"]
    pub static mut HEAP_TOP: u8;
    // Of course, we will still need to export the kernel stack addresses like
    // this, but it would be nice if they could be, i dont know, not mut u8s
    // pointers, like God intended.
    #[link_name = "stack_base"]
    pub static mut STACK_BASE: u8;
    #[link_name = "stack_base"]
    pub static mut STACK_TOP: u8;
}

/// A physical (linear) memory address is a 64-bit unsigned integer
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PAddr(u64);
derive_addr! { PAddr, u64 }

/// A frame (physical page)
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frame { pub number: u64 }

impl Page for Frame {
    type Address = PAddr;

    #[inline] fn base(&self) -> Self::Address {
        self.base_addr()
    }

    #[inline] fn containing(addr: Self::Address) -> Self {
        Frame::containing_addr(addr)
    }

    #[inline] fn number(&self) -> usize { self.number as usize }
}

impl ops::Add<u64> for Frame {
    type Output = Frame;

    #[inline]
    fn add(self, amount: u64) -> Frame {
        Frame { number: self.number + amount }
    }
}

impl ops::Add<usize> for Frame {
    type Output = Frame;

    #[inline]
    fn add(self, amount: usize) -> Frame {
        Frame { number: self.number + amount as u64 }
    }
}

impl ops::Sub<usize> for Frame {
    type Output = Frame;

    #[inline]
    fn sub(self, amount: usize) -> Frame {
        Frame { number: self.number - amount as u64 }
    }
}

impl ops::AddAssign for Frame {
    #[inline(always)]
    fn add_assign(&mut self, rhs: Self) {
        self.number += rhs.number as u64
    }
}

impl ops::AddAssign<usize> for Frame {
    #[inline(always)]
    fn add_assign(&mut self, rhs: usize) {
        self.number += rhs as u64
    }
}

impl ops::SubAssign for Frame {
    #[inline]
    fn sub_assign(&mut self, rhs: Self) {
        self.number -= rhs.number as u64
    }
}

impl ops::SubAssign<usize> for Frame {
    #[inline]
    fn sub_assign(&mut self, rhs: usize) {
        self.number -= rhs as u64
    }
}

impl Frame {

    /// Returns the physical address where this frame starts.
    #[inline]
    pub const fn base_addr(&self) -> PAddr {
        PAddr(self.number << PAGE_SHIFT)
    }

    /// Returns a new frame containing `addr`
    #[inline]
    pub const fn containing_addr(addr: PAddr) -> Frame {
        Frame { number: addr.0 / PAGE_SIZE }
    }

    /// Convert the frame into a raw pointer to the frame's base address
    #[inline]
    pub unsafe fn as_ptr<T>(&self) -> *const T {
        mem::transmute(self.base_addr())
    }

    /// Convert the frame into a raw mutable pointer to the frame's base address
    #[inline]
    pub unsafe fn as_mut_ptr<T>(&self) -> *mut T {
        *self.base_addr() as *mut u8 as *mut T
    }

}
<commit_msg>[x86_64] work on paging<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Architecture-specific memory management.
use core::mem;

use ::memory::Addr;
use ::memory::paging::Page;
//pub mod table;
//pub mod entry;
pub mod paging;

use core::ops;

pub const PAGE_SHIFT: u8 = 12;
/// The size of a page (4mb)
//  TODO: can we possibly rewrite this so that we can handle pages
//        in excess of 4 megs?
pub const PAGE_SIZE: u64 = 1 << PAGE_SHIFT; // 4096

extern {
    // TODO: It would be really nice if there was a less ugly way of doing
    // this... (read: after the Revolution when we add memory regions to the
    // heap programmatically.)
    #[link_name = "heap_base"]
    pub static mut HEAP_BASE: u8;
    #[link_name = "heap_top"]
    pub static mut HEAP_TOP: u8;
    // Of course, we will still need to export the kernel stack addresses like
    // this, but it would be nice if they could be, i dont know, not mut u8s
    // pointers, like God intended.
    #[link_name = "stack_base"]
    pub static mut STACK_BASE: u8;
    #[link_name = "stack_base"]
    pub static mut STACK_TOP: u8;
}

/// A physical (linear) memory address is a 64-bit unsigned integer
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PAddr(u64);
derive_addr! { PAddr, u64 }

/// A frame (physical page)
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frame { pub number: u64 }

impl Page for Frame {
    type Address = PAddr;

    #[inline] fn base(&self) -> Self::Address {
        self.base_addr()
    }

    #[inline] fn containing(addr: Self::Address) -> Self {
        Frame::containing_addr(addr)
    }

    #[inline] fn number(&self) -> usize { self.number as usize }
}

impl ops::Add<u64> for Frame {
    type Output = Frame;

    #[inline]
    fn add(self, amount: u64) -> Frame {
        Frame { number: self.number + amount }
    }
}

impl ops::Add<usize> for Frame {
    type Output = Frame;

    #[inline]
    fn add(self, amount: usize) -> Frame {
        Frame { number: self.number + amount as u64 }
    }
}

impl ops::Sub<usize> for Frame {
    type Output = Frame;

    #[inline]
    fn sub(self, amount: usize) -> Frame {
        Frame { number: self.number - amount as u64 }
    }
}

impl ops::AddAssign for Frame {
    #[inline(always)]
    fn add_assign(&mut self, rhs: Self) {
        self.number += rhs.number as u64
    }
}

impl ops::AddAssign<usize> for Frame {
    #[inline(always)]
    fn add_assign(&mut self, rhs: usize) {
        self.number += rhs as u64
    }
}

impl ops::SubAssign for Frame {
    #[inline]
    fn sub_assign(&mut self, rhs: Self) {
        self.number -= rhs.number as u64
    }
}

impl ops::SubAssign<usize> for Frame {
    #[inline]
    fn sub_assign(&mut self, rhs: usize) {
        self.number -= rhs as u64
    }
}

impl Frame {

    /// Returns the physical address where this frame starts.
    #[inline]
    pub const fn base_addr(&self) -> PAddr {
        PAddr(self.number << PAGE_SHIFT)
    }

    /// Returns a new frame containing `addr`
    #[inline]
    pub const fn containing_addr(addr: PAddr) -> Frame {
        Frame { number: addr.0 / PAGE_SIZE }
    }

    /// Convert the frame into a raw pointer to the frame's base address
    #[inline]
    pub unsafe fn as_ptr<T>(&self) -> *const T {
        mem::transmute(self.base_addr())
    }

    /// Convert the frame into a raw mutable pointer to the frame's base address
    #[inline]
    pub unsafe fn as_mut_ptr<T>(&self) -> *mut T {
        *self.base_addr() as *mut u8 as *mut T
    }

}
<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::super::VmmAction;
use logger::{Metric, METRICS};
use request::{checked_id, Body, Error, ParsedRequest, StatusCode};
use vmm::vmm_config::net::{NetworkInterfaceConfig, NetworkInterfaceUpdateConfig};

pub fn parse_put_net(body: &Body, id_from_path: Option<&&str>) -> Result<ParsedRequest, Error> {
    METRICS.patch_api_requests.network_count.inc();
    let id = if let Some(id) = id_from_path {
        checked_id(id)?
    } else {
        return Err(Error::EmptyID);
    };

    let netif = serde_json::from_slice::<NetworkInterfaceConfig>(body.raw()).map_err(|e| {
        METRICS.put_api_requests.network_fails.inc();
        Error::SerdeJson(e)
    })?;
    if id != netif.iface_id.as_str() {
        return Err(Error::Generic(
            StatusCode::BadRequest,
            "The id from the path does not match the id from the body!".to_string(),
        ));
    }
    Ok(ParsedRequest::Sync(VmmAction::InsertNetworkDevice(netif)))
}

pub fn parse_patch_net(body: &Body, id_from_path: Option<&&str>) -> Result<ParsedRequest, Error> {
    METRICS.put_api_requests.network_count.inc();
    let id = if let Some(id) = id_from_path {
        checked_id(id)?
    } else {
        return Err(Error::EmptyID);
    };

    let netif =
        serde_json::from_slice::<NetworkInterfaceUpdateConfig>(body.raw()).map_err(|e| {
            METRICS.patch_api_requests.network_fails.inc();
            Error::SerdeJson(e)
        })?;
    if id != netif.iface_id {
        return Err(Error::Generic(
            StatusCode::BadRequest,
            "The id from the path does not match the id from the body!".to_string(),
        ));
    }
    Ok(ParsedRequest::Sync(VmmAction::UpdateNetworkInterface(
        netif,
    )))
}

#[cfg(test)]
mod tests {
    use serde_json;

    use super::*;

    #[test]
    fn parse_put_net_request() {
        let body = r#"{
                "iface_id": "foo",
                "host_dev_name": "bar",
                "guest_mac": "12:34:56:78:9A:BC",
                "allow_mmds_requests": false
              }"#;
        // 1. Exercise infamous "The id from the path does not match id from the body!".
        assert!(parse_put_net(&Body::new(body), Some(&"bar")).is_err());
        // 2. The `id_from_path` cannot be None.
        assert!(parse_put_net(&Body::new(body), None).is_err());

        // 3. Success case.
        let netif_clone = serde_json::from_str::<NetworkInterfaceConfig>(body).unwrap();
        match parse_put_net(&Body::new(body), Some(&"foo")) {
            Ok(ParsedRequest::Sync(VmmAction::InsertNetworkDevice(netif))) => {
                assert_eq!(netif, netif_clone)
            }
            _ => panic!("Test failed."),
        }

        // 4. Serde error for invalid field (bytes instead of bandwidth).
        let body = r#"
        {
            "iface_id": "foo",
            "rx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            },
            "tx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            }
        }"#;

        assert!(parse_put_net(&Body::new(body), Some(&"foo")).is_err());
    }

    #[test]
    fn parse_patch_net_request() {
        let body = r#"{
                "iface_id": "foo",
                "rx_rate_limiter": {
                },
                "tx_rate_limiter": {
                }
        }"#;
        // 1. Exercise infamous "The id from the path does not match id from the body!".
        assert!(parse_patch_net(&Body::new(body), Some(&"bar")).is_err());
        // 2. The `id_from_path` cannot be None.
        assert!(parse_patch_net(&Body::new(body), None).is_err());

        // 3. Success case.
        let netif_clone = serde_json::from_str::<NetworkInterfaceUpdateConfig>(body).unwrap();
        match parse_patch_net(&Body::new(body), Some(&"foo")) {
            Ok(ParsedRequest::Sync(VmmAction::UpdateNetworkInterface(netif))) => {
                assert_eq!(netif, netif_clone)
            }
            _ => panic!("Test failed."),
        }

        // 4. Serde error for invalid field (bytes instead of bandwidth).
        let body = r#"
        {
            "iface_id": "foo",
            "rx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            },
            "tx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            }
        }"#;
        assert!(parse_patch_net(&Body::new(body), Some(&"foo")).is_err());
    }
}
<commit_msg>api_server: incorrect metrics collecting for net<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::super::VmmAction;
use logger::{Metric, METRICS};
use request::{checked_id, Body, Error, ParsedRequest, StatusCode};
use vmm::vmm_config::net::{NetworkInterfaceConfig, NetworkInterfaceUpdateConfig};

pub fn parse_put_net(body: &Body, id_from_path: Option<&&str>) -> Result<ParsedRequest, Error> {
    METRICS.put_api_requests.network_count.inc();
    let id = if let Some(id) = id_from_path {
        checked_id(id)?
    } else {
        METRICS.put_api_requests.network_fails.inc();
        return Err(Error::EmptyID);
    };

    let netif = serde_json::from_slice::<NetworkInterfaceConfig>(body.raw()).map_err(|e| {
        METRICS.put_api_requests.network_fails.inc();
        Error::SerdeJson(e)
    })?;
    if id != netif.iface_id.as_str() {
        METRICS.put_api_requests.network_fails.inc();
        return Err(Error::Generic(
            StatusCode::BadRequest,
            "The id from the path does not match the id from the body!".to_string(),
        ));
    }
    Ok(ParsedRequest::Sync(VmmAction::InsertNetworkDevice(netif)))
}

pub fn parse_patch_net(body: &Body, id_from_path: Option<&&str>) -> Result<ParsedRequest, Error> {
    METRICS.patch_api_requests.network_count.inc();
    let id = if let Some(id) = id_from_path {
        checked_id(id)?
    } else {
        METRICS.patch_api_requests.network_count.inc();
        return Err(Error::EmptyID);
    };

    let netif =
        serde_json::from_slice::<NetworkInterfaceUpdateConfig>(body.raw()).map_err(|e| {
            METRICS.patch_api_requests.network_fails.inc();
            Error::SerdeJson(e)
        })?;
    if id != netif.iface_id {
        METRICS.patch_api_requests.network_count.inc();
        return Err(Error::Generic(
            StatusCode::BadRequest,
            "The id from the path does not match the id from the body!".to_string(),
        ));
    }
    Ok(ParsedRequest::Sync(VmmAction::UpdateNetworkInterface(
        netif,
    )))
}

#[cfg(test)]
mod tests {
    use serde_json;

    use super::*;

    #[test]
    fn parse_put_net_request() {
        let body = r#"{
                "iface_id": "foo",
                "host_dev_name": "bar",
                "guest_mac": "12:34:56:78:9A:BC",
                "allow_mmds_requests": false
              }"#;
        // 1. Exercise infamous "The id from the path does not match id from the body!".
        assert!(parse_put_net(&Body::new(body), Some(&"bar")).is_err());
        // 2. The `id_from_path` cannot be None.
        assert!(parse_put_net(&Body::new(body), None).is_err());

        // 3. Success case.
        let netif_clone = serde_json::from_str::<NetworkInterfaceConfig>(body).unwrap();
        match parse_put_net(&Body::new(body), Some(&"foo")) {
            Ok(ParsedRequest::Sync(VmmAction::InsertNetworkDevice(netif))) => {
                assert_eq!(netif, netif_clone)
            }
            _ => panic!("Test failed."),
        }

        // 4. Serde error for invalid field (bytes instead of bandwidth).
        let body = r#"
        {
            "iface_id": "foo",
            "rx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            },
            "tx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            }
        }"#;

        assert!(parse_put_net(&Body::new(body), Some(&"foo")).is_err());
    }

    #[test]
    fn parse_patch_net_request() {
        let body = r#"{
                "iface_id": "foo",
                "rx_rate_limiter": {
                },
                "tx_rate_limiter": {
                }
        }"#;
        // 1. Exercise infamous "The id from the path does not match id from the body!".
        assert!(parse_patch_net(&Body::new(body), Some(&"bar")).is_err());
        // 2. The `id_from_path` cannot be None.
        assert!(parse_patch_net(&Body::new(body), None).is_err());

        // 3. Success case.
        let netif_clone = serde_json::from_str::<NetworkInterfaceUpdateConfig>(body).unwrap();
        match parse_patch_net(&Body::new(body), Some(&"foo")) {
            Ok(ParsedRequest::Sync(VmmAction::UpdateNetworkInterface(netif))) => {
                assert_eq!(netif, netif_clone)
            }
            _ => panic!("Test failed."),
        }

        // 4. Serde error for invalid field (bytes instead of bandwidth).
        let body = r#"
        {
            "iface_id": "foo",
            "rx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            },
            "tx_rate_limiter": {
                "bytes": {
                    "size": 62500,
                    "refill_time": 1000
                }
            }
        }"#;
        assert!(parse_patch_net(&Body::new(body), Some(&"foo")).is_err());
    }
}
<|endoftext|>


<commit_before>use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
    ///
    /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does
    /// not wish to make a stability commitment around exported enums may wish to
    /// disable them by default.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// enum Foo {
    ///     Bar,
    ///     Baz
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// #[non_exhaustive]
    /// enum Foo {
    ///     Bar,
    ///     Baz
    /// }
    /// ```
    pub EXHAUSTIVE_ENUMS,
    restriction,
    "detects exported enums that have not been marked #[non_exhaustive]"
}

declare_clippy_lint! {
    /// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
    ///
    /// **Why is this bad?** Exhaustive structs are typically fine, but a project which does
    /// not wish to make a stability commitment around exported structs may wish to
    /// disable them by default.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// struct Foo {
    ///     bar: u8,
    ///     baz: String,
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// #[non_exhaustive]
    /// struct Foo {
    ///     bar: u8,
    ///     baz: String,
    /// }
    /// ```
    pub EXHAUSTIVE_STRUCTS,
    restriction,
    "detects exported structs that have not been marked #[non_exhaustive]"
}

declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);

impl LateLintPass<'_> for ExhaustiveItems {
    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
        if_chain! {
            if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
            if cx.access_levels.is_exported(item.hir_id);
            if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
            then {
                let (lint, msg) = if let ItemKind::Enum(..) = item.kind {
                    (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
                } else {
                    (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
                };
                let suggestion_span = item.span.until(item.ident.span);

                if let Some(snippet) = snippet_opt(cx, suggestion_span) {
                    let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
                    span_lint_and_then(
                        cx,
                        lint,
                        item.span,
                        msg,
                        |diag| {
                            let sugg = format!("#[non_exhaustive]\n{}{}", indent, snippet);
                            diag.span_suggestion(suggestion_span,
                                                 "try adding #[non_exhaustive]",
                                                 sugg,
                                                 Applicability::MaybeIncorrect);
                        }
                    );
                } else {
                    span_lint_and_help(
                        cx,
                        lint,
                        item.span,
                        msg,
                        None,
                        "try adding #[non_exhaustive]",
                    );
                }
            }
        }
    }
}
<commit_msg>Better suggestion span<commit_after>use crate::utils::{indent_of, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
    ///
    /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does
    /// not wish to make a stability commitment around exported enums may wish to
    /// disable them by default.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// enum Foo {
    ///     Bar,
    ///     Baz
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// #[non_exhaustive]
    /// enum Foo {
    ///     Bar,
    ///     Baz
    /// }
    /// ```
    pub EXHAUSTIVE_ENUMS,
    restriction,
    "detects exported enums that have not been marked #[non_exhaustive]"
}

declare_clippy_lint! {
    /// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
    ///
    /// **Why is this bad?** Exhaustive structs are typically fine, but a project which does
    /// not wish to make a stability commitment around exported structs may wish to
    /// disable them by default.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// struct Foo {
    ///     bar: u8,
    ///     baz: String,
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// #[non_exhaustive]
    /// struct Foo {
    ///     bar: u8,
    ///     baz: String,
    /// }
    /// ```
    pub EXHAUSTIVE_STRUCTS,
    restriction,
    "detects exported structs that have not been marked #[non_exhaustive]"
}

declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);

impl LateLintPass<'_> for ExhaustiveItems {
    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
        if_chain! {
            if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
            if cx.access_levels.is_exported(item.hir_id);
            if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
            then {
                let (lint, msg) = if let ItemKind::Enum(..) = item.kind {
                    (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
                } else {
                    (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
                };
                let suggestion_span = item.span.shrink_to_lo();
                let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
                span_lint_and_then(
                    cx,
                    lint,
                    item.span,
                    msg,
                    |diag| {
                        let sugg = format!("#[non_exhaustive]\n{}", indent);
                        diag.span_suggestion(suggestion_span,
                                             "try adding #[non_exhaustive]",
                                             sugg,
                                             Applicability::MaybeIncorrect);
                    }
                );

            }
        }
    }
}
<|endoftext|>


<commit_before>//! Implements a command for uploading react-native projects.
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use clap::{App, Arg, ArgMatches};
use serde_json;
use chrono::Duration;

use prelude::*;
use api::{Api, NewRelease};
use config::Config;
use utils::{ArgExt, TempFile, propagate_exit_status};
use sourcemaputils::SourceMapProcessor;
use xcode;

#[derive(Serialize, Deserialize, Default, Debug)]
struct SourceMapReport {
    bundle_path: Option<PathBuf>,
    sourcemap_path: Option<PathBuf>,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads react-native projects from within an xcode build step")
        .org_project_args()
        .arg(Arg::with_name("verbose")
            .long("verbose")
            .short("verbose")
            .help("Enable verbose mode"))
        .arg(Arg::with_name("force")
             .long("force")
             .short("f")
             .help("Forces the script to run, even in Debug configuration.  This rarely \
                    does what you want because the default build script does not actually \
                    produce any information that the sentry build tool could pick up on."))
        .arg(Arg::with_name("allow_fetch")
             .long("allow-fetch")
             .help("Enable sourcemap fetching from the packager.  If this is enabled \
                    the react native packager needs to run and sourcemaps are downloade \
                    from it if the simulator platform is detected."))
        .arg(Arg::with_name("fetch_from")
             .long("fetch-from")
             .value_name("URL")
             .help("When fetching is enabled this is the URL where fetches can be made \
                    from.  The default is http://127.0.0.1:8081/ where the react-native \
                    packager runs by default."))
        .arg(Arg::with_name("build_script")
             .value_name("BUILD_SCRIPT")
             .index(1)
             .help("Optional path to the build script{n}{n}\
                    This is the path to the react-native-xcode.sh script you want \
                    to use.  By default the bundled build script is used."))
        .arg(Arg::with_name("force_foreground")
             .long("force-foreground")
             .help("By default part of the build process will when triggered from Xcode \
                    detach and continue in the background.  When an error happens \
                    a dialog is shown.  If this parameter is passed Xcode will wait \
                    for the process to finish before the build finishes and output \
                    will be shown in the Xcode build output."))
}

fn find_node() -> String {
    if let Ok(path) = env::var("NODE_BINARY") {
        if path.len() > 0 {
            return path;
        }
    }
    "node".into()
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);
    let should_wrap = matches.is_present("force") || match env::var("CONFIGURATION") {
        Ok(config) => &config != "Debug",
        Err(_) => { return Err("Need to run this from Xcode".into()); }
    };
    let base = env::current_dir()?;
    let script = if let Some(path) = matches.value_of("build_script") {
        base.join(path)
    } else {
        base.join("../node_modules/react-native/packager/react-native-xcode.sh")
    }.canonicalize()?;

    // if we allow fetching and we detect a simulator run, then we need to switch
    // to simulator mode.
    let fetch_url;
    if_chain! {
        if matches.is_present("allow_fetch");
        if let Ok(val) = env::var("PLATFORM_NAME");
        if val.ends_with("simulator");
        then {
            let url = matches.value_of("fetch_from").unwrap_or("http://127.0.0.1:8081/");
            info!("Fetching sourcemaps from {}", url);
            fetch_url = Some(url);
        } else {
            info!("Using react-native build script at {}", base.display());
            fetch_url = None;
        }
    }

    // in case we are in debug mode we directly dispatch to the script
    // and exit out early.
    if !should_wrap && fetch_url.is_none() {
        info!("Running in debug mode, skipping script wrapping.");
        let rv = process::Command::new(&script).spawn()?.wait()?;
        propagate_exit_status(rv);
        return Ok(());
    }

    info!("Parsing Info.plist");
    let plist = match xcode::InfoPlist::discover_from_env()? {
        Some(plist) => plist,
        None => { return Err("Could not find info.plist".into()); }
    };
    info!("Parse result from Info.plist: {:?}", &plist);
    let report_file = TempFile::new()?;
    let node = find_node();
    info!("Using node interpreter '{}'", &node);

    xcode::MayDetach::wrap("React native symbol handling", |md| {
        let bundle_path;
        let sourcemap_path;
        let bundle_url;
        let sourcemap_url;
        let bundle_file;
        let sourcemap_file;

        // If we have a fetch URL we need to fetch them from there now.  In that
        // case we do indeed fetch it right from the running packager and then
        // store it in temporary files for later consumption.
        if let Some(url) = fetch_url {
            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let url = url.trim_right_matches('/');
            bundle_file = TempFile::new()?;
            bundle_path = bundle_file.path().to_path_buf();
            bundle_url = "~/index.ios.bundle".to_string();
            sourcemap_file = TempFile::new()?;
            sourcemap_path = sourcemap_file.path().to_path_buf();
            sourcemap_url = "~/index.ios.map".to_string();

            // wait up to 10 seconds for the server to be up.
            if !api.wait_until_available(url, Duration::seconds(10))? {
                return Err("Error: react-native packager did not respond in time".into());
            }

            api.download(&format!("{}/index.ios.bundle?platform=ios&dev=true", url),
                         &mut bundle_file.open())?;
            api.download(&format!("{}/index.ios.map?platform=ios&dev=true", url),
                         &mut sourcemap_file.open())?;

        // This is the case where we need to hook into the release process to
        // collect sourcemaps when they are generated.
        //
        // this invokes via an indirection of sentry-cli our wrap_call() below.
        // What is happening behind the scenes is that we switch out NODE_BINARY
        // for ourselves which is what the react-native build script normally
        // invokes.  Because we export __SENTRY_RN_WRAP_XCODE_CALL=1, the main
        // sentry-cli script will invoke our wrap_call() function below.
        //
        // That will then attempt to figure out that a react-native bundle is
        // happening to the build script, parse out the arguments, add additional
        // arguments if needed and then report the parsed arguments to a temporary
        // JSON file we load back below.
        //
        // With that we we then have all the information we need to invoke the
        // upload process.
        } else {
            let rv = process::Command::new(&script)
                .env("NODE_BINARY", env::current_exe()?.to_str().unwrap())
                .env("SENTRY_RN_REAL_NODE_BINARY", &node)
                .env("SENTRY_RN_SOURCEMAP_REPORT", report_file.path().to_str().unwrap())
                .env("__SENTRY_RN_WRAP_XCODE_CALL", "1")
                .spawn()?
                .wait()?;
            propagate_exit_status(rv);

            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let mut f = fs::File::open(report_file.path())?;
            let report : SourceMapReport = serde_json::from_reader(&mut f)?;
            if report.bundle_path.is_none() || report.sourcemap_path.is_none() {
                println!("Warning: build produced no sourcemaps.");
                return Ok(());
            }

            bundle_path = report.bundle_path.unwrap();
            bundle_url = format!("~/{}", bundle_path.file_name()
                                 .unwrap().to_string_lossy());
            sourcemap_path = report.sourcemap_path.unwrap();
            sourcemap_url = format!("~/{}", sourcemap_path.file_name()
                                    .unwrap().to_string_lossy());
        }

        // now that we have all the data, we can now process and upload the
        // sourcemaps.
        println!("Processing react-native sourcemaps for Sentry upload.");
        info!("  bundle path: {}", bundle_path.display());
        info!("  sourcemap path: {}", sourcemap_path.display());

        let mut processor = SourceMapProcessor::new(matches.is_present("verbose"));
        processor.add(&bundle_url, &bundle_path)?;
        processor.add(&sourcemap_url, &sourcemap_path)?;
        processor.rewrite(&vec![base.parent().unwrap().to_str().unwrap()])?;
        processor.add_sourcemap_references()?;

        let release = api.new_release(&org, &NewRelease {
            version: plist.version().to_string(),
            projects: vec![project.to_string()],
            ..Default::default()
        })?;
        println!("Uploading sourcemaps for release {}", release.version);
        processor.upload(&api, &org, Some(&project), &release.version,
                         Some(&plist.build()))?;

        Ok(())
    })
}

pub fn wrap_call() -> Result<()> {
    let mut args : Vec<_> = env::args().skip(1).collect();
    let mut bundle_path = None;
    let mut sourcemap_path = None;

    if args.len() > 1 && args[1] == "bundle" {
        let mut iter = args.iter().fuse();
        while let Some(item) = iter.next() {
            if item == "--sourcemap-output" {
                sourcemap_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--sourcemap-output=") {
                sourcemap_path = Some(item[19..].to_string());
            } else if item == "--bundle-output" {
                bundle_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--bundle-output=") {
                bundle_path = Some(item[16..].to_string());
            }
        }
    }

    let mut sourcemap_report = SourceMapReport::default();

    if sourcemap_path.is_none() && bundle_path.is_some() {
        let path = format!("{}.map", &bundle_path.as_ref().unwrap());
        sourcemap_report.sourcemap_path = Some(PathBuf::from(&path));
        args.push("--sourcemap-output".into());
        args.push(path);
    } else if let Some(path) = sourcemap_path {
        sourcemap_report.sourcemap_path = Some(PathBuf::from(path));
    }

    sourcemap_report.bundle_path = bundle_path.map(|x| PathBuf::from(x));

    let rv = process::Command::new(env::var("SENTRY_RN_REAL_NODE_BINARY").unwrap())
        .args(&args)
        .spawn()?
        .wait()?;
    propagate_exit_status(rv);

    let mut f = fs::File::create(env::var("SENTRY_RN_SOURCEMAP_REPORT").unwrap())?;
    serde_json::to_writer(&mut f, &sourcemap_report)?;

    Ok(())
}
<commit_msg>Change release to bundle-version<commit_after>//! Implements a command for uploading react-native projects.
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use clap::{App, Arg, ArgMatches};
use serde_json;
use chrono::Duration;

use prelude::*;
use api::{Api, NewRelease};
use config::Config;
use utils::{ArgExt, TempFile, propagate_exit_status};
use sourcemaputils::SourceMapProcessor;
use xcode;

#[derive(Serialize, Deserialize, Default, Debug)]
struct SourceMapReport {
    bundle_path: Option<PathBuf>,
    sourcemap_path: Option<PathBuf>,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads react-native projects from within an xcode build step")
        .org_project_args()
        .arg(Arg::with_name("verbose")
            .long("verbose")
            .short("verbose")
            .help("Enable verbose mode"))
        .arg(Arg::with_name("force")
             .long("force")
             .short("f")
             .help("Forces the script to run, even in Debug configuration.  This rarely \
                    does what you want because the default build script does not actually \
                    produce any information that the sentry build tool could pick up on."))
        .arg(Arg::with_name("allow_fetch")
             .long("allow-fetch")
             .help("Enable sourcemap fetching from the packager.  If this is enabled \
                    the react native packager needs to run and sourcemaps are downloade \
                    from it if the simulator platform is detected."))
        .arg(Arg::with_name("fetch_from")
             .long("fetch-from")
             .value_name("URL")
             .help("When fetching is enabled this is the URL where fetches can be made \
                    from.  The default is http://127.0.0.1:8081/ where the react-native \
                    packager runs by default."))
        .arg(Arg::with_name("build_script")
             .value_name("BUILD_SCRIPT")
             .index(1)
             .help("Optional path to the build script{n}{n}\
                    This is the path to the react-native-xcode.sh script you want \
                    to use.  By default the bundled build script is used."))
        .arg(Arg::with_name("force_foreground")
             .long("force-foreground")
             .help("By default part of the build process will when triggered from Xcode \
                    detach and continue in the background.  When an error happens \
                    a dialog is shown.  If this parameter is passed Xcode will wait \
                    for the process to finish before the build finishes and output \
                    will be shown in the Xcode build output."))
}

fn find_node() -> String {
    if let Ok(path) = env::var("NODE_BINARY") {
        if path.len() > 0 {
            return path;
        }
    }
    "node".into()
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);
    let should_wrap = matches.is_present("force") || match env::var("CONFIGURATION") {
        Ok(config) => &config != "Debug",
        Err(_) => { return Err("Need to run this from Xcode".into()); }
    };
    let base = env::current_dir()?;
    let script = if let Some(path) = matches.value_of("build_script") {
        base.join(path)
    } else {
        base.join("../node_modules/react-native/packager/react-native-xcode.sh")
    }.canonicalize()?;

    // if we allow fetching and we detect a simulator run, then we need to switch
    // to simulator mode.
    let fetch_url;
    if_chain! {
        if matches.is_present("allow_fetch");
        if let Ok(val) = env::var("PLATFORM_NAME");
        if val.ends_with("simulator");
        then {
            let url = matches.value_of("fetch_from").unwrap_or("http://127.0.0.1:8081/");
            info!("Fetching sourcemaps from {}", url);
            fetch_url = Some(url);
        } else {
            info!("Using react-native build script at {}", base.display());
            fetch_url = None;
        }
    }

    // in case we are in debug mode we directly dispatch to the script
    // and exit out early.
    if !should_wrap && fetch_url.is_none() {
        info!("Running in debug mode, skipping script wrapping.");
        let rv = process::Command::new(&script).spawn()?.wait()?;
        propagate_exit_status(rv);
        return Ok(());
    }

    info!("Parsing Info.plist");
    let plist = match xcode::InfoPlist::discover_from_env()? {
        Some(plist) => plist,
        None => { return Err("Could not find info.plist".into()); }
    };
    info!("Parse result from Info.plist: {:?}", &plist);
    let report_file = TempFile::new()?;
    let node = find_node();
    info!("Using node interpreter '{}'", &node);

    xcode::MayDetach::wrap("React native symbol handling", |md| {
        let bundle_path;
        let sourcemap_path;
        let bundle_url;
        let sourcemap_url;
        let bundle_file;
        let sourcemap_file;

        // If we have a fetch URL we need to fetch them from there now.  In that
        // case we do indeed fetch it right from the running packager and then
        // store it in temporary files for later consumption.
        if let Some(url) = fetch_url {
            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let url = url.trim_right_matches('/');
            bundle_file = TempFile::new()?;
            bundle_path = bundle_file.path().to_path_buf();
            bundle_url = "~/index.ios.bundle".to_string();
            sourcemap_file = TempFile::new()?;
            sourcemap_path = sourcemap_file.path().to_path_buf();
            sourcemap_url = "~/index.ios.map".to_string();

            // wait up to 10 seconds for the server to be up.
            if !api.wait_until_available(url, Duration::seconds(10))? {
                return Err("Error: react-native packager did not respond in time".into());
            }

            api.download(&format!("{}/index.ios.bundle?platform=ios&dev=true", url),
                         &mut bundle_file.open())?;
            api.download(&format!("{}/index.ios.map?platform=ios&dev=true", url),
                         &mut sourcemap_file.open())?;

        // This is the case where we need to hook into the release process to
        // collect sourcemaps when they are generated.
        //
        // this invokes via an indirection of sentry-cli our wrap_call() below.
        // What is happening behind the scenes is that we switch out NODE_BINARY
        // for ourselves which is what the react-native build script normally
        // invokes.  Because we export __SENTRY_RN_WRAP_XCODE_CALL=1, the main
        // sentry-cli script will invoke our wrap_call() function below.
        //
        // That will then attempt to figure out that a react-native bundle is
        // happening to the build script, parse out the arguments, add additional
        // arguments if needed and then report the parsed arguments to a temporary
        // JSON file we load back below.
        //
        // With that we we then have all the information we need to invoke the
        // upload process.
        } else {
            let rv = process::Command::new(&script)
                .env("NODE_BINARY", env::current_exe()?.to_str().unwrap())
                .env("SENTRY_RN_REAL_NODE_BINARY", &node)
                .env("SENTRY_RN_SOURCEMAP_REPORT", report_file.path().to_str().unwrap())
                .env("__SENTRY_RN_WRAP_XCODE_CALL", "1")
                .spawn()?
                .wait()?;
            propagate_exit_status(rv);

            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let mut f = fs::File::open(report_file.path())?;
            let report : SourceMapReport = serde_json::from_reader(&mut f)?;
            if report.bundle_path.is_none() || report.sourcemap_path.is_none() {
                println!("Warning: build produced no sourcemaps.");
                return Ok(());
            }

            bundle_path = report.bundle_path.unwrap();
            bundle_url = format!("~/{}", bundle_path.file_name()
                                 .unwrap().to_string_lossy());
            sourcemap_path = report.sourcemap_path.unwrap();
            sourcemap_url = format!("~/{}", sourcemap_path.file_name()
                                    .unwrap().to_string_lossy());
        }

        // now that we have all the data, we can now process and upload the
        // sourcemaps.
        println!("Processing react-native sourcemaps for Sentry upload.");
        info!("  bundle path: {}", bundle_path.display());
        info!("  sourcemap path: {}", sourcemap_path.display());

        let mut processor = SourceMapProcessor::new(matches.is_present("verbose"));
        processor.add(&bundle_url, &bundle_path)?;
        processor.add(&sourcemap_url, &sourcemap_path)?;
        processor.rewrite(&vec![base.parent().unwrap().to_str().unwrap()])?;
        processor.add_sourcemap_references()?;

        let release = api.new_release(&org, &NewRelease {
            version: format!("{}-{}", plist.bundle_id(), plist.version()),
            projects: vec![project.to_string()],
            ..Default::default()
        })?;
        println!("Uploading sourcemaps for release {}", release.version);
        processor.upload(&api, &org, Some(&project), &release.version,
                         Some(&plist.build()))?;

        Ok(())
    })
}

pub fn wrap_call() -> Result<()> {
    let mut args : Vec<_> = env::args().skip(1).collect();
    let mut bundle_path = None;
    let mut sourcemap_path = None;

    if args.len() > 1 && args[1] == "bundle" {
        let mut iter = args.iter().fuse();
        while let Some(item) = iter.next() {
            if item == "--sourcemap-output" {
                sourcemap_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--sourcemap-output=") {
                sourcemap_path = Some(item[19..].to_string());
            } else if item == "--bundle-output" {
                bundle_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--bundle-output=") {
                bundle_path = Some(item[16..].to_string());
            }
        }
    }

    let mut sourcemap_report = SourceMapReport::default();

    if sourcemap_path.is_none() && bundle_path.is_some() {
        let path = format!("{}.map", &bundle_path.as_ref().unwrap());
        sourcemap_report.sourcemap_path = Some(PathBuf::from(&path));
        args.push("--sourcemap-output".into());
        args.push(path);
    } else if let Some(path) = sourcemap_path {
        sourcemap_report.sourcemap_path = Some(PathBuf::from(path));
    }

    sourcemap_report.bundle_path = bundle_path.map(|x| PathBuf::from(x));

    let rv = process::Command::new(env::var("SENTRY_RN_REAL_NODE_BINARY").unwrap())
        .args(&args)
        .spawn()?
        .wait()?;
    propagate_exit_status(rv);

    let mut f = fs::File::create(env::var("SENTRY_RN_SOURCEMAP_REPORT").unwrap())?;
    serde_json::to_writer(&mut f, &sourcemap_report)?;

    Ok(())
}
<|endoftext|>


<commit_before>//! An rope.
//!
//! A rope is an efficient data structure for large mutable strings. It's
//! essentially a binary tree whose leaves are strings.
//!
//! For more information, see the following resources:
//! + http://scienceblogs.com/goodmath/2009/01/26/ropes-twining-together-strings/
//! + https://www.ibm.com/developerworks/library/j-ropes/
//! + http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf

#![feature(const_fn)]
#![feature(box_patterns)]

use std::cmp;
use std::ops;
use std::convert;

use self::Node::*;

#[derive(Debug)]
pub struct Rope {
    // can we get away with having these be of &str or will they need
    // to be string?
    root: Node
}

impl Rope {

    /// Returns a new empty Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::new();
    /// assert_eq!(an_rope.len(), 0);
    /// ```
    pub const fn new() -> Rope {
        Rope { root: Node::None }
    }

    /// Returns the length of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::new();
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::from(String::from(""));
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("a string"));
    /// assert_eq!(an_rope.len(), "a string".len());
    /// ```
    pub fn len(&self) -> usize {
        self.root.len()
    }

    /// Appends a rope to the end of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// an_rope.append(Rope::from(String::from("efgh")))
    /// assert_eq!(an_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn append(&mut self, other: Rope) {
        unimplemented!()
    }

    /// Prepends a rope to the front of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("efgh"));
    /// an_rope.preend(Rope::from(String::from("abcd")))
    /// assert_eq!(an_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn prepend(&mut self, other: Rope) {
        unimplemented!()
    }

    /// Splits the rope into two ropes at the given index.
    ///
    /// Consumes this rope.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// let (ab, cd) = an_rope.split(2);
    /// assert_eq!(ab, Rope::from(String::from("ab")));
    /// assert_eq!(cd, Rope::from(String::from("cd")));
    /// ```
    pub fn split(self, index: usize) -> (Rope, Rope) {
        unimplemented!()
    }
}

#[derive(Debug)]
enum Node { /// A leaf node
            // todo: is box<str> the right choice?
            Leaf(Box<str>)
          , /// A branch node
            Branch { l: Box<Node>, r: Box<Node> }
          , /// Nothing
            None
}

impl Node {

    /// Returns the length of a node
    fn len(&self) -> usize {
        match *self { Leaf(box ref s) => s.len()
                    , Branch { box ref l, box ref r} => l.len() + r.len()
                    , None => 0
                    }
    }
}

impl convert::From<String> for Rope {
    fn from(string: String) -> Rope {
        Rope {
            root: if string.len() == 0 { Node::None }
                  else { Node::Leaf(string.into_boxed_str()) }
        }
    }
}

//-- comparisons ----------------------------------------------------
impl cmp::PartialEq for Rope {
    fn eq(&self, other: &Rope) -> bool {
        unimplemented!()
    }
}

impl cmp::PartialEq<str> for Rope {
    fn eq(&self, other: &str) -> bool {
        unimplemented!()
    }
}

//-- concatenation --------------------------------------------------
impl ops::Add for Rope {
    type Output = Rope;

    // This is the concat operation.
    fn add(self, other: Rope) -> Rope {
        unimplemented!()
    }
}

impl ops::Index<usize> for Rope {
    type Output = char;

    // Index a character
    fn index(&self, i: usize) -> &char {
        unimplemented!()
    }
}

//-- slicing operators ----------------------------------------------
impl ops::Index<ops::Range<usize>> for Rope {
    type Output = str;

    // Index a substring
    fn index(&self, i: ops::Range<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeTo<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeTo<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeFrom<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeFrom<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::Range<usize>> for Rope {
    fn index_mut(&mut self, i: ops::Range<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeTo<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeTo<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeFrom<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeFrom<usize>) -> &mut str {
        unimplemented!()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>rope: you can now take the value out of a node<commit_after>//! An rope.
//!
//! A rope is an efficient data structure for large mutable strings. It's
//! essentially a binary tree whose leaves are strings.
//!
//! For more information, see the following resources:
//! + http://scienceblogs.com/goodmath/2009/01/26/ropes-twining-together-strings/
//! + https://www.ibm.com/developerworks/library/j-ropes/
//! + http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf

#![feature(const_fn)]
#![feature(box_patterns)]

use std::cmp;
use std::ops;
use std::convert;

use self::Node::*;

#[derive(Debug)]
pub struct Rope {
    // can we get away with having these be of &str or will they need
    // to be string?
    root: Node
}

impl Rope {

    /// Returns a new empty Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::new();
    /// assert_eq!(an_rope.len(), 0);
    /// ```
    pub const fn new() -> Rope {
        Rope { root: Node::None }
    }

    /// Returns the length of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::new();
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::from(String::from(""));
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("a string"));
    /// assert_eq!(an_rope.len(), "a string".len());
    /// ```
    pub fn len(&self) -> usize {
        self.root.len()
    }

    /// Appends a rope to the end of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// an_rope.append(Rope::from(String::from("efgh")))
    /// assert_eq!(an_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn append(&mut self, other: Rope) {
        unimplemented!()
    }

    /// Prepends a rope to the front of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("efgh"));
    /// an_rope.preend(Rope::from(String::from("abcd")))
    /// assert_eq!(an_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn prepend(&mut self, other: Rope) {
        unimplemented!()
    }

    /// Splits the rope into two ropes at the given index.
    ///
    /// Consumes this rope.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// let (ab, cd) = an_rope.split(2);
    /// assert_eq!(ab, Rope::from(String::from("ab")));
    /// assert_eq!(cd, Rope::from(String::from("cd")));
    /// ```
    pub fn split(self, index: usize) -> (Rope, Rope) {
        unimplemented!()
    }
}

#[derive(Debug)]
enum Node { /// A leaf node
            // todo: is box<str> the right choice?
            Leaf(Box<str>)
          , /// A branch node
            Branch { l: Box<Node>, r: Box<Node> }
          , /// Nothing
            None
}

impl Node {

    /// Returns the length of a node
    fn len(&self) -> usize {
        match *self { Leaf(box ref s) => s.len()
                    , Branch { box ref l, box ref r} => l.len() + r.len()
                    , None => 0
                    }
    }

}

trait Take {
    fn take(&mut self) -> Node;
}

impl Take for Box<Node> {

    /// Take the value out of a `Node`, replacing it with `None`
    #[inline]
    fn take(&mut self) -> Node {
        std::mem::replace(self, None)
    }
}

impl convert::From<String> for Rope {
    fn from(string: String) -> Rope {
        Rope {
            root: if string.len() == 0 { Node::None }
                  else { Node::Leaf(string.into_boxed_str()) }
        }
    }
}

//-- comparisons ----------------------------------------------------
impl cmp::PartialEq for Rope {
    fn eq(&self, other: &Rope) -> bool {
        unimplemented!()
    }
}

impl cmp::PartialEq<str> for Rope {
    fn eq(&self, other: &str) -> bool {
        unimplemented!()
    }
}

//-- concatenation --------------------------------------------------
impl ops::Add for Rope {
    type Output = Rope;

    // This is the concat operation.
    fn add(self, other: Rope) -> Rope {
        unimplemented!()
    }
}

impl ops::Index<usize> for Rope {
    type Output = char;

    // Index a character
    fn index(&self, i: usize) -> &char {
        unimplemented!()
    }
}

//-- slicing operators ----------------------------------------------
impl ops::Index<ops::Range<usize>> for Rope {
    type Output = str;

    // Index a substring
    fn index(&self, i: ops::Range<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeTo<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeTo<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeFrom<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeFrom<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::Range<usize>> for Rope {
    fn index_mut(&mut self, i: ops::Range<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeTo<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeTo<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeFrom<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeFrom<usize>) -> &mut str {
        unimplemented!()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>//! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax`
//! parsed by `rustc_parse` and then lowered, after the passes in this crate,
//! by `rustc_ast_lowering`.
//!
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.

#![feature(bindings_after_at)]
#![feature(iter_is_partitioned)]

pub mod ast_validation;
pub mod feature_gate;
pub mod node_count;
pub mod show_span;
<commit_msg>Bump recursion_limit in rustc_ast_passes<commit_after>//! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax`
//! parsed by `rustc_parse` and then lowered, after the passes in this crate,
//! by `rustc_ast_lowering`.
//!
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.

#![feature(bindings_after_at)]
#![feature(iter_is_partitioned)]
#![recursion_limit = "256"]

pub mod ast_validation;
pub mod feature_gate;
pub mod node_count;
pub mod show_span;
<|endoftext|>