<commit_before>extern crate vk_api;
extern crate xml;

mod crawler;

use xml::{EventReader, ParserConfig};
use xml::attribute::OwnedAttribute;

use std::os::raw::c_char;
use std::ffi::CStr;
use std::ptr;
use std::fmt;

pub fn load_xml() {
    let vk_xml = EventReader::new_with_config(vk_api::VK_XML, ParserConfig::new().trim_whitespace(true));
    // The vulkan registry
    let registry = crawler::crawl(vk_xml.into_iter(), VkRegistry::new(vk_api::VK_XML.len()));
}

pub struct VkRegistry {
    string_buffer: String,
    types: Vec<VkType>
}

impl<'a> VkRegistry {
    fn new(capacity: usize) -> VkRegistry {
        VkRegistry {
            string_buffer: String::with_capacity(capacity),
            types: Vec::with_capacity(256)
        }
    }

    fn push_type(&mut self, vk_type: VkType) -> Result<(), ()> {
        match vk_type {
            VkType::Unhandled => Err(()),
            vk_type           => {self.types.push(vk_type); Ok(())}
        }
    }

    /// Append a given attribute to the internal string buffer and return a pointer to a
    /// null-terminated string that represents the attribute
    fn append_str(&mut self, string: &str) -> *const c_char {
        // We want to have all of the string in one block of memory in order to save heap allocation time.
        self.string_buffer.push_str(string);
        self.string_buffer.push('\0');

        (self.string_buffer.as_ptr() as usize + self.string_buffer.len() - string.len() - 1) as *const c_char
    }
}

enum VkMemberType {
    /// A standard, singular, owned field
    Var(*const c_char),
    /// An intermediate type created when the keyword "const" is detected
    Const(*const c_char),
    ConstPtr(*const c_char),
    MutPtr(*const c_char),
    /// An array whose contents are immutable
    ConstArray(*const c_char, usize),
    /// An array whose contents are mutable
    MutArray(*const c_char, usize),
    /// An const array that uses an API constant as the size
    ConstArrayEnum(*const c_char, *const c_char),
    /// A mutable array that uses an API constant as the size
    MutArrayEnum(*const c_char, *const c_char),
    /// Default value to initialize with.
    Unknown
}

impl fmt::Debug for VkMemberType {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use VkMemberType::*;
        if let Unknown = *self {
            write!(fmt, "Unknown")
        } else {
            let pt;
            let mut pt_enum = None;
            let mut fmt_tuple = fmt.debug_tuple(
                match *self {
                    Var(p)                => {pt = p; "Var"}
                    Const(p)              => {pt = p; "Const"}
                    ConstPtr(p)           => {pt = p; "ConstPtr"}
                    MutPtr(p)             => {pt = p; "MutPtr"}
                    ConstArray(p, _)      => {pt = p; "ConstArray"}
                    ConstArrayEnum(p, en) => {pt = p; pt_enum = Some(en); "ConstArrayEnum"}
                    MutArray(p, _)        => {pt = p; "MutArray"}
                    MutArrayEnum(p, en)   => {pt = p; pt_enum = Some(en); "MutArrayEnum"}
                    Unknown               => unreachable!()
                });
            fmt_tuple.field(unsafe{ &if pt != ptr::null() {CStr::from_ptr(pt).to_str().unwrap()} else {"Error: Null Ptr"} });
            match *self {
                ConstArray(_, s)     |
                MutArray(_, s)      => fmt_tuple.field(&s),
                ConstArrayEnum(_, e) |
                MutArrayEnum(_, e)  => fmt_tuple.field(unsafe{ &if pt != ptr::null() {CStr::from_ptr(e).to_str().unwrap()} else {"Error: Null Ptr"} }),
                _                   => &mut fmt_tuple
            }.finish()
        }
    }
}

struct VkMember {
    field_type: VkMemberType,
    field_name: *const c_char,
}

impl fmt::Debug for VkMember {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt .debug_struct("VkMember")
            .field("type", &self.field_type)
            .field("name", unsafe{ &CStr::from_ptr(self.field_name) })
            .finish()
    }
}

impl VkMember {
    fn empty() -> VkMember {
        VkMember {
            field_type: VkMemberType::Unknown,
            field_name: ptr::null()
        }
    }

    fn type_ptr(&self) -> Option<*const c_char> {
        use VkMemberType::*;
        match self.field_type {
            Var(s)               |
            Const(s)             |
            ConstPtr(s)          |
            MutPtr(s)            |
            ConstArray(s, _)     |
            ConstArrayEnum(s, _) |
            MutArray(s, _)       |
            MutArrayEnum(s, _)  => Some(s),
            Unknown => None
        }
    }

    fn set_type(&mut self, field_type: *const c_char) {
        use VkMemberType::*;
        match self.field_type {
            Var(ref mut s)               |
            Const(ref mut s)             |
            ConstPtr(ref mut s)          |
            MutPtr(ref mut s)            |
            ConstArray(ref mut s, _)     |
            ConstArrayEnum(ref mut s, _) |
            MutArray(ref mut s, _)       |
            MutArrayEnum(ref mut s, _)  =>
                if *s == ptr::null() {
                    *s = field_type
                } else {panic!("Field type already set")},
            Unknown                     => self.field_type = Var(field_type)

        }
    }

    fn change_type_var(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Unknown   => self.field_type = Var(ptr::null()),
            _         => panic!("Unexpected change type to var")
        }
    }

    fn change_type_const(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s)               |
            Const(s)            => self.field_type = Const(s),
            ConstPtr(_)          |
            MutPtr(_)           => panic!("Attempted changing mutability of pointer"),
            ConstArray(_, _)     |
            ConstArrayEnum(_, _) |
            MutArray(_, _)       |
            MutArrayEnum(_, _)  => panic!("Attempted changing mutability of array"),
            Unknown             => self.field_type = Const(ptr::null())
        }
    }

    fn change_type_ptr(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s)              => self.field_type = MutPtr(s),
            Const(s)            => self.field_type = ConstPtr(s),
            MutPtr(_)            |
            ConstPtr(_)         => panic!("Attempted to change type from pointer to pointer"),
            ConstArray(_, _)     |
            ConstArrayEnum(_, _) |
            MutArrayEnum(_, _)   |
            MutArray(_, _)      => panic!("Attempted to change type from array to pointer"),
            Unknown             => self.field_type = MutPtr(ptr::null())
        }
    }

    fn change_type_array(&mut self, size: usize) -> &mut VkMember {
        use VkMemberType::*;
        match self.field_type {
            Var(s)              => self.field_type = if size == 0 {MutArrayEnum(s, ptr::null())} else {MutArray(s, size)},
            Const(s)            => self.field_type = if size == 0 {ConstArrayEnum(s, ptr::null())} else {ConstArray(s, size)},
            MutPtr(_)            |
            ConstPtr(_)         => panic!("Attempted to change type from pointer to array"),
            ConstArray(_, _)     |
            ConstArrayEnum(_, _) |
            MutArrayEnum(_, _)   |
            MutArray(_, _)      => panic!("Attempted to change type from array to array"),
            Unknown             => panic!("Attempted to change type to array without type identifier")
        }
        self
    }

    fn change_type_array_enum(&mut self, size_enum: *const c_char) {
        use VkMemberType::*;
        match self.field_type {
            Var(s)                      => panic!("Attempted to change type from var to array[enum]"),
            Const(s)                    => panic!("Attempted to change type from const to array[enum]"),
            MutPtr(_)                    |
            ConstPtr(_)                 => panic!("Attempted to change type from pointer to array[enum]"),
            ConstArrayEnum(_, ref mut e) |
            MutArrayEnum(_, ref mut e)  => *e = size_enum,
            ConstArray(_, _)             |
            MutArray(_, _)              => panic!("Attempted to change type from array to array[enum]"),
            Unknown                     => panic!("Attempted to change type to array without type identifier")
        }
    }

    fn set_name(&mut self, field_name: *const c_char) {
        if self.field_name != ptr::null() {
            panic!("Unexpected \"name\" tag");
        } else {
            self.field_name = field_name;
        }
    }
}

/// A variant of a vulkan enum
#[derive(Debug)]
struct VkVariant {
    name: *const c_char,
    value: isize
}

#[derive(Debug)]
enum VkType {
    Struct {
        name: *const c_char,
        fields: Vec<VkMember>
    },

    Union {
        name: *const c_char,
        variants: Vec<VkMember>
    },

    Enum {
        name: *const c_char,
        variants: Vec<VkVariant>
    },

    Handle {
        name: *const c_char,
        validity: bool,
        dispatchable: bool
    },

    TypeDef {
        /// The type that is being aliased
        ty: *const c_char,
        /// The name of the new type
        name: *const c_char,
        validity: u8
    },

    Unhandled
}

