<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/. */

//! Off-screen windows.
//!
//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode)
//! are managed by a platform toolkit (Glutin).

use eutil::Downcast;
use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions;
use rustc_unicode::str::Utf16Encoder;
use types::{cef_cursor_handle_t, cef_cursor_type_t, cef_rect_t};

use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
use compositing::windowing::{WindowEvent, WindowMethods};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use gleam::gl;
use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use libc::{c_char, c_void};
use msg::constellation_msg::{Key, KeyModifiers};
use msg::compositor_msg::{ReadyState, PaintState};
use std_url::Url;
use util::cursor::Cursor;
use util::geometry::ScreenPx;
use std::cell::RefCell;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::mpsc::{Sender, channel};

/// The type of an off-screen window.
#[derive(Clone)]
pub struct Window {
    cef_browser: RefCell<Option<CefBrowser>>,
}

#[cfg(target_os="macos")]
fn load_gl() {
    const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void;

    extern {
        fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::new(s).unwrap();
            dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void
        }
    });
}

#[cfg(target_os="linux")]
fn load_gl() {
    extern {
        fn glXGetProcAddress(symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::new(s).unwrap();
            glXGetProcAddress(c_str.as_ptr()) as *const c_void
        }
    });
}

impl Window {
    /// Creates a new window.
    pub fn new() -> Rc<Window> {
        load_gl();

        Rc::new(Window {
            cef_browser: RefCell::new(None),
        })
    }

    /// Sets the current browser.
    pub fn set_browser(&self, browser: CefBrowser) {
        *self.cef_browser.borrow_mut() = Some(browser)
    }

    /// Currently unimplemented.
    pub fn wait_events(&self) -> WindowEvent {
        WindowEvent::Idle
    }

    /// Returns the Cocoa cursor for a CSS cursor. These match Firefox, except where Firefox
    /// bundles custom resources (which we don't yet do).
    #[cfg(target_os="macos")]
    fn cursor_handle_for_cursor(&self, cursor: Cursor) -> cef_cursor_handle_t {
        use cocoa::base::class;

        unsafe {
            match cursor {
                Cursor::NoCursor => return 0 as cef_cursor_handle_t,
                Cursor::ContextMenuCursor => msg_send![class("NSCursor"), contextualMenuCursor],
                Cursor::GrabbingCursor => msg_send![class("NSCursor"), closedHandCursor],
                Cursor::CrosshairCursor => msg_send![class("NSCursor"), crosshairCursor],
                Cursor::CopyCursor => msg_send![class("NSCursor"), dragCopyCursor],
                Cursor::AliasCursor => msg_send![class("NSCursor"), dragLinkCursor],
                Cursor::TextCursor => msg_send![class("NSCursor"), IBeamCursor],
                Cursor::GrabCursor | Cursor::AllScrollCursor =>
                    msg_send![class("NSCursor"), openHandCursor],
                Cursor::NoDropCursor | Cursor::NotAllowedCursor =>
                    msg_send![class("NSCursor"), operationNotAllowedCursor],
                Cursor::PointerCursor => msg_send![class("NSCursor"), pointingHandCursor],
                Cursor::SResizeCursor => msg_send![class("NSCursor"), resizeDownCursor],
                Cursor::WResizeCursor => msg_send![class("NSCursor"), resizeLeftCursor],
                Cursor::EwResizeCursor | Cursor::ColResizeCursor =>
                    msg_send![class("NSCursor"), resizeLeftRightCursor],
                Cursor::EResizeCursor => msg_send![class("NSCursor"), resizeRightCursor],
                Cursor::NResizeCursor => msg_send![class("NSCursor"), resizeUpCursor],
                Cursor::NsResizeCursor | Cursor::RowResizeCursor =>
                    msg_send![class("NSCursor"), resizeUpDownCursor],
                Cursor::VerticalTextCursor => msg_send![class("NSCursor"), IBeamCursorForVerticalLayout],
                _ => msg_send![class("NSCursor"), arrowCursor],
            }
        }
    }

    #[cfg(not(target_os="macos"))]
    fn cursor_handle_for_cursor(&self, _: Cursor) -> cef_cursor_handle_t {
        0
    }
}

impl WindowMethods for Window {
    fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,u32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400, 300),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as u32, rect.height as u32)
            }
        }
    }

    fn size(&self) -> TypedSize2D<ScreenPx,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400.0, 300.0),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as f32, rect.height as f32)
            }
        }
    }

    fn present(&self) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().on_present(browser.clone());
            }
        }
    }

    fn set_ready_state(&self, ready_state: ReadyState) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let is_loading = match ready_state {
            ReadyState::Blank | ReadyState::FinishedLoading => 0,
            ReadyState::Loading | ReadyState::PerformingLayout => 1,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_loading_state_change(browser.clone(), is_loading, 1, 1);
    }

    fn set_paint_state(&self, _: PaintState) {
        // TODO(pcwalton)
    }

    fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => ScaleFactor::new(1.0),
            Some(ref browser) => {
                let mut view_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut view_rect);
                let mut backing_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut backing_rect);
                ScaleFactor::new(backing_rect.width as f32 / view_rect.width as f32)
            }
        }
    }

    #[cfg(target_os="macos")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};

        // FIXME(pcwalton)
        unsafe {
            NativeGraphicsMetadata {
                pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
            }
        }
    }

    #[cfg(target_os="linux")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        extern {
            fn cef_get_xdisplay() -> *mut c_void;
        }

        unsafe {
            NativeGraphicsMetadata {
                display: cef_get_xdisplay()
            }
        }
    }

    fn create_compositor_channel(_: &Option<Rc<Window>>)
                                 -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
        let (sender, receiver) = channel();
        (box CefCompositorProxy {
             sender: sender,
         } as Box<CompositorProxy+Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn prepare_for_composite(&self) -> bool {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().paint(browser.clone());
            }
        }
        true
    }

    fn load_end(&self) {
        // FIXME(pcwalton): The status code 200 is a lie.
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
    }

    fn set_page_title(&self, string: Option<String>) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        let mut title_visitor = frame.title_visitor.borrow_mut();
        match &mut *title_visitor {
            &mut None => {}
            &mut Some(ref mut visitor) => {
                match string {
                    None => visitor.visit(&[]),
                    Some(string) => {
                        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
                        visitor.visit(&utf16_chars)
                    }
                }
            }
        }
    }

    fn set_page_url(&self, url: Url) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        // FIXME(https://github.com/rust-lang/rust/issues/23338)
        let mut frame_url = frame.url.borrow_mut();
        *frame_url = url.to_string()
    }

    fn handle_key(&self, _: Key, _: KeyModifiers) {
        // TODO(negge)
    }

    fn set_cursor(&self, cursor: Cursor) {
        use types::{CefCursorInfo,cef_point_t,cef_size_t};
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                let cursor_handle = self.cursor_handle_for_cursor(cursor);
                let info = CefCursorInfo { hotspot: cef_point_t {x: 0, y: 0}, image_scale_factor: 0.0, buffer: 0 as *mut isize, size: cef_size_t { width: 0, height: 0 } };
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .on_cursor_change(browser.clone(), cursor_handle,
                         CT_POINTER, &info)
            }
        }
    }
}

struct CefCompositorProxy {
    sender: Sender<compositor_task::Msg>,
}

impl CompositorProxy for CefCompositorProxy {
    #[cfg(target_os="macos")]
    fn send(&mut self, msg: compositor_task::Msg) {
        use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined};
        use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype};
        use cocoa::base::nil;
        use cocoa::foundation::{NSAutoreleasePool, NSPoint};

        // Send a message and kick the OS event loop awake.
        self.sender.send(msg).unwrap();

        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            let event =
                NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
                nil,
                NSApplicationDefined,
                NSPoint::new(0.0, 0.0),
                NSEventModifierFlags::empty(),
                0.0,
                0,
                nil,
                NSEventSubtype::NSWindowExposedEventType,
                0,
                0);
            NSApp().postEvent_atStart_(event, 0);
            pool.drain();
        }
    }

    #[cfg(target_os="linux")]
    fn send(&mut self, msg: compositor_task::Msg) {
        // FIXME(pcwalton): Kick the GTK event loop awake?
        self.sender.send(msg).unwrap();
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
        box CefCompositorProxy {
            sender: self.sender.clone(),
        } as Box<CompositorProxy+Send>
    }
}

<commit_msg>implement pointer type matching for cef window cursor setting<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/. */

//! Off-screen windows.
//!
//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode)
//! are managed by a platform toolkit (Glutin).

