<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::fetcher::{Fetcher, NULL_FETCHER};
use crate::headers::Headers;
use crate::http::{HttpRequest, HttpResponse, Method};
use crate::http_cache::{HttpCache, NullCache};
use crate::utils::{console_log, get_sha, signed_headers_and_payload};
use anyhow::{anyhow, Error, Result};
use async_trait::async_trait;
use once_cell::sync::Lazy;
use std::collections::BTreeSet;
use url::Url;

#[async_trait(?Send)]
pub trait HeaderIntegrityFetcher {
    async fn fetch(&self, url: &str) -> Result<String>;
}

pub fn new_fetcher<F: Fetcher, C: HttpCache>(
    subresource_fetcher: F,
    header_integrity_cache: C,
    strip_response_headers: &'_ BTreeSet<String>,
) -> HeaderIntegrityFetcherImpl<'_, F, C> {
    HeaderIntegrityFetcherImpl {
        subresource_fetcher,
        header_integrity_cache,
        strip_response_headers,
    }
}

pub struct HeaderIntegrityFetcherImpl<'a, F: Fetcher, C: HttpCache> {
    subresource_fetcher: F,
    header_integrity_cache: C,
    strip_response_headers: &'a BTreeSet<String>,
}

// A synthesized error response that can be cached, to prevent overloading the
// origin for non-SXG subresources that it preloads.
static ERROR_RESPONSE: Lazy<HttpResponse> = Lazy::new(|| HttpResponse {
    body: vec![],
    headers: vec![("cache-control".into(), "max-age=3600".into())],
    status: 406,
});

#[async_trait(?Send)]
impl<'a, F: Fetcher, C: HttpCache> HeaderIntegrityFetcher for HeaderIntegrityFetcherImpl<'a, F, C> {
    async fn fetch(&self, url: &str) -> Result<String> {
        let integrity_response = match self.cache_get(url).await {
            // Use cached header-integrity.
            Ok(response @ HttpResponse { status: 200, .. }) => response,
            // Respect the cached error status; don't fetch from origin.
            Ok(response @ HttpResponse { status: 406, .. }) => {
                console_log(&format!(
                    "Cached header-integrity error for {}; not refetching for up to an hour: {}",
                    url,
                    String::from_utf8_lossy(&response.body),
                ));
                response
            }
            // Cache miss or error fetching from cache; fall back to origin.
            _ => {
                console_log(&format!(
                    "{} not found in header-integrity cache. Fetching.",
                    url
                ));
                let response = match self.fetch_subresource(url).await {
                    Ok(response) => {
                        match self.compute_integrity(url, &response).await {
                            Ok(integrity) => {
                                // Keep original cache-control headers, so the integrity is
                                // up-to-date with the subresource.
                                HttpResponse {
                                    body: integrity,
                                    ..response
                                }
                            }
                            Err(err) => Self::error_response(&format!(
                                "Error computing header-integrity for {}: {:#}",
                                url, err
                            )),
                        }
                    }
                    Err(err) => Self::error_response(&format!(
                        "Error fetching subresource at {}: {:#}",
                        url, err
                    )),
                };
                let _ = self.cache_put(url, &response).await;
                response
            }
        };
        Self::extract_integrity(integrity_response)
    }
}

impl<'a, F: Fetcher, C: HttpCache> HeaderIntegrityFetcherImpl<'a, F, C> {
    async fn cache_get(&self, url: &str) -> Result<HttpResponse> {
        self.header_integrity_cache.get(url).await
    }
    async fn cache_put(&self, url: &str, response: &HttpResponse) -> Result<()> {
        self.header_integrity_cache.put(url, response).await
    }
    /// Fetches unsigned subresource from URL.
    async fn fetch_subresource(&self, url: &str) -> Result<HttpResponse> {
        // A generic Accept header, for use in populating the
        // subresource integrity cache. This will be cached and reused for
        // other clients, so there is no need to proxy the client's accept
        // header.
        const ACCEPT: &str =
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8";
        let request = HttpRequest {
            body: vec![],
            headers: vec![("Accept".into(), ACCEPT.into())],
            method: Method::Get,
            url: url.into(),
        };
        self.subresource_fetcher.fetch(request).await
    }
    // Computes header-integrity of the given unsigned response, per the definition at
    // https://github.com/WICG/webpackage/blob/main/explainers/signed-exchange-subresource-substitution.md#identifying-exactly-one-version-of-a-signed-exchange
    // and the serialization at
    // https://w3c.github.io/webappsec-csp/#grammardef-hash-source. Though it is
    // not specified, the base64 encoding must use the non-websafe alphabet with
    // padding, per this usage:
    // https://source.chromium.org/chromium/chromium/src/+/main:content/browser/web_package/prefetched_signed_exchange_cache.cc;l=616;drc=d6962609965d2b9f804e66792486506de801f46c
    async fn compute_integrity(&self, url: &str, response: &HttpResponse) -> Result<Vec<u8>> {
        let fallback_base =
            Url::parse(url).map_err(|e| Error::new(e).context("parsing fallback URL"))?;
        // TODO: Figure out how to reduce the amount of data cloned.
        let payload_headers = Headers::new(response.headers.clone(), self.strip_response_headers);
        let (signed_headers, _) = signed_headers_and_payload(
            &fallback_base,
            response.status,
            &payload_headers,
            &response.body,
            NULL_FETCHER,
            NullCache {},
            self.strip_response_headers,
        )
        .await?;
        Ok([
            b"sha256-",
            base64::encode(get_sha(&signed_headers)).as_bytes(),
        ]
        .concat())
    }
    fn error_response(msg: &str) -> HttpResponse {
        console_log(msg);
        HttpResponse {
            body: msg.as_bytes().into(),
            ..ERROR_RESPONSE.clone()
        }
    }
    fn extract_integrity(response: HttpResponse) -> Result<String> {
        if response.status == 200 && response.body.is_ascii() {
            String::from_utf8(response.body)
                .map_err(|e| Error::new(e).context("parsing header-integrity as utf8"))
        } else {
            Err(anyhow!("{}", String::from_utf8_lossy(&response.body)))
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use crate::fetcher::{NullFetcher, NULL_FETCHER};
    use crate::http_cache::NullCache;
    use anyhow::{anyhow, Result};
    use std::cell::RefCell;
    use std::collections::HashMap;

    static EMPTY_SET: Lazy<BTreeSet<String>> = Lazy::new(BTreeSet::new);

    // For use in other modules' tests.
    pub fn null_integrity_fetcher() -> HeaderIntegrityFetcherImpl<'static, NullFetcher, NullCache> {
        new_fetcher(NULL_FETCHER, NullCache {}, &*EMPTY_SET)
    }

    const TEST_URL: &str = "https://signed-exchange-testing.dev/sxgs/image.jpg";
    static TEST_RESPONSE: Lazy<HttpResponse> = Lazy::new(|| HttpResponse {
        // The result of `curl $TEST_URL`.
        body: include_bytes!("static/image.jpg").to_vec(),
        // The headers from `dump-signedexchange -payload=false -uri $TEST_URL`.
        headers: vec![("content-type".into(), "image/jpeg".into())],
        status: 200,
    });
    // The result of `dump-signedexchange -headerIntegrity -uri $TEST_URL`.
    const EXPECTED_HEADER_INTEGRITY: &str = "sha256-ypu/jZuGukVK2EEGlEkiN92qQDg3Zw6Fb0kCtees1bo=";

    struct FakeFetcher<'a>(&'a HttpResponse);

    #[async_trait(?Send)]
    impl<'a> Fetcher for FakeFetcher<'a> {
        async fn fetch(&self, _request: HttpRequest) -> Result<HttpResponse> {
            Ok(self.0.clone())
        }
    }

    #[async_std::test]
    async fn computes_integrity() {
        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(
            FakeFetcher(&TEST_RESPONSE),
            NullCache {},
            &strip_response_headers,
        );
        assert_eq!(
            fetcher.fetch(TEST_URL).await.unwrap(),
            EXPECTED_HEADER_INTEGRITY
        );
    }