impl VkType {
    fn new_struct(name: *const c_char, fields: Vec<VkMember>) -> VkType {
        VkType::Struct {
            name: name,
            fields: fields
        }
    }

    fn new_union(name: *const c_char, variants: Vec<VkMember>) -> VkType {
        VkType::Union {
            name: name,
            variants: variants
        }
    }

    fn empty_handle() -> VkType {
        VkType::Handle {
            name: ptr::null(),
            validity: false,
            dispatchable: true
        }
    }

    fn empty_typedef() -> VkType {
        use tdvalid::*;
        VkType::TypeDef {
            ty: ptr::null(),
            name: ptr::null(),
            validity: NOSEMICOLON | NOTYPEDEF
        }
    }
}

/// VkType::TypeDef validity flags
mod tdvalid {
    pub const NOSEMICOLON: u8 = 0b01;
    pub const NOTYPEDEF: u8   = 0b10;
}<commit_msg>Added string_buffer reallocation detection<commit_after>extern crate vk_api;
extern crate xml;

mod crawler;

use xml::{EventReader, ParserConfig};
use xml::attribute::OwnedAttribute;

use std::os::raw::c_char;
use std::ffi::CStr;
use std::ptr;
use std::fmt;

pub fn load_xml() {
    let vk_xml = EventReader::new_with_config(vk_api::VK_XML, ParserConfig::new().trim_whitespace(true));
    // The vulkan registry
    let registry = crawler::crawl(vk_xml.into_iter(), VkRegistry::new(vk_api::VK_XML.len()));
}

pub struct VkRegistry {
    string_buffer: String,
    types: Vec<VkType>
}

impl<'a> VkRegistry {
    fn new(capacity: usize) -> VkRegistry {
        VkRegistry {
            string_buffer: String::with_capacity(capacity),
            types: Vec::with_capacity(256)
        }
    }

    fn push_type(&mut self, vk_type: VkType) -> Result<(), ()> {
        match vk_type {
            VkType::Unhandled => Err(()),
            vk_type           => {self.types.push(vk_type); Ok(())}
        }
    }

    /// Append a given attribute to the internal string buffer and return a pointer to a
    /// null-terminated string that represents the attribute
    fn append_str(&mut self, string: &str) -> *const c_char {
        let prepushcap = self.string_buffer.capacity();
        // We want to have all of the string in one block of memory in order to save heap allocation time.
        self.string_buffer.push_str(string);
        self.string_buffer.push('\0');

        if prepushcap != self.string_buffer.capacity() {
            panic!("Allocation detected in string buffer")
        }

        (self.string_buffer.as_ptr() as usize + self.string_buffer.len() - string.len() - 1) as *const c_char
    }
}

enum VkMemberType {
    /// A standard, singular, owned field
    Var(*const c_char),
    /// An intermediate type created when the keyword "const" is detected
    Const(*const c_char),
    ConstPtr(*const c_char),
    MutPtr(*const c_char),
    /// An array whose contents are immutable
    ConstArray(*const c_char, usize),
    /// An array whose contents are mutable
    MutArray(*const c_char, usize),
    /// An const array that uses an API constant as the size
    ConstArrayEnum(*const c_char, *const c_char),
    /// A mutable array that uses an API constant as the size
    MutArrayEnum(*const c_char, *const c_char),
    /// Default value to initialize with.
    Unknown
}

impl fmt::Debug for VkMemberType {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use VkMemberType::*;
        if let Unknown = *self {
            write!(fmt, "Unknown")
        } else {
            let pt;
            let mut pt_enum = None;
            let mut fmt_tuple = fmt.debug_tuple(
                match *self {
                    Var(p)                => {pt = p; "Var"}
                    Const(p)              => {pt = p; "Const"}
                    ConstPtr(p)           => {pt = p; "ConstPtr"}
                    MutPtr(p)             => {pt = p; "MutPtr"}
                    ConstArray(p, _)      => {pt = p; "ConstArray"}
                    ConstArrayEnum(p, en) => {pt = p; pt_enum = Some(en); "ConstArrayEnum"}
                    MutArray(p, _)        => {pt = p; "MutArray"}
                    MutArrayEnum(p, en)   => {pt = p; pt_enum = Some(en); "MutArrayEnum"}
                    Unknown               => unreachable!()
                });
            fmt_tuple.field(unsafe{ &if pt != ptr::null() {CStr::from_ptr(pt).to_str().unwrap()} else {"Error: Null Ptr"} });
            match *self {
                ConstArray(_, s)     |
                MutArray(_, s)      => fmt_tuple.field(&s),
                ConstArrayEnum(_, e) |
                MutArrayEnum(_, e)  => fmt_tuple.field(unsafe{ &if pt != ptr::null() {CStr::from_ptr(e).to_str().unwrap()} else {"Error: Null Ptr"} }),
                _                   => &mut fmt_tuple
            }.finish()
        }
    }
}

struct VkMember {
    field_type: VkMemberType,
    field_name: *const c_char,
}

impl fmt::Debug for VkMember {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt .debug_struct("VkMember")
            .field("type", &self.field_type)
            .field("name", unsafe{ &CStr::from_ptr(self.field_name) })
            .finish()
    }
}

impl VkMember {
    fn empty() -> VkMember {
        VkMember {
            field_type: VkMemberType::Unknown,
            field_name: ptr::null()
        }
    }

    fn type_ptr(&self) -> Option<*const c_char> {
        use VkMemberType::*;
        match self.field_type {
            Var(s)               |
            Const(s)             |
            ConstPtr(s)          |
            MutPtr(s)            |
            ConstArray(s, _)     |
            ConstArrayEnum(s, _) |
            MutArray(s, _)       |
            MutArrayEnum(s, _)  => Some(s),
            Unknown => None
        }
    }

    fn set_type(&mut self, field_type: *const c_char) {
        use VkMemberType::*;
        match self.field_type {
            Var(ref mut s)               |
            Const(ref mut s)             |
            ConstPtr(ref mut s)          |
            MutPtr(ref mut s)            |
            ConstArray(ref mut s, _)     |
            ConstArrayEnum(ref mut s, _) |
            MutArray(ref mut s, _)       |
            MutArrayEnum(ref mut s, _)  =>
                if *s == ptr::null() {
                    *s = field_type
                } else {panic!("Field type already set")},
            Unknown                     => self.field_type = Var(field_type)

        }
    }

    fn change_type_var(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Unknown   => self.field_type = Var(ptr::null()),
            _         => panic!("Unexpected change type to var")
        }
    }

    fn change_type_const(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s)               |
            Const(s)            => self.field_type = Const(s),
            ConstPtr(_)          |
            MutPtr(_)           => panic!("Attempted changing mutability of pointer"),
            ConstArray(_, _)     |
            ConstArrayEnum(_, _) |
            MutArray(_, _)       |
            MutArrayEnum(_, _)  => panic!("Attempted changing mutability of array"),
            Unknown             => self.field_type = Const(ptr::null())
        }
    }

    fn change_type_ptr(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s)              => self.field_type = MutPtr(s),
            Const(s)            => self.field_type = ConstPtr(s),
            MutPtr(_)            |
            ConstPtr(_)         => panic!("Attempted to change type from pointer to pointer"),
            ConstArray(_, _)     |
            ConstArrayEnum(_, _) |
            MutArrayEnum(_, _)   |
            MutArray(_, _)      => panic!("Attempted to change type from array to pointer"),
            Unknown             => self.field_type = MutPtr(ptr::null())
        }
    }

    fn change_type_array(&mut self, size: usize) -> &mut VkMember {
        use VkMemberType::*;
        match self.field_type {
            Var(s)              => self.field_type = if size == 0 {MutArrayEnum(s, ptr::null())} else {MutArray(s, size)},
            Const(s)            => self.field_type = if size == 0 {ConstArrayEnum(s, ptr::null())} else {ConstArray(s, size)},
            MutPtr(_)            |
            ConstPtr(_)         => panic!("Attempted to change type from pointer to array"),
            ConstArray(_, _)     |
            ConstArrayEnum(_, _) |
            MutArrayEnum(_, _)   |
            MutArray(_, _)      => panic!("Attempted to change type from array to array"),
            Unknown             => panic!("Attempted to change type to array without type identifier")
        }
        self
    }

    fn change_type_array_enum(&mut self, size_enum: *const c_char) {
        use VkMemberType::*;
        match self.field_type {
            Var(_)                      => panic!("Attempted to change type from var to array[enum]"),
            Const(_)                    => panic!("Attempted to change type from const to array[enum]"),
            MutPtr(_)                    |
            ConstPtr(_)                 => panic!("Attempted to change type from pointer to array[enum]"),
            ConstArrayEnum(_, ref mut e) |
            MutArrayEnum(_, ref mut e)  => *e = size_enum,
            ConstArray(_, _)             |
            MutArray(_, _)              => panic!("Attempted to change type from array to array[enum]"),
            Unknown                     => panic!("Attempted to change type to array without type identifier")
        }
    }

    fn set_name(&mut self, field_name: *const c_char) {
        if self.field_name != ptr::null() {
            panic!("Unexpected \"name\" tag");
        } else {
            self.field_name = field_name;
        }
    }
}