use eutil::Downcast;
use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions;
use rustc_unicode::str::Utf16Encoder;
use types::{cef_cursor_handle_t, cef_cursor_type_t, cef_rect_t};

use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
use compositing::windowing::{WindowEvent, WindowMethods};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use gleam::gl;
use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use libc::{c_char, c_void};
use msg::constellation_msg::{Key, KeyModifiers};
use msg::compositor_msg::{ReadyState, PaintState};
use std_url::Url;
use util::cursor::Cursor;
use util::geometry::ScreenPx;
use std::cell::RefCell;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::mpsc::{Sender, channel};

/// The type of an off-screen window.
#[derive(Clone)]
pub struct Window {
    cef_browser: RefCell<Option<CefBrowser>>,
}

#[cfg(target_os="macos")]
fn load_gl() {
    const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void;

    extern {
        fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::new(s).unwrap();
            dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void
        }
    });
}

#[cfg(target_os="linux")]
fn load_gl() {
    extern {
        fn glXGetProcAddress(symbol: *const c_char) -> *mut c_void;
    }

    gl::load_with(|s| {
        unsafe {
            let c_str = CString::new(s).unwrap();
            glXGetProcAddress(c_str.as_ptr()) as *const c_void
        }
    });
}

impl Window {
    /// Creates a new window.
    pub fn new() -> Rc<Window> {
        load_gl();

        Rc::new(Window {
            cef_browser: RefCell::new(None),
        })
    }

    /// Sets the current browser.
    pub fn set_browser(&self, browser: CefBrowser) {
        *self.cef_browser.borrow_mut() = Some(browser)
    }

    /// Currently unimplemented.
    pub fn wait_events(&self) -> WindowEvent {
        WindowEvent::Idle
    }

    fn cursor_type_for_cursor(&self, cursor: Cursor) -> cef_cursor_type_t {
        match cursor {
            Cursor::NoCursor => return cef_cursor_type_t::CT_NONE,
            Cursor::ContextMenuCursor => return cef_cursor_type_t::CT_CONTEXTMENU,
            Cursor::GrabbingCursor => return cef_cursor_type_t::CT_GRABBING,
            Cursor::CrosshairCursor => return cef_cursor_type_t::CT_CROSS,
            Cursor::CopyCursor => return cef_cursor_type_t::CT_COPY,
            Cursor::AliasCursor => return cef_cursor_type_t::CT_ALIAS,
            Cursor::TextCursor => return cef_cursor_type_t::CT_IBEAM,
            Cursor::GrabCursor | Cursor::AllScrollCursor =>
                return cef_cursor_type_t::CT_GRAB,
            Cursor::NoDropCursor => return cef_cursor_type_t::CT_NODROP,
            Cursor::NotAllowedCursor => return cef_cursor_type_t::CT_NOTALLOWED,
            Cursor::PointerCursor => return cef_cursor_type_t::CT_POINTER,
            Cursor::SResizeCursor => return cef_cursor_type_t::CT_SOUTHRESIZE,
            Cursor::WResizeCursor => return cef_cursor_type_t::CT_WESTRESIZE,
            Cursor::EwResizeCursor => return cef_cursor_type_t::CT_EASTWESTRESIZE,
            Cursor::ColResizeCursor => return cef_cursor_type_t::CT_COLUMNRESIZE,
            Cursor::EResizeCursor => return cef_cursor_type_t::CT_EASTRESIZE,
            Cursor::NResizeCursor => return cef_cursor_type_t::CT_NORTHRESIZE,
            Cursor::NsResizeCursor => return cef_cursor_type_t::CT_NORTHSOUTHRESIZE,
            Cursor::RowResizeCursor => return cef_cursor_type_t::CT_ROWRESIZE,
            Cursor::VerticalTextCursor => return cef_cursor_type_t::CT_VERTICALTEXT,
            _ => return cef_cursor_type_t::CT_POINTER,
        }
    }

    /// Returns the Cocoa cursor for a CSS cursor. These match Firefox, except where Firefox
    /// bundles custom resources (which we don't yet do).
    #[cfg(target_os="macos")]
    fn cursor_handle_for_cursor(&self, cursor: Cursor) -> cef_cursor_handle_t {
        use cocoa::base::class;

        unsafe {
            match cursor {
                Cursor::NoCursor => return 0 as cef_cursor_handle_t,
                Cursor::ContextMenuCursor => msg_send![class("NSCursor"), contextualMenuCursor],
                Cursor::GrabbingCursor => msg_send![class("NSCursor"), closedHandCursor],
                Cursor::CrosshairCursor => msg_send![class("NSCursor"), crosshairCursor],
                Cursor::CopyCursor => msg_send![class("NSCursor"), dragCopyCursor],
                Cursor::AliasCursor => msg_send![class("NSCursor"), dragLinkCursor],
                Cursor::TextCursor => msg_send![class("NSCursor"), IBeamCursor],
                Cursor::GrabCursor | Cursor::AllScrollCursor =>
                    msg_send![class("NSCursor"), openHandCursor],
                Cursor::NoDropCursor | Cursor::NotAllowedCursor =>
                    msg_send![class("NSCursor"), operationNotAllowedCursor],
                Cursor::PointerCursor => msg_send![class("NSCursor"), pointingHandCursor],
                Cursor::SResizeCursor => msg_send![class("NSCursor"), resizeDownCursor],
                Cursor::WResizeCursor => msg_send![class("NSCursor"), resizeLeftCursor],
                Cursor::EwResizeCursor | Cursor::ColResizeCursor =>
                    msg_send![class("NSCursor"), resizeLeftRightCursor],
                Cursor::EResizeCursor => msg_send![class("NSCursor"), resizeRightCursor],
                Cursor::NResizeCursor => msg_send![class("NSCursor"), resizeUpCursor],
                Cursor::NsResizeCursor | Cursor::RowResizeCursor =>
                    msg_send![class("NSCursor"), resizeUpDownCursor],
                Cursor::VerticalTextCursor => msg_send![class("NSCursor"), IBeamCursorForVerticalLayout],
                _ => msg_send![class("NSCursor"), arrowCursor],
            }
        }
    }

    #[cfg(not(target_os="macos"))]
    fn cursor_handle_for_cursor(&self, _: Cursor) -> cef_cursor_handle_t {
        0
    }
}

impl WindowMethods for Window {
    fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,u32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400, 300),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as u32, rect.height as u32)
            }
        }
    }

    fn size(&self) -> TypedSize2D<ScreenPx,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => TypedSize2D(400.0, 300.0),
            Some(ref browser) => {
                let mut rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut rect);
                TypedSize2D(rect.width as f32, rect.height as f32)
            }
        }
    }

    fn present(&self) {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().on_present(browser.clone());
            }
        }
    }

    fn set_ready_state(&self, ready_state: ReadyState) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let is_loading = match ready_state {
            ReadyState::Blank | ReadyState::FinishedLoading => 0,
            ReadyState::Loading | ReadyState::PerformingLayout => 1,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_loading_state_change(browser.clone(), is_loading, 1, 1);
    }

    fn set_paint_state(&self, _: PaintState) {
        // TODO(pcwalton)
    }

    fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => ScaleFactor::new(1.0),
            Some(ref browser) => {
                let mut view_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_view_rect((*browser).clone(), &mut view_rect);
                let mut backing_rect = cef_rect_t::zero();
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .get_backing_rect((*browser).clone(), &mut backing_rect);
                ScaleFactor::new(backing_rect.width as f32 / view_rect.width as f32)
            }
        }
    }

    #[cfg(target_os="macos")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};

        // FIXME(pcwalton)
        unsafe {
            NativeGraphicsMetadata {
                pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
            }
        }
    }

    #[cfg(target_os="linux")]
    fn native_metadata(&self) -> NativeGraphicsMetadata {
        extern {
            fn cef_get_xdisplay() -> *mut c_void;
        }

        unsafe {
            NativeGraphicsMetadata {
                display: cef_get_xdisplay()
            }
        }
    }

    fn create_compositor_channel(_: &Option<Rc<Window>>)
                                 -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
        let (sender, receiver) = channel();
        (box CefCompositorProxy {
             sender: sender,
         } as Box<CompositorProxy+Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn prepare_for_composite(&self) -> bool {
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                browser.get_host().get_client().get_render_handler().paint(browser.clone());
            }
        }
        true
    }

    fn load_end(&self) {
        // FIXME(pcwalton): The status code 200 is a lie.
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        browser.get_host()
               .get_client()
               .get_load_handler()
               .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
    }

    fn set_page_title(&self, string: Option<String>) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        let mut title_visitor = frame.title_visitor.borrow_mut();
        match &mut *title_visitor {
            &mut None => {}
            &mut Some(ref mut visitor) => {
                match string {
                    None => visitor.visit(&[]),
                    Some(string) => {
                        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
                        visitor.visit(&utf16_chars)
                    }
                }
            }
        }
    }

    fn set_page_url(&self, url: Url) {
        let browser = self.cef_browser.borrow();
        let browser = match *browser {
            None => return,
            Some(ref browser) => browser,
        };
        let frame = browser.get_main_frame();
        let frame = frame.downcast();
        // FIXME(https://github.com/rust-lang/rust/issues/23338)
        let mut frame_url = frame.url.borrow_mut();
        *frame_url = url.to_string()
    }

    fn handle_key(&self, _: Key, _: KeyModifiers) {
        // TODO(negge)
    }

    fn set_cursor(&self, cursor: Cursor) {
        use types::{CefCursorInfo,cef_point_t,cef_size_t};
        let browser = self.cef_browser.borrow();
        match *browser {
            None => {}
            Some(ref browser) => {
                let cursor_handle = self.cursor_handle_for_cursor(cursor);
                let info = CefCursorInfo { hotspot: cef_point_t {x: 0, y: 0}, image_scale_factor: 0.0, buffer: 0 as *mut isize, size: cef_size_t { width: 0, height: 0 } };
                browser.get_host()
                       .get_client()
                       .get_render_handler()
                       .on_cursor_change(browser.clone(), cursor_handle,
                         self.cursor_type_for_cursor(cursor), &info)
            }
        }
    }
}

