<commit_before>use winapi::um::wincodec::{IWICImagingFactory, IWICBitmapDecoder};
use winapi::um::objidlbase::IStream;
use winapi::ctypes::{c_void, c_uint};
use winapi::shared::winerror::S_OK;
use winapi::Interface;
use crate::{NwgError, Bitmap, ImageData};
use std::ptr;

pub unsafe fn create_image_factory() -> Result<*mut IWICImagingFactory, NwgError> {
    use winapi::um::combaseapi::CoCreateInstance;
    use winapi::um::wincodec::CLSID_WICImagingFactory;
    use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER;

    let mut image_factory: *mut IWICImagingFactory = ptr::null_mut();
    let result = CoCreateInstance(
        &CLSID_WICImagingFactory,
        ptr::null_mut(),
        CLSCTX_INPROC_SERVER,
        &IWICImagingFactory::uuidof(),
        (&mut image_factory as *mut *mut IWICImagingFactory) as *mut *mut c_void
    );

    if result != S_OK {
        return Err(NwgError::resource_create("Failed to create a image factory"));
    }

    Ok(image_factory)
}

pub unsafe fn create_decoder_from_file<'a>(fact: &IWICImagingFactory, path: &'a str) -> Result<*mut IWICBitmapDecoder, NwgError> {
    use winapi::um::wincodec::WICDecodeMetadataCacheOnDemand;
    use winapi::um::winnt::GENERIC_READ;
    use crate::win32::base_helper::to_utf16;

    let path = to_utf16(path);

    let mut decoder: *mut IWICBitmapDecoder = ptr::null_mut();
    let result = fact.CreateDecoderFromFilename(
        path.as_ptr(),
        ptr::null(),
        GENERIC_READ,
        WICDecodeMetadataCacheOnDemand,
        (&mut decoder as *mut *mut IWICBitmapDecoder) as *mut *mut IWICBitmapDecoder
    );

    if result != S_OK {
        return Err(NwgError::resource_create("Failed to create a bitmap decoder"));
    }

    Ok(decoder)
}

// This function is not declared by winapi yet. But winapi have import libraries
// for shlwapi (when the shellapi feature is enabled), so we can use it if we
// declare it ourselves.
extern "system" {
    fn SHCreateMemStream(p_init: *const u8, cb_init: c_uint) -> *mut IStream;
}

pub unsafe fn create_decoder_from_stream(fact: &IWICImagingFactory, data: &[u8]) -> Result<*mut IWICBitmapDecoder, NwgError> {
    use winapi::um::wincodec::WICDecodeMetadataCacheOnDemand;
    use std::convert::TryInto;

    let stream = SHCreateMemStream(data.as_ptr(), data.len().try_into().map_err(|_| {
        NwgError::resource_create("Failed to create memory stream, stream is too long")
    })?);
    if stream.is_null() {
        return Err(NwgError::resource_create("Failed to create memory stream, allocation failure"));
    }

    let mut decoder: *mut IWICBitmapDecoder = ptr::null_mut();
    let r = fact.CreateDecoderFromStream(
        stream,
        ptr::null(),
        WICDecodeMetadataCacheOnDemand,
        (&mut decoder as *mut *mut IWICBitmapDecoder) as *mut *mut IWICBitmapDecoder
    );

    if r != S_OK {
        return Err(NwgError::resource_create("Failed to create decoder from stream"));
    }

    Ok(decoder)
}

pub unsafe fn create_bitmap_from_wic(image: &ImageData) -> Result<Bitmap, NwgError> {
    use winapi::um::wincodec::{WICConvertBitmapSource, IWICBitmapSource, GUID_WICPixelFormat32bppPBGRA};
    use winapi::um::wingdi::{DeleteObject, CreateDIBSection, BITMAPINFO, BITMAPINFOHEADER, DIB_RGB_COLORS, BI_RGB, RGBQUAD};
    use winapi::shared::{ntdef::LONG, minwindef::DWORD, windef::HBITMAP};
    use winapi::um::winuser::{GetDC, ReleaseDC};
    use std::mem;

    // First convert the image into a bitmap compatible format
    let frame_ptr = (&*image.frame) as &IWICBitmapSource as *const IWICBitmapSource;
    let mut converted = ptr::null_mut();
    let hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppPBGRA, frame_ptr, &mut converted);

    if hr != S_OK {
        return Err(NwgError::image_decoder(hr, "Could not convert image pixels"))
    }

    // Converted size
    let (mut width, mut height) = (0, 0);
    (&*converted).GetSize(&mut width, &mut height);

    // Prepare the bitmap
    let header = BITMAPINFOHEADER {
        biSize: mem::size_of::<BITMAPINFOHEADER>() as DWORD,
        biWidth: width as LONG, biHeight: -(height as LONG),
        biPlanes: 1, biBitCount: 32, biCompression: BI_RGB,
        biSizeImage: (width * height * 3) as u32,
        biXPelsPerMeter: 0, biYPelsPerMeter: 0,
        biClrUsed: 0, biClrImportant: 0
    };

    let quad = RGBQUAD { rgbBlue: 0, rgbGreen: 0, rgbRed: 0, rgbReserved: 0 };
    let bitmap_info = BITMAPINFO {
        bmiHeader: header,
        bmiColors: [quad],
    };

    // Create a DIB
    let mut bits = ptr::null_mut();
    let screen_dc = GetDC(ptr::null_mut());
    let bitmap = CreateDIBSection(screen_dc, &bitmap_info, DIB_RGB_COLORS, &mut bits, ptr::null_mut(), 0) as HBITMAP;
    ReleaseDC(ptr::null_mut(), screen_dc);

    if bitmap.is_null() {
        return Err(NwgError::image_decoder(hr, "Could not create a bitmap"));
    }

    // Write the DIB data
    let stride = width * 4;
    let bitmap_size = width * height * 4;
    let hr = (&*converted).CopyPixels(ptr::null(), stride, bitmap_size, bits as *mut u8);
    if hr != S_OK {
        DeleteObject(bitmap as _);
        return Err(NwgError::image_decoder(hr, "Could not write to bitmap"));
    }

    // Free up the converted image
    (&*converted).Release();

    Ok(
        Bitmap {
            handle: bitmap as _,
            owned: true
        }
    )
}

pub unsafe fn resize_bitmap(fact: &IWICImagingFactory, image: &ImageData, new_size: [u32;2]) -> Result<ImageData, NwgError> {
    use winapi::um::wincodec::{IWICBitmapScaler, IWICBitmapSource, WICBitmapInterpolationModeCubic};

    let mut scaler: *mut IWICBitmapScaler = ptr::null_mut();
    let result = fact.CreateBitmapScaler(&mut scaler);
    if result != S_OK {
        return Err(NwgError::image_decoder(result, "Could not create bitmap scaler"));
    }

    let [w, h] = new_size;
    let image_source = image.frame as *const IWICBitmapSource;
    let result = (&*scaler).Initialize(image_source, w, h, WICBitmapInterpolationModeCubic);
    if result != S_OK {
        return Err(NwgError::image_decoder(result, "Could not initialize bitmap scaler"));
    }

    Ok(ImageData { frame: scaler as *mut IWICBitmapSource })
}
<commit_msg>fixed memory leak in image stream decoder<commit_after>use winapi::um::wincodec::{IWICImagingFactory, IWICBitmapDecoder};
use winapi::um::objidlbase::IStream;
use winapi::ctypes::{c_void, c_uint};
use winapi::shared::winerror::S_OK;
use winapi::Interface;
use crate::{NwgError, Bitmap, ImageData};
use std::ptr;

pub unsafe fn create_image_factory() -> Result<*mut IWICImagingFactory, NwgError> {
    use winapi::um::combaseapi::CoCreateInstance;
    use winapi::um::wincodec::CLSID_WICImagingFactory;
    use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER;

    let mut image_factory: *mut IWICImagingFactory = ptr::null_mut();
    let result = CoCreateInstance(
        &CLSID_WICImagingFactory,
        ptr::null_mut(),
        CLSCTX_INPROC_SERVER,
        &IWICImagingFactory::uuidof(),
        (&mut image_factory as *mut *mut IWICImagingFactory) as *mut *mut c_void
    );

    if result != S_OK {
        return Err(NwgError::resource_create("Failed to create a image factory"));
    }

    Ok(image_factory)
}

pub unsafe fn create_decoder_from_file<'a>(fact: &IWICImagingFactory, path: &'a str) -> Result<*mut IWICBitmapDecoder, NwgError> {
    use winapi::um::wincodec::WICDecodeMetadataCacheOnDemand;
    use winapi::um::winnt::GENERIC_READ;
    use crate::win32::base_helper::to_utf16;

    let path = to_utf16(path);

    let mut decoder: *mut IWICBitmapDecoder = ptr::null_mut();
    let result = fact.CreateDecoderFromFilename(
        path.as_ptr(),
        ptr::null(),
        GENERIC_READ,
        WICDecodeMetadataCacheOnDemand,
        (&mut decoder as *mut *mut IWICBitmapDecoder) as *mut *mut IWICBitmapDecoder
    );

    if result != S_OK {
        return Err(NwgError::resource_create("Failed to create a bitmap decoder"));
    }

    Ok(decoder)
}