    // RefCell is good enough for our single-threaded, single-task unit tests, but async_std::Mutex
    // would be necessary for more complex usage.
    struct InMemoryCache<'a>(&'a RefCell<HashMap<String, HttpResponse>>);

    #[async_trait(?Send)]
    impl HttpCache for InMemoryCache<'_> {
        async fn get(&self, url: &str) -> Result<HttpResponse> {
            self.0
                .try_borrow()?
                .get(url)
                .cloned()
                .ok_or_else(|| anyhow!("not found"))
        }
        async fn put(&self, url: &str, response: &HttpResponse) -> Result<()> {
            self.0
                .try_borrow_mut()?
                .insert(url.into(), response.clone());
            Ok(())
        }
    }

    #[async_std::test]
    async fn gets_header_integrity_from_cache() {
        let store = RefCell::new(HashMap::new());
        let cache = InMemoryCache(&store);
        let response = HttpResponse {
            body: b"sha256-blah".to_vec(),
            headers: vec![],
            status: 200,
        };
        let _ = cache.put(TEST_URL, &response).await;

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(FakeFetcher(&TEST_RESPONSE), cache, &strip_response_headers);

        assert_eq!(fetcher.fetch(TEST_URL).await.unwrap(), "sha256-blah",);
    }
    #[async_std::test]
    async fn gets_error_from_cache() {
        let store = RefCell::new(HashMap::new());
        let cache = InMemoryCache(&store);
        let response = HttpResponse {
            body: b"something went wrong".to_vec(),
            headers: vec![],
            status: 406,
        };
        let _ = cache.put(TEST_URL, &response).await;

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(FakeFetcher(&TEST_RESPONSE), cache, &strip_response_headers);

        assert_eq!(
            fetcher.fetch(TEST_URL).await.unwrap_err().to_string(),
            "something went wrong",
        )
    }
    #[async_std::test]
    async fn puts_into_cache() {
        let store = RefCell::new(HashMap::new());

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(
            FakeFetcher(&TEST_RESPONSE),
            InMemoryCache(&store),
            &strip_response_headers,
        );

        let _ = fetcher.fetch(TEST_URL).await;
        assert_eq!(
            store.borrow().get(TEST_URL).unwrap().body,
            EXPECTED_HEADER_INTEGRITY.as_bytes(),
        );
    }
    #[async_std::test]
    async fn out_of_order() {
        use crate::utils::tests::{out_of_order, OutOfOrderState};
        use futures::{
            future::BoxFuture,
            stream::{self, StreamExt},
        };
        struct OutOfOrderCache<F: Fn() -> BoxFuture<'static, Result<HttpResponse>>>(F);
        #[async_trait(?Send)]
        impl<F: Fn() -> BoxFuture<'static, Result<HttpResponse>>> HttpCache for OutOfOrderCache<F> {
            async fn get(&self, url: &str) -> Result<HttpResponse> {
                println!("get: url = {}", url);
                self.0().await
            }
            async fn put(&self, url: &str, _response: &HttpResponse) -> Result<()> {
                println!("put: url = {}", url);
                Ok(())
            }
        }
        let state = OutOfOrderState::new();
        let cache = OutOfOrderCache(|| {
            out_of_order(state.clone(), || {
                Ok(HttpResponse {
                    body: b"sha256-blah".to_vec(),
                    headers: vec![],
                    status: 200,
                })
            })
        });

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(FakeFetcher(&TEST_RESPONSE), cache, &strip_response_headers);

        stream::iter(1..=2)
            .for_each_concurrent(None, |n| {
                println!("fetch #{}", n);
                async {
                    assert_eq!(fetcher.fetch(TEST_URL).await.unwrap(), "sha256-blah");
                }
            })
            .await;
    }
}
<commit_msg>Remove console logs in header integrity fetcher (#195)<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::fetcher::{Fetcher, NULL_FETCHER};
use crate::headers::Headers;
use crate::http::{HttpRequest, HttpResponse, Method};
use crate::http_cache::{HttpCache, NullCache};
use crate::utils::{get_sha, signed_headers_and_payload};
use anyhow::{anyhow, Error, Result};
use async_trait::async_trait;
use once_cell::sync::Lazy;
use std::collections::BTreeSet;
use url::Url;

#[async_trait(?Send)]
pub trait HeaderIntegrityFetcher {
    async fn fetch(&self, url: &str) -> Result<String>;
}

pub fn new_fetcher<F: Fetcher, C: HttpCache>(
    subresource_fetcher: F,
    header_integrity_cache: C,
    strip_response_headers: &'_ BTreeSet<String>,
) -> HeaderIntegrityFetcherImpl<'_, F, C> {
    HeaderIntegrityFetcherImpl {
        subresource_fetcher,
        header_integrity_cache,
        strip_response_headers,
    }
}

pub struct HeaderIntegrityFetcherImpl<'a, F: Fetcher, C: HttpCache> {
    subresource_fetcher: F,
    header_integrity_cache: C,
    strip_response_headers: &'a BTreeSet<String>,
}

// A synthesized error response that can be cached, to prevent overloading the
// origin for non-SXG subresources that it preloads.
static ERROR_RESPONSE: Lazy<HttpResponse> = Lazy::new(|| HttpResponse {
    body: vec![],
    headers: vec![("cache-control".into(), "max-age=3600".into())],
    status: 406,
});

#[async_trait(?Send)]
impl<'a, F: Fetcher, C: HttpCache> HeaderIntegrityFetcher for HeaderIntegrityFetcherImpl<'a, F, C> {
    async fn fetch(&self, url: &str) -> Result<String> {
        let integrity_response = match self.cache_get(url).await {
            // Use cached header-integrity.
            Ok(response @ HttpResponse { status: 200, .. }) => response,
            // Respect the cached error status; don't fetch from origin.
            Ok(response @ HttpResponse { status: 406, .. }) => response,
            // Cache miss or error fetching from cache; fall back to origin.
            _ => {
                let response = match self.fetch_subresource(url).await {
                    Ok(response) => {
                        match self.compute_integrity(url, &response).await {
                            Ok(integrity) => {
                                // Keep original cache-control headers, so the integrity is
                                // up-to-date with the subresource.
                                HttpResponse {
                                    body: integrity,
                                    ..response
                                }
                            }
                            Err(err) => Self::error_response(&format!(
                                "Error computing header-integrity for {}: {:#}",
                                url, err
                            )),
                        }
                    }
                    Err(err) => Self::error_response(&format!(
                        "Error fetching subresource at {}: {:#}",
                        url, err
                    )),
                };
                let _ = self.cache_put(url, &response).await;
                response
            }
        };
        Self::extract_integrity(integrity_response)
    }
}

impl<'a, F: Fetcher, C: HttpCache> HeaderIntegrityFetcherImpl<'a, F, C> {
    async fn cache_get(&self, url: &str) -> Result<HttpResponse> {
        self.header_integrity_cache.get(url).await
    }
    async fn cache_put(&self, url: &str, response: &HttpResponse) -> Result<()> {
        self.header_integrity_cache.put(url, response).await
    }
    /// Fetches unsigned subresource from URL.
    async fn fetch_subresource(&self, url: &str) -> Result<HttpResponse> {
        // A generic Accept header, for use in populating the
        // subresource integrity cache. This will be cached and reused for
        // other clients, so there is no need to proxy the client's accept
        // header.
        const ACCEPT: &str =
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8";
        let request = HttpRequest {
            body: vec![],
            headers: vec![("Accept".into(), ACCEPT.into())],
            method: Method::Get,
            url: url.into(),
        };
        self.subresource_fetcher.fetch(request).await
    }
    // Computes header-integrity of the given unsigned response, per the definition at
    // https://github.com/WICG/webpackage/blob/main/explainers/signed-exchange-subresource-substitution.md#identifying-exactly-one-version-of-a-signed-exchange
    // and the serialization at
    // https://w3c.github.io/webappsec-csp/#grammardef-hash-source. Though it is
    // not specified, the base64 encoding must use the non-websafe alphabet with
    // padding, per this usage:
    // https://source.chromium.org/chromium/chromium/src/+/main:content/browser/web_package/prefetched_signed_exchange_cache.cc;l=616;drc=d6962609965d2b9f804e66792486506de801f46c
    async fn compute_integrity(&self, url: &str, response: &HttpResponse) -> Result<Vec<u8>> {
        let fallback_base =
            Url::parse(url).map_err(|e| Error::new(e).context("parsing fallback URL"))?;
        // TODO: Figure out how to reduce the amount of data cloned.
        let payload_headers = Headers::new(response.headers.clone(), self.strip_response_headers);
        let (signed_headers, _) = signed_headers_and_payload(
            &fallback_base,
            response.status,
            &payload_headers,
            &response.body,
            NULL_FETCHER,
            NullCache {},
            self.strip_response_headers,
        )
        .await?;
        Ok([
            b"sha256-",
            base64::encode(get_sha(&signed_headers)).as_bytes(),
        ]
        .concat())
    }
    fn error_response(msg: &str) -> HttpResponse {
        HttpResponse {
            body: msg.as_bytes().into(),
            ..ERROR_RESPONSE.clone()
        }
    }
    fn extract_integrity(response: HttpResponse) -> Result<String> {
        if response.status == 200 && response.body.is_ascii() {
            String::from_utf8(response.body)
                .map_err(|e| Error::new(e).context("parsing header-integrity as utf8"))
        } else {
            Err(anyhow!("{}", String::from_utf8_lossy(&response.body)))
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use crate::fetcher::{NullFetcher, NULL_FETCHER};
    use crate::http_cache::NullCache;
    use anyhow::{anyhow, Result};
    use std::cell::RefCell;
    use std::collections::HashMap;

    static EMPTY_SET: Lazy<BTreeSet<String>> = Lazy::new(BTreeSet::new);

    // For use in other modules' tests.
    pub fn null_integrity_fetcher() -> HeaderIntegrityFetcherImpl<'static, NullFetcher, NullCache> {
        new_fetcher(NULL_FETCHER, NullCache {}, &*EMPTY_SET)
    }

    const TEST_URL: &str = "https://signed-exchange-testing.dev/sxgs/image.jpg";
    static TEST_RESPONSE: Lazy<HttpResponse> = Lazy::new(|| HttpResponse {
        // The result of `curl $TEST_URL`.
        body: include_bytes!("static/image.jpg").to_vec(),
        // The headers from `dump-signedexchange -payload=false -uri $TEST_URL`.
        headers: vec![("content-type".into(), "image/jpeg".into())],
        status: 200,
    });
    // The result of `dump-signedexchange -headerIntegrity -uri $TEST_URL`.
    const EXPECTED_HEADER_INTEGRITY: &str = "sha256-ypu/jZuGukVK2EEGlEkiN92qQDg3Zw6Fb0kCtees1bo=";

    struct FakeFetcher<'a>(&'a HttpResponse);

    #[async_trait(?Send)]
    impl<'a> Fetcher for FakeFetcher<'a> {
        async fn fetch(&self, _request: HttpRequest) -> Result<HttpResponse> {
            Ok(self.0.clone())
        }
    }

    #[async_std::test]
    async fn computes_integrity() {
        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(
            FakeFetcher(&TEST_RESPONSE),
            NullCache {},
            &strip_response_headers,
        );
        assert_eq!(
            fetcher.fetch(TEST_URL).await.unwrap(),
            EXPECTED_HEADER_INTEGRITY
        );
    }

