<commit_before>use super::{error::CompilerError, utils::AdjustCasingOfFirstLetter};
use crate::input::Input;
use itertools::Itertools;
use linked_hash_map::LinkedHashMap;
use num_bigint::BigUint;
use std::{
    fmt::{self, Display, Formatter},
    ops::Deref,
};

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Id {
    pub input: Input,
    pub local: usize,
}
impl Id {
    pub fn new(input: Input, local: usize) -> Self {
        Self { input, local }
    }
}
impl Display for Id {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "AstId({}:{})", self.input, self.local)
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Ast {
    pub id: Id,
    pub kind: AstKind,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AstKind {
    Int(Int),
    Text(Text),
    Identifier(Identifier),
    Symbol(Symbol),
    Struct(Struct),
    StructAccess(StructAccess),
    Lambda(Lambda),
    Call(Call),
    Assignment(Assignment),
    Error {
        /// The child may be set if it still makes sense to continue working
        /// with the error-containing subtree.
        child: Option<Box<Ast>>,
        errors: Vec<CompilerError>,
    },
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Int(pub BigUint);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Text(pub AstString);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Identifier(pub AstString);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Symbol(pub AstString);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Struct {
    pub positional_fields: Vec<Ast>,
    pub named_fields: LinkedHashMap<Ast, Ast>,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct StructAccess {
    pub struct_: Box<Ast>,
    pub key: AstString,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Lambda {
    pub parameters: Vec<AstString>,
    pub body: Vec<Ast>,
    pub fuzzable: bool,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Call {
    pub receiver: CallReceiver,
    pub arguments: Vec<Ast>,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum CallReceiver {
    Identifier(AstString),
    StructAccess(StructAccess),
    Call(Box<Call>),
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Assignment {
    pub name: AstString,
    pub is_public: bool,
    pub body: AssignmentBody,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AssignmentBody {
    Lambda(Lambda),
    Body(Vec<Ast>),
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct AstString {
    pub id: Id,
    pub value: String,
}
impl Deref for AstString {
    type Target = String;
    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AstError {
    UnexpectedPunctuation,
    TextWithoutClosingQuote,
    ParenthesizedWithoutClosingParenthesis,
    CallOfANonIdentifier,
    StructWithNonStructField,
    StructWithoutClosingBrace,
    StructKeyWithoutColon,
    StructValueWithoutComma,
    StructPositionalAfterNamedField,
    ExpectedIdentifier,
    ExpectedParameter,
    LambdaWithoutClosingCurlyBrace,
}

pub trait FindAst {
    fn find(&self, id: &Id) -> Option<&Ast>;
}
impl FindAst for Ast {
    fn find(&self, id: &Id) -> Option<&Ast> {
        if id == &self.id {
            return Some(self);
        };

        match &self.kind {
            AstKind::Int(_) => None,
            AstKind::Text(_) => None,
            AstKind::Identifier(_) => None,
            AstKind::Symbol(_) => None,
            AstKind::Struct(struct_) => struct_.find(id),
            AstKind::StructAccess(access) => access.find(id),
            AstKind::Lambda(lambda) => lambda.find(id),
            AstKind::Call(call) => call.find(id),
            AstKind::Assignment(assignment) => assignment.find(id),
            AstKind::Error { child, .. } => child.as_ref().and_then(|child| child.find(id)),
        }
    }
}
impl FindAst for Struct {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.positional_fields
            .iter()
            .filter_map(|value| value.find(id))
            .chain(
                self.named_fields
                    .iter()
                    .filter_map(|(key, value)| key.find(id).or_else(|| value.find(id))),
            )
            .next()
    }
}
impl FindAst for StructAccess {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.struct_.find(id)
    }
}
impl FindAst for Lambda {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.body.find(id)
    }
}
impl FindAst for Call {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.receiver.find(id).or_else(|| self.arguments.find(id))
    }
}
impl FindAst for CallReceiver {
    fn find(&self, id: &Id) -> Option<&Ast> {
        match self {
            CallReceiver::Identifier(_) => None,
            CallReceiver::StructAccess(access) => access.find(id),
            CallReceiver::Call(call) => call.find(id),
        }
    }
}
impl FindAst for Assignment {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.body.find(id)
    }
}
impl FindAst for AssignmentBody {
    fn find(&self, id: &Id) -> Option<&Ast> {
        match self {
            AssignmentBody::Lambda(lambda) => lambda.find(id),
            AssignmentBody::Body(body) => body.find(id),
        }
    }
}
impl FindAst for Vec<Ast> {
    fn find(&self, id: &Id) -> Option<&Ast> {
        for ast in self {
            if let Some(ast) = ast.find(id) {
                return Some(ast);
            }
        }
        None
    }
}

pub trait CollectErrors {
    fn collect_errors(self, errors: &mut Vec<CompilerError>);
}
impl CollectErrors for Ast {
    fn collect_errors(self, errors: &mut Vec<CompilerError>) {
        match self.kind {
            AstKind::Int(_) => {}
            AstKind::Text(_) => {}
            AstKind::Identifier(_) => {}
            AstKind::Symbol(_) => {}
            AstKind::Struct(struct_) => {
                for value in struct_.positional_fields {
                    value.collect_errors(errors);
                }
                for (key, value) in struct_.named_fields {
                    key.collect_errors(errors);
                    value.collect_errors(errors);
                }
            }
            AstKind::StructAccess(struct_access) => {
                struct_access.struct_.collect_errors(errors);
            }
            AstKind::Lambda(lambda) => lambda.body.collect_errors(errors),
            AstKind::Call(call) => call.arguments.collect_errors(errors),
            AstKind::Assignment(assignment) => match assignment.body {
                AssignmentBody::Lambda(lambda) => lambda.body.collect_errors(errors),
                AssignmentBody::Body(body) => {
                    for ast in body {
                        ast.collect_errors(errors)
                    }
                }
            },
            AstKind::Error {
                child,
                errors: mut recovered_errors,
            } => {
                errors.append(&mut recovered_errors);
                child.map(|child| child.collect_errors(errors));
            }
        }
    }
}
impl CollectErrors for Vec<Ast> {
    fn collect_errors(self, errors: &mut Vec<CompilerError>) {
        for ast in self {
            ast.collect_errors(errors);
        }
    }
}

impl Display for Ast {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}: ", self.id)?;
        match &self.kind {
            AstKind::Int(int) => write!(f, "int {}", int.0),
            AstKind::Text(text) => write!(f, "text \"{}\"", text.0),
            AstKind::Identifier(identifier) => write!(f, "identifier {}", identifier.0),
            AstKind::Symbol(symbol) => write!(f, "symbol {}", symbol.0),
            AstKind::Struct(struct_) => {
                write!(
                    f,
                    "struct [\n{}\n]",
                    struct_
                        .positional_fields
                        .iter()
                        .map(|value| format!("{value},"))
                        .chain(
                            struct_
                                .named_fields
                                .iter()
                                .map(|(key, value)| format!("{key}: {value},"))
                        )
                        .join("\n")
                        .lines()
                        .map(|line| format!("  {line}"))
                        .join("\n")
                )
            }
            AstKind::StructAccess(struct_access) => write!(f, "{struct_access}"),
            AstKind::Lambda(lambda) => write!(f, "{}", lambda),
            AstKind::Call(call) => write!(f, "{}", call),
            AstKind::Assignment(assignment) => {
                write!(
                    f,
                    "assignment: {} =\n{}",
                    assignment.name,
                    format!("{}", assignment.body)
                        .lines()
                        .map(|line| format!("  {line}"))
                        .join("\n"),
                )
            }
            AstKind::Error { child, errors } => {
                write!(
                    f,
                    "error:\n{}",
                    errors.iter().map(|error| format!("  {error:?}")).join("\n")
                )?;
                if let Some(child) = child {
                    write!(f, "\n  fallback: {child}")?;
                }
                Ok(())
            }
        }
    }
}
impl Display for AssignmentBody {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            AssignmentBody::Lambda(lambda) => write!(f, "{lambda}"),
            AssignmentBody::Body(body) => {
                write!(f, "{}", body.iter().map(|it| format!("{it}")).join("\n"))
            }
        }
    }
}
impl Display for Lambda {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "lambda ({}) {{ {} ->\n{}\n}}",
            if self.fuzzable {
                "fuzzable"
            } else {
                "non-fuzzable"
            },
            self.parameters.iter().map(|it| format!("{it}")).join(" "),
            self.body
                .iter()
                .map(|it| format!("{it}"))
                .join("\n")
                .lines()
                .map(|line| format!("  {line}"))
                .join("\n")
        )
    }
}
impl Display for StructAccess {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "struct access {}.{}",
            self.struct_,
            self.key.lowercase_first_letter()
        )
    }
}
impl Display for Call {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "call {} with these arguments:\n{}",
            self.receiver,
            self.arguments
                .iter()
                .map(|argument| format!("  {argument}"))
                .join("\n")
        )
    }
}
impl Display for CallReceiver {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            CallReceiver::Identifier(identifier) => write!(f, "{}", identifier),
            CallReceiver::StructAccess(struct_access) => write!(f, "{}", struct_access),
            CallReceiver::Call(call) => write!(f, "{}", call),
        }
    }
}
impl Display for AstString {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}@\"{}\"", self.id, self.value)
    }
}
<commit_msg>Simplify ast.find(…)<commit_after>use super::{error::CompilerError, utils::AdjustCasingOfFirstLetter};
use crate::input::Input;
use itertools::Itertools;
use linked_hash_map::LinkedHashMap;
use num_bigint::BigUint;
use std::{
    fmt::{self, Display, Formatter},
    ops::Deref,
};

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Id {
    pub input: Input,
    pub local: usize,
}
impl Id {
    pub fn new(input: Input, local: usize) -> Self {
        Self { input, local }
    }
}
impl Display for Id {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "AstId({}:{})", self.input, self.local)
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Ast {
    pub id: Id,
    pub kind: AstKind,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AstKind {
    Int(Int),
    Text(Text),
    Identifier(Identifier),
    Symbol(Symbol),
    Struct(Struct),
    StructAccess(StructAccess),
    Lambda(Lambda),
    Call(Call),
    Assignment(Assignment),
    Error {
        /// The child may be set if it still makes sense to continue working
        /// with the error-containing subtree.
        child: Option<Box<Ast>>,
        errors: Vec<CompilerError>,
    },
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Int(pub BigUint);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Text(pub AstString);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Identifier(pub AstString);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Symbol(pub AstString);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Struct {
    pub positional_fields: Vec<Ast>,
    pub named_fields: LinkedHashMap<Ast, Ast>,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct StructAccess {
    pub struct_: Box<Ast>,
    pub key: AstString,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Lambda {
    pub parameters: Vec<AstString>,
    pub body: Vec<Ast>,
    pub fuzzable: bool,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Call {
    pub receiver: CallReceiver,
    pub arguments: Vec<Ast>,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum CallReceiver {
    Identifier(AstString),
    StructAccess(StructAccess),
    Call(Box<Call>),
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Assignment {
    pub name: AstString,
    pub is_public: bool,
    pub body: AssignmentBody,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AssignmentBody {
    Lambda(Lambda),
    Body(Vec<Ast>),
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct AstString {
    pub id: Id,
    pub value: String,
}
impl Deref for AstString {
    type Target = String;
    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AstError {
    UnexpectedPunctuation,
    TextWithoutClosingQuote,
    ParenthesizedWithoutClosingParenthesis,
    CallOfANonIdentifier,
    StructWithNonStructField,
    StructWithoutClosingBrace,
    StructKeyWithoutColon,
    StructValueWithoutComma,
    StructPositionalAfterNamedField,
    ExpectedIdentifier,
    ExpectedParameter,
    LambdaWithoutClosingCurlyBrace,
}

pub trait FindAst {
    fn find(&self, id: &Id) -> Option<&Ast>;
}
impl FindAst for Ast {
    fn find(&self, id: &Id) -> Option<&Ast> {
        if id == &self.id {
            return Some(self);
        };

        match &self.kind {
            AstKind::Int(_) => None,
            AstKind::Text(_) => None,
            AstKind::Identifier(_) => None,
            AstKind::Symbol(_) => None,
            AstKind::Struct(struct_) => struct_.find(id),
            AstKind::StructAccess(access) => access.find(id),
            AstKind::Lambda(lambda) => lambda.find(id),
            AstKind::Call(call) => call.find(id),
            AstKind::Assignment(assignment) => assignment.find(id),
            AstKind::Error { child, .. } => child.as_ref().and_then(|child| child.find(id)),
        }
    }
}
impl FindAst for Struct {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.positional_fields
            .iter()
            .chain(
                self.named_fields
                    .iter()
                    .flat_map(|(key, value)| vec![key, value].into_iter()),
            )
            .filter_map(|value| value.find(id))
            .next()
    }
}
impl FindAst for StructAccess {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.struct_.find(id)
    }
}
impl FindAst for Lambda {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.body.find(id)
    }
}
impl FindAst for Call {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.receiver.find(id).or_else(|| self.arguments.find(id))
    }
}
impl FindAst for CallReceiver {
    fn find(&self, id: &Id) -> Option<&Ast> {
        match self {
            CallReceiver::Identifier(_) => None,
            CallReceiver::StructAccess(access) => access.find(id),
            CallReceiver::Call(call) => call.find(id),
        }
    }
}
impl FindAst for Assignment {
    fn find(&self, id: &Id) -> Option<&Ast> {
        self.body.find(id)
    }
}
impl FindAst for AssignmentBody {
    fn find(&self, id: &Id) -> Option<&Ast> {
        match self {
            AssignmentBody::Lambda(lambda) => lambda.find(id),
            AssignmentBody::Body(body) => body.find(id),
        }
    }
}
impl FindAst for Vec<Ast> {
    fn find(&self, id: &Id) -> Option<&Ast> {
        for ast in self {
            if let Some(ast) = ast.find(id) {
                return Some(ast);
            }
        }
        None
    }
}

pub trait CollectErrors {
    fn collect_errors(self, errors: &mut Vec<CompilerError>);
}
impl CollectErrors for Ast {
    fn collect_errors(self, errors: &mut Vec<CompilerError>) {
        match self.kind {
            AstKind::Int(_) => {}
            AstKind::Text(_) => {}
            AstKind::Identifier(_) => {}
            AstKind::Symbol(_) => {}
            AstKind::Struct(struct_) => {
                for value in struct_.positional_fields {
                    value.collect_errors(errors);
                }
                for (key, value) in struct_.named_fields {
                    key.collect_errors(errors);
                    value.collect_errors(errors);
                }
            }
            AstKind::StructAccess(struct_access) => {
                struct_access.struct_.collect_errors(errors);
            }
            AstKind::Lambda(lambda) => lambda.body.collect_errors(errors),
            AstKind::Call(call) => call.arguments.collect_errors(errors),
            AstKind::Assignment(assignment) => match assignment.body {
                AssignmentBody::Lambda(lambda) => lambda.body.collect_errors(errors),
                AssignmentBody::Body(body) => {
                    for ast in body {
                        ast.collect_errors(errors)
                    }
                }
            },
            AstKind::Error {
                child,
                errors: mut recovered_errors,
            } => {
                errors.append(&mut recovered_errors);
                child.map(|child| child.collect_errors(errors));
            }
        }
    }
}
impl CollectErrors for Vec<Ast> {
    fn collect_errors(self, errors: &mut Vec<CompilerError>) {
        for ast in self {
            ast.collect_errors(errors);
        }
    }
}

impl Display for Ast {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}: ", self.id)?;
        match &self.kind {
            AstKind::Int(int) => write!(f, "int {}", int.0),
            AstKind::Text(text) => write!(f, "text \"{}\"", text.0),
            AstKind::Identifier(identifier) => write!(f, "identifier {}", identifier.0),
            AstKind::Symbol(symbol) => write!(f, "symbol {}", symbol.0),
            AstKind::Struct(struct_) => {
                write!(
                    f,
                    "struct [\n{}\n]",
                    struct_
                        .positional_fields
                        .iter()
                        .map(|value| format!("{value},"))
                        .chain(
                            struct_
                                .named_fields
                                .iter()
                                .map(|(key, value)| format!("{key}: {value},"))
                        )
                        .join("\n")
                        .lines()
                        .map(|line| format!("  {line}"))
                        .join("\n")
                )
            }
            AstKind::StructAccess(struct_access) => write!(f, "{struct_access}"),
            AstKind::Lambda(lambda) => write!(f, "{}", lambda),
            AstKind::Call(call) => write!(f, "{}", call),
            AstKind::Assignment(assignment) => {
                write!(
                    f,
                    "assignment: {} =\n{}",
                    assignment.name,
                    format!("{}", assignment.body)
                        .lines()
                        .map(|line| format!("  {line}"))
                        .join("\n"),
                )
            }
            AstKind::Error { child, errors } => {
                write!(
                    f,
                    "error:\n{}",
                    errors.iter().map(|error| format!("  {error:?}")).join("\n")
                )?;
                if let Some(child) = child {
                    write!(f, "\n  fallback: {child}")?;
                }
                Ok(())
            }
        }
    }
}
impl Display for AssignmentBody {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            AssignmentBody::Lambda(lambda) => write!(f, "{lambda}"),
            AssignmentBody::Body(body) => {
                write!(f, "{}", body.iter().map(|it| format!("{it}")).join("\n"))
            }
        }
    }
}
impl Display for Lambda {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "lambda ({}) {{ {} ->\n{}\n}}",
            if self.fuzzable {
                "fuzzable"
            } else {
                "non-fuzzable"
            },
            self.parameters.iter().map(|it| format!("{it}")).join(" "),
            self.body
                .iter()
                .map(|it| format!("{it}"))
                .join("\n")
                .lines()
                .map(|line| format!("  {line}"))
                .join("\n")
        )
    }
}
impl Display for StructAccess {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "struct access {}.{}",
            self.struct_,
            self.key.lowercase_first_letter()
        )
    }
}
impl Display for Call {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "call {} with these arguments:\n{}",
            self.receiver,
            self.arguments
                .iter()
                .map(|argument| format!("  {argument}"))
                .join("\n")
        )
    }
}
impl Display for CallReceiver {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            CallReceiver::Identifier(identifier) => write!(f, "{}", identifier),
            CallReceiver::StructAccess(struct_access) => write!(f, "{}", struct_access),
            CallReceiver::Call(call) => write!(f, "{}", call),
        }
    }
}
impl Display for AstString {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}@\"{}\"", self.id, self.value)
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::GamepadButtonListBinding;
use dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::root::{Dom, DomRoot, RootedReference};
use dom::gamepadbutton::GamepadButton;
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use webvr_traits::WebVRGamepadButton;