// This function is not declared by winapi yet. But winapi have import libraries
// for shlwapi (when the shellapi feature is enabled), so we can use it if we
// declare it ourselves.
extern "system" {
    fn SHCreateMemStream(p_init: *const u8, cb_init: c_uint) -> *mut IStream;
}

pub unsafe fn create_decoder_from_stream(fact: &IWICImagingFactory, data: &[u8]) -> Result<*mut IWICBitmapDecoder, NwgError> {
    use winapi::um::wincodec::WICDecodeMetadataCacheOnDemand;
    use std::convert::TryInto;

    let stream = SHCreateMemStream(data.as_ptr(), data.len().try_into().map_err(|_| {
        NwgError::resource_create("Failed to create memory stream, stream is too long")
    })?);
    if stream.is_null() {
        return Err(NwgError::resource_create("Failed to create memory stream, allocation failure"));
    }

    let mut decoder: *mut IWICBitmapDecoder = ptr::null_mut();
    let r = fact.CreateDecoderFromStream(
        stream,
        ptr::null(),
        WICDecodeMetadataCacheOnDemand,
        (&mut decoder as *mut *mut IWICBitmapDecoder) as *mut *mut IWICBitmapDecoder
    );

    (*stream).Release();

    if r != S_OK {
        return Err(NwgError::resource_create("Failed to create decoder from stream"));
    }

    Ok(decoder)
}

pub unsafe fn create_bitmap_from_wic(image: &ImageData) -> Result<Bitmap, NwgError> {
    use winapi::um::wincodec::{WICConvertBitmapSource, IWICBitmapSource, GUID_WICPixelFormat32bppPBGRA};
    use winapi::um::wingdi::{DeleteObject, CreateDIBSection, BITMAPINFO, BITMAPINFOHEADER, DIB_RGB_COLORS, BI_RGB, RGBQUAD};
    use winapi::shared::{ntdef::LONG, minwindef::DWORD, windef::HBITMAP};
    use winapi::um::winuser::{GetDC, ReleaseDC};
    use std::mem;

    // First convert the image into a bitmap compatible format
    let frame_ptr = (&*image.frame) as &IWICBitmapSource as *const IWICBitmapSource;
    let mut converted = ptr::null_mut();
    let hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppPBGRA, frame_ptr, &mut converted);

    if hr != S_OK {
        return Err(NwgError::image_decoder(hr, "Could not convert image pixels"))
    }

    // Converted size
    let (mut width, mut height) = (0, 0);
    (&*converted).GetSize(&mut width, &mut height);

    // Prepare the bitmap
    let header = BITMAPINFOHEADER {
        biSize: mem::size_of::<BITMAPINFOHEADER>() as DWORD,
        biWidth: width as LONG, biHeight: -(height as LONG),
        biPlanes: 1, biBitCount: 32, biCompression: BI_RGB,
        biSizeImage: (width * height * 3) as u32,
        biXPelsPerMeter: 0, biYPelsPerMeter: 0,
        biClrUsed: 0, biClrImportant: 0
    };

    let quad = RGBQUAD { rgbBlue: 0, rgbGreen: 0, rgbRed: 0, rgbReserved: 0 };
    let bitmap_info = BITMAPINFO {
        bmiHeader: header,
        bmiColors: [quad],
    };

    // Create a DIB
    let mut bits = ptr::null_mut();
    let screen_dc = GetDC(ptr::null_mut());
    let bitmap = CreateDIBSection(screen_dc, &bitmap_info, DIB_RGB_COLORS, &mut bits, ptr::null_mut(), 0) as HBITMAP;
    ReleaseDC(ptr::null_mut(), screen_dc);

    if bitmap.is_null() {
        return Err(NwgError::image_decoder(hr, "Could not create a bitmap"));
    }

    // Write the DIB data
    let stride = width * 4;
    let bitmap_size = width * height * 4;
    let hr = (&*converted).CopyPixels(ptr::null(), stride, bitmap_size, bits as *mut u8);
    if hr != S_OK {
        DeleteObject(bitmap as _);
        return Err(NwgError::image_decoder(hr, "Could not write to bitmap"));
    }

    // Free up the converted image
    (&*converted).Release();

    Ok(
        Bitmap {
            handle: bitmap as _,
            owned: true
        }
    )
}

pub unsafe fn resize_bitmap(fact: &IWICImagingFactory, image: &ImageData, new_size: [u32;2]) -> Result<ImageData, NwgError> {
    use winapi::um::wincodec::{IWICBitmapScaler, IWICBitmapSource, WICBitmapInterpolationModeCubic};

    let mut scaler: *mut IWICBitmapScaler = ptr::null_mut();
    let result = fact.CreateBitmapScaler(&mut scaler);
    if result != S_OK {
        return Err(NwgError::image_decoder(result, "Could not create bitmap scaler"));
    }

    let [w, h] = new_size;
    let image_source = image.frame as *const IWICBitmapSource;
    let result = (&*scaler).Initialize(image_source, w, h, WICBitmapInterpolationModeCubic);
    if result != S_OK {
        return Err(NwgError::image_decoder(result, "Could not initialize bitmap scaler"));
    }

    Ok(ImageData { frame: scaler as *mut IWICBitmapSource })
}
<|endoftext|>


<commit_before>// errorsn.rs
// This is a bigger error exercise than the previous ones!
// You can do it! :)
//
// Edit the `read_and_validate` function so that it compiles and
// passes the tests... so many things could go wrong!
//
// - Reading from stdin could produce an io::Error
// - Parsing the input could produce a num::ParseIntError
// - Validating the input could produce a CreationError (defined below)
//
// How can we lump these errors into one general error? That is, what
// type goes where the question marks are, and how do we return
// that type from the body of read_and_validate?
//
// Scroll down for hints :)

use std::error;
use std::fmt;
use std::io;

// PositiveNonzeroInteger is a struct defined below the tests.
fn read_and_validate(b: &mut io::BufRead) -> Result<PositiveNonzeroInteger, ???> {
    let mut line = String::new();
    b.read_line(&mut line);
    let num: i64 = line.trim().parse();
    let answer = PositiveNonzeroInteger::new(num);
    answer
}

// This is a test helper function that turns a &str into a BufReader.
fn test_with_str(s: &str) -> Result<PositiveNonzeroInteger, Box<error::Error>> {
    let mut b = io::BufReader::new(s.as_bytes());
    read_and_validate(&mut b)
}

#[test]
fn test_success() {
    let x = test_with_str("42\n");
    assert_eq!(PositiveNonzeroInteger(42), x.unwrap());
}

#[test]
fn test_not_num() {
    let x = test_with_str("eleven billion\n");
    assert!(x.is_err());
}

#[test]
fn test_non_positive() {
    let x = test_with_str("-40\n");
    assert!(x.is_err());
}

#[test]
fn test_ioerror() {
    struct Broken;
    impl io::Read for Broken {
        fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
            Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!"))
        }
    }
    let mut b = io::BufReader::new(Broken);
    assert!(read_and_validate(&mut b).is_err());
    assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
}

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
        if value == 0 {
            Err(CreationError::Zero)
        } else if value < 0 {
            Err(CreationError::Negative)
        } else {
            Ok(PositiveNonzeroInteger(value as u64))
        }
    }
}

#[test]
fn test_positive_nonzero_integer_creation() {
    assert!(PositiveNonzeroInteger::new(10).is_ok());
    assert_eq!(
        Err(CreationError::Negative),
        PositiveNonzeroInteger::new(-10)
    );
    assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
}

#[derive(PartialEq, Debug)]
enum CreationError {
    Negative,
    Zero,
}

impl fmt::Display for CreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str((self as &error::Error).description())
    }
}

impl error::Error for CreationError {
    fn description(&self) -> &str {
        match *self {
            CreationError::Negative => "Negative",
            CreationError::Zero => "Zero",
        }
    }
}

// First hint: To figure out what type should go where the ??? is, take a look
// at the test helper function `test_with_str`, since it returns whatever
// `read_and_validate` returns and`test_with_str` has its signature fully
// specified.

// Next hint: There are three places in `read_and_validate` that we call a
// function that returns a `Result` (that is, the functions might fail).
// Apply the `?` operator on those calls so that we return immediately from
// `read_and_validate` if those function calls fail.

// Another hint: under the hood, the `?` operator calls `From::from`
// on the error value to convert it to a boxed trait object, a Box<error::Error>,
// which is polymorphic-- that means that lots of different kinds of errors
// can be returned from the same function because all errors act the same
// since they all implement the `error::Error` trait.
// Check out this section of the book:
// https://doc.rust-lang.org/stable/book/second-edition/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator

// Another another hint: Note that because the `?` operator returns
// the *unwrapped* value in the `Ok` case, if we want to return a `Result` from
// `read_and_validate` for *its* success case, we'll have to rewrap a value
// that we got from the return value of a `?`ed call in an `Ok`-- this will
// look like `Ok(something)`.

// Another another another hint: `Result`s must be "used", that is, you'll
// get a warning if you don't handle a `Result` that you get in your
// function. Read more about that in the `std::result` module docs:
// https://doc.rust-lang.org/std/result/#results-must-be-used
<commit_msg>Auto merge of #162 - c-rustacean:rustfmt-and-ws-fixes, r=komaeda<commit_after>// errorsn.rs
// This is a bigger error exercise than the previous ones!
// You can do it! :)
//
// Edit the `read_and_validate` function so that it compiles and
// passes the tests... so many things could go wrong!
//
// - Reading from stdin could produce an io::Error
// - Parsing the input could produce a num::ParseIntError
// - Validating the input could produce a CreationError (defined below)
//
// How can we lump these errors into one general error? That is, what
// type goes where the question marks are, and how do we return
// that type from the body of read_and_validate?
//
// Scroll down for hints :)

use std::error;
use std::fmt;
use std::io;

// PositiveNonzeroInteger is a struct defined below the tests.
fn read_and_validate(b: &mut io::BufRead) -> Result<PositiveNonzeroInteger, ???> {
    let mut line = String::new();
    b.read_line(&mut line);
    let num: i64 = line.trim().parse();
    let answer = PositiveNonzeroInteger::new(num);
    answer
}

// This is a test helper function that turns a &str into a BufReader.
fn test_with_str(s: &str) -> Result<PositiveNonzeroInteger, Box<error::Error>> {
    let mut b = io::BufReader::new(s.as_bytes());
    read_and_validate(&mut b)
}

#[test]
fn test_success() {
    let x = test_with_str("42\n");
    assert_eq!(PositiveNonzeroInteger(42), x.unwrap());
}

#[test]
fn test_not_num() {
    let x = test_with_str("eleven billion\n");
    assert!(x.is_err());
}

#[test]
fn test_non_positive() {
    let x = test_with_str("-40\n");
    assert!(x.is_err());
}

#[test]
fn test_ioerror() {
    struct Broken;
    impl io::Read for Broken {
        fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
            Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!"))
        }
    }
    let mut b = io::BufReader::new(Broken);
    assert!(read_and_validate(&mut b).is_err());
    assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
}

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
        if value == 0 {
            Err(CreationError::Zero)
        } else if value < 0 {
            Err(CreationError::Negative)
        } else {
            Ok(PositiveNonzeroInteger(value as u64))
        }
    }
}

#[test]
fn test_positive_nonzero_integer_creation() {
    assert!(PositiveNonzeroInteger::new(10).is_ok());
    assert_eq!(
        Err(CreationError::Negative),
        PositiveNonzeroInteger::new(-10)
    );
    assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
}

#[derive(PartialEq, Debug)]
enum CreationError {
    Negative,
    Zero,
}

impl fmt::Display for CreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str((self as &error::Error).description())
    }
}

impl error::Error for CreationError {
    fn description(&self) -> &str {
        match *self {
            CreationError::Negative => "Negative",
            CreationError::Zero => "Zero",
        }
    }
}

// First hint: To figure out what type should go where the ??? is, take a look
// at the test helper function `test_with_str`, since it returns whatever
// `read_and_validate` returns and`test_with_str` has its signature fully
// specified.

// Next hint: There are three places in `read_and_validate` that we call a
// function that returns a `Result` (that is, the functions might fail).
// Apply the `?` operator on those calls so that we return immediately from
// `read_and_validate` if those function calls fail.

// Another hint: under the hood, the `?` operator calls `From::from`
// on the error value to convert it to a boxed trait object, a Box<error::Error>,
// which is polymorphic-- that means that lots of different kinds of errors
// can be returned from the same function because all errors act the same
// since they all implement the `error::Error` trait.
// Check out this section of the book:
// https://doc.rust-lang.org/stable/book/second-edition/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator

// Another another hint: Note that because the `?` operator returns
// the *unwrapped* value in the `Ok` case, if we want to return a `Result` from
// `read_and_validate` for *its* success case, we'll have to rewrap a value
// that we got from the return value of a `?`ed call in an `Ok`-- this will
// look like `Ok(something)`.

// Another another another hint: `Result`s must be "used", that is, you'll
// get a warning if you don't handle a `Result` that you get in your
// function. Read more about that in the `std::result` module docs:
// https://doc.rust-lang.org/std/result/#results-must-be-used
<|endoftext|>


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

use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontStyle,
           SelectorPlatformIdentifier};
use font::{SpecifiedFontStyle, UsedFontStyle};
use font_list::FontList;
use servo_util::cache::{Cache, LRUCache};
use servo_util::time::ProfilerChan;

use platform::font::FontHandle;
use platform::font_context::FontContextHandle;

use azure::azure_hl::BackendType;
use std::hashmap::HashMap;

// TODO(Rust #3934): creating lots of new dummy styles is a workaround
// for not being able to store symbolic enums in top-level constants.
pub fn dummy_style() -> FontStyle {
    use font::FontWeight300;
    return FontStyle {
        pt_size: 20f64,
        weight: FontWeight300,
        italic: false,
        oblique: false,
        families: ~"serif, sans-serif",
    }
}

pub trait FontContextHandleMethods {
    fn clone(&self) -> FontContextHandle;
    fn create_font_from_identifier(&self, ~str, UsedFontStyle) -> Result<FontHandle, ()>;
}

pub struct FontContext {
    instance_cache: LRUCache<FontDescriptor, @mut Font>,
    font_list: Option<FontList>, // only needed by layout
    group_cache: LRUCache<SpecifiedFontStyle, @FontGroup>,
    handle: FontContextHandle,
    backend: BackendType,
    generic_fonts: HashMap<~str,~str>,
    profiler_chan: ProfilerChan,
}

impl<'self> FontContext {
    pub fn new(backend: BackendType,
           needs_font_list: bool,
           profiler_chan: ProfilerChan)
           -> FontContext {
        let handle = FontContextHandle::new();
        let font_list = if needs_font_list {
                            Some(FontList::new(&handle, profiler_chan.clone())) }
                        else { None };

        // TODO: Allow users to specify these.
        let mut generic_fonts = HashMap::with_capacity(5);
        generic_fonts.insert(~"serif", ~"Times New Roman");
        generic_fonts.insert(~"sans-serif", ~"Arial");
        generic_fonts.insert(~"cursive", ~"Apple Chancery");
        generic_fonts.insert(~"fantasy", ~"Papyrus");
        generic_fonts.insert(~"monospace", ~"Menlo");

        FontContext {
            instance_cache: LRUCache::new(10),
            font_list: font_list,
            group_cache: LRUCache::new(10),
            handle: handle,
            backend: backend,
            generic_fonts: generic_fonts,
            profiler_chan: profiler_chan,
        }
    }

    fn get_font_list(&'self self) -> &'self FontList {
        self.font_list.get_ref()
    }

    pub fn get_resolved_font_for_style(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
        match self.group_cache.find(style) {
            Some(fg) => {
                debug!("font group cache hit");
                fg
            },
            None => {
                debug!("font group cache miss");
                let fg = self.create_font_group(style);
                self.group_cache.insert(style.clone(), fg);
                fg
            }
        }
    }

    pub fn get_font_by_descriptor(&mut self, desc: &FontDescriptor) -> Result<@mut Font, ()> {
        match self.instance_cache.find(desc) {
            Some(f) => {
                debug!("font cache hit");
                Ok(f)
            },
            None => {
                debug!("font cache miss");
                let result = self.create_font_instance(desc);
                match result {
                    Ok(font) => {
                        self.instance_cache.insert(desc.clone(), font);
                    }, _ => {}
                };
                result
            }
        }
    }

    fn transform_family(&self, family: &str) -> ~str {
        // FIXME: Need a find_like() in HashMap.
        let family = family.to_str();
        debug!("(transform family) searching for `{:s}`", family);
        match self.generic_fonts.find(&family) {
            None => family,
            Some(mapped_family) => (*mapped_family).clone()
        }
    }

    fn create_font_group(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
        let mut fonts = ~[];

        debug!("(create font group) --- starting ---");

        // TODO(Issue #193): make iteration over 'font-family' more robust.
        for family in style.families.split_iter(',') {
            let family_name = family.trim();
            let transformed_family_name = self.transform_family(family_name);
            debug!("(create font group) transformed family is `{:s}`", transformed_family_name);

            let result = match self.font_list {
                Some(ref fl) => fl.find_font_in_family(transformed_family_name, style),
                None => None,
            };

            let mut found = false;
            for font_entry in result.iter() {
                found = true;

                let font_id =
                  SelectorPlatformIdentifier(font_entry.handle.face_identifier());
                let font_desc = FontDescriptor::new((*style).clone(), font_id);

                let instance = self.get_font_by_descriptor(&font_desc);

                for font in instance.iter() { fonts.push(*font); }
            };

            if !found {
                debug!("(create font group) didn't find `{:s}`", transformed_family_name);
            }
        }

        let last_resort = FontList::get_last_resort_font_families();

        for family in last_resort.iter() {
            let result = match self.font_list {
                Some(ref fl) => fl.find_font_in_family(*family, style),
                None => None,
            };

            for font_entry in result.iter() {
                let font_id =
                  SelectorPlatformIdentifier(font_entry.handle.face_identifier());
                let font_desc = FontDescriptor::new((*style).clone(), font_id);

                let instance = self.get_font_by_descriptor(&font_desc);

                for font in instance.iter() {
                    fonts.push(*font);
                }
            }
        }

        assert!(fonts.len() > 0);
        // TODO(Issue #179): Split FontStyle into specified and used styles
        let used_style = (*style).clone();

        debug!("(create font group) --- finished ---");

        @FontGroup::new(style.families.to_managed(), &used_style, fonts)
    }

    fn create_font_instance(&self, desc: &FontDescriptor) -> Result<@mut Font, ()> {
        return match &desc.selector {
            // TODO(Issue #174): implement by-platform-name font selectors.
            &SelectorPlatformIdentifier(ref identifier) => {
                let result_handle = self.handle.create_font_from_identifier((*identifier).clone(),
                                                                            desc.style.clone());
                do result_handle.and_then |handle| {
                    Ok(Font::new_from_adopted_handle(self,
                                                     handle,
                                                     &desc.style,
                                                     self.backend,
                                                     self.profiler_chan.clone()))
                }
            }
        };
    }
}
<commit_msg>Use last resort font when only there is no font in font group. There is a little bit of performance gain of layout.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontStyle,
           SelectorPlatformIdentifier};
