<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use mimir;

use geojson;
use geo;
use std::rc::Rc;

#[derive(Serialize, Deserialize, Debug)]
pub struct Geocoding {
    version: String,
    query: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Feature {
    #[serde(rename="type")]
    pub feature_type: String,
    pub geometry: geojson::Geometry,
    pub properties: Properties,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Properties {
    pub geocoding: GeocodingResponse,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct GeocodingResponse {
    pub id: String,
    #[serde(rename="type")]
    pub place_type: String, /* TODO enum cf https://github.com/geocoders/geocodejson-spec/blob/master/draft/README.md#feature-object */
    pub label: Option<String>,
    pub name: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub housenumber: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub street: Option<String>,
    pub postcode: Option<String>,
    pub city: Option<String>,
    pub city_code: Option<String>, /* deprecated */
    pub citycode: Option<String>,
    pub level: Option<u32>,
    // pub accuracy: Option<i32>,
    // pub district: Option<String>,
    // pub county: Option<String>,
    // pub state: Option<String>,
    // pub country: Option<String>,
    // pub geohash: Option<String>,
    pub administrative_regions: Vec<Rc<mimir::Admin>>,
}

trait ToGeom {
    fn to_geom(&self) -> geojson::Geometry;
}

impl ToGeom for mimir::Place {
    fn to_geom(&self) -> geojson::Geometry {
        match self {
            &mimir::Place::Admin(ref admin) => admin.coord.to_geom(),
            &mimir::Place::Street(ref street) => street.coord.to_geom(),
            &mimir::Place::Addr(ref addr) => addr.coord.to_geom(),
            &mimir::Place::Poi(ref poi) => poi.coord.to_geom(),
            &mimir::Place::Stop(ref stop) => stop.coord.to_geom(),
        }
    }
}

impl ToGeom for geo::Coordinate<f64> {
    fn to_geom(&self) -> geojson::Geometry {
        geojson::Geometry::new(geojson::Value::Point(vec![self.y, self.x]))
    }
}

impl From<mimir::Place> for Feature {
    fn from(other: mimir::Place) -> Feature {
        let geom = other.to_geom();
        let geocoding = match other {
            mimir::Place::Admin(admin) => GeocodingResponse::from(admin),
            mimir::Place::Street(street) => GeocodingResponse::from(street),
            mimir::Place::Addr(addr) => GeocodingResponse::from(addr),
            mimir::Place::Poi(poi) => GeocodingResponse::from(poi),
            mimir::Place::Stop(poi) => GeocodingResponse::from(poi),
        };
        Feature {
            feature_type: "Feature".to_string(),
            geometry: geom,
            properties: Properties { geocoding: geocoding },
        }
    }
}

impl From<mimir::Admin> for GeocodingResponse {
    fn from(other: mimir::Admin) -> GeocodingResponse {
        let type_ = "city".to_string(); //to be improved: it can be something else
        let name = Some(other.name);
        let insee = Some(other.insee);
        let level = Some(other.level); //might be used for type_ and become useless
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let label = Some(other.label);
        GeocodingResponse {
            id: other.id,
            city_code: insee.clone(),
            citycode: insee,
            level: level,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: None,
            administrative_regions: vec![],
        }
    }
}

fn get_city_name(admins: &Vec<Rc<mimir::Admin>>) -> Option<String> {
    // for the moment for the 'postcode' and the 'city', we take first admin
    // that has a postcode
    // TODO: change this (with a 'city tag in the admin ?')
    admins.iter()
        .find(|a| !a.zip_codes.is_empty())
        .or_else(|| admins.iter().next())
        .map(|admin| admin.name.clone())
}

fn get_city_code(admins: &Vec<Rc<mimir::Admin>>) -> Option<String> {
    admins.iter()
        .find(|a| !a.zip_codes.is_empty())
        .or_else(|| admins.iter().next())
        .map(|admin| admin.insee.clone())
}

impl From<mimir::Street> for GeocodingResponse {
    fn from(other: mimir::Street) -> GeocodingResponse {
        let type_ = "street".to_string();
        let name = Some(other.street_name);
        let label = Some(other.label);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_city_code(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: city,
            administrative_regions: admins,
        }
    }
}

impl From<mimir::Addr> for GeocodingResponse {
    fn from(other: mimir::Addr) -> GeocodingResponse {
        let type_ = "house".to_string();
        let label = Some(other.label);
        let housenumber = Some(other.house_number.to_string());
        let street_name = Some(other.street.street_name.to_string());
        let name = Some(format!("{} {}", other.house_number, other.street.street_name));
        let admins = other.street.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_city_code(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: housenumber,
            street: street_name,
            city: city,
            administrative_regions: admins,
        }
    }
}

impl From<mimir::Poi> for GeocodingResponse {
    fn from(other: mimir::Poi) -> GeocodingResponse {
        let type_ = "poi".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_city_code(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: city,
            administrative_regions: admins,
        }
    }
}

impl From<mimir::Stop> for GeocodingResponse {
    fn from(other: mimir::Stop) -> GeocodingResponse {
        let type_ = "public_transport:stop_area".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_city_code(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: city,
            administrative_regions: admins,
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Autocomplete {
    #[serde(rename="type")]
    format_type: String,
    geocoding: Geocoding,
    features: Vec<Feature>,
}

impl Autocomplete {
    pub fn new(q: String, features: Vec<Feature>) -> Autocomplete {
        // TODO couldn't we mode this function ? in Autocomplete ?
        Autocomplete {
            format_type: "FeatureCollection".to_string(),
            geocoding: Geocoding {
                version: "0.1.0".to_string(),
                query: Some(q),
            },
            features: features,
        }
    }
}

impl From<Vec<mimir::Place>> for Autocomplete {
    fn from(places: Vec<mimir::Place>) -> Autocomplete {
        Autocomplete::new("".to_string(),
                          places.into_iter().map(|p| Feature::from(p)).collect())
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Coord {
    pub lat: f64,
    pub lon: f64,
}

pub mod v1 {
    use mimir;
    use rs_es;

    // Note: I think this should be in api.rs but with the serde stuff it's easier for all
    // serde struct to be in the same file

    #[derive(Serialize, Deserialize, Debug)]
    pub struct EndPoint {
        pub description: String,
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct CustomError {
        pub short: String,
        pub long: String,
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub enum V1Reponse {
        Error(CustomError),
        Response { description: String },
    }
    #[derive(Debug)]
    pub enum AutocompleteResponse {
        Error(CustomError),
        Autocomplete(super::Autocomplete),
    }

    use serde;
    impl serde::Serialize for AutocompleteResponse {
        fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
            where S: serde::Serializer
        {
            match self {
                &AutocompleteResponse::Autocomplete(ref a) => serializer.serialize_some(a),
                &AutocompleteResponse::Error(ref e) => serializer.serialize_some(e),
            }
        }
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct Status {
        pub version: String,
        pub es: String,
        pub status: String,
    }

    impl From<Result<Vec<mimir::Place>, rs_es::error::EsError>> for AutocompleteResponse {
        fn from(r: Result<Vec<mimir::Place>, rs_es::error::EsError>) -> AutocompleteResponse {
            match r {
                Ok(places) => AutocompleteResponse::Autocomplete(super::Autocomplete::from(places)),
                Err(e) => {
                    AutocompleteResponse::Error(CustomError {
                        short: "query error".to_string(),
                        long: format!("invalid query {:?}", e),
                    })
                }
            }
        }
    }
}
<commit_msg>bragi: refactoring<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use mimir;

use geojson;
use geo;
use std::rc::Rc;

#[derive(Serialize, Deserialize, Debug)]
pub struct Geocoding {
    version: String,
    query: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Feature {
    #[serde(rename="type")]
    pub feature_type: String,
    pub geometry: geojson::Geometry,
    pub properties: Properties,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Properties {
    pub geocoding: GeocodingResponse,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct GeocodingResponse {
    pub id: String,
    #[serde(rename="type")]
    pub place_type: String, /* TODO enum cf https://github.com/geocoders/geocodejson-spec/blob/master/draft/README.md#feature-object */
    pub label: Option<String>,
    pub name: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub housenumber: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub street: Option<String>,
    pub postcode: Option<String>,
    pub city: Option<String>,
    pub city_code: Option<String>, /* deprecated */
    pub citycode: Option<String>,
    pub level: Option<u32>,
    // pub accuracy: Option<i32>,
    // pub district: Option<String>,
    // pub county: Option<String>,
    // pub state: Option<String>,
    // pub country: Option<String>,
    // pub geohash: Option<String>,
    pub administrative_regions: Vec<Rc<mimir::Admin>>,
}

trait ToGeom {
    fn to_geom(&self) -> geojson::Geometry;
}

impl ToGeom for mimir::Place {
    fn to_geom(&self) -> geojson::Geometry {
        match self {
            &mimir::Place::Admin(ref admin) => admin.coord.to_geom(),
            &mimir::Place::Street(ref street) => street.coord.to_geom(),
            &mimir::Place::Addr(ref addr) => addr.coord.to_geom(),
            &mimir::Place::Poi(ref poi) => poi.coord.to_geom(),
            &mimir::Place::Stop(ref stop) => stop.coord.to_geom(),
        }
    }
}

impl ToGeom for geo::Coordinate<f64> {
    fn to_geom(&self) -> geojson::Geometry {
        geojson::Geometry::new(geojson::Value::Point(vec![self.y, self.x]))
    }
}

impl From<mimir::Place> for Feature {
    fn from(other: mimir::Place) -> Feature {
        let geom = other.to_geom();
        let geocoding = match other {
            mimir::Place::Admin(admin) => GeocodingResponse::from(admin),
            mimir::Place::Street(street) => GeocodingResponse::from(street),
            mimir::Place::Addr(addr) => GeocodingResponse::from(addr),
            mimir::Place::Poi(poi) => GeocodingResponse::from(poi),
            mimir::Place::Stop(poi) => GeocodingResponse::from(poi),
        };
        Feature {
            feature_type: "Feature".to_string(),
            geometry: geom,
            properties: Properties { geocoding: geocoding },
        }
    }
}

impl From<mimir::Admin> for GeocodingResponse {
    fn from(other: mimir::Admin) -> GeocodingResponse {
        let type_ = "city".to_string(); //to be improved: it can be something else
        let name = Some(other.name);
        let insee = Some(other.insee);
        let level = Some(other.level); //might be used for type_ and become useless
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let label = Some(other.label);
        GeocodingResponse {
            id: other.id,
            city_code: insee.clone(),
            citycode: insee,
            level: level,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: None,
            administrative_regions: vec![],
        }
    }
}

fn get_city_name(admins: &Vec<Rc<mimir::Admin>>) -> Option<String> {
    // for the moment for the 'postcode' and the 'city', we take first admin
    // that has a postcode
    // TODO: change this (with a 'city tag in the admin ?')
    admins.iter()
        .find(|a| !a.zip_codes.is_empty())
        .or_else(|| admins.iter().next())
        .map(|admin| admin.name.clone())
}

fn get_citycode(admins: &Vec<Rc<mimir::Admin>>) -> Option<String> {
    admins.iter()
        .find(|a| !a.zip_codes.is_empty())
        .or_else(|| admins.iter().next())
        .map(|admin| admin.insee.clone())
}

impl From<mimir::Street> for GeocodingResponse {
    fn from(other: mimir::Street) -> GeocodingResponse {
        let type_ = "street".to_string();
        let name = Some(other.street_name);
        let label = Some(other.label);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: city,
            administrative_regions: admins,
        }
    }
}

impl From<mimir::Addr> for GeocodingResponse {
    fn from(other: mimir::Addr) -> GeocodingResponse {
        let type_ = "house".to_string();
        let label = Some(other.label);
        let housenumber = Some(other.house_number.to_string());
        let street_name = Some(other.street.street_name.to_string());
        let name = Some(format!("{} {}", other.house_number, other.street.street_name));
        let admins = other.street.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: housenumber,
            street: street_name,
            city: city,
            administrative_regions: admins,
        }
    }
}

impl From<mimir::Poi> for GeocodingResponse {
    fn from(other: mimir::Poi) -> GeocodingResponse {
        let type_ = "poi".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: city,
            administrative_regions: admins,
        }
    }
}

impl From<mimir::Stop> for GeocodingResponse {
    fn from(other: mimir::Stop) -> GeocodingResponse {
        let type_ = "public_transport:stop_area".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            city_code: citycode.clone(),
            citycode: citycode,
            level: None,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: None,
            street: None,
            city: city,
            administrative_regions: admins,
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Autocomplete {
    #[serde(rename="type")]
    format_type: String,
    geocoding: Geocoding,
    features: Vec<Feature>,
}

impl Autocomplete {
    pub fn new(q: String, features: Vec<Feature>) -> Autocomplete {
        // TODO couldn't we mode this function ? in Autocomplete ?
        Autocomplete {
            format_type: "FeatureCollection".to_string(),
            geocoding: Geocoding {
                version: "0.1.0".to_string(),
                query: Some(q),
            },
            features: features,
        }
    }
}

impl From<Vec<mimir::Place>> for Autocomplete {
    fn from(places: Vec<mimir::Place>) -> Autocomplete {
        Autocomplete::new("".to_string(),
                          places.into_iter().map(|p| Feature::from(p)).collect())
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Coord {
    pub lat: f64,
    pub lon: f64,
}

pub mod v1 {
    use mimir;
    use rs_es;

    // Note: I think this should be in api.rs but with the serde stuff it's easier for all
    // serde struct to be in the same file

    #[derive(Serialize, Deserialize, Debug)]
    pub struct EndPoint {
        pub description: String,
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct CustomError {
        pub short: String,
        pub long: String,
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub enum V1Reponse {
        Error(CustomError),
        Response { description: String },
    }
    #[derive(Debug)]
    pub enum AutocompleteResponse {
        Error(CustomError),
        Autocomplete(super::Autocomplete),
    }

    use serde;
    impl serde::Serialize for AutocompleteResponse {
        fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
            where S: serde::Serializer
        {
            match self {
                &AutocompleteResponse::Autocomplete(ref a) => serializer.serialize_some(a),
                &AutocompleteResponse::Error(ref e) => serializer.serialize_some(e),
            }
        }
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct Status {
        pub version: String,
        pub es: String,
        pub status: String,
    }

    impl From<Result<Vec<mimir::Place>, rs_es::error::EsError>> for AutocompleteResponse {
        fn from(r: Result<Vec<mimir::Place>, rs_es::error::EsError>) -> AutocompleteResponse {
            match r {
                Ok(places) => AutocompleteResponse::Autocomplete(super::Autocomplete::from(places)),
                Err(e) => {
                    AutocompleteResponse::Error(CustomError {
                        short: "query error".to_string(),
                        long: format!("invalid query {:?}", e),
                    })
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::ffi::*;

use libc;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem;
use std::default;

pub fn initialize() -> Result<(), &'static str> {
    if unsafe { GLSLangInitialize() } == 0 {
        Err("Couldn't initialize GLSLang")
    } else {
        Ok(())
    }
}

pub fn finalize() -> Result<(), &'static str> {
    if unsafe { GLSLangFinalize() } == 0 {
        Err("Couldn't finalize GLSLang")
    } else {
        Ok(())
    }
}

pub trait AsAngleEnum {
    fn as_angle_enum(&self) -> i32;
}

pub enum ShaderSpec {
    Gles2,
    WebGL,
    Gles3,
    WebGL2,
    Css,
}

impl AsAngleEnum for ShaderSpec {
    #[inline]
    fn as_angle_enum(&self) -> i32 {
        match *self {
            ShaderSpec::Gles2 => SH_GLES2_SPEC,
            ShaderSpec::WebGL => SH_WEBGL_SPEC,
            ShaderSpec::Gles3 => SH_GLES3_SPEC,
            ShaderSpec::WebGL2 => SH_WEBGL2_SPEC,
            ShaderSpec::Css => SH_CSS_SHADERS_SPEC,
        }
    }
}

pub enum Output {
    Essl,
    Glsl,
    GlslCompat,
    GlslCore,
    Glsl130,
    Glsl140,
    Glsl150Core,
    Glsl330Core,
    Glsl400Core,
    Glsl410Core,
    Glsl420Core,
    Glsl430Core,
    Glsl440Core,
    Glsl450Core,
}

impl AsAngleEnum for Output {
    #[inline]
    fn as_angle_enum(&self) -> i32 {
        match *self {
            Output::Essl => SH_ESSL_OUTPUT,
            Output::Glsl => SH_GLSL_OUTPUT,
            Output::GlslCompat => SH_GLSL_COMPATIBILITY_OUTPUT,
            Output::GlslCore => SH_GLSL_CORE_OUTPUT,
            Output::Glsl130 => SH_GLSL_130_OUTPUT,
            Output::Glsl140 => SH_GLSL_140_OUTPUT,
            Output::Glsl150Core => SH_GLSL_150_CORE_OUTPUT,
            Output::Glsl330Core => SH_GLSL_330_CORE_OUTPUT,
            Output::Glsl400Core => SH_GLSL_400_CORE_OUTPUT,
            Output::Glsl410Core => SH_GLSL_410_CORE_OUTPUT,
            Output::Glsl420Core => SH_GLSL_420_CORE_OUTPUT,
            Output::Glsl430Core => SH_GLSL_430_CORE_OUTPUT,
            Output::Glsl440Core => SH_GLSL_440_CORE_OUTPUT,
            Output::Glsl450Core => SH_GLSL_450_CORE_OUTPUT,
        }
    }
}

pub type BuiltInResources = ShBuiltInResources;

impl default::Default for BuiltInResources {
    fn default() -> BuiltInResources {
        unsafe {
            let mut ret: BuiltInResources = mem::uninitialized();
            GLSLangInitBuiltInResources(&mut ret);
            ret
        }
    }
}

impl BuiltInResources {
    #[inline]
    pub fn empty() -> BuiltInResources {
        unsafe { mem::zeroed() }
    }
}

pub struct ShaderValidator {
    handle: ShHandle,
}

impl ShaderValidator {
    /// Create a new ShaderValidator instance
    /// NB: To call this you should have called first
    /// initialize()
    pub fn new(shader_type: u32,
               spec: ShaderSpec,
               output: Output,
               resources: &BuiltInResources) -> Option<ShaderValidator> {
        let handle = unsafe {
            GLSLangConstructCompiler(shader_type, spec.as_angle_enum(), output.as_angle_enum(), resources)
        };

        if handle.is_null() {
            return None;
        }

        Some(ShaderValidator {
            handle: handle,
        })
    }

    #[inline]
    pub fn for_webgl(shader_type: u32,
                     output: Output,
                     resources: &BuiltInResources) -> Option<ShaderValidator> {
        Self::new(shader_type, ShaderSpec::WebGL, output, resources)
    }

    pub fn compile(&self, strings: &[&str], options: i32) -> Result<(), &'static str> {
        let mut cstrings = Vec::with_capacity(strings.len());

        for s in strings.iter() {
            cstrings.push(try!(CString::new(*s).map_err(|_| "Found invalid characters")))
        }

        let cptrs: Vec<_> = cstrings.iter().map(|s| s.as_ptr()).collect();

        if unsafe { GLSLangCompile(self.handle,
                                   cptrs.as_ptr() as *const *const libc::c_char,
                                   cstrings.len() as libc::size_t,
                                   options) } == 0 {
            return Err("Couldn't compile shader")
        }
        Ok(())
    }

    pub fn object_code(&self) -> String {
        unsafe {
            let c_str = CStr::from_ptr(GLSLangGetObjectCode(self.handle));
            c_str.to_string_lossy().into_owned()
        }
    }

    pub fn info_log(&self) -> String {
        unsafe {
            let c_str = CStr::from_ptr(GLSLangGetInfoLog(self.handle));
            c_str.to_string_lossy().into_owned()
        }
    }

    pub fn compile_and_translate(&self, strings: &[&str]) -> Result<String, &'static str> {

        let options = SH_VALIDATE | SH_OBJECT_CODE |
                      SH_EMULATE_BUILT_IN_FUNCTIONS | // To workaround drivers
                      SH_TIMING_RESTRICTIONS |
                      SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
                      SH_INIT_GL_POSITION |
                      SH_ENFORCE_PACKING_RESTRICTIONS |
                      SH_LIMIT_CALL_STACK_DEPTH;

        try!(self.compile(strings, options));
        Ok(self.object_code())
    }
}

impl Drop for ShaderValidator {
    fn drop(&mut self) {
        unsafe {
            GLSLangDestructCompiler(self.handle)
        }
    }
}
<commit_msg>Disable SH_TIMING_RESTRICTIONS compilation flag. It's just a experimental feature and doesn't support user callable functions in shaders now, almost all the shaders fail to compile because of this.<commit_after>use super::ffi::*;

use libc;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem;
use std::default;

pub fn initialize() -> Result<(), &'static str> {
    if unsafe { GLSLangInitialize() } == 0 {
        Err("Couldn't initialize GLSLang")
    } else {
        Ok(())
    }
}

pub fn finalize() -> Result<(), &'static str> {
    if unsafe { GLSLangFinalize() } == 0 {
        Err("Couldn't finalize GLSLang")
    } else {
        Ok(())
    }
}

pub trait AsAngleEnum {
    fn as_angle_enum(&self) -> i32;
}

pub enum ShaderSpec {
    Gles2,
    WebGL,
    Gles3,
    WebGL2,
    Css,
}

impl AsAngleEnum for ShaderSpec {
    #[inline]
    fn as_angle_enum(&self) -> i32 {
        match *self {
            ShaderSpec::Gles2 => SH_GLES2_SPEC,
            ShaderSpec::WebGL => SH_WEBGL_SPEC,
            ShaderSpec::Gles3 => SH_GLES3_SPEC,
            ShaderSpec::WebGL2 => SH_WEBGL2_SPEC,
            ShaderSpec::Css => SH_CSS_SHADERS_SPEC,
        }
    }
}

pub enum Output {
    Essl,
    Glsl,
    GlslCompat,
    GlslCore,
    Glsl130,
    Glsl140,
    Glsl150Core,
    Glsl330Core,
    Glsl400Core,
    Glsl410Core,
    Glsl420Core,
    Glsl430Core,
    Glsl440Core,
    Glsl450Core,
}

impl AsAngleEnum for Output {
    #[inline]
    fn as_angle_enum(&self) -> i32 {
        match *self {
            Output::Essl => SH_ESSL_OUTPUT,
            Output::Glsl => SH_GLSL_OUTPUT,
            Output::GlslCompat => SH_GLSL_COMPATIBILITY_OUTPUT,
            Output::GlslCore => SH_GLSL_CORE_OUTPUT,
            Output::Glsl130 => SH_GLSL_130_OUTPUT,
            Output::Glsl140 => SH_GLSL_140_OUTPUT,
            Output::Glsl150Core => SH_GLSL_150_CORE_OUTPUT,
            Output::Glsl330Core => SH_GLSL_330_CORE_OUTPUT,
            Output::Glsl400Core => SH_GLSL_400_CORE_OUTPUT,
            Output::Glsl410Core => SH_GLSL_410_CORE_OUTPUT,
            Output::Glsl420Core => SH_GLSL_420_CORE_OUTPUT,
            Output::Glsl430Core => SH_GLSL_430_CORE_OUTPUT,
            Output::Glsl440Core => SH_GLSL_440_CORE_OUTPUT,
            Output::Glsl450Core => SH_GLSL_450_CORE_OUTPUT,
        }
    }
}

pub type BuiltInResources = ShBuiltInResources;

impl default::Default for BuiltInResources {
    fn default() -> BuiltInResources {
        unsafe {
            let mut ret: BuiltInResources = mem::uninitialized();
            GLSLangInitBuiltInResources(&mut ret);
            ret
        }
    }
}

impl BuiltInResources {
    #[inline]
    pub fn empty() -> BuiltInResources {
        unsafe { mem::zeroed() }
    }
}

pub struct ShaderValidator {
    handle: ShHandle,
}

impl ShaderValidator {
    /// Create a new ShaderValidator instance
    /// NB: To call this you should have called first
    /// initialize()
    pub fn new(shader_type: u32,
               spec: ShaderSpec,
               output: Output,
               resources: &BuiltInResources) -> Option<ShaderValidator> {
        let handle = unsafe {
            GLSLangConstructCompiler(shader_type, spec.as_angle_enum(), output.as_angle_enum(), resources)
        };

        if handle.is_null() {
            return None;
        }

        Some(ShaderValidator {
            handle: handle,
        })
    }

    #[inline]
    pub fn for_webgl(shader_type: u32,
                     output: Output,
                     resources: &BuiltInResources) -> Option<ShaderValidator> {
        Self::new(shader_type, ShaderSpec::WebGL, output, resources)
    }

    pub fn compile(&self, strings: &[&str], options: i32) -> Result<(), &'static str> {
        let mut cstrings = Vec::with_capacity(strings.len());

        for s in strings.iter() {
            cstrings.push(try!(CString::new(*s).map_err(|_| "Found invalid characters")))
        }

        let cptrs: Vec<_> = cstrings.iter().map(|s| s.as_ptr()).collect();

        if unsafe { GLSLangCompile(self.handle,
                                   cptrs.as_ptr() as *const *const libc::c_char,
                                   cstrings.len() as libc::size_t,
                                   options) } == 0 {
            return Err("Couldn't compile shader")
        }
        Ok(())
    }

    pub fn object_code(&self) -> String {
        unsafe {
            let c_str = CStr::from_ptr(GLSLangGetObjectCode(self.handle));
            c_str.to_string_lossy().into_owned()
        }
    }

    pub fn info_log(&self) -> String {
        unsafe {
            let c_str = CStr::from_ptr(GLSLangGetInfoLog(self.handle));
            c_str.to_string_lossy().into_owned()
        }
    }

    pub fn compile_and_translate(&self, strings: &[&str]) -> Result<String, &'static str> {

        let options = SH_VALIDATE | SH_OBJECT_CODE |
                      SH_EMULATE_BUILT_IN_FUNCTIONS | // To workaround drivers
                      SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
                      SH_INIT_GL_POSITION |
                      SH_ENFORCE_PACKING_RESTRICTIONS |
                      SH_LIMIT_CALL_STACK_DEPTH;

        // Todo(Mortimer): Add SH_TIMING_RESTRICTIONS to options when the implementations gets better
        // Right now SH_TIMING_RESTRICTIONS it's experimental
        // and doesn't support user callable functions in shaders

        try!(self.compile(strings, options));
        Ok(self.object_code())
    }
}

impl Drop for ShaderValidator {
    fn drop(&mut self) {
        unsafe {
            GLSLangDestructCompiler(self.handle)
        }
    }
}
<|endoftext|>


<commit_before>//! Utilities to acquire streams and data from from URLs.

use gio::{Cancellable, File as GFile, FileExt, InputStream, MemoryInputStream};
use glib::{self, Bytes as GBytes, Cast};
use std::fmt;

use crate::url_resolver::AllowedUrl;

pub enum IoError {
    BadDataUrl,
    Glib(glib::Error),
}

impl From<glib::Error> for IoError {
    fn from(e: glib::Error) -> IoError {
        IoError::Glib(e)
    }
}

impl fmt::Display for IoError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            IoError::BadDataUrl => write!(f, "invalid data: URL"),
            IoError::Glib(ref e) => e.fmt(f),
        }
    }
}

pub struct BinaryData {
    pub data: Vec<u8>,
    pub content_type: Option<String>,
}

fn decode_data_uri(uri: &str) -> Result<BinaryData, IoError> {
    let data_url = data_url::DataUrl::process(uri).map_err(|_| IoError::BadDataUrl)?;

    let mime_type = data_url.mime_type().to_string();

    let (bytes, fragment_id) = data_url.decode_to_vec().map_err(|_| IoError::BadDataUrl)?;

    // See issue #377 - per the data: URL spec
    // (https://fetch.spec.whatwg.org/#data-urls), those URLs cannot
    // have fragment identifiers.  So, just return an error if we find
    // one.  This probably indicates mis-quoted SVG data inside the
    // data: URL.
    if fragment_id.is_some() {
        return Err(IoError::BadDataUrl);
    }

    Ok(BinaryData {
        data: bytes,
        content_type: Some(mime_type),
    })
}

/// Returns an input stream.  The url can be a data: URL or a plain URI
pub fn acquire_stream(
    aurl: &AllowedUrl,
    cancellable: Option<&Cancellable>,
) -> Result<InputStream, IoError> {
    let uri = aurl.as_str();

    if uri.starts_with("data:") {
        let BinaryData { data, .. } = decode_data_uri(uri)?;

        //        {
        //            use std::fs::File;
        //            use std::io::prelude::*;
        //
        //            let mut file = File::create("data.bin").unwrap();
        //            file.write_all(&data).unwrap();
        //        }

        let stream = MemoryInputStream::new_from_bytes(&GBytes::from_owned(data));
        Ok(stream.upcast::<InputStream>())
    } else {
        let file = GFile::new_for_uri(uri);
        let stream = file.read(cancellable)?;

        Ok(stream.upcast::<InputStream>())
    }
}

/// Returns a chunk of data.  The url can be a data: URL or a plain URI
pub fn acquire_data(
    aurl: &AllowedUrl,
    cancellable: Option<&Cancellable>,
) -> Result<BinaryData, IoError> {
    let uri = aurl.as_str();

    if uri.starts_with("data:") {
        Ok(decode_data_uri(uri)?)
    } else {
        let file = GFile::new_for_uri(uri);
        let (contents, _etag) = file.load_contents(cancellable)?;

        let (content_type, _uncertain) = gio::content_type_guess(Some(uri), &contents);
        let mime_type = gio::content_type_get_mime_type(&content_type).map(String::from);

        Ok(BinaryData {
            data: contents,
            content_type: mime_type.map(From::from),
        })
    }
}
<commit_msg>Clarify docs for acquire_data and acquire_stream<commit_after>//! Utilities to acquire streams and data from from URLs.

use gio::{Cancellable, File as GFile, FileExt, InputStream, MemoryInputStream};
use glib::{self, Bytes as GBytes, Cast};
use std::fmt;

use crate::url_resolver::AllowedUrl;

pub enum IoError {
    BadDataUrl,
    Glib(glib::Error),
}

impl From<glib::Error> for IoError {
    fn from(e: glib::Error) -> IoError {
        IoError::Glib(e)
    }
}

impl fmt::Display for IoError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            IoError::BadDataUrl => write!(f, "invalid data: URL"),
            IoError::Glib(ref e) => e.fmt(f),
        }
    }
}

pub struct BinaryData {
    pub data: Vec<u8>,
    pub content_type: Option<String>,
}

fn decode_data_uri(uri: &str) -> Result<BinaryData, IoError> {
    let data_url = data_url::DataUrl::process(uri).map_err(|_| IoError::BadDataUrl)?;

    let mime_type = data_url.mime_type().to_string();

    let (bytes, fragment_id) = data_url.decode_to_vec().map_err(|_| IoError::BadDataUrl)?;

    // See issue #377 - per the data: URL spec
    // (https://fetch.spec.whatwg.org/#data-urls), those URLs cannot
    // have fragment identifiers.  So, just return an error if we find
    // one.  This probably indicates mis-quoted SVG data inside the
    // data: URL.
    if fragment_id.is_some() {
        return Err(IoError::BadDataUrl);
    }

    Ok(BinaryData {
        data: bytes,
        content_type: Some(mime_type),
    })
}

/// Creates a stream for reading.  The url can be a data: URL or a plain URI.
pub fn acquire_stream(
    aurl: &AllowedUrl,
    cancellable: Option<&Cancellable>,
) -> Result<InputStream, IoError> {
    let uri = aurl.as_str();

    if uri.starts_with("data:") {
        let BinaryData { data, .. } = decode_data_uri(uri)?;

        //        {
        //            use std::fs::File;
        //            use std::io::prelude::*;
        //
        //            let mut file = File::create("data.bin").unwrap();
        //            file.write_all(&data).unwrap();
        //        }

        let stream = MemoryInputStream::new_from_bytes(&GBytes::from_owned(data));
        Ok(stream.upcast::<InputStream>())
    } else {
        let file = GFile::new_for_uri(uri);
        let stream = file.read(cancellable)?;

        Ok(stream.upcast::<InputStream>())
    }
}

/// Reads the entire contents pointed by an URL.  The url can be a data: URL or a plain URI.
pub fn acquire_data(
    aurl: &AllowedUrl,
    cancellable: Option<&Cancellable>,
) -> Result<BinaryData, IoError> {
    let uri = aurl.as_str();

    if uri.starts_with("data:") {
        Ok(decode_data_uri(uri)?)
    } else {
        let file = GFile::new_for_uri(uri);
        let (contents, _etag) = file.load_contents(cancellable)?;

        let (content_type, _uncertain) = gio::content_type_guess(Some(uri), &contents);
        let mime_type = gio::content_type_get_mime_type(&content_type).map(String::from);

        Ok(BinaryData {
            data: contents,
            content_type: mime_type.map(From::from),
        })
    }
}
<|endoftext|>


<commit_before>// Check that the hash of `foo` doesn't change just because we ordered
// the nested items (or even added new ones).

// revisions: cfail1 cfail2
// build-pass (FIXME(62277): could be check-pass?)

#![crate_type = "rlib"]
#![feature(rustc_attrs)]

#[rustc_clean(label="hir_owner", cfg="cfail2")]
#[rustc_dirty(label="hir_owner_items", cfg="cfail2")]
pub fn foo() {
    #[cfg(cfail1)]
    pub fn baz() { } // order is different...

    #[rustc_clean(label="hir_owner", cfg="cfail2")]
    #[rustc_clean(label="hir_owner_items", cfg="cfail2")]
    pub fn bar() { } // but that doesn't matter.

    #[cfg(cfail2)]
    pub fn baz() { } // order is different...

    pub fn bap() { } // neither does adding a new item
}
<commit_msg>Update ich_nested_items.rs<commit_after>// Check that the hash of `foo` doesn't change just because we ordered
// the nested items (or even added new ones).

// revisions: cfail1 cfail2
// build-pass (FIXME(62277): could be check-pass?)

#![crate_type = "rlib"]
#![feature(rustc_attrs)]

#[rustc_clean(label = "hir_owner", cfg = "cfail2")]
#[rustc_dirty(label = "hir_owner_items", cfg = "cfail2")]
pub fn foo() {
    #[cfg(cfail1)]
    pub fn baz() {} // order is different...

    // FIXME: Make "hir_owner" use `rustc_clean` here. Currently "hir_owner" includes a reference to
    // the parent node, which is the statement holding this item. Changing the position of
    // `bar` in `foo` will update that reference and make `hir_owner(bar)` dirty.
    #[rustc_dirty(label = "hir_owner", cfg = "cfail2")]
    #[rustc_clean(label = "hir_owner_items", cfg = "cfail2")]
    pub fn bar() {} // but that doesn't matter.

    #[cfg(cfail2)]
    pub fn baz() {} // order is different...

    pub fn bap() {} // neither does adding a new item
}
<|endoftext|>


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

//! This module contains the public APIs supported by the bytecode verifier.
use crate::binary_views::BinaryIndexedView;
use diem_types::vm_status::StatusCode;
use move_core_types::{identifier::Identifier, language_storage::ModuleId};
use std::collections::{BTreeMap, HashMap};
use vm::{
    access::{ModuleAccess, ScriptAccess},
    errors::{verification_error, Location, PartialVMError, PartialVMResult, VMResult},
    file_format::{
        CompiledModule, CompiledScript, FunctionHandle, FunctionHandleIndex, ModuleHandle,
        ModuleHandleIndex, SignatureToken, StructHandle, StructHandleIndex, TableIndex,
    },
    IndexKind,
};

pub struct DependencyChecker<'a> {
    resolver: BinaryIndexedView<'a>,
    // (Module -> CompiledModule) for (at least) all immediate dependencies
    dependency_map: BTreeMap<ModuleId, &'a CompiledModule>,
    // (Module::StructName -> handle) for all types of all dependencies
    struct_id_to_handle_map: HashMap<(ModuleId, Identifier), StructHandleIndex>,
    // (Module::FunctionName -> handle) for all public functions of all dependencies
    func_id_to_handle_map: HashMap<(ModuleId, Identifier), FunctionHandleIndex>,
}

impl<'a> DependencyChecker<'a> {
    pub fn verify_module(
        module: &'a CompiledModule,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> VMResult<()> {
        Self::verify_module_impl(module, dependencies)
            .map_err(|e| e.finish(Location::Module(module.self_id())))
    }

    fn verify_module_impl(
        module: &'a CompiledModule,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> PartialVMResult<()> {
        let module_id = module.self_id();
        let mut dependency_map = BTreeMap::new();
        for dependency in dependencies {
            let dependency_id = dependency.self_id();
            if module_id != dependency_id {
                dependency_map.insert(dependency_id, dependency);
            }
        }

        let mut checker = Self {
            resolver: BinaryIndexedView::Module(module),
            dependency_map,
            struct_id_to_handle_map: HashMap::new(),
            func_id_to_handle_map: HashMap::new(),
        };
        checker.build_deps_entry_point();

        // verify dependencies
        checker.verify_imported_modules(module.module_handles(), Some(module.self_handle_idx()))?;
        checker.verify_imported_structs(module.struct_handles(), Some(module.self_handle_idx()))?;
        checker.verify_imported_functions(module.function_handles(), Some(module.self_handle_idx()))
    }

    pub fn verify_script(
        script: &'a CompiledScript,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> VMResult<()> {
        Self::verify_script_impl(script, dependencies).map_err(|e| e.finish(Location::Script))
    }

    pub fn verify_script_impl(
        script: &'a CompiledScript,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> PartialVMResult<()> {
        let mut dependency_map = BTreeMap::new();
        for dependency in dependencies {
            let dependency_id = dependency.self_id();
            dependency_map.insert(dependency_id, dependency);
        }
        let mut checker = Self {
            resolver: BinaryIndexedView::Script(script),
            dependency_map,
            struct_id_to_handle_map: HashMap::new(),
            func_id_to_handle_map: HashMap::new(),
        };
        checker.build_deps_entry_point();

        checker.verify_imported_modules(script.module_handles(), None)?;
        checker.verify_imported_structs(script.struct_handles(), None)?;
        checker.verify_imported_functions(script.function_handles(), None)
    }

    fn build_deps_entry_point(&mut self) {
        for (module_id, module) in &self.dependency_map {
            // Module::StructName -> def handle idx
            for struct_def in module.struct_defs() {
                let struct_handle = module.struct_handle_at(struct_def.struct_handle);
                let struct_name = module.identifier_at(struct_handle.name);
                self.struct_id_to_handle_map.insert(
                    (module_id.clone(), struct_name.to_owned()),
                    struct_def.struct_handle,
                );
            }
            // Module::FuncName -> def handle idx
            for func_def in module.function_defs() {
                if !func_def.is_public {
                    continue;
                }
                let func_handle = module.function_handle_at(func_def.function);
                let func_name = module.identifier_at(func_handle.name);
                self.func_id_to_handle_map
                    .insert((module_id.clone(), func_name.to_owned()), func_def.function);
            }
        }
    }

    fn verify_imported_modules(
        &self,
        module_handles: &[ModuleHandle],
        self_module: Option<ModuleHandleIndex>,
    ) -> PartialVMResult<()> {
        for (idx, module_handle) in module_handles.iter().enumerate() {
            let module_id = self.resolver.module_id_for_handle(module_handle);
            if Some(ModuleHandleIndex(idx as u16)) != self_module
                && !self.dependency_map.contains_key(&module_id)
            {
                return Err(verification_error(
                    StatusCode::MISSING_DEPENDENCY,
                    IndexKind::ModuleHandle,
                    idx as TableIndex,
                ));
            }
        }
        Ok(())
    }

    fn verify_imported_structs(
        &self,
        struct_handles: &[StructHandle],
        self_module: Option<ModuleHandleIndex>,
    ) -> PartialVMResult<()> {
        for (idx, struct_handle) in struct_handles.iter().enumerate() {
            if Some(struct_handle.module) == self_module {
                continue;
            }
            let owner_module_id = self
                .resolver
                .module_id_for_handle(self.resolver.module_handle_at(struct_handle.module));
            // TODO: remove unwrap
            let owner_module = self.dependency_map.get(&owner_module_id).unwrap();
            let struct_name = self.resolver.identifier_at(struct_handle.name);
            match self
                .struct_id_to_handle_map
                .get(&(owner_module_id, struct_name.to_owned()))
            {
                Some(def_idx) => {
                    let def_handle = owner_module.struct_handle_at(*def_idx);
                    if struct_handle.is_nominal_resource != def_handle.is_nominal_resource
                        || struct_handle.type_parameters != def_handle.type_parameters
                    {
                        return Err(verification_error(
                            StatusCode::TYPE_MISMATCH,
                            IndexKind::StructHandle,
                            idx as TableIndex,
                        ));
                    }
                }
                None => {
                    return Err(verification_error(
                        StatusCode::LOOKUP_FAILED,
                        IndexKind::StructHandle,
                        idx as TableIndex,
                    ))
                }
            }
        }
        Ok(())
    }

    fn verify_imported_functions(
        &self,
        function_handles: &[FunctionHandle],
        self_module: Option<ModuleHandleIndex>,
    ) -> PartialVMResult<()> {
        for (idx, function_handle) in function_handles.iter().enumerate() {
            if Some(function_handle.module) == self_module {
                continue;
            }
            let owner_module_id = self
                .resolver
                .module_id_for_handle(self.resolver.module_handle_at(function_handle.module));
            let function_name = self.resolver.identifier_at(function_handle.name);
            // TODO: remove unwrap
            let owner_module = self.dependency_map.get(&owner_module_id).unwrap();
            match self
                .func_id_to_handle_map
                .get(&(owner_module_id.clone(), function_name.to_owned()))
            {
                Some(def_idx) => {
                    let def_handle = owner_module.function_handle_at(*def_idx);
                    // same type parameter constraints
                    if function_handle.type_parameters != def_handle.type_parameters {
                        return Err(verification_error(
                            StatusCode::TYPE_MISMATCH,
                            IndexKind::FunctionHandle,
                            idx as TableIndex,
                        ));
                    }
                    // same parameters
                    let handle_params = self.resolver.signature_at(function_handle.parameters);
                    let def_params = match self.dependency_map.get(&owner_module_id) {
                        Some(module) => module.signature_at(def_handle.parameters),
                        None => {
                            return Err(verification_error(
                                StatusCode::LOOKUP_FAILED,
                                IndexKind::FunctionHandle,
                                idx as TableIndex,
                            ))
                        }
                    };
                    self.compare_cross_module_signatures(
                        &handle_params.0,
                        &def_params.0,
                        owner_module,
                    )
                    .map_err(|e| e.at_index(IndexKind::FunctionHandle, idx as TableIndex))?;

                    // same return_
                    let handle_return = self.resolver.signature_at(function_handle.return_);
                    let def_return = match self.dependency_map.get(&owner_module_id) {
                        Some(module) => module.signature_at(def_handle.return_),
                        None => {
                            return Err(verification_error(
                                StatusCode::LOOKUP_FAILED,
                                IndexKind::FunctionHandle,
                                idx as TableIndex,
                            ))
                        }
                    };
                    self.compare_cross_module_signatures(
                        &handle_return.0,
                        &def_return.0,
                        owner_module,
                    )
                    .map_err(|e| e.at_index(IndexKind::FunctionHandle, idx as TableIndex))?;
                }
                None => {
                    return Err(verification_error(
                        StatusCode::LOOKUP_FAILED,
                        IndexKind::FunctionHandle,
                        idx as TableIndex,
                    ));
                }
            }
        }
        Ok(())
    }

    pub fn compare_cross_module_signatures(
        &self,
        handle_sig: &[SignatureToken],
        def_sig: &[SignatureToken],
        def_module: &CompiledModule,
    ) -> PartialVMResult<()> {
        if handle_sig.len() != def_sig.len() {
            return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH));
        }
        for (handle_type, def_type) in handle_sig.iter().zip(def_sig) {
            self.compare_types(handle_type, def_type, def_module)?;
        }
        Ok(())
    }

    fn compare_types(
        &self,
        handle_type: &SignatureToken,
        def_type: &SignatureToken,
        def_module: &CompiledModule,
    ) -> PartialVMResult<()> {
        match (handle_type, def_type) {
            (SignatureToken::Bool, SignatureToken::Bool)
            | (SignatureToken::U8, SignatureToken::U8)
            | (SignatureToken::U64, SignatureToken::U64)
            | (SignatureToken::U128, SignatureToken::U128)
            | (SignatureToken::Address, SignatureToken::Address)
            | (SignatureToken::Signer, SignatureToken::Signer) => Ok(()),
            (SignatureToken::Vector(ty1), SignatureToken::Vector(ty2)) => {
                self.compare_types(ty1, ty2, def_module)
            }
            (SignatureToken::Struct(idx1), SignatureToken::Struct(idx2)) => {
                self.compare_structs(*idx1, *idx2, def_module)
            }
            (
                SignatureToken::StructInstantiation(idx1, inst1),
                SignatureToken::StructInstantiation(idx2, inst2),
            ) => {
                self.compare_structs(*idx1, *idx2, def_module)?;
                self.compare_cross_module_signatures(inst1, inst2, def_module)
            }
            (SignatureToken::Reference(ty1), SignatureToken::Reference(ty2))
            | (SignatureToken::MutableReference(ty1), SignatureToken::MutableReference(ty2)) => {
                self.compare_types(ty1, ty2, def_module)
            }
            (SignatureToken::TypeParameter(idx1), SignatureToken::TypeParameter(idx2)) => {
                if idx1 != idx2 {
                    Err(PartialVMError::new(StatusCode::TYPE_MISMATCH))
                } else {
                    Ok(())
                }
            }
            _ => Err(PartialVMError::new(StatusCode::TYPE_MISMATCH)),
        }
    }

    fn compare_structs(
        &self,
        idx1: StructHandleIndex,
        idx2: StructHandleIndex,
        def_module: &CompiledModule,
    ) -> PartialVMResult<()> {
        // grab ModuleId and struct name for the module being verified
        let struct_handle = self.resolver.struct_handle_at(idx1);
        let module_handle = self.resolver.module_handle_at(struct_handle.module);
        let module_id = self.resolver.module_id_for_handle(module_handle);
        let struct_name = self.resolver.identifier_at(struct_handle.name);

        // grab ModuleId and struct name for the definition
        let def_struct_handle = def_module.struct_handle_at(idx2);
        let def_module_handle = def_module.module_handle_at(def_struct_handle.module);
        let def_module_id = def_module.module_id_for_handle(def_module_handle);
        let def_struct_name = def_module.identifier_at(def_struct_handle.name);

        if module_id != def_module_id || struct_name != def_struct_name {
            Err(PartialVMError::new(StatusCode::TYPE_MISMATCH))
        } else {
            Ok(())
        }
    }
}

pub struct CyclicModuleDependencyChecker {}

impl CyclicModuleDependencyChecker {
    pub fn verify_module<F: Fn(&ModuleId) -> Vec<ModuleId>>(
        module: &CompiledModule,
        immediate_module_dependencies: F,
    ) -> VMResult<()> {
        Self::verify_module_impl(module, immediate_module_dependencies)
            .map_err(|e| e.finish(Location::Module(module.self_id())))
    }

    fn verify_module_impl<F: Fn(&ModuleId) -> Vec<ModuleId>>(
        module: &CompiledModule,
        immediate_module_dependencies: F,
    ) -> PartialVMResult<()> {
        // A standard DFS algorithm that follows the dependency chain from the `cursor_module_id`
        // to see whether the `target_module_id` is in its dependencies.
        fn is_reachable_via_dependencies<F: Fn(&ModuleId) -> Vec<ModuleId>>(
            target_module_id: &ModuleId,
            cursor_module_id: &ModuleId,
            immediate_module_dependencies: &F,
        ) -> bool {
            target_module_id == cursor_module_id
                || immediate_module_dependencies(cursor_module_id)
                    .iter()
                    .any(|next| {
                        is_reachable_via_dependencies(
                            target_module_id,
                            next,
                            immediate_module_dependencies,
                        )
                    })
        }

        let target_module_id = module.self_id();
        let has_cyclic_deps = module.immediate_module_dependencies().iter().any(|dep| {
            is_reachable_via_dependencies(&target_module_id, dep, &immediate_module_dependencies)
        });
        if has_cyclic_deps {
            return Err(PartialVMError::new(StatusCode::CYCLIC_MODULE_DEPENDENCY));
        }
        Ok(())
    }
}
<commit_msg>[bytecode-verifier] improve the cyclic dep check algorithm<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! This module contains the public APIs supported by the bytecode verifier.
use crate::binary_views::BinaryIndexedView;
use diem_types::vm_status::StatusCode;
use move_core_types::{identifier::Identifier, language_storage::ModuleId};
use std::collections::{BTreeMap, BTreeSet, HashMap};
use vm::{
    access::{ModuleAccess, ScriptAccess},
    errors::{verification_error, Location, PartialVMError, PartialVMResult, VMResult},
    file_format::{
        CompiledModule, CompiledScript, FunctionHandle, FunctionHandleIndex, ModuleHandle,
        ModuleHandleIndex, SignatureToken, StructHandle, StructHandleIndex, TableIndex,
    },
    IndexKind,
};

pub struct DependencyChecker<'a> {
    resolver: BinaryIndexedView<'a>,
    // (Module -> CompiledModule) for (at least) all immediate dependencies
    dependency_map: BTreeMap<ModuleId, &'a CompiledModule>,
    // (Module::StructName -> handle) for all types of all dependencies
    struct_id_to_handle_map: HashMap<(ModuleId, Identifier), StructHandleIndex>,
    // (Module::FunctionName -> handle) for all public functions of all dependencies
    func_id_to_handle_map: HashMap<(ModuleId, Identifier), FunctionHandleIndex>,
}

impl<'a> DependencyChecker<'a> {
    pub fn verify_module(
        module: &'a CompiledModule,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> VMResult<()> {
        Self::verify_module_impl(module, dependencies)
            .map_err(|e| e.finish(Location::Module(module.self_id())))
    }

    fn verify_module_impl(
        module: &'a CompiledModule,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> PartialVMResult<()> {
        let module_id = module.self_id();
        let mut dependency_map = BTreeMap::new();
        for dependency in dependencies {
            let dependency_id = dependency.self_id();
            if module_id != dependency_id {
                dependency_map.insert(dependency_id, dependency);
            }
        }

        let mut checker = Self {
            resolver: BinaryIndexedView::Module(module),
            dependency_map,
            struct_id_to_handle_map: HashMap::new(),
            func_id_to_handle_map: HashMap::new(),
        };
        checker.build_deps_entry_point();

        // verify dependencies
        checker.verify_imported_modules(module.module_handles(), Some(module.self_handle_idx()))?;
        checker.verify_imported_structs(module.struct_handles(), Some(module.self_handle_idx()))?;
        checker.verify_imported_functions(module.function_handles(), Some(module.self_handle_idx()))
    }

    pub fn verify_script(
        script: &'a CompiledScript,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> VMResult<()> {
        Self::verify_script_impl(script, dependencies).map_err(|e| e.finish(Location::Script))
    }

    pub fn verify_script_impl(
        script: &'a CompiledScript,
        dependencies: impl IntoIterator<Item = &'a CompiledModule>,
    ) -> PartialVMResult<()> {
        let mut dependency_map = BTreeMap::new();
        for dependency in dependencies {
            let dependency_id = dependency.self_id();
            dependency_map.insert(dependency_id, dependency);
        }
        let mut checker = Self {
            resolver: BinaryIndexedView::Script(script),
            dependency_map,
            struct_id_to_handle_map: HashMap::new(),
            func_id_to_handle_map: HashMap::new(),
        };
        checker.build_deps_entry_point();

        checker.verify_imported_modules(script.module_handles(), None)?;
        checker.verify_imported_structs(script.struct_handles(), None)?;
        checker.verify_imported_functions(script.function_handles(), None)
    }

    fn build_deps_entry_point(&mut self) {
        for (module_id, module) in &self.dependency_map {
            // Module::StructName -> def handle idx
            for struct_def in module.struct_defs() {
                let struct_handle = module.struct_handle_at(struct_def.struct_handle);
                let struct_name = module.identifier_at(struct_handle.name);
                self.struct_id_to_handle_map.insert(
                    (module_id.clone(), struct_name.to_owned()),
                    struct_def.struct_handle,
                );
            }
            // Module::FuncName -> def handle idx
            for func_def in module.function_defs() {
                if !func_def.is_public {
                    continue;
                }
                let func_handle = module.function_handle_at(func_def.function);
                let func_name = module.identifier_at(func_handle.name);
                self.func_id_to_handle_map
                    .insert((module_id.clone(), func_name.to_owned()), func_def.function);
            }
        }
    }

    fn verify_imported_modules(
        &self,
        module_handles: &[ModuleHandle],
        self_module: Option<ModuleHandleIndex>,
    ) -> PartialVMResult<()> {
        for (idx, module_handle) in module_handles.iter().enumerate() {
            let module_id = self.resolver.module_id_for_handle(module_handle);
            if Some(ModuleHandleIndex(idx as u16)) != self_module
                && !self.dependency_map.contains_key(&module_id)
            {
                return Err(verification_error(
                    StatusCode::MISSING_DEPENDENCY,
                    IndexKind::ModuleHandle,
                    idx as TableIndex,
                ));
            }
        }
        Ok(())
    }

    fn verify_imported_structs(
        &self,
        struct_handles: &[StructHandle],
        self_module: Option<ModuleHandleIndex>,
    ) -> PartialVMResult<()> {
        for (idx, struct_handle) in struct_handles.iter().enumerate() {
            if Some(struct_handle.module) == self_module {
                continue;
            }
            let owner_module_id = self
                .resolver
                .module_id_for_handle(self.resolver.module_handle_at(struct_handle.module));
            // TODO: remove unwrap
            let owner_module = self.dependency_map.get(&owner_module_id).unwrap();
            let struct_name = self.resolver.identifier_at(struct_handle.name);
            match self
                .struct_id_to_handle_map
                .get(&(owner_module_id, struct_name.to_owned()))
            {
                Some(def_idx) => {
                    let def_handle = owner_module.struct_handle_at(*def_idx);
                    if struct_handle.is_nominal_resource != def_handle.is_nominal_resource
                        || struct_handle.type_parameters != def_handle.type_parameters
                    {
                        return Err(verification_error(
                            StatusCode::TYPE_MISMATCH,
                            IndexKind::StructHandle,
                            idx as TableIndex,
                        ));
                    }
                }
                None => {
                    return Err(verification_error(
                        StatusCode::LOOKUP_FAILED,
                        IndexKind::StructHandle,
                        idx as TableIndex,
                    ))
                }
            }
        }
        Ok(())
    }

    fn verify_imported_functions(
        &self,
        function_handles: &[FunctionHandle],
        self_module: Option<ModuleHandleIndex>,
    ) -> PartialVMResult<()> {
        for (idx, function_handle) in function_handles.iter().enumerate() {
            if Some(function_handle.module) == self_module {
                continue;
            }
            let owner_module_id = self
                .resolver
                .module_id_for_handle(self.resolver.module_handle_at(function_handle.module));
            let function_name = self.resolver.identifier_at(function_handle.name);
            // TODO: remove unwrap
            let owner_module = self.dependency_map.get(&owner_module_id).unwrap();
            match self
                .func_id_to_handle_map
                .get(&(owner_module_id.clone(), function_name.to_owned()))
            {
                Some(def_idx) => {
                    let def_handle = owner_module.function_handle_at(*def_idx);
                    // same type parameter constraints
                    if function_handle.type_parameters != def_handle.type_parameters {
                        return Err(verification_error(
                            StatusCode::TYPE_MISMATCH,
                            IndexKind::FunctionHandle,
                            idx as TableIndex,
                        ));
                    }
                    // same parameters
                    let handle_params = self.resolver.signature_at(function_handle.parameters);
                    let def_params = match self.dependency_map.get(&owner_module_id) {
                        Some(module) => module.signature_at(def_handle.parameters),
                        None => {
                            return Err(verification_error(
                                StatusCode::LOOKUP_FAILED,
                                IndexKind::FunctionHandle,
                                idx as TableIndex,
                            ))
                        }
                    };
                    self.compare_cross_module_signatures(
                        &handle_params.0,
                        &def_params.0,
                        owner_module,
                    )
                    .map_err(|e| e.at_index(IndexKind::FunctionHandle, idx as TableIndex))?;

                    // same return_
                    let handle_return = self.resolver.signature_at(function_handle.return_);
                    let def_return = match self.dependency_map.get(&owner_module_id) {
                        Some(module) => module.signature_at(def_handle.return_),
                        None => {
                            return Err(verification_error(
                                StatusCode::LOOKUP_FAILED,
                                IndexKind::FunctionHandle,
                                idx as TableIndex,
                            ))
                        }
                    };
                    self.compare_cross_module_signatures(
                        &handle_return.0,
                        &def_return.0,
                        owner_module,
                    )
                    .map_err(|e| e.at_index(IndexKind::FunctionHandle, idx as TableIndex))?;
                }
                None => {
                    return Err(verification_error(
                        StatusCode::LOOKUP_FAILED,
                        IndexKind::FunctionHandle,
                        idx as TableIndex,
                    ));
                }
            }
        }
        Ok(())
    }

    pub fn compare_cross_module_signatures(
        &self,
        handle_sig: &[SignatureToken],
        def_sig: &[SignatureToken],
        def_module: &CompiledModule,
    ) -> PartialVMResult<()> {
        if handle_sig.len() != def_sig.len() {
            return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH));
        }
        for (handle_type, def_type) in handle_sig.iter().zip(def_sig) {
            self.compare_types(handle_type, def_type, def_module)?;
        }
        Ok(())
    }

    fn compare_types(
        &self,
        handle_type: &SignatureToken,
        def_type: &SignatureToken,
        def_module: &CompiledModule,
    ) -> PartialVMResult<()> {
        match (handle_type, def_type) {
            (SignatureToken::Bool, SignatureToken::Bool)
            | (SignatureToken::U8, SignatureToken::U8)
            | (SignatureToken::U64, SignatureToken::U64)
            | (SignatureToken::U128, SignatureToken::U128)
            | (SignatureToken::Address, SignatureToken::Address)
            | (SignatureToken::Signer, SignatureToken::Signer) => Ok(()),
            (SignatureToken::Vector(ty1), SignatureToken::Vector(ty2)) => {
                self.compare_types(ty1, ty2, def_module)
            }
            (SignatureToken::Struct(idx1), SignatureToken::Struct(idx2)) => {
                self.compare_structs(*idx1, *idx2, def_module)
            }
            (
                SignatureToken::StructInstantiation(idx1, inst1),
                SignatureToken::StructInstantiation(idx2, inst2),
            ) => {
                self.compare_structs(*idx1, *idx2, def_module)?;
                self.compare_cross_module_signatures(inst1, inst2, def_module)
            }
            (SignatureToken::Reference(ty1), SignatureToken::Reference(ty2))
            | (SignatureToken::MutableReference(ty1), SignatureToken::MutableReference(ty2)) => {
                self.compare_types(ty1, ty2, def_module)
            }
            (SignatureToken::TypeParameter(idx1), SignatureToken::TypeParameter(idx2)) => {
                if idx1 != idx2 {
                    Err(PartialVMError::new(StatusCode::TYPE_MISMATCH))
                } else {
                    Ok(())
                }
            }
            _ => Err(PartialVMError::new(StatusCode::TYPE_MISMATCH)),
        }
    }

    fn compare_structs(
        &self,
        idx1: StructHandleIndex,
        idx2: StructHandleIndex,
        def_module: &CompiledModule,
    ) -> PartialVMResult<()> {
        // grab ModuleId and struct name for the module being verified
        let struct_handle = self.resolver.struct_handle_at(idx1);
        let module_handle = self.resolver.module_handle_at(struct_handle.module);
        let module_id = self.resolver.module_id_for_handle(module_handle);
        let struct_name = self.resolver.identifier_at(struct_handle.name);

        // grab ModuleId and struct name for the definition
        let def_struct_handle = def_module.struct_handle_at(idx2);
        let def_module_handle = def_module.module_handle_at(def_struct_handle.module);
        let def_module_id = def_module.module_id_for_handle(def_module_handle);
        let def_struct_name = def_module.identifier_at(def_struct_handle.name);

        if module_id != def_module_id || struct_name != def_struct_name {
            Err(PartialVMError::new(StatusCode::TYPE_MISMATCH))
        } else {
            Ok(())
        }
    }
}

pub struct CyclicModuleDependencyChecker {}

impl CyclicModuleDependencyChecker {
    pub fn verify_module<F: Fn(&ModuleId) -> Vec<ModuleId>>(
        module: &CompiledModule,
        immediate_module_dependencies: F,
    ) -> VMResult<()> {
        Self::verify_module_impl(module, immediate_module_dependencies)
            .map_err(|e| e.finish(Location::Module(module.self_id())))
    }

    fn verify_module_impl<F: Fn(&ModuleId) -> Vec<ModuleId>>(
        module: &CompiledModule,
        immediate_module_dependencies: F,
    ) -> PartialVMResult<()> {
        // A variant of the three-color DFS, visit each node at max once
        fn dfs_visit<F: Fn(&ModuleId) -> Vec<ModuleId>>(
            cursor_module: ModuleId,
            modules_discovered: &mut BTreeSet<ModuleId>,
            modules_finished: &mut BTreeSet<ModuleId>,
            immediate_module_dependencies: &F,
        ) -> bool {
            modules_discovered.insert(cursor_module.clone());
            for next_module in immediate_module_dependencies(&cursor_module) {
                if modules_discovered.contains(&next_module) {
                    return true;
                }
                if !modules_finished.contains(&next_module)
                    && dfs_visit(
                        next_module,
                        modules_discovered,
                        modules_finished,
                        immediate_module_dependencies,
                    )
                {
                    return true;
                }
            }
            modules_discovered.remove(&cursor_module);
            modules_finished.insert(cursor_module);
            false
        }

        let mut modules_discovered = BTreeSet::new();
        let mut modules_finished = BTreeSet::new();

        modules_discovered.insert(module.self_id());
        for dep in module.immediate_module_dependencies() {
            if dfs_visit(
                dep,
                &mut modules_discovered,
                &mut modules_finished,
                &immediate_module_dependencies,
            ) {
                return Err(PartialVMError::new(StatusCode::CYCLIC_MODULE_DEPENDENCY));
            }
        }

        Ok(())
    }
}
<|endoftext|>