// https://w3c.github.io/gamepad/#gamepadbutton-interface
#[dom_struct]
pub struct GamepadButtonList {
    reflector_: Reflector,
    list: Vec<Dom<GamepadButton>>
}

impl GamepadButtonList {
    #[allow(unrooted_must_root)]
    fn new_inherited(list: &[&GamepadButton]) -> GamepadButtonList {
        GamepadButtonList {
            reflector_: Reflector::new(),
            list: list.iter().map(|button| Dom::from_ref(*button)).collect(),
        }
    }

    pub fn new_from_vr(global: &GlobalScope, buttons: &[WebVRGamepadButton]) -> DomRoot<GamepadButtonList> {
        rooted_vec!(let list <- buttons.iter()
                                       .map(|btn| GamepadButton::new(&global, btn.pressed, btn.touched)));

        reflect_dom_object(Box::new(GamepadButtonList::new_inherited(list.r())),
                           global,
                           GamepadButtonListBinding::Wrap)
    }

    pub fn sync_from_vr(&self, vr_buttons: &[WebVRGamepadButton]) {
        let mut index = 0;
        for btn in vr_buttons {
            self.list.get(index).as_ref().unwrap().update(btn.pressed, btn.touched);
            index += 1;
        }
    }
}

impl GamepadButtonListMethods for GamepadButtonList {
    // https://w3c.github.io/gamepad/#dom-gamepad-buttons
    fn Length(&self) -> u32 {
        self.list.len() as u32
    }

    // https://w3c.github.io/gamepad/#dom-gamepad-buttons
    fn Item(&self, index: u32) -> Option<DomRoot<GamepadButton>> {
        self.list.get(index as usize).map(|button| DomRoot::from_ref(&**button))
    }

    // https://w3c.github.io/gamepad/#dom-gamepad-buttons
    fn IndexedGetter(&self, index: u32) -> Option<DomRoot<GamepadButton>> {
        self.Item(index)
    }
}
<commit_msg>Auto merge of #20404 - MaximilianDauner:issue_20392, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::GamepadButtonListBinding;
use dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::root::{Dom, DomRoot, RootedReference};
use dom::gamepadbutton::GamepadButton;
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use webvr_traits::WebVRGamepadButton;