    // RefCell is good enough for our single-threaded, single-task unit tests, but async_std::Mutex
    // would be necessary for more complex usage.
    struct InMemoryCache<'a>(&'a RefCell<HashMap<String, HttpResponse>>);

    #[async_trait(?Send)]
    impl HttpCache for InMemoryCache<'_> {
        async fn get(&self, url: &str) -> Result<HttpResponse> {
            self.0
                .try_borrow()?
                .get(url)
                .cloned()
                .ok_or_else(|| anyhow!("not found"))
        }
        async fn put(&self, url: &str, response: &HttpResponse) -> Result<()> {
            self.0
                .try_borrow_mut()?
                .insert(url.into(), response.clone());
            Ok(())
        }
    }

    #[async_std::test]
    async fn gets_header_integrity_from_cache() {
        let store = RefCell::new(HashMap::new());
        let cache = InMemoryCache(&store);
        let response = HttpResponse {
            body: b"sha256-blah".to_vec(),
            headers: vec![],
            status: 200,
        };
        let _ = cache.put(TEST_URL, &response).await;

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(FakeFetcher(&TEST_RESPONSE), cache, &strip_response_headers);

        assert_eq!(fetcher.fetch(TEST_URL).await.unwrap(), "sha256-blah",);
    }
    #[async_std::test]
    async fn gets_error_from_cache() {
        let store = RefCell::new(HashMap::new());
        let cache = InMemoryCache(&store);
        let response = HttpResponse {
            body: b"something went wrong".to_vec(),
            headers: vec![],
            status: 406,
        };
        let _ = cache.put(TEST_URL, &response).await;

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(FakeFetcher(&TEST_RESPONSE), cache, &strip_response_headers);

        assert_eq!(
            fetcher.fetch(TEST_URL).await.unwrap_err().to_string(),
            "something went wrong",
        )
    }
    #[async_std::test]
    async fn puts_into_cache() {
        let store = RefCell::new(HashMap::new());

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(
            FakeFetcher(&TEST_RESPONSE),
            InMemoryCache(&store),
            &strip_response_headers,
        );

        let _ = fetcher.fetch(TEST_URL).await;
        assert_eq!(
            store.borrow().get(TEST_URL).unwrap().body,
            EXPECTED_HEADER_INTEGRITY.as_bytes(),
        );
    }
    #[async_std::test]
    async fn out_of_order() {
        use crate::utils::tests::{out_of_order, OutOfOrderState};
        use futures::{
            future::BoxFuture,
            stream::{self, StreamExt},
        };
        struct OutOfOrderCache<F: Fn() -> BoxFuture<'static, Result<HttpResponse>>>(F);
        #[async_trait(?Send)]
        impl<F: Fn() -> BoxFuture<'static, Result<HttpResponse>>> HttpCache for OutOfOrderCache<F> {
            async fn get(&self, url: &str) -> Result<HttpResponse> {
                println!("get: url = {}", url);
                self.0().await
            }
            async fn put(&self, url: &str, _response: &HttpResponse) -> Result<()> {
                println!("put: url = {}", url);
                Ok(())
            }
        }
        let state = OutOfOrderState::new();
        let cache = OutOfOrderCache(|| {
            out_of_order(state.clone(), || {
                Ok(HttpResponse {
                    body: b"sha256-blah".to_vec(),
                    headers: vec![],
                    status: 200,
                })
            })
        });

        let strip_response_headers = BTreeSet::new();
        let fetcher = new_fetcher(FakeFetcher(&TEST_RESPONSE), cache, &strip_response_headers);

        stream::iter(1..=2)
            .for_each_concurrent(None, |n| {
                println!("fetch #{}", n);
                async {
                    assert_eq!(fetcher.fetch(TEST_URL).await.unwrap(), "sha256-blah");
                }
            })
            .await;
    }
}
<|endoftext|>


<commit_before>extern crate combine;

use std::str::FromStr;

use combine::*;
use combine::combinator::FnParser;
use combine::primitives::{Stream};

use list::{Sym, Node, List};

enum Sign { Pos, Neg }

// fn sign<I>(input: State<I>) -> ParseResult<(Sign, String), I> {
//     let mut parser = choice([token('-'), token('+')]
//     match parser.parse(input) {
//         Ok(("-", _)) => Sign::Pos,
//         Ok(("-")) | Err(_) => Sign::Neg,
//     }
// }

fn digits_to_int(digits : Vec<char>) -> i32
{
    digits.iter().fold(0, |acc, &d| acc * 10 + d.to_digit(10).expect("digit") as i32)
}

fn many_digits<I>() -> FnParser<I, fn (State<I>) -> ParseResult<i32, I>>
where I: Stream<Item=char> {

    fn many_digits_<I>(input: State<I>) -> ParseResult<i32, I>
    where I: Stream<Item=char> {
        many1::<Vec<char>, _>(digit())
            .map(digits_to_int)
            .parse_state(input)
    }

    parser(many_digits_)
}

fn numeric<I>(input: State<I>) -> ParseResult<i32, I> where I: Stream<Item=char> {
    (
        optional(choice([token('+'), token('-')])),
        many_digits()
    )
        .map(|(sign, digits)| -> i32 {
            match sign {
                Some('-') => -digits,
                _ => digits
            }
        })
        .parse_state(input)
}

#[test]
fn test() {
    let result = parser(numeric).parse("1000");
    println!("{:?}", result);
    assert_eq!(result, Ok((1000, "")));
}
<commit_msg>Add symbol parser with combine<commit_after>extern crate combine;

use std::str::FromStr;

use combine::*;
use combine::combinator::FnParser;
use combine::primitives::{Stream};

use list::{Sym, Node, List};

enum Sign { Pos, Neg }

// fn sign<I>(input: State<I>) -> ParseResult<(Sign, String), I> {
//     let mut parser = choice([token('-'), token('+')]
//     match parser.parse(input) {
//         Ok(("-", _)) => Sign::Pos,
//         Ok(("-")) | Err(_) => Sign::Neg,
//     }
// }

fn digits_to_int(digits : Vec<char>) -> i32
{
    digits.iter().fold(0, |acc, &d| acc * 10 + d.to_digit(10).expect("digit") as i32)
}

fn many_digits<I>() -> FnParser<I, fn (State<I>) -> ParseResult<i32, I>>
where I: Stream<Item=char> {

    fn many_digits_<I>(input: State<I>) -> ParseResult<i32, I>
    where I: Stream<Item=char> {
        many1::<Vec<char>, _>(digit())
            .map(digits_to_int)
            .parse_state(input)
    }

    parser(many_digits_)
}

fn numeric<I>(input: State<I>) -> ParseResult<i32, I> where I: Stream<Item=char> {
    (
        optional(choice([token('+'), token('-')])),
        many_digits()
    )
        .map(|(sign, digits)| -> i32 {
            match sign {
                Some('-') => -digits,
                _ => digits
            }
        })
        .parse_state(input)
}

fn symbol<I>(input: State<I>) -> ParseResult<Sym, I> where I: Stream<Item=char> {
    choice([
        token('+'),
        token('-'),
        token('*'),
        token('/')
    ])
        .map(|sym| {
            match sym {
                '+' => Sym::Add,
                '-' => Sym::Sub,
                '*' => Sym::Mul,
                '/' => Sym::Div,
                _ => unreachable!()
            }
        })
        .parse_state(input)
}

#[test]
fn test_numeric() {
    let result = parser(numeric).parse("1000");
    assert_eq!(result, Ok((1000, "")));
}

#[test]
fn test_symbol() {
    let result = parser(symbol).parse("+1");
    assert_eq!(result, Ok((Sym::Add, "1")));

}<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::error::Error;
use std::time::Duration;

use serialport as sp;
use serial_support::errors::*;

/// Struct for containing Port information
struct OpenPort {
  /// The opened serial port
  /// SerialPort is not Sized, so it makes hashmap mad
  /// and so we deal with these shennanigans
  port: RefCell<Box<sp::SerialPort>>,
}

impl OpenPort {
  /// Write data to the serial port
  pub fn write_port(&self, data: &[u8]) -> Result<()> {
    self
      .port
      .borrow_mut()
      .write_all(data)
      .map_err(|err| ErrorKind::Io(err).into())
  }
  /// Read data from the serial port
  pub fn read_port(&self, buff: &mut [u8]) -> Result<usize> {
    self
      .port
      .borrow_mut()
      .read(buff)
      .map_err(|err| ErrorKind::Io(err).into())
  }
}

/// Manages ports and their locks
pub struct PortManager {
  /// Maintains list of ports
  open_ports: HashMap<String, OpenPort>,
}

impl PortManager {
  /// Create a new PortManager instance
  pub fn new() -> PortManager {
    PortManager { open_ports: HashMap::new() }
  }

  /// Has the port been opened
  pub fn is_port_open(&self, port_name: &String) -> bool {
    self.open_ports.contains_key(port_name)
  }

  /// List all serial ports
  pub fn list_ports(&self) -> Result<Vec<sp::SerialPortInfo>> {
    sp::available_ports().map_err(|e| ErrorKind::Serialport(e).into())
  }

  /// Open a port
  pub fn open_port(&mut self, port_name: &String) -> Result<()> {

    if self.is_port_open(port_name) {
      Ok(())
    } else {

      let sp_settings = sp::SerialPortSettings {
        baud_rate: sp::BaudRate::Baud115200,
        data_bits: sp::DataBits::Eight,
        flow_control: sp::FlowControl::None,
        parity: sp::Parity::None,
        // Bug: serialport 1.0.1 has One and Two mixed up
        // FIXME: Will be fixed in next release
        // So here we use Two to select One stop bit
        stop_bits: sp::StopBits::Two,
        timeout: Duration::from_millis(1),
      };

      match sp::open_with_settings(&port_name, &sp_settings) {
        Ok(serial_port) => {
          let open_port = OpenPort { port: RefCell::new(serial_port) };
          self.open_ports.insert(port_name.to_string(), open_port);
          Ok(())
        }
        Err(e) => Err(ErrorKind::Serialport(e).into()),
      }
    }
  }

  pub fn close_port(&mut self, port_name: &String) {
    // This drops the underlying serial port and box
    self.open_ports.remove(port_name);
  }