struct CefCompositorProxy {
    sender: Sender<compositor_task::Msg>,
}

impl CompositorProxy for CefCompositorProxy {
    #[cfg(target_os="macos")]
    fn send(&mut self, msg: compositor_task::Msg) {
        use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined};
        use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype};
        use cocoa::base::nil;
        use cocoa::foundation::{NSAutoreleasePool, NSPoint};

        // Send a message and kick the OS event loop awake.
        self.sender.send(msg).unwrap();

        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            let event =
                NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
                nil,
                NSApplicationDefined,
                NSPoint::new(0.0, 0.0),
                NSEventModifierFlags::empty(),
                0.0,
                0,
                nil,
                NSEventSubtype::NSWindowExposedEventType,
                0,
                0);
            NSApp().postEvent_atStart_(event, 0);
            pool.drain();
        }
    }

    #[cfg(target_os="linux")]
    fn send(&mut self, msg: compositor_task::Msg) {
        // FIXME(pcwalton): Kick the GTK event loop awake?
        self.sender.send(msg).unwrap();
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
        box CefCompositorProxy {
            sender: self.sender.clone(),
        } as Box<CompositorProxy+Send>
    }
}

<|endoftext|>


<commit_before>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;

struct Platform {
    id: cl_platform_id
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create opencl context!"
    }

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: &Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create command queue!"
    }

    CommandQueue { cqueue: cqueue }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: &Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create buffer!"
    }

    Buffer { buffer: buffer, size: size }
}

pub fn enqueue_write_buffer(cqueue: CommandQueue, buf: Buffer, host_vector) {
    let ret = clEnqueueWriteBuffer(cqueue.cqueue, buf.buffer, CL_TRUE, 0,
                                   buf.size as libc::size_t,
                                   vec::raw::to_ptr(host_vector) as *libc::c_void,
                                   0, ptr::null(), ptr::null());
    if ret != CL_SUCCESS {
        fail ~"Failed to enqueue write buffer!"
    }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: Context, device: Device, binary_path: & Path){
    let mut errcode = 0;
    let binary = io::read_whole_file_str(binary_path).get();
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.leb() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::cuchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    if errcode != CL_SUCCESS {
        fail ~"Failed to create open cl program with binary!"
    }

    Program { prg: program }
}

<commit_msg>minor fix to function definition.<commit_after>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;

struct Platform {
    id: cl_platform_id
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create opencl context!"
    }

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: &Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create command queue!"
    }

    CommandQueue { cqueue: cqueue }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: &Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create buffer!"
    }

    Buffer { buffer: buffer, size: size }
}

pub fn enqueue_write_buffer(cqueue: CommandQueue, buf: Buffer, host_vector: ~[int]) {
    let ret = clEnqueueWriteBuffer(cqueue.cqueue, buf.buffer, CL_TRUE, 0,
                                   buf.size as libc::size_t,
                                   vec::raw::to_ptr(host_vector) as *libc::c_void,
                                   0, ptr::null(), ptr::null());
    if ret != CL_SUCCESS {
        fail ~"Failed to enqueue write buffer!"
    }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: Context, device: Device, binary_path: & Path){
    let mut errcode = 0;
    let binary = io::read_whole_file_str(binary_path).get();
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.leb() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::cuchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    if errcode != CL_SUCCESS {
        fail ~"Failed to create open cl program with binary!"
    }

    Program { prg: program }
}

<|endoftext|>


<commit_before>use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::path;
use infer::TokensResult;

pub(super) fn infer_v3(api_st: &Ident, schema: &Yaml) -> TokensResult {
    let mut tokens = quote! {
        #[allow(plugin_as_library)]
        extern crate tapioca;
        use tapioca::*;
    };

    let paths = schema["paths"].clone();
    for (path, path_schema) in paths.as_hash().expect("Paths must be a map.") {
        let path = path.as_str().expect("Path must be a string.");
        tokens.append(path::infer_v3(&api_st, &path, &path_schema)?);
    }

    Ok(tokens)
}
<commit_msg>Refactor nested quasi-quotes<commit_after>use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::path;
use infer::TokensResult;

pub(super) fn infer_v3(api_st: &Ident, schema: &Yaml) -> TokensResult {
    let paths = schema["paths"].clone();
    let path_impls: Vec<Tokens> = paths.as_hash().expect("Paths must be a map.")
        .iter()
        .map(|(path, path_schema)| path::infer_v3(
            &api_st, path.as_str().expect("Path must be a string."), &path_schema
        ).unwrap())
        .collect();

    Ok(quote! {
        #[allow(plugin_as_library)]
        extern crate tapioca;
        use tapioca::traits::*;

        #(#path_impls)*
    })
}
<|endoftext|>


<commit_before>// This assumes that either uchardet dev packages are installed where
// `pkg_config` can find them, or that CMake and supporting tools are
// available.  Patches are welcome to help make it work on other operating
// systems!

extern crate pkg_config;
extern crate cmake;

use cmake::Config;

fn main() {
    // Do nothing if this package is already provided by the system.
    if pkg_config::find_library("uchardet").is_ok() { return; }

    // Build uchardet ourselves
    let mut config = Config::new("uchardet");

    // Mustn't build the binaries as they aren't compatible with Windows
    // and cause a compiler error
    config.define("BUILD_BINARY", "OFF");
    config.define("BUILD_STATIC", "ON");
    config.define("BUILD_SHARED_LIBS", "OFF");

    if cfg!(target_os = "windows") && cfg!(target_env = "gnu") {
        // FIXME: This is only needed on newer versions of gcc (>5 ?); Older
        //        versions fail with "unrecognized command line option" and
        //        abort the build; We need to somehow detect the compiler version
        // Disable sized deallocation as we're unable to link when it's enabled
        config.cxxflag("-fno-sized-deallocation");
    }

    let dst = config.build();

    // Print out link instructions for Cargo.
    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-search=native={}/lib64", dst.display());
    println!("cargo:rustc-link-lib=static=uchardet");

    if !(cfg!(target_os = "windows") && cfg!(target_env = "msvc")) {
        // Not needed on windows-msvc

        // Decide how to link our C++ runtime.  Feel free to submit patches
        // to make this work on your platform.  Other likely options are "c++"
        // and "c++abi" depending on OS and compiler.
        let cxx_abi = "stdc++";
        println!("cargo:rustc-flags=-l {}", cxx_abi);
    }
}
<commit_msg>Fix windows-gnu build<commit_after>// This assumes that either uchardet dev packages are installed where
// `pkg_config` can find them, or that CMake and supporting tools are
// available.  Patches are welcome to help make it work on other operating
// systems!

extern crate pkg_config;
extern crate cmake;