// https://w3c.github.io/gamepad/#gamepadbutton-interface
#[dom_struct]
pub struct GamepadButtonList {
    reflector_: Reflector,
    list: Vec<Dom<GamepadButton>>
}

impl GamepadButtonList {
    #[allow(unrooted_must_root)]
    fn new_inherited(list: &[&GamepadButton]) -> GamepadButtonList {
        GamepadButtonList {
            reflector_: Reflector::new(),
            list: list.iter().map(|button| Dom::from_ref(*button)).collect(),
        }
    }

    pub fn new_from_vr(global: &GlobalScope, buttons: &[WebVRGamepadButton]) -> DomRoot<GamepadButtonList> {
        rooted_vec!(let list <- buttons.iter()
                                       .map(|btn| GamepadButton::new(&global, btn.pressed, btn.touched)));

        reflect_dom_object(Box::new(GamepadButtonList::new_inherited(list.r())),
                           global,
                           GamepadButtonListBinding::Wrap)
    }

    pub fn sync_from_vr(&self, vr_buttons: &[WebVRGamepadButton]) {
        for (gp_btn, btn) in self.list.iter().zip(vr_buttons.iter()) {
            gp_btn.update(btn.pressed, btn.touched);
        }
    }
}

impl GamepadButtonListMethods for GamepadButtonList {
    // https://w3c.github.io/gamepad/#dom-gamepad-buttons
    fn Length(&self) -> u32 {
        self.list.len() as u32
    }

    // https://w3c.github.io/gamepad/#dom-gamepad-buttons
    fn Item(&self, index: u32) -> Option<DomRoot<GamepadButton>> {
        self.list.get(index as usize).map(|button| DomRoot::from_ref(&**button))
    }

    // https://w3c.github.io/gamepad/#dom-gamepad-buttons
    fn IndexedGetter(&self, index: u32) -> Option<DomRoot<GamepadButton>> {
        self.Item(index)
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::Attr;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding;
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, ElementCast, HTMLElementCast, NodeCast, TextDerived};
use dom::bindings::codegen::InheritTypes::{HTMLOptionElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::{Element, AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId};
use dom::virtualmethods::VirtualMethods;
use std::cell::Cell;
use util::str::{DOMString, split_html_space_chars, str_join};

#[dom_struct]
pub struct HTMLOptionElement {
    htmlelement: HTMLElement,

    /// https://html.spec.whatwg.org/multipage/#attr-option-selected
    selectedness: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/#concept-option-dirtiness
    dirtiness: Cell<bool>,
}

impl HTMLOptionElementDerived for EventTarget {
    fn is_htmloptionelement(&self) -> bool {
        *self.type_id() ==
            EventTargetTypeId::Node(
                NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement)))
    }
}

impl HTMLOptionElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLOptionElement {
        HTMLOptionElement {
            htmlelement:
                HTMLElement::new_inherited(HTMLElementTypeId::HTMLOptionElement, localName, prefix, document),
            selectedness: Cell::new(false),
            dirtiness: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLOptionElement> {
        let element = HTMLOptionElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLOptionElementBinding::Wrap)
    }
}

fn collect_text(element: &Element, value: &mut DOMString) {
    let svg_script = *element.namespace() == ns!(SVG) && element.local_name() == &atom!("script");
    let html_script = element.is_htmlscriptelement();
    if svg_script || html_script {
        return;
    } else {
        for child in NodeCast::from_ref(element).children() {
            if child.r().is_text() {
                let characterdata = CharacterDataCast::to_ref(child.r()).unwrap();
                value.push_str(&characterdata.Data());
            } else if let Some(element_child) = ElementCast::to_ref(&*child) {
                collect_text(element_child, value);
            }
        }
    }
}

impl HTMLOptionElementMethods for HTMLOptionElement {
    // https://www.whatwg.org/html/#dom-option-disabled
    make_bool_getter!(Disabled);

    // https://www.whatwg.org/html/#dom-option-disabled
    fn SetDisabled(&self, disabled: bool) {
        let elem = ElementCast::from_ref(self);
        elem.set_bool_attribute(&atom!("disabled"), disabled)
    }

    // https://www.whatwg.org/html/#dom-option-text
    fn Text(&self) -> DOMString {
        let element = ElementCast::from_ref(self);
        let mut content = String::new();
        collect_text(element, &mut content);
        str_join(split_html_space_chars(&content), " ")
    }

    // https://www.whatwg.org/html/#dom-option-text
    fn SetText(&self, value: DOMString) {
        let node = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }

    // https://html.spec.whatwg.org/multipage/#attr-option-value
    fn Value(&self) -> DOMString {
        let element = ElementCast::from_ref(self);
        let attr = &atom!("value");
        if element.has_attribute(attr) {
            element.get_string_attribute(attr)
        } else {
            self.Text()
        }
    }

    // https://html.spec.whatwg.org/multipage/#attr-option-value
    make_setter!(SetValue, "value");

    // https://html.spec.whatwg.org/multipage/#attr-option-label
    fn Label(&self) -> DOMString {
        let element = ElementCast::from_ref(self);
        let attr = &atom!("label");
        if element.has_attribute(attr) {
            element.get_string_attribute(attr)
        } else {
            self.Text()
        }
    }

    // https://html.spec.whatwg.org/multipage/#attr-option-label
    make_setter!(SetLabel, "label");

    // https://html.spec.whatwg.org/multipage/#dom-option-defaultselected
    make_bool_getter!(DefaultSelected, "selected");

    // https://html.spec.whatwg.org/multipage/#dom-option-defaultselected
    make_bool_setter!(SetDefaultSelected, "selected");

    // https://html.spec.whatwg.org/multipage/#dom-option-selected
    fn Selected(&self) -> bool {
        self.selectedness.get()
    }

    // https://html.spec.whatwg.org/multipage/#dom-option-selected
    fn SetSelected(&self, selected: bool) {
        self.dirtiness.set(true);
        self.selectedness.set(selected);
        // FIXME: as per the spec, implement 'ask for a reset'
        // https://github.com/servo/servo/issues/7774
    }
}

impl VirtualMethods for HTMLOptionElement {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &HTMLElement = HTMLElementCast::from_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &atom!(disabled) => {
                let node = NodeCast::from_ref(self);
                match mutation {
                    AttributeMutation::Set(_) => {
                        node.set_disabled_state(true);
                        node.set_enabled_state(false);
                    },
                    AttributeMutation::Removed => {
                        node.set_disabled_state(false);
                        node.set_enabled_state(true);
                        node.check_parent_disabled_state_for_option();
                    }
                }
            },
            &atom!(selected) => {
                match mutation {
                    AttributeMutation::Set(_) => {
                        // https://html.spec.whatwg.org/multipage/#concept-option-selectedness
                        if !self.dirtiness.get() {
                            self.selectedness.set(true);
                        }
                    },
                    AttributeMutation::Removed => {
                        // https://html.spec.whatwg.org/multipage/#concept-option-selectedness
                        if !self.dirtiness.get() {
                            self.selectedness.set(false);
                        }
                    },
                }
            },
            _ => {},
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        let node = NodeCast::from_ref(self);
        node.check_parent_disabled_state_for_option();
    }

    fn unbind_from_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.unbind_from_tree(tree_in_doc);
        }

        let node = NodeCast::from_ref(self);
        if node.GetParentNode().is_some() {
            node.check_parent_disabled_state_for_option();
        } else {
            node.check_disabled_attribute();
        }
    }
}
<commit_msg>Remove a pointless level of indentation.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::Attr;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding;
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, ElementCast, HTMLElementCast, NodeCast, TextDerived};
use dom::bindings::codegen::InheritTypes::{HTMLOptionElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::{Element, AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId};
use dom::virtualmethods::VirtualMethods;
use std::cell::Cell;
use util::str::{DOMString, split_html_space_chars, str_join};

#[dom_struct]
pub struct HTMLOptionElement {
    htmlelement: HTMLElement,

    /// https://html.spec.whatwg.org/multipage/#attr-option-selected
    selectedness: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/#concept-option-dirtiness
    dirtiness: Cell<bool>,
}

impl HTMLOptionElementDerived for EventTarget {
    fn is_htmloptionelement(&self) -> bool {
        *self.type_id() ==
            EventTargetTypeId::Node(
                NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement)))
    }
}

impl HTMLOptionElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLOptionElement {
        HTMLOptionElement {
            htmlelement:
                HTMLElement::new_inherited(HTMLElementTypeId::HTMLOptionElement, localName, prefix, document),
            selectedness: Cell::new(false),
            dirtiness: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLOptionElement> {
        let element = HTMLOptionElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLOptionElementBinding::Wrap)
    }
}

fn collect_text(element: &Element, value: &mut DOMString) {
    let svg_script = *element.namespace() == ns!(SVG) && element.local_name() == &atom!("script");
    let html_script = element.is_htmlscriptelement();
    if svg_script || html_script {
        return;
    }

    for child in NodeCast::from_ref(element).children() {
        if child.r().is_text() {
            let characterdata = CharacterDataCast::to_ref(child.r()).unwrap();
            value.push_str(&characterdata.Data());
        } else if let Some(element_child) = ElementCast::to_ref(&*child) {
            collect_text(element_child, value);
        }
    }
}

impl HTMLOptionElementMethods for HTMLOptionElement {
    // https://www.whatwg.org/html/#dom-option-disabled
    make_bool_getter!(Disabled);

    // https://www.whatwg.org/html/#dom-option-disabled
    fn SetDisabled(&self, disabled: bool) {
        let elem = ElementCast::from_ref(self);
        elem.set_bool_attribute(&atom!("disabled"), disabled)
    }

    // https://www.whatwg.org/html/#dom-option-text
    fn Text(&self) -> DOMString {
        let element = ElementCast::from_ref(self);
        let mut content = String::new();
        collect_text(element, &mut content);
        str_join(split_html_space_chars(&content), " ")
    }

    // https://www.whatwg.org/html/#dom-option-text
    fn SetText(&self, value: DOMString) {
        let node = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }

    // https://html.spec.whatwg.org/multipage/#attr-option-value
    fn Value(&self) -> DOMString {
        let element = ElementCast::from_ref(self);
        let attr = &atom!("value");
        if element.has_attribute(attr) {
            element.get_string_attribute(attr)
        } else {
            self.Text()
        }
    }

    // https://html.spec.whatwg.org/multipage/#attr-option-value
    make_setter!(SetValue, "value");

