<commit_before>//! Gradient Descent
//!
//! Implementation of gradient descent algorithm. Module contains
//! the struct GradientDesc which is instantiated within models
//! implementing the Optimizable trait.
//!
//! Currently standard batch gradient descent is the only implemented
//! optimization algorithm but there is flexibility to introduce new
//! algorithms and git them into the same scheme easily.

use learning::optim::{Optimizable, OptimAlgorithm};
use linalg::vector::Vector;

pub struct GradientDesc {
	alpha: f64,
	iters: usize
}

impl GradientDesc {

	/// Constructs a new gradient descent algorithm.
	///
	/// Currently defaults to 1000 iterations and step size
	/// of 0.3. More control coming soon!
	pub fn new() -> GradientDesc {
		GradientDesc{ alpha: 0.3, iters: 1000 }
	}
}

// This could take in the model to be optimized, which has an optimizable trait.
// This trait specifies a function which we can call within this trait.
impl<M: Optimizable> OptimAlgorithm<M> for GradientDesc {
    fn optimize(&self, model: &M, start: &[f64], data: &M::Data, outputs: &M::Target) -> Vec<f64> {

		let mut optimizing_val = Vector::new(start.to_vec());

		for _i in 0..self.iters {
			optimizing_val = optimizing_val.clone() - Vector::new(model.compute_grad(&optimizing_val.data[..], data, outputs)) * self.alpha;
		}
		optimizing_val.data
	}
} <commit_msg>Added basic and custom constructions.<commit_after>//! Gradient Descent
//!
//! Implementation of gradient descent algorithm. Module contains
//! the struct GradientDesc which is instantiated within models
//! implementing the Optimizable trait.
//!
//! Currently standard batch gradient descent is the only implemented
//! optimization algorithm but there is flexibility to introduce new
//! algorithms and git them into the same scheme easily.

use learning::optim::{Optimizable, OptimAlgorithm};
use linalg::vector::Vector;

pub struct GradientDesc {
	pub alpha: f64,
	pub iters: usize
}

impl Default for GradientDesc {

	/// Constructs a gradient descent algorithm
	/// with default settings.
	///
	/// Uses 10000 iterations and step size of 0.3.
	fn default() -> GradientDesc {
		GradientDesc{ alpha: 0.3, iters: 10000 }
	}
}

impl GradientDesc {

	/// Construct a gradient descent algorithm.
	///
	/// Requires the step size and iteration count
	/// to be specified.
	///
	/// # Examples
	///
	/// ```
	/// use rusty_machine::learning::optim::grad_desc::GradientDesc;
	///
	/// let gd = GradientDesc::new(0.3, 10000);
	/// ```
	pub fn new(alpha: f64, iters: usize) -> GradientDesc {
		GradientDesc{ alpha: alpha, iters: iters }
	}
}

// This could take in the model to be optimized, which has an optimizable trait.
// This trait specifies a function which we can call within this trait.
impl<M: Optimizable> OptimAlgorithm<M> for GradientDesc {

    fn optimize(&self, model: &M, start: &[f64], data: &M::Data, outputs: &M::Target) -> Vec<f64> {

		let mut optimizing_val = Vector::new(start.to_vec());

		for _i in 0..self.iters {
			optimizing_val = optimizing_val.clone() - Vector::new(model.compute_grad(&optimizing_val.data[..], data, outputs)) * self.alpha;
		}
		optimizing_val.data
	}
} <|endoftext|>


<commit_before>use ra_db::SourceDatabase;
use ra_syntax::{
    AstNode, ast,
    algo::find_token_at_offset
};

use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};

pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let file = db.parse(position.file_id);

    let node = find_token_at_offset(file.syntax(), position.offset).find_map(|token| {
        token
            .parent()
            .ancestors()
            .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
    })?;

    let analyzer = hir::SourceAnalyzer::new(db, position.file_id, node, None);

    let ty: hir::Ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e))
    {
        ty
    } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
        ty
    } else {
        return None;
    };

    if let Some((adt_def, _)) = ty.as_adt() {
        let nav = NavigationTarget::from_adt_def(db, adt_def);
        return Some(RangeInfo::new(node.range(), vec![nav]));
    };

    None
}

#[cfg(test)]
mod tests {
    use crate::mock_analysis::analysis_and_position;

    fn check_goto(fixture: &str, expected: &str) {
        let (analysis, pos) = analysis_and_position(fixture);

        let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info;
        assert_eq!(navs.len(), 1);
        let nav = navs.pop().unwrap();
        nav.assert_match(expected);
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check_goto(
            "
            //- /lib.rs
            struct Foo;
            fn foo() {
                let f: Foo;
                f<|>
            }
            ",
            "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
        );
    }
}
<commit_msg>Simplify<commit_after>use ra_db::SourceDatabase;
use ra_syntax::{
    AstNode, ast,
    algo::find_token_at_offset
};

use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};

pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let file = db.parse(position.file_id);

    let node = find_token_at_offset(file.syntax(), position.offset).find_map(|token| {
        token
            .parent()
            .ancestors()
            .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
    })?;

    let analyzer = hir::SourceAnalyzer::new(db, position.file_id, node, None);

    let ty: hir::Ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e))
    {
        ty
    } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
        ty
    } else {
        return None;
    };

    let (adt_def, _) = ty.as_adt()?;
    let nav = NavigationTarget::from_adt_def(db, adt_def);

    Some(RangeInfo::new(node.range(), vec![nav]))
}

#[cfg(test)]
mod tests {
    use crate::mock_analysis::analysis_and_position;

    fn check_goto(fixture: &str, expected: &str) {
        let (analysis, pos) = analysis_and_position(fixture);

        let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info;
        assert_eq!(navs.len(), 1);
        let nav = navs.pop().unwrap();
        nav.assert_match(expected);
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check_goto(
            "
            //- /lib.rs
            struct Foo;
            fn foo() {
                let f: Foo;
                f<|>
            }
            ",
            "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
        );
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "aead-cipher-2022")]
use std::time::Duration;

use cfg_if::cfg_if;
#[cfg(feature = "aead-cipher-2022")]
use lru_time_cache::LruCache;
#[allow(dead_code)]
use spin::Mutex as SpinMutex;

#[cfg(feature = "aead-cipher-2022")]
use crate::relay::tcprelay::proxy_stream::protocol::v2::SERVER_STREAM_TIMESTAMP_MAX_DIFF;
use crate::{config::ServerType, crypto::CipherKind};

#[cfg(feature = "security-replay-attack-detect")]
use self::ppbloom::PingPongBloom;

#[cfg(feature = "security-replay-attack-detect")]
mod ppbloom;

/// A Bloom Filter based protector against replay attach
pub struct ReplayProtector {
    // Check for duplicated IV/Nonce, for prevent replay attack
    // https://github.com/shadowsocks/shadowsocks-org/issues/44
    #[cfg(feature = "security-replay-attack-detect")]
    nonce_ppbloom: SpinMutex<PingPongBloom>,

    // AEAD 2022 specific filter.
    // AEAD 2022 TCP protocol has a timestamp, which can already reject most of the replay requests,
    // so we only need to remember nonce that are in the valid time range
    #[cfg(feature = "aead-cipher-2022")]
    nonce_set: SpinMutex<LruCache<Vec<u8>, ()>>,
}

impl ReplayProtector {
    /// Create a new ReplayProtector
    #[allow(unused_variables)]
    pub fn new(config_type: ServerType) -> ReplayProtector {
        ReplayProtector {
            #[cfg(feature = "security-replay-attack-detect")]
            nonce_ppbloom: SpinMutex::new(PingPongBloom::new(config_type)),
            #[cfg(feature = "aead-cipher-2022")]
            nonce_set: SpinMutex::new(LruCache::with_expiry_duration(Duration::from_secs(
                SERVER_STREAM_TIMESTAMP_MAX_DIFF,
            ))),
        }
    }

    /// Check if nonce exist or not
    #[inline(always)]
    pub fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
        // Plain cipher doesn't have a nonce
        // Always treated as non-duplicated
        if nonce.is_empty() {
            return false;
        }

        #[cfg(feature = "aead-cipher-2022")]
        if method.is_aead_2022() {
            let mut set = self.nonce_set.lock();
            if set.get(nonce).is_some() {
                return true;
            }
            set.insert(nonce.to_vec(), ());
            return false;
        }

        cfg_if! {
            if #[cfg(feature = "security-replay-attack-detect")] {
                let mut ppbloom = self.nonce_ppbloom.lock();
                ppbloom.check_and_set(nonce)
            } else {
                false
            }
        }
    }
}
<commit_msg>replay filter should remember 2*MAX_DIFF<commit_after>#[cfg(feature = "aead-cipher-2022")]
use std::time::Duration;

use cfg_if::cfg_if;
#[cfg(feature = "aead-cipher-2022")]
use lru_time_cache::LruCache;
#[allow(dead_code)]
use spin::Mutex as SpinMutex;

