<commit_before>use super::super::{Binary, DirectoryStack, Shell, Variables};
use super::super::completer::*;
use liner::{BasicCompleter, CursorPosition, Event, EventKind};
use smallstring::SmallString;
use std::env;
use std::io::{self, ErrorKind, Write};
use std::mem;
use std::path::PathBuf;
use sys;
use types::*;

pub(crate) fn readln(shell: &mut Shell) -> Option<String> {
    {
        let vars_ptr = &shell.variables as *const Variables;
        let dirs_ptr = &shell.directory_stack as *const DirectoryStack;

        // Collects the current list of values from history for completion.
        let history = &shell.context.as_ref().unwrap().history.buffers.iter()
                // Map each underlying `liner::Buffer` into a `String`.
                .map(|x| x.chars().cloned().collect())
                // Collect each result into a vector to avoid borrowing issues.
                .collect::<Vec<SmallString>>();

        loop {
            let prompt = shell.prompt();
            let funcs = &shell.functions;
            let vars = &shell.variables;
            let builtins = &shell.builtins;

            let line = shell.context.as_mut().unwrap().read_line(
                prompt,
                &mut move |Event { editor, kind }| {
                    if let EventKind::BeforeComplete = kind {
                        let (words, pos) = editor.get_words_and_cursor_position();

                        let filename = match pos {
                            CursorPosition::InWord(index) => index > 0,
                            CursorPosition::InSpace(Some(_), _) => true,
                            CursorPosition::InSpace(None, _) => false,
                            CursorPosition::OnWordLeftEdge(index) => index >= 1,
                            CursorPosition::OnWordRightEdge(index) => {
                                match (words.into_iter().nth(index), env::current_dir()) {
                                    (Some((start, end)), Ok(file)) => {
                                        let filename = editor.current_buffer().range(start, end);
                                        complete_as_file(file, filename, index)
                                    }
                                    _ => false,
                                }
                            }
                        };

                        if filename {
                            if let Ok(current_dir) = env::current_dir() {
                                if let Some(url) = current_dir.to_str() {
                                    let completer =
                                        IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr);
                                    mem::replace(
                                        &mut editor.context().completer,
                                        Some(Box::new(completer)),
                                    );
                                }
                            }
                        } else {
                            // Creates a list of definitions from the shell environment that
                            // will be used
                            // in the creation of a custom completer.
                            let words = builtins.keys().iter()
                                // Add built-in commands to the completer's definitions.
                                .map(|&s| Identifier::from(s))
                                // Add the history list to the completer's definitions.
                                .chain(history.iter().cloned())
                                // Add the aliases to the completer's definitions.
                                .chain(vars.aliases.keys().cloned())
                                // Add the list of available functions to the completer's definitions.
                                .chain(funcs.keys().cloned())
                                // Add the list of available variables to the completer's definitions.
                                // TODO: We should make it free to do String->SmallString
                                //       and mostly free to go back (free if allocated)
                                .chain(vars.get_vars().map(|s| ["$", &s].concat().into()))
                                .collect();

                            // Initialize a new completer from the definitions collected.
                            let custom_completer = BasicCompleter::new(words);

                            // Creates completers containing definitions from all directories
                            // listed
                            // in the environment's **$PATH** variable.
                            let mut file_completers = if let Ok(val) = env::var("PATH") {
                                val.split(sys::PATH_SEPARATOR)
                                    .map(|s| IonFileCompleter::new(Some(s), dirs_ptr, vars_ptr))
                                    .collect()
                            } else {
                                vec![IonFileCompleter::new(Some("/bin/"), dirs_ptr, vars_ptr)]
                            };

                            // Also add files/directories in the current directory to the
                            // completion list.
                            if let Ok(current_dir) = env::current_dir() {
                                if let Some(url) = current_dir.to_str() {
                                    file_completers
                                        .push(IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr));
                                }
                            }

                            // Merge the collected definitions with the file path definitions.
                            let completer = MultiCompleter::new(file_completers, custom_completer);

                            // Replace the shell's current completer with the newly-created
                            // completer.
                            mem::replace(
                                &mut editor.context().completer,
                                Some(Box::new(completer)),
                            );
                        }
                    }
                },
            );

            match line {
                Ok(line) => return Some(line),
                // Handles Ctrl + C
                Err(ref err) if err.kind() == ErrorKind::Interrupted => return None,
                // Handles Ctrl + D
                Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => break,
                Err(err) => {
                    let stderr = io::stderr();
                    let mut stderr = stderr.lock();
                    let _ = writeln!(stderr, "ion: liner: {}", err);
                    return None;
                }
            }
        }
    }

    let previous_status = shell.previous_status;
    shell.exit(previous_status);
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: PathBuf, filename: String, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as a file
    if filename.starts_with(".") {
        return true;
    }
    // If the file starts with a dollar sign, it's a variable, not a file
    if filename.starts_with("$") {
        return false;
    }
    // Once we are beyond the first string, assume its a file
    if index > 0 {
        return true;
    }
    // If we are referencing a file that exists then just complete to that file
    if file.exists() {
        return true;
    }
    // If we have a partial file inside an existing directory, e.g. /foo/b when /foo/bar
    // exists, then treat it as file as long as `foo` isn't the current directory, otherwise
    // this would apply to any string `foo`
    if let Some(parent) = file.parent() {
        return parent.exists() && parent != current_dir;
    }
    // By default assume its not a file
    false
}
<commit_msg>prompt now displays newlines correctly<commit_after>use super::super::{Binary, DirectoryStack, Shell, Variables};
use super::super::completer::*;
use liner::{BasicCompleter, CursorPosition, Event, EventKind};
use smallstring::SmallString;
use std::env;
use std::io::{self, ErrorKind, Write};
use std::mem;
use std::path::PathBuf;
use sys;
use types::*;

pub(crate) fn readln(shell: &mut Shell) -> Option<String> {
    {
        let vars_ptr = &shell.variables as *const Variables;
        let dirs_ptr = &shell.directory_stack as *const DirectoryStack;

        // Collects the current list of values from history for completion.
        let history = &shell.context.as_ref().unwrap().history.buffers.iter()
                // Map each underlying `liner::Buffer` into a `String`.
                .map(|x| x.chars().cloned().collect())
                // Collect each result into a vector to avoid borrowing issues.
                .collect::<Vec<SmallString>>();

        loop {
            let prompt = handle_prompt(shell.prompt()).unwrap();
            let funcs = &shell.functions;
            let vars = &shell.variables;
            let builtins = &shell.builtins;

            let line = shell.context.as_mut().unwrap().read_line(
                prompt,
                &mut move |Event { editor, kind }| {
                    if let EventKind::BeforeComplete = kind {
                        let (words, pos) = editor.get_words_and_cursor_position();

                        let filename = match pos {
                            CursorPosition::InWord(index) => index > 0,
                            CursorPosition::InSpace(Some(_), _) => true,
                            CursorPosition::InSpace(None, _) => false,
                            CursorPosition::OnWordLeftEdge(index) => index >= 1,
                            CursorPosition::OnWordRightEdge(index) => {
                                match (words.into_iter().nth(index), env::current_dir()) {
                                    (Some((start, end)), Ok(file)) => {
                                        let filename = editor.current_buffer().range(start, end);
                                        complete_as_file(file, filename, index)
                                    }
                                    _ => false,
                                }
                            }
                        };

                        if filename {
                            if let Ok(current_dir) = env::current_dir() {
                                if let Some(url) = current_dir.to_str() {
                                    let completer =
                                        IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr);
                                    mem::replace(
                                        &mut editor.context().completer,
                                        Some(Box::new(completer)),
                                    );
                                }
                            }
                        } else {
                            // Creates a list of definitions from the shell environment that
                            // will be used
                            // in the creation of a custom completer.
                            let words = builtins.keys().iter()
                                // Add built-in commands to the completer's definitions.
                                .map(|&s| Identifier::from(s))
                                // Add the history list to the completer's definitions.
                                .chain(history.iter().cloned())
                                // Add the aliases to the completer's definitions.
                                .chain(vars.aliases.keys().cloned())
                                // Add the list of available functions to the completer's definitions.
                                .chain(funcs.keys().cloned())
                                // Add the list of available variables to the completer's definitions.
                                // TODO: We should make it free to do String->SmallString
                                //       and mostly free to go back (free if allocated)
                                .chain(vars.get_vars().map(|s| ["$", &s].concat().into()))
                                .collect();

                            // Initialize a new completer from the definitions collected.
                            let custom_completer = BasicCompleter::new(words);

                            // Creates completers containing definitions from all directories
                            // listed
                            // in the environment's **$PATH** variable.
                            let mut file_completers = if let Ok(val) = env::var("PATH") {
                                val.split(sys::PATH_SEPARATOR)
                                    .map(|s| IonFileCompleter::new(Some(s), dirs_ptr, vars_ptr))
                                    .collect()
                            } else {
                                vec![IonFileCompleter::new(Some("/bin/"), dirs_ptr, vars_ptr)]
                            };

                            // Also add files/directories in the current directory to the
                            // completion list.
                            if let Ok(current_dir) = env::current_dir() {
                                if let Some(url) = current_dir.to_str() {
                                    file_completers
                                        .push(IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr));
                                }
                            }

                            // Merge the collected definitions with the file path definitions.
                            let completer = MultiCompleter::new(file_completers, custom_completer);

                            // Replace the shell's current completer with the newly-created
                            // completer.
                            mem::replace(
                                &mut editor.context().completer,
                                Some(Box::new(completer)),
                            );
                        }
                    }
                },
            );

            match line {
                Ok(line) => return Some(line),
                // Handles Ctrl + C
                Err(ref err) if err.kind() == ErrorKind::Interrupted => return None,
                // Handles Ctrl + D
                Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => break,
                Err(err) => {
                    let stderr = io::stderr();
                    let mut stderr = stderr.lock();
                    let _ = writeln!(stderr, "ion: liner: {}", err);
                    return None;
                }
            }
        }
    }

    let previous_status = shell.previous_status;
    shell.exit(previous_status);
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: PathBuf, filename: String, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as a file
    if filename.starts_with(".") {
        return true;
    }
    // If the file starts with a dollar sign, it's a variable, not a file
    if filename.starts_with("$") {
        return false;
    }
    // Once we are beyond the first string, assume its a file
    if index > 0 {
        return true;
    }
    // If we are referencing a file that exists then just complete to that file
    if file.exists() {
        return true;
    }
    // If we have a partial file inside an existing directory, e.g. /foo/b when /foo/bar
    // exists, then treat it as file as long as `foo` isn't the current directory, otherwise
    // this would apply to any string `foo`
    if let Some(parent) = file.parent() {
        return parent.exists() && parent != current_dir;
    }
    // By default assume its not a file
    false
}