    // https://html.spec.whatwg.org/multipage/#attr-option-label
    fn Label(&self) -> DOMString {
        let element = ElementCast::from_ref(self);
        let attr = &atom!("label");
        if element.has_attribute(attr) {
            element.get_string_attribute(attr)
        } else {
            self.Text()
        }
    }

    // https://html.spec.whatwg.org/multipage/#attr-option-label
    make_setter!(SetLabel, "label");

    // https://html.spec.whatwg.org/multipage/#dom-option-defaultselected
    make_bool_getter!(DefaultSelected, "selected");

    // https://html.spec.whatwg.org/multipage/#dom-option-defaultselected
    make_bool_setter!(SetDefaultSelected, "selected");

    // https://html.spec.whatwg.org/multipage/#dom-option-selected
    fn Selected(&self) -> bool {
        self.selectedness.get()
    }

    // https://html.spec.whatwg.org/multipage/#dom-option-selected
    fn SetSelected(&self, selected: bool) {
        self.dirtiness.set(true);
        self.selectedness.set(selected);
        // FIXME: as per the spec, implement 'ask for a reset'
        // https://github.com/servo/servo/issues/7774
    }
}

impl VirtualMethods for HTMLOptionElement {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &HTMLElement = HTMLElementCast::from_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &atom!(disabled) => {
                let node = NodeCast::from_ref(self);
                match mutation {
                    AttributeMutation::Set(_) => {
                        node.set_disabled_state(true);
                        node.set_enabled_state(false);
                    },
                    AttributeMutation::Removed => {
                        node.set_disabled_state(false);
                        node.set_enabled_state(true);
                        node.check_parent_disabled_state_for_option();
                    }
                }
            },
            &atom!(selected) => {
                match mutation {
                    AttributeMutation::Set(_) => {
                        // https://html.spec.whatwg.org/multipage/#concept-option-selectedness
                        if !self.dirtiness.get() {
                            self.selectedness.set(true);
                        }
                    },
                    AttributeMutation::Removed => {
                        // https://html.spec.whatwg.org/multipage/#concept-option-selectedness
                        if !self.dirtiness.get() {
                            self.selectedness.set(false);
                        }
                    },
                }
            },
            _ => {},
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        let node = NodeCast::from_ref(self);
        node.check_parent_disabled_state_for_option();
    }

    fn unbind_from_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.unbind_from_tree(tree_in_doc);
        }

        let node = NodeCast::from_ref(self);
        if node.GetParentNode().is_some() {
            node.check_parent_disabled_state_for_option();
        } else {
            node.check_disabled_attribute();
        }
    }
}
<|endoftext|>


<commit_before>use std::mem::size_of;

// some data
static B: [u8; 10] = [99, 97, 114, 114, 121, 116, 111, 119, 101, 108];
static C: [u8; 11] = [116, 104, 97, 110, 107, 115, 102, 105, 115, 104, 0];

// print_info
fn print_info_ptr<T: std::fmt::Pointer>(desc: &str, x: T) {
    println!("{}:", desc);
    println!(" location:  {:p}", &x);
    println!(" size:      {:?} bytes", size_of::<T>());
    println!(" points to: {:p}", x);
}

fn print_info<T: std::fmt::Debug>(desc: &str, x: T) {
    println!("{}:", desc);
    println!(" location:  {:p}", &x);
    println!(" size:      {:?} bytes", size_of::<T>());
    println!(" value:     {:?}", x);
}

fn main() {
    let a = 42;
    let b = &B;
    let c = Box::new(C);

    print_info("a (an unsigned integer)", a);
    println!();

    print_info_ptr("b (a reference to B)", b);
    println!();

    print_info_ptr("c (a 'box' for C):", c);
    println!();

    print_info("B (an array of 10 bytes)", B);
    println!();

    print_info("C (an array of 11 bytes)", C);
}
<commit_msg>use where clause<commit_after>use std::mem::size_of;

// some data
static B: [u8; 10] = [99, 97, 114, 114, 121, 116, 111, 119, 101, 108];
static C: [u8; 11] = [116, 104, 97, 110, 107, 115, 102, 105, 115, 104, 0];

// print_info
fn print_info_ptr<T>(desc: &str, x: T)
where T : std::fmt::Pointer + std::fmt::Debug {
    println!("{}:", desc);
    println!(" location:  {:p}", &x);
    println!(" size:      {:?} bytes", size_of::<T>());
    println!(" points to: {:p}", x);
}

fn print_info<T: std::fmt::Debug>(desc: &str, x: T) {
    println!("{}:", desc);
    println!(" location:  {:p}", &x);
    println!(" size:      {:?} bytes", size_of::<T>());
    println!(" value:     {:?}", x);
}

fn main() {
    let a = 42;
    let b = &B;
    let c = Box::new(C);

    print_info("a (an unsigned integer)", a);
    println!();

    print_info_ptr("b (a reference to B)", b);
    println!();

    print_info_ptr("c (a 'box' for C):", c);
    println!();

    print_info("B (an array of 10 bytes)", B);
    println!();

    print_info("C (an array of 11 bytes)", C);
}
<|endoftext|>


<commit_before>use crate::ui::{NullUi,
                UIWriter};
use habitat_api_client as api_client;
use habitat_core as hcore;
use lazy_static::lazy_static;
use std::{collections::HashMap,
          env,
          ffi::OsStr,
          iter::FromIterator};

extern crate json;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[cfg_attr(test, macro_use)]
extern crate serde_json;
#[cfg(windows)]
extern crate winapi;

pub use self::error::{Error,
                      Result};

pub mod cli;
pub mod command;
pub mod error;
pub mod liveliness_checker;
pub mod output;
pub mod owning_refs;
pub mod package_graph;
pub mod templating;
pub mod types;
pub mod ui;
pub mod util;

lazy_static::lazy_static! {
    pub static ref PROGRAM_NAME: String = {
        match env::current_exe() {
            Ok(path) => path.file_stem().and_then(OsStr::to_str).unwrap().to_string(),
            Err(e) => {
                error!("Error getting path of current_exe: {}", e);
                String::from("hab-?")
            }
        }
    };
}

// TODO (CM): It would be nice to come up with a way to more
// programmatically manage these flags. It's a bit of a pain to define
// the flag, and then define the environment variables
// separately. Nothing statically guarantees that you've specified an
// variable for a flag.

// TODO (CM): It'd be great to have a built-in way to document them,
// too.

// TODO (CM): Part of that documentation might be *when* a flag was
// added. In general, long-lived flags are a code-smell.

// TODO (CM): It may also be useful to break out features by area of
// concern. We can have any number of bitflags-generated structs.

bitflags::bitflags! {
    /// All the feature flags that are recogized by Habitat.
    ///
    /// In general, feature flags are enabled by setting the corresponding
    /// environment variable.
    ///
    /// Your binary should call `FeatureFlag::from_env` to get a set
    /// of flags to use.
    ///
    /// To add a new feature flag, you will need to add the bit mask
    /// constant here, as well as a mapping from the feature to the
    /// environment variable to which it corresponds in the `ENV_VARS`
    /// map below.
    pub struct FeatureFlag: u32 {
        const LIST                       = 0b0000_0000_0001;
        const TEST_EXIT                  = 0b0000_0000_0010;
        const TEST_BOOT_FAIL             = 0b0000_0000_0100;
        const REDACT_HTTP                = 0b0000_0000_1000;
        const OFFLINE_INSTALL            = 0b0000_0100_0000;
        const IGNORE_LOCAL               = 0b0000_1000_0000;
        const TRIGGER_ELECTION           = 0b0010_0000_0000;
        const STRUCTOPT_CLI              = 0b0100_0000_0000;
        const NO_NAMED_PIPE_HEALTH_CHECK = 0b1000_0000_0000;
        const SERVICE_CONFIG_FILES       = 0b0000_0001_0000;
    }
}

lazy_static! {
    static ref ENV_VARS: HashMap<FeatureFlag, &'static str> = {
        let mapping = vec![(FeatureFlag::LIST, "HAB_FEAT_LIST"),
                           (FeatureFlag::TEST_EXIT, "HAB_FEAT_TEST_EXIT"),
                           (FeatureFlag::TEST_BOOT_FAIL, "HAB_FEAT_BOOT_FAIL"),
                           (FeatureFlag::REDACT_HTTP, "HAB_FEAT_REDACT_HTTP"),
                           (FeatureFlag::OFFLINE_INSTALL, "HAB_FEAT_OFFLINE_INSTALL"),
                           (FeatureFlag::IGNORE_LOCAL, "HAB_FEAT_IGNORE_LOCAL"),
                           (FeatureFlag::TRIGGER_ELECTION, "HAB_FEAT_TRIGGER_ELECTION"),
                           (FeatureFlag::STRUCTOPT_CLI, "HAB_FEAT_STRUCTOPT_CLI"),
                           (FeatureFlag::NO_NAMED_PIPE_HEALTH_CHECK,
                            "HAB_FEAT_NO_NAMED_PIPE_HEALTH_CHECK"),
                           (FeatureFlag::SERVICE_CONFIG_FILES, "HAB_FEAT_SERVICE_CONFIG_FILES"),];

        HashMap::from_iter(mapping)
    };
    pub static ref FEATURE_FLAGS: FeatureFlag = FeatureFlag::from_env(&mut NullUi::new());
}

impl FeatureFlag {
    /// If the environment variable for a flag is set to _anything_ but
    /// the empty string, it is activated.
    pub fn from_env<T>(ui: &mut T) -> Self
        where T: UIWriter
    {
        let mut flags = FeatureFlag::empty();

        for (feature, env_var) in ENV_VARS.iter() {
            if let Some(val) = env::var_os(env_var) {
                if !val.is_empty() {
                    flags.insert(*feature);
                    ui.warn(&format!("Enabling feature: {:?}", feature))
                      .unwrap();
                }
            }
        }

        // TODO (CM): Once the other TODOs above are done (especially the
        // documentation bits), it would be nice to extract this logic
        // into an actual discoverable CLI subcommand; it's a little weird
        // that you have to know how to enable a feature flag before you
        // can even find out that there *are* feature flags to enable.
        //
        // There's no reason why "list feature flags" should itself be a
        // feature-flag.
        if flags.contains(FeatureFlag::LIST) {
            ui.warn("Listing feature flags environment variables:")
              .unwrap();
            for (feature, env_var) in ENV_VARS.iter() {
                ui.warn(&format!("  * {:?}: {}={:?}",
                                 feature,
                                 env_var,
                                 env::var_os(env_var).unwrap_or_default()))
                  .unwrap();
            }
        }

        flags
    }
}