#[cfg(feature = "aead-cipher-2022")]
use crate::relay::tcprelay::proxy_stream::protocol::v2::SERVER_STREAM_TIMESTAMP_MAX_DIFF;
use crate::{config::ServerType, crypto::CipherKind};

#[cfg(feature = "security-replay-attack-detect")]
use self::ppbloom::PingPongBloom;

#[cfg(feature = "security-replay-attack-detect")]
mod ppbloom;

/// A Bloom Filter based protector against replay attach
pub struct ReplayProtector {
    // Check for duplicated IV/Nonce, for prevent replay attack
    // https://github.com/shadowsocks/shadowsocks-org/issues/44
    #[cfg(feature = "security-replay-attack-detect")]
    nonce_ppbloom: SpinMutex<PingPongBloom>,

    // AEAD 2022 specific filter.
    // AEAD 2022 TCP protocol has a timestamp, which can already reject most of the replay requests,
    // so we only need to remember nonce that are in the valid time range
    #[cfg(feature = "aead-cipher-2022")]
    nonce_set: SpinMutex<LruCache<Vec<u8>, ()>>,
}

impl ReplayProtector {
    /// Create a new ReplayProtector
    #[allow(unused_variables)]
    pub fn new(config_type: ServerType) -> ReplayProtector {
        ReplayProtector {
            #[cfg(feature = "security-replay-attack-detect")]
            nonce_ppbloom: SpinMutex::new(PingPongBloom::new(config_type)),
            #[cfg(feature = "aead-cipher-2022")]
            nonce_set: SpinMutex::new(LruCache::with_expiry_duration(Duration::from_secs(
                SERVER_STREAM_TIMESTAMP_MAX_DIFF * 2,
            ))),
        }
    }

    /// Check if nonce exist or not
    #[inline(always)]
    pub fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
        // Plain cipher doesn't have a nonce
        // Always treated as non-duplicated
        if nonce.is_empty() {
            return false;
        }

        #[cfg(feature = "aead-cipher-2022")]
        if method.is_aead_2022() {
            let mut set = self.nonce_set.lock();
            if set.get(nonce).is_some() {
                return true;
            }
            set.insert(nonce.to_vec(), ());
            return false;
        }

        cfg_if! {
            if #[cfg(feature = "security-replay-attack-detect")] {
                let mut ppbloom = self.nonce_ppbloom.lock();
                ppbloom.check_and_set(nonce)
            } else {
                false
            }
        }
    }
}
<|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/. */

//#![feature(mpsc_select)]

//! A GPU based Webrender.
//!
//! It serves as an experimental render backend for [Servo](https://servo.org/),
//! but it can also be used as such in a standalone application.
//!
//! # External dependencies
//! Webrender currently depends on [FreeType](https://www.freetype.org/)
//!
//! # Api Structure
//! The main entry point to webrender is the `webrender::renderer::Renderer`.
//!
//! By calling `Renderer::new(...)` you get a `Renderer`, as well as a `RenderApiSender`.
//! Your `Renderer` is responsible to render the previously processed frames onto the screen.
//!
//! By calling `yourRenderApiSenderInstance.create_api()`, you'll get a `RenderApi` instance,
//! which is responsible for the processing of new frames. A worker thread is used internally to
//! untie the workload from the application thread and therefore be able
//! to make better use of multicore systems.
//!
//! What is referred to as a `frame`, is the current geometry on the screen.
//! A new Frame is created by calling [`set_root_display_list()`][newframe] on the `RenderApi`.
//! When the geometry is processed, the application will be informed via a `RenderNotifier`,
//! a callback which you employ with [set_render_notifier][notifier] on the `Renderer`
//! More information about [stacking contexts][stacking_contexts].
//!
//! `set_root_display_list()` also needs to be supplied with `BuiltDisplayList`s.
//! These are obtained by finalizing a `DisplayListBuilder`. These are used to draw your geometry.
//! But it doesn't only contain trivial geometry, it can also store another StackingContext, as
//! they're nestable.
//!
//! [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_root_display_list
//! [notifier]: renderer/struct.Renderer.html#method.set_render_notifier

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate thread_profiler;

mod batch_builder;
mod clip_scroll_node;
mod clip_scroll_tree;
mod debug_colors;
mod debug_font_data;
mod debug_render;
mod device;
mod frame;
mod frame_builder;
mod freelist;
mod geometry;
mod gpu_store;
mod internal_types;
mod mask_cache;
mod prim_store;
mod profiler;
mod record;
mod render_backend;
mod render_task;
mod resource_cache;
mod scene;
mod spring;
mod texture_cache;
mod tiling;
mod util;

#[cfg(feature = "webgl")]
mod webgl_types;

#[cfg(not(feature = "webgl"))]
#[path = "webgl_stubs.rs"]
mod webgl_types;

mod shader_source {
    include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
}

pub use record::{ApiRecordingReceiver, BinaryRecorder, WEBRENDER_RECORDING_HEADER};

mod platform {
    #[cfg(target_os="macos")]
    pub use platform::macos::font;
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub use platform::unix::font;
    #[cfg(target_os = "windows")]
    pub use platform::windows::font;

    #[cfg(target_os="macos")]
    pub mod macos {
        pub mod font;
    }
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub mod unix {
        pub mod font;
    }
    #[cfg(target_os = "windows")]
    pub mod windows {
        pub mod font;
    }
}

pub mod renderer;

#[cfg(target_os="macos")]
extern crate core_graphics;
#[cfg(target_os="macos")]
extern crate core_text;

#[cfg(all(unix, not(target_os="macos")))]
extern crate freetype;

#[cfg(target_os = "windows")]
extern crate dwrote;

extern crate app_units;
extern crate bincode;
extern crate euclid;
extern crate fnv;
extern crate gleam;
extern crate num_traits;
//extern crate notify;
extern crate time;
extern crate webrender_traits;
#[cfg(feature = "webgl")]
extern crate offscreen_gl_context;
extern crate byteorder;
extern crate threadpool;

#[cfg(any(target_os="macos", target_os="windows"))]
extern crate gamma_lut;

pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
pub use renderer::{Renderer, RendererOptions};
<commit_msg>Remove commented out feature for mpsc_select.<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/. */

//! A GPU based Webrender.
//!
//! It serves as an experimental render backend for [Servo](https://servo.org/),
//! but it can also be used as such in a standalone application.
//!
//! # External dependencies
//! Webrender currently depends on [FreeType](https://www.freetype.org/)
//!
//! # Api Structure
//! The main entry point to webrender is the `webrender::renderer::Renderer`.
//!
//! By calling `Renderer::new(...)` you get a `Renderer`, as well as a `RenderApiSender`.
//! Your `Renderer` is responsible to render the previously processed frames onto the screen.
//!
//! By calling `yourRenderApiSenderInstance.create_api()`, you'll get a `RenderApi` instance,
//! which is responsible for the processing of new frames. A worker thread is used internally to
//! untie the workload from the application thread and therefore be able
//! to make better use of multicore systems.
//!
//! What is referred to as a `frame`, is the current geometry on the screen.
//! A new Frame is created by calling [`set_root_display_list()`][newframe] on the `RenderApi`.
//! When the geometry is processed, the application will be informed via a `RenderNotifier`,
//! a callback which you employ with [set_render_notifier][notifier] on the `Renderer`
//! More information about [stacking contexts][stacking_contexts].
//!
//! `set_root_display_list()` also needs to be supplied with `BuiltDisplayList`s.
//! These are obtained by finalizing a `DisplayListBuilder`. These are used to draw your geometry.
//! But it doesn't only contain trivial geometry, it can also store another StackingContext, as
//! they're nestable.
//!
//! [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_root_display_list
//! [notifier]: renderer/struct.Renderer.html#method.set_render_notifier

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate thread_profiler;

mod batch_builder;
mod clip_scroll_node;
mod clip_scroll_tree;
mod debug_colors;
mod debug_font_data;
mod debug_render;
mod device;
mod frame;
mod frame_builder;
mod freelist;
mod geometry;
mod gpu_store;
mod internal_types;
mod mask_cache;
mod prim_store;
mod profiler;
mod record;
mod render_backend;
mod render_task;
mod resource_cache;
mod scene;
mod spring;
mod texture_cache;
mod tiling;
mod util;

#[cfg(feature = "webgl")]
mod webgl_types;

#[cfg(not(feature = "webgl"))]
#[path = "webgl_stubs.rs"]
mod webgl_types;

mod shader_source {
    include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
}

pub use record::{ApiRecordingReceiver, BinaryRecorder, WEBRENDER_RECORDING_HEADER};

mod platform {
    #[cfg(target_os="macos")]
    pub use platform::macos::font;
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub use platform::unix::font;
    #[cfg(target_os = "windows")]
    pub use platform::windows::font;

    #[cfg(target_os="macos")]
    pub mod macos {
        pub mod font;
    }
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub mod unix {
        pub mod font;
    }
    #[cfg(target_os = "windows")]
    pub mod windows {
        pub mod font;
    }
}