/// takes the input shell,
/// prints prompt info lines, and
/// returns the last prompt line.
fn handle_prompt(full_prompt: String) -> Result<String, String> {
    if !full_prompt.contains("\n") {
        return Ok(full_prompt)
    }

    let stdout = io::stdout();
    let mut handle = stdout.lock();
    if let Err(why) =
        handle.write(full_prompt.as_bytes()) {
        return Err(format!("unable to print prompt info: {}", why));
    }

    match full_prompt.lines().last() {
        Some(prompt) => Ok(String::from(prompt)),
        None => Err(format!("unable to get prompt"))
    }
}
<|endoftext|>


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

use util::nodemap::{FxHashMap, FxHashSet};
use ty::context::TyCtxt;
use ty::{AdtDef, VariantDef, FieldDef, Ty, TyS};
use ty::{DefId, Substs};
use ty::{AdtKind, Visibility};
use ty::TyKind::*;

pub use self::def_id_forest::DefIdForest;

mod def_id_forest;

// The methods in this module calculate DefIdForests of modules in which a
// AdtDef/VariantDef/FieldDef is visibly uninhabited.
//
// # Example
// ```rust
// enum Void {}
// mod a {
//     pub mod b {
//         pub struct SecretlyUninhabited {
//             _priv: !,
//         }
//     }
// }
//
// mod c {
//     pub struct AlsoSecretlyUninhabited {
//         _priv: Void,
//     }
//     mod d {
//     }
// }
//
// struct Foo {
//     x: a::b::SecretlyUninhabited,
//     y: c::AlsoSecretlyUninhabited,
// }
// ```
// In this code, the type Foo will only be visibly uninhabited inside the
// modules b, c and d. Calling uninhabited_from on Foo or its AdtDef will
// return the forest of modules {b, c->d} (represented in a DefIdForest by the
// set {b, c})
//
// We need this information for pattern-matching on Foo or types that contain
// Foo.
//
// # Example
// ```rust
// let foo_result: Result<T, Foo> = ... ;
// let Ok(t) = foo_result;
// ```
// This code should only compile in modules where the uninhabitedness of Foo is
// visible.

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
    /// Checks whether a type is visibly uninhabited from a particular module.
    /// # Example
    /// ```rust
    /// enum Void {}
    /// mod a {
    ///     pub mod b {
    ///         pub struct SecretlyUninhabited {
    ///             _priv: !,
    ///         }
    ///     }
    /// }
    ///
    /// mod c {
    ///     pub struct AlsoSecretlyUninhabited {
    ///         _priv: Void,
    ///     }
    ///     mod d {
    ///     }
    /// }
    ///
    /// struct Foo {
    ///     x: a::b::SecretlyUninhabited,
    ///     y: c::AlsoSecretlyUninhabited,
    /// }
    /// ```
    /// In this code, the type `Foo` will only be visibly uninhabited inside the
    /// modules b, c and d. This effects pattern-matching on `Foo` or types that
    /// contain `Foo`.
    ///
    /// # Example
    /// ```rust
    /// let foo_result: Result<T, Foo> = ... ;
    /// let Ok(t) = foo_result;
    /// ```
    /// This code should only compile in modules where the uninhabitedness of Foo is
    /// visible.
    pub fn is_ty_uninhabited_from(self, module: DefId, ty: Ty<'tcx>) -> bool {
        // To check whether this type is uninhabited at all (not just from the
        // given node) you could check whether the forest is empty.
        // ```
        // forest.is_empty()
        // ```
        self.ty_inhabitedness_forest(ty).contains(self, module)
    }

    pub fn is_ty_uninhabited_from_all_modules(self, ty: Ty<'tcx>) -> bool {
        !self.ty_inhabitedness_forest(ty).is_empty()
    }

    fn ty_inhabitedness_forest(self, ty: Ty<'tcx>) -> DefIdForest {
        ty.uninhabited_from(&mut FxHashMap::default(), self)
    }

    pub fn is_enum_variant_uninhabited_from(self,
                                            module: DefId,
                                            variant: &'tcx VariantDef,
                                            substs: &'tcx Substs<'tcx>)
                                            -> bool
    {
        self.variant_inhabitedness_forest(variant, substs).contains(self, module)
    }

    pub fn is_variant_uninhabited_from_all_modules(self,
                                                   variant: &'tcx VariantDef,
                                                   substs: &'tcx Substs<'tcx>)
                                                   -> bool
    {
        !self.variant_inhabitedness_forest(variant, substs).is_empty()
    }

    fn variant_inhabitedness_forest(self, variant: &'tcx VariantDef, substs: &'tcx Substs<'tcx>)
                                    -> DefIdForest {
        // Determine the ADT kind:
        let adt_def_id = self.adt_def_id_of_variant(variant);
        let adt_kind = self.adt_def(adt_def_id).adt_kind();

        // Compute inhabitedness forest:
        variant.uninhabited_from(&mut FxHashMap::default(), self, substs, adt_kind)
    }
}

impl<'a, 'gcx, 'tcx> AdtDef {
    /// Calculate the forest of DefIds from which this adt is visibly uninhabited.
    fn uninhabited_from(
        &self,
        visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        substs: &'tcx Substs<'tcx>) -> DefIdForest
    {
        DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
            v.uninhabited_from(visited, tcx, substs, self.adt_kind())
        }))
    }
}

impl<'a, 'gcx, 'tcx> VariantDef {
    /// Calculate the forest of DefIds from which this variant is visibly uninhabited.
    fn uninhabited_from(
        &self,
        visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        substs: &'tcx Substs<'tcx>,
        adt_kind: AdtKind) -> DefIdForest
    {
        let is_enum = match adt_kind {
            // For now, `union`s are never considered uninhabited.
            // The precise semantics of inhabitedness with respect to unions is currently undecided.
            AdtKind::Union => return DefIdForest::empty(),
            AdtKind::Enum => true,
            AdtKind::Struct => false,
        };
        DefIdForest::union(tcx, self.fields.iter().map(|f| {
            f.uninhabited_from(visited, tcx, substs, is_enum)
        }))
    }
}

