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

//! Factory extension. Provides resource construction shortcuts.

use device;
use device::shade::{Stage, CreateShaderError};
use render::{Renderer, RenderState, ParamStorage};
use render::mesh::{Mesh, VertexFormat};
use super::shade::*;

/// Factory extension that allows creating new renderers.
pub trait RenderFactory<R: device::Resources, C: device::draw::CommandBuffer<R>> {
    /// Create a new renderer
    fn create_renderer(&mut self) -> Renderer<R, C>;
}

impl<
    R: device::Resources,
    C: device::draw::CommandBuffer<R>,
    F: device::Factory<R>,
> RenderFactory<R, C> for F {
    fn create_renderer(&mut self) -> Renderer<R, C> {
        Renderer {
            command_buffer: device::draw::CommandBuffer::new(),
            data_buffer: device::draw::DataBuffer::new(),
            handles: device::handle::Manager::new(),
            common_array_buffer: self.create_array_buffer(),
            draw_frame_buffer: self.create_frame_buffer(),
            read_frame_buffer: self.create_frame_buffer(),
            default_frame_buffer: self.get_main_frame_buffer(),
            render_state: RenderState::new(),
            parameters: ParamStorage::new(),
        }
    }
}

/// Factory extension trait
pub trait FactoryExt<R: device::Resources> {
    /// Create a new mesh from the given vertex data.
    /// Convenience function around `create_buffer` and `Mesh::from_format`.
    fn create_mesh<T: VertexFormat + Copy>(&mut self, data: &[T]) -> Mesh<R>;
    /// Create a simple program given a vertex shader with a fragment one.
    fn link_program(&mut self, vs_code: &[u8], fs_code: &[u8])
                    -> Result<device::handle::Program<R>, ProgramError>;
    /// Create a simple program given `ShaderSource` versions of vertex and
    /// fragment shaders, chooss the matching versions for the device.
    fn link_program_source(&mut self, vs_src: ShaderSource, fs_src: ShaderSource,
                           caps: &device::Capabilities)
                           -> Result<device::handle::Program<R>, ProgramError>;
}

impl<R: device::Resources, F: device::Factory<R>> FactoryExt<R> for F {
    fn create_mesh<T: VertexFormat + Copy>(&mut self, data: &[T]) -> Mesh<R> {
        let nv = data.len();
        //debug_assert!(nv < self.max_vertex_count); //TODO
        let buf = self.create_buffer_static(data);
        Mesh::from_format(buf, nv as device::VertexCount)
    }

    fn link_program(&mut self, vs_code: &[u8], fs_code: &[u8])
                    -> Result<device::handle::Program<R>, ProgramError> {
        let vs = match self.create_shader(Stage::Vertex, vs_code) {
            Ok(s) => s,
            Err(e) => return Err(ProgramError::Vertex(e)),
        };
        let fs = match self.create_shader(Stage::Fragment, fs_code) {
            Ok(s) => s,
            Err(e) => return Err(ProgramError::Fragment(e)),
        };

        self.create_program(&[vs, fs], None)
            .map_err(|e| ProgramError::Link(e))
    }

    fn link_program_source(&mut self, vs_src: ShaderSource, fs_src: ShaderSource,
                           caps: &device::Capabilities)
                           -> Result<device::handle::Program<R>, ProgramError> {
        let model = caps.shader_model;
        let err_model = CreateShaderError::ModelNotSupported;

        let vs = match vs_src.choose(model) {
            Ok(code) => match self.create_shader(Stage::Vertex, code) {
                Ok(s) => s,
                Err(e) => return Err(ProgramError::Vertex(e)),
            },
            Err(_) => return Err(ProgramError::Vertex(err_model))
        };

        let fs = match fs_src.choose(model) {
            Ok(code) => match self.create_shader(Stage::Fragment, code) {
                Ok(s) => s,
                Err(e) => return Err(ProgramError::Fragment(e)),
            },
            Err(_) => return Err(ProgramError::Fragment(err_model))
        };

        self.create_program(&[vs, fs], Some(fs_src.targets))
            .map_err(|e| ProgramError::Link(e))
    }
}
<commit_msg>Added standard texture constructors<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Factory extension. Provides resource construction shortcuts.

use device;
use device::{handle, tex};
use device::shade::{Stage, CreateShaderError};
use render::{Renderer, RenderState, ParamStorage};
use render::mesh::{Mesh, VertexFormat};
use super::shade::*;

/// Factory extension that allows creating new renderers.
pub trait RenderFactory<R: device::Resources, C: device::draw::CommandBuffer<R>> {
    /// Create a new renderer
    fn create_renderer(&mut self) -> Renderer<R, C>;
}

impl<
    R: device::Resources,
    C: device::draw::CommandBuffer<R>,
    F: device::Factory<R>,
> RenderFactory<R, C> for F {
    fn create_renderer(&mut self) -> Renderer<R, C> {
        Renderer {
            command_buffer: device::draw::CommandBuffer::new(),
            data_buffer: device::draw::DataBuffer::new(),
            handles: handle::Manager::new(),
            common_array_buffer: self.create_array_buffer(),
            draw_frame_buffer: self.create_frame_buffer(),
            read_frame_buffer: self.create_frame_buffer(),
            default_frame_buffer: self.get_main_frame_buffer(),
            render_state: RenderState::new(),
            parameters: ParamStorage::new(),
        }
    }
}

/// Factory extension trait
pub trait FactoryExt<R: device::Resources> {
    /// Create a new mesh from the given vertex data.
    /// Convenience function around `create_buffer` and `Mesh::from_format`.
    fn create_mesh<T: VertexFormat + Copy>(&mut self, data: &[T]) -> Mesh<R>;
    /// Create a simple program given a vertex shader with a fragment one.
    fn link_program(&mut self, vs_code: &[u8], fs_code: &[u8])
                    -> Result<handle::Program<R>, ProgramError>;
    /// Create a simple program given `ShaderSource` versions of vertex and
    /// fragment shaders, chooss the matching versions for the device.
    fn link_program_source(&mut self, vs_src: ShaderSource, fs_src: ShaderSource,
                           caps: &device::Capabilities)
                           -> Result<handle::Program<R>, ProgramError>;
    /// Create a simple RGBA8 2D texture
    fn crate_texture_rgba8(&mut self, width: u16, height: u16, mipmap: bool)
                           -> Result<handle::Texture<R>, tex::TextureError>;
    /// Create a simple depth+stencil 2D texture
    fn create_texture_depth_stencil(&mut self, width: u16, height: u16)
                                    -> Result<handle::Texture<R>, tex::TextureError>;
}

impl<R: device::Resources, F: device::Factory<R>> FactoryExt<R> for F {
    fn create_mesh<T: VertexFormat + Copy>(&mut self, data: &[T]) -> Mesh<R> {
        let nv = data.len();
        //debug_assert!(nv < self.max_vertex_count); //TODO
        let buf = self.create_buffer_static(data);
        Mesh::from_format(buf, nv as device::VertexCount)
    }

    fn link_program(&mut self, vs_code: &[u8], fs_code: &[u8])
                    -> Result<handle::Program<R>, ProgramError> {
        let vs = match self.create_shader(Stage::Vertex, vs_code) {
            Ok(s) => s,
            Err(e) => return Err(ProgramError::Vertex(e)),
        };
        let fs = match self.create_shader(Stage::Fragment, fs_code) {
            Ok(s) => s,
            Err(e) => return Err(ProgramError::Fragment(e)),
        };

        self.create_program(&[vs, fs], None)
            .map_err(|e| ProgramError::Link(e))
    }

    fn link_program_source(&mut self, vs_src: ShaderSource, fs_src: ShaderSource,
                           caps: &device::Capabilities)
                           -> Result<handle::Program<R>, ProgramError> {
        let model = caps.shader_model;
        let err_model = CreateShaderError::ModelNotSupported;

        let vs = match vs_src.choose(model) {
            Ok(code) => match self.create_shader(Stage::Vertex, code) {
                Ok(s) => s,
                Err(e) => return Err(ProgramError::Vertex(e)),
            },
            Err(_) => return Err(ProgramError::Vertex(err_model))
        };

        let fs = match fs_src.choose(model) {
            Ok(code) => match self.create_shader(Stage::Fragment, code) {
                Ok(s) => s,
                Err(e) => return Err(ProgramError::Fragment(e)),
            },
            Err(_) => return Err(ProgramError::Fragment(err_model))
        };

        self.create_program(&[vs, fs], Some(fs_src.targets))
            .map_err(|e| ProgramError::Link(e))
    }

    fn crate_texture_rgba8(&mut self, width: u16, height: u16, mipmap: bool)
                           -> Result<handle::Texture<R>, tex::TextureError> {
        self.create_texture(tex::TextureInfo {
            width: width,
            height: height,
            depth: 0,
            levels: if mipmap {99} else {1},
            kind: tex::TextureKind::Texture2D,
            format: tex::RGBA8,
        })
    }

    fn create_texture_depth_stencil(&mut self, width: u16, height: u16)
                                    -> Result<handle::Texture<R>, tex::TextureError> {
        self.create_texture(tex::TextureInfo {
            width: width,
            height: height,
            depth: 0,
            levels: 1,
            kind: tex::TextureKind::Texture2D,
            format: tex::Format::DEPTH24STENCIL8,
        })
    }
}
<|endoftext|>


<commit_before>//! Pulls type information out of the AST and attaches it to the document

use doc::ItemUtils;
use syntax::ast;
use syntax::print::pprust;
use syntax::ast_map;
use std::map::HashMap;
use extract::to_str;

export mk_pass;

fn mk_pass() -> Pass {
    {
        name: ~"tystr",
        f: run
    }
}

fn run(
    srv: astsrv::Srv,
    doc: doc::Doc
) -> doc::Doc {
    let fold = fold::Fold({
        fold_fn: fold_fn,
        fold_const: fold_const,
        fold_enum: fold_enum,
        fold_trait: fold_trait,
        fold_impl: fold_impl,
        fold_type: fold_type,
        fold_struct: fold_struct,
        .. *fold::default_any_fold(srv)
    });
    fold.fold_doc(fold, doc)
}

fn fold_fn(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::FnDoc
) -> doc::FnDoc {

    let srv = fold.ctxt;

    {
        sig: get_fn_sig(srv, doc.id()),
        .. doc
    }
}

fn get_fn_sig(srv: astsrv::Srv, fn_id: doc::AstId) -> Option<~str> {
    do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            ident: ident,
            node: ast::item_fn(decl, _, tys, _), _
          }, _) |
          ast_map::node_foreign_item(@{
            ident: ident,
            node: ast::foreign_item_fn(decl, _, tys), _
          }, _, _) => {
            Some(pprust::fun_to_str(decl, ident, tys, extract::interner()))
          }
          _ => fail ~"get_fn_sig: fn_id not bound to a fn item"
        }
    }
}