pub mod renderer;

#[cfg(target_os="macos")]
extern crate core_graphics;
#[cfg(target_os="macos")]
extern crate core_text;

#[cfg(all(unix, not(target_os="macos")))]
extern crate freetype;

#[cfg(target_os = "windows")]
extern crate dwrote;

extern crate app_units;
extern crate bincode;
extern crate euclid;
extern crate fnv;
extern crate gleam;
extern crate num_traits;
//extern crate notify;
extern crate time;
extern crate webrender_traits;
#[cfg(feature = "webgl")]
extern crate offscreen_gl_context;
extern crate byteorder;
extern crate threadpool;

#[cfg(any(target_os="macos", target_os="windows"))]
extern crate gamma_lut;

pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
pub use renderer::{Renderer, RendererOptions};
<|endoftext|>


<commit_before>use executor::*;
use il::*;
use std::rc::Rc;
use translator::mips::*;
use types::Endian;

fn init_driver_block<'d>(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    memory: memory::Memory,
    arch: &'d ::translator::Arch
) -> driver::Driver<'d> {
    let mut bytes = vec![0x00, 0x00, 0x00, 0x00];
    bytes.append(&mut instruction_bytes.to_vec());
    bytes.append(&mut vec![0x00, 0x00, 0x00, 0x00]);

    let block_translation_result = Mips::new().translate_block(&bytes, 0).unwrap();
    let control_flow_graph = block_translation_result.control_flow_graph();
    let function = Function::new(0, control_flow_graph.clone());
    let mut program = Program::new();

    program.add_function(function);

    let location = ProgramLocation::new(0, FunctionLocation::EmptyBlock(0));

    let mut engine = engine::Engine::new(memory);
    for scalar in scalars {
        engine.set_scalar(scalar.0, scalar.1);
    }

    driver::Driver::new(Rc::new(program), location, engine, arch)
}

fn init_driver_function<'d>(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    mut memory: memory::Memory,
    arch: &'d ::translator::Arch
) -> driver::Driver<'d> {
    for i in 0..instruction_bytes.len() {
        memory.store(i as u64, expr_const(instruction_bytes[i] as u64, 8))
              .unwrap();
    }

    let function = arch.translate_function(&memory, 0).unwrap();
    let mut program = Program::new();

    program.add_function(function);

    let location = ProgramLocation::new(0, FunctionLocation::Instruction(0, 0));

    let mut engine = engine::Engine::new(memory);
    for scalar in scalars {
        engine.set_scalar(scalar.0, scalar.1);
    }

    driver::Driver::new(Rc::new(program), location, engine, arch)
}

#[cfg(test)]
fn get_scalar(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    memory: memory::Memory,
    result_scalar: &str
) -> Constant {

    let arch = Mips::new();

    let mut driver = init_driver_block(instruction_bytes, scalars, memory, &arch);
    let num_blocks = driver.program()
                           .function(0)
                           .unwrap()
                           .control_flow_graph()
                           .blocks()
                           .len();

    loop {
        driver = driver.step().unwrap();
        if let Some(index) = driver.location().block_index() {
            if index == num_blocks as u64 - 1 {
                break;
            }
        }
    }

    return driver.engine()
                 .symbolize_and_eval(driver.engine().get_scalar(result_scalar).unwrap())
                 .unwrap();
}

#[cfg(test)]
fn get_raise(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    memory: memory::Memory
) -> Option<Expression> {

    let arch = Mips::new();

    let mut driver = init_driver_block(instruction_bytes, scalars, memory, &arch);
    let num_blocks = driver.program()
                           .function(0)
                           .unwrap()
                           .control_flow_graph()
                           .blocks()
                           .len();

    loop {
        driver = driver.step().unwrap();
        let location = driver.location().apply(driver.program()).unwrap();
        if let Some(instruction) = location.instruction() {
            if let Operation::Raise { ref expr } = *instruction.operation() {
                return Some(expr.clone());
            }
        }
        if let Some(index) = driver.location().block_index() {
            if index == num_blocks as u64 - 1 {
                break;
            }
        }
    }

    None
}

#[test]
fn add() {
    // add $a0, $a1, $a2
    let instruction_bytes = &[0x00, 0xa6, 0x20, 0x20];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 2);

    let result = get_raise(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big)
    ).unwrap();
    if let Expression::Scalar(ref scalar) = result {
        assert_eq!(scalar.name(), "IntegerOverflow");
    }
    else {
        assert!(false);
    }

    let result = get_raise(
        instruction_bytes,
        vec![("$a1", expr_const(0xffffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big)
    ).unwrap();
    if let Expression::Scalar(ref scalar) = result {
        assert_eq!(scalar.name(), "IntegerOverflow");
    }
    else {
        assert!(false);
    }
}

#[test]
fn addi() {
    // addi $a0, $a1, 0x1234
    let instruction_bytes = &[0x20, 0xa4, 0x12, 0x34];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1235);

    let result = get_raise(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32))],
        memory::Memory::new(Endian::Big)
    ).unwrap();
    if let Expression::Scalar(ref scalar) = result {
        assert_eq!(scalar.name(), "IntegerOverflow");
    }
    else {
        assert!(false);
    }
}

#[test]
fn addiu() {
    // addiu $a0, $a1, 0x1234
    let instruction_bytes = &[0x24, 0xa4, 0x12, 0x34];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1235);

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x80001233);
}

#[test]
fn addu() {
    // addu $a0, $a1, $a2
    let instruction_bytes = &[0x00, 0xa6, 0x20, 0x21];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 2);

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x80000000);

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0xffffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0);
}

#[test]
fn and() {
    // and $a0, $a1, $a2
    let instruction_bytes = &[0x00, 0xa6, 0x20, 0x24];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x8000ffff, 32)),
             ("$a2", expr_const(0x1234, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1234);
}

#[test]
fn andi() {
    // andi $a0, $a1, 0x1234
    let instruction_bytes = &[0x30, 0xa4, 0x12, 0x34];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x8000ffff, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1234);
}

#[test]
fn b() {

    /*
    b target
    ori $a0, $a0, 0
    ori $a0, $a0, 1
    ori $a0, $a0, 1
    target :
    ori $a0, $a0, 2
    jr $ra
    nop
    */
    let instruction_bytes = &[
        0x10, 0x00, 0x00, 0x03,
        0x34, 0x84, 0x00, 0x00,
        0x34, 0x84, 0x00, 0x01,
        0x34, 0x84, 0x00, 0x01,
        0x34, 0x84, 0x00, 0x02,
        0x03, 0xe0, 0x00, 0x08,
        0x00, 0x00, 0x00, 0x00
    ];

    let arch = Mips::new();

    let mut driver = init_driver_function(
        instruction_bytes,
        vec![("$a0", expr_const(0, 32))],
        memory::Memory::new(Endian::Big),
        &arch
    );

    loop {
        driver = driver.step().unwrap();
        if let Some(address) = driver.location()
                                     .apply(driver.program())
                                     .unwrap()
                                     .address() {
            if address == 0x14 {
                assert_eq!(
                    eval(driver.engine()
                               .get_scalar("$a0")
                               .unwrap()).unwrap()
                                         .value(),
                    2
                );
                break;
            }
        }
    }
}<commit_msg>bal test<commit_after>use executor::*;
use il::*;
use std::rc::Rc;
use translator::mips::*;
use types::Endian;

fn init_driver_block<'d>(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    memory: memory::Memory,
    arch: &'d ::translator::Arch
) -> driver::Driver<'d> {
    let mut bytes = vec![0x00, 0x00, 0x00, 0x00];
    bytes.append(&mut instruction_bytes.to_vec());
    bytes.append(&mut vec![0x00, 0x00, 0x00, 0x00]);

    let block_translation_result = Mips::new().translate_block(&bytes, 0).unwrap();
    let control_flow_graph = block_translation_result.control_flow_graph();
    let function = Function::new(0, control_flow_graph.clone());
    let mut program = Program::new();

    program.add_function(function);

    let location = ProgramLocation::new(0, FunctionLocation::EmptyBlock(0));

    let mut engine = engine::Engine::new(memory);
    for scalar in scalars {
        engine.set_scalar(scalar.0, scalar.1);
    }

    driver::Driver::new(Rc::new(program), location, engine, arch)
}

fn init_driver_function<'d>(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    mut memory: memory::Memory,
    arch: &'d ::translator::Arch
) -> driver::Driver<'d> {
    for i in 0..instruction_bytes.len() {
        memory.store(i as u64, expr_const(instruction_bytes[i] as u64, 8))
              .unwrap();
    }

    let function = arch.translate_function(&memory, 0).unwrap();
    let mut program = Program::new();

    program.add_function(function);

    let location = ProgramLocation::new(0, FunctionLocation::Instruction(0, 0));

    let mut engine = engine::Engine::new(memory);
    for scalar in scalars {
        engine.set_scalar(scalar.0, scalar.1);
    }

    driver::Driver::new(Rc::new(program), location, engine, arch)
}