pub mod sync {
    use std::time::Duration;

    #[cfg(feature = "deadlock_detection")]
    mod deadlock_detection {
        use super::*;
        use parking_lot::deadlock;
        use std::{sync::Once,
                  thread};

        static INIT: Once = Once::new();

        pub fn init() { INIT.call_once(spawn_deadlock_detector_thread); }

        fn spawn_deadlock_detector_thread() {
            thread::spawn(move || {
                loop {
                    thread::sleep(Duration::from_secs(10));
                    let deadlocks = deadlock::check_deadlock();
                    if deadlocks.is_empty() {
                        continue;
                    }

                    println!("{} deadlocks detected", deadlocks.len());
                    for (i, threads) in deadlocks.iter().enumerate() {
                        println!("Deadlock #{}", i);
                        for t in threads {
                            println!("Thread Id {:#?}", t.thread_id());
                            println!("{:#?}", t.backtrace());
                        }
                    }

                    // Unfortunately, we can't do anything to resolve the deadlock and
                    // continue, so we have to abort the whole process
                    std::process::exit(1);
                }
            });
        }
    }

    #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
    type InnerLock<T> = parking_lot::RwLock<T>;
    #[cfg(feature = "lock_as_mutex")]
    type InnerLock<T> = parking_lot::Mutex<T>;

    #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
    pub type ReadGuard<'a, T> = parking_lot::RwLockReadGuard<'a, T>;
    #[cfg(feature = "lock_as_mutex")]
    pub type ReadGuard<'a, T> = parking_lot::MutexGuard<'a, T>;

    #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
    pub type WriteGuard<'a, T> = parking_lot::RwLockWriteGuard<'a, T>;
    #[cfg(feature = "lock_as_mutex")]
    pub type WriteGuard<'a, T> = parking_lot::MutexGuard<'a, T>;

    /// A lock which provides the interface of a read/write lock, but which has the option to
    /// internally use either a RwLock or a Mutex in order to make it easier to expose erroneous
    /// recursive locking in tests while still using an RwLock in production to avoid deadlocking
    /// as much as possible.
    #[derive(Debug)]
    pub struct Lock<T> {
        inner: InnerLock<T>,
    }

    impl<T: Default> Default for Lock<T> {
        fn default() -> Self { Self { inner: InnerLock::new(T::default()), } }
    }

    impl<T> Lock<T> {
        pub fn new(val: T) -> Self {
            #[cfg(feature = "lock_as_mutex")]
            println!("Lock::new is using Mutex to help find recursive locking");

            #[cfg(feature = "deadlock_detection")]
            deadlock_detection::init();

            Self { inner: InnerLock::new(val), }
        }

        /// This acquires a read lock and will not deadlock if the same thread tries to acquire
        /// the lock recursively. However, it may result in writer starvation. Once we are confident
        /// that all recursive locking has been eliminated, we may replace this implementation
        /// and try_read_for (or add an additional methods) to provide fair locking for readers
        /// and writers.
        ///
        /// See https://github.com/habitat-sh/habitat/issues/6435
        pub fn read(&self) -> ReadGuard<T> {
            #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
            {
                self.inner.read_recursive()
            }
            #[cfg(feature = "lock_as_mutex")]
            {
                self.inner.lock()
            }
        }

        pub fn try_read_for(&self, timeout: Duration) -> Option<ReadGuard<T>> {
            #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
            {
                self.inner.try_read_recursive_for(timeout)
            }
            #[cfg(feature = "lock_as_mutex")]
            {
                self.inner.try_lock_for(timeout)
            }
        }

        pub fn write(&self) -> WriteGuard<T> {
            #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
            {
                self.inner.write()
            }
            #[cfg(feature = "lock_as_mutex")]
            {
                self.inner.lock()
            }
        }
    }
}
<commit_msg>Fix ordering of feature flags<commit_after>use crate::ui::{NullUi,
                UIWriter};
use habitat_api_client as api_client;
use habitat_core as hcore;
use lazy_static::lazy_static;
use std::{collections::HashMap,
          env,
          ffi::OsStr,
          iter::FromIterator};

extern crate json;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[cfg_attr(test, macro_use)]
extern crate serde_json;
#[cfg(windows)]
extern crate winapi;

pub use self::error::{Error,
                      Result};

pub mod cli;
pub mod command;
pub mod error;
pub mod liveliness_checker;
pub mod output;
pub mod owning_refs;
pub mod package_graph;
pub mod templating;
pub mod types;
pub mod ui;
pub mod util;

lazy_static::lazy_static! {
    pub static ref PROGRAM_NAME: String = {
        match env::current_exe() {
            Ok(path) => path.file_stem().and_then(OsStr::to_str).unwrap().to_string(),
            Err(e) => {
                error!("Error getting path of current_exe: {}", e);
                String::from("hab-?")
            }
        }
    };
}

// TODO (CM): It would be nice to come up with a way to more
// programmatically manage these flags. It's a bit of a pain to define
// the flag, and then define the environment variables
// separately. Nothing statically guarantees that you've specified an
// variable for a flag.

// TODO (CM): It'd be great to have a built-in way to document them,
// too.

// TODO (CM): Part of that documentation might be *when* a flag was
// added. In general, long-lived flags are a code-smell.

// TODO (CM): It may also be useful to break out features by area of
// concern. We can have any number of bitflags-generated structs.

bitflags::bitflags! {
    /// All the feature flags that are recogized by Habitat.
    ///
    /// In general, feature flags are enabled by setting the corresponding
    /// environment variable.
    ///
    /// Your binary should call `FeatureFlag::from_env` to get a set
    /// of flags to use.
    ///
    /// To add a new feature flag, you will need to add the bit mask
    /// constant here, as well as a mapping from the feature to the
    /// environment variable to which it corresponds in the `ENV_VARS`
    /// map below.
    pub struct FeatureFlag: u32 {
        const LIST                       = 0b0000_0000_0001;
        const TEST_EXIT                  = 0b0000_0000_0010;
        const TEST_BOOT_FAIL             = 0b0000_0000_0100;
        const REDACT_HTTP                = 0b0000_0000_1000;
        const SERVICE_CONFIG_FILES       = 0b0000_0001_0000;
        const OFFLINE_INSTALL            = 0b0000_0100_0000;
        const IGNORE_LOCAL               = 0b0000_1000_0000;
        const TRIGGER_ELECTION           = 0b0010_0000_0000;
        const STRUCTOPT_CLI              = 0b0100_0000_0000;
        const NO_NAMED_PIPE_HEALTH_CHECK = 0b1000_0000_0000;
    }
}

lazy_static! {
    static ref ENV_VARS: HashMap<FeatureFlag, &'static str> = {
        let mapping = vec![(FeatureFlag::LIST, "HAB_FEAT_LIST"),
                           (FeatureFlag::TEST_EXIT, "HAB_FEAT_TEST_EXIT"),
                           (FeatureFlag::TEST_BOOT_FAIL, "HAB_FEAT_BOOT_FAIL"),
                           (FeatureFlag::REDACT_HTTP, "HAB_FEAT_REDACT_HTTP"),
                           (FeatureFlag::OFFLINE_INSTALL, "HAB_FEAT_OFFLINE_INSTALL"),
                           (FeatureFlag::IGNORE_LOCAL, "HAB_FEAT_IGNORE_LOCAL"),
                           (FeatureFlag::TRIGGER_ELECTION, "HAB_FEAT_TRIGGER_ELECTION"),
                           (FeatureFlag::STRUCTOPT_CLI, "HAB_FEAT_STRUCTOPT_CLI"),
                           (FeatureFlag::NO_NAMED_PIPE_HEALTH_CHECK,
                            "HAB_FEAT_NO_NAMED_PIPE_HEALTH_CHECK"),
                           (FeatureFlag::SERVICE_CONFIG_FILES, "HAB_FEAT_SERVICE_CONFIG_FILES"),];

        HashMap::from_iter(mapping)
    };
    pub static ref FEATURE_FLAGS: FeatureFlag = FeatureFlag::from_env(&mut NullUi::new());
}

impl FeatureFlag {
    /// If the environment variable for a flag is set to _anything_ but
    /// the empty string, it is activated.
    pub fn from_env<T>(ui: &mut T) -> Self
        where T: UIWriter
    {
        let mut flags = FeatureFlag::empty();

        for (feature, env_var) in ENV_VARS.iter() {
            if let Some(val) = env::var_os(env_var) {
                if !val.is_empty() {
                    flags.insert(*feature);
                    ui.warn(&format!("Enabling feature: {:?}", feature))
                      .unwrap();
                }
            }
        }

        // TODO (CM): Once the other TODOs above are done (especially the
        // documentation bits), it would be nice to extract this logic
        // into an actual discoverable CLI subcommand; it's a little weird
        // that you have to know how to enable a feature flag before you
        // can even find out that there *are* feature flags to enable.
        //
        // There's no reason why "list feature flags" should itself be a
        // feature-flag.
        if flags.contains(FeatureFlag::LIST) {
            ui.warn("Listing feature flags environment variables:")
              .unwrap();
            for (feature, env_var) in ENV_VARS.iter() {
                ui.warn(&format!("  * {:?}: {}={:?}",
                                 feature,
                                 env_var,
                                 env::var_os(env_var).unwrap_or_default()))
                  .unwrap();
            }
        }

        flags
    }
}

pub mod sync {
    use std::time::Duration;

    #[cfg(feature = "deadlock_detection")]
    mod deadlock_detection {
        use super::*;
        use parking_lot::deadlock;
        use std::{sync::Once,
                  thread};

        static INIT: Once = Once::new();

        pub fn init() { INIT.call_once(spawn_deadlock_detector_thread); }

        fn spawn_deadlock_detector_thread() {
            thread::spawn(move || {
                loop {
                    thread::sleep(Duration::from_secs(10));
                    let deadlocks = deadlock::check_deadlock();
                    if deadlocks.is_empty() {
                        continue;
                    }

                    println!("{} deadlocks detected", deadlocks.len());
                    for (i, threads) in deadlocks.iter().enumerate() {
                        println!("Deadlock #{}", i);
                        for t in threads {
                            println!("Thread Id {:#?}", t.thread_id());
                            println!("{:#?}", t.backtrace());
                        }
                    }

                    // Unfortunately, we can't do anything to resolve the deadlock and
                    // continue, so we have to abort the whole process
                    std::process::exit(1);
                }
            });
        }
    }

    #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
    type InnerLock<T> = parking_lot::RwLock<T>;
    #[cfg(feature = "lock_as_mutex")]
    type InnerLock<T> = parking_lot::Mutex<T>;