/// A variant of a vulkan enum
#[derive(Debug)]
struct VkVariant {
    name: *const c_char,
    value: isize
}

#[derive(Debug)]
enum VkType {
    Struct {
        name: *const c_char,
        fields: Vec<VkMember>
    },

    Union {
        name: *const c_char,
        variants: Vec<VkMember>
    },

    Enum {
        name: *const c_char,
        variants: Vec<VkVariant>
    },

    Handle {
        name: *const c_char,
        validity: bool,
        dispatchable: bool
    },

    TypeDef {
        /// The type that is being aliased
        ty: *const c_char,
        /// The name of the new type
        name: *const c_char,
        validity: u8
    },

    Unhandled
}

impl VkType {
    fn new_struct(name: *const c_char, fields: Vec<VkMember>) -> VkType {
        VkType::Struct {
            name: name,
            fields: fields
        }
    }

    fn new_union(name: *const c_char, variants: Vec<VkMember>) -> VkType {
        VkType::Union {
            name: name,
            variants: variants
        }
    }

    fn empty_handle() -> VkType {
        VkType::Handle {
            name: ptr::null(),
            validity: false,
            dispatchable: true
        }
    }

    fn empty_typedef() -> VkType {
        use tdvalid::*;
        VkType::TypeDef {
            ty: ptr::null(),
            name: ptr::null(),
            validity: NOSEMICOLON | NOTYPEDEF
        }
    }
}

/// VkType::TypeDef validity flags
mod tdvalid {
    pub const NOSEMICOLON: u8 = 0b01;
    pub const NOTYPEDEF: u8   = 0b10;
}<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::primitive::*;
use common::Span;
use std::fmt;

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TypeAnnotation {
    Named(Identifier),
    List(Box<ListTypeAnnotation>),
    NonNull(Box<NonNullTypeAnnotation>),
}

impl fmt::Display for TypeAnnotation {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TypeAnnotation::Named(named) => f.write_fmt(format_args!("{}", named)),
            TypeAnnotation::List(list) => f.write_fmt(format_args!("[{}]", list.type_)),
            TypeAnnotation::NonNull(non_null) => f.write_fmt(format_args!("{}!", non_null.type_)),
        }
    }
}

impl TypeAnnotation {
    pub fn inner(&self) -> &TypeAnnotation {
        match self {
            TypeAnnotation::Named(_) => self,
            TypeAnnotation::List(of) => (*of).type_.inner(),
            TypeAnnotation::NonNull(of) => (*of).type_.inner(),
        }
    }