  /// Write data to the port
  /// To write data to a port the port must have been previously locked by sub_id
  pub fn write_port(&self, port_name: &String, data: &[u8]) -> Result<()> {
    match self.open_ports.get(port_name) {
      None => Err(ErrorKind::OpenPortNotFound(port_name.to_string()).into()),
      Some(p) => p.write_port(data),
    }
  }

  /// Read data from a port into the buffer buff
  /// If successful, returns Ok(usize) which is the number of
  /// bytes read
  pub fn read_port(&self, port_name: &String, buff: &mut [u8]) -> Result<usize> {
    match self.open_ports.get(port_name) {
      None => Err(ErrorKind::OpenPortNotFound(port_name.to_string()).into()),
      Some(p) => p.read_port(buff),
    }
  }

  /// Read all currently open ports, return a hashmap of
  /// ports to Result<Vec<u8>>
  pub fn read_all_ports(&self) -> HashMap<String, Result<Vec<u8>>> {
    let mut buffer = vec![0; 4096];
    let mut map = HashMap::new();
    for port_name in self.open_ports.keys() {
      match self.read_port(port_name, buffer.as_mut_slice()) {
        Ok(bytes_read) => {
          if bytes_read == 0 {
            // EOF
            info!("Received EOF reading from port {}", port_name);
            map.insert(port_name.to_string(),
                       Err(ErrorKind::PortEOFError(port_name.clone()).into()));
          } else {
            let bytes = buffer[0..bytes_read].to_vec();
            map.insert(port_name.to_string(), Ok(bytes));
          }
        }
        Err(e) => {
          debug!("Error {} reading from port {}", e, port_name);
          match e.description() {
            "Operation timed out" => {}
            _ => {
              map.insert(port_name.to_string(), Err(e.into()));
            }
          }

        }
      }
    }
    map
  }

  /// Get a vec of open ports
  pub fn open_ports(&self) -> Vec<String> {
    let mut open_ports = Vec::<String>::new();
    for port_name in self.open_ports.keys() {
      open_ports.push(port_name.clone());
    }
    open_ports
  }
}
<commit_msg>Turn down log spew<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::error::Error;
use std::time::Duration;

use serialport as sp;
use serial_support::errors::*;

/// Struct for containing Port information
struct OpenPort {
  /// The opened serial port
  /// SerialPort is not Sized, so it makes hashmap mad
  /// and so we deal with these shennanigans
  port: RefCell<Box<sp::SerialPort>>,
}

impl OpenPort {
  /// Write data to the serial port
  pub fn write_port(&self, data: &[u8]) -> Result<()> {
    self
      .port
      .borrow_mut()
      .write_all(data)
      .map_err(|err| ErrorKind::Io(err).into())
  }
  /// Read data from the serial port
  pub fn read_port(&self, buff: &mut [u8]) -> Result<usize> {
    self
      .port
      .borrow_mut()
      .read(buff)
      .map_err(|err| ErrorKind::Io(err).into())
  }
}

/// Manages ports and their locks
pub struct PortManager {
  /// Maintains list of ports
  open_ports: HashMap<String, OpenPort>,
}

impl PortManager {
  /// Create a new PortManager instance
  pub fn new() -> PortManager {
    PortManager { open_ports: HashMap::new() }
  }

  /// Has the port been opened
  pub fn is_port_open(&self, port_name: &String) -> bool {
    self.open_ports.contains_key(port_name)
  }

  /// List all serial ports
  pub fn list_ports(&self) -> Result<Vec<sp::SerialPortInfo>> {
    sp::available_ports().map_err(|e| ErrorKind::Serialport(e).into())
  }

  /// Open a port
  pub fn open_port(&mut self, port_name: &String) -> Result<()> {

    if self.is_port_open(port_name) {
      Ok(())
    } else {

      let sp_settings = sp::SerialPortSettings {
        baud_rate: sp::BaudRate::Baud115200,
        data_bits: sp::DataBits::Eight,
        flow_control: sp::FlowControl::None,
        parity: sp::Parity::None,
        // Bug: serialport 1.0.1 has One and Two mixed up
        // FIXME: Will be fixed in next release
        // So here we use Two to select One stop bit
        stop_bits: sp::StopBits::Two,
        timeout: Duration::from_millis(1),
      };

      match sp::open_with_settings(&port_name, &sp_settings) {
        Ok(serial_port) => {
          let open_port = OpenPort { port: RefCell::new(serial_port) };
          self.open_ports.insert(port_name.to_string(), open_port);
          Ok(())
        }
        Err(e) => Err(ErrorKind::Serialport(e).into()),
      }
    }
  }

  pub fn close_port(&mut self, port_name: &String) {
    // This drops the underlying serial port and box
    self.open_ports.remove(port_name);
  }

  /// Write data to the port
  /// To write data to a port the port must have been previously locked by sub_id
  pub fn write_port(&self, port_name: &String, data: &[u8]) -> Result<()> {
    match self.open_ports.get(port_name) {
      None => Err(ErrorKind::OpenPortNotFound(port_name.to_string()).into()),
      Some(p) => p.write_port(data),
    }
  }

  /// Read data from a port into the buffer buff
  /// If successful, returns Ok(usize) which is the number of
  /// bytes read
  pub fn read_port(&self, port_name: &String, buff: &mut [u8]) -> Result<usize> {
    match self.open_ports.get(port_name) {
      None => Err(ErrorKind::OpenPortNotFound(port_name.to_string()).into()),
      Some(p) => p.read_port(buff),
    }
  }

  /// Read all currently open ports, return a hashmap of
  /// ports to Result<Vec<u8>>
  pub fn read_all_ports(&self) -> HashMap<String, Result<Vec<u8>>> {
    let mut buffer = vec![0; 4096];
    let mut map = HashMap::new();
    for port_name in self.open_ports.keys() {
      match self.read_port(port_name, buffer.as_mut_slice()) {
        Ok(bytes_read) => {
          if bytes_read == 0 {
            // EOF
            info!("Received EOF reading from port {}", port_name);
            map.insert(port_name.to_string(),
                       Err(ErrorKind::PortEOFError(port_name.clone()).into()));
          } else {
            let bytes = buffer[0..bytes_read].to_vec();
            map.insert(port_name.to_string(), Ok(bytes));
          }
        }
        Err(e) => {
          // debug!("Error {} reading from port {}", e, port_name);
          match e.description() {
            "Operation timed out" => {}
            _ => {
              map.insert(port_name.to_string(), Err(e.into()));
            }
          }

        }
      }
    }
    map
  }

  /// Get a vec of open ports
  pub fn open_ports(&self) -> Vec<String> {
    let mut open_ports = Vec::<String>::new();
    for port_name in self.open_ports.keys() {
      open_ports.push(port_name.clone());
    }
    open_ports
  }
}
<|endoftext|>


<commit_before>//! Various C++-related types and functions needed for the `cpp_to_rust` project.

use std::ops::{Deref, DerefMut};
use std::ptr;

#[cfg(test)]
mod tests {
    use crate::{CppBox, CppDeletable};
    use std::cell::RefCell;
    use std::rc::Rc;

    struct Struct1 {
        value: Rc<RefCell<i32>>,
    }

    unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
        (*this_ptr).value.borrow_mut().clone_from(&42);
    }

    impl CppDeletable for Struct1 {
        unsafe fn delete(&mut self) {
            struct1_delete(self);
        }
    }

    #[test]
    fn test_drop_calls_deleter() {
        let value1 = Rc::new(RefCell::new(10));
        let mut object1 = Struct1 {
            value: value1.clone(),
        };
        assert!(*value1.borrow() == 10);
        unsafe {
            CppBox::new(&mut object1 as *mut _);
        }
        assert!(*value1.borrow() == 42);
    }
}

/// Indicates that the type can be put into a CppBox.
///
/// Example of implementation:
/// ```
/// use cpp_utils::{CppDeletable};
///
/// struct Struct1;
///
/// unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
///     unimplemented!()
/// }
///
/// impl CppDeletable for Struct1 {
///     unsafe fn delete(&mut self) {
///         struct1_delete(self)
///     }
/// }
/// ```
pub trait CppDeletable: Sized {
    /// Calls C++ `delete x` on `self`.
    unsafe fn delete(&mut self);
}

/// A C++ pointer wrapper to manage deletion of objects.
///
/// Objects of CppBox should be created by calling into_box() for
/// types that implement CppDeletable trait. The object will
/// be deleted when corresponding CppBox is deleted.
pub struct CppBox<T: CppDeletable>(*mut T);

impl<T: CppDeletable> CppBox<T> {
    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(&self) -> *const T {
        self.0
    }
    /// Returns mutable raw pointer to the value in the box.
    pub fn as_mut_ptr(&self) -> *mut T {
        self.0
    }
    /// Returns the pointer that was used to create the object and destroys the box.
    /// The caller of the function becomes the owner of the object and should
    /// ensure that the object will be deleted at some point.
    pub fn into_raw(mut self) -> *mut T {
        let ptr = self.0;
        self.0 = ptr::null_mut();
        ptr
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_ref(&self) -> &T {
        self.0.as_ref().unwrap()
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_mut(&mut self) -> &mut T {
        self.0.as_mut().unwrap()
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T: CppDeletable> CppBox<T> {
    /// Encapsulates the object into a CppBox.
    ///
    /// You should use this function only for
    /// pointers that were created on C++ side and passed through
    /// a FFI boundary to Rust. An object created with C++ `new`
    /// must be deleted using C++ `delete`, which is executed by `CppBox`.
    ///
    /// Do not use this function for objects created in memory managed by Rust.
    /// Any wrapper constructor or function that returns an owned object
    /// is supposed to be deleted using Rust's ownage system and Drop trait.
    ///
    /// Do not use this function for objects that would be deleted by other means.
    /// If another C++ object is the owner of the passed object,
    /// it will attempt to delete it. If `CppBox` containing the object still exists,
    /// it would result in a double deletion, which should never happen.
    ///
    /// Use `CppBox::into_raw` to unwrap the pointer before passing it to
    /// a function that takes ownership of the object.
    ///
    /// It's permitted to put a null pointer into a `CppBox`. Deleter function
    /// will not be called for a null pointer. However, attempting to dereference
    /// a null pointer in a `CppBox`
    /// using `as_ref`, `as_mut`, `deref` or `deref_mut` will result in a panic.
    pub unsafe fn new(ptr: *mut T) -> CppBox<T> {
        CppBox(ptr)
    }
}

impl<T: CppDeletable> Deref for CppBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { self.0.as_ref().unwrap() }
    }
}

impl<T: CppDeletable> DerefMut for CppBox<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { self.0.as_mut().unwrap() }
    }
}