#[test]
fn should_add_fn_sig() {
    let doc = test::mk_doc(~"fn a<T>() -> int { }");
    assert doc.cratemod().fns()[0].sig == Some(~"fn a<T>() -> int");
}

#[test]
fn should_add_foreign_fn_sig() {
    let doc = test::mk_doc(~"extern mod a { fn a<T>() -> int; }");
    assert doc.cratemod().nmods()[0].fns[0].sig == Some(~"fn a<T>() -> int");
}

fn fold_const(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::ConstDoc
) -> doc::ConstDoc {
    let srv = fold.ctxt;

    {
        sig: Some(do astsrv::exec(srv) |ctxt| {
            match ctxt.ast_map.get(doc.id()) {
              ast_map::node_item(@{
                node: ast::item_const(ty, _), _
              }, _) => {
                pprust::ty_to_str(ty, extract::interner())
              }
              _ => fail ~"fold_const: id not bound to a const item"
            }
        }),
        .. doc
    }
}

#[test]
fn should_add_const_types() {
    let doc = test::mk_doc(~"const a: bool = true;");
    assert doc.cratemod().consts()[0].sig == Some(~"bool");
}

fn fold_enum(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::EnumDoc
) -> doc::EnumDoc {
    let doc_id = doc.id();
    let srv = fold.ctxt;

    {
        variants: do par::map(doc.variants) |variant| {
            let sig = do astsrv::exec(srv) |ctxt| {
                match ctxt.ast_map.get(doc_id) {
                  ast_map::node_item(@{
                    node: ast::item_enum(enum_definition, _), _
                  }, _) => {
                    let ast_variant = option::get(
                        do vec::find(enum_definition.variants) |v| {
                            to_str(v.node.name) == variant.name
                        });

                    pprust::variant_to_str(ast_variant, extract::interner())
                  }
                  _ => fail ~"enum variant not bound to an enum item"
                }
            };

            {
                sig: Some(sig),
                .. variant
            }
        },
        .. doc
    }
}

#[test]
fn should_add_variant_sigs() {
    let doc = test::mk_doc(~"enum a { b(int) }");
    assert doc.cratemod().enums()[0].variants[0].sig == Some(~"b(int)");
}

fn fold_trait(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::TraitDoc
) -> doc::TraitDoc {
    {
        methods: merge_methods(fold.ctxt, doc.id(), doc.methods),
        .. doc
    }
}

fn merge_methods(
    srv: astsrv::Srv,
    item_id: doc::AstId,
    docs: ~[doc::MethodDoc]
) -> ~[doc::MethodDoc] {
    do par::map(docs) |doc| {
        {
            sig: get_method_sig(srv, item_id, doc.name),
            .. doc
        }
    }
}

fn get_method_sig(
    srv: astsrv::Srv,
    item_id: doc::AstId,
    method_name: ~str
) -> Option<~str> {
    do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(item_id) {
          ast_map::node_item(@{
            node: ast::item_trait(_, _, methods), _
          }, _) => {
            match vec::find(methods, |method| {
                match method {
                  ast::required(ty_m) => to_str(ty_m.ident) == method_name,
                  ast::provided(m) => to_str(m.ident) == method_name,
                }
            }) {
                Some(method) => {
                  match method {
                    ast::required(ty_m) => {
                      Some(pprust::fun_to_str(
                          ty_m.decl,
                          ty_m.ident,
                          ty_m.tps,
                          extract::interner()
                      ))
                    }
                    ast::provided(m) => {
                      Some(pprust::fun_to_str(
                          m.decl,
                          m.ident,
                          m.tps,
                          extract::interner()
                      ))
                    }
                  }
                }
                _ => fail ~"method not found"
            }
          }
          ast_map::node_item(@{
            node: ast::item_impl(_, _, _, methods), _
          }, _) => {
            match vec::find(methods, |method| {
                to_str(method.ident) == method_name
            }) {
                Some(method) => {
                    Some(pprust::fun_to_str(
                        method.decl,
                        method.ident,
                        method.tps,
                        extract::interner()
                    ))
                }
                None => fail ~"method not found"
            }
          }
          _ => fail ~"get_method_sig: item ID not bound to trait or impl"
        }
    }
}

#[test]
fn should_add_trait_method_sigs() {
    let doc = test::mk_doc(~"trait i { fn a<T>() -> int; }");
    assert doc.cratemod().traits()[0].methods[0].sig
        == Some(~"fn a<T>() -> int");
}

fn fold_impl(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::ImplDoc
) -> doc::ImplDoc {

    let srv = fold.ctxt;

    let (trait_types, self_ty) = do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(doc.id()) {
          ast_map::node_item(@{
            node: ast::item_impl(_, opt_trait_type, self_ty, _), _
          }, _) => {
            let trait_types = opt_trait_type.map_default(~[], |p| {
                ~[pprust::path_to_str(p.path, extract::interner())]
            });
            (trait_types, Some(pprust::ty_to_str(self_ty,
                                                 extract::interner())))
          }
          _ => fail ~"expected impl"
        }
    };

    {
        trait_types: trait_types,
        self_ty: self_ty,
        methods: merge_methods(fold.ctxt, doc.id(), doc.methods),
        .. doc
    }
}

#[test]
fn should_add_impl_trait_types() {
    let doc = test::mk_doc(~"impl int: j { fn a<T>() { } }");
    assert doc.cratemod().impls()[0].trait_types[0] == ~"j";
}

#[test]
fn should_not_add_impl_trait_types_if_none() {
    let doc = test::mk_doc(~"impl int { fn a() { } }");
    assert vec::len(doc.cratemod().impls()[0].trait_types) == 0;
}

#[test]
fn should_add_impl_self_ty() {
    let doc = test::mk_doc(~"impl int { fn a() { } }");
    assert doc.cratemod().impls()[0].self_ty == Some(~"int");
}

#[test]
fn should_add_impl_method_sigs() {
    let doc = test::mk_doc(~"impl int { fn a<T>() -> int { fail } }");
    assert doc.cratemod().impls()[0].methods[0].sig
        == Some(~"fn a<T>() -> int");
}

fn fold_type(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::TyDoc
) -> doc::TyDoc {

    let srv = fold.ctxt;

    {
        sig: do astsrv::exec(srv) |ctxt| {
            match ctxt.ast_map.get(doc.id()) {
              ast_map::node_item(@{
                ident: ident,
                node: ast::item_ty(ty, params), _
              }, _) => {
                Some(fmt!(
                    "type %s%s = %s",
                    to_str(ident),
                    pprust::typarams_to_str(params, extract::interner()),
                    pprust::ty_to_str(ty, extract::interner())
                ))
              }
              _ => fail ~"expected type"
            }
        },
        .. doc
    }
}

#[test]
fn should_add_type_signatures() {
    let doc = test::mk_doc(~"type t<T> = int;");
    assert doc.cratemod().types()[0].sig == Some(~"type t<T> = int");
}

fn fold_struct(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::StructDoc
) -> doc::StructDoc {
    let srv = fold.ctxt;

    {
        sig: do astsrv::exec(srv) |ctxt| {
            match ctxt.ast_map.get(doc.id()) {
                ast_map::node_item(item, _) => {
                    Some(pprust::item_to_str(item,
                                             extract::interner()))
                }
                _ => fail ~"not an item"
            }
        },
        .. doc
    }
}

#[test]
fn should_add_struct_defs() {
    let doc = test::mk_doc(~"struct S { field: () }");
    assert doc.cratemod().structs()[0].sig.get().contains("struct S {");
}

#[cfg(test)]
mod test {
    fn mk_doc(source: ~str) -> doc::Doc {
        do astsrv::from_str(source) |srv| {
            let doc = extract::from_srv(srv, ~"");
            run(srv, doc)
        }
    }
}
<commit_msg>rustdoc: Don't print struct drop blocks<commit_after>//! Pulls type information out of the AST and attaches it to the document

use doc::ItemUtils;
use syntax::ast;
use syntax::print::pprust;
use syntax::ast_map;
use std::map::HashMap;
use extract::to_str;

export mk_pass;

fn mk_pass() -> Pass {
    {
        name: ~"tystr",
        f: run
    }
}

fn run(
    srv: astsrv::Srv,
    doc: doc::Doc
) -> doc::Doc {
    let fold = fold::Fold({
        fold_fn: fold_fn,
        fold_const: fold_const,
        fold_enum: fold_enum,
        fold_trait: fold_trait,
        fold_impl: fold_impl,
        fold_type: fold_type,
        fold_struct: fold_struct,
        .. *fold::default_any_fold(srv)
    });
    fold.fold_doc(fold, doc)
}

fn fold_fn(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::FnDoc
) -> doc::FnDoc {

    let srv = fold.ctxt;

    {
        sig: get_fn_sig(srv, doc.id()),
        .. doc
    }
}

fn get_fn_sig(srv: astsrv::Srv, fn_id: doc::AstId) -> Option<~str> {
    do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(fn_id) {
          ast_map::node_item(@{
            ident: ident,
            node: ast::item_fn(decl, _, tys, _), _
          }, _) |
          ast_map::node_foreign_item(@{
            ident: ident,
            node: ast::foreign_item_fn(decl, _, tys), _
          }, _, _) => {
            Some(pprust::fun_to_str(decl, ident, tys, extract::interner()))
          }
          _ => fail ~"get_fn_sig: fn_id not bound to a fn item"
        }
    }
}

#[test]
fn should_add_fn_sig() {
    let doc = test::mk_doc(~"fn a<T>() -> int { }");
    assert doc.cratemod().fns()[0].sig == Some(~"fn a<T>() -> int");
}

#[test]
fn should_add_foreign_fn_sig() {
    let doc = test::mk_doc(~"extern mod a { fn a<T>() -> int; }");
    assert doc.cratemod().nmods()[0].fns[0].sig == Some(~"fn a<T>() -> int");
}

fn fold_const(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::ConstDoc
) -> doc::ConstDoc {
    let srv = fold.ctxt;

    {
        sig: Some(do astsrv::exec(srv) |ctxt| {
            match ctxt.ast_map.get(doc.id()) {
              ast_map::node_item(@{
                node: ast::item_const(ty, _), _
              }, _) => {
                pprust::ty_to_str(ty, extract::interner())
              }
              _ => fail ~"fold_const: id not bound to a const item"
            }
        }),
        .. doc
    }
}

#[test]
fn should_add_const_types() {
    let doc = test::mk_doc(~"const a: bool = true;");
    assert doc.cratemod().consts()[0].sig == Some(~"bool");
}

fn fold_enum(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::EnumDoc
) -> doc::EnumDoc {
    let doc_id = doc.id();
    let srv = fold.ctxt;

    {
        variants: do par::map(doc.variants) |variant| {
            let sig = do astsrv::exec(srv) |ctxt| {
                match ctxt.ast_map.get(doc_id) {
                  ast_map::node_item(@{
                    node: ast::item_enum(enum_definition, _), _
                  }, _) => {
                    let ast_variant = option::get(
                        do vec::find(enum_definition.variants) |v| {
                            to_str(v.node.name) == variant.name
                        });

                    pprust::variant_to_str(ast_variant, extract::interner())
                  }
                  _ => fail ~"enum variant not bound to an enum item"
                }
            };

            {
                sig: Some(sig),
                .. variant
            }
        },
        .. doc
    }
}