use std::env;
use cmake::Config;

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");

    // Do nothing if this package is already provided by the system.
    if pkg_config::find_library("uchardet").is_ok() { return; }

    // Build uchardet ourselves
    let mut config = Config::new("uchardet");

    // Mustn't build the binaries as they aren't compatible with Windows
    // and cause a compiler error
    config.define("BUILD_BINARY", "OFF");
    config.define("BUILD_STATIC", "ON");
    config.define("BUILD_SHARED_LIBS", "OFF");

    if target.contains("windows-gnu") {
        // FIXME: This is only needed on newer versions of gcc (>5 ?); Older
        //        versions fail with "unrecognized command line option" and
        //        abort the build; We need to somehow detect the compiler version
        // Disable sized deallocation as we're unable to link when it's enabled
        config.cxxflag("-fno-sized-deallocation");
    }

    // unset the makeflags (jobserver currently has a bug on this system)
    // For more information see https://github.com/alexcrichton/jobserver-rs/issues/4
    if target.contains("windows-gnu") {
        env::set_var("CARGO_MAKEFLAGS", "");
    }

    let dst = config.build();

    // Print out link instructions for Cargo.
    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-search=native={}/lib64", dst.display());
    println!("cargo:rustc-link-lib=static=uchardet");

    // Not needed on windows-msvc
    if !target.contains("windows-msvc") {
        // Decide how to link our C++ runtime.  Feel free to submit patches
        // to make this work on your platform.  Other likely options are "c++"
        // and "c++abi" depending on OS and compiler.
        let cxx_abi = "stdc++";
        println!("cargo:rustc-flags=-l {}", cxx_abi);
    }

    // make TLS work.  For more information see https://github.com/rust-lang/rust/issues/41607
    // The right fix would be static-nobundle but that is nightly only.
    if target.contains("windows-gnu") {
        println!("cargo:rustc-link-lib=dylib=gcc_eh");
        println!("cargo:rustc-link-lib=dylib=pthread");
    }
}
<|endoftext|>


<commit_before>use decoders::*;
use decoders::basics::*;

pub fn is_mrw(buf: &[u8]) -> bool {
  BEu32(buf,0) == 0x004D524D
}

pub struct MrwDecoder<'a> {
  buffer: &'a [u8],
  data_offset: usize,
  raw_width: u16,
  raw_height: u16,
  packed: bool,
  wb_coeffs: [f32;4],
}

impl<'a> MrwDecoder<'a> {
  pub fn new(buf: &[u8]) -> MrwDecoder {
    let data_offset: usize = (BEu32(buf, 4) + 8) as usize;
    let mut raw_height: u16 = 0;
    let mut raw_width: u16 = 0;
    let mut packed = false;
    let mut wb_coeffs: [f32;4] = [0.0,0.0,0.0,0.0];

    let mut currpos: usize = 8;
    // At most we read 20 bytes from currpos so check we don't step outside that
    while currpos+20 < data_offset {
      let tag: u32 = BEu32(buf,currpos);
      let len: u32 = BEu32(buf,currpos+4);

      match tag {
        0x505244 => { // PRD
          raw_height = BEu16(buf,currpos+16);
          raw_width = BEu16(buf,currpos+18);
          packed = buf[currpos+24] == 12;
        }
        0x574247 => { // WBG
          for i in 0..4 {
            wb_coeffs[i] = (BEu16(buf, currpos+12+i*2)) as f32;
          }
        }
        0x545457 => { // TTW
          // Base value for offsets needs to be at the beginning of the
          // TIFF block, not the file
//          FileMap *f = new FileMap(mFile, currpos+8);
//          if (little == getHostEndianness())
//            tiff_meta = new TiffIFDBE(f, 8);
//          else
//            tiff_meta = new TiffIFD(f, 8);
//          delete f;
//          break;
        }
        _ => {}
      }
      currpos += (len+8) as usize;
    }

    MrwDecoder {
      buffer: buf,
      data_offset: data_offset,
      raw_width: raw_width,
      raw_height: raw_height,
      packed: packed,
      wb_coeffs: wb_coeffs,
    }
  }
}

impl<'a> Decoder for MrwDecoder<'a> {
  fn make(&self) -> String {
    "Minolta".to_string()
  }

  fn model(&self) -> String {
    "SomeModel".to_string()
  }

  fn image(&self) -> Image {
    let src = &self.buffer[self.data_offset .. self.buffer.len()];
    let w = self.raw_width as usize;
    let h = self.raw_height as usize;

    let buffer = if self.packed {
      decode_12be(&src, w, h)
    }
    else {
      decode_12be_unpacked(&src, w, h)
    };

    Image {
      width: self.raw_width as u32,
      height: self.raw_height as u32,
      wb_coeffs: self.wb_coeffs,
      data: buffer.into_boxed_slice(),
    }
  }
}
<commit_msg>MRW: Return WB in correct order<commit_after>use decoders::*;
use decoders::basics::*;
use std::f32;

pub fn is_mrw(buf: &[u8]) -> bool {
  BEu32(buf,0) == 0x004D524D
}

pub struct MrwDecoder<'a> {
  buffer: &'a [u8],
  data_offset: usize,
  raw_width: u16,
  raw_height: u16,
  packed: bool,
  wb_vals: [u16;4],
}

impl<'a> MrwDecoder<'a> {
  pub fn new(buf: &[u8]) -> MrwDecoder {
    let data_offset: usize = (BEu32(buf, 4) + 8) as usize;
    let mut raw_height: u16 = 0;
    let mut raw_width: u16 = 0;
    let mut packed = false;
    let mut wb_vals: [u16;4] = [0;4];

    let mut currpos: usize = 8;
    // At most we read 20 bytes from currpos so check we don't step outside that
    while currpos+20 < data_offset {
      let tag: u32 = BEu32(buf,currpos);
      let len: u32 = BEu32(buf,currpos+4);

      match tag {
        0x505244 => { // PRD
          raw_height = BEu16(buf,currpos+16);
          raw_width = BEu16(buf,currpos+18);
          packed = buf[currpos+24] == 12;
        }
        0x574247 => { // WBG
          for i in 0..4 {
            wb_vals[i] = BEu16(buf, currpos+12+i*2);
          }
        }
        0x545457 => { // TTW
          // Base value for offsets needs to be at the beginning of the
          // TIFF block, not the file
//          FileMap *f = new FileMap(mFile, currpos+8);
//          if (little == getHostEndianness())
//            tiff_meta = new TiffIFDBE(f, 8);
//          else
//            tiff_meta = new TiffIFD(f, 8);
//          delete f;
//          break;
        }
        _ => {}
      }
      currpos += (len+8) as usize;
    }

    MrwDecoder {
      buffer: buf,
      data_offset: data_offset,
      raw_width: raw_width,
      raw_height: raw_height,
      packed: packed,
      wb_vals: wb_vals,
    }
  }
}

impl<'a> Decoder for MrwDecoder<'a> {
  fn make(&self) -> String {
    "Minolta".to_string()
  }

  fn model(&self) -> String {
    "SomeModel".to_string()
  }

  fn image(&self) -> Image {
    let src = &self.buffer[self.data_offset .. self.buffer.len()];
    let w = self.raw_width as usize;
    let h = self.raw_height as usize;

    let buffer = if self.packed {
      decode_12be(&src, w, h)
    }
    else {
      decode_12be_unpacked(&src, w, h)
    };

    let swapped_wb = false;

    let wb_coeffs = if swapped_wb {
      [self.wb_vals[2] as f32,
       self.wb_vals[0] as f32,
       self.wb_vals[1] as f32,
       f32::NAN]
    } else {
      [self.wb_vals[0] as f32,
       self.wb_vals[1] as f32,
       self.wb_vals[3] as f32,
       f32::NAN]
    };

    Image {
      width: self.raw_width as u32,
      height: self.raw_height as u32,
      wb_coeffs: wb_coeffs,
      data: buffer.into_boxed_slice(),
    }
  }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   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
//
//       http://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.
//

//!
//! The DFA compiler converts NDFAs into DFAs, using a DFA builder. Usually the `SymbolRangeDfaBuilder` would be used but the
//! compiler can be used with any object implementing the `DfaBuilder` trait.
//!
//! The NDFA should not have any overlapping symbols, which is to say symbols that are not equal and yet could match the same
//! input symbol. If the builder finds that two NDFA states have identical output symbols, then the builder will pick the symbol
//! that compares as being lower as the final output symbol.
//!
//! Any NDFA can be converted into a DFA: if the NDFA can move to two states as the result of a particular input symbol, the DFA
//! just needs a single new state representing both those possible states. In this way, the NDFA can be converted into a form where
//! every input symbol only maps to a single transition.
//!
//! Normally the compiler isn't used directly: it's called by implementations of `PrepareToMatch`. It's useful for manually building
//! DFAs for other purposes or for creating new types of state machine that aren't already in this library.
//!
//! ```
//! let ndfa     = "abc".into_pattern().to_ndfa("Success");
//! let builder  = SymbolRangeDfaBuilder::new();
//!
//! let state_machine = DfaCompiler::build(ndfa, builder);
//! ```
//!

use std::marker::PhantomData;
use std::collections::HashSet;
use std::collections::HashMap;
use std::iter::*;

use super::dfa_builder::*;
use super::state_machine::*;

///
/// Builds a deterministic finite automaton from a NDFA
///
pub struct DfaCompiler<InputSymbol: Ord+Clone, OutputSymbol, DfaType, Ndfa: StateMachine<InputSymbol, OutputSymbol>, Builder: DfaBuilder<InputSymbol, OutputSymbol, DfaType>> {
    /// State machine that is to be compiled
    ndfa: Ndfa,