    #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
    pub type ReadGuard<'a, T> = parking_lot::RwLockReadGuard<'a, T>;
    #[cfg(feature = "lock_as_mutex")]
    pub type ReadGuard<'a, T> = parking_lot::MutexGuard<'a, T>;

    #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
    pub type WriteGuard<'a, T> = parking_lot::RwLockWriteGuard<'a, T>;
    #[cfg(feature = "lock_as_mutex")]
    pub type WriteGuard<'a, T> = parking_lot::MutexGuard<'a, T>;

    /// A lock which provides the interface of a read/write lock, but which has the option to
    /// internally use either a RwLock or a Mutex in order to make it easier to expose erroneous
    /// recursive locking in tests while still using an RwLock in production to avoid deadlocking
    /// as much as possible.
    #[derive(Debug)]
    pub struct Lock<T> {
        inner: InnerLock<T>,
    }

    impl<T: Default> Default for Lock<T> {
        fn default() -> Self { Self { inner: InnerLock::new(T::default()), } }
    }

    impl<T> Lock<T> {
        pub fn new(val: T) -> Self {
            #[cfg(feature = "lock_as_mutex")]
            println!("Lock::new is using Mutex to help find recursive locking");

            #[cfg(feature = "deadlock_detection")]
            deadlock_detection::init();

            Self { inner: InnerLock::new(val), }
        }

        /// This acquires a read lock and will not deadlock if the same thread tries to acquire
        /// the lock recursively. However, it may result in writer starvation. Once we are confident
        /// that all recursive locking has been eliminated, we may replace this implementation
        /// and try_read_for (or add an additional methods) to provide fair locking for readers
        /// and writers.
        ///
        /// See https://github.com/habitat-sh/habitat/issues/6435
        pub fn read(&self) -> ReadGuard<T> {
            #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
            {
                self.inner.read_recursive()
            }
            #[cfg(feature = "lock_as_mutex")]
            {
                self.inner.lock()
            }
        }

        pub fn try_read_for(&self, timeout: Duration) -> Option<ReadGuard<T>> {
            #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
            {
                self.inner.try_read_recursive_for(timeout)
            }
            #[cfg(feature = "lock_as_mutex")]
            {
                self.inner.try_lock_for(timeout)
            }
        }

        pub fn write(&self) -> WriteGuard<T> {
            #[cfg(any(feature = "lock_as_rwlock", not(feature = "lock_as_mutex")))]
            {
                self.inner.write()
            }
            #[cfg(feature = "lock_as_mutex")]
            {
                self.inner.lock()
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters, experimental::errors::Error, metrics_safety_rules::MetricsSafetyRules,
    network::NetworkSender, network_interface::ConsensusMsg, round_manager::VerifiedEvent,
    state_replication::StateComputer,
};
use channel::{Receiver, Sender};
use consensus_types::{
    common::Author,
    executed_block::ExecutedBlock,
    experimental::{commit_decision::CommitDecision, commit_vote::CommitVote},
};
use core::sync::atomic::Ordering;
use diem_crypto::ed25519::Ed25519Signature;
use diem_infallible::Mutex;
use diem_logger::prelude::*;
use diem_metrics::monitor;
use diem_types::{
    account_address::AccountAddress,
    block_info::BlockInfo,
    ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
    validator_verifier::{ValidatorVerifier, VerifyError},
};
use executor_types::Error as ExecutionError;
use futures::{select, FutureExt, SinkExt, StreamExt};
use safety_rules::TSafetyRules;
use std::{
    collections::BTreeMap,
    sync::{atomic::AtomicU64, Arc},
};
use tokio::time;

use crate::{
    experimental::execution_phase::{reset_ack_new, ResetAck},
    state_replication::StateComputerCommitCallBackType,
};
use diem_logger::error;
use futures::channel::oneshot;

/*
Commit phase takes in the executed blocks from the execution
phase and commit them. Specifically, commit phase signs a commit
vote message containing the execution result and broadcast it.
Upon collecting a quorum of agreeing votes to a execution result,
the commit phase commits the blocks as well as broadcasts a commit
decision message together with the quorum of signatures. The commit
decision message helps the slower nodes to quickly catch up without
having to collect the signatures.
*/

const COMMIT_PHASE_TIMEOUT_SEC: u64 = 1; // retry timeout in seconds

pub struct CommitChannelType(
    pub Vec<ExecutedBlock>,
    pub LedgerInfoWithSignatures,
    pub StateComputerCommitCallBackType,
);

impl std::fmt::Debug for CommitChannelType {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}

impl std::fmt::Display for CommitChannelType {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "CommitChannelType({:?}, {})", self.0, self.1)
    }
}

//#[derive(Clone)]
pub struct PendingBlocks {
    blocks: Vec<ExecutedBlock>,
    ledger_info_sig: LedgerInfoWithSignatures,
    block_info: BlockInfo,
    callback: StateComputerCommitCallBackType,
}

impl PendingBlocks {
    pub fn new(
        blocks: Vec<ExecutedBlock>,
        ledger_info_sig: LedgerInfoWithSignatures,
        callback: StateComputerCommitCallBackType,
    ) -> Self {
        assert!(!blocks.is_empty()); // the commit phase should not accept empty blocks.
        let block_info = blocks.last().unwrap().block_info();
        Self {
            blocks,
            ledger_info_sig,
            block_info,
            callback,
        }
    }

    pub fn block_info(&self) -> &BlockInfo {
        &self.block_info
    }

    pub fn round(&self) -> u64 {
        self.block_info().round()
    }

    pub fn take_callback(self) -> StateComputerCommitCallBackType {
        self.callback
    }

    pub fn blocks(&self) -> &Vec<ExecutedBlock> {
        &self.blocks
    }

    pub fn ledger_info_sig(&self) -> &LedgerInfoWithSignatures {
        &self.ledger_info_sig
    }

    pub fn ledger_info_sig_mut(&mut self) -> &mut LedgerInfoWithSignatures {
        &mut self.ledger_info_sig
    }

    pub fn replace_ledger_info_sig(&mut self, new_ledger_info_sig: LedgerInfoWithSignatures) {
        self.ledger_info_sig = new_ledger_info_sig
    }

    pub fn verify(&self, verifier: &ValidatorVerifier) -> ::std::result::Result<(), VerifyError> {
        if &self.block_info == self.ledger_info_sig.ledger_info().commit_info() {
            self.ledger_info_sig.verify_signatures(verifier)
        } else {
            Err(VerifyError::InconsistentBlockInfo)
        }
    }
}

pub struct CommitPhase {
    commit_channel_recv: Receiver<CommitChannelType>,
    execution_proxy: Arc<dyn StateComputer>,
    blocks: Option<PendingBlocks>,
    commit_msg_rx: channel::Receiver<VerifiedEvent>,
    verifier: ValidatorVerifier,
    safety_rules: Arc<Mutex<MetricsSafetyRules>>,
    author: Author,
    back_pressure: Arc<AtomicU64>,
    network_sender: NetworkSender,
    timeout_event_tx: Sender<CommitVote>,
    timeout_event_rx: Receiver<CommitVote>,
    reset_event_rx: Receiver<oneshot::Sender<ResetAck>>,
}

/// Wrapper for ExecutionProxy.commit
pub async fn commit(
    execution_proxy: &Arc<dyn StateComputer>,
    pending_blocks: PendingBlocks,
) -> Result<(), ExecutionError> {
    let blocks_to_commit = pending_blocks
        .blocks()
        .iter()
        .map(|eb| Arc::new(eb.clone()))
        .collect::<Vec<Arc<ExecutedBlock>>>();
    execution_proxy
        .commit(
            &blocks_to_commit,
            pending_blocks.ledger_info_sig().clone(),
            pending_blocks.take_callback(),
        )
        .await
        .expect("Failed to persist commit");

    Ok(())
}

macro_rules! report_err {
    ($result:expr, $error_string:literal) => {
        if let Err(err) = $result {
            counters::ERROR_COUNT.inc();
            error!(error = err.to_string(), $error_string,)
        }
    };
}

/// shortcut for sendng a message with a timeout retry event
async fn broadcast_commit_vote_with_retry(
    mut network_sender: NetworkSender,
    cv: CommitVote,
    mut notification: Sender<CommitVote>,
) {
    network_sender
        .broadcast(ConsensusMsg::CommitVoteMsg(Box::new(cv.clone())))
        .await;
    time::sleep(time::Duration::from_secs(COMMIT_PHASE_TIMEOUT_SEC)).await;
    report_err!(
        notification.send(cv).await,
        "Error in sending timeout events"
    )
}

impl CommitPhase {
    pub fn new(
        commit_channel_recv: Receiver<CommitChannelType>,
        execution_proxy: Arc<dyn StateComputer>,
        commit_msg_rx: channel::Receiver<VerifiedEvent>,
        verifier: ValidatorVerifier,
        safety_rules: Arc<Mutex<MetricsSafetyRules>>,
        author: Author,
        back_pressure: Arc<AtomicU64>,
        network_sender: NetworkSender,
        reset_event_rx: Receiver<oneshot::Sender<ResetAck>>,
    ) -> Self {
        let (timeout_event_tx, timeout_event_rx) = channel::new::<CommitVote>(
            2,
            &counters::DECOUPLED_EXECUTION__COMMIT_MESSAGE_TIMEOUT_CHANNEL,
        );
        Self {
            commit_channel_recv,
            execution_proxy,
            blocks: None,
            commit_msg_rx,
            verifier,
            safety_rules,
            author,
            back_pressure,
            network_sender,
            timeout_event_tx,
            timeout_event_rx,
            reset_event_rx,
        }
    }

    /// Notified when receiving a commit vote message (assuming verified)
    pub async fn process_commit_vote(
        &mut self,
        commit_vote: &CommitVote,
    ) -> anyhow::Result<(), Error> {
        if let Some(pending_blocks) = self.blocks.as_mut() {
            let commit_ledger_info = commit_vote.ledger_info();

            // if the block infos do not match
            if commit_ledger_info.commit_info() != pending_blocks.block_info() {
                return Err(Error::InconsistentBlockInfo(
                    commit_ledger_info.commit_info().clone(),
                    pending_blocks.block_info().clone(),
                )); // ignore the message
            }

            // add the signature into the signature tree
            pending_blocks
                .ledger_info_sig_mut()
                .add_signature(commit_vote.author(), commit_vote.signature().clone());
        } else {
            info!("Ignore the commit vote message because the commit phase does not have a pending block.")
        }

        Ok(())
    }