#[test]
fn should_add_variant_sigs() {
    let doc = test::mk_doc(~"enum a { b(int) }");
    assert doc.cratemod().enums()[0].variants[0].sig == Some(~"b(int)");
}

fn fold_trait(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::TraitDoc
) -> doc::TraitDoc {
    {
        methods: merge_methods(fold.ctxt, doc.id(), doc.methods),
        .. doc
    }
}

fn merge_methods(
    srv: astsrv::Srv,
    item_id: doc::AstId,
    docs: ~[doc::MethodDoc]
) -> ~[doc::MethodDoc] {
    do par::map(docs) |doc| {
        {
            sig: get_method_sig(srv, item_id, doc.name),
            .. doc
        }
    }
}

fn get_method_sig(
    srv: astsrv::Srv,
    item_id: doc::AstId,
    method_name: ~str
) -> Option<~str> {
    do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(item_id) {
          ast_map::node_item(@{
            node: ast::item_trait(_, _, methods), _
          }, _) => {
            match vec::find(methods, |method| {
                match method {
                  ast::required(ty_m) => to_str(ty_m.ident) == method_name,
                  ast::provided(m) => to_str(m.ident) == method_name,
                }
            }) {
                Some(method) => {
                  match method {
                    ast::required(ty_m) => {
                      Some(pprust::fun_to_str(
                          ty_m.decl,
                          ty_m.ident,
                          ty_m.tps,
                          extract::interner()
                      ))
                    }
                    ast::provided(m) => {
                      Some(pprust::fun_to_str(
                          m.decl,
                          m.ident,
                          m.tps,
                          extract::interner()
                      ))
                    }
                  }
                }
                _ => fail ~"method not found"
            }
          }
          ast_map::node_item(@{
            node: ast::item_impl(_, _, _, methods), _
          }, _) => {
            match vec::find(methods, |method| {
                to_str(method.ident) == method_name
            }) {
                Some(method) => {
                    Some(pprust::fun_to_str(
                        method.decl,
                        method.ident,
                        method.tps,
                        extract::interner()
                    ))
                }
                None => fail ~"method not found"
            }
          }
          _ => fail ~"get_method_sig: item ID not bound to trait or impl"
        }
    }
}

#[test]
fn should_add_trait_method_sigs() {
    let doc = test::mk_doc(~"trait i { fn a<T>() -> int; }");
    assert doc.cratemod().traits()[0].methods[0].sig
        == Some(~"fn a<T>() -> int");
}

fn fold_impl(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::ImplDoc
) -> doc::ImplDoc {

    let srv = fold.ctxt;

    let (trait_types, self_ty) = do astsrv::exec(srv) |ctxt| {
        match ctxt.ast_map.get(doc.id()) {
          ast_map::node_item(@{
            node: ast::item_impl(_, opt_trait_type, self_ty, _), _
          }, _) => {
            let trait_types = opt_trait_type.map_default(~[], |p| {
                ~[pprust::path_to_str(p.path, extract::interner())]
            });
            (trait_types, Some(pprust::ty_to_str(self_ty,
                                                 extract::interner())))
          }
          _ => fail ~"expected impl"
        }
    };

    {
        trait_types: trait_types,
        self_ty: self_ty,
        methods: merge_methods(fold.ctxt, doc.id(), doc.methods),
        .. doc
    }
}

#[test]
fn should_add_impl_trait_types() {
    let doc = test::mk_doc(~"impl int: j { fn a<T>() { } }");
    assert doc.cratemod().impls()[0].trait_types[0] == ~"j";
}

#[test]
fn should_not_add_impl_trait_types_if_none() {
    let doc = test::mk_doc(~"impl int { fn a() { } }");
    assert vec::len(doc.cratemod().impls()[0].trait_types) == 0;
}

#[test]
fn should_add_impl_self_ty() {
    let doc = test::mk_doc(~"impl int { fn a() { } }");
    assert doc.cratemod().impls()[0].self_ty == Some(~"int");
}

#[test]
fn should_add_impl_method_sigs() {
    let doc = test::mk_doc(~"impl int { fn a<T>() -> int { fail } }");
    assert doc.cratemod().impls()[0].methods[0].sig
        == Some(~"fn a<T>() -> int");
}

fn fold_type(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::TyDoc
) -> doc::TyDoc {

    let srv = fold.ctxt;

    {
        sig: do astsrv::exec(srv) |ctxt| {
            match ctxt.ast_map.get(doc.id()) {
              ast_map::node_item(@{
                ident: ident,
                node: ast::item_ty(ty, params), _
              }, _) => {
                Some(fmt!(
                    "type %s%s = %s",
                    to_str(ident),
                    pprust::typarams_to_str(params, extract::interner()),
                    pprust::ty_to_str(ty, extract::interner())
                ))
              }
              _ => fail ~"expected type"
            }
        },
        .. doc
    }
}

#[test]
fn should_add_type_signatures() {
    let doc = test::mk_doc(~"type t<T> = int;");
    assert doc.cratemod().types()[0].sig == Some(~"type t<T> = int");
}

fn fold_struct(
    fold: fold::Fold<astsrv::Srv>,
    doc: doc::StructDoc
) -> doc::StructDoc {
    let srv = fold.ctxt;

    {
        sig: do astsrv::exec(srv) |ctxt| {
            match ctxt.ast_map.get(doc.id()) {
                ast_map::node_item(item, _) => {
                    let item = strip_struct_drop_block(item);
                    Some(pprust::item_to_str(item,
                                             extract::interner()))
                }
                _ => fail ~"not an item"
            }
        },
        .. doc
    }
}

/// Removes the drop block from structs so that they aren't displayed
/// as part of the type
fn strip_struct_drop_block(item: @ast::item) -> @ast::item {
    let node = match item.node {
        ast::item_class(def, tys) => {
            let def = @{
                dtor: None,
                .. *def
            };
            ast::item_class(def, tys)
        }
        _ => fail ~"not a struct"
    };

    @{
        node: node,
        .. *item
    }
}

#[test]
fn should_add_struct_defs() {
    let doc = test::mk_doc(~"struct S { field: () }");
    assert doc.cratemod().structs()[0].sig.get().contains("struct S {");
}

#[test]
fn should_not_serialize_struct_drop_blocks() {
    // All we care about are the fields
    let doc = test::mk_doc(~"struct S { field: (), drop { } }");
    assert !doc.cratemod().structs()[0].sig.get().contains("drop");
}

#[cfg(test)]
mod test {
    fn mk_doc(source: ~str) -> doc::Doc {
        do astsrv::from_str(source) |srv| {
            let doc = extract::from_srv(srv, ~"");
            run(srv, doc)
        }
    }
}
<|endoftext|>


<commit_before>//! The Bernoulli distribution.

use Rng;
use distributions::Distribution;

/// The Bernoulli distribution.
///
/// This is a special case of the Binomial distribution where `n = 1`.
///
/// # Example
///
/// ```rust
/// use rand::distributions::{Bernoulli, Distribution};
///
/// let d = Bernoulli::new(0.3);
/// let v = d.sample(&mut rand::thread_rng());
/// println!("{} is from a Bernoulli distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
pub struct Bernoulli {
    /// Probability of success, relative to the maximal integer.
    p_int: u64,
}

impl Bernoulli {
    /// Construct a new `Bernoulli` with the given probability of success `p`.
    ///
    /// # Panics
    ///
    /// If `p < 0` or `p > 1`.
    ///
    /// # Precision
    ///
    /// For `p = 1.0`, the resulting distribution will always generate true.
    /// For `p = 0.0`, the resulting distribution will always generate false.
    #[inline]
    pub fn new(p: f64) -> Bernoulli {
        assert!(p >= 0.0, "Bernoulli::new called with p < 0");
        assert!(p <= 1.0, "Bernoulli::new called with p > 1");
        let p_int = if p < 1.0 {
            (p * (::core::u64::MAX as f64)) as u64
        } else {
            // Avoid overflow: u64::MAX as f64 cannot be represented as u64.
            ::core::u64::MAX
        };
        Bernoulli { p_int }
    }
}

impl Distribution<bool> for Bernoulli {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> bool {
        // Make sure to always return true for p = 1.0.
        if self.p_int == ::core::u64::MAX {
            return true;
        }
        let r: u64 = rng.gen();
        r < self.p_int
    }
}

#[cfg(test)]
mod test {
    use {Rng, SmallRng, FromEntropy};
    use distributions::Distribution;
    use super::Bernoulli;

    #[test]
    fn test_trivial() {
        let mut r = SmallRng::from_entropy();
        let always_false = Bernoulli::new(0.0);
        let always_true = Bernoulli::new(1.0);
        for _ in 0..5 {
            assert_eq!(r.sample::<bool, _>(&always_false), false);
            assert_eq!(r.sample::<bool, _>(&always_true), true);
            assert_eq!(Distribution::<bool>::sample(&always_false, &mut r), false);
            assert_eq!(Distribution::<bool>::sample(&always_true, &mut r), true);
        }
    }

    #[test]
    fn test_average() {
        const P: f64 = 0.3;
        let d = Bernoulli::new(P);
        const N: u32 = 10_000_000;

        let mut sum: u32 = 0;
        let mut rng = SmallRng::from_entropy();
        for _ in 0..N {
            if d.sample(&mut rng) {
                sum += 1;
            }
        }
        let avg = (sum as f64) / (N as f64);

        assert!((avg - P).abs() < 1e-3);
    }
}
<commit_msg>Add copyright notice<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! The Bernoulli distribution.

use Rng;
use distributions::Distribution;

/// The Bernoulli distribution.
///
/// This is a special case of the Binomial distribution where `n = 1`.
///
/// # Example
///
/// ```rust
/// use rand::distributions::{Bernoulli, Distribution};
///
/// let d = Bernoulli::new(0.3);
/// let v = d.sample(&mut rand::thread_rng());
/// println!("{} is from a Bernoulli distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
pub struct Bernoulli {
    /// Probability of success, relative to the maximal integer.
    p_int: u64,
}

impl Bernoulli {
    /// Construct a new `Bernoulli` with the given probability of success `p`.
    ///
    /// # Panics
    ///
    /// If `p < 0` or `p > 1`.
    ///
    /// # Precision
    ///
    /// For `p = 1.0`, the resulting distribution will always generate true.
    /// For `p = 0.0`, the resulting distribution will always generate false.
    #[inline]
    pub fn new(p: f64) -> Bernoulli {
        assert!(p >= 0.0, "Bernoulli::new called with p < 0");
        assert!(p <= 1.0, "Bernoulli::new called with p > 1");
        let p_int = if p < 1.0 {
            (p * (::core::u64::MAX as f64)) as u64
        } else {
            // Avoid overflow: u64::MAX as f64 cannot be represented as u64.
            ::core::u64::MAX
        };
        Bernoulli { p_int }
    }
}