impl<T: CppDeletable> Drop for CppBox<T> {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                T::delete(&mut *self.0);
            }
        }
    }
}

impl<T: CppDeletable> Default for CppBox<T> {
    fn default() -> CppBox<T> {
        CppBox(ptr::null_mut())
    }
}

/// Provides access to C++ `static_cast` conversion from derived class to base class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `StaticCast<T2>` is implemented for `T1`.
///
/// `StaticCast` allows to convert a reference to a class into
/// a reference to a base class.
///
/// `static_cast` and `static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticUpcast<T> {
    /// Convert type of a const reference.
    unsafe fn static_upcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_upcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast<R, T: StaticUpcast<R>>(value: &T) -> ConstPtr<R> {
    value.static_upcast()
}

/// Converts type of a mutable pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast_mut<R, T: StaticUpcast<R>>(value: &mut T) -> Ptr<R> {
    value.static_upcast_mut()
}

/// Provides access to C++ `static_cast` conversion from base class to derived class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `UnsafeStaticCast<T1>` is implemented for `T2`.
///
/// `UnsafeStaticCast` allows to convert a reference to a class into
/// a reference to a derived class without runtime check of the type.
/// Casting from base class type to a derived class type which is
/// not the actual type of the object will result in an invalid reference.
///
/// `unsafe_static_cast` and `unsafe_static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticDowncast<T> {
    /// Convert type of a const reference.
    unsafe fn static_downcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_downcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast<R, T: StaticDowncast<R>>(value: &T) -> ConstPtr<R> {
    value.static_downcast()
}

/// Converts type of a mutable pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast_mut<R, T: StaticDowncast<R>>(value: &mut T) -> Ptr<R> {
    value.static_downcast_mut()
}

/// Provides access to C++ `dynamic_cast` conversion.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `DynamicCast<T1>` is implemented for `T2`.
/// Use `StaticCast` to convert from `T1` to `T2`.
///
/// `DynamicCast` allows to convert a reference to a class into
/// a reference to a derived class with a runtime check of the type.
/// Conversion returns `None` if the object is actually not an instance of
/// the target type.
///
/// `dynamic_cast` and `dynamic_cast_mut` free functions can be used
/// to convert pointer types.
pub trait DynamicCast<T> {
    /// Convert type of a const reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast(&self) -> Option<ConstPtr<T>>;
    /// Convert type of a mutable reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast_mut(&mut self) -> Option<Ptr<T>>;
}

/// Converts type of a const pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R` or an instance of
/// a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.

pub unsafe fn dynamic_cast<R, T: DynamicCast<R>>(value: &T) -> Option<ConstPtr<R>> {
    value.dynamic_cast()
}

/// Converts type of a mutable pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn dynamic_cast_mut<R, T: DynamicCast<R>>(value: &mut T) -> Option<Ptr<R>> {
    value.dynamic_cast_mut()
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ptr<T>(*mut T);

impl<T> Ptr<T> {
    pub unsafe fn new(ptr: *mut T) -> Self {
        Ptr(ptr)
    }

    pub unsafe fn new_option(ptr: *mut T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(Ptr(ptr))
        }
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(&self) -> *const T {
        self.0
    }

    /// Returns mutable raw pointer to the value in the box.
    pub fn as_mut_ptr(&self) -> *mut T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T: CppDeletable> Ptr<T> {
    pub unsafe fn to_box(&self) -> CppBox<T> {
        CppBox::new(self.0)
    }
}

impl<T> Deref for Ptr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        if self.0.is_null() {
            panic!("attempted to deref a null Ptr<T>");
        }
        unsafe { &(*self.0) }
    }
}

impl<T> DerefMut for Ptr<T> {
    fn deref_mut(&mut self) -> &mut T {
        if self.0.is_null() {
            panic!("attempted to deref a null Ptr<T>");
        }
        unsafe { &mut (*self.0) }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ConstPtr<T>(*const T);

impl<T> ConstPtr<T> {
    pub unsafe fn new(ptr: *const T) -> Self {
        ConstPtr(ptr)
    }

    pub unsafe fn new_option(ptr: *const T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(ConstPtr(ptr))
        }
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(&self) -> *const T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T> Deref for ConstPtr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        if self.0.is_null() {
            panic!("attempted to deref a null ConstPtr<T>");
        }
        unsafe { &(*self.0) }
    }
}

#[test]
fn ptr_deref() {
    let mut i = 42;
    unsafe {
        let ptr: Ptr<i32> = Ptr::new(&mut i as *mut i32);
        assert_eq!(*ptr, 42);
    }
}
<commit_msg>remove null pointer check on deref<commit_after>//! Various C++-related types and functions needed for the `cpp_to_rust` project.

use std::ops::{Deref, DerefMut};
use std::ptr;

#[cfg(test)]
mod tests {
    use crate::{CppBox, CppDeletable};
    use std::cell::RefCell;
    use std::rc::Rc;

    struct Struct1 {
        value: Rc<RefCell<i32>>,
    }

    unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
        (*this_ptr).value.borrow_mut().clone_from(&42);
    }

    impl CppDeletable for Struct1 {
        unsafe fn delete(&mut self) {
            struct1_delete(self);
        }
    }

    #[test]
    fn test_drop_calls_deleter() {
        let value1 = Rc::new(RefCell::new(10));
        let mut object1 = Struct1 {
            value: value1.clone(),
        };
        assert!(*value1.borrow() == 10);
        unsafe {
            CppBox::new(&mut object1 as *mut _);
        }
        assert!(*value1.borrow() == 42);
    }
}

/// Indicates that the type can be put into a CppBox.
///
/// Example of implementation:
/// ```
/// use cpp_utils::{CppDeletable};
///
/// struct Struct1;
///
/// unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
///     unimplemented!()
/// }
///
/// impl CppDeletable for Struct1 {
///     unsafe fn delete(&mut self) {
///         struct1_delete(self)
///     }
/// }
/// ```
pub trait CppDeletable: Sized {
    /// Calls C++ `delete x` on `self`.
    unsafe fn delete(&mut self);
}

/// A C++ pointer wrapper to manage deletion of objects.
///
/// Objects of CppBox should be created by calling into_box() for
/// types that implement CppDeletable trait. The object will
/// be deleted when corresponding CppBox is deleted.
pub struct CppBox<T: CppDeletable>(*mut T);

impl<T: CppDeletable> CppBox<T> {
    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(&self) -> *const T {
        self.0
    }
    /// Returns mutable raw pointer to the value in the box.
    pub fn as_mut_ptr(&self) -> *mut T {
        self.0
    }
    /// Returns the pointer that was used to create the object and destroys the box.
    /// The caller of the function becomes the owner of the object and should
    /// ensure that the object will be deleted at some point.
    pub fn into_raw(mut self) -> *mut T {
        let ptr = self.0;
        self.0 = ptr::null_mut();
        ptr
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_ref(&self) -> &T {
        self.0.as_ref().unwrap()
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_mut(&mut self) -> &mut T {
        self.0.as_mut().unwrap()
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T: CppDeletable> CppBox<T> {
    /// Encapsulates the object into a CppBox.
    ///
    /// You should use this function only for
    /// pointers that were created on C++ side and passed through
    /// a FFI boundary to Rust. An object created with C++ `new`
    /// must be deleted using C++ `delete`, which is executed by `CppBox`.
    ///
    /// Do not use this function for objects created in memory managed by Rust.
    /// Any wrapper constructor or function that returns an owned object
    /// is supposed to be deleted using Rust's ownage system and Drop trait.
    ///
    /// Do not use this function for objects that would be deleted by other means.
    /// If another C++ object is the owner of the passed object,
    /// it will attempt to delete it. If `CppBox` containing the object still exists,
    /// it would result in a double deletion, which should never happen.
    ///
    /// Use `CppBox::into_raw` to unwrap the pointer before passing it to
    /// a function that takes ownership of the object.
    ///
    /// It's permitted to put a null pointer into a `CppBox`. Deleter function
    /// will not be called for a null pointer. However, attempting to dereference
    /// a null pointer in a `CppBox`
    /// using `as_ref`, `as_mut`, `deref` or `deref_mut` will result in a panic.
    pub unsafe fn new(ptr: *mut T) -> CppBox<T> {
        CppBox(ptr)
    }
}

impl<T: CppDeletable> Deref for CppBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { self.0.as_ref().unwrap() }
    }
}

impl<T: CppDeletable> DerefMut for CppBox<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { self.0.as_mut().unwrap() }
    }
}

impl<T: CppDeletable> Drop for CppBox<T> {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                T::delete(&mut *self.0);
            }
        }
    }
}

impl<T: CppDeletable> Default for CppBox<T> {
    fn default() -> CppBox<T> {
        CppBox(ptr::null_mut())
    }
}

/// Provides access to C++ `static_cast` conversion from derived class to base class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `StaticCast<T2>` is implemented for `T1`.
///
/// `StaticCast` allows to convert a reference to a class into
/// a reference to a base class.
///
/// `static_cast` and `static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticUpcast<T> {
    /// Convert type of a const reference.
    unsafe fn static_upcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_upcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast<R, T: StaticUpcast<R>>(value: &T) -> ConstPtr<R> {
    value.static_upcast()
}