#[cfg(test)]
fn get_scalar(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    memory: memory::Memory,
    result_scalar: &str
) -> Constant {

    let arch = Mips::new();

    let mut driver = init_driver_block(instruction_bytes, scalars, memory, &arch);
    let num_blocks = driver.program()
                           .function(0)
                           .unwrap()
                           .control_flow_graph()
                           .blocks()
                           .len();

    loop {
        driver = driver.step().unwrap();
        if let Some(index) = driver.location().block_index() {
            if index == num_blocks as u64 - 1 {
                break;
            }
        }
    }

    return driver.engine()
                 .symbolize_and_eval(driver.engine().get_scalar(result_scalar).unwrap())
                 .unwrap();
}

#[cfg(test)]
fn get_raise(
    instruction_bytes: &[u8],
    scalars: Vec<(&str, Expression)>,
    memory: memory::Memory
) -> Option<Expression> {

    let arch = Mips::new();

    let mut driver = init_driver_block(instruction_bytes, scalars, memory, &arch);
    let num_blocks = driver.program()
                           .function(0)
                           .unwrap()
                           .control_flow_graph()
                           .blocks()
                           .len();

    loop {
        driver = driver.step().unwrap();
        let location = driver.location().apply(driver.program()).unwrap();
        if let Some(instruction) = location.instruction() {
            if let Operation::Raise { ref expr } = *instruction.operation() {
                return Some(expr.clone());
            }
        }
        if let Some(index) = driver.location().block_index() {
            if index == num_blocks as u64 - 1 {
                break;
            }
        }
    }

    None
}

#[test]
fn add() {
    // add $a0, $a1, $a2
    let instruction_bytes = &[0x00, 0xa6, 0x20, 0x20];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 2);

    let result = get_raise(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big)
    ).unwrap();
    if let Expression::Scalar(ref scalar) = result {
        assert_eq!(scalar.name(), "IntegerOverflow");
    }
    else {
        assert!(false);
    }

    let result = get_raise(
        instruction_bytes,
        vec![("$a1", expr_const(0xffffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big)
    ).unwrap();
    if let Expression::Scalar(ref scalar) = result {
        assert_eq!(scalar.name(), "IntegerOverflow");
    }
    else {
        assert!(false);
    }
}

#[test]
fn addi() {
    // addi $a0, $a1, 0x1234
    let instruction_bytes = &[0x20, 0xa4, 0x12, 0x34];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1235);

    let result = get_raise(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32))],
        memory::Memory::new(Endian::Big)
    ).unwrap();
    if let Expression::Scalar(ref scalar) = result {
        assert_eq!(scalar.name(), "IntegerOverflow");
    }
    else {
        assert!(false);
    }
}

#[test]
fn addiu() {
    // addiu $a0, $a1, 0x1234
    let instruction_bytes = &[0x24, 0xa4, 0x12, 0x34];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1235);

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x80001233);
}

#[test]
fn addu() {
    // addu $a0, $a1, $a2
    let instruction_bytes = &[0x00, 0xa6, 0x20, 0x21];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(1, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 2);

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x7fffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x80000000);

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0xffffffff, 32)),
             ("$a2", expr_const(1, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0);
}

#[test]
fn and() {
    // and $a0, $a1, $a2
    let instruction_bytes = &[0x00, 0xa6, 0x20, 0x24];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x8000ffff, 32)),
             ("$a2", expr_const(0x1234, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1234);
}

#[test]
fn andi() {
    // andi $a0, $a1, 0x1234
    let instruction_bytes = &[0x30, 0xa4, 0x12, 0x34];

    let result = get_scalar(
        instruction_bytes,
        vec![("$a1", expr_const(0x8000ffff, 32))],
        memory::Memory::new(Endian::Big),
        "$a0"
    );
    assert_eq!(result.value(), 0x1234);
}

#[test]
fn b() {

    /*
    b target
    ori $a0, $a0, 0
    ori $a0, $a0, 1
    ori $a0, $a0, 1
    target :
    ori $a0, $a0, 2
    jr $ra
    nop
    */
    let instruction_bytes = &[
        0x10, 0x00, 0x00, 0x03,
        0x34, 0x84, 0x00, 0x00,
        0x34, 0x84, 0x00, 0x01,
        0x34, 0x84, 0x00, 0x01,
        0x34, 0x84, 0x00, 0x02,
        0x03, 0xe0, 0x00, 0x08,
        0x00, 0x00, 0x00, 0x00
    ];

    let arch = Mips::new();

    let mut driver = init_driver_function(
        instruction_bytes,
        vec![("$a0", expr_const(0, 32))],
        memory::Memory::new(Endian::Big),
        &arch
    );

    loop {
        driver = driver.step().unwrap();
        if let Some(address) = driver.location()
                                     .apply(driver.program())
                                     .unwrap()
                                     .address() {
            if address == 0x14 {
                assert_eq!(
                    eval(driver.engine()
                               .get_scalar("$a0")
                               .unwrap()).unwrap()
                                         .value(),
                    2
                );
                break;
            }
        }
    }
}

#[test]
fn bal() {

    /*
    ori $a0, $a0, 0
    bal target
    addi $a0, $a0, 0x1234
    nop
    nop
    target :
    jr $ra
    nop
    */
    let instruction_bytes = &[
        0x34, 0x84, 0x00, 0x00,
        0x04, 0x11, 0x00, 0x03,
        0x20, 0x84, 0x12, 0x34,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x03, 0xe0, 0x00, 0x08,
        0x00, 0x00, 0x00, 0x00
    ];

    let arch = Mips::new();

    let mut driver = init_driver_function(
        instruction_bytes,
        vec![("$a0", expr_const(0, 32))],
        memory::Memory::new(Endian::Big),
        &arch
    );

    println!("{}", driver.program().function(0).unwrap().control_flow_graph());

    loop {
        println!("{:?}", driver.location());
        driver = driver.step().unwrap();
        if let Some(address) = driver.location()
                                     .apply(driver.program())
                                     .unwrap()
                                     .address() {
            if address == 0x14 {
                assert_eq!(
                    eval(driver.engine()
                               .get_scalar("$a0")
                               .unwrap()).unwrap()
                                         .value(),
                    0x1234
                );
                assert_eq!(
                    eval(driver.engine()
                               .get_scalar("$ra")
                               .unwrap()).unwrap()
                                         .value(),
                    0xc
                );
                break;
            }
        }
    }
}<|endoftext|>


<commit_before>use na::{DefaultAllocator, Dim, VectorN, U1, U2, U3};
use na::allocator::Allocator;
use domain::NPoint;

/// A kd tree
pub struct KdTree<N : Dim>(Box<kd_tree::KdTreeImpl<N>>) where DefaultAllocator: Allocator<f64, N>;

impl<N : Dim> KdTree<N> where DefaultAllocator: Allocator<f64, N> {
    /// Get all the points from the kd-tree
    fn get_points(self) -> Vec<Box<NPoint<N>>>  {
        let mut pts = Vec::new();
        let KdTree(tree) = self;
        kd_tree::add_points_to_vector(tree, &mut pts);
        pts
    }

    fn build<TItem : NPoint<N> + Copy + 'static>(pts : &Vec<TItem>) -> Self where DefaultAllocator: Allocator<f64, N> {
        KdTree(Box::new(kd_tree::build(pts, 0)))
    }
}

mod kd_tree {
    use na::{DefaultAllocator, Dim, Vector3, VectorN, U1, U2, U3};
    use na::allocator::Allocator;
    use std::cmp::Ordering;
    use domain::NPoint;
    use super::{KdTree};

    /// kd-Tree implementation structure
    pub(super) enum KdTreeImpl<N : Dim> where DefaultAllocator: Allocator<f64, N> {
        Node(usize, Box<KdTreeImpl<N>>, Box<NPoint<N>>, Box<KdTreeImpl<N>>),
        Empty()
    }

    /// mutably enunerate points from a kd tree into a vector
    pub(super) fn add_points_to_vector<N : Dim>(tree : Box<KdTreeImpl<N>>, pts : &mut Vec<Box<NPoint<N>>>) where DefaultAllocator: Allocator<f64, N> {
        let unboxed_tree = *tree;
        match unboxed_tree {
            KdTreeImpl::Node(_, l, pt, r) => {
                add_points_to_vector(l, pts);
                pts.push(pt);
                add_points_to_vector(r, pts);
            },
            KdTreeImpl::Empty() => {}
        }
    }