impl Distribution<bool> for Bernoulli {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> bool {
        // Make sure to always return true for p = 1.0.
        if self.p_int == ::core::u64::MAX {
            return true;
        }
        let r: u64 = rng.gen();
        r < self.p_int
    }
}

#[cfg(test)]
mod test {
    use {Rng, SmallRng, FromEntropy};
    use distributions::Distribution;
    use super::Bernoulli;

    #[test]
    fn test_trivial() {
        let mut r = SmallRng::from_entropy();
        let always_false = Bernoulli::new(0.0);
        let always_true = Bernoulli::new(1.0);
        for _ in 0..5 {
            assert_eq!(r.sample::<bool, _>(&always_false), false);
            assert_eq!(r.sample::<bool, _>(&always_true), true);
            assert_eq!(Distribution::<bool>::sample(&always_false, &mut r), false);
            assert_eq!(Distribution::<bool>::sample(&always_true, &mut r), true);
        }
    }

    #[test]
    fn test_average() {
        const P: f64 = 0.3;
        let d = Bernoulli::new(P);
        const N: u32 = 10_000_000;

        let mut sum: u32 = 0;
        let mut rng = SmallRng::from_entropy();
        for _ in 0..N {
            if d.sample(&mut rng) {
                sum += 1;
            }
        }
        let avg = (sum as f64) / (N as f64);

        assert!((avg - P).abs() < 1e-3);
    }
}
<|endoftext|>


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

use super::{Pty, HANDLE};

use std::i16;
use std::mem;
use std::os::windows::io::IntoRawHandle;
use std::ptr;
use std::sync::Arc;

use dunce::canonicalize;
use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite};
use miow;
use widestring::U16CString;
use winapi::ctypes::c_void;
use winapi::shared::basetsd::{PSIZE_T, SIZE_T};
use winapi::shared::minwindef::{BYTE, DWORD};
use winapi::shared::ntdef::{HANDLE, HRESULT, LPWSTR};
use winapi::shared::winerror::S_OK;
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
use winapi::um::processthreadsapi::{
    CreateProcessW, InitializeProcThreadAttributeList, UpdateProcThreadAttribute,
    PROCESS_INFORMATION, STARTUPINFOW,
};
use winapi::um::winbase::{EXTENDED_STARTUPINFO_PRESENT, STARTF_USESTDHANDLES, STARTUPINFOEXW};
use winapi::um::wincon::COORD;

use crate::cli::Options;
use crate::config::{Config, Shell};
use crate::display::OnResize;
use crate::term::SizeInfo;

// This will be merged into winapi as PR #699
// TODO: Use the winapi definition directly after that.
pub type HPCON = *mut c_void;

/// Dynamically-loaded Pseudoconsole API from kernel32.dll
///
/// The field names are deliberately PascalCase as this matches
/// the defined symbols in kernel32 and also is the convention
/// that the `winapi` crate follows.
#[allow(non_snake_case)]
struct ConptyApi {
    CreatePseudoConsole:
        unsafe extern "system" fn(COORD, HANDLE, HANDLE, DWORD, *mut HPCON) -> HRESULT,
    ResizePseudoConsole: unsafe extern "system" fn(HPCON, COORD) -> HRESULT,
    ClosePseudoConsole: unsafe extern "system" fn(HPCON),
}

impl ConptyApi {
    /// Load the API or None if it cannot be found.
    pub fn new() -> Option<Self> {
        // Unsafe because windows API calls
        unsafe {
            let hmodule = GetModuleHandleA("kernel32\0".as_ptr() as _);
            assert!(!hmodule.is_null());

            let cpc = GetProcAddress(hmodule, "CreatePseudoConsole\0".as_ptr() as _);
            let rpc = GetProcAddress(hmodule, "ResizePseudoConsole\0".as_ptr() as _);
            let clpc = GetProcAddress(hmodule, "ClosePseudoConsole\0".as_ptr() as _);

            if cpc.is_null() || rpc.is_null() || clpc.is_null() {
                None
            } else {
                Some(Self {
                    CreatePseudoConsole: mem::transmute(cpc),
                    ResizePseudoConsole: mem::transmute(rpc),
                    ClosePseudoConsole: mem::transmute(clpc),
                })
            }
        }
    }
}

/// RAII Pseudoconsole
pub struct Conpty {
    pub handle: HPCON,
    api: ConptyApi,
}

/// Handle can be cloned freely and moved between threads.
pub type ConptyHandle = Arc<Conpty>;

impl Drop for Conpty {
    fn drop(&mut self) {
        unsafe { (self.api.ClosePseudoConsole)(self.handle) }
    }
}

// The Conpty API can be accessed from multiple threads.
unsafe impl Send for Conpty {}
unsafe impl Sync for Conpty {}

pub fn new<'a>(
    config: &Config,
    options: &Options,
    size: &SizeInfo,
    _window_id: Option<usize>,
) -> Option<Pty<'a>> {
    if !config.enable_experimental_conpty_backend() {
        return None;
    }

    let api = ConptyApi::new()?;

    let mut pty_handle = 0 as HPCON;

    // Passing 0 as the size parameter allows the "system default" buffer
    // size to be used. There may be small performance and memory advantages
    // to be gained by tuning this in the future, but it's likely a reasonable
    // start point.
    let (conout, conout_pty_handle) = miow::pipe::anonymous(0).unwrap();
    let (conin_pty_handle, conin) = miow::pipe::anonymous(0).unwrap();

    let coord =
        coord_from_sizeinfo(size).expect("Overflow when creating initial size on pseudoconsole");

    // Create the Pseudo Console, using the pipes
    let result = unsafe {
        (api.CreatePseudoConsole)(
            coord,
            conin_pty_handle.into_raw_handle(),
            conout_pty_handle.into_raw_handle(),
            0,
            &mut pty_handle as *mut HPCON,
        )
    };

    assert!(result == S_OK);

    let mut success;

    // Prepare child process startup info

    let mut size: SIZE_T = 0;

    let mut startup_info_ex: STARTUPINFOEXW = Default::default();

    let title = options.title.as_ref().map(|w| w.as_str()).unwrap_or("Alacritty");
    let title = U16CString::from_str(title).unwrap();
    startup_info_ex.StartupInfo.lpTitle = title.as_ptr() as LPWSTR;

    startup_info_ex.StartupInfo.cb = mem::size_of::<STARTUPINFOEXW>() as u32;

    // Setting this flag but leaving all the handles as default (null) ensures the
    // pty process does not inherit any handles from this Alacritty process.
    startup_info_ex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;

    // Create the appropriately sized thread attribute list.
    unsafe {
        success =
            InitializeProcThreadAttributeList(ptr::null_mut(), 1, 0, &mut size as PSIZE_T) > 0;

        // This call was expected to return false.
        assert!(!success);
    }

    let mut attr_list: Box<[BYTE]> = vec![0; size].into_boxed_slice();

    // Set startup info's attribute list & initialize it
    //
    // Lint failure is spurious; it's because winapi's definition of PROC_THREAD_ATTRIBUTE_LIST
    // implies it is one pointer in size (32 or 64 bits) but really this is just a dummy value.
    // Casting a *mut u8 (pointer to 8 bit type) might therefore not be aligned correctly in
    // the compiler's eyes.
    #[allow(clippy::cast_ptr_alignment)]
    {
        startup_info_ex.lpAttributeList = attr_list.as_mut_ptr() as _;
    }

    unsafe {
        success = InitializeProcThreadAttributeList(
            startup_info_ex.lpAttributeList,
            1,
            0,
            &mut size as PSIZE_T,
        ) > 0;

        assert!(success);
    }

    // Set thread attribute list's Pseudo Console to the specified ConPTY
    unsafe {
        success = UpdateProcThreadAttribute(
            startup_info_ex.lpAttributeList,
            0,
            22 | 0x0002_0000, // PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
            pty_handle,
            mem::size_of::<HPCON>(),
            ptr::null_mut(),
            ptr::null_mut(),
        ) > 0;

        assert!(success);
    }

    // Get process commandline
    let default_shell = &Shell::new("powershell");
    let shell = config.shell().unwrap_or(default_shell);
    let initial_command = options.command().unwrap_or(shell);
    let mut cmdline = initial_command.args().to_vec();
    cmdline.insert(0, initial_command.program().into());

    // Warning, here be borrow hell
    let cwd = options
        .working_dir
        .as_ref()
        .map(|dir| canonicalize(dir).unwrap());
    let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap());

    // Create the client application, using startup info containing ConPTY info
    let cmdline = U16CString::from_str(&cmdline.join(" ")).unwrap();
    let cwd = cwd.map(|s| U16CString::from_str(&s).unwrap());

    let mut proc_info: PROCESS_INFORMATION = Default::default();
    unsafe {
        success = CreateProcessW(
            ptr::null(),
            cmdline.as_ptr() as LPWSTR,
            ptr::null_mut(),
            ptr::null_mut(),
            false as i32,
            EXTENDED_STARTUPINFO_PRESENT,
            ptr::null_mut(),
            cwd.map_or_else(ptr::null, |s| s.as_ptr()),
            &mut startup_info_ex.StartupInfo as *mut STARTUPINFOW,
            &mut proc_info as *mut PROCESS_INFORMATION,
        ) > 0;

        assert!(success);
    }

    // Store handle to console
    unsafe {
        HANDLE = proc_info.hProcess;
    }

    let conin = EventedAnonWrite::new(conin);
    let conout = EventedAnonRead::new(conout);

    let agent = Conpty {
        handle: pty_handle,
        api,
    };

    Some(Pty {
        handle: super::PtyHandle::Conpty(ConptyHandle::new(agent)),
        conout: super::EventedReadablePipe::Anonymous(conout),
        conin: super::EventedWritablePipe::Anonymous(conin),
        read_token: 0.into(),
        write_token: 0.into(),
    })
}

impl OnResize for ConptyHandle {
    fn on_resize(&mut self, sizeinfo: &SizeInfo) {
        if let Some(coord) = coord_from_sizeinfo(sizeinfo) {
            let result = unsafe { (self.api.ResizePseudoConsole)(self.handle, coord) };
            assert!(result == S_OK);
        }
    }
}

/// Helper to build a COORD from a SizeInfo, returing None in overflow cases.
fn coord_from_sizeinfo(sizeinfo: &SizeInfo) -> Option<COORD> {
    let cols = sizeinfo.cols().0;
    let lines = sizeinfo.lines().0;

    if cols <= i16::MAX as usize && lines <= i16::MAX as usize {
        Some(COORD {
            X: sizeinfo.cols().0 as i16,
            Y: sizeinfo.lines().0 as i16,
        })
    } else {
        None
    }
}
<commit_msg>Fix invalid --working-dir string ptr for ConPTY<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{Pty, HANDLE};