    /// Notified when receiving a commit decision message (assuming verified)
    pub async fn process_commit_decision(
        &mut self,
        commit_decision: &CommitDecision,
    ) -> anyhow::Result<(), Error> {
        if let Some(pending_blocks) = self.blocks.as_mut() {
            let commit_ledger_info = commit_decision.ledger_info();

            // if the block infos do not match
            if commit_ledger_info.commit_info() != pending_blocks.block_info() {
                return Err(Error::InconsistentBlockInfo(
                    commit_ledger_info.ledger_info().commit_info().clone(),
                    pending_blocks.block_info().clone(),
                )); // ignore the message
            }

            // replace the signature tree
            pending_blocks.replace_ledger_info_sig(commit_ledger_info.clone());
        } else {
            info!("Ignore the commit decision message because the commit phase does not have a pending block.")
        }

        Ok(())
    }

    pub async fn check_commit(&mut self) -> anyhow::Result<()> {
        if let Some(pending_blocks) = self.blocks.as_ref() {
            if pending_blocks.verify(&self.verifier).is_ok() {
                // asynchronously broadcast the commit decision first to
                // save the time of other nodes.
                self.network_sender
                    .broadcast(ConsensusMsg::CommitDecisionMsg(Box::new(
                        CommitDecision::new(pending_blocks.ledger_info_sig().clone()),
                    )))
                    .await;

                let pending_blocks = self.blocks.take().unwrap();
                let round = pending_blocks.round();

                commit(&self.execution_proxy, pending_blocks)
                    .await
                    .expect("Failed to commit the executed blocks.");

                // update the back pressure
                self.back_pressure.store(round, Ordering::SeqCst);

                // now self.blocks is none, ready for the next batch of blocks
            }
        }

        Ok(())
    }

    pub async fn process_executed_blocks(
        &mut self,
        blocks: Vec<ExecutedBlock>,
        ordered_ledger_info: LedgerInfoWithSignatures,
        callback: StateComputerCommitCallBackType,
    ) -> anyhow::Result<()> {
        // TODO: recover from the safety_rules error

        let commit_ledger_info = LedgerInfo::new(
            blocks.last().unwrap().block_info(),
            ordered_ledger_info.ledger_info().consensus_data_hash(),
        );

        let signature = self
            .safety_rules
            .lock()
            .sign_commit_vote(ordered_ledger_info, commit_ledger_info.clone())?;

        let commit_vote =
            CommitVote::new_with_signature(self.author, commit_ledger_info.clone(), signature);

        let commit_ledger_info_with_sig = LedgerInfoWithSignatures::new(
            commit_ledger_info,
            BTreeMap::<AccountAddress, Ed25519Signature>::new(),
        );

        // we need to wait for the commit vote itself to collect the signature.
        //commit_ledger_info_with_sig.add_signature(self.author, signature);
        self.set_blocks(Some(PendingBlocks::new(
            blocks,
            commit_ledger_info_with_sig,
            callback,
        )));

        // asynchronously broadcast the message.
        // note that this message will also reach the node itself
        // if the message delivery fails, it needs to resend the message, or otherwise the liveness might compromise.
        tokio::spawn(broadcast_commit_vote_with_retry(
            self.network_sender.clone(),
            commit_vote,
            self.timeout_event_tx.clone(),
        ));

        Ok(())
    }

    pub fn set_blocks(&mut self, blocks_or_none: Option<PendingBlocks>) {
        self.blocks = blocks_or_none;
    }

    pub fn blocks(&self) -> &Option<PendingBlocks> {
        &self.blocks
    }

    pub fn load_back_pressure(&self) -> u64 {
        self.back_pressure.load(Ordering::SeqCst)
    }

    pub async fn process_reset_event(
        &mut self,
        reset_event_callback: oneshot::Sender<ResetAck>,
    ) -> anyhow::Result<()> {
        // reset the commit phase

        // exhaust the commit channel
        // we do not have to exhaust the commit message channel because inconsistent messages will be ignored
        while self.commit_channel_recv.next().now_or_never().is_some() {}

        // reset local block
        self.blocks = None;

        // activate the callback
        reset_event_callback
            .send(reset_ack_new())
            .map_err(|_| Error::ResetDropped)?;

        Ok(())
    }

    pub async fn start(mut self) {
        loop {
            while self.blocks.is_some() {
                // if we are still collecting the signatures
                select! {
                    // process messages dispatched from epoch_manager
                    msg = self.commit_msg_rx.select_next_some() => {
                        match msg {
                            VerifiedEvent::CommitVote(cv) => {
                                monitor!(
                                    "process_commit_vote",
                                    report_err!(self.process_commit_vote(&*cv).await, "Error in processing commit vote.")
                                );
                            }
                            VerifiedEvent::CommitDecision(cd) => {
                                monitor!(
                                    "process_commit_decision",
                                    report_err!(self.process_commit_decision(&*cd).await, "Error in processing commit decision.")
                                );
                            }
                            _ => {
                                unreachable!("Unexpected messages: something wrong with message dispatching.")
                            }
                        };
                    }
                    retry_cv = self.timeout_event_rx.select_next_some() => {
                        if let Some(ref pending_blocks) = self.blocks {
                            if pending_blocks.block_info() == retry_cv.commit_info() {
                                // retry broadcasting the message if the blocks are still pending
                                tokio::spawn(broadcast_commit_vote_with_retry(self.network_sender.clone(), retry_cv, self.timeout_event_tx.clone()));
                            }
                        }
                    }
                    // callback event might come when self.blocks is not empty
                    reset_event_callback = self.reset_event_rx.select_next_some() => {
                        self.process_reset_event(reset_event_callback).await.map_err(|e| ExecutionError::InternalError {
                            error: e.to_string(),
                        })
                        .unwrap();
                    }
                    complete => break,
                }
                report_err!(
                    // check if the blocks are ready to commit
                    self.check_commit().await,
                    "Error in checking whether self.block is ready to commit."
                );
            }

            select! {
                CommitChannelType(blocks, ordered_ledger_info, callback) = self.commit_channel_recv.select_next_some() => {
                    report_err!(
                        // receive new blocks from execution phase
                        self.process_executed_blocks(blocks, ordered_ledger_info, callback)
                            .await,
                        "Error in processing received blocks"
                    );
                }
                // callback event might come when self.blocks is none
                reset_event_callback = self.reset_event_rx.select_next_some() => {
                    self.process_reset_event(reset_event_callback).await.map_err(|e| ExecutionError::InternalError {
                        error: e.to_string(),
                    })
                    .unwrap();
                }
                complete => break,
            }
        }
    }
}
<commit_msg>[decoupled-execution] use tokio::select with preconditions instead of futures::select<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters, experimental::errors::Error, metrics_safety_rules::MetricsSafetyRules,
    network::NetworkSender, network_interface::ConsensusMsg, round_manager::VerifiedEvent,
    state_replication::StateComputer,
};
use channel::{Receiver, Sender};
use consensus_types::{
    common::Author,
    executed_block::ExecutedBlock,
    experimental::{commit_decision::CommitDecision, commit_vote::CommitVote},
};
use core::sync::atomic::Ordering;
use diem_crypto::ed25519::Ed25519Signature;
use diem_infallible::Mutex;
use diem_logger::prelude::*;
use diem_metrics::monitor;
use diem_types::{
    account_address::AccountAddress,
    block_info::BlockInfo,
    ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
    validator_verifier::{ValidatorVerifier, VerifyError},
};
use executor_types::Error as ExecutionError;
use futures::{FutureExt, SinkExt, StreamExt};
use safety_rules::TSafetyRules;
use std::{
    collections::BTreeMap,
    sync::{atomic::AtomicU64, Arc},
};
use tokio::time;

use crate::{
    experimental::execution_phase::{reset_ack_new, ResetAck},
    state_replication::StateComputerCommitCallBackType,
};
use diem_logger::error;
use futures::{channel::oneshot, prelude::stream::FusedStream};

/*
Commit phase takes in the executed blocks from the execution
phase and commit them. Specifically, commit phase signs a commit
vote message containing the execution result and broadcast it.
Upon collecting a quorum of agreeing votes to a execution result,
the commit phase commits the blocks as well as broadcasts a commit
decision message together with the quorum of signatures. The commit
decision message helps the slower nodes to quickly catch up without
having to collect the signatures.
*/

const COMMIT_PHASE_TIMEOUT_SEC: u64 = 1; // retry timeout in seconds

pub struct CommitChannelType(
    pub Vec<ExecutedBlock>,
    pub LedgerInfoWithSignatures,
    pub StateComputerCommitCallBackType,
);

impl std::fmt::Debug for CommitChannelType {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}

impl std::fmt::Display for CommitChannelType {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "CommitChannelType({:?}, {})", self.0, self.1)
    }
}

//#[derive(Clone)]
pub struct PendingBlocks {
    blocks: Vec<ExecutedBlock>,
    ledger_info_sig: LedgerInfoWithSignatures,
    block_info: BlockInfo,
    callback: StateComputerCommitCallBackType,
}

impl PendingBlocks {
    pub fn new(
        blocks: Vec<ExecutedBlock>,
        ledger_info_sig: LedgerInfoWithSignatures,
        callback: StateComputerCommitCallBackType,
    ) -> Self {
        assert!(!blocks.is_empty()); // the commit phase should not accept empty blocks.
        let block_info = blocks.last().unwrap().block_info();
        Self {
            blocks,
            ledger_info_sig,
            block_info,
            callback,
        }
    }

    pub fn block_info(&self) -> &BlockInfo {
        &self.block_info
    }

    pub fn round(&self) -> u64 {
        self.block_info().round()
    }

    pub fn take_callback(self) -> StateComputerCommitCallBackType {
        self.callback
    }

    pub fn blocks(&self) -> &Vec<ExecutedBlock> {
        &self.blocks
    }

    pub fn ledger_info_sig(&self) -> &LedgerInfoWithSignatures {
        &self.ledger_info_sig
    }

    pub fn ledger_info_sig_mut(&mut self) -> &mut LedgerInfoWithSignatures {
        &mut self.ledger_info_sig
    }

    pub fn replace_ledger_info_sig(&mut self, new_ledger_info_sig: LedgerInfoWithSignatures) {
        self.ledger_info_sig = new_ledger_info_sig
    }

    pub fn verify(&self, verifier: &ValidatorVerifier) -> ::std::result::Result<(), VerifyError> {
        if &self.block_info == self.ledger_info_sig.ledger_info().commit_info() {
            self.ledger_info_sig.verify_signatures(verifier)
        } else {
            Err(VerifyError::InconsistentBlockInfo)
        }
    }
}