    fn split_vec<N : Dim, TItem : NPoint<N> + Copy + 'static>(pts : &Vec<TItem>, dim_count :usize) -> (Option<Vec<TItem>>, Option<TItem>, Option<Vec<TItem>>) where DefaultAllocator: Allocator<f64, N> {
        unsafe {
            let mut pts = pts.to_vec();
            pts.sort_by( |a,b| a.from_origin().get_unchecked(0, dim_count).partial_cmp(b.from_origin().get_unchecked(0, dim_count)).unwrap() );
            if pts.len() >= 3 {
                let length = pts.len();
                let mut middle = pts.split_off(length / 2);
                let right = middle.split_off(1);
                (Some(pts), Some(middle[0]), Some(right))
            } else if pts.len() == 2 {
                let length = pts.len();
                let mut middle = pts.split_off(1);
                (Some(pts), Some(middle[0]), None)
            } else if pts.len() == 1 {
                (None, Some(pts[0]), None)
            } else{
                (None, None, None)
            }
        }
    }

    pub(super) fn build<N : Dim, TItem : NPoint<N> + Copy + 'static>(pts : &Vec<TItem>, dimIdx :usize) -> KdTreeImpl<N> where DefaultAllocator: Allocator<f64, N> {
        match pts.first() {
            Some(firstPt) => {
                let nextDimIdx = if dimIdx + 1 >= firstPt.from_origin().ncols() {0} else {dimIdx + 1};
                match split_vec(pts, dimIdx) {
                    (Some(vec1), Some(pt), Some(vec2))  => KdTreeImpl::Node(nextDimIdx,     Box::new(build(&vec1, nextDimIdx)),   Box::new(pt),     Box::new(build(&vec2, nextDimIdx))),
                    (Some(vec1), Some(pt), None)        => KdTreeImpl::Node(nextDimIdx,     Box::new(build(&vec1, nextDimIdx)),   Box::new(pt),     Box::new(KdTreeImpl::Empty())),
                    (None,       Some(pt), None)        => KdTreeImpl::Node(nextDimIdx,     Box::new(KdTreeImpl::Empty()),        Box::new(pt),     Box::new(KdTreeImpl::Empty())),
                    _                                   => panic!("Should be impossible to have a first point but still split into (None, None, None)"),
                }
            }
            None => {
                KdTreeImpl::Empty()
            }
        }
    }
}
<commit_msg>Improved error handling and naming<commit_after>use na::{DefaultAllocator, Dim, VectorN, U1, U2, U3};
use na::allocator::Allocator;
use domain::NPoint;

/// A kd tree
pub struct KdTree<N : Dim>(Box<kd_tree::KdTreeImpl<N>>) where DefaultAllocator: Allocator<f64, N>;

impl<N : Dim> KdTree<N> where DefaultAllocator: Allocator<f64, N> {
    /// Get all the points from the kd-tree
    fn get_points(self) -> Vec<Box<NPoint<N>>>  {
        let mut pts = Vec::new();
        let KdTree(tree) = self;
        kd_tree::add_points_to_vector(tree, &mut pts);
        pts
    }

    fn build<TItem : NPoint<N> + Copy + 'static>(pts : &Vec<TItem>) -> Self where DefaultAllocator: Allocator<f64, N> {
        KdTree(Box::new(kd_tree::build(pts, 0)))
    }
}

mod kd_tree {
    use na::{DefaultAllocator, Dim, Vector3, VectorN, U1, U2, U3};
    use na::allocator::Allocator;
    use std::cmp::Ordering;
    use domain::NPoint;
    use super::{KdTree};

    /// kd-Tree implementation structure
    pub(super) enum KdTreeImpl<N : Dim> where DefaultAllocator: Allocator<f64, N> {
        Node(usize, Box<KdTreeImpl<N>>, Box<NPoint<N>>, Box<KdTreeImpl<N>>),
        Empty()
    }

    /// mutably enunerate points from a kd tree into a vector
    pub(super) fn add_points_to_vector<N : Dim>(tree : Box<KdTreeImpl<N>>, pts : &mut Vec<Box<NPoint<N>>>) where DefaultAllocator: Allocator<f64, N> {
        let unboxed_tree = *tree;
        match unboxed_tree {
            KdTreeImpl::Node(_, l, pt, r) => {
                add_points_to_vector(l, pts);
                pts.push(pt);
                add_points_to_vector(r, pts);
            },
            KdTreeImpl::Empty() => {}
        }
    }

    fn split_vec_with_median<N : Dim, TItem : NPoint<N> + Copy + 'static>(pts : &Vec<TItem>, dim_count :usize) -> (Option<Vec<TItem>>, Option<TItem>, Option<Vec<TItem>>) where DefaultAllocator: Allocator<f64, N> {
        unsafe {
            let mut pts = pts.to_vec();
            pts.sort_by( |a,b| a.from_origin().get_unchecked(0, dim_count).partial_cmp(b.from_origin().get_unchecked(0, dim_count)).unwrap() );
            if pts.len() >= 3 {
                let length = pts.len();
                let mut middle = pts.split_off(length / 2);
                let right = middle.split_off(1);
                (Some(pts), Some(middle[0]), Some(right))
            } else if pts.len() == 2 {
                let length = pts.len();
                let mut middle = pts.split_off(1);
                (Some(pts), Some(middle[0]), None)
            } else if pts.len() == 1 {
                (None, Some(pts[0]), None)
            } else{
                (None, None, None)
            }
        }
    }

    pub(super) fn build<N : Dim, TItem : NPoint<N> + Copy + 'static>(pts : &Vec<TItem>, dimIdx :usize) -> KdTreeImpl<N> where DefaultAllocator: Allocator<f64, N> {
        match pts.first() {
            Some(firstPt) => {
                let nextDimIdx = if dimIdx + 1 >= firstPt.from_origin().ncols() {0} else {dimIdx + 1};
                match split_vec_with_median(pts, dimIdx) {
                    (Some(vec1), Some(pt), Some(vec2))  => KdTreeImpl::Node(nextDimIdx,     Box::new(build(&vec1, nextDimIdx)),   Box::new(pt),     Box::new(build(&vec2, nextDimIdx))),
                    (Some(vec1), Some(pt), None      )  => KdTreeImpl::Node(nextDimIdx,     Box::new(build(&vec1, nextDimIdx)),   Box::new(pt),     Box::new(KdTreeImpl::Empty())),
                    (None,       Some(pt), Some(vec2))  => KdTreeImpl::Node(nextDimIdx,     Box::new(KdTreeImpl::Empty()),        Box::new(pt),     Box::new(build(&vec2, nextDimIdx))),
                    (None,       Some(pt), None      )  => KdTreeImpl::Node(nextDimIdx,     Box::new(KdTreeImpl::Empty()),        Box::new(pt),     Box::new(KdTreeImpl::Empty())),
                    (None,       None,     None      )  => panic!("Should be impossible to have a first point but still split into (None, None, None)."),
                    (_      ,    None,     _         )  => panic!("Meaningless result: point must be contained in a kd-tree node.")
                }
            }
            None => {
                KdTreeImpl::Empty()
            }
        }
    }
}
<|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>Allow embedders to access front buffer surface.<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
            })?;
        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
            })
    }

    /// Invoke a closure with the surface associated with the current front buffer.
    /// This can be used to create a surfman::SurfaceTexture to blit elsewhere.
    pub fn with_front_buffer<F: FnMut(&Device, Surface) -> Surface>(&self, mut f: F) {
        let ref mut device = self.0.device.borrow_mut();
        let ref mut context = self.0.context.borrow_mut();
        let surface = device
            .unbind_surface_from_context(context)
            .unwrap()
            .unwrap();
        let surface = f(device, surface);
        device.bind_surface_to_context(context, surface).unwrap();
    }

    pub fn device(&self) -> std::cell::Ref<Device> {
        self.0.device.borrow()
    }

    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>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    writer::{Prop, Writer, AST, SPREAD_KEY},
    FlowTypegenRollout, KEY_FRAGMENT_REFS, KEY_REF_TYPE,
};
use interner::{intern, StringKey};
use itertools::Itertools;
use std::fmt::{Result, Write};

pub struct FlowPrinter {
    result: String,
    indentation: usize,
    flow_typegen_rollout: FlowTypegenRollout,
}

impl Writer for FlowPrinter {
    fn into_string(self: Box<Self>) -> String {
        self.result
    }

    fn write(&mut self, ast: &AST) -> Result {
        match ast {
            AST::Any => write!(&mut self.result, "any"),
            AST::String => write!(&mut self.result, "string"),
            AST::StringLiteral(literal) => self.write_string_literal(*literal),
            AST::OtherTypename => self.write_other_string(),
            AST::Number => write!(&mut self.result, "number"),
            AST::Boolean => write!(&mut self.result, "boolean"),
            AST::Identifier(identifier) => write!(&mut self.result, "{}", identifier),
            AST::RawType(raw) => write!(&mut self.result, "{}", raw),
            AST::Union(members) => self.write_union(members),
            AST::ReadOnlyArray(of_type) => self.write_read_only_array(of_type),
            AST::Nullable(of_type) => self.write_nullable(of_type),
            AST::ExactObject(props) => self.write_object(props, true),
            AST::InexactObject(props) => self.write_object(props, false),
            AST::Local3DPayload(document_name, selections) => {
                self.write_local_3d_payload(*document_name, selections)
            }
            AST::FragmentReference(fragments) => self.write_fragment_references(fragments),
            AST::FragmentReferenceType(fragment) => match self.flow_typegen_rollout {
                FlowTypegenRollout::Old => {
                    write!(&mut self.result, "{}$ref", fragment)
                }
                FlowTypegenRollout::New => {
                    write!(&mut self.result, "{}$type", fragment)
                }
            },
            AST::FunctionReturnType(function_name) => {
                self.write_function_return_type(*function_name)
            }
            AST::ActorChangePoint(selections) => self.write_actor_change_point(selections),
        }
    }