    pub fn span(&self) -> Span {
        match self {
            TypeAnnotation::Named(x) => x.span,
            TypeAnnotation::List(of) => (*of).span,
            TypeAnnotation::NonNull(of) => (*of).span,
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ListTypeAnnotation {
    pub span: Span,
    pub open: Token,
    pub type_: TypeAnnotation,
    pub close: Token,
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NonNullTypeAnnotation {
    pub span: Span,
    pub type_: TypeAnnotation,
    pub exclamation: Token,
}
<commit_msg>Change TypeAnnotation::inner to return &Identifier<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::primitive::*;
use common::Span;
use std::fmt;

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TypeAnnotation {
    Named(Identifier),
    List(Box<ListTypeAnnotation>),
    NonNull(Box<NonNullTypeAnnotation>),
}

impl fmt::Display for TypeAnnotation {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TypeAnnotation::Named(named) => f.write_fmt(format_args!("{}", named)),
            TypeAnnotation::List(list) => f.write_fmt(format_args!("[{}]", list.type_)),
            TypeAnnotation::NonNull(non_null) => f.write_fmt(format_args!("{}!", non_null.type_)),
        }
    }
}

impl TypeAnnotation {
    pub fn inner(&self) -> &Identifier {
        match self {
            TypeAnnotation::Named(inner) => inner,
            TypeAnnotation::List(of) => (*of).type_.inner(),
            TypeAnnotation::NonNull(of) => (*of).type_.inner(),
        }
    }

    pub fn span(&self) -> Span {
        match self {
            TypeAnnotation::Named(x) => x.span,
            TypeAnnotation::List(of) => (*of).span,
            TypeAnnotation::NonNull(of) => (*of).span,
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ListTypeAnnotation {
    pub span: Span,
    pub open: Token,
    pub type_: TypeAnnotation,
    pub close: Token,
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NonNullTypeAnnotation {
    pub span: Span,
    pub type_: TypeAnnotation,
    pub exclamation: Token,
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::query_builder::{get_all_roots, get_watchman_expr};
use super::{Clock, WatchmanFile};
use crate::errors::{Error, Result};
use crate::{compiler_state::CompilerState, config::Config, saved_state::SavedStateLoader};
use common::{PerfLogEvent, PerfLogger};
use log::{debug, info, warn};
use serde_bser::value::Value;
use std::process::Command;
use watchman_client::prelude::*;
use watchman_client::{Subscription as WatchmanSubscription, SubscriptionData};

pub struct FileSource<'config> {
    client: Client,
    config: &'config Config,
    resolved_root: ResolvedRoot,
}

#[derive(Debug)]
pub enum FileSourceSubscriptionNextChange {
    Result(FileSourceResult),
    /// This value indicated the beginning of the source control update.
    /// We may stop the compilation process and wait for the next event.
    SourceControlUpdateEnter,
    /// If source control update has not changed the base revision of the commit
    /// We may continue the `watch(...)` loop of the compiler, expecting to receive
    /// a `Result` event after `SourceControlUpdateLeave`.
    SourceControlUpdateLeave,
    /// When source control update completed and we detected changed base revision,
    /// we may need to create a new compiler state.
    SourceControlUpdate,
    None,
}

#[derive(Debug)]
pub struct FileSourceResult {
    pub files: Vec<WatchmanFile>,
    pub resolved_root: ResolvedRoot,
    pub clock: Clock,
    pub saved_state_info: Option<Value>,
}

impl<'config> FileSource<'config> {
    pub async fn connect(
        config: &'config Config,
        perf_logger_event: &impl PerfLogEvent,
    ) -> Result<FileSource<'config>> {
        let connect_timer = perf_logger_event.start("file_source_connect_time");
        let client = Connector::new().connect().await?;
        let canonical_root = CanonicalPath::canonicalize(&config.root_dir).map_err(|err| {
            Error::CanonicalizeRoot {
                root: config.root_dir.clone(),
                source: err,
            }
        })?;
        let resolved_root = client.resolve_root(canonical_root).await?;
        perf_logger_event.stop(connect_timer);

        Ok(Self {
            client,
            config,
            resolved_root,
        })
    }

    /// Executes a point query (as opposed to a subscription) to find all files
    /// to compile and returns the result.
    pub async fn query(
        &self,
        perf_logger_event: &impl PerfLogEvent,
        perf_logger: &impl PerfLogger,
    ) -> Result<CompilerState> {
        info!("querying files to compile...");
        let query_time = perf_logger_event.start("file_source_query_time");
        // If the saved state flag is passed, load from it or fail.
        if let Some(saved_state_path) = &self.config.load_saved_state_file {
            let mut compiler_state = perf_logger_event.time("deserialize_saved_state", || {
                CompilerState::deserialize_from_file(&saved_state_path)
            })?;
            let file_source_result = self
                .query_file_result(Some(compiler_state.clock.clone()), perf_logger_event)
                .await?;
            compiler_state
                .pending_file_source_changes
                .write()
                .unwrap()
                .push(file_source_result);
            compiler_state.merge_file_source_changes(
                &self.config,
                perf_logger_event,
                perf_logger,
                true,
            )?;
            perf_logger_event.stop(query_time);
            return Ok(compiler_state);
        }

        // If saved state is configured, try using saved state unless the config
        // forces a full build.
        if let Config {
            compile_everything: false,
            saved_state_config: Some(saved_state_config),
            saved_state_loader: Some(saved_state_loader),
            saved_state_version,
            ..
        } = self.config
        {
            match self
                .try_saved_state(
                    perf_logger,
                    perf_logger_event,
                    saved_state_config.clone(),
                    saved_state_loader.as_ref(),
                    saved_state_version,
                )
                .await
            {
                Ok(load_result) => {
                    perf_logger_event.stop(query_time);
                    return load_result;
                }
                Err(saved_state_failure) => {
                    warn!(
                        "Unable to load saved state, falling back to full build: {}",
                        saved_state_failure
                    );
                }
            }
        }

        // Finally, do a simple full query.
        let file_source_result = self.query_file_result(None, perf_logger_event).await?;
        let compiler_state = perf_logger_event.time("from_file_source_changes", || {
            CompilerState::from_file_source_changes(
                &self.config,
                &file_source_result,
                perf_logger_event,
                perf_logger,
            )
        })?;
        perf_logger_event.stop(query_time);
        Ok(compiler_state)
    }

    /// Starts a subscription sending updates since the given clock.
    pub async fn subscribe(
        self,
        perf_logger_event: &impl PerfLogEvent,
        perf_logger: &impl PerfLogger,
    ) -> Result<(CompilerState, FileSourceSubscription)> {
        let timer = perf_logger_event.start("file_source_subscribe_time");
        let compiler_state = self.query(perf_logger_event, perf_logger).await?;

        let expression = get_watchman_expr(&self.config);

        let file_source_result = self
            .query_file_result(Some(compiler_state.clock.clone()), perf_logger_event)
            .await?;

        let (subscription, _initial) = self
            .client
            .subscribe::<WatchmanFile>(
                &self.resolved_root,
                SubscribeRequest {
                    expression: Some(expression),
                    since: Some(file_source_result.clock.clone()),
                    defer: vec!["hg.update"],
                    ..Default::default()
                },
            )
            .await?;

        perf_logger_event.stop(timer);

        Ok((
            compiler_state,
            FileSourceSubscription::new(self.resolved_root.clone(), subscription),
        ))
    }

    /// Internal method to issue a watchman query, returning a raw
    /// FileSourceResult.
    async fn query_file_result(
        &self,
        since_clock: Option<Clock>,
        perf_logger_event: &impl PerfLogEvent,
    ) -> Result<FileSourceResult> {
        let expression = get_watchman_expr(&self.config);

        let query_timer = perf_logger_event.start("watchman_query_time");
        // If `since` is available, we should not pass the `path` parameter.
        // Watchman ignores `since` parameter if both `path` and `since` are
        // passed as the request params
        let request = if since_clock.is_some() {
            QueryRequestCommon {
                expression: Some(expression),
                since: since_clock,
                ..Default::default()
            }
        } else {
            let query_roots = get_all_roots(&self.config)
                .into_iter()
                .map(PathGeneratorElement::RecursivePath)
                .collect();
            QueryRequestCommon {
                expression: Some(expression),
                path: Some(query_roots),
                ..Default::default()
            }
        };
        let query_result = self
            .client
            .query::<WatchmanFile>(&self.resolved_root, request)
            .await?;
        perf_logger_event.stop(query_timer);

        let files = query_result.files.ok_or(Error::EmptyQueryResult)?;
        Ok(FileSourceResult {
            files,
            resolved_root: self.resolved_root.clone(),
            clock: query_result.clock,
            saved_state_info: query_result.saved_state_info,
        })
    }

    /// Tries to load saved state with a watchman query.
    /// The return value is a nested Result:
    /// The outer Result indicates the result of a possible saved state infrastructure failure.
    /// The inner Result is a potential parse error.
    async fn try_saved_state(
        &self,
        perf_logger: &impl PerfLogger,
        perf_logger_event: &impl PerfLogEvent,
        saved_state_config: ScmAwareClockData,
        saved_state_loader: &'_ (dyn SavedStateLoader + Send + Sync),
        saved_state_version: &str,
    ) -> std::result::Result<Result<CompilerState>, &'static str> {
        let scm_since = Clock::ScmAware(FatClockData {
            clock: ClockSpec::null(),
            scm: Some(saved_state_config),
        });
        let file_source_result = self
            .query_file_result(Some(scm_since), perf_logger_event)
            .await
            .map_err(|_| "query failed")?;
        let saved_state_info = file_source_result
            .saved_state_info
            .as_ref()
            .ok_or("no saved state in watchman response")?;
        let saved_state_path = perf_logger_event.time("saved_state_loading_time", || {
            saved_state_loader
                .load(&saved_state_info, self.config)
                .ok_or("unable to load")
        })?;
        let mut compiler_state = perf_logger_event
            .time("deserialize_saved_state", || {
                CompilerState::deserialize_from_file(&saved_state_path)
            })
            .map_err(|err| {
                let error_event = perf_logger.create_event("saved_state_loader_error");
                error_event.string("error", format!("Failed to deserialize: {}", err));
                perf_logger.complete_event(error_event);
                perf_logger.flush();
                "failed to deserialize"
            })?;
        // For cases, where we want to debug saved state integration, that doesn't include
        // saved_state format changes we may need to disable this by adding this env variable
        if std::env::var("RELAY_COMPILER_IGNORE_SAVED_STATE_VERSION").is_err()
            && compiler_state.saved_state_version != saved_state_version
        {
            return Err("Saved state version doesn't match.");
        }
        compiler_state
            .pending_file_source_changes
            .write()
            .unwrap()
            .push(file_source_result);
        if let Err(parse_error) = perf_logger_event.time("merge_file_source_changes", || {
            compiler_state.merge_file_source_changes(
                &self.config,
                perf_logger_event,
                perf_logger,
                true,
            )
        }) {
            Ok(Err(parse_error))
        } else {
            Ok(Ok(compiler_state))
        }
    }
}

pub struct FileSourceSubscription {
    resolved_root: ResolvedRoot,
    subscription: WatchmanSubscription<WatchmanFile>,
    base_revision: String,
}

impl FileSourceSubscription {
    fn new(resolved_root: ResolvedRoot, subscription: WatchmanSubscription<WatchmanFile>) -> Self {
        Self {
            resolved_root,
            subscription,
            base_revision: get_base_revision(None),
        }
    }

    /// Awaits changes from Watchman and provides the next set of changes
    /// if there were any changes to files
    pub async fn next_change(&mut self) -> Result<FileSourceSubscriptionNextChange> {
        match self.subscription.next().await? {
            SubscriptionData::FilesChanged(changes) => {
                if let Some(files) = changes.files {
                    debug!("number of files in this update: {}", files.len());
                    return Ok(FileSourceSubscriptionNextChange::Result(FileSourceResult {
                        files,
                        resolved_root: self.resolved_root.clone(),
                        clock: changes.clock,
                        saved_state_info: None,
                    }));
                }
            }
            SubscriptionData::StateEnter { state_name, .. } => {
                if state_name == "hg.update" {
                    return Ok(FileSourceSubscriptionNextChange::SourceControlUpdateEnter);
                }
            }
            SubscriptionData::StateLeave {
                state_name,
                metadata,
            } => {
                if state_name == "hg.update" {
                    let current_commit = if let Some(Value::ByteString(value)) = metadata {
                        Some(value.to_string())
                    } else {
                        None
                    };
                    let current_base_revision = get_base_revision(current_commit);
                    if current_base_revision != self.base_revision {
                        self.base_revision = current_base_revision;
                        return Ok(FileSourceSubscriptionNextChange::SourceControlUpdate);
                    } else {
                        return Ok(FileSourceSubscriptionNextChange::SourceControlUpdateLeave);
                    }
                }
            }
            SubscriptionData::Canceled => {
                return Err(Error::WatchmanSubscriptionCanceled);
            }
        }
        Ok(FileSourceSubscriptionNextChange::None)
    }
}

/// Base revision in this case is a common ancestor of two revisions:
/// `master` and current commit hash or `.`
fn get_base_revision(commit_hash: Option<String>) -> String {
    let output = Command::new("hg")
        .arg("log".to_string())
        .arg("-r".to_string())
        .arg(format!(
            "ancestor(master, {})",
            commit_hash.unwrap_or_else(|| ".".to_string())
        ))
        .arg("-T={node}")
        .output()
        .expect("Expect `hg` command getting base revision.");

    if !output.stderr.is_empty() {
        panic!(
            "Stderr getting base revision hash:\n {:?}",
            String::from_utf8_lossy(&output.stderr)
        );
    }

    String::from_utf8_lossy(&output.stdout).to_string()
}
<commit_msg>Avoid panics when the hg command warns<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::query_builder::{get_all_roots, get_watchman_expr};
use super::{Clock, WatchmanFile};
use crate::errors::{Error, Result};
use crate::{compiler_state::CompilerState, config::Config, saved_state::SavedStateLoader};
use common::{PerfLogEvent, PerfLogger};
use log::{debug, info, warn};
use serde_bser::value::Value;
use std::process::Command;
use watchman_client::prelude::*;
use watchman_client::{Subscription as WatchmanSubscription, SubscriptionData};

pub struct FileSource<'config> {
    client: Client,
    config: &'config Config,
    resolved_root: ResolvedRoot,
}

#[derive(Debug)]
pub enum FileSourceSubscriptionNextChange {
    Result(FileSourceResult),
    /// This value indicated the beginning of the source control update.
    /// We may stop the compilation process and wait for the next event.
    SourceControlUpdateEnter,
    /// If source control update has not changed the base revision of the commit
    /// We may continue the `watch(...)` loop of the compiler, expecting to receive
    /// a `Result` event after `SourceControlUpdateLeave`.
    SourceControlUpdateLeave,
    /// When source control update completed and we detected changed base revision,
    /// we may need to create a new compiler state.
    SourceControlUpdate,
    None,
}

#[derive(Debug)]
pub struct FileSourceResult {
    pub files: Vec<WatchmanFile>,
    pub resolved_root: ResolvedRoot,
    pub clock: Clock,
    pub saved_state_info: Option<Value>,
}

impl<'config> FileSource<'config> {
    pub async fn connect(
        config: &'config Config,
        perf_logger_event: &impl PerfLogEvent,
    ) -> Result<FileSource<'config>> {
        let connect_timer = perf_logger_event.start("file_source_connect_time");
        let client = Connector::new().connect().await?;
        let canonical_root = CanonicalPath::canonicalize(&config.root_dir).map_err(|err| {
            Error::CanonicalizeRoot {
                root: config.root_dir.clone(),
                source: err,
            }
        })?;
        let resolved_root = client.resolve_root(canonical_root).await?;
        perf_logger_event.stop(connect_timer);

        Ok(Self {
            client,
            config,
            resolved_root,
        })
    }

    /// Executes a point query (as opposed to a subscription) to find all files
    /// to compile and returns the result.
    pub async fn query(
        &self,
        perf_logger_event: &impl PerfLogEvent,
        perf_logger: &impl PerfLogger,
    ) -> Result<CompilerState> {
        info!("querying files to compile...");
        let query_time = perf_logger_event.start("file_source_query_time");
        // If the saved state flag is passed, load from it or fail.
        if let Some(saved_state_path) = &self.config.load_saved_state_file {
            let mut compiler_state = perf_logger_event.time("deserialize_saved_state", || {
                CompilerState::deserialize_from_file(&saved_state_path)
            })?;
            let file_source_result = self
                .query_file_result(Some(compiler_state.clock.clone()), perf_logger_event)
                .await?;
            compiler_state
                .pending_file_source_changes
                .write()
                .unwrap()
                .push(file_source_result);
            compiler_state.merge_file_source_changes(
                &self.config,
                perf_logger_event,
                perf_logger,
                true,
            )?;
            perf_logger_event.stop(query_time);
            return Ok(compiler_state);
        }

        // If saved state is configured, try using saved state unless the config
        // forces a full build.
        if let Config {
            compile_everything: false,
            saved_state_config: Some(saved_state_config),
            saved_state_loader: Some(saved_state_loader),
            saved_state_version,
            ..
        } = self.config
        {
            match self
                .try_saved_state(
                    perf_logger,
                    perf_logger_event,
                    saved_state_config.clone(),
                    saved_state_loader.as_ref(),
                    saved_state_version,
                )
                .await
            {
                Ok(load_result) => {
                    perf_logger_event.stop(query_time);
                    return load_result;
                }
                Err(saved_state_failure) => {
                    warn!(
                        "Unable to load saved state, falling back to full build: {}",
                        saved_state_failure
                    );
                }
            }
        }

        // Finally, do a simple full query.
        let file_source_result = self.query_file_result(None, perf_logger_event).await?;
        let compiler_state = perf_logger_event.time("from_file_source_changes", || {
            CompilerState::from_file_source_changes(
                &self.config,
                &file_source_result,
                perf_logger_event,
                perf_logger,
            )
        })?;
        perf_logger_event.stop(query_time);
        Ok(compiler_state)
    }

    /// Starts a subscription sending updates since the given clock.
    pub async fn subscribe(
        self,
        perf_logger_event: &impl PerfLogEvent,
        perf_logger: &impl PerfLogger,
    ) -> Result<(CompilerState, FileSourceSubscription)> {
        let timer = perf_logger_event.start("file_source_subscribe_time");
        let compiler_state = self.query(perf_logger_event, perf_logger).await?;

        let expression = get_watchman_expr(&self.config);

        let file_source_result = self
            .query_file_result(Some(compiler_state.clock.clone()), perf_logger_event)
            .await?;

        let (subscription, _initial) = self
            .client
            .subscribe::<WatchmanFile>(
                &self.resolved_root,
                SubscribeRequest {
                    expression: Some(expression),
                    since: Some(file_source_result.clock.clone()),
                    defer: vec!["hg.update"],
                    ..Default::default()
                },
            )
            .await?;

        perf_logger_event.stop(timer);

        Ok((
            compiler_state,
            FileSourceSubscription::new(self.resolved_root.clone(), subscription),
        ))
    }

    /// Internal method to issue a watchman query, returning a raw
    /// FileSourceResult.
    async fn query_file_result(
        &self,
        since_clock: Option<Clock>,
        perf_logger_event: &impl PerfLogEvent,
    ) -> Result<FileSourceResult> {
        let expression = get_watchman_expr(&self.config);

        let query_timer = perf_logger_event.start("watchman_query_time");
        // If `since` is available, we should not pass the `path` parameter.
        // Watchman ignores `since` parameter if both `path` and `since` are
        // passed as the request params
        let request = if since_clock.is_some() {
            QueryRequestCommon {
                expression: Some(expression),
                since: since_clock,
                ..Default::default()
            }
        } else {
            let query_roots = get_all_roots(&self.config)
                .into_iter()
                .map(PathGeneratorElement::RecursivePath)
                .collect();
            QueryRequestCommon {
                expression: Some(expression),
                path: Some(query_roots),
                ..Default::default()
            }
        };
        let query_result = self
            .client
            .query::<WatchmanFile>(&self.resolved_root, request)
            .await?;
        perf_logger_event.stop(query_timer);

        let files = query_result.files.ok_or(Error::EmptyQueryResult)?;
        Ok(FileSourceResult {
            files,
            resolved_root: self.resolved_root.clone(),
            clock: query_result.clock,
            saved_state_info: query_result.saved_state_info,
        })
    }

    /// Tries to load saved state with a watchman query.
    /// The return value is a nested Result:
    /// The outer Result indicates the result of a possible saved state infrastructure failure.
    /// The inner Result is a potential parse error.
    async fn try_saved_state(
        &self,
        perf_logger: &impl PerfLogger,
        perf_logger_event: &impl PerfLogEvent,
        saved_state_config: ScmAwareClockData,
        saved_state_loader: &'_ (dyn SavedStateLoader + Send + Sync),
        saved_state_version: &str,
    ) -> std::result::Result<Result<CompilerState>, &'static str> {
        let scm_since = Clock::ScmAware(FatClockData {
            clock: ClockSpec::null(),
            scm: Some(saved_state_config),
        });
        let file_source_result = self
            .query_file_result(Some(scm_since), perf_logger_event)
            .await
            .map_err(|_| "query failed")?;
        let saved_state_info = file_source_result
            .saved_state_info
            .as_ref()
            .ok_or("no saved state in watchman response")?;
        let saved_state_path = perf_logger_event.time("saved_state_loading_time", || {
            saved_state_loader
                .load(&saved_state_info, self.config)
                .ok_or("unable to load")
        })?;
        let mut compiler_state = perf_logger_event
            .time("deserialize_saved_state", || {
                CompilerState::deserialize_from_file(&saved_state_path)
            })
            .map_err(|err| {
                let error_event = perf_logger.create_event("saved_state_loader_error");
                error_event.string("error", format!("Failed to deserialize: {}", err));
                perf_logger.complete_event(error_event);
                perf_logger.flush();
                "failed to deserialize"
            })?;
        // For cases, where we want to debug saved state integration, that doesn't include
        // saved_state format changes we may need to disable this by adding this env variable
        if std::env::var("RELAY_COMPILER_IGNORE_SAVED_STATE_VERSION").is_err()
            && compiler_state.saved_state_version != saved_state_version
        {
            return Err("Saved state version doesn't match.");
        }
        compiler_state
            .pending_file_source_changes
            .write()
            .unwrap()
            .push(file_source_result);
        if let Err(parse_error) = perf_logger_event.time("merge_file_source_changes", || {
            compiler_state.merge_file_source_changes(
                &self.config,
                perf_logger_event,
                perf_logger,
                true,
            )
        }) {
            Ok(Err(parse_error))
        } else {
            Ok(Ok(compiler_state))
        }
    }
}

pub struct FileSourceSubscription {
    resolved_root: ResolvedRoot,
    subscription: WatchmanSubscription<WatchmanFile>,
    base_revision: String,
}

impl FileSourceSubscription {
    fn new(resolved_root: ResolvedRoot, subscription: WatchmanSubscription<WatchmanFile>) -> Self {
        Self {
            resolved_root,
            subscription,
            base_revision: get_base_revision(None),
        }
    }