use std::i16;
use std::mem;
use std::os::windows::io::IntoRawHandle;
use std::ptr;
use std::sync::Arc;

use dunce::canonicalize;
use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite};
use miow;
use widestring::U16CString;
use winapi::ctypes::c_void;
use winapi::shared::basetsd::{PSIZE_T, SIZE_T};
use winapi::shared::minwindef::{BYTE, DWORD};
use winapi::shared::ntdef::{HANDLE, HRESULT, LPWSTR};
use winapi::shared::winerror::S_OK;
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
use winapi::um::processthreadsapi::{
    CreateProcessW, InitializeProcThreadAttributeList, UpdateProcThreadAttribute,
    PROCESS_INFORMATION, STARTUPINFOW,
};
use winapi::um::winbase::{EXTENDED_STARTUPINFO_PRESENT, STARTF_USESTDHANDLES, STARTUPINFOEXW};
use winapi::um::wincon::COORD;

use crate::cli::Options;
use crate::config::{Config, Shell};
use crate::display::OnResize;
use crate::term::SizeInfo;

// This will be merged into winapi as PR #699
// TODO: Use the winapi definition directly after that.
pub type HPCON = *mut c_void;

/// Dynamically-loaded Pseudoconsole API from kernel32.dll
///
/// The field names are deliberately PascalCase as this matches
/// the defined symbols in kernel32 and also is the convention
/// that the `winapi` crate follows.
#[allow(non_snake_case)]
struct ConptyApi {
    CreatePseudoConsole:
        unsafe extern "system" fn(COORD, HANDLE, HANDLE, DWORD, *mut HPCON) -> HRESULT,
    ResizePseudoConsole: unsafe extern "system" fn(HPCON, COORD) -> HRESULT,
    ClosePseudoConsole: unsafe extern "system" fn(HPCON),
}

impl ConptyApi {
    /// Load the API or None if it cannot be found.
    pub fn new() -> Option<Self> {
        // Unsafe because windows API calls
        unsafe {
            let hmodule = GetModuleHandleA("kernel32\0".as_ptr() as _);
            assert!(!hmodule.is_null());

            let cpc = GetProcAddress(hmodule, "CreatePseudoConsole\0".as_ptr() as _);
            let rpc = GetProcAddress(hmodule, "ResizePseudoConsole\0".as_ptr() as _);
            let clpc = GetProcAddress(hmodule, "ClosePseudoConsole\0".as_ptr() as _);

            if cpc.is_null() || rpc.is_null() || clpc.is_null() {
                None
            } else {
                Some(Self {
                    CreatePseudoConsole: mem::transmute(cpc),
                    ResizePseudoConsole: mem::transmute(rpc),
                    ClosePseudoConsole: mem::transmute(clpc),
                })
            }
        }
    }
}

/// RAII Pseudoconsole
pub struct Conpty {
    pub handle: HPCON,
    api: ConptyApi,
}

/// Handle can be cloned freely and moved between threads.
pub type ConptyHandle = Arc<Conpty>;

impl Drop for Conpty {
    fn drop(&mut self) {
        unsafe { (self.api.ClosePseudoConsole)(self.handle) }
    }
}

// The Conpty API can be accessed from multiple threads.
unsafe impl Send for Conpty {}
unsafe impl Sync for Conpty {}

pub fn new<'a>(
    config: &Config,
    options: &Options,
    size: &SizeInfo,
    _window_id: Option<usize>,
) -> Option<Pty<'a>> {
    if !config.enable_experimental_conpty_backend() {
        return None;
    }

    let api = ConptyApi::new()?;

    let mut pty_handle = 0 as HPCON;

    // Passing 0 as the size parameter allows the "system default" buffer
    // size to be used. There may be small performance and memory advantages
    // to be gained by tuning this in the future, but it's likely a reasonable
    // start point.
    let (conout, conout_pty_handle) = miow::pipe::anonymous(0).unwrap();
    let (conin_pty_handle, conin) = miow::pipe::anonymous(0).unwrap();

    let coord =
        coord_from_sizeinfo(size).expect("Overflow when creating initial size on pseudoconsole");

    // Create the Pseudo Console, using the pipes
    let result = unsafe {
        (api.CreatePseudoConsole)(
            coord,
            conin_pty_handle.into_raw_handle(),
            conout_pty_handle.into_raw_handle(),
            0,
            &mut pty_handle as *mut HPCON,
        )
    };

    assert!(result == S_OK);

    let mut success;

    // Prepare child process startup info

    let mut size: SIZE_T = 0;

    let mut startup_info_ex: STARTUPINFOEXW = Default::default();

    let title = options.title.as_ref().map(|w| w.as_str()).unwrap_or("Alacritty");
    let title = U16CString::from_str(title).unwrap();
    startup_info_ex.StartupInfo.lpTitle = title.as_ptr() as LPWSTR;

    startup_info_ex.StartupInfo.cb = mem::size_of::<STARTUPINFOEXW>() as u32;

    // Setting this flag but leaving all the handles as default (null) ensures the
    // pty process does not inherit any handles from this Alacritty process.
    startup_info_ex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;

    // Create the appropriately sized thread attribute list.
    unsafe {
        success =
            InitializeProcThreadAttributeList(ptr::null_mut(), 1, 0, &mut size as PSIZE_T) > 0;

        // This call was expected to return false.
        assert!(!success);
    }

    let mut attr_list: Box<[BYTE]> = vec![0; size].into_boxed_slice();

    // Set startup info's attribute list & initialize it
    //
    // Lint failure is spurious; it's because winapi's definition of PROC_THREAD_ATTRIBUTE_LIST
    // implies it is one pointer in size (32 or 64 bits) but really this is just a dummy value.
    // Casting a *mut u8 (pointer to 8 bit type) might therefore not be aligned correctly in
    // the compiler's eyes.
    #[allow(clippy::cast_ptr_alignment)]
    {
        startup_info_ex.lpAttributeList = attr_list.as_mut_ptr() as _;
    }

    unsafe {
        success = InitializeProcThreadAttributeList(
            startup_info_ex.lpAttributeList,
            1,
            0,
            &mut size as PSIZE_T,
        ) > 0;

        assert!(success);
    }

    // Set thread attribute list's Pseudo Console to the specified ConPTY
    unsafe {
        success = UpdateProcThreadAttribute(
            startup_info_ex.lpAttributeList,
            0,
            22 | 0x0002_0000, // PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
            pty_handle,
            mem::size_of::<HPCON>(),
            ptr::null_mut(),
            ptr::null_mut(),
        ) > 0;

        assert!(success);
    }

    // Get process commandline
    let default_shell = &Shell::new("powershell");
    let shell = config.shell().unwrap_or(default_shell);
    let initial_command = options.command().unwrap_or(shell);
    let mut cmdline = initial_command.args().to_vec();
    cmdline.insert(0, initial_command.program().into());

    // Warning, here be borrow hell
    let cwd = options
        .working_dir
        .as_ref()
        .map(|dir| canonicalize(dir).unwrap());
    let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap());

    // Create the client application, using startup info containing ConPTY info
    let cmdline = U16CString::from_str(&cmdline.join(" ")).unwrap();
    let cwd = cwd.map(|s| U16CString::from_str(&s).unwrap());

    let mut proc_info: PROCESS_INFORMATION = Default::default();
    unsafe {
        success = CreateProcessW(
            ptr::null(),
            cmdline.as_ptr() as LPWSTR,
            ptr::null_mut(),
            ptr::null_mut(),
            false as i32,
            EXTENDED_STARTUPINFO_PRESENT,
            ptr::null_mut(),
            cwd.as_ref().map_or_else(ptr::null, |s| s.as_ptr()),
            &mut startup_info_ex.StartupInfo as *mut STARTUPINFOW,
            &mut proc_info as *mut PROCESS_INFORMATION,
        ) > 0;

        assert!(success);
    }

    // Store handle to console
    unsafe {
        HANDLE = proc_info.hProcess;
    }

    let conin = EventedAnonWrite::new(conin);
    let conout = EventedAnonRead::new(conout);

    let agent = Conpty {
        handle: pty_handle,
        api,
    };

    Some(Pty {
        handle: super::PtyHandle::Conpty(ConptyHandle::new(agent)),
        conout: super::EventedReadablePipe::Anonymous(conout),
        conin: super::EventedWritablePipe::Anonymous(conin),
        read_token: 0.into(),
        write_token: 0.into(),
    })
}

impl OnResize for ConptyHandle {
    fn on_resize(&mut self, sizeinfo: &SizeInfo) {
        if let Some(coord) = coord_from_sizeinfo(sizeinfo) {
            let result = unsafe { (self.api.ResizePseudoConsole)(self.handle, coord) };
            assert!(result == S_OK);
        }
    }
}