/// Converts type of a mutable pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast_mut<R, T: StaticUpcast<R>>(value: &mut T) -> Ptr<R> {
    value.static_upcast_mut()
}

/// Provides access to C++ `static_cast` conversion from base class to derived class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `UnsafeStaticCast<T1>` is implemented for `T2`.
///
/// `UnsafeStaticCast` allows to convert a reference to a class into
/// a reference to a derived class without runtime check of the type.
/// Casting from base class type to a derived class type which is
/// not the actual type of the object will result in an invalid reference.
///
/// `unsafe_static_cast` and `unsafe_static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticDowncast<T> {
    /// Convert type of a const reference.
    unsafe fn static_downcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_downcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast<R, T: StaticDowncast<R>>(value: &T) -> ConstPtr<R> {
    value.static_downcast()
}

/// Converts type of a mutable pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast_mut<R, T: StaticDowncast<R>>(value: &mut T) -> Ptr<R> {
    value.static_downcast_mut()
}

/// Provides access to C++ `dynamic_cast` conversion.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `DynamicCast<T1>` is implemented for `T2`.
/// Use `StaticCast` to convert from `T1` to `T2`.
///
/// `DynamicCast` allows to convert a reference to a class into
/// a reference to a derived class with a runtime check of the type.
/// Conversion returns `None` if the object is actually not an instance of
/// the target type.
///
/// `dynamic_cast` and `dynamic_cast_mut` free functions can be used
/// to convert pointer types.
pub trait DynamicCast<T> {
    /// Convert type of a const reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast(&self) -> Option<ConstPtr<T>>;
    /// Convert type of a mutable reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast_mut(&mut self) -> Option<Ptr<T>>;
}

/// Converts type of a const pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R` or an instance of
/// a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.

pub unsafe fn dynamic_cast<R, T: DynamicCast<R>>(value: &T) -> Option<ConstPtr<R>> {
    value.dynamic_cast()
}

/// Converts type of a mutable pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn dynamic_cast_mut<R, T: DynamicCast<R>>(value: &mut T) -> Option<Ptr<R>> {
    value.dynamic_cast_mut()
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ptr<T>(*mut T);

impl<T> Ptr<T> {
    pub unsafe fn new(ptr: *mut T) -> Self {
        Ptr(ptr)
    }

    pub unsafe fn new_option(ptr: *mut T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(Ptr(ptr))
        }
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(&self) -> *const T {
        self.0
    }

    /// Returns mutable raw pointer to the value in the box.
    pub fn as_mut_ptr(&self) -> *mut T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T: CppDeletable> Ptr<T> {
    pub unsafe fn to_box(&self) -> CppBox<T> {
        CppBox::new(self.0)
    }
}

impl<T> Deref for Ptr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &(*self.0) }
    }
}

impl<T> DerefMut for Ptr<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut (*self.0) }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ConstPtr<T>(*const T);

impl<T> ConstPtr<T> {
    pub unsafe fn new(ptr: *const T) -> Self {
        ConstPtr(ptr)
    }

    pub unsafe fn new_option(ptr: *const T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(ConstPtr(ptr))
        }
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(&self) -> *const T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T> Deref for ConstPtr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        if self.0.is_null() {
            panic!("attempted to deref a null ConstPtr<T>");
        }
        unsafe { &(*self.0) }
    }
}

#[test]
fn ptr_deref() {
    let mut i = 42;
    unsafe {
        let ptr: Ptr<i32> = Ptr::new(&mut i as *mut i32);
        assert_eq!(*ptr, 42);
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::HashSet;

use proc_macro2::TokenStream;
use quote::quote;
use syn::{
    parse::Parser,
    parse_quote,
    punctuated::Punctuated,
    token::{Comma, Unsafe},
    Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType,
};

use super::{
    unqualify::{unqualify_params, unqualify_ret_type},
    ImplBlockDetails, RsCodegenResult, TraitImplBlockDetails, Use,
};
use crate::{
    conversion::{
        analysis::fun::{
            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy,
            TraitMethodDetails,
        },
        api::UnsafetyNeeded,
    },
    types::{Namespace, QualifiedName},
};
use crate::{
    conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary},
    types::make_ident,
};

impl UnsafetyNeeded {
    pub(crate) fn bridge_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::None => None,
            _ => Some(parse_quote! { unsafe }),
        }
    }

    pub(crate) fn wrapper_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::Always => Some(parse_quote! { unsafe }),
            _ => None,
        }
    }

    pub(crate) fn from_param_details(params: &[ArgumentAnalysis], ignore_receiver: bool) -> Self {
        params.iter().fold(UnsafetyNeeded::None, |accumulator, pd| {
            if matches!(accumulator, UnsafetyNeeded::Always) {
                UnsafetyNeeded::Always
            } else if pd.self_type.is_some() && ignore_receiver {
                if matches!(
                    pd.requires_unsafe,
                    UnsafetyNeeded::Always | UnsafetyNeeded::JustBridge
                ) {
                    UnsafetyNeeded::JustBridge
                } else {
                    accumulator
                }
            } else if matches!(pd.requires_unsafe, UnsafetyNeeded::Always) {
                UnsafetyNeeded::Always
            } else if matches!(accumulator, UnsafetyNeeded::JustBridge)
                || matches!(pd.requires_unsafe, UnsafetyNeeded::JustBridge)
            {
                UnsafetyNeeded::JustBridge
            } else {
                UnsafetyNeeded::None
            }
        })
    }
}

pub(super) fn gen_function(
    ns: &Namespace,
    fun: FuncToConvert,
    analysis: FnAnalysis,
    cpp_call_name: String,
    non_pod_types: &HashSet<QualifiedName>,
) -> RsCodegenResult {
    if analysis.ignore_reason.is_err() || !analysis.externally_callable {
        return RsCodegenResult::default();
    }
    let cxxbridge_name = analysis.cxxbridge_name;
    let rust_name = &analysis.rust_name;
    let ret_type = analysis.ret_type;
    let param_details = analysis.param_details;
    let wrapper_function_needed = analysis.cpp_wrapper.is_some();
    let params = analysis.params;
    let vis = analysis.vis;
    let kind = analysis.kind;
    let doc_attrs = fun.doc_attrs;

    let mut cpp_name_attr = Vec::new();
    let mut impl_entry = None;
    let mut trait_impl_entry = None;
    let mut bindgen_mod_items = Vec::new();
    let always_unsafe_due_to_trait_definition = match kind {
        FnKind::TraitMethod { ref details, .. } => details.trait_call_is_unsafe,
        _ => false,
    };
    let fn_generator = FnGenerator {
        param_details: &param_details,
        cxxbridge_name: &cxxbridge_name,
        rust_name,
        unsafety: &analysis.requires_unsafe,
        always_unsafe_due_to_trait_definition,
        doc_attrs: &doc_attrs,
        non_pod_types,
    };
    // In rare occasions, we might need to give an explicit lifetime.
    let (lifetime_tokens, params, ret_type) =
        add_explicit_lifetime_if_necessary(&param_details, params, &ret_type, non_pod_types, true);

    if analysis.rust_wrapper_needed {
        match kind {
            FnKind::Method {
                ref impl_for,
                method_kind: MethodKind::Constructor { .. },
                ..
            } => {
                // Constructor.
                impl_entry = Some(fn_generator.generate_constructor_impl(impl_for));
            }
            FnKind::Method {
                ref impl_for,
                ref method_kind,
                ..
            } => {
                // Method, or static method.
                impl_entry = Some(fn_generator.generate_method_impl(
                    matches!(
                        method_kind,
                        MethodKind::MakeUnique | MethodKind::Constructor { .. }
                    ),
                    impl_for,
                    &ret_type,
                    if matches!(method_kind, MethodKind::MakeUnique) {
                        Some("Instead, ensure you have imported autocxx::prelude::* (specifically the Within trait) and then use new().within_unique_ptr()")
                    } else {
                        None
                    }
                ));
            }
            FnKind::TraitMethod { ref details, .. } => {
                trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type));
            }
            _ => {
                // Generate plain old function
                bindgen_mod_items.push(fn_generator.generate_function_impl(&ret_type));
            }
        }
    }

    let materialization = match kind {
        FnKind::Method { .. } | FnKind::TraitMethod { .. } => None,
        FnKind::Function => match analysis.rust_rename_strategy {
            _ if analysis.rust_wrapper_needed => {
                Some(Use::SpecificNameFromBindgen(make_ident(rust_name)))
            }
            RustRenameStrategy::RenameInOutputMod(ref alias) => {
                Some(Use::UsedFromCxxBridgeWithAlias(alias.clone()))
            }
            _ => Some(Use::UsedFromCxxBridge),
        },
    };
    if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
        cpp_name_attr = Attribute::parse_outer
            .parse2(quote!(
                #[cxx_name = #cpp_call_name]
            ))
            .unwrap();
    }

    // Finally - namespace support. All the Types in everything
    // above this point are fully qualified. We need to unqualify them.
    // We need to do that _after_ the above wrapper_function_needed
    // work, because it relies upon spotting fully qualified names like
    // std::unique_ptr. However, after it's done its job, all such
    // well-known types should be unqualified already (e.g. just UniquePtr)
    // and the following code will act to unqualify only those types
    // which the user has declared.
    let params = unqualify_params(params);
    let ret_type = unqualify_ret_type(ret_type.into_owned());
    // And we need to make an attribute for the namespace that the function
    // itself is in.
    let namespace_attr = if ns.is_empty() || wrapper_function_needed {
        Vec::new()
    } else {
        let namespace_string = ns.to_string();
        Attribute::parse_outer
            .parse2(quote!(
                #[namespace = #namespace_string]
            ))
            .unwrap()
    };
    // At last, actually generate the cxx::bridge entry.
    let bridge_unsafety = analysis.requires_unsafe.bridge_token();
    let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
        #(#namespace_attr)*
        #(#cpp_name_attr)*
        #(#doc_attrs)*
        #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type;
    ));
    RsCodegenResult {
        extern_c_mod_items: vec![extern_c_mod_item],
        bindgen_mod_items,
        impl_entry,
        trait_impl_entry,
        materializations: materialization.into_iter().collect(),
        ..Default::default()
    }
}