    fn get_runtime_fragment_import(&self) -> StringKey {
        match self.flow_typegen_rollout {
            FlowTypegenRollout::Old => intern!("FragmentReference"),
            FlowTypegenRollout::New => {
                intern!("FragmentType")
            }
        }
    }

    fn write_export_type(&mut self, name: StringKey, value: &AST) -> Result {
        write!(&mut self.result, "export type {} = ", name)?;
        self.write(value)?;
        writeln!(&mut self.result, ";")
    }

    fn write_import_module_default(&mut self, name: StringKey, from: StringKey) -> Result {
        writeln!(&mut self.result, "import {} from \"{}\";", name, from)
    }

    fn write_import_type(&mut self, types: &[StringKey], from: StringKey) -> Result {
        writeln!(
            &mut self.result,
            "import type {{ {} }} from \"{}\";",
            types.iter().format(", "),
            from
        )
    }

    fn write_import_fragment_type(&mut self, types: &[StringKey], from: StringKey) -> Result {
        self.write_import_type(types, from)
    }

    fn write_export_fragment_type(
        &mut self,
        old_name: StringKey,
        other_old_name: StringKey,
        new_name: StringKey,
    ) -> Result {
        match self.flow_typegen_rollout {
            FlowTypegenRollout::Old => writeln!(
                &mut self.result,
                "declare export opaque type {old_name}: FragmentReference;
declare export opaque type {other_old_name}: {old_name};",
                old_name = old_name,
                other_old_name = other_old_name
            ),
            FlowTypegenRollout::New => writeln!(
                &mut self.result,
                "declare export opaque type {new_name}: FragmentType;",
                new_name = new_name
            ),
        }
    }

    fn write_export_fragment_types(
        &mut self,
        fragment_type_name_1: StringKey,
        fragment_type_name_2: StringKey,
    ) -> Result {
        writeln!(
            &mut self.result,
            "export type {{ {}, {} }};",
            fragment_type_name_1, fragment_type_name_2
        )
    }

    fn write_any_type_definition(&mut self, name: StringKey) -> Result {
        writeln!(&mut self.result, "type {} = any;", name)
    }
}

impl FlowPrinter {
    pub fn new(rollout_phase: FlowTypegenRollout) -> Self {
        Self {
            result: String::new(),
            indentation: 0,
            flow_typegen_rollout: rollout_phase,
        }
    }

    fn write_indentation(&mut self) -> Result {
        self.result.write_str(&"  ".repeat(self.indentation))
    }

    fn write_string_literal(&mut self, literal: StringKey) -> Result {
        write!(&mut self.result, "\"{}\"", literal)
    }

    fn write_other_string(&mut self) -> Result {
        write!(&mut self.result, r#""%other""#)
    }

    fn write_union(&mut self, members: &[AST]) -> Result {
        let mut first = true;
        for member in members {
            if first {
                first = false;
            } else {
                write!(&mut self.result, " | ")?;
            }
            self.write(member)?;
        }
        Ok(())
    }

    fn write_fragment_references(&mut self, fragments: &[StringKey]) -> Result {
        let mut first = true;
        for fragment in fragments {
            if first {
                first = false;
            } else {
                write!(&mut self.result, " & ")?;
            }
            match self.flow_typegen_rollout {
                FlowTypegenRollout::Old => {
                    write!(&mut self.result, "{}$ref", fragment)?;
                }
                FlowTypegenRollout::New => {
                    write!(&mut self.result, "{}$type", fragment)?;
                }
            }
        }
        Ok(())
    }

    fn write_read_only_array(&mut self, of_type: &AST) -> Result {
        write!(&mut self.result, "$ReadOnlyArray<")?;
        self.write(of_type)?;
        write!(&mut self.result, ">")
    }

    fn write_nullable(&mut self, of_type: &AST) -> Result {
        write!(&mut self.result, "?")?;
        match of_type {
            AST::Union(members) if members.len() > 1 => {
                write!(&mut self.result, "(")?;
                self.write(of_type)?;
                write!(&mut self.result, ")")?;
            }
            _ => {
                self.write(of_type)?;
            }
        }
        Ok(())
    }

    fn write_object(&mut self, props: &[Prop], exact: bool) -> Result {
        if props.is_empty() && exact {
            write!(&mut self.result, "{{||}}")?;
            return Ok(());
        }

        if exact {
            writeln!(&mut self.result, "{{|")?;
        } else {
            writeln!(&mut self.result, "{{")?;
        }
        self.indentation += 1;

        for prop in props {
            self.write_indentation()?;
            if prop.key == *SPREAD_KEY {
                write!(&mut self.result, "...")?;
                self.write(&prop.value)?;
                writeln!(&mut self.result, ",")?;
                continue;
            }
            if let AST::OtherTypename = prop.value {
                writeln!(
                    &mut self.result,
                    "// This will never be '%other', but we need some"
                )?;
                self.write_indentation()?;
                writeln!(
                    &mut self.result,
                    "// value in case none of the concrete values match."
                )?;
                self.write_indentation()?;
            }
            if prop.read_only {
                write!(&mut self.result, "+")?;
            }
            match self.flow_typegen_rollout {
                FlowTypegenRollout::Old => {
                    write!(&mut self.result, "{}", prop.key)?;
                }
                FlowTypegenRollout::New => {
                    write!(
                        &mut self.result,
                        "{}",
                        match self.flow_typegen_rollout {
                            FlowTypegenRollout::Old => prop.key,
                            FlowTypegenRollout::New => {
                                if prop.key == *KEY_FRAGMENT_REFS {
                                    intern!("$spreads")
                                } else if prop.key == *KEY_REF_TYPE {
                                    intern!("$type")
                                } else {
                                    prop.key
                                }
                            }
                        }
                    )?;
                }
            }
            if prop.optional {
                write!(&mut self.result, "?")?;
            }
            write!(&mut self.result, ": ")?;
            self.write(&prop.value)?;
            writeln!(&mut self.result, ",")?;
        }
        if !exact {
            self.write_indentation()?;
            writeln!(&mut self.result, "...")?;
        }
        self.indentation -= 1;
        self.write_indentation()?;
        if exact {
            write!(&mut self.result, "|}}")?;
        } else {
            write!(&mut self.result, "}}")?;
        }
        Ok(())
    }

    fn write_local_3d_payload(&mut self, document_name: StringKey, selections: &AST) -> Result {
        write!(&mut self.result, "Local3DPayload<\"{}\", ", document_name)?;
        self.write(selections)?;
        write!(&mut self.result, ">")?;
        Ok(())
    }

    fn write_function_return_type(&mut self, function_name: StringKey) -> Result {
        write!(
            &mut self.result,
            "$Call<<R>((...empty[]) => R) => R, typeof {}>",
            function_name
        )
    }

    fn write_actor_change_point(&mut self, selections: &AST) -> Result {
        write!(&mut self.result, "ActorChangePoint<")?;
        self.write(selections)?;
        write!(&mut self.result, ">")?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use interner::Intern;

    fn print_type(ast: &AST) -> String {
        let mut printer = Box::new(FlowPrinter::new(FlowTypegenRollout::New));
        printer.write(ast).unwrap();
        printer.into_string()
    }

    #[test]
    fn scalar_types() {
        assert_eq!(print_type(&AST::Any), "any".to_string());
        assert_eq!(print_type(&AST::String), "string".to_string());
        assert_eq!(print_type(&AST::Number), "number".to_string());
    }

    #[test]
    fn union_type() {
        assert_eq!(
            print_type(&AST::Union(vec![AST::String, AST::Number])),
            "string | number".to_string()
        );
    }

    #[test]
    fn read_only_array_type() {
        assert_eq!(
            print_type(&AST::ReadOnlyArray(Box::new(AST::String))),
            "$ReadOnlyArray<string>".to_string()
        );
    }

    #[test]
    fn nullable_type() {
        assert_eq!(
            print_type(&AST::Nullable(Box::new(AST::String))),
            "?string".to_string()
        );

        assert_eq!(
            print_type(&AST::Nullable(Box::new(AST::Union(vec![
                AST::String,
                AST::Number,
            ])))),
            "?(string | number)"
        )
    }

    #[test]
    fn exact_object() {
        assert_eq!(
            print_type(&AST::ExactObject(Vec::new())),
            r"{||}".to_string()
        );

        assert_eq!(
            print_type(&AST::ExactObject(vec![Prop {
                key: "single".intern(),
                optional: false,
                read_only: false,
                value: AST::String,
            },])),
            r"{|
  single: string,
|}"
            .to_string()
        );
        assert_eq!(
            print_type(&AST::ExactObject(vec![
                Prop {
                    key: "foo".intern(),
                    optional: true,
                    read_only: false,
                    value: AST::String,
                },
                Prop {
                    key: "bar".intern(),
                    optional: false,
                    read_only: true,
                    value: AST::Number,
                },
            ])),
            r"{|
  foo?: string,
  +bar: number,
|}"
            .to_string()
        );
    }