impl<'a, 'gcx, 'tcx> FieldDef {
    /// Calculate the forest of DefIds from which this field is visibly uninhabited.
    fn uninhabited_from(
        &self,
        visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        substs: &'tcx Substs<'tcx>,
        is_enum: bool,
    ) -> DefIdForest {
        let mut data_uninhabitedness = move || {
            self.ty(tcx, substs).uninhabited_from(visited, tcx)
        };
        // FIXME(canndrew): Currently enum fields are (incorrectly) stored with
        // Visibility::Invisible so we need to override self.vis if we're
        // dealing with an enum.
        if is_enum {
            data_uninhabitedness()
        } else {
            match self.vis {
                Visibility::Invisible => DefIdForest::empty(),
                Visibility::Restricted(from) => {
                    let forest = DefIdForest::from_id(from);
                    let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness()));
                    DefIdForest::intersection(tcx, iter)
                },
                Visibility::Public => data_uninhabitedness(),
            }
        }
    }
}

impl<'a, 'gcx, 'tcx> TyS<'tcx> {
    /// Calculate the forest of DefIds from which this type is visibly uninhabited.
    fn uninhabited_from(
        &self,
        visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
        tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
    {
        match self.sty {
            Adt(def, substs) => {
                {
                    let substs_set = visited.entry(def.did).or_default();
                    if !substs_set.insert(substs) {
                        // We are already calculating the inhabitedness of this type.
                        // The type must contain a reference to itself. Break the
                        // infinite loop.
                        return DefIdForest::empty();
                    }
                    if substs_set.len() >= tcx.sess.recursion_limit.get() / 4 {
                        // We have gone very deep, reinstantiating this ADT inside
                        // itself with different type arguments. We are probably
                        // hitting an infinite loop. For example, it's possible to write:
                        //                a type Foo<T>
                        //      which contains a Foo<(T, T)>
                        //      which contains a Foo<((T, T), (T, T))>
                        //      which contains a Foo<(((T, T), (T, T)), ((T, T), (T, T)))>
                        //      etc.
                        let error = format!("reached recursion limit while checking \
                                             inhabitedness of `{}`", self);
                        tcx.sess.fatal(&error);
                    }
                }
                let ret = def.uninhabited_from(visited, tcx, substs);
                let substs_set = visited.get_mut(&def.did).unwrap();
                substs_set.remove(substs);
                ret
            }

            Never => DefIdForest::full(tcx),

            Tuple(ref tys) => {
                DefIdForest::union(tcx, tys.iter().map(|ty| {
                    ty.uninhabited_from(visited, tcx)
                }))
            }

            Array(ty, len) => {
                match len.assert_usize(tcx) {
                    // If the array is definitely non-empty, it's uninhabited if
                    // the type of its elements is uninhabited.
                    Some(n) if n != 0 => ty.uninhabited_from(visited, tcx),
                    _ => DefIdForest::empty()
                }
            }

            // References to uninitialised memory is valid for any type, including
            // uninhabited types, in unsafe code, so we treat all references as
            // inhabited.
            // The precise semantics of inhabitedness with respect to references is currently
            // undecided.
            Ref(..) => DefIdForest::empty(),

            _ => DefIdForest::empty(),
        }
    }
}

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

use ty::context::TyCtxt;
use ty::{AdtDef, VariantDef, FieldDef, Ty, TyS};
use ty::{DefId, Substs};
use ty::{AdtKind, Visibility};
use ty::TyKind::*;

pub use self::def_id_forest::DefIdForest;

mod def_id_forest;

// The methods in this module calculate DefIdForests of modules in which a
// AdtDef/VariantDef/FieldDef is visibly uninhabited.
//
// # Example
// ```rust
// enum Void {}
// mod a {
//     pub mod b {
//         pub struct SecretlyUninhabited {
//             _priv: !,
//         }
//     }
// }
//
// mod c {
//     pub struct AlsoSecretlyUninhabited {
//         _priv: Void,
//     }
//     mod d {
//     }
// }
//
// struct Foo {
//     x: a::b::SecretlyUninhabited,
//     y: c::AlsoSecretlyUninhabited,
// }
// ```
// In this code, the type Foo will only be visibly uninhabited inside the
// modules b, c and d. Calling uninhabited_from on Foo or its AdtDef will
// return the forest of modules {b, c->d} (represented in a DefIdForest by the
// set {b, c})
//
// We need this information for pattern-matching on Foo or types that contain
// Foo.
//
// # Example
// ```rust
// let foo_result: Result<T, Foo> = ... ;
// let Ok(t) = foo_result;
// ```
// This code should only compile in modules where the uninhabitedness of Foo is
// visible.

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
    /// Checks whether a type is visibly uninhabited from a particular module.
    /// # Example
    /// ```rust
    /// enum Void {}
    /// mod a {
    ///     pub mod b {
    ///         pub struct SecretlyUninhabited {
    ///             _priv: !,
    ///         }
    ///     }
    /// }
    ///
    /// mod c {
    ///     pub struct AlsoSecretlyUninhabited {
    ///         _priv: Void,
    ///     }
    ///     mod d {
    ///     }
    /// }
    ///
    /// struct Foo {
    ///     x: a::b::SecretlyUninhabited,
    ///     y: c::AlsoSecretlyUninhabited,
    /// }
    /// ```
    /// In this code, the type `Foo` will only be visibly uninhabited inside the
    /// modules b, c and d. This effects pattern-matching on `Foo` or types that
    /// contain `Foo`.
    ///
    /// # Example
    /// ```rust
    /// let foo_result: Result<T, Foo> = ... ;
    /// let Ok(t) = foo_result;
    /// ```
    /// This code should only compile in modules where the uninhabitedness of Foo is
    /// visible.
    pub fn is_ty_uninhabited_from(self, module: DefId, ty: Ty<'tcx>) -> bool {
        // To check whether this type is uninhabited at all (not just from the
        // given node) you could check whether the forest is empty.
        // ```
        // forest.is_empty()
        // ```
        self.ty_inhabitedness_forest(ty).contains(self, module)
    }

    pub fn is_ty_uninhabited_from_all_modules(self, ty: Ty<'tcx>) -> bool {
        !self.ty_inhabitedness_forest(ty).is_empty()
    }

    fn ty_inhabitedness_forest(self, ty: Ty<'tcx>) -> DefIdForest {
        ty.uninhabited_from(self)
    }

    pub fn is_enum_variant_uninhabited_from(self,
                                            module: DefId,
                                            variant: &'tcx VariantDef,
                                            substs: &'tcx Substs<'tcx>)
                                            -> bool
    {
        self.variant_inhabitedness_forest(variant, substs).contains(self, module)
    }

    pub fn is_variant_uninhabited_from_all_modules(self,
                                                   variant: &'tcx VariantDef,
                                                   substs: &'tcx Substs<'tcx>)
                                                   -> bool
    {
        !self.variant_inhabitedness_forest(variant, substs).is_empty()
    }

    fn variant_inhabitedness_forest(self, variant: &'tcx VariantDef, substs: &'tcx Substs<'tcx>)
                                    -> DefIdForest {
        // Determine the ADT kind:
        let adt_def_id = self.adt_def_id_of_variant(variant);
        let adt_kind = self.adt_def(adt_def_id).adt_kind();

        // Compute inhabitedness forest:
        variant.uninhabited_from(self, substs, adt_kind)
    }
}

impl<'a, 'gcx, 'tcx> AdtDef {
    /// Calculate the forest of DefIds from which this adt is visibly uninhabited.
    fn uninhabited_from(
        &self,
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        substs: &'tcx Substs<'tcx>) -> DefIdForest
    {
        DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
            v.uninhabited_from(tcx, substs, self.adt_kind())
        }))
    }
}

impl<'a, 'gcx, 'tcx> VariantDef {
    /// Calculate the forest of DefIds from which this variant is visibly uninhabited.
    fn uninhabited_from(
        &self,
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        substs: &'tcx Substs<'tcx>,
        adt_kind: AdtKind) -> DefIdForest
    {
        let is_enum = match adt_kind {
            // For now, `union`s are never considered uninhabited.
            // The precise semantics of inhabitedness with respect to unions is currently undecided.
            AdtKind::Union => return DefIdForest::empty(),
            AdtKind::Enum => true,
            AdtKind::Struct => false,
        };
        DefIdForest::union(tcx, self.fields.iter().map(|f| {
            f.uninhabited_from(tcx, substs, is_enum)
        }))
    }
}