/// Helper to build a COORD from a SizeInfo, returing None in overflow cases.
fn coord_from_sizeinfo(sizeinfo: &SizeInfo) -> Option<COORD> {
    let cols = sizeinfo.cols().0;
    let lines = sizeinfo.lines().0;

    if cols <= i16::MAX as usize && lines <= i16::MAX as usize {
        Some(COORD {
            X: sizeinfo.cols().0 as i16,
            Y: sizeinfo.lines().0 as i16,
        })
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use std::mem;
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
use syntax::ext::quote::rt::ToTokens;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::parse;
use syntax::parse::parser::Parser as RustParser;
use syntax::parse::token::{self, DelimToken};
use syntax::ptr::P;

use super::render::{Escape, Renderer};

macro_rules! dollar {
    () => (TtToken(_, token::Dollar))
}
macro_rules! dot {
    () => (TtToken(_, token::Dot))
}
macro_rules! eq {
    () => (TtToken(_, token::Eq))
}
macro_rules! not {
    () => (TtToken(_, token::Not))
}
macro_rules! question {
    () => (TtToken(_, token::Question))
}
macro_rules! semi {
    () => (TtToken(_, token::Semi))
}
macro_rules! minus {
    () => (TtToken(_, token::BinOp(token::Minus)))
}
macro_rules! slash {
    () => (TtToken(_, token::BinOp(token::Slash)))
}
macro_rules! literal {
    () => (TtToken(_, token::Literal(..)))
}
macro_rules! ident {
    ($x:pat) => (ident!(_, $x));
    ($sp:pat, $x:pat) => (TtToken($sp, token::Ident($x, token::IdentStyle::Plain)))
}

pub fn parse(cx: &ExtCtxt, input: &[TokenTree], sp: Span) -> P<Expr> {
    let mut parser = Parser {
        in_attr: false,
        input: input,
        span: sp,
        render: Renderer::new(cx),
    };
    parser.markups();
    parser.into_render().into_expr()
}

struct Parser<'cx, 's: 'cx, 'i> {
    in_attr: bool,
    input: &'i [TokenTree],
    span: Span,
    render: Renderer<'cx, 's>,
}

impl<'cx, 's, 'i> Parser<'cx, 's, 'i> {
    /// Finalize the `Parser`, returning the `Renderer` underneath.
    fn into_render(self) -> Renderer<'cx, 's> {
        let Parser { render, .. } = self;
        render
    }

    /// Consume `n` items from the input.
    fn shift(&mut self, n: usize) {
        self.input = &self.input[n..];
    }

    /// Construct a Rust AST parser from the given token tree.
    fn new_rust_parser(&self, tts: Vec<TokenTree>) -> RustParser<'s> {
        parse::tts_to_parser(self.render.cx.parse_sess, tts, self.render.cx.cfg.clone())
    }

    fn markups(&mut self) {
        loop {
            match self.input {
                [] => return,
                [semi!(), ..] => self.shift(1),
                [_, ..] => if !self.markup() { return },
            }
        }
    }

    fn markup(&mut self) -> bool {
        match self.input {
            // Literal
            [minus!(), ref tt @ literal!(), ..] => {
                self.shift(2);
                self.literal(tt, true);
            },
            [ref tt @ literal!(), ..] => {
                self.shift(1);
                self.literal(tt, false);
            },
            // If
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "if" => {
                self.shift(2);
                self.if_expr(sp);
            },
            // For
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "for" => {
                self.shift(2);
                self.for_expr(sp);
            },
            // Splice
            [ref tt @ dollar!(), dollar!(), ..] => {
                self.shift(2);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::PassThru);
            },
            [ref tt @ dollar!(), ..] => {
                self.shift(1);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::Escape);
            },
            // Element
            [ident!(sp, name), ..] => {
                self.shift(1);
                self.element(sp, name.as_str());
            },
            // Block
            [TtDelimited(sp, ref d), ..] if d.delim == token::DelimToken::Brace => {
                self.shift(1);
                let stmts = self.block(sp, &d.tts);
                self.render.push_stmts(stmts);
            },
            // ???
            _ => {
                if let [ref tt, ..] = self.input {
                    self.render.cx.span_err(tt.get_span(), "invalid syntax");
                } else {
                    self.render.cx.span_err(self.span, "unexpected end of block");
                }
                return false;
            },
        }
        true
    }

    fn literal(&mut self, tt: &TokenTree, minus: bool) {
        let lit = self.new_rust_parser(vec![tt.clone()]).parse_lit();
        match lit_to_string(self.render.cx, lit, minus) {
            Some(s) => self.render.string(&s, Escape::Escape),
            None => {},
        }
    }

    fn if_expr(&mut self, sp: Span) {
        // Parse the initial if
        let mut if_cond = vec![];
        let if_body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                if_body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                if_cond.push(tt.clone());
            },
            [] => self.render.cx.span_fatal(sp, "expected body for this $if"),
        }}
        // Parse the (optional) else
        let else_body = match self.input {
            [dollar!(), ident!(else_), ..] if else_.as_str() == "else" => {
                self.shift(2);
                match self.input {
                    [ident!(sp, if_), ..] if if_.as_str() == "if" => {
                        self.shift(1);
                        let else_body = {
                            // Parse an if expression, but capture the result
                            // rather than emitting it right away
                            let mut render = self.render.fork();
                            mem::swap(&mut self.render, &mut render);
                            self.if_expr(sp);
                            mem::swap(&mut self.render, &mut render);
                            render.into_stmts()
                        };
                        Some(else_body)
                    },
                    [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                        self.shift(1);
                        Some(self.block(sp, &d.tts))
                    },
                    _ => self.render.cx.span_fatal(sp, "expected body for this $else"),
                }
            },
            _ => None,
        };
        self.render.emit_if(if_cond, if_body, else_body);
    }

    fn for_expr(&mut self, sp: Span) {
        let mut pattern = vec![];
        loop { match self.input {
            [ident!(in_), ..] if in_.as_str() == "in" => {
                self.shift(1);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                pattern.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let pattern = self.new_rust_parser(pattern).parse_pat();
        let mut iterable = vec![];
        let body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                iterable.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let iterable = self.new_rust_parser(iterable).parse_expr();
        self.render.emit_for(pattern, iterable, body);
    }

    fn splice(&mut self, sp: Span) -> P<Expr> {
        let mut tts = vec![];
        // First, munch a single token tree
        if let [ref tt, ..] = self.input {
            self.shift(1);
            tts.push(tt.clone());
        }
        loop { match self.input {
            // Munch attribute lookups e.g. `$person.address.street`
            [ref dot @ dot!(), ref ident @ ident!(_), ..] => {
                self.shift(2);
                tts.push(dot.clone());
                tts.push(ident.clone());
            },
            // Munch function calls `()` and indexing operations `[]`
            [TtDelimited(sp, ref d), ..] if d.delim != token::DelimToken::Brace => {
                self.shift(1);
                tts.push(TtDelimited(sp, d.clone()));
            },
            _ => break,
        }}
        if tts.is_empty() {
            self.render.cx.span_fatal(sp, "expected expression for this splice");
        } else {
            self.new_rust_parser(tts).parse_expr()
        }
    }

    fn element(&mut self, sp: Span, name: &str) {
        if self.in_attr {
            self.render.cx.span_err(sp, "unexpected element, you silly bumpkin");
            return;
        }
        self.render.element_open_start(name);
        self.attrs();
        self.render.element_open_end();
        if let [slash!(), ..] = self.input {
            self.shift(1);
        } else {
            self.markup();
            self.render.element_close(name);
        }
    }

    fn attrs(&mut self) {
        loop { match self.input {
            [ident!(name), eq!(), ..] => {
                // Non-empty attribute
                self.shift(2);
                self.render.attribute_start(name.as_str());
                {
                    // Parse a value under an attribute context
                    let mut in_attr = true;
                    mem::swap(&mut self.in_attr, &mut in_attr);
                    self.markup();
                    mem::swap(&mut self.in_attr, &mut in_attr);
                }
                self.render.attribute_end();
            },
            [ident!(name), question!(), ..] => {
                // Empty attribute
                self.shift(2);
                if let [ref tt @ eq!(), ..] = self.input {
                    // Toggle the attribute based on a boolean expression
                    self.shift(1);
                    let cond = self.splice(tt.get_span());
                    // Silence "unnecessary parentheses" warnings
                    let cond = strip_outer_parens(cond).to_tokens(self.render.cx);
                    let body = {
                        let mut r = self.render.fork();
                        r.attribute_empty(name.as_str());
                        r.into_stmts()
                    };
                    self.render.emit_if(cond, body, None);
                } else {
                    // Write the attribute unconditionally
                    self.render.attribute_empty(name.as_str());
                }
            },
            _ => return,
        }}
    }

    fn block(&mut self, sp: Span, tts: &[TokenTree]) -> Vec<P<Stmt>> {
        let mut parse = Parser {
            in_attr: self.in_attr,
            input: tts,
            span: sp,
            render: self.render.fork(),
        };
        parse.markups();
        parse.into_render().into_stmts()
    }
}

/// Convert a literal to a string.
fn lit_to_string(cx: &ExtCtxt, lit: Lit, minus: bool) -> Option<String> {
    use syntax::ast::Lit_::*;
    let mut result = String::new();
    if minus {
        result.push('-');
    }
    match lit.node {
        LitStr(s, _) => result.push_str(&s),
        LitBinary(..) | LitByte(..) => {
            cx.span_err(lit.span, "cannot splice binary data");
            return None;
        },
        LitChar(c) => result.push(c),
        LitInt(x, _) => result.push_str(&x.to_string()),
        LitFloat(s, _) | LitFloatUnsuffixed(s) => result.push_str(&s),
        LitBool(b) => result.push_str(if b { "true" } else { "false" }),
    };
    Some(result)
}

/// If the expression is wrapped in parentheses, strip them off.
fn strip_outer_parens(expr: P<Expr>) -> P<Expr> {
    expr.and_then(|expr| match expr {
        Expr { node: ExprParen(inner), .. } => inner,
        expr => P(expr),
    })
}
<commit_msg>Guard against trailing tokens in Rust exprs<commit_after>use std::mem;
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
use syntax::ext::quote::rt::ToTokens;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::parse;
use syntax::parse::parser::Parser as RustParser;
use syntax::parse::token::{self, DelimToken};
use syntax::ptr::P;

use super::render::{Escape, Renderer};

macro_rules! dollar {
    () => (TtToken(_, token::Dollar))
}
macro_rules! dot {
    () => (TtToken(_, token::Dot))
}
macro_rules! eq {
    () => (TtToken(_, token::Eq))
}
macro_rules! not {
    () => (TtToken(_, token::Not))
}
macro_rules! question {
    () => (TtToken(_, token::Question))
}
macro_rules! semi {
    () => (TtToken(_, token::Semi))
}
macro_rules! minus {
    () => (TtToken(_, token::BinOp(token::Minus)))
}
macro_rules! slash {
    () => (TtToken(_, token::BinOp(token::Slash)))
}
macro_rules! literal {
    () => (TtToken(_, token::Literal(..)))
}
macro_rules! ident {
    ($x:pat) => (ident!(_, $x));
    ($sp:pat, $x:pat) => (TtToken($sp, token::Ident($x, token::IdentStyle::Plain)))
}

pub fn parse(cx: &ExtCtxt, input: &[TokenTree], sp: Span) -> P<Expr> {
    let mut parser = Parser {
        in_attr: false,
        input: input,
        span: sp,
        render: Renderer::new(cx),
    };
    parser.markups();
    parser.into_render().into_expr()
}

struct Parser<'cx, 's: 'cx, 'i> {
    in_attr: bool,
    input: &'i [TokenTree],
    span: Span,
    render: Renderer<'cx, 's>,
}

impl<'cx, 's, 'i> Parser<'cx, 's, 'i> {
    /// Finalize the `Parser`, returning the `Renderer` underneath.
    fn into_render(self) -> Renderer<'cx, 's> {
        let Parser { render, .. } = self;
        render
    }

    /// Consume `n` items from the input.
    fn shift(&mut self, n: usize) {
        self.input = &self.input[n..];
    }

    /// Construct a Rust AST parser from the given token tree.
    fn with_rust_parser<F, T>(&self, tts: Vec<TokenTree>, callback: F) -> T where
        F: FnOnce(&mut RustParser<'s>) -> T
    {
        let mut parser = parse::tts_to_parser(self.render.cx.parse_sess, tts,
                                              self.render.cx.cfg.clone());
        let result = callback(&mut parser);
        // Make sure all tokens were consumed
        if parser.token != token::Eof {
            let token = parser.this_token_to_string();
            self.render.cx.span_err(parser.span,
                                    &format!("unexpected token: `{}`", token));
        }
        result
    }

    fn markups(&mut self) {
        loop {
            match self.input {
                [] => return,
                [semi!(), ..] => self.shift(1),
                [_, ..] => if !self.markup() { return },
            }
        }
    }

    fn markup(&mut self) -> bool {
        match self.input {
            // Literal
            [minus!(), ref tt @ literal!(), ..] => {
                self.shift(2);
                self.literal(tt, true);
            },
            [ref tt @ literal!(), ..] => {
                self.shift(1);
                self.literal(tt, false);
            },
            // If
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "if" => {
                self.shift(2);
                self.if_expr(sp);
            },
            // For
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "for" => {
                self.shift(2);
                self.for_expr(sp);
            },
            // Splice
            [ref tt @ dollar!(), dollar!(), ..] => {
                self.shift(2);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::PassThru);
            },
            [ref tt @ dollar!(), ..] => {
                self.shift(1);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::Escape);
            },
            // Element
            [ident!(sp, name), ..] => {
                self.shift(1);
                self.element(sp, name.as_str());
            },
            // Block
            [TtDelimited(sp, ref d), ..] if d.delim == token::DelimToken::Brace => {
                self.shift(1);
                let stmts = self.block(sp, &d.tts);
                self.render.push_stmts(stmts);
            },
            // ???
            _ => {
                if let [ref tt, ..] = self.input {
                    self.render.cx.span_err(tt.get_span(), "invalid syntax");
                } else {
                    self.render.cx.span_err(self.span, "unexpected end of block");
                }
                return false;
            },
        }
        true
    }

    fn literal(&mut self, tt: &TokenTree, minus: bool) {
        let lit = self.with_rust_parser(vec![tt.clone()], RustParser::parse_lit);
        match lit_to_string(self.render.cx, lit, minus) {
            Some(s) => self.render.string(&s, Escape::Escape),
            None => {},
        }
    }

    fn if_expr(&mut self, sp: Span) {
        // Parse the initial if
        let mut if_cond = vec![];
        let if_body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                if_body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                if_cond.push(tt.clone());
            },
            [] => self.render.cx.span_fatal(sp, "expected body for this $if"),
        }}
        // Parse the (optional) else
        let else_body = match self.input {
            [dollar!(), ident!(else_), ..] if else_.as_str() == "else" => {
                self.shift(2);
                match self.input {
                    [ident!(sp, if_), ..] if if_.as_str() == "if" => {
                        self.shift(1);
                        let else_body = {
                            // Parse an if expression, but capture the result
                            // rather than emitting it right away
                            let mut render = self.render.fork();
                            mem::swap(&mut self.render, &mut render);
                            self.if_expr(sp);
                            mem::swap(&mut self.render, &mut render);
                            render.into_stmts()
                        };
                        Some(else_body)
                    },
                    [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                        self.shift(1);
                        Some(self.block(sp, &d.tts))
                    },
                    _ => self.render.cx.span_fatal(sp, "expected body for this $else"),
                }
            },
            _ => None,
        };
        self.render.emit_if(if_cond, if_body, else_body);
    }

    fn for_expr(&mut self, sp: Span) {
        let mut pattern = vec![];
        loop { match self.input {
            [ident!(in_), ..] if in_.as_str() == "in" => {
                self.shift(1);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                pattern.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let pattern = self.with_rust_parser(pattern, RustParser::parse_pat);
        let mut iterable = vec![];
        let body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                iterable.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let iterable = self.with_rust_parser(iterable, RustParser::parse_expr);
        self.render.emit_for(pattern, iterable, body);
    }

    fn splice(&mut self, sp: Span) -> P<Expr> {
        let mut tts = vec![];
        // First, munch a single token tree
        if let [ref tt, ..] = self.input {
            self.shift(1);
            tts.push(tt.clone());
        }
        loop { match self.input {
            // Munch attribute lookups e.g. `$person.address.street`
            [ref dot @ dot!(), ref ident @ ident!(_), ..] => {
                self.shift(2);
                tts.push(dot.clone());
                tts.push(ident.clone());
            },
            // Munch function calls `()` and indexing operations `[]`
            [TtDelimited(sp, ref d), ..] if d.delim != token::DelimToken::Brace => {
                self.shift(1);
                tts.push(TtDelimited(sp, d.clone()));
            },
            _ => break,
        }}
        if tts.is_empty() {
            self.render.cx.span_fatal(sp, "expected expression for this splice");
        } else {
            self.with_rust_parser(tts, RustParser::parse_expr)
        }
    }

    fn element(&mut self, sp: Span, name: &str) {
        if self.in_attr {
            self.render.cx.span_err(sp, "unexpected element, you silly bumpkin");
            return;
        }
        self.render.element_open_start(name);
        self.attrs();
        self.render.element_open_end();
        if let [slash!(), ..] = self.input {
            self.shift(1);
        } else {
            self.markup();
            self.render.element_close(name);
        }
    }

    fn attrs(&mut self) {
        loop { match self.input {
            [ident!(name), eq!(), ..] => {
                // Non-empty attribute
                self.shift(2);
                self.render.attribute_start(name.as_str());
                {
                    // Parse a value under an attribute context
                    let mut in_attr = true;
                    mem::swap(&mut self.in_attr, &mut in_attr);
                    self.markup();
                    mem::swap(&mut self.in_attr, &mut in_attr);
                }
                self.render.attribute_end();
            },
            [ident!(name), question!(), ..] => {
                // Empty attribute
                self.shift(2);
                if let [ref tt @ eq!(), ..] = self.input {
                    // Toggle the attribute based on a boolean expression
                    self.shift(1);
                    let cond = self.splice(tt.get_span());
                    // Silence "unnecessary parentheses" warnings
                    let cond = strip_outer_parens(cond).to_tokens(self.render.cx);
                    let body = {
                        let mut r = self.render.fork();
                        r.attribute_empty(name.as_str());
                        r.into_stmts()
                    };
                    self.render.emit_if(cond, body, None);
                } else {
                    // Write the attribute unconditionally
                    self.render.attribute_empty(name.as_str());
                }
            },
            _ => return,
        }}
    }

    fn block(&mut self, sp: Span, tts: &[TokenTree]) -> Vec<P<Stmt>> {
        let mut parse = Parser {
            in_attr: self.in_attr,
            input: tts,
            span: sp,
            render: self.render.fork(),
        };
        parse.markups();
        parse.into_render().into_stmts()
    }
}

/// Convert a literal to a string.
fn lit_to_string(cx: &ExtCtxt, lit: Lit, minus: bool) -> Option<String> {
    use syntax::ast::Lit_::*;
    let mut result = String::new();
    if minus {
        result.push('-');
    }
    match lit.node {
        LitStr(s, _) => result.push_str(&s),
        LitBinary(..) | LitByte(..) => {
            cx.span_err(lit.span, "cannot splice binary data");
            return None;
        },
        LitChar(c) => result.push(c),
        LitInt(x, _) => result.push_str(&x.to_string()),
        LitFloat(s, _) | LitFloatUnsuffixed(s) => result.push_str(&s),
        LitBool(b) => result.push_str(if b { "true" } else { "false" }),
    };
    Some(result)
}

/// If the expression is wrapped in parentheses, strip them off.
fn strip_outer_parens(expr: P<Expr>) -> P<Expr> {
    expr.and_then(|expr| match expr {
        Expr { node: ExprParen(inner), .. } => inner,
        expr => P(expr),
    })
}
<|endoftext|>


<commit_before>use std::{mem, rc::Rc};

use log::{debug, warn};
use petgraph::graph::NodeIndex;
use rustc_hash::FxHashMap;

use crate::{
    error::Result,
    typemap::{TypeGraphIdx, TypeMap},
};

impl TypeMap {
    pub(crate) fn merge(
        &mut self,
        id_of_code: &str,
        code: &str,
        target_pointer_width: usize,
    ) -> Result<()> {
        debug!("merging {} with our rules", id_of_code);
        let mut was_traits_usage_code = FxHashMap::default();
        mem::swap(&mut was_traits_usage_code, &mut self.traits_usage_code);
        let mut new_data = crate::typemap::parse::parse(
            id_of_code,
            code,
            target_pointer_width,
            was_traits_usage_code,
        )?;
        mem::swap(&mut new_data.traits_usage_code, &mut self.traits_usage_code);

        for node in new_data.conv_graph.node_indices() {
            process_new_node(node, &new_data, self);
        }
        self.utils_code.append(&mut new_data.utils_code);
        //TODO: more intellect to process new generics
        self.generic_edges.append(&mut new_data.generic_edges);
        Ok(())
    }
}

fn get_graph_node_idx(
    node_new_data_idx: NodeIndex<TypeGraphIdx>,
    new_data: &TypeMap,
    data: &mut TypeMap,
) -> NodeIndex<TypeGraphIdx> {
    let node = &new_data.conv_graph[node_new_data_idx];
    debug!("get_graph_node_idx: handling new node {}", node);
    let node2 = node.clone();
    let data_rust_names_map = &mut data.rust_names_map;
    let data_conv_graph = &mut data.conv_graph;
    let idx = *data_rust_names_map
        .entry(node.normalized_name.clone())
        .or_insert_with(|| {
            let idx = data_conv_graph.add_node(node2);
            Rc::make_mut(&mut data_conv_graph[idx]).graph_idx = idx;
            idx
        });

    Rc::make_mut(&mut data_conv_graph[idx]).merge(node);

    if let Some((foreign_name, _)) = new_data
        .foreign_names_map
        .iter()
        .find(|x| *x.1 == node_new_data_idx)
    {
        data.foreign_names_map.insert(foreign_name.clone(), idx);
    }
    idx
}

fn process_new_node(new_data_idx: NodeIndex<TypeGraphIdx>, new_data: &TypeMap, data: &mut TypeMap) {
    let self_src = get_graph_node_idx(new_data_idx, new_data, data);
    let mut edges = new_data
        .conv_graph
        .neighbors_directed(new_data_idx, petgraph::Outgoing)
        .detach();
    while let Some((edge, target)) = edges.next(&new_data.conv_graph) {
        let self_target = get_graph_node_idx(target, new_data, data);

        if let Some(existing_edge) = data.conv_graph.find_edge(self_src, self_target) {
            warn!(
                "typemap merge: Converstation {:?} from {:?} to {:?} ignored, we use {:?} instead",
                new_data.conv_graph[edge], self_src, self_target, data.conv_graph[existing_edge]
            );
        } else {
            data.conv_graph
                .add_edge(self_src, self_target, new_data.conv_graph[edge].clone());
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::typemap::find_conversation_path;
    use proc_macro2::Span;
    use rustc_hash::FxHashSet;
    use syn::{parse_quote, Type};

    #[test]
    fn test_merge() {
        let mut types_map = TypeMap::default();
        types_map
            .merge(
                "test_merge",
                r#"
mod swig_foreign_types_map {
    #![swig_foreigner_type="boolean"]
    #![swig_rust_type="jboolean"]
    #![swig_foreigner_type="int"]
    #![swig_rust_type="jint"]
}

fn helper1() {
}

#[swig_code = "let mut {to_var}: {to_var_type} = {from_var}.swig_into(env);"]
trait SwigInto<T> {
    fn swig_into(self, env: *mut JNIEnv) -> T;
}

#[swig_code = "let mut {to_var}: {to_var_type} = <{to_var_type}>::swig_from({from_var}, env);"]
trait SwigFrom<T> {
    fn swig_from(T, env: *mut JNIEnv) -> Self;
}

impl SwigInto<bool> for jboolean {
    fn swig_into(self, _: *mut JNIEnv) -> bool {
        self != 0
    }
}

fn helper2() {
}

impl SwigFrom<bool> for jboolean {
    fn swig_from(x: bool, _: *mut JNIEnv) -> Self {
        if x { 1 as jboolean } else { 0 as jboolean }
    }
}
impl SwigFrom<i32> for jint {
    fn swig_from(x: i32, _: *mut JNIEnv) -> Self {
        x
    }
}

fn helper3() {
}
"#,
                64,
            )
            .unwrap();
        assert_eq!(
            {
                let mut set = FxHashSet::default();
                for k in types_map.foreign_names_map.keys() {
                    set.insert(k.clone());
                }
                set
            },
            {
                let mut set = FxHashSet::default();
                set.insert("boolean".into());
                set.insert("int".into());
                set
            }
        );
        let ty_i32 = types_map.find_or_alloc_rust_type(&parse_type! { i32 });
        assert_eq!(
            types_map
                .map_through_conversation_to_foreign(
                    &ty_i32,
                    petgraph::Direction::Outgoing,
                    Span::call_site(),
                    |_, fc| fc.constructor_ret_type.clone(),
                )
                .unwrap()
                .name,
            "int"
        );
        assert_eq!(
            "let mut {to_var}: {to_var_type} = {from_var}.swig_into(env);",
            {
                let from = types_map.rust_names_map["jboolean"];
                let to = types_map.rust_names_map["bool"];
                let conv = &types_map.conv_graph[types_map.conv_graph.find_edge(from, to).unwrap()];
                conv.code_template.clone()
            },
        );

        let from = types_map.rust_names_map["jboolean"];
        let to = types_map.rust_names_map["bool"];
        assert_eq!(
            find_conversation_path(&types_map.conv_graph, from, to, Span::call_site()).unwrap(),
            vec![types_map.conv_graph.find_edge(from, to).unwrap()]
        );

        let from = types_map.rust_names_map["bool"];
        let to = types_map.rust_names_map["jboolean"];
        assert_eq!(
            find_conversation_path(&types_map.conv_graph, from, to, Span::call_site()).unwrap(),
            vec![types_map.conv_graph.find_edge(from, to).unwrap()]
        );
        assert_eq!(
            types_map
                .utils_code
                .iter()
                .filter_map(|v| {
                    let item: syn::Item = v.clone();
                    match item {
                        syn::Item::Fn(ref fun) => Some(fun.ident.to_string()),
                        syn::Item::Trait(ref trait_) => Some(trait_.ident.to_string()),
                        _ => None,
                    }
                })
                .collect::<Vec<_>>(),
            vec!["helper1", "SwigInto", "SwigFrom", "helper2", "helper3"]
        );
    }
}
<commit_msg>refactoring: change merge strategy<commit_after>use std::{mem, rc::Rc};

use log::{debug, info};
use petgraph::graph::NodeIndex;
use rustc_hash::FxHashMap;

use crate::{error::Result, typemap::TypeMap};

impl TypeMap {
    pub(crate) fn merge(
        &mut self,
        id_of_code: &str,
        code: &str,
        target_pointer_width: usize,
    ) -> Result<()> {
        debug!("merging {} with our rules", id_of_code);
        let mut was_traits_usage_code = FxHashMap::default();
        mem::swap(&mut was_traits_usage_code, &mut self.traits_usage_code);
        let mut new_data = crate::typemap::parse::parse(
            id_of_code,
            code,
            target_pointer_width,
            was_traits_usage_code,
        )?;
        mem::swap(&mut new_data.traits_usage_code, &mut self.traits_usage_code);

        let mut new_node_to_our_map = FxHashMap::<NodeIndex, NodeIndex>::default();
        add_new_nodes(&new_data, self, &mut new_node_to_our_map);
        add_new_edges(&new_data, self, &new_node_to_our_map);
        add_new_ftypes(&new_data, self, &new_node_to_our_map);

        self.utils_code.append(&mut new_data.utils_code);
        //TODO: more intellect to process new generics
        self.generic_edges.append(&mut new_data.generic_edges);
        Ok(())
    }
}

fn add_new_nodes(
    new_data: &TypeMap,
    data: &mut TypeMap,
    new_node_to_our_map: &mut FxHashMap<NodeIndex, NodeIndex>,
) {
    for new_node_idx in new_data.conv_graph.node_indices() {
        let new_node = &new_data.conv_graph[new_node_idx];
        let data_rust_names_map = &mut data.rust_names_map;
        let data_conv_graph = &mut data.conv_graph;
        let data_idx = *data_rust_names_map
            .entry(new_node.normalized_name.clone())
            .or_insert_with(|| {
                let idx = data_conv_graph.add_node((*new_node).clone());
                Rc::make_mut(&mut data_conv_graph[idx]).graph_idx = idx;
                idx
            });
        Rc::make_mut(&mut data_conv_graph[data_idx]).merge(&new_data.conv_graph[new_node_idx]);
        new_node_to_our_map.insert(new_node_idx, data_idx);
    }
}

fn add_new_edges(
    new_data: &TypeMap,
    data: &mut TypeMap,
    new_node_to_our_map: &FxHashMap<NodeIndex, NodeIndex>,
) {
    for (new_node_idx, our_idx) in new_node_to_our_map {
        let mut new_edges = new_data
            .conv_graph
            .neighbors_directed(*new_node_idx, petgraph::Outgoing)
            .detach();
        while let Some((new_edge, new_target)) = new_edges.next(&new_data.conv_graph) {
            let our_target = *new_node_to_our_map
                .get(&new_target)
                .expect("At this step we should have full map new -> our");
            if let Some(existing_edge) = data.conv_graph.find_edge(*our_idx, our_target) {
                info!(
                    "typemap merge: replace {:?} with new conversation rule {:?}, for {} -> {}",
                    data.conv_graph[existing_edge],
                    new_data.conv_graph[new_edge],
                    data.conv_graph[*our_idx],
                    data.conv_graph[our_target],
                );
            }
            data.conv_graph.update_edge(
                *our_idx,
                our_target,
                new_data.conv_graph[new_edge].clone(),
            );
        }
    }
}

fn add_new_ftypes(
    new_data: &TypeMap,
    data: &mut TypeMap,
    new_node_to_our_map: &FxHashMap<NodeIndex, NodeIndex>,
) {
    for (new_ftype, correspoding_rtype) in &new_data.foreign_names_map {
        let our_rtype = new_node_to_our_map
            .get(correspoding_rtype)
            .expect("At this step we should have full map new -> our");
        *data
            .foreign_names_map
            .entry(new_ftype.clone())
            .or_insert(*our_rtype) = *our_rtype;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::typemap::find_conversation_path;
    use proc_macro2::Span;
    use rustc_hash::FxHashSet;
    use syn::{parse_quote, Type};

    #[test]
    fn test_merge() {
        let mut types_map = TypeMap::default();
        types_map
            .merge(
                "base",
                r#"
mod swig_foreign_types_map {
    #![swig_foreigner_type="boolean"]
    #![swig_rust_type="jboolean"]
    #![swig_foreigner_type="int"]
    #![swig_rust_type="jint"]
}
"#,
                64,
            )
            .unwrap();
        types_map
            .merge(
                "test_merge",
                r#"
mod swig_foreign_types_map {
    #![swig_foreigner_type="boolean"]
    #![swig_rust_type="jboolean"]
    #![swig_foreigner_type="int"]
    #![swig_rust_type="jint"]
}

fn helper1() {
}

#[swig_code = "let mut {to_var}: {to_var_type} = {from_var}.swig_into(env);"]
trait SwigInto<T> {
    fn swig_into(self, env: *mut JNIEnv) -> T;
}

#[swig_code = "let mut {to_var}: {to_var_type} = <{to_var_type}>::swig_from({from_var}, env);"]
trait SwigFrom<T> {
    fn swig_from(T, env: *mut JNIEnv) -> Self;
}

impl SwigInto<bool> for jboolean {
    fn swig_into(self, _: *mut JNIEnv) -> bool {
        self != 0
    }
}

fn helper2() {
}

impl SwigFrom<bool> for jboolean {
    fn swig_from(x: bool, _: *mut JNIEnv) -> Self {
        if x { 1 as jboolean } else { 0 as jboolean }
    }
}
impl SwigFrom<i32> for jint {
    fn swig_from(x: i32, _: *mut JNIEnv) -> Self {
        x
    }
}

fn helper3() {
}
"#,
                64,
            )
            .unwrap();
        assert_eq!(
            {
                let mut set = FxHashSet::default();
                for k in types_map.foreign_names_map.keys() {
                    set.insert(k.clone());
                }
                set
            },
            {
                let mut set = FxHashSet::default();
                set.insert("boolean".into());
                set.insert("int".into());
                set
            }
        );
        let ty_i32 = types_map.find_or_alloc_rust_type(&parse_type! { i32 });
        assert_eq!(
            types_map
                .map_through_conversation_to_foreign(
                    &ty_i32,
                    petgraph::Direction::Outgoing,
                    Span::call_site(),
                    |_, fc| fc.constructor_ret_type.clone(),
                )
                .unwrap()
                .name,
            "int"
        );
        assert_eq!(
            "let mut {to_var}: {to_var_type} = {from_var}.swig_into(env);",
            {
                let from = types_map.rust_names_map["jboolean"];
                let to = types_map.rust_names_map["bool"];
                let conv = &types_map.conv_graph[types_map.conv_graph.find_edge(from, to).unwrap()];
                conv.code_template.clone()
            },
        );

        let from = types_map.rust_names_map["jboolean"];
        let to = types_map.rust_names_map["bool"];
        assert_eq!(
            find_conversation_path(&types_map.conv_graph, from, to, Span::call_site()).unwrap(),
            vec![types_map.conv_graph.find_edge(from, to).unwrap()]
        );

        let from = types_map.rust_names_map["bool"];
        let to = types_map.rust_names_map["jboolean"];
        assert_eq!(
            find_conversation_path(&types_map.conv_graph, from, to, Span::call_site()).unwrap(),
            vec![types_map.conv_graph.find_edge(from, to).unwrap()]
        );
        assert_eq!(
            types_map
                .utils_code
                .iter()
                .filter_map(|v| {
                    let item: syn::Item = v.clone();
                    match item {
                        syn::Item::Fn(ref fun) => Some(fun.ident.to_string()),
                        syn::Item::Trait(ref trait_) => Some(trait_.ident.to_string()),
                        _ => None,
                    }
                })
                .collect::<Vec<_>>(),
            vec!["helper1", "SwigInto", "SwigFrom", "helper2", "helper3"]
        );
    }
}
<|endoftext|>