    #[test]
    fn nested_object() {
        assert_eq!(
            print_type(&AST::ExactObject(vec![
                Prop {
                    key: "foo".intern(),
                    optional: true,
                    read_only: false,
                    value: AST::ExactObject(vec![
                        Prop {
                            key: "nested_foo".intern(),
                            optional: true,
                            read_only: false,
                            value: AST::String,
                        },
                        Prop {
                            key: "nested_foo2".intern(),
                            optional: false,
                            read_only: true,
                            value: AST::Number,
                        },
                    ]),
                },
                Prop {
                    key: "bar".intern(),
                    optional: false,
                    read_only: true,
                    value: AST::Number,
                },
            ])),
            r"{|
  foo?: {|
    nested_foo?: string,
    +nested_foo2: number,
  |},
  +bar: number,
|}"
            .to_string()
        );
    }

    #[test]
    fn inexact_object() {
        assert_eq!(
            print_type(&AST::InexactObject(Vec::new())),
            r"{
  ...
}"
            .to_string()
        );

        assert_eq!(
            print_type(&AST::InexactObject(vec![Prop {
                key: "single".intern(),
                optional: false,
                read_only: false,
                value: AST::String,
            },])),
            r"{
  single: string,
  ...
}"
            .to_string()
        );

        assert_eq!(
            print_type(&AST::InexactObject(vec![
                Prop {
                    key: "foo".intern(),
                    optional: false,
                    read_only: false,
                    value: AST::String,
                },
                Prop {
                    key: "bar".intern(),
                    optional: true,
                    read_only: true,
                    value: AST::Number,
                }
            ])),
            r"{
  foo: string,
  +bar?: number,
  ...
}"
            .to_string()
        );
    }

    #[test]
    fn other_comment() {
        assert_eq!(
            print_type(&AST::ExactObject(vec![Prop {
                key: "with_comment".intern(),
                optional: false,
                read_only: false,
                value: AST::OtherTypename,
            },])),
            r#"{|
  // This will never be '%other', but we need some
  // value in case none of the concrete values match.
  with_comment: "%other",
|}"#
            .to_string()
        );
    }

    #[test]
    fn import_type() {
        let mut printer = Box::new(FlowPrinter::new(FlowTypegenRollout::New));
        printer
            .write_import_type(&["A".intern(), "B".intern()], "module".intern())
            .unwrap();
        assert_eq!(
            printer.into_string(),
            "import type { A, B } from \"module\";\n"
        );
    }

    #[test]
    fn import_module() {
        let mut printer = Box::new(FlowPrinter::new(FlowTypegenRollout::New));
        printer
            .write_import_module_default("A".intern(), "module".intern())
            .unwrap();
        assert_eq!(printer.into_string(), "import A from \"module\";\n");
    }

    #[test]
    fn function_return_type() {
        assert_eq!(
            print_type(&AST::FunctionReturnType("someFunc".intern())),
            "$Call<<R>((...empty[]) => R) => R, typeof someFunc>".to_string()
        );
    }
}
<commit_msg>rename $spreads to $fragmentSpreads<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    writer::{Prop, Writer, AST, SPREAD_KEY},
    FlowTypegenRollout, KEY_FRAGMENT_REFS, KEY_REF_TYPE,
};
use interner::{intern, StringKey};
use itertools::Itertools;
use std::fmt::{Result, Write};

pub struct FlowPrinter {
    result: String,
    indentation: usize,
    flow_typegen_rollout: FlowTypegenRollout,
}

impl Writer for FlowPrinter {
    fn into_string(self: Box<Self>) -> String {
        self.result
    }

    fn write(&mut self, ast: &AST) -> Result {
        match ast {
            AST::Any => write!(&mut self.result, "any"),
            AST::String => write!(&mut self.result, "string"),
            AST::StringLiteral(literal) => self.write_string_literal(*literal),
            AST::OtherTypename => self.write_other_string(),
            AST::Number => write!(&mut self.result, "number"),
            AST::Boolean => write!(&mut self.result, "boolean"),
            AST::Identifier(identifier) => write!(&mut self.result, "{}", identifier),
            AST::RawType(raw) => write!(&mut self.result, "{}", raw),
            AST::Union(members) => self.write_union(members),
            AST::ReadOnlyArray(of_type) => self.write_read_only_array(of_type),
            AST::Nullable(of_type) => self.write_nullable(of_type),
            AST::ExactObject(props) => self.write_object(props, true),
            AST::InexactObject(props) => self.write_object(props, false),
            AST::Local3DPayload(document_name, selections) => {
                self.write_local_3d_payload(*document_name, selections)
            }
            AST::FragmentReference(fragments) => self.write_fragment_references(fragments),
            AST::FragmentReferenceType(fragment) => match self.flow_typegen_rollout {
                FlowTypegenRollout::Old => {
                    write!(&mut self.result, "{}$ref", fragment)
                }
                FlowTypegenRollout::New => {
                    write!(&mut self.result, "{}$type", fragment)
                }
            },
            AST::FunctionReturnType(function_name) => {
                self.write_function_return_type(*function_name)
            }
            AST::ActorChangePoint(selections) => self.write_actor_change_point(selections),
        }
    }

    fn get_runtime_fragment_import(&self) -> StringKey {
        match self.flow_typegen_rollout {
            FlowTypegenRollout::Old => intern!("FragmentReference"),
            FlowTypegenRollout::New => {
                intern!("FragmentType")
            }
        }
    }

    fn write_export_type(&mut self, name: StringKey, value: &AST) -> Result {
        write!(&mut self.result, "export type {} = ", name)?;
        self.write(value)?;
        writeln!(&mut self.result, ";")
    }

    fn write_import_module_default(&mut self, name: StringKey, from: StringKey) -> Result {
        writeln!(&mut self.result, "import {} from \"{}\";", name, from)
    }

    fn write_import_type(&mut self, types: &[StringKey], from: StringKey) -> Result {
        writeln!(
            &mut self.result,
            "import type {{ {} }} from \"{}\";",
            types.iter().format(", "),
            from
        )
    }

    fn write_import_fragment_type(&mut self, types: &[StringKey], from: StringKey) -> Result {
        self.write_import_type(types, from)
    }

    fn write_export_fragment_type(
        &mut self,
        old_name: StringKey,
        other_old_name: StringKey,
        new_name: StringKey,
    ) -> Result {
        match self.flow_typegen_rollout {
            FlowTypegenRollout::Old => writeln!(
                &mut self.result,
                "declare export opaque type {old_name}: FragmentReference;
declare export opaque type {other_old_name}: {old_name};",
                old_name = old_name,
                other_old_name = other_old_name
            ),
            FlowTypegenRollout::New => writeln!(
                &mut self.result,
                "declare export opaque type {new_name}: FragmentType;",
                new_name = new_name
            ),
        }
    }

    fn write_export_fragment_types(
        &mut self,
        fragment_type_name_1: StringKey,
        fragment_type_name_2: StringKey,
    ) -> Result {
        writeln!(
            &mut self.result,
            "export type {{ {}, {} }};",
            fragment_type_name_1, fragment_type_name_2
        )
    }

    fn write_any_type_definition(&mut self, name: StringKey) -> Result {
        writeln!(&mut self.result, "type {} = any;", name)
    }
}

impl FlowPrinter {
    pub fn new(rollout_phase: FlowTypegenRollout) -> Self {
        Self {
            result: String::new(),
            indentation: 0,
            flow_typegen_rollout: rollout_phase,
        }
    }

    fn write_indentation(&mut self) -> Result {
        self.result.write_str(&"  ".repeat(self.indentation))
    }

    fn write_string_literal(&mut self, literal: StringKey) -> Result {
        write!(&mut self.result, "\"{}\"", literal)
    }