    /// Builder where the state machine should be generated
    builder: Builder,

    // Phantom data to poke Rust's type system (it's too dumb to see that InputSymbol is used in both Ndfa and Builder there via the type constraint)
    #[allow(dead_code)]
    phantom: (PhantomData<InputSymbol>, PhantomData<OutputSymbol>, PhantomData<DfaType>)
}

/// Represents a state in the DFA (one or more states from the source)
#[derive(Eq, PartialEq, Hash, Clone)]
struct DfaState {
    /// List of the states from the NDFA that make up this DFA state (will be ordered if `dedupe` is called)
    source_states: Vec<StateId>
}

impl DfaState {
    /// Creates a DFA state from a set of source states
    fn create(source_states: Vec<StateId>) -> DfaState {
        let mut set = HashSet::new();
        for state in source_states {
            set.insert(state);
        }

        DfaState { source_states: Vec::from_iter(set.into_iter()) }
    }

    /// Removes any duplicate states from source_states
    fn dedupe(&mut self) {
        self.source_states.sort();
        self.source_states.dedup();
    }
}

/// Represents the transitions for a source state
struct DfaTransitions<InputSymbol, OutputSymbol: Ord> {
    /// ID of this state in the resulting DFA
    state_id: StateId,

    /// Transitions for this state (will be unique if `merge_states` is called)
    transitions: Vec<(InputSymbol, DfaState)>,

    /// The output symbols for this state (empty if this is not an accepting state)
    output: Vec<OutputSymbol>
}

impl<InputSymbol: Ord+Clone, OutputSymbol: Ord> DfaTransitions<InputSymbol, OutputSymbol> {
    /// Goes through the transitions in the object and merges the states of any with the same symbol
    fn merge_states(&mut self) {
        if self.transitions.len() > 0 {
            // Order the transitions so that if two transitions have the same input symbol, they are neighbours
            self.transitions.sort_by(|a, b| {
                let &(ref symbol_a, _) = a;
                let &(ref symbol_b, _) = b;

                symbol_a.cmp(symbol_b)
            });

            // For any transition that has a duplicate state, combine it with the previous state
            let mut new_transitions = vec![];
            new_transitions.push(self.transitions[0].clone());

            for transit_idx in 1..self.transitions.len() {
                let len          = new_transitions.len();
                let this_transit = &mut self.transitions[transit_idx];

                if new_transitions[len-1].0 == this_transit.0 {
                    // Same symbol: expand this state
                    new_transitions[len-1].1.source_states.append(&mut this_transit.1.source_states);
                } else {
                    // Different symbol: add new state
                    new_transitions.push(this_transit.clone());
                }
            }

            // Ensure all of the states are sorted and de-duplicated
            for transit in &mut new_transitions {
                transit.1.dedupe();
            }

            // Update the transitions
            self.transitions = new_transitions;
        }
    }