pub struct CommitPhase {
    commit_channel_recv: Receiver<CommitChannelType>,
    execution_proxy: Arc<dyn StateComputer>,
    blocks: Option<PendingBlocks>,
    commit_msg_rx: channel::Receiver<VerifiedEvent>,
    verifier: ValidatorVerifier,
    safety_rules: Arc<Mutex<MetricsSafetyRules>>,
    author: Author,
    back_pressure: Arc<AtomicU64>,
    network_sender: NetworkSender,
    timeout_event_tx: Sender<CommitVote>,
    timeout_event_rx: Receiver<CommitVote>,
    reset_event_rx: Receiver<oneshot::Sender<ResetAck>>,
}

/// Wrapper for ExecutionProxy.commit
pub async fn commit(
    execution_proxy: &Arc<dyn StateComputer>,
    pending_blocks: PendingBlocks,
) -> Result<(), ExecutionError> {
    let blocks_to_commit = pending_blocks
        .blocks()
        .iter()
        .map(|eb| Arc::new(eb.clone()))
        .collect::<Vec<Arc<ExecutedBlock>>>();
    execution_proxy
        .commit(
            &blocks_to_commit,
            pending_blocks.ledger_info_sig().clone(),
            pending_blocks.take_callback(),
        )
        .await
        .expect("Failed to persist commit");

    Ok(())
}

macro_rules! report_err {
    ($result:expr, $error_string:literal) => {
        if let Err(err) = $result {
            counters::ERROR_COUNT.inc();
            error!(error = err.to_string(), $error_string,)
        }
    };
}

/// shortcut for sendng a message with a timeout retry event
async fn broadcast_commit_vote_with_retry(
    mut network_sender: NetworkSender,
    cv: CommitVote,
    mut notification: Sender<CommitVote>,
) {
    network_sender
        .broadcast(ConsensusMsg::CommitVoteMsg(Box::new(cv.clone())))
        .await;
    time::sleep(time::Duration::from_secs(COMMIT_PHASE_TIMEOUT_SEC)).await;
    report_err!(
        notification.send(cv).await,
        "Error in sending timeout events"
    )
}

impl CommitPhase {
    pub fn new(
        commit_channel_recv: Receiver<CommitChannelType>,
        execution_proxy: Arc<dyn StateComputer>,
        commit_msg_rx: channel::Receiver<VerifiedEvent>,
        verifier: ValidatorVerifier,
        safety_rules: Arc<Mutex<MetricsSafetyRules>>,
        author: Author,
        back_pressure: Arc<AtomicU64>,
        network_sender: NetworkSender,
        reset_event_rx: Receiver<oneshot::Sender<ResetAck>>,
    ) -> Self {
        let (timeout_event_tx, timeout_event_rx) = channel::new::<CommitVote>(
            2,
            &counters::DECOUPLED_EXECUTION__COMMIT_MESSAGE_TIMEOUT_CHANNEL,
        );
        Self {
            commit_channel_recv,
            execution_proxy,
            blocks: None,
            commit_msg_rx,
            verifier,
            safety_rules,
            author,
            back_pressure,
            network_sender,
            timeout_event_tx,
            timeout_event_rx,
            reset_event_rx,
        }
    }

    /// Notified when receiving a commit vote message (assuming verified)
    pub async fn process_commit_vote(
        &mut self,
        commit_vote: &CommitVote,
    ) -> anyhow::Result<(), Error> {
        if let Some(pending_blocks) = self.blocks.as_mut() {
            let commit_ledger_info = commit_vote.ledger_info();

            // if the block infos do not match
            if commit_ledger_info.commit_info() != pending_blocks.block_info() {
                return Err(Error::InconsistentBlockInfo(
                    commit_ledger_info.commit_info().clone(),
                    pending_blocks.block_info().clone(),
                )); // ignore the message
            }

            // add the signature into the signature tree
            pending_blocks
                .ledger_info_sig_mut()
                .add_signature(commit_vote.author(), commit_vote.signature().clone());
        } else {
            info!("Ignore the commit vote message because the commit phase does not have a pending block.")
        }

        Ok(())
    }

    /// Notified when receiving a commit decision message (assuming verified)
    pub async fn process_commit_decision(
        &mut self,
        commit_decision: &CommitDecision,
    ) -> anyhow::Result<(), Error> {
        if let Some(pending_blocks) = self.blocks.as_mut() {
            let commit_ledger_info = commit_decision.ledger_info();

            // if the block infos do not match
            if commit_ledger_info.commit_info() != pending_blocks.block_info() {
                return Err(Error::InconsistentBlockInfo(
                    commit_ledger_info.ledger_info().commit_info().clone(),
                    pending_blocks.block_info().clone(),
                )); // ignore the message
            }

            // replace the signature tree
            pending_blocks.replace_ledger_info_sig(commit_ledger_info.clone());
        } else {
            info!("Ignore the commit decision message because the commit phase does not have a pending block.")
        }

        Ok(())
    }

    pub async fn check_commit(&mut self) -> anyhow::Result<()> {
        if let Some(pending_blocks) = self.blocks.as_ref() {
            if pending_blocks.verify(&self.verifier).is_ok() {
                // asynchronously broadcast the commit decision first to
                // save the time of other nodes.
                self.network_sender
                    .broadcast(ConsensusMsg::CommitDecisionMsg(Box::new(
                        CommitDecision::new(pending_blocks.ledger_info_sig().clone()),
                    )))
                    .await;

                let pending_blocks = self.blocks.take().unwrap();
                let round = pending_blocks.round();

                commit(&self.execution_proxy, pending_blocks)
                    .await
                    .expect("Failed to commit the executed blocks.");

                // update the back pressure
                self.back_pressure.store(round, Ordering::SeqCst);

                // now self.blocks is none, ready for the next batch of blocks
            }
        }

        Ok(())
    }

    pub async fn process_executed_blocks(
        &mut self,
        blocks: Vec<ExecutedBlock>,
        ordered_ledger_info: LedgerInfoWithSignatures,
        callback: StateComputerCommitCallBackType,
    ) -> anyhow::Result<()> {
        // TODO: recover from the safety_rules error

        let commit_ledger_info = LedgerInfo::new(
            blocks.last().unwrap().block_info(),
            ordered_ledger_info.ledger_info().consensus_data_hash(),
        );

        let signature = self
            .safety_rules
            .lock()
            .sign_commit_vote(ordered_ledger_info, commit_ledger_info.clone())?;

        let commit_vote =
            CommitVote::new_with_signature(self.author, commit_ledger_info.clone(), signature);

        let commit_ledger_info_with_sig = LedgerInfoWithSignatures::new(
            commit_ledger_info,
            BTreeMap::<AccountAddress, Ed25519Signature>::new(),
        );

        // we need to wait for the commit vote itself to collect the signature.
        //commit_ledger_info_with_sig.add_signature(self.author, signature);
        self.set_blocks(Some(PendingBlocks::new(
            blocks,
            commit_ledger_info_with_sig,
            callback,
        )));

        // asynchronously broadcast the message.
        // note that this message will also reach the node itself
        // if the message delivery fails, it needs to resend the message, or otherwise the liveness might compromise.
        tokio::spawn(broadcast_commit_vote_with_retry(
            self.network_sender.clone(),
            commit_vote,
            self.timeout_event_tx.clone(),
        ));

        Ok(())
    }

    pub fn set_blocks(&mut self, blocks_or_none: Option<PendingBlocks>) {
        self.blocks = blocks_or_none;
    }

    pub fn blocks(&self) -> &Option<PendingBlocks> {
        &self.blocks
    }

    pub fn load_back_pressure(&self) -> u64 {
        self.back_pressure.load(Ordering::SeqCst)
    }

    pub async fn process_reset_event(
        &mut self,
        reset_event_callback: oneshot::Sender<ResetAck>,
    ) -> anyhow::Result<()> {
        // reset the commit phase

        // exhaust the commit channel
        // we do not have to exhaust the commit message channel because inconsistent messages will be ignored
        while self.commit_channel_recv.next().now_or_never().is_some() {}

        // reset local block
        self.blocks = None;

        // activate the callback
        reset_event_callback
            .send(reset_ack_new())
            .map_err(|_| Error::ResetDropped)?;

        Ok(())
    }

    pub async fn start(mut self) {
        loop {
            // if we are still collecting the signatures
            tokio::select! {
                // process messages dispatched from epoch_manager
                msg = self.commit_msg_rx.select_next_some(), if self.blocks.is_some() => {
                        match msg {
                            VerifiedEvent::CommitVote(cv) => {
                                monitor!(
                                    "process_commit_vote",
                                    report_err!(self.process_commit_vote(&*cv).await, "Error in processing commit vote.")
                                );
                            }
                            VerifiedEvent::CommitDecision(cd) => {
                                monitor!(
                                    "process_commit_decision",
                                    report_err!(self.process_commit_decision(&*cd).await, "Error in processing commit decision.")
                                );
                            }
                            _ => {
                                unreachable!("Unexpected messages: something wrong with message dispatching.")
                            }
                        };
                        report_err!(
                            // check if the blocks are ready to commit
                            self.check_commit().await,
                            "Error in checking whether self.block is ready to commit."
                        );
                }
                retry_cv = self.timeout_event_rx.select_next_some(), if self.blocks.is_some() && !self.commit_msg_rx.is_terminated()  => {
                    let pending_blocks = self.blocks.as_ref().unwrap();
                    if pending_blocks.block_info() == retry_cv.commit_info() {
                        // retry broadcasting the message if the blocks are still pending
                        tokio::spawn(broadcast_commit_vote_with_retry(self.network_sender.clone(), retry_cv, self.timeout_event_tx.clone()));
                    }
                }
                // callback event might come when self.blocks is not empty
                reset_event_callback = self.reset_event_rx.select_next_some() => {
                        self.process_reset_event(reset_event_callback).await.map_err(|e| ExecutionError::InternalError {
                            error: e.to_string(),
                        })
                        .unwrap();
                }
                CommitChannelType(blocks, ordered_ledger_info, callback) = self.commit_channel_recv.select_next_some(),
                    if self.blocks.is_none() => {
                        report_err!(
                            // receive new blocks from execution phase
                            self.process_executed_blocks(blocks, ordered_ledger_info, callback)
                                .await,
                            "Error in processing received blocks"
                        );
                }
                else => break,
            }
        }
    }
}
<|endoftext|>