impl<'a, 'gcx, 'tcx> FieldDef {
    /// Calculate the forest of DefIds from which this field is visibly uninhabited.
    fn uninhabited_from(
        &self,
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        substs: &'tcx Substs<'tcx>,
        is_enum: bool,
    ) -> DefIdForest {
        let data_uninhabitedness = move || {
            self.ty(tcx, substs).uninhabited_from(tcx)
        };
        // FIXME(canndrew): Currently enum fields are (incorrectly) stored with
        // Visibility::Invisible so we need to override self.vis if we're
        // dealing with an enum.
        if is_enum {
            data_uninhabitedness()
        } else {
            match self.vis {
                Visibility::Invisible => DefIdForest::empty(),
                Visibility::Restricted(from) => {
                    let forest = DefIdForest::from_id(from);
                    let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness()));
                    DefIdForest::intersection(tcx, iter)
                },
                Visibility::Public => data_uninhabitedness(),
            }
        }
    }
}

impl<'a, 'gcx, 'tcx> TyS<'tcx> {
    /// Calculate the forest of DefIds from which this type is visibly uninhabited.
    fn uninhabited_from(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
    {
        match self.sty {
            Adt(def, substs) => def.uninhabited_from(tcx, substs),

            Never => DefIdForest::full(tcx),

            Tuple(ref tys) => {
                DefIdForest::union(tcx, tys.iter().map(|ty| {
                    ty.uninhabited_from(tcx)
                }))
            }

            Array(ty, len) => {
                match len.assert_usize(tcx) {
                    // If the array is definitely non-empty, it's uninhabited if
                    // the type of its elements is uninhabited.
                    Some(n) if n != 0 => ty.uninhabited_from(tcx),
                    _ => DefIdForest::empty()
                }
            }

            // References to uninitialised memory is valid for any type, including
            // uninhabited types, in unsafe code, so we treat all references as
            // inhabited.
            // The precise semantics of inhabitedness with respect to references is currently
            // undecided.
            Ref(..) => DefIdForest::empty(),

            _ => DefIdForest::empty(),
        }
    }
}

<|endoftext|>


<commit_before>use rustc::ty::{self, Ty, Instance, TypeFoldable};
use rustc::ty::layout::{Size, Align, LayoutOf, HasDataLayout};
use rustc::mir::interpret::{Scalar, Pointer, InterpResult, PointerArithmetic,};

use super::{InterpCx, Machine, MemoryKind, FnVal};

impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
    /// objects.
    ///
    /// The `trait_ref` encodes the erased self type. Hence if we are
    /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
    /// `trait_ref` would map `T:Trait`.
    pub fn get_vtable(
        &mut self,
        ty: Ty<'tcx>,
        poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
        trace!("get_vtable(trait_ref={:?})", poly_trait_ref);

        let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref));

        // All vtables must be monomorphic, bail out otherwise.
        if ty.needs_subst() || poly_trait_ref.needs_subst() {
            throw_inval!(TooGeneric);
        }

        if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) {
            // This means we guarantee that there are no duplicate vtables, we will
            // always use the same vtable for the same (Type, Trait) combination.
            // That's not what happens in rustc, but emulating per-crate deduplication
            // does not sound like it actually makes anything any better.
            return Ok(vtable);
        }

        let methods = if let Some(poly_trait_ref) = poly_trait_ref {
            let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
            let trait_ref = self.tcx.erase_regions(&trait_ref);

            self.tcx.vtable_methods(trait_ref)
        } else {
            &[]
        };

        let layout = self.layout_of(ty)?;
        assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
        let size = layout.size.bytes();
        let align = layout.align.abi.bytes();

        let ptr_size = self.pointer_size();
        let ptr_align = self.tcx.data_layout.pointer_align.abi;
        // /////////////////////////////////////////////////////////////////////////////////////////
        // If you touch this code, be sure to also make the corresponding changes to
        // `get_vtable` in rust_codegen_llvm/meth.rs
        // /////////////////////////////////////////////////////////////////////////////////////////
        let vtable = self.memory.allocate(
            ptr_size * (3 + methods.len() as u64),
            ptr_align,
            MemoryKind::Vtable,
        );
        let tcx = &*self.tcx;

        let drop = Instance::resolve_drop_in_place(*tcx, ty);
        let drop = self.memory.create_fn_alloc(FnVal::Instance(drop));

        // No need to do any alignment checks on the memory accesses below, because we know the
        // allocation is correctly aligned as we created it above. Also we're only offsetting by
        // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
        let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
        vtable_alloc.write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?;

        let size_ptr = vtable.offset(ptr_size, tcx)?;
        vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
        let align_ptr = vtable.offset(ptr_size * 2, tcx)?;
        vtable_alloc.write_ptr_sized(tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?;

        for (i, method) in methods.iter().enumerate() {
            if let Some((def_id, substs)) = *method {
                // resolve for vtable: insert shims where needed
                let instance = ty::Instance::resolve_for_vtable(
                    *tcx,
                    self.param_env,
                    def_id,
                    substs,
                ).ok_or_else(|| err_inval!(TooGeneric))?;
                let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                // We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
                let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
                self.memory.get_raw_mut(vtable.alloc_id)?
                    .write_ptr_sized(tcx, method_ptr, Scalar::Ptr(fn_ptr).into())?;
            }
        }

        self.memory.mark_immutable(vtable.alloc_id)?;
        assert!(self.vtables.insert((ty, poly_trait_ref), vtable).is_none());

        Ok(vtable)
    }

    /// Resolve the function at the specified slot in the provided
    /// vtable. An index of '0' corresponds to the first method
    /// declared in the trait of the provided vtable
    pub fn get_vtable_slot(
        &self,
        vtable: Scalar<M::PointerTag>,
        idx: usize
    ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
        let ptr_size = self.pointer_size();
        // Skip over the 'drop_ptr', 'size', and 'align' fields
        let vtable_slot = vtable.ptr_offset(ptr_size * (idx as u64 + 3), self)?;
        let vtable_slot = self.memory.check_ptr_access(
            vtable_slot,
            ptr_size,
            self.tcx.data_layout.pointer_align.abi,
        )?.expect("cannot be a ZST");
        let fn_ptr = self.memory.get(vtable_slot.alloc_id)?
            .read_ptr_sized(self, vtable_slot)?.not_undef()?;
        Ok(self.memory.get_fn(fn_ptr)?)
    }

    /// Returns the drop fn instance as well as the actual dynamic type
    pub fn read_drop_type_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
        // we don't care about the pointee type, we just want a pointer
        let vtable = self.memory.check_ptr_access(
            vtable,
            self.tcx.data_layout.pointer_size,
            self.tcx.data_layout.pointer_align.abi,
        )?.expect("cannot be a ZST");
        let drop_fn = self.memory
            .get_raw(vtable.alloc_id)?
            .read_ptr_sized(self, vtable)?
            .not_undef()?;
        // We *need* an instance here, no other kind of function value, to be able
        // to determine the type.
        let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?;
        trace!("Found drop fn: {:?}", drop_instance);
        let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx);
        let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
        // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
        let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty;
        Ok((drop_instance, ty))
    }

    pub fn read_size_and_align_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (Size, Align)> {
        let pointer_size = self.pointer_size();
        // We check for size = 3*ptr_size, that covers the drop fn (unused here),
        // the size, and the align (which we read below).
        let vtable = self.memory.check_ptr_access(
            vtable,
            3*pointer_size,
            self.tcx.data_layout.pointer_align.abi,
        )?.expect("cannot be a ZST");
        let alloc = self.memory.get_raw(vtable.alloc_id)?;
        let size = alloc.read_ptr_sized(
            self,
            vtable.offset(pointer_size, self)?
        )?.not_undef()?;
        let size = self.force_bits(size, pointer_size)? as u64;
        let align = alloc.read_ptr_sized(
            self,
            vtable.offset(pointer_size * 2, self)?,
        )?.not_undef()?;
        let align = self.force_bits(align, pointer_size)? as u64;

        if size >= self.tcx.data_layout().obj_size_bound() {
            throw_ub_format!("invalid vtable: \
                size is bigger than largest supported object");
        }
        Ok((Size::from_bytes(size), Align::from_bytes(align).unwrap()))
    }
}
<commit_msg>Update<commit_after>use rustc::ty::{self, Ty, Instance, TypeFoldable};
use rustc::ty::layout::{Size, Align, LayoutOf, HasDataLayout};
use rustc::mir::interpret::{Scalar, Pointer, InterpResult, PointerArithmetic,};

use super::{InterpCx, Machine, MemoryKind, FnVal};

impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
    /// objects.
    ///
    /// The `trait_ref` encodes the erased self type. Hence if we are
    /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
    /// `trait_ref` would map `T:Trait`.
    pub fn get_vtable(
        &mut self,
        ty: Ty<'tcx>,
        poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
        trace!("get_vtable(trait_ref={:?})", poly_trait_ref);

        let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref));

        // All vtables must be monomorphic, bail out otherwise.
        if ty.needs_subst() || poly_trait_ref.needs_subst() {
            throw_inval!(TooGeneric);
        }

        if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) {
            // This means we guarantee that there are no duplicate vtables, we will
            // always use the same vtable for the same (Type, Trait) combination.
            // That's not what happens in rustc, but emulating per-crate deduplication
            // does not sound like it actually makes anything any better.
            return Ok(vtable);
        }

        let methods = if let Some(poly_trait_ref) = poly_trait_ref {
            let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
            let trait_ref = self.tcx.erase_regions(&trait_ref);

            self.tcx.vtable_methods(trait_ref)
        } else {
            &[]
        };

        let layout = self.layout_of(ty)?;
        assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
        let size = layout.size.bytes();
        let align = layout.align.abi.bytes();

        let ptr_size = self.pointer_size();
        let ptr_align = self.tcx.data_layout.pointer_align.abi;
        // /////////////////////////////////////////////////////////////////////////////////////////
        // If you touch this code, be sure to also make the corresponding changes to
        // `get_vtable` in rust_codegen_llvm/meth.rs
        // /////////////////////////////////////////////////////////////////////////////////////////
        let vtable = self.memory.allocate(
            ptr_size * (3 + methods.len() as u64),
            ptr_align,
            MemoryKind::Vtable,
        );
        let tcx = &*self.tcx;

        let drop = Instance::resolve_drop_in_place(*tcx, ty);
        let drop = self.memory.create_fn_alloc(FnVal::Instance(drop));

        // No need to do any alignment checks on the memory accesses below, because we know the
        // allocation is correctly aligned as we created it above. Also we're only offsetting by
        // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
        let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
        vtable_alloc.write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?;

        let size_ptr = vtable.offset(ptr_size, tcx)?;
        vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
        let align_ptr = vtable.offset(ptr_size * 2, tcx)?;
        vtable_alloc.write_ptr_sized(tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?;

        for (i, method) in methods.iter().enumerate() {
            if let Some((def_id, substs)) = *method {
                // resolve for vtable: insert shims where needed
                let instance = ty::Instance::resolve_for_vtable(
                    *tcx,
                    self.param_env,
                    def_id,
                    substs,
                ).ok_or_else(|| err_inval!(TooGeneric))?;
                let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                // We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
                let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
                self.memory.get_raw_mut(vtable.alloc_id)?
                    .write_ptr_sized(tcx, method_ptr, Scalar::Ptr(fn_ptr).into())?;
            }
        }

        self.memory.mark_immutable(vtable.alloc_id)?;
        assert!(self.vtables.insert((ty, poly_trait_ref), vtable).is_none());

        Ok(vtable)
    }

    /// Resolve the function at the specified slot in the provided
    /// vtable. An index of '0' corresponds to the first method
    /// declared in the trait of the provided vtable
    pub fn get_vtable_slot(
        &self,
        vtable: Scalar<M::PointerTag>,
        idx: usize
    ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
        let ptr_size = self.pointer_size();
        // Skip over the 'drop_ptr', 'size', and 'align' fields
        let vtable_slot = vtable.ptr_offset(ptr_size * (idx as u64 + 3), self)?;
        let vtable_slot = self.memory.check_ptr_access(
            vtable_slot,
            ptr_size,
            self.tcx.data_layout.pointer_align.abi,
        )?.expect("cannot be a ZST");
        let fn_ptr = self.memory.get_raw(vtable_slot.alloc_id)?
            .read_ptr_sized(self, vtable_slot)?.not_undef()?;
        Ok(self.memory.get_fn(fn_ptr)?)
    }

    /// Returns the drop fn instance as well as the actual dynamic type
    pub fn read_drop_type_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
        // we don't care about the pointee type, we just want a pointer
        let vtable = self.memory.check_ptr_access(
            vtable,
            self.tcx.data_layout.pointer_size,
            self.tcx.data_layout.pointer_align.abi,
        )?.expect("cannot be a ZST");
        let drop_fn = self.memory
            .get_raw(vtable.alloc_id)?
            .read_ptr_sized(self, vtable)?
            .not_undef()?;
        // We *need* an instance here, no other kind of function value, to be able
        // to determine the type.
        let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?;
        trace!("Found drop fn: {:?}", drop_instance);
        let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx);
        let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
        // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
        let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty;
        Ok((drop_instance, ty))
    }

    pub fn read_size_and_align_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (Size, Align)> {
        let pointer_size = self.pointer_size();
        // We check for size = 3*ptr_size, that covers the drop fn (unused here),
        // the size, and the align (which we read below).
        let vtable = self.memory.check_ptr_access(
            vtable,
            3*pointer_size,
            self.tcx.data_layout.pointer_align.abi,
        )?.expect("cannot be a ZST");
        let alloc = self.memory.get_raw(vtable.alloc_id)?;
        let size = alloc.read_ptr_sized(
            self,
            vtable.offset(pointer_size, self)?
        )?.not_undef()?;
        let size = self.force_bits(size, pointer_size)? as u64;
        let align = alloc.read_ptr_sized(
            self,
            vtable.offset(pointer_size * 2, self)?,
        )?.not_undef()?;
        let align = self.force_bits(align, pointer_size)? as u64;

        if size >= self.tcx.data_layout().obj_size_bound() {
            throw_ub_format!("invalid vtable: \
                size is bigger than largest supported object");
        }
        Ok((Size::from_bytes(size), Align::from_bytes(align).unwrap()))
    }
}
<|endoftext|>


<commit_before><commit_msg>Removed temp border buffer from my editor, soz<commit_after><|endoftext|>


<commit_before>//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::visit::{Visitor, LvalueContext};
use rustc::mir;
use rustc::traits::Reveal;
use rustc::ty;
use rustc::ty::layout::Layout;
use rustc::ty::subst::{Subst, Substs};

use error::{EvalResult, EvalError};
use eval_context::{EvalContext, StackPopCleanup};
use lvalue::{Global, GlobalId, Lvalue};
use validation::ValidationCtx;
use value::{Value, PrimVal};
use syntax::codemap::Span;
use syntax::ast::Mutability;

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
        self.steps_remaining = self.steps_remaining.saturating_sub(n);
        if self.steps_remaining > 0 {
            Ok(())
        } else {
            Err(EvalError::ExecutionTimeLimitReached)
        }
    }

    /// Returns true as long as there are more things to do.
    pub fn step(&mut self) -> EvalResult<'tcx, bool> {
        self.inc_step_counter_and_check_limit(1)?;
        if self.stack.is_empty() {
            return Ok(false);
        }

        let cur_frame = self.cur_frame();
        self.memory.set_cur_frame(cur_frame);

        let block = self.frame().block;
        let stmt_id = self.frame().stmt;
        let mir = self.mir();
        let basic_block = &mir.basic_blocks()[block];

        if let Some(stmt) = basic_block.statements.get(stmt_id) {
            let mut new = Ok(0);
            ConstantExtractor {
                span: stmt.source_info.span,
                instance: self.frame().instance,
                ecx: self,
                mir,
                new_constants: &mut new,
            }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
            // if ConstantExtractor added new frames, we don't execute anything here
            // but await the next call to step
            if new? == 0 {
                self.statement(stmt)?;
            }
            return Ok(true);
        }

        let terminator = basic_block.terminator();
        let mut new = Ok(0);
        ConstantExtractor {
            span: terminator.source_info.span,
            instance: self.frame().instance,
            ecx: self,
            mir,
            new_constants: &mut new,
        }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
        // if ConstantExtractor added new frames, we don't execute anything here
        // but await the next call to step
        if new? == 0 {
            self.terminator(terminator)?;
        }
        Ok(true)
    }

    fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", stmt);

        use rustc::mir::StatementKind::*;
        match stmt.kind {
            Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,

            SetDiscriminant { ref lvalue, variant_index } => {
                let dest = self.eval_lvalue(lvalue)?;
                let dest_ty = self.lvalue_ty(lvalue);
                let dest_layout = self.type_layout(dest_ty)?;

                match *dest_layout {
                    Layout::General { discr, .. } => {
                        let discr_size = discr.size().bytes();
                        let dest_ptr = self.force_allocation(dest)?.to_ptr()?;
                        self.memory.write_uint(dest_ptr, variant_index as u128, discr_size)?
                    }

                    Layout::RawNullablePointer { nndiscr, .. } => {
                        if variant_index as u64 != nndiscr {
                            self.write_null(dest, dest_ty)?;
                        }
                    }

                    Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
                        if variant_index as u64 != nndiscr {
                            let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
                            let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
                            trace!("struct wrapped nullable pointer type: {}", ty);
                            // only the pointer part of a fat pointer is used for this space optimization
                            let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
                            self.memory.write_uint(nonnull, 0, discr_size)?;
                        }
                    },

                    _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
                }
            }

            // Mark locals as dead or alive.
            StorageLive(ref lvalue) | StorageDead(ref lvalue)=> {
                let (frame, local) = match self.eval_lvalue(lvalue)? {
                    Lvalue::Local{ frame, local } if self.cur_frame() == frame => (frame, local),
                    _ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
                };
                let old_val = match stmt.kind {
                    StorageLive(_) => self.stack[frame].storage_live(local)?,
                    StorageDead(_) =>  self.stack[frame].storage_dead(local)?,
                    _ => bug!("We already checked that we are a storage stmt")
                };
                self.deallocate_local(old_val)?;
            }

            // Validity checks.
            Validate(op, ref lvalues) => {
                for operand in lvalues {
                    // We need to monomorphize ty *without* erasing lifetimes
                    let ty = operand.ty.subst(self.tcx, self.substs());
                    let lvalue = self.eval_lvalue(&operand.lval)?;
                    self.validate(lvalue, ty, ValidationCtx::new(op))?;
                }
            }
            EndRegion(ce) => {
                self.memory.locks_lifetime_ended(Some(ce));
            }

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}

            InlineAsm { .. } => return Err(EvalError::InlineAsm),
        }

        self.frame_mut().stmt += 1;
        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", terminator.kind);
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            trace!("// {:?}", self.frame().block);
        }
        Ok(())
    }
}

// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
// this includes any method that might access the stack
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
    span: Span,
    ecx: &'a mut EvalContext<'b, 'tcx>,
    mir: &'tcx mir::Mir<'tcx>,
    instance: ty::Instance<'tcx>,
    new_constants: &'a mut EvalResult<'tcx, u64>,
}

impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
    fn global_item(
        &mut self,
        def_id: DefId,
        substs: &'tcx Substs<'tcx>,
        span: Span,
        mutability: Mutability,
    ) {
        let instance = self.ecx.resolve_associated_const(def_id, substs);
        let cid = GlobalId { instance, promoted: None };
        if self.ecx.globals.contains_key(&cid) {
            return;
        }
        if self.ecx.tcx.has_attr(def_id, "linkage") {
            trace!("Initializing an extern global with NULL");
            self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), mutability));
            return;
        }
        self.try(|this| {
            let mir = this.ecx.load_mir(instance.def)?;
            this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
            let internally_mutable = !mir.return_ty.is_freeze(
                    this.ecx.tcx,
                    ty::ParamEnv::empty(Reveal::All),
                    span);
            let mutability = if mutability == Mutability::Mutable || internally_mutable {
                Mutability::Mutable
            } else {
                Mutability::Immutable
            };
            let cleanup = StackPopCleanup::MarkStatic(mutability);
            let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
            trace!("pushing stack frame for global: {}", name);
            this.ecx.push_stack_frame(
                instance,
                span,
                mir,
                Lvalue::Global(cid),
                cleanup,
            )
        });
    }

    fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
        if let Ok(ref mut n) = *self.new_constants {
            *n += 1;
        } else {
            return;
        }
        if let Err(e) = f(self) {
            *self.new_constants = Err(e);
        }
    }
}

impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
    fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
        self.super_constant(constant, location);
        match constant.literal {
            // already computed by rustc
            mir::Literal::Value { .. } => {}
            mir::Literal::Item { def_id, substs } => {
                self.global_item(def_id, substs, constant.span, Mutability::Immutable);
            },
            mir::Literal::Promoted { index } => {
                let cid = GlobalId {
                    instance: self.instance,
                    promoted: Some(index),
                };
                if self.ecx.globals.contains_key(&cid) {
                    return;
                }
                let mir = &self.mir.promoted[index];
                self.try(|this| {
                    let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs);
                    this.ecx.globals.insert(cid, Global::uninitialized(ty));
                    trace!("pushing stack frame for {:?}", index);
                    this.ecx.push_stack_frame(this.instance,
                                              constant.span,
                                              mir,
                                              Lvalue::Global(cid),
                                              StackPopCleanup::MarkStatic(Mutability::Immutable),
                    )
                });
            }
        }
    }

    fn visit_lvalue(
        &mut self,
        lvalue: &mir::Lvalue<'tcx>,
        context: LvalueContext<'tcx>,
        location: mir::Location
    ) {
        self.super_lvalue(lvalue, context, location);
        if let mir::Lvalue::Static(ref static_) = *lvalue {
            let def_id = static_.def_id;
            let substs = self.ecx.tcx.intern_substs(&[]);
            let span = self.span;
            if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
                if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
                    if let hir::ItemStatic(_, m, _) = *node {
                        self.global_item(def_id, substs, span, if m == hir::MutMutable { Mutability::Mutable } else { Mutability::Immutable });
                        return;
                    } else {
                        bug!("static def id doesn't point to static");
                    }
                } else {
                    bug!("static def id doesn't point to item");
                }
            } else {
                let def = self.ecx.tcx.describe_def(def_id).expect("static not found");
                if let hir::def::Def::Static(_, mutable) = def {
                    self.global_item(def_id, substs, span, if mutable { Mutability::Mutable } else { Mutability::Immutable });
                } else {
                    bug!("static found but isn't a static: {:?}", def);
                }
            }
        }
    }
}
<commit_msg>we don't have to set the frame on every step<commit_after>//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::visit::{Visitor, LvalueContext};
use rustc::mir;
use rustc::traits::Reveal;
use rustc::ty;
use rustc::ty::layout::Layout;
use rustc::ty::subst::{Subst, Substs};

use error::{EvalResult, EvalError};
use eval_context::{EvalContext, StackPopCleanup};
use lvalue::{Global, GlobalId, Lvalue};
use validation::ValidationCtx;
use value::{Value, PrimVal};
use syntax::codemap::Span;
use syntax::ast::Mutability;

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
        self.steps_remaining = self.steps_remaining.saturating_sub(n);
        if self.steps_remaining > 0 {
            Ok(())
        } else {
            Err(EvalError::ExecutionTimeLimitReached)
        }
    }

    /// Returns true as long as there are more things to do.
    pub fn step(&mut self) -> EvalResult<'tcx, bool> {
        self.inc_step_counter_and_check_limit(1)?;
        if self.stack.is_empty() {
            return Ok(false);
        }

        let block = self.frame().block;
        let stmt_id = self.frame().stmt;
        let mir = self.mir();
        let basic_block = &mir.basic_blocks()[block];

        if let Some(stmt) = basic_block.statements.get(stmt_id) {
            let mut new = Ok(0);
            ConstantExtractor {
                span: stmt.source_info.span,
                instance: self.frame().instance,
                ecx: self,
                mir,
                new_constants: &mut new,
            }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
            // if ConstantExtractor added new frames, we don't execute anything here
            // but await the next call to step
            if new? == 0 {
                self.statement(stmt)?;
            }
            return Ok(true);
        }

        let terminator = basic_block.terminator();
        let mut new = Ok(0);
        ConstantExtractor {
            span: terminator.source_info.span,
            instance: self.frame().instance,
            ecx: self,
            mir,
            new_constants: &mut new,
        }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
        // if ConstantExtractor added new frames, we don't execute anything here
        // but await the next call to step
        if new? == 0 {
            self.terminator(terminator)?;
        }
        Ok(true)
    }

    fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", stmt);

        use rustc::mir::StatementKind::*;
        match stmt.kind {
            Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,

            SetDiscriminant { ref lvalue, variant_index } => {
                let dest = self.eval_lvalue(lvalue)?;
                let dest_ty = self.lvalue_ty(lvalue);
                let dest_layout = self.type_layout(dest_ty)?;

                match *dest_layout {
                    Layout::General { discr, .. } => {
                        let discr_size = discr.size().bytes();
                        let dest_ptr = self.force_allocation(dest)?.to_ptr()?;
                        self.memory.write_uint(dest_ptr, variant_index as u128, discr_size)?
                    }

                    Layout::RawNullablePointer { nndiscr, .. } => {
                        if variant_index as u64 != nndiscr {
                            self.write_null(dest, dest_ty)?;
                        }
                    }

                    Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
                        if variant_index as u64 != nndiscr {
                            let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
                            let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
                            trace!("struct wrapped nullable pointer type: {}", ty);
                            // only the pointer part of a fat pointer is used for this space optimization
                            let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
                            self.memory.write_uint(nonnull, 0, discr_size)?;
                        }
                    },

                    _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
                }
            }

            // Mark locals as dead or alive.
            StorageLive(ref lvalue) | StorageDead(ref lvalue)=> {
                let (frame, local) = match self.eval_lvalue(lvalue)? {
                    Lvalue::Local{ frame, local } if self.cur_frame() == frame => (frame, local),
                    _ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
                };
                let old_val = match stmt.kind {
                    StorageLive(_) => self.stack[frame].storage_live(local)?,
                    StorageDead(_) =>  self.stack[frame].storage_dead(local)?,
                    _ => bug!("We already checked that we are a storage stmt")
                };
                self.deallocate_local(old_val)?;
            }

            // Validity checks.
            Validate(op, ref lvalues) => {
                for operand in lvalues {
                    // We need to monomorphize ty *without* erasing lifetimes
                    let ty = operand.ty.subst(self.tcx, self.substs());
                    let lvalue = self.eval_lvalue(&operand.lval)?;
                    self.validate(lvalue, ty, ValidationCtx::new(op))?;
                }
            }
            EndRegion(ce) => {
                self.memory.locks_lifetime_ended(Some(ce));
            }

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}

            InlineAsm { .. } => return Err(EvalError::InlineAsm),
        }

        self.frame_mut().stmt += 1;
        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
        trace!("{:?}", terminator.kind);
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            trace!("// {:?}", self.frame().block);
        }
        Ok(())
    }
}

// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
// this includes any method that might access the stack
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
    span: Span,
    ecx: &'a mut EvalContext<'b, 'tcx>,
    mir: &'tcx mir::Mir<'tcx>,
    instance: ty::Instance<'tcx>,
    new_constants: &'a mut EvalResult<'tcx, u64>,
}

impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
    fn global_item(
        &mut self,
        def_id: DefId,
        substs: &'tcx Substs<'tcx>,
        span: Span,
        mutability: Mutability,
    ) {
        let instance = self.ecx.resolve_associated_const(def_id, substs);
        let cid = GlobalId { instance, promoted: None };
        if self.ecx.globals.contains_key(&cid) {
            return;
        }
        if self.ecx.tcx.has_attr(def_id, "linkage") {
            trace!("Initializing an extern global with NULL");
            self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), mutability));
            return;
        }
        self.try(|this| {
            let mir = this.ecx.load_mir(instance.def)?;
            this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
            let internally_mutable = !mir.return_ty.is_freeze(
                    this.ecx.tcx,
                    ty::ParamEnv::empty(Reveal::All),
                    span);
            let mutability = if mutability == Mutability::Mutable || internally_mutable {
                Mutability::Mutable
            } else {
                Mutability::Immutable
            };
            let cleanup = StackPopCleanup::MarkStatic(mutability);
            let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
            trace!("pushing stack frame for global: {}", name);
            this.ecx.push_stack_frame(
                instance,
                span,
                mir,
                Lvalue::Global(cid),
                cleanup,
            )
        });
    }

    fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
        if let Ok(ref mut n) = *self.new_constants {
            *n += 1;
        } else {
            return;
        }
        if let Err(e) = f(self) {
            *self.new_constants = Err(e);
        }
    }
}

impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
    fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
        self.super_constant(constant, location);
        match constant.literal {
            // already computed by rustc
            mir::Literal::Value { .. } => {}
            mir::Literal::Item { def_id, substs } => {
                self.global_item(def_id, substs, constant.span, Mutability::Immutable);
            },
            mir::Literal::Promoted { index } => {
                let cid = GlobalId {
                    instance: self.instance,
                    promoted: Some(index),
                };
                if self.ecx.globals.contains_key(&cid) {
                    return;
                }
                let mir = &self.mir.promoted[index];
                self.try(|this| {
                    let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs);
                    this.ecx.globals.insert(cid, Global::uninitialized(ty));
                    trace!("pushing stack frame for {:?}", index);
                    this.ecx.push_stack_frame(this.instance,
                                              constant.span,
                                              mir,
                                              Lvalue::Global(cid),
                                              StackPopCleanup::MarkStatic(Mutability::Immutable),
                    )
                });
            }
        }
    }

    fn visit_lvalue(
        &mut self,
        lvalue: &mir::Lvalue<'tcx>,
        context: LvalueContext<'tcx>,
        location: mir::Location
    ) {
        self.super_lvalue(lvalue, context, location);
        if let mir::Lvalue::Static(ref static_) = *lvalue {
            let def_id = static_.def_id;
            let substs = self.ecx.tcx.intern_substs(&[]);
            let span = self.span;
            if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
                if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
                    if let hir::ItemStatic(_, m, _) = *node {
                        self.global_item(def_id, substs, span, if m == hir::MutMutable { Mutability::Mutable } else { Mutability::Immutable });
                        return;
                    } else {
                        bug!("static def id doesn't point to static");
                    }
                } else {
                    bug!("static def id doesn't point to item");
                }
            } else {
                let def = self.ecx.tcx.describe_def(def_id).expect("static not found");
                if let hir::def::Def::Static(_, mutable) = def {
                    self.global_item(def_id, substs, span, if mutable { Mutability::Mutable } else { Mutability::Immutable });
                } else {
                    bug!("static found but isn't a static: {:?}", def);
                }
            }
        }
    }
}
<|endoftext|>


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

use crate::{
    counters,
    errors::JsonRpcError,
    methods::{build_registry, JsonRpcRequest, JsonRpcService, RpcRegistry},
};
use futures::future::join_all;
use libra_config::config::{NodeConfig, RoleType};
use libra_json_rpc_types::views::{
    JSONRPC_LIBRA_CHAIN_ID, JSONRPC_LIBRA_LEDGER_TIMESTAMPUSECS, JSONRPC_LIBRA_LEDGER_VERSION,
};
use libra_mempool::MempoolClientSender;
use libra_types::{chain_id::ChainId, ledger_info::LedgerInfoWithSignatures};
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use storage_interface::DbReader;
use tokio::runtime::{Builder, Runtime};
use warp::{
    reject::{self, Reject},
    Filter,
};

// Counter labels for runtime metrics
const LABEL_FAIL: &str = "fail";
const LABEL_INVALID_FORMAT: &str = "invalid_format";
const LABEL_INVALID_METHOD: &str = "invalid_method";
const LABEL_INVALID_PARAMS: &str = "invalid_params";
const LABEL_MISSING_METHOD: &str = "method_not_found";
const LABEL_SUCCESS: &str = "success";

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
    role: RoleType,
    chain_id: ChainId,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("rpc-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(libra_db, mp_sender, role, chain_id);

    let base_route = warp::any()
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint);

    // For now we still allow user to use "/", but user should start to move to "/v1" soon
    let route_root = warp::path::end().and(base_route.clone());

    let route_v1 = warp::path::path("v1")
        .and(warp::path::end())
        .and(base_route);

    let full_route = route_v1.or(route_root);

    // Ensure that we actually bind to the socket first before spawning the
    // server tasks. This helps in tests to prevent races where a client attempts
    // to make a request before the server task is actually listening on the
    // socket.
    //
    // Note: we need to enter the runtime context first to actually bind, since
    //       tokio TcpListener can only be bound inside a tokio context.
    let server = runtime.enter(move || warp::serve(full_route).bind(address));
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(
        config.rpc.address,
        libra_db,
        mp_sender,
        config.base.role,
        config.base.chain_id,
    )
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    // take snapshot of latest version of DB to be used across all requests, especially for batched requests
    let ledger_info = service
        .get_latest_ledger_info()
        .map_err(|_| reject::custom(DatabaseError))?;

    let resp = Ok(if let Value::Array(requests) = data {
        // batch API call
        let futures = requests.into_iter().map(|req| {
            rpc_request_handler(
                req,
                service.clone(),
                Arc::clone(&registry),
                ledger_info.clone(),
            )
        });
        let responses = join_all(futures).await;
        warp::reply::json(&Value::Array(responses))
    } else {
        // single API call
        let resp = rpc_request_handler(data, service, registry, ledger_info).await;
        warp::reply::json(&resp)
    });

    Ok(Box::new(resp) as Box<dyn warp::Reply>)
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
    ledger_info: LedgerInfoWithSignatures,
) -> Value {
    let request: Map<String, Value>;
    let mut response = Map::new();
    let version = ledger_info.ledger_info().version();
    let timestamp = ledger_info.ledger_info().timestamp_usecs();

    // set defaults: protocol version to 2.0, request id to null
    response.insert("jsonrpc".to_string(), Value::String("2.0".to_string()));
    response.insert("id".to_string(), Value::Null);
    response.insert(
        JSONRPC_LIBRA_LEDGER_VERSION.to_string(),
        Value::Number(version.into()),
    );
    response.insert(
        JSONRPC_LIBRA_LEDGER_TIMESTAMPUSECS.to_string(),
        Value::Number(timestamp.into()),
    );
    response.insert(
        JSONRPC_LIBRA_CHAIN_ID.to_string(),
        Value::Number(service.chain_id().id().into()),
    );

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_request(),
                Some(LABEL_INVALID_FORMAT),
            );
            return Value::Object(response);
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.insert("id".to_string(), request_id);
        }
        Err(err) => {
            set_response_error(&mut response, err, Some(LABEL_INVALID_FORMAT));
            return Value::Object(response);
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        set_response_error(&mut response, err, Some(LABEL_INVALID_FORMAT));
        return Value::Object(response);
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_params(None),
                Some(LABEL_INVALID_PARAMS),
            );
            return Value::Object(response);
        }
    }

    let request_params = JsonRpcRequest {
        ledger_info,
        params,
    };
    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => match handler(service, request_params).await {
                Ok(result) => {
                    response.insert("result".to_string(), result);
                    counters::REQUESTS
                        .with_label_values(&[name, LABEL_SUCCESS])
                        .inc();
                }
                Err(err) => {
                    // check for custom error
                    if let Some(custom_error) = err.downcast_ref::<JsonRpcError>() {
                        set_response_error(&mut response, custom_error.clone(), None);
                    } else {
                        set_response_error(
                            &mut response,
                            JsonRpcError::internal_error(err.to_string()),
                            None,
                        );
                    }
                    counters::REQUESTS
                        .with_label_values(&[name, LABEL_FAIL])
                        .inc();
                }
            },
            None => {
                set_response_error(
                    &mut response,
                    JsonRpcError::method_not_found(),
                    Some(LABEL_MISSING_METHOD),
                );
            }
        },
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_request(),
                Some(LABEL_INVALID_METHOD),
            );
        }
    }

    Value::Object(response)
}