    ///
    /// Finds the output symbol that corresponds to this state
    ///
    /// Rule is that if there is more than one output symbol then the symbol whose value is ordered lowest is the output for this state
    ///
    fn output_symbol(&mut self) -> Option<&OutputSymbol> {
        if self.output.len() > 0 {
            self.output.sort();
            Some(&self.output[0])
        } else {
            None
        }
    }
}

impl<InputSymbol: Ord+Clone, OutputSymbol: Ord+Clone, DfaType, Ndfa: StateMachine<InputSymbol, OutputSymbol>, Builder: DfaBuilder<InputSymbol, OutputSymbol, DfaType>>
    DfaCompiler<InputSymbol, OutputSymbol, DfaType, Ndfa, Builder> {
    ///
    /// Builds a DFA using an NDFA and a builder
    ///
    pub fn build(ndfa: Ndfa, builder: Builder) -> DfaType {
        let compiler = DfaCompiler::new(ndfa, builder);
        compiler.compile()
    }

    ///
    /// Creates a new DFA compiler using a particular builder and NDFA
    ///
    pub fn new(ndfa: Ndfa, builder: Builder) -> Self {
        DfaCompiler { ndfa: ndfa, builder: builder, phantom: (PhantomData, PhantomData, PhantomData) }
    }

    ///
    /// Compiles the NDFA into a DFA
    ///
    pub fn compile(self) -> DfaType {
        // We assume that input symbols are non-overlapping, which is not automatically the case for symbol ranges
        // You can call Ndfa.fix_overlapping_ranges() to remove any overlapping ranges from an NDFA

        // Work out the state mapping for each input symbol
        let mut states       = vec![];
        let mut known_states = HashMap::new();
        let mut to_process   = vec![];

        // All state machines have state 0 as their starting state
        let state_zero = DfaState::create(vec![0]);

        known_states.insert(state_zero.clone(), 0);
        to_process.push(state_zero);

        while let Some(state) = to_process.pop() {
            // Create a new transitions object for this state
            let mut transitions = vec![];
            let mut output      = vec![];

            for source_state in &state.source_states {
                let source_transitions = self.ndfa.get_transitions_for_state(*source_state);

                for (symbol, state) in source_transitions {
                    transitions.push((symbol, DfaState::create(vec![state])));
                }

                if let Some(source_output) = self.ndfa.output_symbol_for_state(*source_state) {
                    output.push(source_output.clone());
                }
            }

            // Merge it so that we only have one transition per symbol
            let mut dfa_transitions = DfaTransitions { state_id: states.len() as StateId, transitions: transitions, output: output };
            dfa_transitions.merge_states();

            // Process any generated states that are not already in the DFA
            for &(_, ref maybe_new_state) in &dfa_transitions.transitions {
                if !known_states.contains_key(maybe_new_state) {
                    to_process.push(maybe_new_state.clone());
                }
            }

            // Store the new state
            known_states.insert(state.clone(), dfa_transitions.state_id);
            states.push(dfa_transitions);
        }

        // Build the DFA
        let mut builder = self.builder;

        for mut dfa_state in states {
            builder.start_state();

            if let Some(output_symbol) = dfa_state.output_symbol() {
                builder.accept(output_symbol.clone());
            }

            for (symbol, target_state) in dfa_state.transitions {
                builder.transition(symbol, known_states[&target_state]);
            }
        }

        // Generate the final DFA
        builder.build()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::regular_pattern::*;
    use super::super::state_machine::*;
    use super::super::symbol_range_dfa::*;
    use super::super::pattern_matcher::*;
    use super::super::symbol_reader::*;

    #[test]
    fn can_create_compiler() {
        let ndfa     = "abc".into_pattern().to_ndfa("success");
        let builder  = SymbolRangeDfaBuilder::new();

        DfaCompiler::new(ndfa, builder);
    }

    #[test]
    fn can_build_dfa() {
        // Generate a state machine from the "abc" pattern
        let ndfa     = "abc".into_pattern().to_ndfa("Success");
        let builder  = SymbolRangeDfaBuilder::new();

        let state_machine = DfaCompiler::build(ndfa, builder);

        // Read back 'abc' manually
        let mut state = state_machine.start();
        let mut input = "abc".read_symbols();

        while let More(this_state) = state {
            let next_state =
                if let Some(next_char) = input.next_symbol() {
                    this_state.next(next_char)
                } else {
                    this_state.finish()
                };

            state = next_state;
        }

        // Check for acceptance
        if let Accept(count, output) = state {
            assert!(count == 3);
            assert!(output == &"Success");
        } else {
            assert!(false);
        }
    }

    #[test]
    fn can_build_dfa_with_overlapping_range() {
        // Generate a state machine from the "abc" pattern
        let ndfa     = (MatchRange('a', 'c').append("a")).or(MatchRange('b', 'd').append("b")).to_ndfa("Success");
        let builder  = SymbolRangeDfaBuilder::new();

        let state_machine = DfaCompiler::build(ndfa, builder);

        println!("{:?}", state_machine);

        // Read back 'ba' manually
        let mut state = state_machine.start();
        let mut input = "ba".read_symbols();

        while let More(this_state) = state {
            let next_state =
                if let Some(next_char) = input.next_symbol() {
                    this_state.next(next_char)
                } else {
                    this_state.finish()
                };

            state = next_state;
        }

        // Check for acceptance
        if let Accept(count, output) = state {
            assert!(count == 2);
            assert!(output == &"Success");
        } else {
            assert!(false);
        }

        // Read back 'bb' manually
        state = state_machine.start();
        input = "bb".read_symbols();

        while let More(this_state) = state {
            let next_state =
                if let Some(next_char) = input.next_symbol() {
                    this_state.next(next_char)
                } else {
                    this_state.finish()
                };

            state = next_state;
        }

        // Check for acceptance
        if let Accept(count, output) = state {
            assert!(count == 2);
            assert!(output == &"Success");
        } else {
            assert!(false);
        }
    }
}
<commit_msg>Fix doc comment<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   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
//
//       http://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.
//

//!
//! The DFA compiler converts NDFAs into DFAs, using a DFA builder. Usually the `SymbolRangeDfaBuilder` would be used but the
//! compiler can be used with any object implementing the `DfaBuilder` trait.
//!
//! The NDFA should not have any overlapping symbols, which is to say symbols that are not equal and yet could match the same
//! input symbol. If the builder finds that two NDFA states have identical output symbols, then the builder will pick the symbol
//! that compares as being lower as the final output symbol.
//!
//! Any NDFA can be converted into a DFA: if the NDFA can move to two states as the result of a particular input symbol, the DFA
//! just needs a single new state representing both those possible states. In this way, the NDFA can be converted into a form where
//! every input symbol only maps to a single transition.
//!
//! Normally the compiler isn't used directly: it's called by implementations of `PrepareToMatch`. It's useful for manually building
//! DFAs for other purposes or for creating new types of state machine that aren't already in this library.
//!
//! ```
//! # use ndfa::*;
//! let ndfa     = "abc".into_pattern().to_ndfa("Success");
//! let builder  = SymbolRangeDfaBuilder::new();
//!
//! let state_machine = DfaCompiler::build(ndfa, builder);
//! ```
//!

use std::marker::PhantomData;
use std::collections::HashSet;
use std::collections::HashMap;
use std::iter::*;

use super::dfa_builder::*;
use super::state_machine::*;

///
/// Builds a deterministic finite automaton from a NDFA
///
pub struct DfaCompiler<InputSymbol: Ord+Clone, OutputSymbol, DfaType, Ndfa: StateMachine<InputSymbol, OutputSymbol>, Builder: DfaBuilder<InputSymbol, OutputSymbol, DfaType>> {
    /// State machine that is to be compiled
    ndfa: Ndfa,

    /// Builder where the state machine should be generated
    builder: Builder,

    // Phantom data to poke Rust's type system (it's too dumb to see that InputSymbol is used in both Ndfa and Builder there via the type constraint)
    #[allow(dead_code)]
    phantom: (PhantomData<InputSymbol>, PhantomData<OutputSymbol>, PhantomData<DfaType>)
}

/// Represents a state in the DFA (one or more states from the source)
#[derive(Eq, PartialEq, Hash, Clone)]
struct DfaState {
    /// List of the states from the NDFA that make up this DFA state (will be ordered if `dedupe` is called)
    source_states: Vec<StateId>
}

impl DfaState {
    /// Creates a DFA state from a set of source states
    fn create(source_states: Vec<StateId>) -> DfaState {
        let mut set = HashSet::new();
        for state in source_states {
            set.insert(state);
        }

        DfaState { source_states: Vec::from_iter(set.into_iter()) }
    }

    /// Removes any duplicate states from source_states
    fn dedupe(&mut self) {
        self.source_states.sort();
        self.source_states.dedup();
    }
}

/// Represents the transitions for a source state
struct DfaTransitions<InputSymbol, OutputSymbol: Ord> {
    /// ID of this state in the resulting DFA
    state_id: StateId,

    /// Transitions for this state (will be unique if `merge_states` is called)
    transitions: Vec<(InputSymbol, DfaState)>,

    /// The output symbols for this state (empty if this is not an accepting state)
    output: Vec<OutputSymbol>
}

impl<InputSymbol: Ord+Clone, OutputSymbol: Ord> DfaTransitions<InputSymbol, OutputSymbol> {
    /// Goes through the transitions in the object and merges the states of any with the same symbol
    fn merge_states(&mut self) {
        if self.transitions.len() > 0 {
            // Order the transitions so that if two transitions have the same input symbol, they are neighbours
            self.transitions.sort_by(|a, b| {
                let &(ref symbol_a, _) = a;
                let &(ref symbol_b, _) = b;

                symbol_a.cmp(symbol_b)
            });

            // For any transition that has a duplicate state, combine it with the previous state
            let mut new_transitions = vec![];
            new_transitions.push(self.transitions[0].clone());

            for transit_idx in 1..self.transitions.len() {
                let len          = new_transitions.len();
                let this_transit = &mut self.transitions[transit_idx];

                if new_transitions[len-1].0 == this_transit.0 {
                    // Same symbol: expand this state
                    new_transitions[len-1].1.source_states.append(&mut this_transit.1.source_states);
                } else {
                    // Different symbol: add new state
                    new_transitions.push(this_transit.clone());
                }
            }

            // Ensure all of the states are sorted and de-duplicated
            for transit in &mut new_transitions {
                transit.1.dedupe();
            }

            // Update the transitions
            self.transitions = new_transitions;
        }
    }

    ///
    /// Finds the output symbol that corresponds to this state
    ///
    /// Rule is that if there is more than one output symbol then the symbol whose value is ordered lowest is the output for this state
    ///
    fn output_symbol(&mut self) -> Option<&OutputSymbol> {
        if self.output.len() > 0 {
            self.output.sort();
            Some(&self.output[0])
        } else {
            None
        }
    }
}

impl<InputSymbol: Ord+Clone, OutputSymbol: Ord+Clone, DfaType, Ndfa: StateMachine<InputSymbol, OutputSymbol>, Builder: DfaBuilder<InputSymbol, OutputSymbol, DfaType>>
    DfaCompiler<InputSymbol, OutputSymbol, DfaType, Ndfa, Builder> {
    ///
    /// Builds a DFA using an NDFA and a builder
    ///
    pub fn build(ndfa: Ndfa, builder: Builder) -> DfaType {
        let compiler = DfaCompiler::new(ndfa, builder);
        compiler.compile()
    }

    ///
    /// Creates a new DFA compiler using a particular builder and NDFA
    ///
    pub fn new(ndfa: Ndfa, builder: Builder) -> Self {
        DfaCompiler { ndfa: ndfa, builder: builder, phantom: (PhantomData, PhantomData, PhantomData) }
    }

    ///
    /// Compiles the NDFA into a DFA
    ///
    pub fn compile(self) -> DfaType {
        // We assume that input symbols are non-overlapping, which is not automatically the case for symbol ranges
        // You can call Ndfa.fix_overlapping_ranges() to remove any overlapping ranges from an NDFA

        // Work out the state mapping for each input symbol
        let mut states       = vec![];
        let mut known_states = HashMap::new();
        let mut to_process   = vec![];

        // All state machines have state 0 as their starting state
        let state_zero = DfaState::create(vec![0]);

        known_states.insert(state_zero.clone(), 0);
        to_process.push(state_zero);

        while let Some(state) = to_process.pop() {
            // Create a new transitions object for this state
            let mut transitions = vec![];
            let mut output      = vec![];

            for source_state in &state.source_states {
                let source_transitions = self.ndfa.get_transitions_for_state(*source_state);

                for (symbol, state) in source_transitions {
                    transitions.push((symbol, DfaState::create(vec![state])));
                }

                if let Some(source_output) = self.ndfa.output_symbol_for_state(*source_state) {
                    output.push(source_output.clone());
                }
            }

            // Merge it so that we only have one transition per symbol
            let mut dfa_transitions = DfaTransitions { state_id: states.len() as StateId, transitions: transitions, output: output };
            dfa_transitions.merge_states();

            // Process any generated states that are not already in the DFA
            for &(_, ref maybe_new_state) in &dfa_transitions.transitions {
                if !known_states.contains_key(maybe_new_state) {
                    to_process.push(maybe_new_state.clone());
                }
            }

            // Store the new state
            known_states.insert(state.clone(), dfa_transitions.state_id);
            states.push(dfa_transitions);
        }

        // Build the DFA
        let mut builder = self.builder;

        for mut dfa_state in states {
            builder.start_state();

            if let Some(output_symbol) = dfa_state.output_symbol() {
                builder.accept(output_symbol.clone());
            }

            for (symbol, target_state) in dfa_state.transitions {
                builder.transition(symbol, known_states[&target_state]);
            }
        }

        // Generate the final DFA
        builder.build()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::regular_pattern::*;
    use super::super::state_machine::*;
    use super::super::symbol_range_dfa::*;
    use super::super::pattern_matcher::*;
    use super::super::symbol_reader::*;

    #[test]
    fn can_create_compiler() {
        let ndfa     = "abc".into_pattern().to_ndfa("success");
        let builder  = SymbolRangeDfaBuilder::new();

        DfaCompiler::new(ndfa, builder);
    }

    #[test]
    fn can_build_dfa() {
        // Generate a state machine from the "abc" pattern
        let ndfa     = "abc".into_pattern().to_ndfa("Success");
        let builder  = SymbolRangeDfaBuilder::new();

        let state_machine = DfaCompiler::build(ndfa, builder);

        // Read back 'abc' manually
        let mut state = state_machine.start();
        let mut input = "abc".read_symbols();

        while let More(this_state) = state {
            let next_state =
                if let Some(next_char) = input.next_symbol() {
                    this_state.next(next_char)
                } else {
                    this_state.finish()
                };

            state = next_state;
        }

        // Check for acceptance
        if let Accept(count, output) = state {
            assert!(count == 3);
            assert!(output == &"Success");
        } else {
            assert!(false);
        }
    }

    #[test]
    fn can_build_dfa_with_overlapping_range() {
        // Generate a state machine from the "abc" pattern
        let ndfa     = (MatchRange('a', 'c').append("a")).or(MatchRange('b', 'd').append("b")).to_ndfa("Success");
        let builder  = SymbolRangeDfaBuilder::new();

        let state_machine = DfaCompiler::build(ndfa, builder);

        println!("{:?}", state_machine);

        // Read back 'ba' manually
        let mut state = state_machine.start();
        let mut input = "ba".read_symbols();

        while let More(this_state) = state {
            let next_state =
                if let Some(next_char) = input.next_symbol() {
                    this_state.next(next_char)
                } else {
                    this_state.finish()
                };

            state = next_state;
        }

        // Check for acceptance
        if let Accept(count, output) = state {
            assert!(count == 2);
            assert!(output == &"Success");
        } else {
            assert!(false);
        }

        // Read back 'bb' manually
        state = state_machine.start();
        input = "bb".read_symbols();

        while let More(this_state) = state {
            let next_state =
                if let Some(next_char) = input.next_symbol() {
                    this_state.next(next_char)
                } else {
                    this_state.finish()
                };

            state = next_state;
        }

        // Check for acceptance
        if let Accept(count, output) = state {
            assert!(count == 2);
            assert!(output == &"Success");
        } else {
            assert!(false);
        }
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::path::Path;

use crate::vm::VM;
use crate::vm::{exception_get_and_clear, Fct, FctId};
use dora_parser::ast::{self, Ast};
use dora_parser::error::msg::Msg;

use crate::driver::cmd;
use crate::object;
use crate::os;
use crate::timer::Timer;
use dora_parser::lexer::position::Position;
use dora_parser::lexer::reader::Reader;

use crate::semck;
use crate::semck::specialize::specialize_class_id;
use crate::ty::BuiltinType;
use dora_parser::parser::Parser;

pub fn start(content: Option<&str>) -> i32 {
    os::mem::init_page_size();
    let fuzzing = content.is_some();

    let args = if fuzzing {
        Default::default()
    } else {
        cmd::parse()
    };

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut ast = Ast::new();
    let empty = Ast::new();
    let mut vm = VM::new(args, &empty);

    if let Err(code) = parse_all_files(&mut vm, &mut ast, content) {
        return code;
    }

    vm.ast = *

    if vm.args.flag_emit_ast {
        ast::dump::dump(&vm.ast, &vm.interner);
    }

    semck::check(&mut vm);

    // register signal handler
    os::register_signals();

    let main = if vm.args.cmd_test {
        None
    } else {
        find_main(&vm)
    };

    if vm.diag.lock().has_errors() {
        vm.diag.lock().dump();
        let no_errors = vm.diag.lock().errors().len();

        if no_errors == 1 {
            println!("{} error found.", no_errors);
        } else {
            println!("{} errors found.", no_errors);
        }

        return 1;
    }

    // if --check given, stop after type/semantic check
    if vm.args.flag_check {
        return 0;
    }

    let mut timer = Timer::new(vm.args.flag_gc_stats);

    vm.threads.attach_current_thread();

    let code = if vm.args.cmd_test {
        run_tests(&vm)
    } else {
        run_main(&vm, main.unwrap())
    };

    vm.threads.detach_current_thread();
    vm.threads.join_all();

    os::unregister_signals();

    if vm.args.flag_gc_stats {
        let duration = timer.stop();
        vm.dump_gc_summary(duration);
    }

    code
}

fn parse_all_files(vm: &mut VM, ast: &mut Ast, content: Option<&str>) -> Result<(), i32> {
    let fuzzing = content.is_some();

    parse_dir("stdlib", vm, ast).and_then(|_| {
        if fuzzing {
            return parse_str(content.unwrap(), vm, ast);
        }

        let arg_file = vm.args.arg_file.clone();
        let path = Path::new(&arg_file);

        if path.is_file() {
            parse_file(&arg_file, vm, ast)
        } else if path.is_dir() {
            parse_dir(&arg_file, vm, ast)
        } else {
            println!("file or directory `{}` does not exist.", &arg_file);
            Err(1)
        }
    })
}

fn run_tests<'ast>(vm: &VM<'ast>) -> i32 {
    let mut tests = 0;
    let mut passed = 0;

    for fct in vm.fcts.iter() {
        let fct = fct.read();

        if !is_test_fct(vm, &*fct) {
            continue;
        }

        tests += 1;

        print!("test {} ... ", vm.interner.str(fct.name));

        if run_test(vm, fct.id) {
            passed += 1;
            println!("ok");
        } else {
            println!("failed");
        }
    }

    println!(
        "{} tests executed; {} passed; {} failed.",
        tests,
        passed,
        tests - passed
    );

    // if all tests passed exit with 0, otherwise 1
    if tests == passed {
        0
    } else {
        1
    }
}

fn run_test<'ast>(vm: &VM<'ast>, fct: FctId) -> bool {
    let testing_class = vm.vips.testing_class;
    let testing_class = specialize_class_id(vm, testing_class);
    let testing = object::alloc(vm, testing_class).cast();
    vm.run_test(fct, testing);

    // see if test failed with exception
    let exception = exception_get_and_clear();

    exception.is_null() && !testing.has_failed()
}

fn is_test_fct<'ast>(vm: &VM<'ast>, fct: &Fct<'ast>) -> bool {
    // tests need to be standalone functions, with no return type and a single parameter
    if !fct.parent.is_none() || !fct.return_type.is_unit() || fct.param_types.len() != 1 {
        return false;
    }

    // parameter needs to be of type Testing
    let testing_cls = vm.cls(vm.vips.testing_class);
    if fct.param_types[0] != testing_cls {
        return false;
    }

    // the functions name needs to start with `test`
    let fct_name = vm.interner.str(fct.name);
    fct_name.starts_with("test")
}

fn run_main<'ast>(vm: &VM<'ast>, main: FctId) -> i32 {
    let res = vm.run(main);
    let fct = vm.fcts.idx(main);
    let fct = fct.read();
    let is_unit = fct.return_type.is_unit();

    // main-fct without return value exits with status 0
    if is_unit {
        0

    // else use return value of main for exit status
    } else {
        res
    }
}

fn parse_dir(dirname: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let path = Path::new(dirname);

    if path.is_dir() {
        for entry in fs::read_dir(path).unwrap() {
            let path = entry.unwrap().path();

            if path.is_file() && path.extension().unwrap() == "dora" {
                parse_file(path.to_str().unwrap(), vm, ast)?;
            }
        }

        Ok(())
    } else {
        println!("directory `{}` does not exist.", dirname);

        Err(1)
    }
}

fn parse_file(filename: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = if filename == "-" {
        match Reader::from_input() {
            Err(_) => {
                println!("unable to read from stdin.");
                return Err(1);
            }

            Ok(reader) => reader,
        }
    } else {
        match Reader::from_file(filename) {
            Err(_) => {
                println!("unable to read file `{}`", filename);
                return Err(1);
            }

            Ok(reader) => reader,
        }
    };

    let parser = Parser::new(reader, &vm.id_generator, ast, &mut vm.interner);

    match parser.parse() {
        Ok(file) => {
            vm.files.push(file);
            Ok(())
        }

        Err(error) => {
            println!("{}", error);
            println!("1 error found.");

            Err(1)
        }
    }
}

fn parse_str(file: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = Reader::from_string(file);

    if let Err(error) = Parser::new(reader, &vm.id_generator, ast, &mut vm.interner).parse() {
        println!("{}", error);
        println!("1 error found.");
        return Err(1);
    }

    Ok(())
}

fn find_main<'ast>(vm: &VM<'ast>) -> Option<FctId> {
    let name = vm.interner.intern("main");
    let fctid = match vm.sym.lock().get_fct(name) {
        Some(id) => id,
        None => {
            vm.diag
                .lock()
                .report_without_path(Position::new(1, 1), Msg::MainNotFound);
            return None;
        }
    };

    let fct = vm.fcts.idx(fctid);
    let fct = fct.read();
    let ret = fct.return_type;

    if (ret != BuiltinType::Unit && ret != BuiltinType::Int) || fct.params_without_self().len() > 0
    {
        let pos = fct.ast.pos;
        vm.diag
            .lock()
            .report_without_path(pos, Msg::WrongMainDefinition);
        return None;
    }

    Some(fctid)
}
<commit_msg>store code string in VM as well<commit_after>use std::fs;
use std::path::Path;