/// Knows how to generate a given function.
#[derive(Clone)]
struct FnGenerator<'a> {
    param_details: &'a [ArgumentAnalysis],
    cxxbridge_name: &'a Ident,
    rust_name: &'a str,
    unsafety: &'a UnsafetyNeeded,
    always_unsafe_due_to_trait_definition: bool,
    doc_attrs: &'a Vec<Attribute>,
    non_pod_types: &'a HashSet<QualifiedName>,
}

impl<'a> FnGenerator<'a> {
    fn common_parts<'b>(
        &self,
        avoid_self: bool,
        parameter_reordering: &Option<Vec<usize>>,
        ret_type: &'b ReturnType,
    ) -> (
        Option<TokenStream>,
        Punctuated<FnArg, Comma>,
        std::borrow::Cow<'b, ReturnType>,
        TokenStream,
    ) {
        let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new();
        let mut local_variables = Vec::new();
        let mut arg_list = Vec::new();
        let mut ptr_arg_name = None;
        let wrap_unsafe_calls = self.should_wrap_unsafe_calls();
        for pd in self.param_details {
            let type_name = pd.conversion.rust_wrapper_unconverted_type();
            let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
                parse_quote!(self)
            } else {
                pd.name.clone()
            };
            let (local_variable, actual_arg) = pd
                .conversion
                .rust_conversion(wrapper_arg_name.clone(), wrap_unsafe_calls);
            arg_list.push(actual_arg.clone());
            local_variables.extend(local_variable.into_iter());
            if pd.is_placement_return_destination {
                ptr_arg_name = Some(actual_arg);
            } else {
                let param_mutability = pd.conversion.rust_conversion.requires_mutability();
                wrapper_params.push(parse_quote!(
                    #param_mutability #wrapper_arg_name: #type_name
                ));
            }
        }
        let local_variables = quote! { #(#local_variables);* };
        if let Some(parameter_reordering) = &parameter_reordering {
            wrapper_params = Self::reorder_parameters(wrapper_params, parameter_reordering);
        }
        let (lifetime_tokens, wrapper_params, ret_type) = add_explicit_lifetime_if_necessary(
            self.param_details,
            wrapper_params,
            ret_type,
            self.non_pod_types,
            false,
        );

        let cxxbridge_name = self.cxxbridge_name;
        let call_body = quote! {
            cxxbridge::#cxxbridge_name ( #(#arg_list),* )
        };
        let call_body = if let Some(ptr_arg_name) = ptr_arg_name {
            quote! {
                #local_variables
                autocxx::moveit::new::by_raw(move |#ptr_arg_name| {
                    let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr();
                    #call_body
                })
            }
        } else {
            quote! {
                #local_variables
                #call_body
            }
        };
        let call_body = if self.should_wrap_unsafe_calls() {
            quote! {
                unsafe {
                    #call_body
                }
            }
        } else {
            call_body
        };
        (lifetime_tokens, wrapper_params, ret_type, call_body)
    }

    /// Generate an 'impl Type { methods-go-here }' item
    fn generate_method_impl(
        &self,
        avoid_self: bool,
        impl_block_type_name: &QualifiedName,
        ret_type: &ReturnType,
        deprecation: Option<&str>,
    ) -> Box<ImplBlockDetails> {
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(avoid_self, &None, ret_type);
        let rust_name = make_ident(self.rust_name);
        let unsafety = self.unsafety.wrapper_token();
        let doc_attrs = self.doc_attrs;
        let deprecation = deprecation.map(|reason| {
            quote! {
                #[deprecated = #reason]
            }
        });
        Box::new(ImplBlockDetails {
            item: ImplItem::Method(parse_quote! {
                #(#doc_attrs)*
                #deprecation
                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                    #call_body
                }
            }),
            ty: impl_block_type_name.get_final_ident(),
        })
    }

    /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety.
    fn generate_trait_impl(
        &self,
        details: &TraitMethodDetails,
        ret_type: &ReturnType,
    ) -> Box<TraitImplBlockDetails> {
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(details.avoid_self, &details.parameter_reordering, ret_type);
        let doc_attrs = self.doc_attrs;
        let unsafety = self.unsafety.wrapper_token();
        let key = details.trt.clone();
        let method_name = &details.method_name;
        let item = parse_quote! {
            #(#doc_attrs)*
            #unsafety fn #method_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                #call_body
            }
        };
        Box::new(TraitImplBlockDetails { item, key })
    }

    /// Generate a 'impl Type { methods-go-here }' item which is a constructor
    /// for use with moveit traits.
    fn generate_constructor_impl(
        &self,
        impl_block_type_name: &QualifiedName,
    ) -> Box<ImplBlockDetails> {
        let ret_type: ReturnType = parse_quote! { -> impl autocxx::moveit::new::New<Output=Self> };
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(true, &None, &ret_type);
        let rust_name = make_ident(&self.rust_name);
        let doc_attrs = self.doc_attrs;
        let unsafety = self.unsafety.wrapper_token();
        Box::new(ImplBlockDetails {
            item: ImplItem::Method(parse_quote! {
                #(#doc_attrs)*
                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                    #call_body
                }
            }),
            ty: impl_block_type_name.get_final_ident(),
        })
    }

    /// Generate a function call wrapper
    fn generate_function_impl(&self, ret_type: &ReturnType) -> Item {
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(false, &None, ret_type);
        let rust_name = make_ident(self.rust_name);
        let doc_attrs = self.doc_attrs;
        let unsafety = self.unsafety.wrapper_token();
        Item::Fn(parse_quote! {
            #(#doc_attrs)*
            pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                #call_body
            }
        })
    }

    fn should_wrap_unsafe_calls(&self) -> bool {
        matches!(self.unsafety, UnsafetyNeeded::JustBridge)
            || self.always_unsafe_due_to_trait_definition
    }

    fn reorder_parameters(
        params: Punctuated<FnArg, Comma>,
        parameter_ordering: &[usize],
    ) -> Punctuated<FnArg, Comma> {
        let old_params = params.into_iter().collect::<Vec<_>>();
        parameter_ordering
            .iter()
            .map(|n| old_params.get(*n).unwrap().clone())
            .collect()
    }
}
<commit_msg>Sort out unsafety wrapping problems.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::HashSet;

use proc_macro2::TokenStream;
use quote::quote;
use syn::{
    parse::Parser,
    parse_quote,
    punctuated::Punctuated,
    token::{Comma, Unsafe},
    Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType,
};

use super::{
    unqualify::{unqualify_params, unqualify_ret_type},
    ImplBlockDetails, RsCodegenResult, TraitImplBlockDetails, Use,
};
use crate::{
    conversion::{
        analysis::fun::{
            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy,
            TraitMethodDetails,
        },
        api::UnsafetyNeeded,
    },
    types::{Namespace, QualifiedName},
};
use crate::{
    conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary},
    types::make_ident,
};

impl UnsafetyNeeded {
    pub(crate) fn bridge_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::None => None,
            _ => Some(parse_quote! { unsafe }),
        }
    }

    pub(crate) fn wrapper_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::Always => Some(parse_quote! { unsafe }),
            _ => None,
        }
    }

    pub(crate) fn from_param_details(params: &[ArgumentAnalysis], ignore_receiver: bool) -> Self {
        params.iter().fold(UnsafetyNeeded::None, |accumulator, pd| {
            if matches!(accumulator, UnsafetyNeeded::Always) {
                UnsafetyNeeded::Always
            } else if pd.self_type.is_some() && ignore_receiver {
                if matches!(
                    pd.requires_unsafe,
                    UnsafetyNeeded::Always | UnsafetyNeeded::JustBridge
                ) {
                    UnsafetyNeeded::JustBridge
                } else {
                    accumulator
                }
            } else if matches!(pd.requires_unsafe, UnsafetyNeeded::Always) {
                UnsafetyNeeded::Always
            } else if matches!(accumulator, UnsafetyNeeded::JustBridge)
                || matches!(pd.requires_unsafe, UnsafetyNeeded::JustBridge)
            {
                UnsafetyNeeded::JustBridge
            } else {
                UnsafetyNeeded::None
            }
        })
    }
}