    fn write_other_string(&mut self) -> Result {
        write!(&mut self.result, r#""%other""#)
    }

    fn write_union(&mut self, members: &[AST]) -> Result {
        let mut first = true;
        for member in members {
            if first {
                first = false;
            } else {
                write!(&mut self.result, " | ")?;
            }
            self.write(member)?;
        }
        Ok(())
    }

    fn write_fragment_references(&mut self, fragments: &[StringKey]) -> Result {
        let mut first = true;
        for fragment in fragments {
            if first {
                first = false;
            } else {
                write!(&mut self.result, " & ")?;
            }
            match self.flow_typegen_rollout {
                FlowTypegenRollout::Old => {
                    write!(&mut self.result, "{}$ref", fragment)?;
                }
                FlowTypegenRollout::New => {
                    write!(&mut self.result, "{}$type", fragment)?;
                }
            }
        }
        Ok(())
    }

    fn write_read_only_array(&mut self, of_type: &AST) -> Result {
        write!(&mut self.result, "$ReadOnlyArray<")?;
        self.write(of_type)?;
        write!(&mut self.result, ">")
    }

    fn write_nullable(&mut self, of_type: &AST) -> Result {
        write!(&mut self.result, "?")?;
        match of_type {
            AST::Union(members) if members.len() > 1 => {
                write!(&mut self.result, "(")?;
                self.write(of_type)?;
                write!(&mut self.result, ")")?;
            }
            _ => {
                self.write(of_type)?;
            }
        }
        Ok(())
    }

    fn write_object(&mut self, props: &[Prop], exact: bool) -> Result {
        if props.is_empty() && exact {
            write!(&mut self.result, "{{||}}")?;
            return Ok(());
        }

        if exact {
            writeln!(&mut self.result, "{{|")?;
        } else {
            writeln!(&mut self.result, "{{")?;
        }
        self.indentation += 1;

        for prop in props {
            self.write_indentation()?;
            if prop.key == *SPREAD_KEY {
                write!(&mut self.result, "...")?;
                self.write(&prop.value)?;
                writeln!(&mut self.result, ",")?;
                continue;
            }
            if let AST::OtherTypename = prop.value {
                writeln!(
                    &mut self.result,
                    "// This will never be '%other', but we need some"
                )?;
                self.write_indentation()?;
                writeln!(
                    &mut self.result,
                    "// value in case none of the concrete values match."
                )?;
                self.write_indentation()?;
            }
            if prop.read_only {
                write!(&mut self.result, "+")?;
            }
            match self.flow_typegen_rollout {
                FlowTypegenRollout::Old => {
                    write!(&mut self.result, "{}", prop.key)?;
                }
                FlowTypegenRollout::New => {
                    write!(
                        &mut self.result,
                        "{}",
                        match self.flow_typegen_rollout {
                            FlowTypegenRollout::Old => prop.key,
                            FlowTypegenRollout::New => {
                                if prop.key == *KEY_FRAGMENT_REFS {
                                    intern!("$fragmentSpreads")
                                } else if prop.key == *KEY_REF_TYPE {
                                    intern!("$type")
                                } else {
                                    prop.key
                                }
                            }
                        }
                    )?;
                }
            }
            if prop.optional {
                write!(&mut self.result, "?")?;
            }
            write!(&mut self.result, ": ")?;
            self.write(&prop.value)?;
            writeln!(&mut self.result, ",")?;
        }
        if !exact {
            self.write_indentation()?;
            writeln!(&mut self.result, "...")?;
        }
        self.indentation -= 1;
        self.write_indentation()?;
        if exact {
            write!(&mut self.result, "|}}")?;
        } else {
            write!(&mut self.result, "}}")?;
        }
        Ok(())
    }

    fn write_local_3d_payload(&mut self, document_name: StringKey, selections: &AST) -> Result {
        write!(&mut self.result, "Local3DPayload<\"{}\", ", document_name)?;
        self.write(selections)?;
        write!(&mut self.result, ">")?;
        Ok(())
    }

    fn write_function_return_type(&mut self, function_name: StringKey) -> Result {
        write!(
            &mut self.result,
            "$Call<<R>((...empty[]) => R) => R, typeof {}>",
            function_name
        )
    }

    fn write_actor_change_point(&mut self, selections: &AST) -> Result {
        write!(&mut self.result, "ActorChangePoint<")?;
        self.write(selections)?;
        write!(&mut self.result, ">")?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use interner::Intern;

    fn print_type(ast: &AST) -> String {
        let mut printer = Box::new(FlowPrinter::new(FlowTypegenRollout::New));
        printer.write(ast).unwrap();
        printer.into_string()
    }

    #[test]
    fn scalar_types() {
        assert_eq!(print_type(&AST::Any), "any".to_string());
        assert_eq!(print_type(&AST::String), "string".to_string());
        assert_eq!(print_type(&AST::Number), "number".to_string());
    }

    #[test]
    fn union_type() {
        assert_eq!(
            print_type(&AST::Union(vec![AST::String, AST::Number])),
            "string | number".to_string()
        );
    }

    #[test]
    fn read_only_array_type() {
        assert_eq!(
            print_type(&AST::ReadOnlyArray(Box::new(AST::String))),
            "$ReadOnlyArray<string>".to_string()
        );
    }

    #[test]
    fn nullable_type() {
        assert_eq!(
            print_type(&AST::Nullable(Box::new(AST::String))),
            "?string".to_string()
        );

        assert_eq!(
            print_type(&AST::Nullable(Box::new(AST::Union(vec![
                AST::String,
                AST::Number,
            ])))),
            "?(string | number)"
        )
    }

    #[test]
    fn exact_object() {
        assert_eq!(
            print_type(&AST::ExactObject(Vec::new())),
            r"{||}".to_string()
        );

        assert_eq!(
            print_type(&AST::ExactObject(vec![Prop {
                key: "single".intern(),
                optional: false,
                read_only: false,
                value: AST::String,
            },])),
            r"{|
  single: string,
|}"
            .to_string()
        );
        assert_eq!(
            print_type(&AST::ExactObject(vec![
                Prop {
                    key: "foo".intern(),
                    optional: true,
                    read_only: false,
                    value: AST::String,
                },
                Prop {
                    key: "bar".intern(),
                    optional: false,
                    read_only: true,
                    value: AST::Number,
                },
            ])),
            r"{|
  foo?: string,
  +bar: number,
|}"
            .to_string()
        );
    }

    #[test]
    fn nested_object() {
        assert_eq!(
            print_type(&AST::ExactObject(vec![
                Prop {
                    key: "foo".intern(),
                    optional: true,
                    read_only: false,
                    value: AST::ExactObject(vec![
                        Prop {
                            key: "nested_foo".intern(),
                            optional: true,
                            read_only: false,
                            value: AST::String,
                        },
                        Prop {
                            key: "nested_foo2".intern(),
                            optional: false,
                            read_only: true,
                            value: AST::Number,
                        },
                    ]),
                },
                Prop {
                    key: "bar".intern(),
                    optional: false,
                    read_only: true,
                    value: AST::Number,
                },
            ])),
            r"{|
  foo?: {|
    nested_foo?: string,
    +nested_foo2: number,
  |},
  +bar: number,
|}"
            .to_string()
        );
    }

    #[test]
    fn inexact_object() {
        assert_eq!(
            print_type(&AST::InexactObject(Vec::new())),
            r"{
  ...
}"
            .to_string()
        );

        assert_eq!(
            print_type(&AST::InexactObject(vec![Prop {
                key: "single".intern(),
                optional: false,
                read_only: false,
                value: AST::String,
            },])),
            r"{
  single: string,
  ...
}"
            .to_string()
        );

        assert_eq!(
            print_type(&AST::InexactObject(vec![
                Prop {
                    key: "foo".intern(),
                    optional: false,
                    read_only: false,
                    value: AST::String,
                },
                Prop {
                    key: "bar".intern(),
                    optional: true,
                    read_only: true,
                    value: AST::Number,
                }
            ])),
            r"{
  foo: string,
  +bar?: number,
  ...
}"
            .to_string()
        );
    }

    #[test]
    fn other_comment() {
        assert_eq!(
            print_type(&AST::ExactObject(vec![Prop {
                key: "with_comment".intern(),
                optional: false,
                read_only: false,
                value: AST::OtherTypename,
            },])),
            r#"{|
  // This will never be '%other', but we need some
  // value in case none of the concrete values match.
  with_comment: "%other",
|}"#
            .to_string()
        );
    }

    #[test]
    fn import_type() {
        let mut printer = Box::new(FlowPrinter::new(FlowTypegenRollout::New));
        printer
            .write_import_type(&["A".intern(), "B".intern()], "module".intern())
            .unwrap();
        assert_eq!(
            printer.into_string(),
            "import type { A, B } from \"module\";\n"
        );
    }

    #[test]
    fn import_module() {
        let mut printer = Box::new(FlowPrinter::new(FlowTypegenRollout::New));
        printer
            .write_import_module_default("A".intern(), "module".intern())
            .unwrap();
        assert_eq!(printer.into_string(), "import A from \"module\";\n");
    }

    #[test]
    fn function_return_type() {
        assert_eq!(
            print_type(&AST::FunctionReturnType("someFunc".intern())),
            "$Call<<R>((...empty[]) => R) => R, typeof someFunc>".to_string()
        );
    }
}
<|endoftext|>