use font::{SpecifiedFontStyle, UsedFontStyle};
use font_list::FontList;
use servo_util::cache::{Cache, LRUCache};
use servo_util::time::ProfilerChan;

use platform::font::FontHandle;
use platform::font_context::FontContextHandle;

use azure::azure_hl::BackendType;
use std::hashmap::HashMap;

// TODO(Rust #3934): creating lots of new dummy styles is a workaround
// for not being able to store symbolic enums in top-level constants.
pub fn dummy_style() -> FontStyle {
    use font::FontWeight300;
    return FontStyle {
        pt_size: 20f64,
        weight: FontWeight300,
        italic: false,
        oblique: false,
        families: ~"serif, sans-serif",
    }
}

pub trait FontContextHandleMethods {
    fn clone(&self) -> FontContextHandle;
    fn create_font_from_identifier(&self, ~str, UsedFontStyle) -> Result<FontHandle, ()>;
}

pub struct FontContext {
    instance_cache: LRUCache<FontDescriptor, @mut Font>,
    font_list: Option<FontList>, // only needed by layout
    group_cache: LRUCache<SpecifiedFontStyle, @FontGroup>,
    handle: FontContextHandle,
    backend: BackendType,
    generic_fonts: HashMap<~str,~str>,
    profiler_chan: ProfilerChan,
}

impl<'self> FontContext {
    pub fn new(backend: BackendType,
           needs_font_list: bool,
           profiler_chan: ProfilerChan)
           -> FontContext {
        let handle = FontContextHandle::new();
        let font_list = if needs_font_list {
                            Some(FontList::new(&handle, profiler_chan.clone())) }
                        else { None };

        // TODO: Allow users to specify these.
        let mut generic_fonts = HashMap::with_capacity(5);
        generic_fonts.insert(~"serif", ~"Times New Roman");
        generic_fonts.insert(~"sans-serif", ~"Arial");
        generic_fonts.insert(~"cursive", ~"Apple Chancery");
        generic_fonts.insert(~"fantasy", ~"Papyrus");
        generic_fonts.insert(~"monospace", ~"Menlo");

        FontContext {
            instance_cache: LRUCache::new(10),
            font_list: font_list,
            group_cache: LRUCache::new(10),
            handle: handle,
            backend: backend,
            generic_fonts: generic_fonts,
            profiler_chan: profiler_chan,
        }
    }

    fn get_font_list(&'self self) -> &'self FontList {
        self.font_list.get_ref()
    }

    pub fn get_resolved_font_for_style(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
        match self.group_cache.find(style) {
            Some(fg) => {
                debug!("font group cache hit");
                fg
            },
            None => {
                debug!("font group cache miss");
                let fg = self.create_font_group(style);
                self.group_cache.insert(style.clone(), fg);
                fg
            }
        }
    }

    pub fn get_font_by_descriptor(&mut self, desc: &FontDescriptor) -> Result<@mut Font, ()> {
        match self.instance_cache.find(desc) {
            Some(f) => {
                debug!("font cache hit");
                Ok(f)
            },
            None => {
                debug!("font cache miss");
                let result = self.create_font_instance(desc);
                match result {
                    Ok(font) => {
                        self.instance_cache.insert(desc.clone(), font);
                    }, _ => {}
                };
                result
            }
        }
    }

    fn transform_family(&self, family: &str) -> ~str {
        // FIXME: Need a find_like() in HashMap.
        let family = family.to_str();
        debug!("(transform family) searching for `{:s}`", family);
        match self.generic_fonts.find(&family) {
            None => family,
            Some(mapped_family) => (*mapped_family).clone()
        }
    }

    fn create_font_group(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
        let mut fonts = ~[];

        debug!("(create font group) --- starting ---");

        // TODO(Issue #193): make iteration over 'font-family' more robust.
        for family in style.families.split_iter(',') {
            let family_name = family.trim();
            let transformed_family_name = self.transform_family(family_name);
            debug!("(create font group) transformed family is `{:s}`", transformed_family_name);

            let result = match self.font_list {
                Some(ref fl) => fl.find_font_in_family(transformed_family_name, style),
                None => None,
            };

            let mut found = false;
            for font_entry in result.iter() {
                found = true;

                let font_id =
                  SelectorPlatformIdentifier(font_entry.handle.face_identifier());
                let font_desc = FontDescriptor::new((*style).clone(), font_id);

                let instance = self.get_font_by_descriptor(&font_desc);

                for font in instance.iter() { fonts.push(*font); }
            };

            if !found {
                debug!("(create font group) didn't find `{:s}`", transformed_family_name);
            }
        }

        if fonts.len() == 0 {
            let last_resort = FontList::get_last_resort_font_families();

            for family in last_resort.iter() {
                let result = match self.font_list {
                    Some(ref fl) => fl.find_font_in_family(*family, style),
                        None => None,
                };

                for font_entry in result.iter() {
                    let font_id =
                        SelectorPlatformIdentifier(font_entry.handle.face_identifier());
                    let font_desc = FontDescriptor::new((*style).clone(), font_id);

                    let instance = self.get_font_by_descriptor(&font_desc);

                    for font in instance.iter() {
                        fonts.push(*font);
                    }
                }
            }
        }

        assert!(fonts.len() > 0);
        // TODO(Issue #179): Split FontStyle into specified and used styles
        let used_style = (*style).clone();

        debug!("(create font group) --- finished ---");

        @FontGroup::new(style.families.to_managed(), &used_style, fonts)
    }

    fn create_font_instance(&self, desc: &FontDescriptor) -> Result<@mut Font, ()> {
        return match &desc.selector {
            // TODO(Issue #174): implement by-platform-name font selectors.
            &SelectorPlatformIdentifier(ref identifier) => {
                let result_handle = self.handle.create_font_from_identifier((*identifier).clone(),
                                                                            desc.style.clone());
                do result_handle.and_then |handle| {
                    Ok(Font::new_from_adopted_handle(self,
                                                     handle,
                                                     &desc.style,
                                                     self.backend,
                                                     self.profiler_chan.clone()))
                }
            }
        };
    }
}
<|endoftext|>


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