pub(super) fn gen_function(
    ns: &Namespace,
    fun: FuncToConvert,
    analysis: FnAnalysis,
    cpp_call_name: String,
    non_pod_types: &HashSet<QualifiedName>,
) -> RsCodegenResult {
    if analysis.ignore_reason.is_err() || !analysis.externally_callable {
        return RsCodegenResult::default();
    }
    let cxxbridge_name = analysis.cxxbridge_name;
    let rust_name = &analysis.rust_name;
    let ret_type = analysis.ret_type;
    let param_details = analysis.param_details;
    let wrapper_function_needed = analysis.cpp_wrapper.is_some();
    let params = analysis.params;
    let vis = analysis.vis;
    let kind = analysis.kind;
    let doc_attrs = fun.doc_attrs;

    let mut cpp_name_attr = Vec::new();
    let mut impl_entry = None;
    let mut trait_impl_entry = None;
    let mut bindgen_mod_items = Vec::new();
    let always_unsafe_due_to_trait_definition = match kind {
        FnKind::TraitMethod { ref details, .. } => details.trait_call_is_unsafe,
        _ => false,
    };
    let fn_generator = FnGenerator {
        param_details: &param_details,
        cxxbridge_name: &cxxbridge_name,
        rust_name,
        unsafety: &analysis.requires_unsafe,
        always_unsafe_due_to_trait_definition,
        doc_attrs: &doc_attrs,
        non_pod_types,
    };
    // In rare occasions, we might need to give an explicit lifetime.
    let (lifetime_tokens, params, ret_type) =
        add_explicit_lifetime_if_necessary(&param_details, params, &ret_type, non_pod_types, true);

    if analysis.rust_wrapper_needed {
        match kind {
            FnKind::Method {
                ref impl_for,
                method_kind: MethodKind::Constructor { .. },
                ..
            } => {
                // Constructor.
                impl_entry = Some(fn_generator.generate_constructor_impl(impl_for));
            }
            FnKind::Method {
                ref impl_for,
                ref method_kind,
                ..
            } => {
                // Method, or static method.
                impl_entry = Some(fn_generator.generate_method_impl(
                    matches!(
                        method_kind,
                        MethodKind::MakeUnique | MethodKind::Constructor { .. }
                    ),
                    impl_for,
                    &ret_type,
                    if matches!(method_kind, MethodKind::MakeUnique) {
                        Some("Instead, ensure you have imported autocxx::prelude::* (specifically the Within trait) and then use new().within_unique_ptr()")
                    } else {
                        None
                    }
                ));
            }
            FnKind::TraitMethod { ref details, .. } => {
                trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type));
            }
            _ => {
                // Generate plain old function
                bindgen_mod_items.push(fn_generator.generate_function_impl(&ret_type));
            }
        }
    }

    let materialization = match kind {
        FnKind::Method { .. } | FnKind::TraitMethod { .. } => None,
        FnKind::Function => match analysis.rust_rename_strategy {
            _ if analysis.rust_wrapper_needed => {
                Some(Use::SpecificNameFromBindgen(make_ident(rust_name)))
            }
            RustRenameStrategy::RenameInOutputMod(ref alias) => {
                Some(Use::UsedFromCxxBridgeWithAlias(alias.clone()))
            }
            _ => Some(Use::UsedFromCxxBridge),
        },
    };
    if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
        cpp_name_attr = Attribute::parse_outer
            .parse2(quote!(
                #[cxx_name = #cpp_call_name]
            ))
            .unwrap();
    }

    // Finally - namespace support. All the Types in everything
    // above this point are fully qualified. We need to unqualify them.
    // We need to do that _after_ the above wrapper_function_needed
    // work, because it relies upon spotting fully qualified names like
    // std::unique_ptr. However, after it's done its job, all such
    // well-known types should be unqualified already (e.g. just UniquePtr)
    // and the following code will act to unqualify only those types
    // which the user has declared.
    let params = unqualify_params(params);
    let ret_type = unqualify_ret_type(ret_type.into_owned());
    // And we need to make an attribute for the namespace that the function
    // itself is in.
    let namespace_attr = if ns.is_empty() || wrapper_function_needed {
        Vec::new()
    } else {
        let namespace_string = ns.to_string();
        Attribute::parse_outer
            .parse2(quote!(
                #[namespace = #namespace_string]
            ))
            .unwrap()
    };
    // At last, actually generate the cxx::bridge entry.
    let bridge_unsafety = analysis.requires_unsafe.bridge_token();
    let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
        #(#namespace_attr)*
        #(#cpp_name_attr)*
        #(#doc_attrs)*
        #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type;
    ));
    RsCodegenResult {
        extern_c_mod_items: vec![extern_c_mod_item],
        bindgen_mod_items,
        impl_entry,
        trait_impl_entry,
        materializations: materialization.into_iter().collect(),
        ..Default::default()
    }
}

/// Knows how to generate a given function.
#[derive(Clone)]
struct FnGenerator<'a> {
    param_details: &'a [ArgumentAnalysis],
    cxxbridge_name: &'a Ident,
    rust_name: &'a str,
    unsafety: &'a UnsafetyNeeded,
    always_unsafe_due_to_trait_definition: bool,
    doc_attrs: &'a Vec<Attribute>,
    non_pod_types: &'a HashSet<QualifiedName>,
}

impl<'a> FnGenerator<'a> {
    fn common_parts<'b>(
        &self,
        avoid_self: bool,
        parameter_reordering: &Option<Vec<usize>>,
        ret_type: &'b ReturnType,
    ) -> (
        Option<TokenStream>,
        Punctuated<FnArg, Comma>,
        std::borrow::Cow<'b, ReturnType>,
        TokenStream,
    ) {
        let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new();
        let mut local_variables = Vec::new();
        let mut arg_list = Vec::new();
        let mut ptr_arg_name = None;
        let wrap_unsafe_calls = self.should_wrap_unsafe_calls();
        for pd in self.param_details {
            let type_name = pd.conversion.rust_wrapper_unconverted_type();
            let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
                parse_quote!(self)
            } else {
                pd.name.clone()
            };
            let (local_variable, actual_arg) = pd
                .conversion
                .rust_conversion(wrapper_arg_name.clone(), wrap_unsafe_calls);
            arg_list.push(actual_arg.clone());
            local_variables.extend(local_variable.into_iter());
            if pd.is_placement_return_destination {
                ptr_arg_name = Some(actual_arg);
            } else {
                let param_mutability = pd.conversion.rust_conversion.requires_mutability();
                wrapper_params.push(parse_quote!(
                    #param_mutability #wrapper_arg_name: #type_name
                ));
            }
        }
        let local_variables = quote! { #(#local_variables);* };
        if let Some(parameter_reordering) = &parameter_reordering {
            wrapper_params = Self::reorder_parameters(wrapper_params, parameter_reordering);
        }
        let (lifetime_tokens, wrapper_params, ret_type) = add_explicit_lifetime_if_necessary(
            self.param_details,
            wrapper_params,
            ret_type,
            self.non_pod_types,
            false,
        );

        let cxxbridge_name = self.cxxbridge_name;
        let call_body = quote! {
            cxxbridge::#cxxbridge_name ( #(#arg_list),* )
        };
        let call_body = if let Some(ptr_arg_name) = ptr_arg_name {
            quote! {
                autocxx::moveit::new::by_raw(move |#ptr_arg_name| {
                    let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr();
                    #call_body
                })
            }
        } else {
            quote! {
                #call_body
            }
        };
        let call_body = if self.should_wrap_unsafe_calls() {
            quote! {
                unsafe {
                    #call_body
                }
            }
        } else {
            call_body
        };
        let call_body = quote! {
            #local_variables
            #call_body
        };
        (lifetime_tokens, wrapper_params, ret_type, call_body)
    }

    /// Generate an 'impl Type { methods-go-here }' item
    fn generate_method_impl(
        &self,
        avoid_self: bool,
        impl_block_type_name: &QualifiedName,
        ret_type: &ReturnType,
        deprecation: Option<&str>,
    ) -> Box<ImplBlockDetails> {
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(avoid_self, &None, ret_type);
        let rust_name = make_ident(self.rust_name);
        let unsafety = self.unsafety.wrapper_token();
        let doc_attrs = self.doc_attrs;
        let deprecation = deprecation.map(|reason| {
            quote! {
                #[deprecated = #reason]
            }
        });
        Box::new(ImplBlockDetails {
            item: ImplItem::Method(parse_quote! {
                #(#doc_attrs)*
                #deprecation
                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                    #call_body
                }
            }),
            ty: impl_block_type_name.get_final_ident(),
        })
    }

    /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety.
    fn generate_trait_impl(
        &self,
        details: &TraitMethodDetails,
        ret_type: &ReturnType,
    ) -> Box<TraitImplBlockDetails> {
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(details.avoid_self, &details.parameter_reordering, ret_type);
        let doc_attrs = self.doc_attrs;
        let unsafety = self.unsafety.wrapper_token();
        let key = details.trt.clone();
        let method_name = &details.method_name;
        let item = parse_quote! {
            #(#doc_attrs)*
            #unsafety fn #method_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                #call_body
            }
        };
        Box::new(TraitImplBlockDetails { item, key })
    }

    /// Generate a 'impl Type { methods-go-here }' item which is a constructor
    /// for use with moveit traits.
    fn generate_constructor_impl(
        &self,
        impl_block_type_name: &QualifiedName,
    ) -> Box<ImplBlockDetails> {
        let ret_type: ReturnType = parse_quote! { -> impl autocxx::moveit::new::New<Output=Self> };
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(true, &None, &ret_type);
        let rust_name = make_ident(&self.rust_name);
        let doc_attrs = self.doc_attrs;
        let unsafety = self.unsafety.wrapper_token();
        Box::new(ImplBlockDetails {
            item: ImplItem::Method(parse_quote! {
                #(#doc_attrs)*
                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                    #call_body
                }
            }),
            ty: impl_block_type_name.get_final_ident(),
        })
    }

    /// Generate a function call wrapper
    fn generate_function_impl(&self, ret_type: &ReturnType) -> Item {
        let (lifetime_tokens, wrapper_params, ret_type, call_body) =
            self.common_parts(false, &None, ret_type);
        let rust_name = make_ident(self.rust_name);
        let doc_attrs = self.doc_attrs;
        let unsafety = self.unsafety.wrapper_token();
        Item::Fn(parse_quote! {
            #(#doc_attrs)*
            pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                #call_body
            }
        })
    }

    fn should_wrap_unsafe_calls(&self) -> bool {
        matches!(self.unsafety, UnsafetyNeeded::JustBridge)
            || self.always_unsafe_due_to_trait_definition
    }

    fn reorder_parameters(
        params: Punctuated<FnArg, Comma>,
        parameter_ordering: &[usize],
    ) -> Punctuated<FnArg, Comma> {
        let old_params = params.into_iter().collect::<Vec<_>>();
        parameter_ordering
            .iter()
            .map(|n| old_params.get(*n).unwrap().clone())
            .collect()
    }
}
<|endoftext|>