use crate::vm::VM;
use crate::vm::{exception_get_and_clear, Fct, FctId};
use dora_parser::ast::{self, Ast};
use dora_parser::error::msg::Msg;

use crate::driver::cmd;
use crate::object;
use crate::os;
use crate::timer::Timer;
use dora_parser::lexer::position::Position;
use dora_parser::lexer::reader::Reader;

use crate::semck;
use crate::semck::specialize::specialize_class_id;
use crate::ty::BuiltinType;
use dora_parser::parser::Parser;

pub fn start(content: Option<&str>) -> i32 {
    os::mem::init_page_size();
    let fuzzing = content.is_some();

    let args = if fuzzing {
        Default::default()
    } else {
        cmd::parse()
    };

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut ast = Ast::new();
    let empty = Ast::new();
    let mut vm = VM::new(args, &empty);

    if let Err(code) = parse_all_files(&mut vm, &mut ast, content) {
        return code;
    }

    vm.ast = *

    if vm.args.flag_emit_ast {
        ast::dump::dump(&vm.ast, &vm.interner);
    }

    semck::check(&mut vm);

    // register signal handler
    os::register_signals();

    let main = if vm.args.cmd_test {
        None
    } else {
        find_main(&vm)
    };

    if vm.diag.lock().has_errors() {
        vm.diag.lock().dump();
        let no_errors = vm.diag.lock().errors().len();

        if no_errors == 1 {
            println!("{} error found.", no_errors);
        } else {
            println!("{} errors found.", no_errors);
        }

        return 1;
    }

    // if --check given, stop after type/semantic check
    if vm.args.flag_check {
        return 0;
    }

    let mut timer = Timer::new(vm.args.flag_gc_stats);

    vm.threads.attach_current_thread();

    let code = if vm.args.cmd_test {
        run_tests(&vm)
    } else {
        run_main(&vm, main.unwrap())
    };

    vm.threads.detach_current_thread();
    vm.threads.join_all();

    os::unregister_signals();

    if vm.args.flag_gc_stats {
        let duration = timer.stop();
        vm.dump_gc_summary(duration);
    }

    code
}