use crate::rustc::hir::intravisit::FnKind;
use crate::rustc::hir::{def_id, Body, FnDecl};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::mir::{
    self, traversal,
    visit::{PlaceContext, Visitor},
    TerminatorKind,
};
use crate::rustc::ty;
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::{
    ast::NodeId,
    source_map::{BytePos, Span},
};
use crate::utils::{
    in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint, span_lint_and_then,
    walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use std::convert::TryFrom;

/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
/// value that is going to be dropped without further use.
///
/// **Why is this bad?** It is not always possible for the compiler to eliminate useless
/// allocations and deallocations generated by redundant `clone()`s.
///
/// **Known problems:**
///
/// * Suggestions made by this lint could require NLL to be enabled.
/// * False-positive if there is a borrow preventing the value from moving out.
///
/// ```rust
/// let x = String::new();
///
/// let y = &x;
///
/// foo(x.clone()); // This lint suggests to remove this `clone()`
/// ```
///
/// **Example:**
/// ```rust
/// {
///     let x = Foo::new();
///     call(x.clone());
///     call(x.clone()); // this can just pass `x`
/// }
///
/// ["lorem", "ipsum"].join(" ").to_string()
///
/// Path::new("/a/b").join("c").to_path_buf()
/// ```
declare_clippy_lint! {
    pub REDUNDANT_CLONE,
    nursery,
    "`clone()` of an owned value that is going to be dropped immediately"
}

pub struct RedundantClone;

impl LintPass for RedundantClone {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_CLONE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: NodeId,
    ) {
        let def_id = cx.tcx.hir.body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        // Looks for `call(&T)` where `T: !Copy`
        let call = |kind: &mir::TerminatorKind<'tcx>| -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>)> {
            if_chain! {
                if let TerminatorKind::Call { func, args, .. } = kind;
                if args.len() == 1;
                if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
                if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
                if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
                if !is_copy(cx, inner_ty);
                then {
                    Some((def_id, *local, inner_ty))
                } else {
                    None
                }
            }
        };

        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
            let terminator = if let Some(terminator) = &bbdata.terminator {
                terminator
            } else {
                continue;
            };

            // Give up on loops
            if terminator.successors().any(|s| *s == bb) {
                continue;
            }

            let (fn_def_id, arg, arg_ty) = if let Some(t) = call(&terminator.kind) {
                t
            } else {
                continue;
            };

            let from_borrow = match_def_path(cx.tcx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
                || match_def_path(cx.tcx, fn_def_id, &paths::TO_OWNED_METHOD)
                || (match_def_path(cx.tcx, fn_def_id, &paths::TO_STRING_METHOD)
                    && match_type(cx, arg_ty, &paths::STRING));

            let from_deref = !from_borrow
                && (match_def_path(cx.tcx, fn_def_id, &paths::PATH_TO_PATH_BUF)
                    || match_def_path(cx.tcx, fn_def_id, &paths::OS_STR_TO_OS_STRING));

            if !from_borrow && !from_deref {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); }
            let cloned = if let Some(referent) = bbdata
                .statements
                .iter()
                .rev()
                .filter_map(|stmt| {
                    if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
                        if *local == arg {
                            if from_deref {
                                // `r` is already a reference.
                                if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
                                    return Some(r);
                                }
                            } else if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
                                return Some(r);
                            }
                        }
                    }

                    None
                })
                .next()
            {
                referent
            } else {
                continue;
            };

            // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
            let referent = if from_deref {
                let ps = mir.predecessors_for(bb);
                let pred_arg = if_chain! {
                    if ps.len() == 1;
                    if let Some(pred_terminator) = &mir[ps[0]].terminator;
                    if let mir::TerminatorKind::Call { destination: Some((res, _)), .. } = &pred_terminator.kind;
                    if *res == mir::Place::Local(cloned);
                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty)) = call(&pred_terminator.kind);
                    if match_def_path(cx.tcx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
                    if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
                        || match_type(cx, pred_arg_ty, &paths::OS_STRING);
                    then {
                        pred_arg
                    } else {
                        continue;
                    }
                };

                if let Some(referent) = mir[ps[0]]
                    .statements
                    .iter()
                    .rev()
                    .filter_map(|stmt| {
                        if let mir::StatementKind::Assign(mir::Place::Local(l), v) = &stmt.kind {
                            if *l == pred_arg {
                                if let mir::Rvalue::Ref(_, _, mir::Place::Local(referent)) = **v {
                                    return Some(referent);
                                }
                            }
                        }

                        None
                    })
                    .next()
                {
                    referent
                } else {
                    continue;
                }
            } else {
                cloned
            };

            let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
                if let Some(term) = &tdata.terminator {
                    // Give up on loops
                    if term.successors().any(|s| *s == bb) {
                        return true;
                    }
                }

                let mut vis = LocalUseVisitor {
                    local: referent,
                    used_other_than_drop: false,
                };
                vis.visit_basic_block_data(tbb, tdata);
                vis.used_other_than_drop
            });

            if !used_later {
                let span = terminator.source_info.span;
                if_chain! {
                    if !in_macro(span);
                    if let Some(snip) = snippet_opt(cx, span);
                    if let Some(dot) = snip.rfind('.');
                    then {
                        let sugg_span = span.with_lo(
                            span.lo() + BytePos(u32::try_from(dot).unwrap())
                        );

                        span_lint_and_then(cx, REDUNDANT_CLONE, sugg_span, "redundant clone", |db| {
                            db.span_suggestion_with_applicability(
                                sugg_span,
                                "remove this",
                                String::new(),
                                Applicability::MaybeIncorrect,
                            );
                            db.span_note(
                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
                                "this value is dropped without further use",
                            );
                        });
                    } else {
                        span_lint(cx, REDUNDANT_CLONE, span, "redundant clone");
                    }
                }
            }
        }
    }
}

struct LocalUseVisitor {
    local: mir::Local,
    used_other_than_drop: bool,
}

impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
    fn visit_statement(&mut self, block: mir::BasicBlock, statement: &mir::Statement<'tcx>, location: mir::Location) {
        // Once flagged, skip remaining statements
        if !self.used_other_than_drop {
            self.super_statement(block, statement, location);
        }
    }

    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
        match ctx {
            PlaceContext::Drop | PlaceContext::StorageDead => return,
            _ => {},
        }

        if *local == self.local {
            self.used_other_than_drop = true;
        }
    }
}
<commit_msg>Implement visit_basic_block_data<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir::intravisit::FnKind;
use crate::rustc::hir::{def_id, Body, FnDecl};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::mir::{
    self, traversal,
    visit::{PlaceContext, Visitor},
    TerminatorKind,
};
use crate::rustc::ty;
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::{
    ast::NodeId,
    source_map::{BytePos, Span},
};
use crate::utils::{
    in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint, span_lint_and_then,
    walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use std::convert::TryFrom;

/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
/// value that is going to be dropped without further use.
///
/// **Why is this bad?** It is not always possible for the compiler to eliminate useless
/// allocations and deallocations generated by redundant `clone()`s.
///
/// **Known problems:**
///
/// * Suggestions made by this lint could require NLL to be enabled.
/// * False-positive if there is a borrow preventing the value from moving out.
///
/// ```rust
/// let x = String::new();
///
/// let y = &x;
///
/// foo(x.clone()); // This lint suggests to remove this `clone()`
/// ```
///
/// **Example:**
/// ```rust
/// {
///     let x = Foo::new();
///     call(x.clone());
///     call(x.clone()); // this can just pass `x`
/// }
///
/// ["lorem", "ipsum"].join(" ").to_string()
///
/// Path::new("/a/b").join("c").to_path_buf()
/// ```
declare_clippy_lint! {
    pub REDUNDANT_CLONE,
    nursery,
    "`clone()` of an owned value that is going to be dropped immediately"
}

pub struct RedundantClone;

impl LintPass for RedundantClone {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_CLONE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: NodeId,
    ) {
        let def_id = cx.tcx.hir.body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        // Looks for `call(&T)` where `T: !Copy`
        let call = |kind: &mir::TerminatorKind<'tcx>| -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>)> {
            if_chain! {
                if let TerminatorKind::Call { func, args, .. } = kind;
                if args.len() == 1;
                if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
                if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
                if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
                if !is_copy(cx, inner_ty);
                then {
                    Some((def_id, *local, inner_ty))
                } else {
                    None
                }
            }
        };

        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
            let terminator = if let Some(terminator) = &bbdata.terminator {
                terminator
            } else {
                continue;
            };

            // Give up on loops
            if terminator.successors().any(|s| *s == bb) {
                continue;
            }

            let (fn_def_id, arg, arg_ty) = if let Some(t) = call(&terminator.kind) {
                t
            } else {
                continue;
            };

            let from_borrow = match_def_path(cx.tcx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
                || match_def_path(cx.tcx, fn_def_id, &paths::TO_OWNED_METHOD)
                || (match_def_path(cx.tcx, fn_def_id, &paths::TO_STRING_METHOD)
                    && match_type(cx, arg_ty, &paths::STRING));

            let from_deref = !from_borrow
                && (match_def_path(cx.tcx, fn_def_id, &paths::PATH_TO_PATH_BUF)
                    || match_def_path(cx.tcx, fn_def_id, &paths::OS_STR_TO_OS_STRING));

            if !from_borrow && !from_deref {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); }
            let cloned = if let Some(referent) = bbdata
                .statements
                .iter()
                .rev()
                .filter_map(|stmt| {
                    if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
                        if *local == arg {
                            if from_deref {
                                // `r` is already a reference.
                                if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
                                    return Some(r);
                                }
                            } else if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
                                return Some(r);
                            }
                        }
                    }

                    None
                })
                .next()
            {
                referent
            } else {
                continue;
            };

            // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
            let referent = if from_deref {
                let ps = mir.predecessors_for(bb);
                let pred_arg = if_chain! {
                    if ps.len() == 1;
                    if let Some(pred_terminator) = &mir[ps[0]].terminator;
                    if let mir::TerminatorKind::Call { destination: Some((res, _)), .. } = &pred_terminator.kind;
                    if *res == mir::Place::Local(cloned);
                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty)) = call(&pred_terminator.kind);
                    if match_def_path(cx.tcx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
                    if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
                        || match_type(cx, pred_arg_ty, &paths::OS_STRING);
                    then {
                        pred_arg
                    } else {
                        continue;
                    }
                };

                if let Some(referent) = mir[ps[0]]
                    .statements
                    .iter()
                    .rev()
                    .filter_map(|stmt| {
                        if let mir::StatementKind::Assign(mir::Place::Local(l), v) = &stmt.kind {
                            if *l == pred_arg {
                                if let mir::Rvalue::Ref(_, _, mir::Place::Local(referent)) = **v {
                                    return Some(referent);
                                }
                            }
                        }

                        None
                    })
                    .next()
                {
                    referent
                } else {
                    continue;
                }
            } else {
                cloned
            };

            let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
                if let Some(term) = &tdata.terminator {
                    // Give up on loops
                    if term.successors().any(|s| *s == bb) {
                        return true;
                    }
                }

                let mut vis = LocalUseVisitor {
                    local: referent,
                    used_other_than_drop: false,
                };
                vis.visit_basic_block_data(tbb, tdata);
                vis.used_other_than_drop
            });

            if !used_later {
                let span = terminator.source_info.span;
                if_chain! {
                    if !in_macro(span);
                    if let Some(snip) = snippet_opt(cx, span);
                    if let Some(dot) = snip.rfind('.');
                    then {
                        let sugg_span = span.with_lo(
                            span.lo() + BytePos(u32::try_from(dot).unwrap())
                        );

                        span_lint_and_then(cx, REDUNDANT_CLONE, sugg_span, "redundant clone", |db| {
                            db.span_suggestion_with_applicability(
                                sugg_span,
                                "remove this",
                                String::new(),
                                Applicability::MaybeIncorrect,
                            );
                            db.span_note(
                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
                                "this value is dropped without further use",
                            );
                        });
                    } else {
                        span_lint(cx, REDUNDANT_CLONE, span, "redundant clone");
                    }
                }
            }
        }
    }
}