// Sets the JSON RPC error value for a given response.
// If a counter label is supplied, also increments the invalid request counter using the label,
fn set_response_error(response: &mut Map<String, Value>, error: JsonRpcError, label: Option<&str>) {
    response.insert("error".to_string(), error.serialize());

    if let Some(label) = label {
        counters::INVALID_REQUESTS.with_label_values(&[label]).inc();
    }
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_request())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}

/// Warp rejection types
#[derive(Debug)]
struct DatabaseError;

impl Reject for DatabaseError {}
<commit_msg>[json-rpc] Add healthcheck endpoint<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters,
    errors::JsonRpcError,
    methods::{build_registry, JsonRpcRequest, JsonRpcService, RpcRegistry},
};
use futures::future::join_all;
use libra_config::config::{NodeConfig, RoleType};
use libra_json_rpc_types::views::{
    JSONRPC_LIBRA_CHAIN_ID, JSONRPC_LIBRA_LEDGER_TIMESTAMPUSECS, JSONRPC_LIBRA_LEDGER_VERSION,
};
use libra_mempool::MempoolClientSender;
use libra_types::{chain_id::ChainId, ledger_info::LedgerInfoWithSignatures};
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use storage_interface::DbReader;
use tokio::runtime::{Builder, Runtime};
use warp::{
    reject::{self, Reject},
    Filter,
};

// Counter labels for runtime metrics
const LABEL_FAIL: &str = "fail";
const LABEL_INVALID_FORMAT: &str = "invalid_format";
const LABEL_INVALID_METHOD: &str = "invalid_method";
const LABEL_INVALID_PARAMS: &str = "invalid_params";
const LABEL_MISSING_METHOD: &str = "method_not_found";
const LABEL_SUCCESS: &str = "success";

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
    role: RoleType,
    chain_id: ChainId,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("rpc-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(libra_db, mp_sender, role, chain_id);

    let base_route = warp::any()
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint);

    // For now we still allow user to use "/", but user should start to move to "/v1" soon
    let route_root = warp::path::end().and(base_route.clone());

    let route_v1 = warp::path::path("v1")
        .and(warp::path::end())
        .and(base_route);

    let health_route = warp::path!("-" / "healthy")
        .and(warp::path::end())
        .map(|| "libra-node:ok");

    let full_route = health_route.or(route_v1.or(route_root));

    // Ensure that we actually bind to the socket first before spawning the
    // server tasks. This helps in tests to prevent races where a client attempts
    // to make a request before the server task is actually listening on the
    // socket.
    //
    // Note: we need to enter the runtime context first to actually bind, since
    //       tokio TcpListener can only be bound inside a tokio context.
    let server = runtime.enter(move || warp::serve(full_route).bind(address));
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(
        config.rpc.address,
        libra_db,
        mp_sender,
        config.base.role,
        config.base.chain_id,
    )
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    // take snapshot of latest version of DB to be used across all requests, especially for batched requests
    let ledger_info = service
        .get_latest_ledger_info()
        .map_err(|_| reject::custom(DatabaseError))?;

    let resp = Ok(if let Value::Array(requests) = data {
        // batch API call
        let futures = requests.into_iter().map(|req| {
            rpc_request_handler(
                req,
                service.clone(),
                Arc::clone(&registry),
                ledger_info.clone(),
            )
        });
        let responses = join_all(futures).await;
        warp::reply::json(&Value::Array(responses))
    } else {
        // single API call
        let resp = rpc_request_handler(data, service, registry, ledger_info).await;
        warp::reply::json(&resp)
    });

    Ok(Box::new(resp) as Box<dyn warp::Reply>)
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
    ledger_info: LedgerInfoWithSignatures,
) -> Value {
    let request: Map<String, Value>;
    let mut response = Map::new();
    let version = ledger_info.ledger_info().version();
    let timestamp = ledger_info.ledger_info().timestamp_usecs();

    // set defaults: protocol version to 2.0, request id to null
    response.insert("jsonrpc".to_string(), Value::String("2.0".to_string()));
    response.insert("id".to_string(), Value::Null);
    response.insert(
        JSONRPC_LIBRA_LEDGER_VERSION.to_string(),
        Value::Number(version.into()),
    );
    response.insert(
        JSONRPC_LIBRA_LEDGER_TIMESTAMPUSECS.to_string(),
        Value::Number(timestamp.into()),
    );
    response.insert(
        JSONRPC_LIBRA_CHAIN_ID.to_string(),
        Value::Number(service.chain_id().id().into()),
    );

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_request(),
                Some(LABEL_INVALID_FORMAT),
            );
            return Value::Object(response);
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.insert("id".to_string(), request_id);
        }
        Err(err) => {
            set_response_error(&mut response, err, Some(LABEL_INVALID_FORMAT));
            return Value::Object(response);
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        set_response_error(&mut response, err, Some(LABEL_INVALID_FORMAT));
        return Value::Object(response);
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_params(None),
                Some(LABEL_INVALID_PARAMS),
            );
            return Value::Object(response);
        }
    }

    let request_params = JsonRpcRequest {
        ledger_info,
        params,
    };
    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => match handler(service, request_params).await {
                Ok(result) => {
                    response.insert("result".to_string(), result);
                    counters::REQUESTS
                        .with_label_values(&[name, LABEL_SUCCESS])
                        .inc();
                }
                Err(err) => {
                    // check for custom error
                    if let Some(custom_error) = err.downcast_ref::<JsonRpcError>() {
                        set_response_error(&mut response, custom_error.clone(), None);
                    } else {
                        set_response_error(
                            &mut response,
                            JsonRpcError::internal_error(err.to_string()),
                            None,
                        );
                    }
                    counters::REQUESTS
                        .with_label_values(&[name, LABEL_FAIL])
                        .inc();
                }
            },
            None => {
                set_response_error(
                    &mut response,
                    JsonRpcError::method_not_found(),
                    Some(LABEL_MISSING_METHOD),
                );
            }
        },
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_request(),
                Some(LABEL_INVALID_METHOD),
            );
        }
    }

    Value::Object(response)
}

// Sets the JSON RPC error value for a given response.
// If a counter label is supplied, also increments the invalid request counter using the label,
fn set_response_error(response: &mut Map<String, Value>, error: JsonRpcError, label: Option<&str>) {
    response.insert("error".to_string(), error.serialize());

    if let Some(label) = label {
        counters::INVALID_REQUESTS.with_label_values(&[label]).inc();
    }
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_request())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}

/// Warp rejection types
#[derive(Debug)]
struct DatabaseError;

impl Reject for DatabaseError {}
<|endoftext|>