fn parse_all_files(vm: &mut VM, ast: &mut Ast, content: Option<&str>) -> Result<(), i32> {
    let fuzzing = content.is_some();

    parse_dir("stdlib", vm, ast).and_then(|_| {
        if fuzzing {
            return parse_str(content.unwrap(), vm, ast);
        }

        let arg_file = vm.args.arg_file.clone();
        let path = Path::new(&arg_file);

        if path.is_file() {
            parse_file(&arg_file, vm, ast)
        } else if path.is_dir() {
            parse_dir(&arg_file, vm, ast)
        } else {
            println!("file or directory `{}` does not exist.", &arg_file);
            Err(1)
        }
    })
}

fn run_tests<'ast>(vm: &VM<'ast>) -> i32 {
    let mut tests = 0;
    let mut passed = 0;

    for fct in vm.fcts.iter() {
        let fct = fct.read();

        if !is_test_fct(vm, &*fct) {
            continue;
        }

        tests += 1;

        print!("test {} ... ", vm.interner.str(fct.name));

        if run_test(vm, fct.id) {
            passed += 1;
            println!("ok");
        } else {
            println!("failed");
        }
    }

    println!(
        "{} tests executed; {} passed; {} failed.",
        tests,
        passed,
        tests - passed
    );

    // if all tests passed exit with 0, otherwise 1
    if tests == passed {
        0
    } else {
        1
    }
}

fn run_test<'ast>(vm: &VM<'ast>, fct: FctId) -> bool {
    let testing_class = vm.vips.testing_class;
    let testing_class = specialize_class_id(vm, testing_class);
    let testing = object::alloc(vm, testing_class).cast();
    vm.run_test(fct, testing);

    // see if test failed with exception
    let exception = exception_get_and_clear();

    exception.is_null() && !testing.has_failed()
}

fn is_test_fct<'ast>(vm: &VM<'ast>, fct: &Fct<'ast>) -> bool {
    // tests need to be standalone functions, with no return type and a single parameter
    if !fct.parent.is_none() || !fct.return_type.is_unit() || fct.param_types.len() != 1 {
        return false;
    }

    // parameter needs to be of type Testing
    let testing_cls = vm.cls(vm.vips.testing_class);
    if fct.param_types[0] != testing_cls {
        return false;
    }

    // the functions name needs to start with `test`
    let fct_name = vm.interner.str(fct.name);
    fct_name.starts_with("test")
}

fn run_main<'ast>(vm: &VM<'ast>, main: FctId) -> i32 {
    let res = vm.run(main);
    let fct = vm.fcts.idx(main);
    let fct = fct.read();
    let is_unit = fct.return_type.is_unit();

    // main-fct without return value exits with status 0
    if is_unit {
        0

    // else use return value of main for exit status
    } else {
        res
    }
}

fn parse_dir(dirname: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let path = Path::new(dirname);

    if path.is_dir() {
        for entry in fs::read_dir(path).unwrap() {
            let path = entry.unwrap().path();

            if path.is_file() && path.extension().unwrap() == "dora" {
                parse_file(path.to_str().unwrap(), vm, ast)?;
            }
        }

        Ok(())
    } else {
        println!("directory `{}` does not exist.", dirname);

        Err(1)
    }
}

fn parse_file(filename: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = if filename == "-" {
        match Reader::from_input() {
            Err(_) => {
                println!("unable to read from stdin.");
                return Err(1);
            }

            Ok(reader) => reader,
        }
    } else {
        match Reader::from_file(filename) {
            Err(_) => {
                println!("unable to read file `{}`", filename);
                return Err(1);
            }

            Ok(reader) => reader,
        }
    };

    let parser = Parser::new(reader, &vm.id_generator, ast, &mut vm.interner);

    match parser.parse() {
        Ok(file) => {
            vm.files.push(file);
            Ok(())
        }

        Err(error) => {
            println!("{}", error);
            println!("1 error found.");

            Err(1)
        }
    }
}

fn parse_str(file: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = Reader::from_string(file);
    let parser = Parser::new(reader, &vm.id_generator, ast, &mut vm.interner);

    match parser.parse() {
        Ok(file) => {
            vm.files.push(file);
            Ok(())
        }

        Err(error) => {
            println!("{}", error);
            println!("1 error found.");

            Err(1)
        }
    }
}

fn find_main<'ast>(vm: &VM<'ast>) -> Option<FctId> {
    let name = vm.interner.intern("main");
    let fctid = match vm.sym.lock().get_fct(name) {
        Some(id) => id,
        None => {
            vm.diag
                .lock()
                .report_without_path(Position::new(1, 1), Msg::MainNotFound);
            return None;
        }
    };

    let fct = vm.fcts.idx(fctid);
    let fct = fct.read();
    let ret = fct.return_type;

    if (ret != BuiltinType::Unit && ret != BuiltinType::Int) || fct.params_without_self().len() > 0
    {
        let pos = fct.ast.pos;
        vm.diag
            .lock()
            .report_without_path(pos, Msg::WrongMainDefinition);
        return None;
    }

    Some(fctid)
}
<|endoftext|>