struct LocalUseVisitor {
    local: mir::Local,
    used_other_than_drop: bool,
}

impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
    fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
        let mir::BasicBlockData {
            statements,
            terminator,
            is_cleanup: _,
        } = data;

        for (statement_index, statement) in statements.iter().enumerate() {
            self.visit_statement(block, statement, mir::Location { block, statement_index });

            // Once flagged, skip remaining statements
            if self.used_other_than_drop {
                return;
            }
        }

        if let Some(terminator) = terminator {
            self.visit_terminator(
                block,
                terminator,
                mir::Location {
                    block,
                    statement_index: statements.len(),
                },
            );
        }
    }

    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
        match ctx {
            PlaceContext::Drop | PlaceContext::StorageDead => return,
            _ => {},
        }

        if *local == self.local {
            self.used_other_than_drop = true;
        }
    }
}
<|endoftext|>


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

#![deny(unsafe_code)]

use euclid::default::Size2D;

use std::cell::RefCell;
use std::ffi::c_void;
use std::rc::Rc;
use surfman::Adapter;
use surfman::Connection;
use surfman::Context;
use surfman::ContextAttributeFlags;
use surfman::ContextAttributes;
use surfman::Device;
use surfman::Error;
use surfman::GLApi;
use surfman::GLVersion;
use surfman::NativeContext;
use surfman::NativeDevice;
use surfman::NativeWidget;
use surfman::Surface;
use surfman::SurfaceAccess;
use surfman::SurfaceInfo;
use surfman::SurfaceTexture;
use surfman::SurfaceType;
use surfman_chains::SwapChain;

/// A bridge between webrender and surfman
// TODO: move this into a different crate so that script doesn't depend on surfman
#[derive(Clone)]
pub struct WebrenderSurfman(Rc<WebrenderSurfmanData>);

struct WebrenderSurfmanData {
    device: RefCell<Device>,
    context: RefCell<Context>,
    // We either render to a swap buffer or to a native widget
    swap_chain: Option<SwapChain<Device>>,
}

impl Drop for WebrenderSurfmanData {
    fn drop(&mut self) {
        let ref mut device = self.device.borrow_mut();
        let ref mut context = self.context.borrow_mut();
        if let Some(ref swap_chain) = self.swap_chain {
            let _ = swap_chain.destroy(device, context);
        }
        let _ = device.destroy_context(context);
    }
}

impl WebrenderSurfman {
    pub fn create(
        connection: &Connection,
        adapter: &Adapter,
        surface_type: SurfaceType<NativeWidget>,
    ) -> Result<Self, Error> {
        let mut device = connection.create_device(&adapter)?;
        let flags = ContextAttributeFlags::ALPHA |
            ContextAttributeFlags::DEPTH |
            ContextAttributeFlags::STENCIL;
        let version = match connection.gl_api() {
            GLApi::GLES => GLVersion { major: 3, minor: 0 },
            GLApi::GL => GLVersion { major: 3, minor: 2 },
        };
        let context_attributes = ContextAttributes { flags, version };
        let context_descriptor = device.create_context_descriptor(&context_attributes)?;
        let mut context = device.create_context(&context_descriptor, None)?;
        let surface_access = SurfaceAccess::GPUOnly;
        let headless = match surface_type {
            SurfaceType::Widget { .. } => false,
            SurfaceType::Generic { .. } => true,
        };
        let surface = device.create_surface(&context, surface_access, surface_type)?;
        device
            .bind_surface_to_context(&mut context, surface)
            .map_err(|(err, mut surface)| {
                let _ = device.destroy_surface(&mut context, &mut surface);
                err
            })?;
        let swap_chain = if headless {
            Some(SwapChain::create_attached(
                &mut device,
                &mut context,
                surface_access,
            )?)
        } else {
            None
        };
        let device = RefCell::new(device);
        let context = RefCell::new(context);
        let data = WebrenderSurfmanData {
            device,
            context,
            swap_chain,
        };
        Ok(WebrenderSurfman(Rc::new(data)))
    }

    pub fn create_surface_texture(
        &self,
        surface: Surface,
    ) -> Result<SurfaceTexture, (Error, Surface)> {
        let ref device = self.0.device.borrow();
        let ref mut context = self.0.context.borrow_mut();
        device.create_surface_texture(context, surface)
    }

    pub fn destroy_surface_texture(
        &self,
        surface_texture: SurfaceTexture,
    ) -> Result<Surface, (Error, SurfaceTexture)> {
        let ref device = self.0.device.borrow();
        let ref mut context = self.0.context.borrow_mut();
        device.destroy_surface_texture(context, surface_texture)
    }

    pub fn make_gl_context_current(&self) -> Result<(), Error> {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.make_context_current(context)
    }

    pub fn swap_chain(&self) -> Result<&SwapChain<Device>, Error> {
        self.0.swap_chain.as_ref().ok_or(Error::WidgetAttached)
    }

    pub fn resize(&self, size: Size2D<i32>) -> Result<(), Error> {
        let ref mut device = self.0.device.borrow_mut();
        let ref mut context = self.0.context.borrow_mut();
        if let Some(swap_chain) = self.0.swap_chain.as_ref() {
            return swap_chain.resize(device, context, size);
        }
        let mut surface = device.unbind_surface_from_context(context)?.unwrap();
        device.resize_surface(context, &mut surface, size)?;
        device
            .bind_surface_to_context(context, surface)
            .map_err(|(err, mut surface)| {
                let _ = device.destroy_surface(context, &mut surface);
                err
            })
    }

    pub fn present(&self) -> Result<(), Error> {
        let ref mut device = self.0.device.borrow_mut();
        let ref mut context = self.0.context.borrow_mut();
        if let Some(ref swap_chain) = self.0.swap_chain {
            return swap_chain.swap_buffers(device, context, surfman_chains::PreserveBuffer::No);
        }
        let mut surface = device.unbind_surface_from_context(context)?.unwrap();
        device.present_surface(context, &mut surface)?;
        device
            .bind_surface_to_context(context, surface)
            .map_err(|(err, mut surface)| {
                let _ = device.destroy_surface(context, &mut surface);
                err
            })
    }

    pub fn connection(&self) -> Connection {
        let ref device = self.0.device.borrow();
        device.connection()
    }

    pub fn adapter(&self) -> Adapter {
        let ref device = self.0.device.borrow();
        device.adapter()
    }

    pub fn native_context(&self) -> NativeContext {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.native_context(context)
    }

    pub fn native_device(&self) -> NativeDevice {
        let ref device = self.0.device.borrow();
        device.native_device()
    }

    pub fn context_attributes(&self) -> ContextAttributes {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        let ref descriptor = device.context_descriptor(context);
        device.context_descriptor_attributes(descriptor)
    }

    pub fn context_surface_info(&self) -> Result<Option<SurfaceInfo>, Error> {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.context_surface_info(context)
    }

    pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo {
        let ref device = self.0.device.borrow();
        device.surface_info(surface)
    }

    pub fn surface_texture_object(&self, surface: &SurfaceTexture) -> u32 {
        let ref device = self.0.device.borrow();
        device.surface_texture_object(surface)
    }

    pub fn get_proc_address(&self, name: &str) -> *const c_void {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.get_proc_address(context, name)
    }
}
<commit_msg>Auto merge of #27743 - lorenzbutter:master, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#![deny(unsafe_code)]

use euclid::default::Size2D;

use std::cell::RefCell;
use std::ffi::c_void;
use std::rc::Rc;
use surfman::Adapter;
use surfman::Connection;
use surfman::Context;
use surfman::ContextAttributeFlags;
use surfman::ContextAttributes;
use surfman::Device;
use surfman::Error;
use surfman::GLApi;
use surfman::GLVersion;
use surfman::NativeContext;
use surfman::NativeDevice;
use surfman::NativeWidget;
use surfman::Surface;
use surfman::SurfaceAccess;
use surfman::SurfaceInfo;
use surfman::SurfaceTexture;
use surfman::SurfaceType;
use surfman_chains::SwapChain;

/// A bridge between webrender and surfman
// TODO: move this into a different crate so that script doesn't depend on surfman
#[derive(Clone)]
pub struct WebrenderSurfman(Rc<WebrenderSurfmanData>);

struct WebrenderSurfmanData {
    device: RefCell<Device>,
    context: RefCell<Context>,
    // We either render to a swap buffer or to a native widget
    swap_chain: Option<SwapChain<Device>>,
}

impl Drop for WebrenderSurfmanData {
    fn drop(&mut self) {
        let ref mut device = self.device.borrow_mut();
        let ref mut context = self.context.borrow_mut();
        if let Some(ref swap_chain) = self.swap_chain {
            let _ = swap_chain.destroy(device, context);
        }
        let _ = device.destroy_context(context);
    }
}

impl WebrenderSurfman {
    pub fn create(
        connection: &Connection,
        adapter: &Adapter,
        surface_type: SurfaceType<NativeWidget>,
    ) -> Result<Self, Error> {
        let mut device = connection.create_device(&adapter)?;
        let flags = ContextAttributeFlags::ALPHA |
            ContextAttributeFlags::DEPTH |
            ContextAttributeFlags::STENCIL;
        let version = match connection.gl_api() {
            GLApi::GLES => GLVersion { major: 3, minor: 0 },
            GLApi::GL => GLVersion { major: 3, minor: 2 },
        };
        let context_attributes = ContextAttributes { flags, version };
        let context_descriptor = device.create_context_descriptor(&context_attributes)?;
        let mut context = device.create_context(&context_descriptor, None)?;
        let surface_access = SurfaceAccess::GPUOnly;
        let headless = match surface_type {
            SurfaceType::Widget { .. } => false,
            SurfaceType::Generic { .. } => true,
        };
        let surface = device.create_surface(&context, surface_access, surface_type)?;
        device
            .bind_surface_to_context(&mut context, surface)
            .map_err(|(err, mut surface)| {
                let _ = device.destroy_surface(&mut context, &mut surface);
                err
            })?;

        device.make_context_current(&context)?;

        let swap_chain = if headless {
            Some(SwapChain::create_attached(
                &mut device,
                &mut context,
                surface_access,
            )?)
        } else {
            None
        };
        let device = RefCell::new(device);
        let context = RefCell::new(context);
        let data = WebrenderSurfmanData {
            device,
            context,
            swap_chain,
        };
        Ok(WebrenderSurfman(Rc::new(data)))
    }

    pub fn create_surface_texture(
        &self,
        surface: Surface,
    ) -> Result<SurfaceTexture, (Error, Surface)> {
        let ref device = self.0.device.borrow();
        let ref mut context = self.0.context.borrow_mut();
        device.create_surface_texture(context, surface)
    }

    pub fn destroy_surface_texture(
        &self,
        surface_texture: SurfaceTexture,
    ) -> Result<Surface, (Error, SurfaceTexture)> {
        let ref device = self.0.device.borrow();
        let ref mut context = self.0.context.borrow_mut();
        device.destroy_surface_texture(context, surface_texture)
    }

    pub fn make_gl_context_current(&self) -> Result<(), Error> {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.make_context_current(context)
    }

    pub fn swap_chain(&self) -> Result<&SwapChain<Device>, Error> {
        self.0.swap_chain.as_ref().ok_or(Error::WidgetAttached)
    }

    pub fn resize(&self, size: Size2D<i32>) -> Result<(), Error> {
        let ref mut device = self.0.device.borrow_mut();
        let ref mut context = self.0.context.borrow_mut();
        if let Some(swap_chain) = self.0.swap_chain.as_ref() {
            return swap_chain.resize(device, context, size);
        }
        let mut surface = device.unbind_surface_from_context(context)?.unwrap();
        device.resize_surface(context, &mut surface, size)?;
        device
            .bind_surface_to_context(context, surface)
            .map_err(|(err, mut surface)| {
                let _ = device.destroy_surface(context, &mut surface);
                err
            })
    }

    pub fn present(&self) -> Result<(), Error> {
        let ref mut device = self.0.device.borrow_mut();
        let ref mut context = self.0.context.borrow_mut();
        if let Some(ref swap_chain) = self.0.swap_chain {
            return swap_chain.swap_buffers(device, context, surfman_chains::PreserveBuffer::No);
        }
        let mut surface = device.unbind_surface_from_context(context)?.unwrap();
        device.present_surface(context, &mut surface)?;
        device
            .bind_surface_to_context(context, surface)
            .map_err(|(err, mut surface)| {
                let _ = device.destroy_surface(context, &mut surface);
                err
            })
    }

    pub fn connection(&self) -> Connection {
        let ref device = self.0.device.borrow();
        device.connection()
    }

    pub fn adapter(&self) -> Adapter {
        let ref device = self.0.device.borrow();
        device.adapter()
    }

    pub fn native_context(&self) -> NativeContext {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.native_context(context)
    }

    pub fn native_device(&self) -> NativeDevice {
        let ref device = self.0.device.borrow();
        device.native_device()
    }

    pub fn context_attributes(&self) -> ContextAttributes {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        let ref descriptor = device.context_descriptor(context);
        device.context_descriptor_attributes(descriptor)
    }

    pub fn context_surface_info(&self) -> Result<Option<SurfaceInfo>, Error> {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.context_surface_info(context)
    }

    pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo {
        let ref device = self.0.device.borrow();
        device.surface_info(surface)
    }

    pub fn surface_texture_object(&self, surface: &SurfaceTexture) -> u32 {
        let ref device = self.0.device.borrow();
        device.surface_texture_object(surface)
    }

    pub fn get_proc_address(&self, name: &str) -> *const c_void {
        let ref device = self.0.device.borrow();
        let ref context = self.0.context.borrow();
        device.get_proc_address(context, name)
    }
}
<|endoftext|>


<commit_before>//! Loads "sysroot" crate.
//!
//! One confusing point here is that normally sysroot is a bunch of `.rlib`s,
//! but we can't process `.rlib` and need source code instead. The source code
//! is typically installed with `rustup component add rust-src` command.

use std::{convert::TryFrom, env, ops, path::PathBuf, process::Command};

use anyhow::{format_err, Result};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};

use crate::utf8_stdout;

#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct Sysroot {
    crates: Arena<SysrootCrateData>,
}

pub(crate) type SysrootCrate = Idx<SysrootCrateData>;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SysrootCrateData {
    pub name: String,
    pub root: AbsPathBuf,
    pub deps: Vec<SysrootCrate>,
}

impl ops::Index<SysrootCrate> for Sysroot {
    type Output = SysrootCrateData;
    fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
        &self.crates[index]
    }
}

impl Sysroot {
    pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
        // core is added as a dependency before std in order to
        // mimic rustcs dependency order
        ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?)))
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
        log::debug!("Discovering sysroot for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().ok_or_else(|| {
            format_err!("Failed to find the parent directory for {}", cargo_toml.display())
        })?;
        let sysroot_dir = discover_sysroot_dir(current_dir)?;
        let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
        let res = Sysroot::load(&sysroot_src_dir)?;
        Ok(res)
    }

    pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> {
        log::debug!("Discovering rustc source for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().unwrap();
        discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
    }

    pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> {
        let mut sysroot = Sysroot { crates: Arena::default() };

        for path in SYSROOT_CRATES.trim().lines() {
            let name = path.split('/').last().unwrap();
            let root = [format!("{}/src/lib.rs", path), format!("lib{}/lib.rs", path)]
                .iter()
                .map(|it| sysroot_src_dir.join(it))
                .find(|it| it.exists());

            if let Some(root) = root {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }

        if let Some(std) = sysroot.by_name("std") {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }

        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.by_name("core") {
                sysroot.crates[alloc].deps.push(core);
            }
        }

        if sysroot.by_name("core").is_none() {
            let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
                " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
            } else {
                ""
            };
            anyhow::bail!(
                "could not find libcore in sysroot path `{}`{}",
                sysroot_src_dir.as_ref().display(),
                var_note,
            );
        }

        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
        Some(id)
    }
}

fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
    let mut rustc = Command::new(toolchain::rustc());
    rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
    log::debug!("Discovering sysroot by {:?}", rustc);
    let stdout = utf8_stdout(rustc)?;
    Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
}

fn discover_sysroot_src_dir(
    sysroot_path: &AbsPathBuf,
    current_dir: &AbsPath,
) -> Result<AbsPathBuf> {
    if let Ok(path) = env::var("RUST_SRC_PATH") {
        let path = AbsPathBuf::try_from(path.as_str())
            .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
        let core = path.join("core");
        if core.exists() {
            log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
            return Ok(path);
        }
        log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
    }

    get_rust_src(sysroot_path)
        .or_else(|| {
            let mut rustup = Command::new(toolchain::rustup());
            rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
            utf8_stdout(rustup).ok()?;
            get_rust_src(sysroot_path)
        })
        .ok_or_else(|| {
            format_err!(
                "\
can't load standard library from sysroot
{}
(discovered via `rustc --print sysroot`)
try installing the Rust source the same way you installed rustc",
                sysroot_path.display(),
            )
        })
}

fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
    log::debug!("Checking for rustc source code: {}", rustc_src.display());
    if rustc_src.exists() {
        Some(rustc_src)
    } else {
        None
    }
}

fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    // Try the new path first since the old one still exists.
    let rust_src = sysroot_path.join("lib/rustlib/src/rust");
    log::debug!("Checking sysroot (looking for `library` and `src` dirs): {}", rust_src.display());
    ["library", "src"].iter().map(|it| rust_src.join(it)).find(|it| it.exists())
}

impl SysrootCrateData {
    pub fn root_dir(&self) -> &AbsPath {
        self.root.parent().unwrap()
    }
}

const SYSROOT_CRATES: &str = "
alloc
core
panic_abort
panic_unwind
proc_macro
profiler_builtins
std
stdarch/crates/std_detect
term
test
unwind";

const STD_DEPS: &str = "
alloc
core
panic_abort
panic_unwind
profiler_builtins
proc_macro
std_detect
term
test
unwind";
<commit_msg>sysroot: add proc_macro -> std dependency edge<commit_after>//! Loads "sysroot" crate.
//!
//! One confusing point here is that normally sysroot is a bunch of `.rlib`s,
//! but we can't process `.rlib` and need source code instead. The source code
//! is typically installed with `rustup component add rust-src` command.

use std::{convert::TryFrom, env, ops, path::PathBuf, process::Command};

use anyhow::{format_err, Result};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};

use crate::utf8_stdout;

#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct Sysroot {
    crates: Arena<SysrootCrateData>,
}

pub(crate) type SysrootCrate = Idx<SysrootCrateData>;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SysrootCrateData {
    pub name: String,
    pub root: AbsPathBuf,
    pub deps: Vec<SysrootCrate>,
}

impl ops::Index<SysrootCrate> for Sysroot {
    type Output = SysrootCrateData;
    fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
        &self.crates[index]
    }
}

impl Sysroot {
    pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
        // core is added as a dependency before std in order to
        // mimic rustcs dependency order
        ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?)))
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
        log::debug!("Discovering sysroot for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().ok_or_else(|| {
            format_err!("Failed to find the parent directory for {}", cargo_toml.display())
        })?;
        let sysroot_dir = discover_sysroot_dir(current_dir)?;
        let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
        let res = Sysroot::load(&sysroot_src_dir)?;
        Ok(res)
    }

    pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> {
        log::debug!("Discovering rustc source for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().unwrap();
        discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
    }

    pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> {
        let mut sysroot = Sysroot { crates: Arena::default() };

        for path in SYSROOT_CRATES.trim().lines() {
            let name = path.split('/').last().unwrap();
            let root = [format!("{}/src/lib.rs", path), format!("lib{}/lib.rs", path)]
                .iter()
                .map(|it| sysroot_src_dir.join(it))
                .find(|it| it.exists());

            if let Some(root) = root {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }

        if let Some(std) = sysroot.by_name("std") {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }

        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.by_name("core") {
                sysroot.crates[alloc].deps.push(core);
            }
        }

        if let Some(proc_macro) = sysroot.by_name("proc_macro") {
            if let Some(std) = sysroot.by_name("std") {
                sysroot.crates[proc_macro].deps.push(std);
            }
        }

        if sysroot.by_name("core").is_none() {
            let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
                " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
            } else {
                ""
            };
            anyhow::bail!(
                "could not find libcore in sysroot path `{}`{}",
                sysroot_src_dir.as_ref().display(),
                var_note,
            );
        }

        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
        Some(id)
    }
}

fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
    let mut rustc = Command::new(toolchain::rustc());
    rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
    log::debug!("Discovering sysroot by {:?}", rustc);
    let stdout = utf8_stdout(rustc)?;
    Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
}

fn discover_sysroot_src_dir(
    sysroot_path: &AbsPathBuf,
    current_dir: &AbsPath,
) -> Result<AbsPathBuf> {
    if let Ok(path) = env::var("RUST_SRC_PATH") {
        let path = AbsPathBuf::try_from(path.as_str())
            .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
        let core = path.join("core");
        if core.exists() {
            log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
            return Ok(path);
        }
        log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
    }

    get_rust_src(sysroot_path)
        .or_else(|| {
            let mut rustup = Command::new(toolchain::rustup());
            rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
            utf8_stdout(rustup).ok()?;
            get_rust_src(sysroot_path)
        })
        .ok_or_else(|| {
            format_err!(
                "\
can't load standard library from sysroot
{}
(discovered via `rustc --print sysroot`)
try installing the Rust source the same way you installed rustc",
                sysroot_path.display(),
            )
        })
}

fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
    log::debug!("Checking for rustc source code: {}", rustc_src.display());
    if rustc_src.exists() {
        Some(rustc_src)
    } else {
        None
    }
}

fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    // Try the new path first since the old one still exists.
    let rust_src = sysroot_path.join("lib/rustlib/src/rust");
    log::debug!("Checking sysroot (looking for `library` and `src` dirs): {}", rust_src.display());
    ["library", "src"].iter().map(|it| rust_src.join(it)).find(|it| it.exists())
}

impl SysrootCrateData {
    pub fn root_dir(&self) -> &AbsPath {
        self.root.parent().unwrap()
    }
}

const SYSROOT_CRATES: &str = "
alloc
core
panic_abort
panic_unwind
proc_macro
profiler_builtins
std
stdarch/crates/std_detect
term
test
unwind";

const STD_DEPS: &str = "
alloc
core
panic_abort
panic_unwind
profiler_builtins
proc_macro
std_detect
term
test
unwind";
<|endoftext|>


<commit_before>//! RA Proc Macro Server
//!
//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code.
//! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander.
//!
//! But we adapt it to better fit RA needs:
//!
//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipulate and interact with
//!   RA than `proc-macro2` token stream.
//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
//!   rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue)

#[allow(dead_code)]
#[doc(hidden)]
mod proc_macro;

#[doc(hidden)]
mod rustc_server;

mod dylib;

use proc_macro::bridge::client::TokenStream;
use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
use std::path::Path;

pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
    let expander = create_expander(&task.lib);

    match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
        Ok(expansion) => Ok(ExpansionResult { expansion }),
        Err(msg) => {
            Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
        }
    }
}

pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
    let expander = create_expander(&task.lib);

    ListMacrosResult { macros: expander.list_macros() }
}

fn create_expander(lib: &Path) -> dylib::Expander {
    dylib::Expander::new(lib)
        .unwrap_or_else(|err| panic!("Cannot create expander for {}: {:?}", lib.display(), err))
}

pub mod cli;

#[cfg(test)]
mod tests;
<commit_msg>Fix typo<commit_after>//! RA Proc Macro Server
//!
//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code.
//! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander.
//!
//! But we adapt it to better fit RA needs:
//!
//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with
//!   RA than `proc-macro2` token stream.
//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
//!   rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue)

#[allow(dead_code)]
#[doc(hidden)]
mod proc_macro;

#[doc(hidden)]
mod rustc_server;

mod dylib;

use proc_macro::bridge::client::TokenStream;
use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
use std::path::Path;

pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
    let expander = create_expander(&task.lib);

    match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
        Ok(expansion) => Ok(ExpansionResult { expansion }),
        Err(msg) => {
            Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
        }
    }
}

pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
    let expander = create_expander(&task.lib);

    ListMacrosResult { macros: expander.list_macros() }
}

fn create_expander(lib: &Path) -> dylib::Expander {
    dylib::Expander::new(lib)
        .unwrap_or_else(|err| panic!("Cannot create expander for {}: {:?}", lib.display(), err))
}

pub mod cli;

#[cfg(test)]
mod tests;
<|endoftext|>