    /// Awaits changes from Watchman and provides the next set of changes
    /// if there were any changes to files
    pub async fn next_change(&mut self) -> Result<FileSourceSubscriptionNextChange> {
        match self.subscription.next().await? {
            SubscriptionData::FilesChanged(changes) => {
                if let Some(files) = changes.files {
                    debug!("number of files in this update: {}", files.len());
                    return Ok(FileSourceSubscriptionNextChange::Result(FileSourceResult {
                        files,
                        resolved_root: self.resolved_root.clone(),
                        clock: changes.clock,
                        saved_state_info: None,
                    }));
                }
            }
            SubscriptionData::StateEnter { state_name, .. } => {
                if state_name == "hg.update" {
                    return Ok(FileSourceSubscriptionNextChange::SourceControlUpdateEnter);
                }
            }
            SubscriptionData::StateLeave {
                state_name,
                metadata,
            } => {
                if state_name == "hg.update" {
                    let current_commit = if let Some(Value::ByteString(value)) = metadata {
                        Some(value.to_string())
                    } else {
                        None
                    };
                    let current_base_revision = get_base_revision(current_commit);
                    if current_base_revision != self.base_revision {
                        self.base_revision = current_base_revision;
                        return Ok(FileSourceSubscriptionNextChange::SourceControlUpdate);
                    } else {
                        return Ok(FileSourceSubscriptionNextChange::SourceControlUpdateLeave);
                    }
                }
            }
            SubscriptionData::Canceled => {
                return Err(Error::WatchmanSubscriptionCanceled);
            }
        }
        Ok(FileSourceSubscriptionNextChange::None)
    }
}

/// Base revision in this case is a common ancestor of two revisions:
/// `master` and current commit hash or `.`
fn get_base_revision(commit_hash: Option<String>) -> String {
    let output = Command::new("hg")
        .arg("log".to_string())
        .arg("-r".to_string())
        .arg(format!(
            "ancestor(master, {})",
            commit_hash.unwrap_or_else(|| ".".to_string())
        ))
        .arg("-T={node}")
        .output()
        .expect("Expect `hg` command getting base revision.");

    if output.stdout.is_empty() {
        panic!(
            "Failed to get base revision hash:\n {:?}",
            String::from_utf8_lossy(&output.stderr)
        );
    }

    String::from_utf8_lossy(&output.stdout).to_string()
}
<|endoftext|>


<commit_before>use common::target::Target;
use common::log;
use cpp_data::CppTypeData;
use cpp_method::CppMethod;
use cpp_method::CppMethodDoc;
use cpp_data::CppTypeDoc;
use cpp_data::CppOriginLocation;
use cpp_data::CppEnumValue;
use cpp_data::CppClassField;
use cpp_data::CppBaseSpecifier;
use new_impl::html_logger::HtmlLogger;
use std::path::Path;
use common::errors::{ChainErr, Result};
use std::fmt::Display;
use std::fmt::Formatter;
use cpp_data::CppTypeKind;
use common::string_utils::JoinWithSeparator;
use cpp_data::CppVisibility;
use new_impl::html_logger::escape_html;
//use common::errors::Result;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CppField; // TODO: fill

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataSource {
  CppParser,
  CppChecker,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DataEnv {
  pub target: Target,
  pub data_source: DataSource,
  pub cpp_library_version: Option<String>,
}

// TODO: attach this data to DataSource enum instead?
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct DataEnvInfo {
  pub is_success: bool,
  pub error: Option<String>,
  /// File name of the include file (without full path)
  pub include_file: Option<String>,
  /// Exact location of the declaration
  pub origin_location: Option<CppOriginLocation>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DataEnvWithInfo {
  pub env: DataEnv,
  pub info: DataEnvInfo,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CppItemData {
  Type(CppTypeData),
  Method(CppMethod),
  EnumValue(CppEnumValue),
  ClassField(CppClassField),
  ClassBase(CppBaseSpecifier),
}

impl Display for CppItemData {
  fn fmt(&self, f: &mut Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
    let s = match *self {
      CppItemData::Type(ref type1) => match type1.kind {
        CppTypeKind::Enum => format!("enum {}", type1.name),
        CppTypeKind::Class {
          ref template_arguments,
        } => format!(
          "class {}{}",
          type1.name,
          if let Some(ref args) = *template_arguments {
            format!(
              "<{}>",
              args.iter().map(|arg| arg.to_cpp_pseudo_code()).join(", ")
            )
          } else {
            String::new()
          }
        ),
      },
      CppItemData::Method(ref method) => method.short_text(),
      CppItemData::EnumValue(ref value) => format!(
        "enum {} {{ {} = {}, ... }}",
        value.enum_name, value.name, value.value
      ),
      CppItemData::ClassField(ref field) => {
        let visibility_text = match field.visibility {
          CppVisibility::Public => "",
          CppVisibility::Protected => "protected ",
          CppVisibility::Private => "private ",
        };
        format!(
          "class {} {{ {}{} {}; ... }}",
          field.class_type.to_cpp_pseudo_code(),
          visibility_text,
          field.field_type.to_cpp_pseudo_code(),
          field.name
        )
      }
      CppItemData::ClassBase(ref class_base) => {
        let virtual_text = if class_base.is_virtual {
          "virtual "
        } else {
          ""
        };
        let visibility_text = match class_base.visibility {
          CppVisibility::Public => "public",
          CppVisibility::Protected => "protected",
          CppVisibility::Private => "private",
        };
        let index_text = if class_base.base_index > 0 {
          format!(" (index: {}", class_base.base_index)
        } else {
          String::new()
        };
        format!(
          "class {} : {}{} {}{}",
          class_base.derived_class_type.to_cpp_pseudo_code(),
          virtual_text,
          visibility_text,
          class_base.base_class_type.to_cpp_pseudo_code(),
          index_text
        )
      }
    };
    f.write_str(&s)
  }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CppItemDoc {
  Type(CppTypeDoc),
  Method(CppMethodDoc),
  EnumValue(String),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseItem {
  pub environments: Vec<DataEnvWithInfo>,
  pub cpp_data: CppItemData,
  /// C++ documentation data for this type
  pub doc: Option<CppItemDoc>,
  // TODO: add cpp_ffi and rust data
}

/// Represents all collected data related to a crate.
#[derive(Debug, Serialize, Deserialize)]
pub struct Database {
  pub crate_name: String,
  pub items: Vec<DatabaseItem>,
  pub environments: Vec<DataEnv>,
}

impl Database {
  pub fn empty(crate_name: &str) -> Database {
    Database {
      crate_name: crate_name.to_owned(),
      items: Vec::new(),
      environments: Vec::new(),
    }
  }

  pub fn items(&self) -> &[DatabaseItem] {
    &self.items
  }

  pub fn crate_name(&self) -> &str {
    &self.crate_name
  }

  pub fn add_cpp_data(&mut self, env: DataEnv, data: CppItemData, info: DataEnvInfo) {
    if let Some(r) = self.items.iter_mut().find(|item| item.cpp_data == data) {
      if let Some(env1) = r.environments.iter_mut().find(|env2| env2.env == env) {
        if env1.info != info {
          log::llog(log::LoggerCategory::DebugGeneral, || {
            format!(
              "cpp env result changed for existing data!\n\
               env: {:?}\ndata: {:?}\nnew info: {:?}\nold info: {:?}\n",
              env, data, info, env1.info
            )
          });
          env1.info = info;
        }
        return;
      }
      log::llog(log::LoggerCategory::DebugGeneral, || {
        format!(
          "cpp new env for existing data!\n\
           env: {:?}\ndata: {:?}\ninfo: {:?}\n",
          env, data, info
        )
      });
      r.environments.push(DataEnvWithInfo { env, info });
      return;
    }
    log::llog(log::LoggerCategory::DebugGeneral, || {
      format!(
        "cpp new data!\n\
         env: {:?}\ndata: {:?}\ninfo: {:?}\n",
        env, data, info
      )
    });
    self.items.push(DatabaseItem {
      environments: vec![DataEnvWithInfo { env, info }],
      cpp_data: data,
      doc: None,
    });
  }

  pub fn print_as_html(&self, path: &Path) -> Result<()> {
    let mut logger = HtmlLogger::new(
      path,
      &format!("Database for crate \"{}\"", &self.crate_name),
    )?;
    logger.add_header(&["Item", "Environments"]);
    for item in &self.items {
      logger.add(&[&escape_html(&item.cpp_data.to_string()), "..."], "");
    }
    Ok(())
  }
  /*
  pub fn mark_missing_cpp_data(&mut self, env: DataEnv) {
    let info = DataEnvInfo {
      is_success: false,
      ..DataEnvInfo::default()
    };
    for item in &mut self.items {
      if !item.environments.iter().any(|env2| env2.env == env) {
        item.environments.push(DataEnvWithInfo {
          env: env.clone(),
          info: info.clone(),
        });
      }
    }
  }*/
}
<commit_msg>database print to html added<commit_after>use common::target::Target;
use common::log;
use cpp_data::CppTypeData;
use cpp_method::CppMethod;
use cpp_method::CppMethodDoc;
use cpp_data::CppTypeDoc;
use cpp_data::CppOriginLocation;
use cpp_data::CppEnumValue;
use cpp_data::CppClassField;
use cpp_data::CppBaseSpecifier;
use new_impl::html_logger::HtmlLogger;
use std::path::Path;
use common::errors::{ChainErr, Result};
use std::fmt::Display;
use std::fmt::Formatter;
use cpp_data::CppTypeKind;
use common::string_utils::JoinWithSeparator;
use cpp_data::CppVisibility;
use new_impl::html_logger::escape_html;
//use common::errors::Result;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CppField; // TODO: fill

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataSource {
  CppParser,
  CppChecker,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DataEnv {
  pub target: Target,
  pub data_source: DataSource,
  pub cpp_library_version: Option<String>,
}

impl DataEnv {
  pub fn short_text(&self) -> String {
    format!(
      "{}/{:?}-{:?}-{:?}-{:?}",
      self
        .cpp_library_version
        .as_ref()
        .map(|s| s.as_str())
        .unwrap_or("None"),
      self.target.arch,
      self.target.os,
      self.target.family,
      self.target.env
    )
  }
}

// TODO: attach this data to DataSource enum instead?
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct DataEnvInfo {
  pub is_success: bool,
  pub error: Option<String>,
  /// File name of the include file (without full path)
  pub include_file: Option<String>,
  /// Exact location of the declaration
  pub origin_location: Option<CppOriginLocation>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DataEnvWithInfo {
  pub env: DataEnv,
  pub info: DataEnvInfo,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CppItemData {
  Type(CppTypeData),
  Method(CppMethod),
  EnumValue(CppEnumValue),
  ClassField(CppClassField),
  ClassBase(CppBaseSpecifier),
}

impl Display for CppItemData {
  fn fmt(&self, f: &mut Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
    let s = match *self {
      CppItemData::Type(ref type1) => match type1.kind {
        CppTypeKind::Enum => format!("enum {}", type1.name),
        CppTypeKind::Class {
          ref template_arguments,
        } => format!(
          "class {}{}",
          type1.name,
          if let Some(ref args) = *template_arguments {
            format!(
              "<{}>",
              args.iter().map(|arg| arg.to_cpp_pseudo_code()).join(", ")
            )
          } else {
            String::new()
          }
        ),
      },
      CppItemData::Method(ref method) => method.short_text(),
      CppItemData::EnumValue(ref value) => format!(
        "enum {} {{ {} = {}, ... }}",
        value.enum_name, value.name, value.value
      ),
      CppItemData::ClassField(ref field) => {
        let visibility_text = match field.visibility {
          CppVisibility::Public => "",
          CppVisibility::Protected => "protected ",
          CppVisibility::Private => "private ",
        };
        format!(
          "class {} {{ {}{} {}; ... }}",
          field.class_type.to_cpp_pseudo_code(),
          visibility_text,
          field.field_type.to_cpp_pseudo_code(),
          field.name
        )
      }
      CppItemData::ClassBase(ref class_base) => {
        let virtual_text = if class_base.is_virtual {
          "virtual "
        } else {
          ""
        };
        let visibility_text = match class_base.visibility {
          CppVisibility::Public => "public",
          CppVisibility::Protected => "protected",
          CppVisibility::Private => "private",
        };
        let index_text = if class_base.base_index > 0 {
          format!(" (index: {}", class_base.base_index)
        } else {
          String::new()
        };
        format!(
          "class {} : {}{} {}{}",
          class_base.derived_class_type.to_cpp_pseudo_code(),
          virtual_text,
          visibility_text,
          class_base.base_class_type.to_cpp_pseudo_code(),
          index_text
        )
      }
    };
    f.write_str(&s)
  }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CppItemDoc {
  Type(CppTypeDoc),
  Method(CppMethodDoc),
  EnumValue(String),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseItem {
  pub environments: Vec<DataEnvWithInfo>,
  pub cpp_data: CppItemData,
  /// C++ documentation data for this type
  pub doc: Option<CppItemDoc>,
  // TODO: add cpp_ffi and rust data
}

/// Represents all collected data related to a crate.
#[derive(Debug, Serialize, Deserialize)]
pub struct Database {
  pub crate_name: String,
  pub items: Vec<DatabaseItem>,
  pub environments: Vec<DataEnv>,
}

impl Database {
  pub fn empty(crate_name: &str) -> Database {
    Database {
      crate_name: crate_name.to_owned(),
      items: Vec::new(),
      environments: Vec::new(),
    }
  }

  pub fn items(&self) -> &[DatabaseItem] {
    &self.items
  }

  pub fn crate_name(&self) -> &str {
    &self.crate_name
  }

  pub fn add_cpp_data(&mut self, env: DataEnv, data: CppItemData, info: DataEnvInfo) {
    if let Some(r) = self.items.iter_mut().find(|item| item.cpp_data == data) {
      if let Some(env1) = r.environments.iter_mut().find(|env2| env2.env == env) {
        if env1.info != info {
          log::llog(log::LoggerCategory::DebugGeneral, || {
            format!(
              "cpp env result changed for existing data!\n\
               env: {:?}\ndata: {:?}\nnew info: {:?}\nold info: {:?}\n",
              env, data, info, env1.info
            )
          });
          env1.info = info;
        }
        return;
      }
      log::llog(log::LoggerCategory::DebugGeneral, || {
        format!(
          "cpp new env for existing data!\n\
           env: {:?}\ndata: {:?}\ninfo: {:?}\n",
          env, data, info
        )
      });
      r.environments.push(DataEnvWithInfo { env, info });
      return;
    }
    log::llog(log::LoggerCategory::DebugGeneral, || {
      format!(
        "cpp new data!\n\
         env: {:?}\ndata: {:?}\ninfo: {:?}\n",
        env, data, info
      )
    });
    self.items.push(DatabaseItem {
      environments: vec![DataEnvWithInfo { env, info }],
      cpp_data: data,
      doc: None,
    });
  }

  pub fn print_as_html(&self, path: &Path) -> Result<()> {
    let mut logger = HtmlLogger::new(
      path,
      &format!("Database for crate \"{}\"", &self.crate_name),
    )?;
    logger.add_header(&["Item", "Environments"]);
    for item in &self.items {
      let item_text = item.cpp_data.to_string();
      let mut env_text = String::new();
      if !item.environments.is_empty() {
        env_text += &format!(
          "C++ parser: {}",
          item
            .environments
            .iter()
            .map(|env| env.env.short_text())
            .join(", ")
        );
      }

      logger.add(&[escape_html(&item_text).as_str(), env_text.as_str()], "");
    }
    Ok(())
  }
  /*
  pub fn mark_missing_cpp_data(&mut self, env: DataEnv) {
    let info = DataEnvInfo {
      is_success: false,
      ..DataEnvInfo::default()
    };
    for item in &mut self.items {
      if !item.environments.iter().any(|env2| env2.env == env) {
        item.environments.push(DataEnvWithInfo {
          env: env.clone(),
          info: info.clone(),
        });
      }
    }
  }*/
}
<|endoftext|>


<commit_before>use super::{Color, Point, Rect, Renderer, Widget, orbital};

pub struct WindowRenderer<'a> {
    inner: &'a mut Box<orbital::Window>
}

impl<'a> WindowRenderer<'a> {
    pub fn new(inner: &'a mut Box<orbital::Window>) -> WindowRenderer {
        WindowRenderer {
            inner: inner
        }
    }
}

impl<'a> Renderer for WindowRenderer<'a> {
    fn char(&mut self, pos: Point, c: char, color: Color) {
        self.inner.char(pos.x, pos.y, c, color);
    }

    fn rect(&mut self, rect: Rect, color: Color) {
        self.inner.rect(rect.x, rect.y, rect.width, rect.height, color);
    }
}

impl<'a> Drop for WindowRenderer<'a> {
    fn drop(&mut self) {
        self.inner.sync();
    }
}

pub struct Window {
    inner: Box<orbital::Window>,
    pub widgets: Vec<Box<Widget>>,
    pub bg: Color,
}

impl Window {
    pub fn new(rect: Rect, title: &str) -> Box<Window> {
        box Window {
            inner: orbital::Window::new(rect.x, rect.y, rect.width, rect.height, title).unwrap(),
            widgets: Vec::new(),
            bg: Color::rgb(255, 255, 255),
        }
    }

    pub fn draw(&mut self) {
        self.inner.set(self.bg);

        let mut renderer = WindowRenderer::new(&mut self.inner);
        for widget in self.widgets.iter() {
            widget.draw(&mut renderer);
        }
    }

    pub fn exec(&mut self) {
        self.draw();
        while let Some(event) = self.inner.poll() {
            for mut widget in self.widgets.iter_mut() {
                widget.event(&event);
            }

            self.draw();
        }
    }
}
<commit_msg>Change color to a typical window color<commit_after>use super::{Color, Point, Rect, Renderer, Widget, orbital};

pub struct WindowRenderer<'a> {
    inner: &'a mut Box<orbital::Window>
}

impl<'a> WindowRenderer<'a> {
    pub fn new(inner: &'a mut Box<orbital::Window>) -> WindowRenderer {
        WindowRenderer {
            inner: inner
        }
    }
}

impl<'a> Renderer for WindowRenderer<'a> {
    fn char(&mut self, pos: Point, c: char, color: Color) {
        self.inner.char(pos.x, pos.y, c, color);
    }

    fn rect(&mut self, rect: Rect, color: Color) {
        self.inner.rect(rect.x, rect.y, rect.width, rect.height, color);
    }
}

impl<'a> Drop for WindowRenderer<'a> {
    fn drop(&mut self) {
        self.inner.sync();
    }
}

pub struct Window {
    inner: Box<orbital::Window>,
    pub widgets: Vec<Box<Widget>>,
    pub bg: Color,
}

impl Window {
    pub fn new(rect: Rect, title: &str) -> Box<Window> {
        box Window {
            inner: orbital::Window::new(rect.x, rect.y, rect.width, rect.height, title).unwrap(),
            widgets: Vec::new(),
            bg: Color::rgb(237, 233, 227),
        }
    }

    pub fn draw(&mut self) {
        self.inner.set(self.bg);

        let mut renderer = WindowRenderer::new(&mut self.inner);
        for widget in self.widgets.iter() {
            widget.draw(&mut renderer);
        }
    }

    pub fn exec(&mut self) {
        self.draw();
        while let Some(event) = self.inner.poll() {
            for mut widget in self.widgets.iter_mut() {
                widget.event(&event);
            }

            self.draw();
        }
    }
}
<|endoftext|>


<commit_before>/// The macro that surrounds definitions of Lucet hostcalls in Rust.
///
/// It is important to use this macro for hostcalls, rather than exporting them directly, as it
/// installs unwind protection that prevents panics from unwinding into the guest stack.
///
/// Since this is not yet a proc macro, the syntax is unfortunately fairly brittle. The functions it
/// encloses must be of the form:
///
/// ```ignore
/// #[$attr1]
/// #[$attr2]
/// ... // any number of attributes are supported; in most cases you will want `#[no_mangle]`
/// pub unsafe extern "C" fn $name( // must be `pub unsafe extern "C"`
///     &mut $vmctx,
///     $arg1: $arg1_ty,
///     $arg2: $arg2_ty,
///     ... , // trailing comma must always be present
/// ) -> $ret_ty { // return type must always be present even if it is `()`
///     // body
/// }
/// ```
#[macro_export]
macro_rules! lucet_hostcalls {
    {
        $(
            $(#[$attr:meta])*
            pub unsafe extern "C" fn $name:ident(
                &mut $vmctx:ident
                $(, $arg:ident : $arg_ty:ty )*,
            ) -> $ret_ty:ty {
                $($body:tt)*
            }
        )*
    } => {
        $(
            $(#[$attr])*
            pub unsafe extern "C" fn $name(
                vmctx_raw: *mut $crate::vmctx::lucet_vmctx,
                $( $arg: $arg_ty ),*
            ) -> $ret_ty {
                unsafe fn hostcall_impl(
                    $vmctx: &mut $crate::vmctx::Vmctx,
                    $( $arg : $arg_ty ),*
                ) -> $ret_ty {
                    $($body)*
                }
                let res = std::panic::catch_unwind(|| {
                    hostcall_impl(&mut $crate::vmctx::Vmctx::from_raw(vmctx_raw), $( $arg ),*)
                });
                match res {
                    Ok(res) => res,
                    Err(e) => {
                        if let Some(details) = e.downcast_ref::<$crate::instance::TerminationDetails>() {
                            let mut vmctx = $crate::vmctx::Vmctx::from_raw(vmctx_raw);
                            vmctx.terminate_no_unwind(details.clone());
                        } else {
                            std::panic::resume_unwind(e);
                        }
                    }
                }
            }
        )*
    }
}

/// Terminate an instance from within a hostcall, returning an optional value as an error.
///
/// Use this instead of `panic!` when you want the instance to terminate, but not the entire host
/// program. Like `panic!`, you can pass a format string with arguments, a value that implements
/// `Any`, or nothing to return a default message.
///
/// Upon termination, the call to `Instance::run()` will return with an
/// `Err(Error::RuntimeTerminated)` value containing the value you pass to this macro.
///
/// This macro safely unwinds the hostcall stack out to the entrypoint of the hostcall, so any
/// resources that may have been acquired will be properly dropped.
#[macro_export]
macro_rules! lucet_hostcall_terminate {
    () => {
        lucet_hostcall_terminate!("lucet_hostcall_terminate")
    };
    ( $payload:expr ) => {
        panic!($crate::instance::TerminationDetails::provide($payload))
    };
    ( $payload:expr, ) => {
        lucet_hostcall_terminate!($payload)
    };
    ( $fmt:expr, $($arg:tt)+ ) => {
        lucet_hostcall_terminate!(format!($fmt, $($arg),+))
    };
}
<commit_msg>[lucet-runtime] always inline hostcall implementation; use `move`<commit_after>/// The macro that surrounds definitions of Lucet hostcalls in Rust.
///
/// It is important to use this macro for hostcalls, rather than exporting them directly, as it
/// installs unwind protection that prevents panics from unwinding into the guest stack.
///
/// Since this is not yet a proc macro, the syntax is unfortunately fairly brittle. The functions it
/// encloses must be of the form:
///
/// ```ignore
/// #[$attr1]
/// #[$attr2]
/// ... // any number of attributes are supported; in most cases you will want `#[no_mangle]`
/// pub unsafe extern "C" fn $name( // must be `pub unsafe extern "C"`
///     &mut $vmctx,
///     $arg1: $arg1_ty,
///     $arg2: $arg2_ty,
///     ... , // trailing comma must always be present
/// ) -> $ret_ty { // return type must always be present even if it is `()`
///     // body
/// }
/// ```
#[macro_export]
macro_rules! lucet_hostcalls {
    {
        $(
            $(#[$attr:meta])*
            pub unsafe extern "C" fn $name:ident(
                &mut $vmctx:ident
                $(, $arg:ident : $arg_ty:ty )*,
            ) -> $ret_ty:ty {
                $($body:tt)*
            }
        )*
    } => {
        $(
            $(#[$attr])*
            pub unsafe extern "C" fn $name(
                vmctx_raw: *mut $crate::vmctx::lucet_vmctx,
                $( $arg: $arg_ty ),*
            ) -> $ret_ty {
                #[inline(always)]
                unsafe fn hostcall_impl(
                    $vmctx: &mut $crate::vmctx::Vmctx,
                    $( $arg : $arg_ty ),*
                ) -> $ret_ty {
                    $($body)*
                }
                let res = std::panic::catch_unwind(move || {
                    hostcall_impl(&mut $crate::vmctx::Vmctx::from_raw(vmctx_raw), $( $arg ),*)
                });
                match res {
                    Ok(res) => res,
                    Err(e) => {
                        if let Some(details) = e.downcast_ref::<$crate::instance::TerminationDetails>() {
                            let mut vmctx = $crate::vmctx::Vmctx::from_raw(vmctx_raw);
                            vmctx.terminate_no_unwind(details.clone());
                        } else {
                            std::panic::resume_unwind(e);
                        }
                    }
                }
            }
        )*
    }
}

/// Terminate an instance from within a hostcall, returning an optional value as an error.
///
/// Use this instead of `panic!` when you want the instance to terminate, but not the entire host
/// program. Like `panic!`, you can pass a format string with arguments, a value that implements
/// `Any`, or nothing to return a default message.
///
/// Upon termination, the call to `Instance::run()` will return with an
/// `Err(Error::RuntimeTerminated)` value containing the value you pass to this macro.
///
/// This macro safely unwinds the hostcall stack out to the entrypoint of the hostcall, so any
/// resources that may have been acquired will be properly dropped.
#[macro_export]
macro_rules! lucet_hostcall_terminate {
    () => {
        lucet_hostcall_terminate!("lucet_hostcall_terminate")
    };
    ( $payload:expr ) => {
        panic!($crate::instance::TerminationDetails::provide($payload))
    };
    ( $payload:expr, ) => {
        lucet_hostcall_terminate!($payload)
    };
    ( $fmt:expr, $($arg:tt)+ ) => {
        lucet_hostcall_terminate!(format!($fmt, $($arg),+))
    };
}
<|endoftext|>