<commit_before>// Copyright 2015 Brendan Zabarauskas and the gl-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.

use registry::Registry;
use std::io;

#[allow(missing_copy_implementations)]
pub struct GlobalGenerator;

impl super::Generator for GlobalGenerator {
    fn write<W>(&self, registry: &Registry, dest: &mut W) -> io::Result<()>
    where
        W: io::Write,
    {
        try!(write_header(dest));
        try!(write_metaloadfn(dest));
        try!(write_type_aliases(registry, dest));
        try!(write_enums(registry, dest));
        try!(write_fns(registry, dest));
        try!(write_fnptr_struct_def(dest));
        try!(write_ptrs(registry, dest));
        try!(write_fn_mods(registry, dest));
        try!(write_panicking_fns(registry, dest));
        try!(write_load_fn(registry, dest));
        Ok(())
    }
}

/// Creates a `__gl_imports` module which contains all the external symbols that we need for the
///  bindings.
fn write_header<W>(dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(
        dest,
        r#"
        mod __gl_imports {{
            pub use std::mem;
            pub use std::os::raw;
        }}
    "#
    )
}

/// Creates the metaloadfn function for fallbacks
fn write_metaloadfn<W>(dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(
        dest,
        r#"
        #[inline(never)]
        fn metaloadfn(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void,
                      symbol: &'static str,
                      fallbacks: &[&'static str]) -> *const __gl_imports::raw::c_void {{
            let mut ptr = loadfn(symbol);
            if ptr.is_null() {{
                for &sym in fallbacks {{
                    ptr = loadfn(sym);
                    if !ptr.is_null() {{ break; }}
                }}
            }}
            ptr
        }}
    "#
    )
}

/// Creates a `types` module which contains all the type aliases.
///
/// See also `generators::gen_types`.
fn write_type_aliases<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    try!(writeln!(
        dest,
        r#"
        pub mod types {{
            #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)]
    "#
    ));

    try!(super::gen_types(registry.api, dest));

    writeln!(
        dest,
        "
        }}
    "
    )
}

/// Creates all the `<enum>` elements at the root of the bindings.
fn write_enums<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    for enm in &registry.enums {
        try!(super::gen_enum_item(enm, "types::", dest));
    }

    Ok(())
}

/// Creates the functions corresponding to the GL commands.
///
/// The function calls the corresponding function pointer stored in the `storage` module created
///  by `write_ptrs`.
fn write_fns<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    for cmd in &registry.cmds {
        if let Some(v) = registry.aliases.get(&cmd.proto.ident) {
            try!(writeln!(dest, "/// Fallbacks: {}", v.join(", ")));
        }

        try!(writeln!(dest,
            "#[allow(non_snake_case, unused_variables, dead_code)] #[inline]
            pub unsafe fn {name}({params}) -> {return_suffix} {{ \
                __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) -> {return_suffix}>\
                    (storage::{name}.f)({idents}) \
            }}",
            name = cmd.proto.ident,
            params = super::gen_parameters(cmd, true, true).join(", "),
            typed_params = super::gen_parameters(cmd, false, true).join(", "),
            return_suffix = cmd.proto.ty,
            idents = super::gen_parameters(cmd, true, false).join(", "),
        ));
    }

    Ok(())
}

/// Creates a `FnPtr` structure which contains the store for a single binding.
fn write_fnptr_struct_def<W>(dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(dest,
             "
        #[allow(missing_copy_implementations)]
        pub struct FnPtr {{
            /// The function pointer that will be used when calling the function.
            f: *const __gl_imports::raw::c_void,
            /// True if the pointer points to a real function, false if points to a `panic!` fn.
            is_loaded: bool,
        }}

        impl FnPtr {{
            /// Creates a `FnPtr` from a load attempt.
            pub fn new(ptr: *const __gl_imports::raw::c_void) -> FnPtr {{
                if ptr.is_null() {{
                    FnPtr {{ f: missing_fn_panic as *const __gl_imports::raw::c_void, is_loaded: false }}
                }} else {{
                    FnPtr {{ f: ptr, is_loaded: true }}
                }}
            }}
        }}
    ")
}

/// Creates a `storage` module which contains a static `FnPtr` per GL command in the registry.
fn write_ptrs<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    try!(writeln!(
        dest,
        "mod storage {{
            #![allow(non_snake_case)]
            #![allow(non_upper_case_globals)]
            use super::__gl_imports::raw;
            use super::FnPtr;"
    ));

    for c in &registry.cmds {
        try!(writeln!(
            dest,
            "pub static mut {name}: FnPtr = FnPtr {{
                f: super::missing_fn_panic as *const raw::c_void,
                is_loaded: false
            }};",
            name = c.proto.ident
        ));
    }

    writeln!(dest, "}}")
}

/// Creates one module for each GL command.
///
/// Each module contains `is_loaded` and `load_with` which interact with the `storage` module
///  created by `write_ptrs`.
fn write_fn_mods<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    for c in &registry.cmds {
        let fallbacks = match registry.aliases.get(&c.proto.ident) {
            Some(v) => {
                let names = v
                    .iter()
                    .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name[..])))
                    .collect::<Vec<_>>();
                format!("&[{}]", names.join(", "))
            },
            None => "&[]".to_string(),
        };
        let fnname = &c.proto.ident[..];
        let symbol = super::gen_symbol_name(registry.api, &c.proto.ident[..]);
        let symbol = &symbol[..];

        try!(writeln!(dest, r##"
            #[allow(non_snake_case)]
            pub mod {fnname} {{
                use super::{{storage, metaloadfn}};
                use super::__gl_imports::raw;
                use super::FnPtr;

                #[inline]
                #[allow(dead_code)]
                pub fn is_loaded() -> bool {{
                    unsafe {{ storage::{fnname}.is_loaded }}
                }}

                #[allow(dead_code)]
                pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const raw::c_void {{
                    unsafe {{
                        storage::{fnname} = FnPtr::new(metaloadfn(&mut loadfn, "{symbol}", {fallbacks}))
                    }}
                }}
            }}
        "##, fnname = fnname, fallbacks = fallbacks, symbol = symbol));
    }

    Ok(())
}

/// Creates a `missing_fn_panic` function.
///
/// This function is the mock that is called if the real function could not be called.
fn write_panicking_fns<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(
        dest,
        "#[inline(never)]
        fn missing_fn_panic() -> ! {{
            panic!(\"{api} function was not loaded\")
        }}
        ",
        api = registry.api
    )
}

/// Creates the `load_with` function.
///
/// The function calls `load_with` in each module created by `write_fn_mods`.
fn write_load_fn<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    try!(writeln!(dest,
                  "
        /// Load each OpenGL symbol using a custom load function. This allows for the
        /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
        /// ~~~ignore
        /// gl::load_with(|s| glfw.get_proc_address(s));
        /// ~~~
        #[allow(dead_code)]
        pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const __gl_imports::raw::c_void {{
    "));

    for c in &registry.cmds {
        try!(writeln!(
            dest,
            "{cmd_name}::load_with(&mut loadfn);",
            cmd_name = &c.proto.ident[..]
        ));
    }

    writeln!(
        dest,
        "
        }}
    "
    )
}
<commit_msg>Prevent inlining of `gl::load_with` to avoid large compile times<commit_after>// Copyright 2015 Brendan Zabarauskas and the gl-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.

use registry::Registry;
use std::io;

#[allow(missing_copy_implementations)]
pub struct GlobalGenerator;

impl super::Generator for GlobalGenerator {
    fn write<W>(&self, registry: &Registry, dest: &mut W) -> io::Result<()>
    where
        W: io::Write,
    {
        try!(write_header(dest));
        try!(write_metaloadfn(dest));
        try!(write_type_aliases(registry, dest));
        try!(write_enums(registry, dest));
        try!(write_fns(registry, dest));
        try!(write_fnptr_struct_def(dest));
        try!(write_ptrs(registry, dest));
        try!(write_fn_mods(registry, dest));
        try!(write_panicking_fns(registry, dest));
        try!(write_load_fn(registry, dest));
        Ok(())
    }
}

/// Creates a `__gl_imports` module which contains all the external symbols that we need for the
///  bindings.
fn write_header<W>(dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(
        dest,
        r#"
        mod __gl_imports {{
            pub use std::mem;
            pub use std::os::raw;
        }}
    "#
    )
}

/// Creates the metaloadfn function for fallbacks
fn write_metaloadfn<W>(dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(
        dest,
        r#"
        #[inline(never)]
        fn metaloadfn(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void,
                      symbol: &'static str,
                      fallbacks: &[&'static str]) -> *const __gl_imports::raw::c_void {{
            let mut ptr = loadfn(symbol);
            if ptr.is_null() {{
                for &sym in fallbacks {{
                    ptr = loadfn(sym);
                    if !ptr.is_null() {{ break; }}
                }}
            }}
            ptr
        }}
    "#
    )
}

/// Creates a `types` module which contains all the type aliases.
///
/// See also `generators::gen_types`.
fn write_type_aliases<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    try!(writeln!(
        dest,
        r#"
        pub mod types {{
            #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)]
    "#
    ));

    try!(super::gen_types(registry.api, dest));

    writeln!(
        dest,
        "
        }}
    "
    )
}

/// Creates all the `<enum>` elements at the root of the bindings.
fn write_enums<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    for enm in &registry.enums {
        try!(super::gen_enum_item(enm, "types::", dest));
    }

    Ok(())
}

/// Creates the functions corresponding to the GL commands.
///
/// The function calls the corresponding function pointer stored in the `storage` module created
///  by `write_ptrs`.
fn write_fns<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    for cmd in &registry.cmds {
        if let Some(v) = registry.aliases.get(&cmd.proto.ident) {
            try!(writeln!(dest, "/// Fallbacks: {}", v.join(", ")));
        }

        try!(writeln!(dest,
            "#[allow(non_snake_case, unused_variables, dead_code)] #[inline]
            pub unsafe fn {name}({params}) -> {return_suffix} {{ \
                __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) -> {return_suffix}>\
                    (storage::{name}.f)({idents}) \
            }}",
            name = cmd.proto.ident,
            params = super::gen_parameters(cmd, true, true).join(", "),
            typed_params = super::gen_parameters(cmd, false, true).join(", "),
            return_suffix = cmd.proto.ty,
            idents = super::gen_parameters(cmd, true, false).join(", "),
        ));
    }

    Ok(())
}

/// Creates a `FnPtr` structure which contains the store for a single binding.
fn write_fnptr_struct_def<W>(dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(dest,
             "
        #[allow(missing_copy_implementations)]
        pub struct FnPtr {{
            /// The function pointer that will be used when calling the function.
            f: *const __gl_imports::raw::c_void,
            /// True if the pointer points to a real function, false if points to a `panic!` fn.
            is_loaded: bool,
        }}

        impl FnPtr {{
            /// Creates a `FnPtr` from a load attempt.
            pub fn new(ptr: *const __gl_imports::raw::c_void) -> FnPtr {{
                if ptr.is_null() {{
                    FnPtr {{ f: missing_fn_panic as *const __gl_imports::raw::c_void, is_loaded: false }}
                }} else {{
                    FnPtr {{ f: ptr, is_loaded: true }}
                }}
            }}
        }}
    ")
}

/// Creates a `storage` module which contains a static `FnPtr` per GL command in the registry.
fn write_ptrs<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    try!(writeln!(
        dest,
        "mod storage {{
            #![allow(non_snake_case)]
            #![allow(non_upper_case_globals)]
            use super::__gl_imports::raw;
            use super::FnPtr;"
    ));

    for c in &registry.cmds {
        try!(writeln!(
            dest,
            "pub static mut {name}: FnPtr = FnPtr {{
                f: super::missing_fn_panic as *const raw::c_void,
                is_loaded: false
            }};",
            name = c.proto.ident
        ));
    }

    writeln!(dest, "}}")
}

/// Creates one module for each GL command.
///
/// Each module contains `is_loaded` and `load_with` which interact with the `storage` module
///  created by `write_ptrs`.
fn write_fn_mods<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    for c in &registry.cmds {
        let fallbacks = match registry.aliases.get(&c.proto.ident) {
            Some(v) => {
                let names = v
                    .iter()
                    .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name[..])))
                    .collect::<Vec<_>>();
                format!("&[{}]", names.join(", "))
            },
            None => "&[]".to_string(),
        };
        let fnname = &c.proto.ident[..];
        let symbol = super::gen_symbol_name(registry.api, &c.proto.ident[..]);
        let symbol = &symbol[..];

        try!(writeln!(dest, r##"
            #[allow(non_snake_case)]
            pub mod {fnname} {{
                use super::{{storage, metaloadfn}};
                use super::__gl_imports::raw;
                use super::FnPtr;

                #[inline]
                #[allow(dead_code)]
                pub fn is_loaded() -> bool {{
                    unsafe {{ storage::{fnname}.is_loaded }}
                }}

                #[allow(dead_code)]
                pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const raw::c_void {{
                    unsafe {{
                        storage::{fnname} = FnPtr::new(metaloadfn(&mut loadfn, "{symbol}", {fallbacks}))
                    }}
                }}
            }}
        "##, fnname = fnname, fallbacks = fallbacks, symbol = symbol));
    }

    Ok(())
}

/// Creates a `missing_fn_panic` function.
///
/// This function is the mock that is called if the real function could not be called.
fn write_panicking_fns<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    writeln!(
        dest,
        "#[inline(never)]
        fn missing_fn_panic() -> ! {{
            panic!(\"{api} function was not loaded\")
        }}
        ",
        api = registry.api
    )
}

/// Creates the `load_with` function.
///
/// The function calls `load_with` in each module created by `write_fn_mods`.
fn write_load_fn<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
where
    W: io::Write,
{
    try!(writeln!(dest,
                  "
        /// Load each OpenGL symbol using a custom load function. This allows for the
        /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
        /// ~~~ignore
        /// gl::load_with(|s| glfw.get_proc_address(s));
        /// ~~~
        #[allow(dead_code)]
        pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const __gl_imports::raw::c_void {{
            #[inline(never)]
            fn inner(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void) {{
    "));

    for c in &registry.cmds {
        try!(writeln!(
            dest,
            "{cmd_name}::load_with(&mut *loadfn);",
            cmd_name = &c.proto.ident[..]
        ));
    }

    writeln!(
        dest,
        "
            }}

            inner(&mut loadfn)
        }}
    "
    )
}
<|endoftext|>


<commit_before>pub struct KeyBuilder {
    key: Vec<u8>,
}

impl KeyBuilder {
    pub fn new() -> KeyBuilder {
        KeyBuilder {
            key: Vec::new(),
        }
    }

    pub fn with_capacity(size: usize) -> KeyBuilder {
        KeyBuilder {
            key: Vec::with_capacity(size),
        }
    }

    pub fn doc_key_mapping(key: &[u8]) -> KeyBuilder {
        let mut kb = KeyBuilder::with_capacity(1 + key.len());
        kb.push_char(b'k');
        kb.push_string(key);
        kb
    }

    pub fn term_dict_mapping(term: &[u8]) -> KeyBuilder {
        let mut kb = KeyBuilder::with_capacity(1 + term.len());
        kb.push_char(b't');
        kb.push_string(term);
        kb
    }

    pub fn chunk_dir_list(chunk: u32, field_ord: u32, term_ord: u32) -> KeyBuilder {
        let mut kb = KeyBuilder::new();
        kb.push_char(b'd');
        kb.push_string(field_ord.to_string().as_bytes());
        kb.separator();
        kb.push_string(term_ord.to_string().as_bytes());
        kb.separator();
        kb.push_string(chunk.to_string().as_bytes());
        kb
    }

    pub fn chunk_stat(chunk: u32, name: &[u8]) -> KeyBuilder {
        let mut kb = KeyBuilder::new();
        kb.push_char(b's');
        kb.push_string(name);
        kb.separator();
        kb.push_string(chunk.to_string().as_bytes());
        kb
    }

    pub fn chunk_del_list(chunk: u32) -> KeyBuilder {
        let mut kb = KeyBuilder::new();
        kb.push_char(b'x');
        kb.push_string(chunk.to_string().as_bytes());
        kb
    }

    #[inline]
    pub fn key(&self) -> &[u8] {
        &self.key[..]
    }

    #[inline]
    pub fn push_char(&mut self, c: u8) {
        if c == b'/' || c == b'\\' {
            self.key.push(b'\\');
        }
        self.key.push(c);
    }

    pub fn push_string(&mut self, s: &[u8]) {
        for c in s {
            self.push_char(*c);
        }
    }

    pub fn separator(&mut self) {
        self.key.push(b'/');
    }
}<commit_msg>Change key format<commit_after>pub struct KeyBuilder {
    key: Vec<u8>,
}

impl KeyBuilder {
    pub fn new() -> KeyBuilder {
        KeyBuilder {
            key: Vec::new(),
        }
    }

    pub fn with_capacity(size: usize) -> KeyBuilder {
        KeyBuilder {
            key: Vec::with_capacity(size),
        }
    }

    pub fn doc_key_mapping(key: &[u8]) -> KeyBuilder {
        let mut kb = KeyBuilder::with_capacity(1 + key.len());
        kb.push_char(b'k');
        kb.push_string(key);
        kb
    }

    pub fn term_dict_mapping(term: &[u8]) -> KeyBuilder {
        let mut kb = KeyBuilder::with_capacity(1 + term.len());
        kb.push_char(b't');
        kb.push_string(term);
        kb
    }

    pub fn chunk_dir_list(chunk: u32, field_ord: u32, term_ord: u32) -> KeyBuilder {
        let mut kb = KeyBuilder::new();
        kb.chunk(chunk);
        kb.push_char(b'd');
        kb.push_string(field_ord.to_string().as_bytes());
        kb.separator();
        kb.push_string(term_ord.to_string().as_bytes());
        kb
    }

    pub fn chunk_stat(chunk: u32, name: &[u8]) -> KeyBuilder {
        let mut kb = KeyBuilder::new();
        kb.chunk(chunk);
        kb.push_char(b's');
        kb.push_string(name);
        kb
    }

    pub fn chunk_del_list(chunk: u32) -> KeyBuilder {
        let mut kb = KeyBuilder::new();
        kb.chunk(chunk);
        kb.push_char(b'x');
        kb
    }

    #[inline]
    pub fn key(&self) -> &[u8] {
        &self.key[..]
    }

    #[inline]
    pub fn push_char(&mut self, c: u8) {
        if c == b'/' || c == b'\\' {
            self.key.push(b'\\');
        }
        self.key.push(c);
    }

    pub fn push_string(&mut self, s: &[u8]) {
        for c in s {
            self.push_char(*c);
        }
    }

    pub fn separator(&mut self) {
        self.key.push(b'/');
    }

    pub fn chunk(&mut self, chunk: u32) {
        self.key.push(b'c');
        self.push_string(chunk.to_string().as_bytes());
    }
}<|endoftext|>


<commit_before>use crate::cell::UnsafeCell;
use crate::mem;
use crate::mem::MaybeUninit;
use crate::sync::atomic::{AtomicU32, Ordering};
use crate::sys::cloudabi::abi;
use crate::sys::rwlock::{self, RWLock};

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

// Implement Mutex using an RWLock. This doesn't introduce any
// performance overhead in this environment, as the operations would be
// implemented identically.
pub struct Mutex(RWLock);

pub type MovableMutex = Mutex;

pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 {
    rwlock::raw(&m.0)
}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex(RWLock::new())
    }

    pub unsafe fn init(&mut self) {
        // This function should normally reinitialize the mutex after
        // moving it to a different memory address. This implementation
        // does not require adjustments after moving.
    }

    pub unsafe fn try_lock(&self) -> bool {
        self.0.try_write()
    }

    pub unsafe fn lock(&self) {
        self.0.write()
    }

    pub unsafe fn unlock(&self) {
        self.0.write_unlock()
    }

    pub unsafe fn destroy(&self) {
        self.0.destroy()
    }
}

pub struct ReentrantMutex {
    lock: UnsafeCell<MaybeUninit<AtomicU32>>,
    recursion: UnsafeCell<MaybeUninit<u32>>,
}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex {
            lock: UnsafeCell::new(MaybeUninit::uninit()),
            recursion: UnsafeCell::new(MaybeUninit::uninit()),
        }
    }

    pub unsafe fn init(&self) {
        *self.lock.get() = MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0));
        *self.recursion.get() = MaybeUninit::new(0);
    }

    pub unsafe fn try_lock(&self) -> bool {
        // Attempt to acquire the lock.
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        if let Err(old) = (*lock).compare_exchange(
            abi::LOCK_UNLOCKED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            Ordering::Acquire,
            Ordering::Relaxed,
        ) {
            // If we fail to acquire the lock, it may be the case
            // that we've already acquired it and may need to recurse.
            if old & !abi::LOCK_KERNEL_MANAGED.0 == __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 {
                *recursion += 1;
                true
            } else {
                false
            }
        } else {
            // Success.
            assert_eq!(*recursion, 0, "Mutex has invalid recursion count");
            true
        }
    }

    pub unsafe fn lock(&self) {
        if !self.try_lock() {
            // Call into the kernel to acquire a write lock.
            let lock = self.lock.get();
            let subscription = abi::subscription {
                type_: abi::eventtype::LOCK_WRLOCK,
                union: abi::subscription_union {
                    lock: abi::subscription_lock {
                        lock: lock as *mut abi::lock,
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            };
            let mut event = MaybeUninit::<abi::event>::uninit();
            let mut nevents = MaybeUninit::<usize>::uninit();
            let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex");
            let event = event.assume_init();
            assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex");
        }
    }

    pub unsafe fn unlock(&self) {
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        assert_eq!(
            (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This mutex is locked by a different thread"
        );

        if *recursion > 0 {
            *recursion -= 1;
        } else if !(*lock)
            .compare_exchange(
                __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
                abi::LOCK_UNLOCKED.0,
                Ordering::Release,
                Ordering::Relaxed,
            )
            .is_ok()
        {
            // Lock is managed by kernelspace. Call into the kernel
            // to unblock waiting threads.
            let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex");
        }
    }

    pub unsafe fn destroy(&self) {
        let lock = (*self.lock.get()).as_mut_ptr();
        let recursion = (*self.recursion.get()).as_mut_ptr();
        assert_eq!(
            (*lock).load(Ordering::Relaxed),
            abi::LOCK_UNLOCKED.0,
            "Attempted to destroy locked mutex"
        );
        assert_eq!(*recursion, 0, "Recursion counter invalid");
    }
}
<commit_msg>Get rid of UnsafeCell<MaybeUninit>s in cloudabi mutex.<commit_after>use crate::cell::Cell;
use crate::mem;
use crate::mem::MaybeUninit;
use crate::sync::atomic::{AtomicU32, Ordering};
use crate::sys::cloudabi::abi;
use crate::sys::rwlock::{self, RWLock};

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

// Implement Mutex using an RWLock. This doesn't introduce any
// performance overhead in this environment, as the operations would be
// implemented identically.
pub struct Mutex(RWLock);

pub type MovableMutex = Mutex;

pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 {
    rwlock::raw(&m.0)
}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex(RWLock::new())
    }

    pub unsafe fn init(&mut self) {
        // This function should normally reinitialize the mutex after
        // moving it to a different memory address. This implementation
        // does not require adjustments after moving.
    }

    pub unsafe fn try_lock(&self) -> bool {
        self.0.try_write()
    }

    pub unsafe fn lock(&self) {
        self.0.write()
    }

    pub unsafe fn unlock(&self) {
        self.0.write_unlock()
    }

    pub unsafe fn destroy(&self) {
        self.0.destroy()
    }
}

pub struct ReentrantMutex {
    lock: AtomicU32,
    recursion: Cell<u32>,
}

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { lock: AtomicU32::new(abi::LOCK_UNLOCKED.0), recursion: Cell::new(0) }
    }

    pub unsafe fn init(&self) {}

    pub unsafe fn try_lock(&self) -> bool {
        // Attempt to acquire the lock.
        if let Err(old) = self.lock.compare_exchange(
            abi::LOCK_UNLOCKED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            Ordering::Acquire,
            Ordering::Relaxed,
        ) {
            // If we fail to acquire the lock, it may be the case
            // that we've already acquired it and may need to recurse.
            if old & !abi::LOCK_KERNEL_MANAGED.0 == __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 {
                self.recursion.set(self.recursion.get() + 1);
                true
            } else {
                false
            }
        } else {
            // Success.
            assert_eq!(self.recursion.get(), 0, "Mutex has invalid recursion count");
            true
        }
    }

    pub unsafe fn lock(&self) {
        if !self.try_lock() {
            // Call into the kernel to acquire a write lock.
            let lock = &self.lock as *const AtomicU32;
            let subscription = abi::subscription {
                type_: abi::eventtype::LOCK_WRLOCK,
                union: abi::subscription_union {
                    lock: abi::subscription_lock {
                        lock: lock as *mut abi::lock,
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            };
            let mut event = MaybeUninit::<abi::event>::uninit();
            let mut nevents = MaybeUninit::<usize>::uninit();
            let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex");
            let event = event.assume_init();
            assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex");
        }
    }

    pub unsafe fn unlock(&self) {
        assert_eq!(
            self.lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This mutex is locked by a different thread"
        );

        let r = self.recursion.get();
        if r > 0 {
            self.recursion.set(r - 1);
        } else if !self
            .lock
            .compare_exchange(
                __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
                abi::LOCK_UNLOCKED.0,
                Ordering::Release,
                Ordering::Relaxed,
            )
            .is_ok()
        {
            // Lock is managed by kernelspace. Call into the kernel
            // to unblock waiting threads.
            let ret = abi::lock_unlock(
                &self.lock as *const AtomicU32 as *mut abi::lock,
                abi::scope::PRIVATE,
            );
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex");
        }
    }

    pub unsafe fn destroy(&self) {
        assert_eq!(
            self.lock.load(Ordering::Relaxed),
            abi::LOCK_UNLOCKED.0,
            "Attempted to destroy locked mutex"
        );
        assert_eq!(self.recursion.get(), 0, "Recursion counter invalid");
    }
}
<|endoftext|>


<commit_before>use fall_tree::{NodeType, INode};
use lex_engine::Token;
use super::{BlackNode, BlackIdx};

pub fn into_white(
    node: BlackNode,
    tokens: &[Token],
    whitespace_binder: fn(ty: NodeType, adjacent_spaces: &[Token], leading: bool) -> usize
) -> WhiteNode {
    let file_ty = match node {
        BlackNode::Composite { ty: Some(ty), .. } => ty,
        _ => panic!("Bad file node")
    };
    let binder = |ty: Option<NodeType>, adjacent_spaces: &[Token], leading: bool| -> usize {
        match ty {
            None => 0,
            Some(ty) if ty == file_ty => adjacent_spaces.len(),
            Some(ty) => whitespace_binder(ty, adjacent_spaces, leading)
        }
    };

    black_to_white(node, tokens, (0, tokens.len()), &binder)
}

#[derive(Debug)]
pub struct WhiteNode {
    ty: Option<NodeType>,
    token_range: (usize, usize),
    children: Vec<WhiteNode>,
}

impl WhiteNode {
    pub fn new(
        ty: Option<NodeType>,
        token_range: (usize, usize),
        children: Vec<WhiteNode>,
    ) -> WhiteNode {
        if !children.is_empty() {
            let first = children.first().unwrap();
            let last = children.last().unwrap();
            assert_eq!(token_range, (first.token_range.0, last.token_range.1),
                       "child ranges: {:?}", children.iter().map(|n| n.token_range).collect::<Vec<_>>());
        }

        WhiteNode { ty, token_range, children }
    }

    pub fn leaf(ty: Option<NodeType>, token_idx: usize) -> WhiteNode {
        WhiteNode::new(ty, (token_idx, token_idx + 1), Vec::new())
    }

    pub fn into_inode(self, tokens: &[Token]) -> INode {
        let ty = self.ty.unwrap();
        assert_eq!(self.token_range, (0, tokens.len()));
        let mut result = INode::new(ty);
        self.inject_into(&mut result, tokens);
        result
    }

    fn inject_into(self, parent: &mut INode, tokens: &[Token]) {
        if self.children.is_empty() {
            let len = tokens[self.token_range.0..self.token_range.1]
                .iter()
                .map(|t| t.len)
                .sum();
            parent.push_token_part(len);
            return;
        }

        for child in self.children {
            if let Some(ty) = child.ty {
                let mut new_node = INode::new(ty);
                child.inject_into(&mut new_node, tokens);
                parent.push_child(new_node);
            } else {
                child.inject_into(parent, tokens);
            }
        }
    }
}

fn black_to_white(
    black: BlackNode,
    tokens: &[Token],
    cover_range: (usize, usize),
    whitespace_binder: &Fn(Option<NodeType>, &[Token], bool) -> usize
) -> WhiteNode {
    if let Some((BlackIdx(l), BlackIdx(r))) = black.token_range() {
        assert!(cover_range.0 <= l && r <= cover_range.1);
    }

    match black {
        BlackNode::Leaf { ty, token_idx: BlackIdx(token_idx) } =>
            return WhiteNode::leaf(ty, token_idx),
        BlackNode::Composite { ty, mut children } => {
            let mut internal_children = Vec::new();
            children.reverse();

            let mut left_edge = cover_range.0;
            let mut first_child = true;
            while let Some(child) = children.pop() {
                let right_edge = children.last()
                    .and_then(|n| n.token_range())
                    .map(|(_, BlackIdx(i))| i)
                    .unwrap_or(cover_range.1);
                let child = black_to_white(child, tokens, (left_edge, right_edge), whitespace_binder);

                if !first_child {
                    for i in left_edge..child.token_range.0 {
                        internal_children.push(WhiteNode::leaf(Some(tokens[i].ty), i))
                    }
                }
                first_child = false;
                left_edge = child.token_range.1;
                internal_children.push(child);
            }

            let internal_start = internal_children
                .first()
                .map(|n| n.token_range.0)
                .unwrap_or(cover_range.0);
            let internal_end = internal_children
                .last()
                .map(|n| n.token_range.1)
                .unwrap_or(cover_range.0);

            let external_start = internal_start
                - whitespace_binder(ty, &tokens[cover_range.0..internal_start], true);

            let external_end = internal_end
                + whitespace_binder(ty, &tokens[internal_end..cover_range.1], false);

            let mut children = Vec::with_capacity(internal_children.len());
            for i in external_start..internal_start {
                children.push(WhiteNode::leaf(Some(tokens[i].ty), i))
            }
            children.extend(internal_children);
            for i in internal_end..external_end {
                children.push(WhiteNode::leaf(Some(tokens[i].ty), i))
            }
            WhiteNode::new(ty, (external_start, external_end), children)
        }
    }
}
<commit_msg>Refactoring<commit_after>use fall_tree::{NodeType, INode};
use lex_engine::Token;
use super::{BlackNode, BlackIdx};

pub fn into_white(
    node: BlackNode,
    tokens: &[Token],
    whitespace_binder: fn(ty: NodeType, adjacent_spaces: &[Token], leading: bool) -> usize
) -> WhiteNode {
    let file_ty = match node {
        BlackNode::Composite { ty: Some(ty), .. } => ty,
        _ => panic!("Bad file node")
    };
    let binder = |ty: Option<NodeType>, adjacent_spaces: &[Token], leading: bool| -> usize {
        match ty {
            None => 0,
            Some(ty) if ty == file_ty => adjacent_spaces.len(),
            Some(ty) => whitespace_binder(ty, adjacent_spaces, leading)
        }
    };

    Whitespacer {
        tokens: tokens,
        whitespace_binder: &binder,
    }.black_to_white(node, (0, tokens.len()))
}

#[derive(Debug)]
pub struct WhiteNode {
    ty: Option<NodeType>,
    token_range: (usize, usize),
    children: Vec<WhiteNode>,
}

impl WhiteNode {
    pub fn new(
        ty: Option<NodeType>,
        token_range: (usize, usize),
        children: Vec<WhiteNode>,
    ) -> WhiteNode {
        if !children.is_empty() {
            let first = children.first().unwrap();
            let last = children.last().unwrap();
            assert_eq!(token_range, (first.token_range.0, last.token_range.1),
                       "child ranges: {:?}", children.iter().map(|n| n.token_range).collect::<Vec<_>>());
        }

        WhiteNode { ty, token_range, children }
    }

    pub fn into_inode(self, tokens: &[Token]) -> INode {
        let ty = self.ty.unwrap();
        assert_eq!(self.token_range, (0, tokens.len()));
        let mut result = INode::new(ty);
        self.inject_into(&mut result, tokens);
        result
    }

    fn inject_into(self, parent: &mut INode, tokens: &[Token]) {
        if self.children.is_empty() {
            let len = tokens[self.token_range.0..self.token_range.1]
                .iter()
                .map(|t| t.len)
                .sum();
            parent.push_token_part(len);
            return;
        }

        for child in self.children {
            if let Some(ty) = child.ty {
                let mut new_node = INode::new(ty);
                child.inject_into(&mut new_node, tokens);
                parent.push_child(new_node);
            } else {
                child.inject_into(parent, tokens);
            }
        }
    }
}

struct Whitespacer<'a> {
    tokens: &'a [Token],
    whitespace_binder: &'a Fn(Option<NodeType>, &[Token], bool) -> usize,
}

impl<'a> Whitespacer<'a> {
    fn black_to_white(&self, black: BlackNode, cover_range: (usize, usize)) -> WhiteNode {
        if let Some((BlackIdx(l), BlackIdx(r))) = black.token_range() {
            assert!(cover_range.0 <= l && r <= cover_range.1);
        }

        match black {
            BlackNode::Leaf { ty, token_idx: BlackIdx(token_idx) } =>
                return WhiteNode {
                    ty: ty,
                    token_range: (token_idx, token_idx + 1),
                    children: Vec::new(),
                },
            BlackNode::Composite { ty, mut children } => {
                let mut internal_children = Vec::new();
                children.reverse();

                let mut left_edge = cover_range.0;
                let mut first_child = true;
                while let Some(child) = children.pop() {
                    let right_edge = children.last()
                        .and_then(|n| n.token_range())
                        .map(|(_, BlackIdx(i))| i)
                        .unwrap_or(cover_range.1);
                    let child = self.black_to_white(child, (left_edge, right_edge));

                    if !first_child {
                        for i in left_edge..child.token_range.0 {
                            internal_children.push(self.leaf(i))
                        }
                    }
                    first_child = false;
                    left_edge = child.token_range.1;
                    internal_children.push(child);
                }

                let internal_start = internal_children
                    .first()
                    .map(|n| n.token_range.0)
                    .unwrap_or(cover_range.0);
                let internal_end = internal_children
                    .last()
                    .map(|n| n.token_range.1)
                    .unwrap_or(cover_range.0);

                let external_start = internal_start
                    - (self.whitespace_binder)(ty, &self.tokens[cover_range.0..internal_start], true);

                let external_end = internal_end
                    + (self.whitespace_binder)(ty, &self.tokens[internal_end..cover_range.1], false);

                let mut children = Vec::with_capacity(internal_children.len());
                for i in external_start..internal_start {
                    children.push(self.leaf(i))
                }
                children.extend(internal_children);
                for i in internal_end..external_end {
                    children.push(self.leaf(i))
                }
                WhiteNode::new(ty, (external_start, external_end), children)
            }
        }
    }

    fn leaf(&self, token_idx: usize) -> WhiteNode {
        let ty = self.tokens[token_idx].ty;
        WhiteNode::new(Some(ty), (token_idx, token_idx + 1), Vec::new())
    }
}
<|endoftext|>


<commit_before>// ignore-windows: Concurrency on Windows is not supported yet.
// compile-flags: -Zmiri-disable-isolation

use std::thread::{spawn, sleep};
use std::ptr::null_mut;
use std::sync::atomic::{Ordering, AtomicPtr};
use std::time::Duration;

#[derive(Copy, Clone)]
struct EvilSend<T>(pub T);

unsafe impl<T> Send for EvilSend<T> {}
unsafe impl<T> Sync for EvilSend<T> {}

pub fn main() {
    // Shared atomic pointer
    let pointer = AtomicPtr::new(null_mut::<usize>());
    let ptr = EvilSend(&pointer as *const AtomicPtr<usize>);

    // Note: this is scheduler-dependent
    // the operations need to occur in
    // order, otherwise the allocation is
    // not visible to the other-thread to
    // detect the race:
    //  1. stack-allocate
    //  2. atomic_store
    //  3. atomic_load
    //  4. write-value
    //  5. read-value
    unsafe {
        let j1 = spawn(move || {
            // Concurrent allocate the memory.
            // Uses relaxed semantics to not generate
            // a release sequence.
            let pointer = &*ptr.0;

            let mut stack_var = 0usize;

            pointer.store(&mut stack_var as *mut _, Ordering::Release);

            sleep(Duration::from_millis(1000));

            // Read, the add 1 fixes -Z mir-opt-level=3 from removing the read via dest-prop and breaking
            // the test.
            stack_var + 1 //~ ERROR Data race detected between Read on Thread(id = 1) and Write on Thread(id = 2)
        });

        let j2 = spawn(move || {
            let pointer = &*ptr.0;
            *pointer.load(Ordering::Acquire) = 3;
        });

        j1.join().unwrap();
        j2.join().unwrap();
    }
}
<commit_msg>Change to disable mir-opt in compile-flags<commit_after>// ignore-windows: Concurrency on Windows is not supported yet.
// compile-flags: -Zmiri-disable-isolation -Zmir-opt-level=0

use std::thread::{spawn, sleep};
use std::ptr::null_mut;
use std::sync::atomic::{Ordering, AtomicPtr};
use std::time::Duration;

#[derive(Copy, Clone)]
struct EvilSend<T>(pub T);

unsafe impl<T> Send for EvilSend<T> {}
unsafe impl<T> Sync for EvilSend<T> {}

pub fn main() {
    // Shared atomic pointer
    let pointer = AtomicPtr::new(null_mut::<usize>());
    let ptr = EvilSend(&pointer as *const AtomicPtr<usize>);

    // Note: this is scheduler-dependent
    // the operations need to occur in
    // order, otherwise the allocation is
    // not visible to the other-thread to
    // detect the race:
    //  1. stack-allocate
    //  2. atomic_store
    //  3. atomic_load
    //  4. write-value
    //  5. read-value
    unsafe {
        let j1 = spawn(move || {
            // Concurrent allocate the memory.
            // Uses relaxed semantics to not generate
            // a release sequence.
            let pointer = &*ptr.0;

            let mut stack_var = 0usize;

            pointer.store(&mut stack_var as *mut _, Ordering::Release);

            sleep(Duration::from_millis(1000));

            stack_var //~ ERROR Data race detected between Read on Thread(id = 1) and Write on Thread(id = 2)
        });

        let j2 = spawn(move || {
            let pointer = &*ptr.0;
            *pointer.load(Ordering::Acquire) = 3;
        });

        j1.join().unwrap();
        j2.join().unwrap();
    }
}
<|endoftext|>


<commit_before>use super::macros::*;
use super::*;
use crate::prelude::*;

use crate::ast_converters::*;
use crate::guid::GUID;
use crate::idents::{self, SomeIdent};
use crate::methodinfo::ComMethodInfo;
use crate::quote::ToTokens;
use crate::tyhandlers::ModelTypeSystem;
use indexmap::IndexMap;
use proc_macro2::Span;
use std::iter::FromIterator;
use syn::{spanned::Spanned, Ident, LitStr, Path, TypePath, Visibility};

intercom_attribute!(
    ComInterfaceAttr< ComInterfaceAttrParam, NoParams > {
        com_iid : LitStr,
        raw_iid : LitStr,
        base : Path,
        vtable_of: Path,
        implemented_by: Path,
    }
);

impl ComInterfaceAttr
{
    pub fn iid(&self, ts: ModelTypeSystem) -> Result<Option<&LitStr>, String>
    {
        match ts {
            ModelTypeSystem::Raw => self.raw_iid(),
            ModelTypeSystem::Automation => self.com_iid(),
        }
    }
}

#[derive(Debug)]
pub struct ComInterface
{
    pub path: Path,
    pub ident: Ident,
    pub visibility: Visibility,
    pub base_interface: Option<Path>,
    pub variants: IndexMap<ModelTypeSystem, ComInterfaceVariant>,
    pub item_type: crate::utils::InterfaceType,
    pub span: Span,
    pub is_unsafe: bool,
    pub itf_ref: TokenStream,
    pub vtable_of: Option<Path>,
    pub implemented_by: Option<Path>,
}

#[derive(Debug, PartialEq)]
pub struct ComInterfaceVariant
{
    pub type_system: ModelTypeSystem,
    pub iid: GUID,
    pub methods: Vec<ComMethodInfo>,
}

impl ComInterface
{
    /// Creates ComInterface from AST elements.
    pub fn from_ast(
        crate_name: &str,
        attr: TokenStream,
        item: TokenStream,
    ) -> ParseResult<ComInterface>
    {
        let item: syn::Item = ::syn::parse2(item).map_err(|_| {
            ParseError::ComInterface("<Unknown>".into(), "Item syntax error".into())
        })?;

        let attr: ComInterfaceAttr = ::syn::parse2(attr).map_err(|_| {
            ParseError::ComInterface(
                item.get_ident().unwrap().to_string(),
                "Attribute syntax error".into(),
            )
        })?;

        // Get the interface details. As [com_interface] can be applied to both
        // impls and traits this handles both of those.
        let (path, fns, itf_type, unsafety) =
            crate::utils::get_ident_and_fns(&item).ok_or_else(|| {
                ParseError::ComInterface(
                    item.get_ident().unwrap().to_string(),
                    "Unsupported associated item".into(),
                )
            })?;

        let ident = path.get_some_ident().ok_or_else(|| {
            ParseError::ComInterface(
                path.to_token_stream().to_string(),
                "Could not resolve ident for".to_string(),
            )
        })?;

        // The second argument is the optional base class. If there's no base
        // class defined, use IUnknown as the default. The value of NO_BASE will
        // construct an interface that has no base class.
        //
        // In practice the NO_BASE should be used ONLY for the IUnknown itself.
        let base = attr
            .base()
            .map_err(|msg| ParseError::ComInterface(item.get_ident().unwrap().to_string(), msg))?;
        let base = match base {
            Some(b) => {
                if b.get_ident().map(|i| i == "NO_BASE") == Some(true) {
                    None
                } else {
                    Some(b.to_owned())
                }
            }
            None => Some(syn::parse2(quote!(intercom::IUnknown)).unwrap()),
        };

        // Visibility for trait interfaces is the visibility of the trait.
        //
        // For implicit interfaces (impl Struct) the visibility is always public.
        // These interfaces should only exist for COM types that are meant to be
        // called from external sources as they can't be impl'd for random ComItf.
        //
        // Note this may conflict with visibility of the actual [com_class], but
        // nothing we can do for this really.
        let visibility = if let ::syn::Item::Trait(ref t) = item {
            t.vis.clone()
        } else {
            parse_quote!(pub)
        };

        let variants = IndexMap::from_iter(
            [ModelTypeSystem::Automation, ModelTypeSystem::Raw]
                .iter()
                .map(|&ts| {
                    let iid_attr = attr.iid(ts).map_err(|msg| {
                        ParseError::ComInterface(item.get_ident().unwrap().to_string(), msg)
                    })?;
                    let iid = match iid_attr {
                        Some(iid) => GUID::parse(&iid.value()).map_err(|_| {
                            ParseError::ComInterface(
                                item.get_ident().unwrap().to_string(),
                                "Bad IID format".into(),
                            )
                        })?,
                        None => crate::utils::generate_iid(crate_name, &ident.to_string(), ts),
                    };

                    // Read the method details.
                    //
                    // TODO: Currently we ignore invalid methods. We should probably do
                    //       something smarter.
                    let methods = fns
                        .iter()
                        .map(|sig| ComMethodInfo::new(sig, ts))
                        .filter_map(Result::ok)
                        .collect::<Vec<_>>();

                    Ok((
                        ts,
                        ComInterfaceVariant {
                            type_system: ts,
                            iid,
                            methods,
                        },
                    ))
                })
                .collect::<Result<Vec<_>, _>>()?,
        );

        let itf_ref = match itf_type {
            crate::utils::InterfaceType::Trait => quote_spanned!(ident.span() => dyn #path),
            crate::utils::InterfaceType::Struct => quote_spanned!(ident.span() => #path),
        };

        Ok(ComInterface {
            base_interface: base,
            item_type: itf_type,
            is_unsafe: unsafety.is_some(),
            span: ident.span(),
            vtable_of: attr
                .vtable_of()
                .map_err(|e| ParseError::ComInterface(ident.to_string(), e))?
                .cloned(),
            implemented_by: attr
                .implemented_by()
                .map_err(|e| ParseError::ComInterface(ident.to_string(), e))?
                .cloned(),
            path,
            ident,
            visibility,
            variants,
            itf_ref,
        })
    }

    pub fn vtable(&self, ts: ModelTypeSystem) -> TypePath
    {
        let ts_type_tokens = ts.as_typesystem_type(self.ident.span());
        let tt = match &self.vtable_of {
            Some(path) => {
                quote_spanned!(self.ident.span() => <dyn #path as intercom::attributes::ComInterfaceVariant<#ts_type_tokens>>::VTable)
            }
            None => {
                let ident = idents::vtable(&self.ident, ts);
                quote!(#ident)
            }
        };
        syn::parse2(tt).unwrap()
    }
}

#[cfg(test)]
mod test
{
    use super::*;
    use crate::tyhandlers::ModelTypeSystem::*;

    #[test]
    fn parse_com_interface()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(
                com_iid = "12345678-1234-1234-1234-567890ABCDEF",
                raw_iid = "12345678-1234-1234-1234-567890FEDCBA",
            ),
            quote!(
                trait ITrait
                {
                    fn foo(&self);
                    fn bar(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(ITrait));
        assert_eq!(itf.visibility, Visibility::Inherited);
        assert_eq!(
            itf.base_interface.as_ref().unwrap(),
            &parse_quote!(intercom::IUnknown)
        );

        let variant = &itf.variants[&Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("12345678-1234-1234-1234-567890ABCDEF").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "foo");
        assert_eq!(variant.methods[1].name, "bar");

        let variant = &itf.variants[&Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("12345678-1234-1234-1234-567890FEDCBA").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "foo");
        assert_eq!(variant.methods[1].name, "bar");
    }

    #[test]
    fn parse_com_interface_with_auto_guid()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(),
            quote!(
                pub trait IAutoGuid
                {
                    fn one(&self);
                    fn two(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(IAutoGuid));

        let pub_visibility: Visibility = parse_quote!(pub);
        assert_eq!(itf.visibility, pub_visibility);
        assert_eq!(
            itf.base_interface.as_ref().unwrap(),
            &parse_quote!(intercom::IUnknown)
        );

        let variant = &itf.variants[&Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");

        let variant = &itf.variants[&Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");
    }

    #[test]
    fn parse_com_interface_with_base_interface()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(base = IBase),
            quote!(
                pub trait IAutoGuid
                {
                    fn one(&self);
                    fn two(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(IAutoGuid));

        let pub_visibility: Visibility = parse_quote!(pub);
        assert_eq!(itf.visibility, pub_visibility);
        assert_eq!(itf.base_interface.as_ref().unwrap(), &parse_quote!(IBase));

        let variant = &itf.variants[&ModelTypeSystem::Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");

        let variant = &itf.variants[&ModelTypeSystem::Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");
    }

    #[test]
    fn parse_com_interface_with_no_base_interface()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(base = NO_BASE),
            quote!(
                pub trait IAutoGuid
                {
                    fn one(&self);
                    fn two(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(IAutoGuid));

        let pub_visibility: Visibility = parse_quote!(pub);
        assert_eq!(itf.visibility, pub_visibility);
        assert_eq!(itf.base_interface, None);

        let variant = &itf.variants[&Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");

        let variant = &itf.variants[&Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");
    }
}
<commit_msg>Remove unused import<commit_after>use super::macros::*;
use super::*;
use crate::prelude::*;

use crate::ast_converters::*;
use crate::guid::GUID;
use crate::idents::{self, SomeIdent};
use crate::methodinfo::ComMethodInfo;
use crate::quote::ToTokens;
use crate::tyhandlers::ModelTypeSystem;
use indexmap::IndexMap;
use proc_macro2::Span;
use std::iter::FromIterator;
use syn::{Ident, LitStr, Path, TypePath, Visibility};

intercom_attribute!(
    ComInterfaceAttr< ComInterfaceAttrParam, NoParams > {
        com_iid : LitStr,
        raw_iid : LitStr,
        base : Path,
        vtable_of: Path,
        implemented_by: Path,
    }
);

impl ComInterfaceAttr
{
    pub fn iid(&self, ts: ModelTypeSystem) -> Result<Option<&LitStr>, String>
    {
        match ts {
            ModelTypeSystem::Raw => self.raw_iid(),
            ModelTypeSystem::Automation => self.com_iid(),
        }
    }
}

#[derive(Debug)]
pub struct ComInterface
{
    pub path: Path,
    pub ident: Ident,
    pub visibility: Visibility,
    pub base_interface: Option<Path>,
    pub variants: IndexMap<ModelTypeSystem, ComInterfaceVariant>,
    pub item_type: crate::utils::InterfaceType,
    pub span: Span,
    pub is_unsafe: bool,
    pub itf_ref: TokenStream,
    pub vtable_of: Option<Path>,
    pub implemented_by: Option<Path>,
}

#[derive(Debug, PartialEq)]
pub struct ComInterfaceVariant
{
    pub type_system: ModelTypeSystem,
    pub iid: GUID,
    pub methods: Vec<ComMethodInfo>,
}

impl ComInterface
{
    /// Creates ComInterface from AST elements.
    pub fn from_ast(
        crate_name: &str,
        attr: TokenStream,
        item: TokenStream,
    ) -> ParseResult<ComInterface>
    {
        let item: syn::Item = ::syn::parse2(item).map_err(|_| {
            ParseError::ComInterface("<Unknown>".into(), "Item syntax error".into())
        })?;

        let attr: ComInterfaceAttr = ::syn::parse2(attr).map_err(|_| {
            ParseError::ComInterface(
                item.get_ident().unwrap().to_string(),
                "Attribute syntax error".into(),
            )
        })?;

        // Get the interface details. As [com_interface] can be applied to both
        // impls and traits this handles both of those.
        let (path, fns, itf_type, unsafety) =
            crate::utils::get_ident_and_fns(&item).ok_or_else(|| {
                ParseError::ComInterface(
                    item.get_ident().unwrap().to_string(),
                    "Unsupported associated item".into(),
                )
            })?;

        let ident = path.get_some_ident().ok_or_else(|| {
            ParseError::ComInterface(
                path.to_token_stream().to_string(),
                "Could not resolve ident for".to_string(),
            )
        })?;

        // The second argument is the optional base class. If there's no base
        // class defined, use IUnknown as the default. The value of NO_BASE will
        // construct an interface that has no base class.
        //
        // In practice the NO_BASE should be used ONLY for the IUnknown itself.
        let base = attr
            .base()
            .map_err(|msg| ParseError::ComInterface(item.get_ident().unwrap().to_string(), msg))?;
        let base = match base {
            Some(b) => {
                if b.get_ident().map(|i| i == "NO_BASE") == Some(true) {
                    None
                } else {
                    Some(b.to_owned())
                }
            }
            None => Some(syn::parse2(quote!(intercom::IUnknown)).unwrap()),
        };

        // Visibility for trait interfaces is the visibility of the trait.
        //
        // For implicit interfaces (impl Struct) the visibility is always public.
        // These interfaces should only exist for COM types that are meant to be
        // called from external sources as they can't be impl'd for random ComItf.
        //
        // Note this may conflict with visibility of the actual [com_class], but
        // nothing we can do for this really.
        let visibility = if let ::syn::Item::Trait(ref t) = item {
            t.vis.clone()
        } else {
            parse_quote!(pub)
        };

        let variants = IndexMap::from_iter(
            [ModelTypeSystem::Automation, ModelTypeSystem::Raw]
                .iter()
                .map(|&ts| {
                    let iid_attr = attr.iid(ts).map_err(|msg| {
                        ParseError::ComInterface(item.get_ident().unwrap().to_string(), msg)
                    })?;
                    let iid = match iid_attr {
                        Some(iid) => GUID::parse(&iid.value()).map_err(|_| {
                            ParseError::ComInterface(
                                item.get_ident().unwrap().to_string(),
                                "Bad IID format".into(),
                            )
                        })?,
                        None => crate::utils::generate_iid(crate_name, &ident.to_string(), ts),
                    };

                    // Read the method details.
                    //
                    // TODO: Currently we ignore invalid methods. We should probably do
                    //       something smarter.
                    let methods = fns
                        .iter()
                        .map(|sig| ComMethodInfo::new(sig, ts))
                        .filter_map(Result::ok)
                        .collect::<Vec<_>>();

                    Ok((
                        ts,
                        ComInterfaceVariant {
                            type_system: ts,
                            iid,
                            methods,
                        },
                    ))
                })
                .collect::<Result<Vec<_>, _>>()?,
        );

        let itf_ref = match itf_type {
            crate::utils::InterfaceType::Trait => quote_spanned!(ident.span() => dyn #path),
            crate::utils::InterfaceType::Struct => quote_spanned!(ident.span() => #path),
        };

        Ok(ComInterface {
            base_interface: base,
            item_type: itf_type,
            is_unsafe: unsafety.is_some(),
            span: ident.span(),
            vtable_of: attr
                .vtable_of()
                .map_err(|e| ParseError::ComInterface(ident.to_string(), e))?
                .cloned(),
            implemented_by: attr
                .implemented_by()
                .map_err(|e| ParseError::ComInterface(ident.to_string(), e))?
                .cloned(),
            path,
            ident,
            visibility,
            variants,
            itf_ref,
        })
    }

    pub fn vtable(&self, ts: ModelTypeSystem) -> TypePath
    {
        let ts_type_tokens = ts.as_typesystem_type(self.ident.span());
        let tt = match &self.vtable_of {
            Some(path) => {
                quote_spanned!(self.ident.span() => <dyn #path as intercom::attributes::ComInterfaceVariant<#ts_type_tokens>>::VTable)
            }
            None => {
                let ident = idents::vtable(&self.ident, ts);
                quote!(#ident)
            }
        };
        syn::parse2(tt).unwrap()
    }
}

#[cfg(test)]
mod test
{
    use super::*;
    use crate::tyhandlers::ModelTypeSystem::*;

    #[test]
    fn parse_com_interface()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(
                com_iid = "12345678-1234-1234-1234-567890ABCDEF",
                raw_iid = "12345678-1234-1234-1234-567890FEDCBA",
            ),
            quote!(
                trait ITrait
                {
                    fn foo(&self);
                    fn bar(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(ITrait));
        assert_eq!(itf.visibility, Visibility::Inherited);
        assert_eq!(
            itf.base_interface.as_ref().unwrap(),
            &parse_quote!(intercom::IUnknown)
        );

        let variant = &itf.variants[&Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("12345678-1234-1234-1234-567890ABCDEF").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "foo");
        assert_eq!(variant.methods[1].name, "bar");

        let variant = &itf.variants[&Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("12345678-1234-1234-1234-567890FEDCBA").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "foo");
        assert_eq!(variant.methods[1].name, "bar");
    }

    #[test]
    fn parse_com_interface_with_auto_guid()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(),
            quote!(
                pub trait IAutoGuid
                {
                    fn one(&self);
                    fn two(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(IAutoGuid));

        let pub_visibility: Visibility = parse_quote!(pub);
        assert_eq!(itf.visibility, pub_visibility);
        assert_eq!(
            itf.base_interface.as_ref().unwrap(),
            &parse_quote!(intercom::IUnknown)
        );

        let variant = &itf.variants[&Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");

        let variant = &itf.variants[&Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");
    }

    #[test]
    fn parse_com_interface_with_base_interface()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(base = IBase),
            quote!(
                pub trait IAutoGuid
                {
                    fn one(&self);
                    fn two(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(IAutoGuid));

        let pub_visibility: Visibility = parse_quote!(pub);
        assert_eq!(itf.visibility, pub_visibility);
        assert_eq!(itf.base_interface.as_ref().unwrap(), &parse_quote!(IBase));

        let variant = &itf.variants[&ModelTypeSystem::Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");

        let variant = &itf.variants[&ModelTypeSystem::Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");
    }

    #[test]
    fn parse_com_interface_with_no_base_interface()
    {
        let itf = ComInterface::from_ast(
            "not used",
            quote!(base = NO_BASE),
            quote!(
                pub trait IAutoGuid
                {
                    fn one(&self);
                    fn two(&self);
                }
            ),
        )
        .expect("com_interface attribute parsing failed");

        assert_eq!(itf.path, parse_quote!(IAutoGuid));

        let pub_visibility: Visibility = parse_quote!(pub);
        assert_eq!(itf.visibility, pub_visibility);
        assert_eq!(itf.base_interface, None);

        let variant = &itf.variants[&Automation];
        assert_eq!(
            variant.iid,
            GUID::parse("82B905D9-D292-3531-452F-E04722F567DD").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");

        let variant = &itf.variants[&Raw];
        assert_eq!(
            variant.iid,
            GUID::parse("E16EEA74-C0E0-34DE-6F51-1D949883DE06").unwrap()
        );
        assert_eq!(variant.methods.len(), 2);
        assert_eq!(variant.methods[0].name, "one");
        assert_eq!(variant.methods[1].name, "two");
    }
}
<|endoftext|>


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

use std::cmp;
use collections::HashSet;
use std::local_data;
use std::uint;
use syntax::ast;
use rustc::util::nodemap::NodeSet;

use clean;
use clean::Item;
use plugins;
use fold;
use fold::DocFolder;

/// Strip items marked `#[doc(hidden)]`
pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
    let mut stripped = HashSet::new();

    // strip all #[doc(hidden)] items
    let krate = {
        struct Stripper<'a> {
            stripped: &'a mut HashSet<ast::NodeId>
        };
        impl<'a> fold::DocFolder for Stripper<'a> {
            fn fold_item(&mut self, i: Item) -> Option<Item> {
                for attr in i.attrs.iter() {
                    match attr {
                        &clean::List(ref x, ref l) if "doc" == *x => {
                            for innerattr in l.iter() {
                                match innerattr {
                                    &clean::Word(ref s) if "hidden" == *s => {
                                        debug!("found one in strip_hidden; removing");
                                        self.stripped.insert(i.id);
                                        return None;
                                    },
                                    _ => (),
                                }
                            }
                        },
                        _ => ()
                    }
                }
                self.fold_item_recur(i)
            }
        }
        let mut stripper = Stripper{ stripped: &mut stripped };
        stripper.fold_crate(krate)
    };

    // strip any traits implemented on stripped items
    let krate = {
        struct ImplStripper<'a> {
            stripped: &'a mut HashSet<ast::NodeId>
        };
        impl<'a> fold::DocFolder for ImplStripper<'a> {
            fn fold_item(&mut self, i: Item) -> Option<Item> {
                match i.inner {
                    clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: for_id, .. },
                                                 .. }) => {
                        if self.stripped.contains(&for_id) {
                            return None;
                        }
                    }
                    _ => {}
                }
                self.fold_item_recur(i)
            }
        }
        let mut stripper = ImplStripper{ stripped: &mut stripped };
        stripper.fold_crate(krate)
    };

    (krate, None)
}

/// Strip private items from the point of view of a crate or externally from a
/// crate, specified by the `xcrate` flag.
pub fn strip_private(krate: clean::Crate) -> plugins::PluginResult {
    // This stripper collects all *retained* nodes.
    let mut retained = HashSet::new();
    let exported_items = local_data::get(super::analysiskey, |analysis| {
        analysis.unwrap().exported_items.clone()
    });
    let mut krate = krate;

    // strip all private items
    {
        let mut stripper = Stripper {
            retained: &mut retained,
            exported_items: &exported_items,
        };
        krate = stripper.fold_crate(krate);
    }

    // strip all private implementations of traits
    {
        let mut stripper = ImplStripper(&retained);
        krate = stripper.fold_crate(krate);
    }
    (krate, None)
}

struct Stripper<'a> {
    retained: &'a mut HashSet<ast::NodeId>,
    exported_items: &'a NodeSet,
}

impl<'a> fold::DocFolder for Stripper<'a> {
    fn fold_item(&mut self, i: Item) -> Option<Item> {
        match i.inner {
            // These items can all get re-exported
            clean::TypedefItem(..) | clean::StaticItem(..) |
            clean::StructItem(..) | clean::EnumItem(..) |
            clean::TraitItem(..) | clean::FunctionItem(..) |
            clean::VariantItem(..) | clean::MethodItem(..) |
            clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => {
                if !self.exported_items.contains(&i.id) {
                    return None;
                }
            }

            clean::ViewItemItem(..) => {
                if i.visibility != Some(ast::Public) {
                    return None
                }
            }

            clean::StructFieldItem(..) => {
                if i.visibility == Some(ast::Private) {
                    return None;
                }
            }

            // handled below
            clean::ModuleItem(..) => {}

            // trait impls for private items should be stripped
            clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: ref for_id, .. }, .. }) => {
                if !self.exported_items.contains(for_id) {
                    return None;
                }
            }
            clean::ImplItem(..) => {}

            // tymethods/macros have no control over privacy
            clean::MacroItem(..) | clean::TyMethodItem(..) => {}
        }

        let fastreturn = match i.inner {
            // nothing left to do for traits (don't want to filter their
            // methods out, visibility controlled by the trait)
            clean::TraitItem(..) => true,

            // implementations of traits are always public.
            clean::ImplItem(ref imp) if imp.trait_.is_some() => true,

            _ => false,
        };

        let i = if fastreturn {
            self.retained.insert(i.id);
            return Some(i);
        } else {
            self.fold_item_recur(i)
        };

        match i {
            Some(i) => {
                match i.inner {
                    // emptied modules/impls have no need to exist
                    clean::ModuleItem(ref m)
                        if m.items.len() == 0 &&
                           i.doc_value().is_none() => None,
                    clean::ImplItem(ref i) if i.methods.len() == 0 => None,
                    _ => {
                        self.retained.insert(i.id);
                        Some(i)
                    }
                }
            }
            None => None,
        }
    }
}

// This stripper discards all private impls of traits
struct ImplStripper<'a>(&'a HashSet<ast::NodeId>);
impl<'a> fold::DocFolder for ImplStripper<'a> {
    fn fold_item(&mut self, i: Item) -> Option<Item> {
        match i.inner {
            clean::ImplItem(ref imp) => {
                match imp.trait_ {
                    Some(clean::ResolvedPath{ id, .. }) => {
                        let ImplStripper(s) = *self;
                        if !s.contains(&id) {
                            return None;
                        }
                    }
                    Some(..) | None => {}
                }
            }
            _ => {}
        }
        self.fold_item_recur(i)
    }
}

pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
    struct CommentCleaner;
    impl fold::DocFolder for CommentCleaner {
        fn fold_item(&mut self, i: Item) -> Option<Item> {
            let mut i = i;
            let mut avec: ~[clean::Attribute] = ~[];
            for attr in i.attrs.iter() {
                match attr {
                    &clean::NameValue(ref x, ref s) if "doc" == *x => avec.push(
                        clean::NameValue(~"doc", unindent(*s))),
                    x => avec.push(x.clone())
                }
            }
            i.attrs = avec;
            self.fold_item_recur(i)
        }
    }
    let mut cleaner = CommentCleaner;
    let krate = cleaner.fold_crate(krate);
    (krate, None)
}

pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
    struct Collapser;
    impl fold::DocFolder for Collapser {
        fn fold_item(&mut self, i: Item) -> Option<Item> {
            let mut docstr = ~"";
            let mut i = i;
            for attr in i.attrs.iter() {
                match *attr {
                    clean::NameValue(ref x, ref s) if "doc" == *x => {
                        docstr.push_str(s.clone());
                        docstr.push_char('\n');
                    },
                    _ => ()
                }
            }
            let mut a: ~[clean::Attribute] = i.attrs.iter().filter(|&a| match a {
                &clean::NameValue(ref x, _) if "doc" == *x => false,
                _ => true
            }).map(|x| x.clone()).collect();
            if "" != docstr {
                a.push(clean::NameValue(~"doc", docstr));
            }
            i.attrs = a;
            self.fold_item_recur(i)
        }
    }
    let mut collapser = Collapser;
    let krate = collapser.fold_crate(krate);
    (krate, None)
}

pub fn unindent(s: &str) -> ~str {
    let lines = s.lines_any().collect::<~[&str]>();
    let mut saw_first_line = false;
    let mut saw_second_line = false;
    let min_indent = lines.iter().fold(uint::MAX, |min_indent, line| {

        // After we see the first non-whitespace line, look at
        // the line we have. If it is not whitespace, and therefore
        // part of the first paragraph, then ignore the indentation
        // level of the first line
        let ignore_previous_indents =
            saw_first_line &&
            !saw_second_line &&
            !line.is_whitespace();

        let min_indent = if ignore_previous_indents {
            uint::MAX
        } else {
            min_indent
        };

        if saw_first_line {
            saw_second_line = true;
        }

        if line.is_whitespace() {
            min_indent
        } else {
            saw_first_line = true;
            let mut spaces = 0;
            line.chars().all(|char| {
                // Only comparing against space because I wouldn't
                // know what to do with mixed whitespace chars
                if char == ' ' {
                    spaces += 1;
                    true
                } else {
                    false
                }
            });
            cmp::min(min_indent, spaces)
        }
    });

    if lines.len() >= 1 {
        let mut unindented = ~[ lines[0].trim() ];
        unindented.push_all(lines.tail().map(|&line| {
            if line.is_whitespace() {
                line
            } else {
                assert!(line.len() >= min_indent);
                line.slice_from(min_indent)
            }
        }));
        unindented.connect("\n")
    } else {
        s.to_owned()
    }
}

#[cfg(test)]
mod unindent_tests {
    use super::unindent;

    #[test]
    fn should_unindent() {
        let s = ~"    line1\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\nline2");
    }

    #[test]
    fn should_unindent_multiple_paragraphs() {
        let s = ~"    line1\n\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\n\nline2");
    }

    #[test]
    fn should_leave_multiple_indent_levels() {
        // Line 2 is indented another level beyond the
        // base indentation and should be preserved
        let s = ~"    line1\n\n        line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\n\n    line2");
    }

    #[test]
    fn should_ignore_first_line_indent() {
        // Thi first line of the first paragraph may not be indented as
        // far due to the way the doc string was written:
        //
        // #[doc = "Start way over here
        //          and continue here"]
        let s = ~"line1\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\nline2");
    }

    #[test]
    fn should_not_ignore_first_line_indent_in_a_single_line_para() {
        let s = ~"line1\n\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\n\n    line2");
    }
}
<commit_msg>Modules are either public, or private, so private modules should be stripped out<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cmp;
use collections::HashSet;
use std::local_data;
use std::uint;
use syntax::ast;
use rustc::util::nodemap::NodeSet;

use clean;
use clean::Item;
use plugins;
use fold;
use fold::DocFolder;

/// Strip items marked `#[doc(hidden)]`
pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
    let mut stripped = HashSet::new();

    // strip all #[doc(hidden)] items
    let krate = {
        struct Stripper<'a> {
            stripped: &'a mut HashSet<ast::NodeId>
        };
        impl<'a> fold::DocFolder for Stripper<'a> {
            fn fold_item(&mut self, i: Item) -> Option<Item> {
                for attr in i.attrs.iter() {
                    match attr {
                        &clean::List(ref x, ref l) if "doc" == *x => {
                            for innerattr in l.iter() {
                                match innerattr {
                                    &clean::Word(ref s) if "hidden" == *s => {
                                        debug!("found one in strip_hidden; removing");
                                        self.stripped.insert(i.id);
                                        return None;
                                    },
                                    _ => (),
                                }
                            }
                        },
                        _ => ()
                    }
                }
                self.fold_item_recur(i)
            }
        }
        let mut stripper = Stripper{ stripped: &mut stripped };
        stripper.fold_crate(krate)
    };

    // strip any traits implemented on stripped items
    let krate = {
        struct ImplStripper<'a> {
            stripped: &'a mut HashSet<ast::NodeId>
        };
        impl<'a> fold::DocFolder for ImplStripper<'a> {
            fn fold_item(&mut self, i: Item) -> Option<Item> {
                match i.inner {
                    clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: for_id, .. },
                                                 .. }) => {
                        if self.stripped.contains(&for_id) {
                            return None;
                        }
                    }
                    _ => {}
                }
                self.fold_item_recur(i)
            }
        }
        let mut stripper = ImplStripper{ stripped: &mut stripped };
        stripper.fold_crate(krate)
    };

    (krate, None)
}

/// Strip private items from the point of view of a crate or externally from a
/// crate, specified by the `xcrate` flag.
pub fn strip_private(krate: clean::Crate) -> plugins::PluginResult {
    // This stripper collects all *retained* nodes.
    let mut retained = HashSet::new();
    let exported_items = local_data::get(super::analysiskey, |analysis| {
        analysis.unwrap().exported_items.clone()
    });
    let mut krate = krate;

    // strip all private items
    {
        let mut stripper = Stripper {
            retained: &mut retained,
            exported_items: &exported_items,
        };
        krate = stripper.fold_crate(krate);
    }

    // strip all private implementations of traits
    {
        let mut stripper = ImplStripper(&retained);
        krate = stripper.fold_crate(krate);
    }
    (krate, None)
}

struct Stripper<'a> {
    retained: &'a mut HashSet<ast::NodeId>,
    exported_items: &'a NodeSet,
}

impl<'a> fold::DocFolder for Stripper<'a> {
    fn fold_item(&mut self, i: Item) -> Option<Item> {
        match i.inner {
            // These items can all get re-exported
            clean::TypedefItem(..) | clean::StaticItem(..) |
            clean::StructItem(..) | clean::EnumItem(..) |
            clean::TraitItem(..) | clean::FunctionItem(..) |
            clean::VariantItem(..) | clean::MethodItem(..) |
            clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => {
                if !self.exported_items.contains(&i.id) {
                    return None;
                }
            }

            clean::ViewItemItem(..) |
            clean::ModuleItem(..) => {
                if i.visibility != Some(ast::Public) {
                    return None
                }
            }

            clean::StructFieldItem(..) => {
                if i.visibility == Some(ast::Private) {
                    return None;
                }
            }

            // trait impls for private items should be stripped
            clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: ref for_id, .. }, .. }) => {
                if !self.exported_items.contains(for_id) {
                    return None;
                }
            }
            clean::ImplItem(..) => {}

            // tymethods/macros have no control over privacy
            clean::MacroItem(..) | clean::TyMethodItem(..) => {}
        }

        let fastreturn = match i.inner {
            // nothing left to do for traits (don't want to filter their
            // methods out, visibility controlled by the trait)
            clean::TraitItem(..) => true,

            // implementations of traits are always public.
            clean::ImplItem(ref imp) if imp.trait_.is_some() => true,

            _ => false,
        };

        let i = if fastreturn {
            self.retained.insert(i.id);
            return Some(i);
        } else {
            self.fold_item_recur(i)
        };

        match i {
            Some(i) => {
                match i.inner {
                    // emptied modules/impls have no need to exist
                    clean::ModuleItem(ref m)
                        if m.items.len() == 0 &&
                           i.doc_value().is_none() => None,
                    clean::ImplItem(ref i) if i.methods.len() == 0 => None,
                    _ => {
                        self.retained.insert(i.id);
                        Some(i)
                    }
                }
            }
            None => None,
        }
    }
}

// This stripper discards all private impls of traits
struct ImplStripper<'a>(&'a HashSet<ast::NodeId>);
impl<'a> fold::DocFolder for ImplStripper<'a> {
    fn fold_item(&mut self, i: Item) -> Option<Item> {
        match i.inner {
            clean::ImplItem(ref imp) => {
                match imp.trait_ {
                    Some(clean::ResolvedPath{ id, .. }) => {
                        let ImplStripper(s) = *self;
                        if !s.contains(&id) {
                            return None;
                        }
                    }
                    Some(..) | None => {}
                }
            }
            _ => {}
        }
        self.fold_item_recur(i)
    }
}

pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
    struct CommentCleaner;
    impl fold::DocFolder for CommentCleaner {
        fn fold_item(&mut self, i: Item) -> Option<Item> {
            let mut i = i;
            let mut avec: ~[clean::Attribute] = ~[];
            for attr in i.attrs.iter() {
                match attr {
                    &clean::NameValue(ref x, ref s) if "doc" == *x => avec.push(
                        clean::NameValue(~"doc", unindent(*s))),
                    x => avec.push(x.clone())
                }
            }
            i.attrs = avec;
            self.fold_item_recur(i)
        }
    }
    let mut cleaner = CommentCleaner;
    let krate = cleaner.fold_crate(krate);
    (krate, None)
}

pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
    struct Collapser;
    impl fold::DocFolder for Collapser {
        fn fold_item(&mut self, i: Item) -> Option<Item> {
            let mut docstr = ~"";
            let mut i = i;
            for attr in i.attrs.iter() {
                match *attr {
                    clean::NameValue(ref x, ref s) if "doc" == *x => {
                        docstr.push_str(s.clone());
                        docstr.push_char('\n');
                    },
                    _ => ()
                }
            }
            let mut a: ~[clean::Attribute] = i.attrs.iter().filter(|&a| match a {
                &clean::NameValue(ref x, _) if "doc" == *x => false,
                _ => true
            }).map(|x| x.clone()).collect();
            if "" != docstr {
                a.push(clean::NameValue(~"doc", docstr));
            }
            i.attrs = a;
            self.fold_item_recur(i)
        }
    }
    let mut collapser = Collapser;
    let krate = collapser.fold_crate(krate);
    (krate, None)
}

pub fn unindent(s: &str) -> ~str {
    let lines = s.lines_any().collect::<~[&str]>();
    let mut saw_first_line = false;
    let mut saw_second_line = false;
    let min_indent = lines.iter().fold(uint::MAX, |min_indent, line| {

        // After we see the first non-whitespace line, look at
        // the line we have. If it is not whitespace, and therefore
        // part of the first paragraph, then ignore the indentation
        // level of the first line
        let ignore_previous_indents =
            saw_first_line &&
            !saw_second_line &&
            !line.is_whitespace();

        let min_indent = if ignore_previous_indents {
            uint::MAX
        } else {
            min_indent
        };

        if saw_first_line {
            saw_second_line = true;
        }

        if line.is_whitespace() {
            min_indent
        } else {
            saw_first_line = true;
            let mut spaces = 0;
            line.chars().all(|char| {
                // Only comparing against space because I wouldn't
                // know what to do with mixed whitespace chars
                if char == ' ' {
                    spaces += 1;
                    true
                } else {
                    false
                }
            });
            cmp::min(min_indent, spaces)
        }
    });

    if lines.len() >= 1 {
        let mut unindented = ~[ lines[0].trim() ];
        unindented.push_all(lines.tail().map(|&line| {
            if line.is_whitespace() {
                line
            } else {
                assert!(line.len() >= min_indent);
                line.slice_from(min_indent)
            }
        }));
        unindented.connect("\n")
    } else {
        s.to_owned()
    }
}

#[cfg(test)]
mod unindent_tests {
    use super::unindent;

    #[test]
    fn should_unindent() {
        let s = ~"    line1\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\nline2");
    }

    #[test]
    fn should_unindent_multiple_paragraphs() {
        let s = ~"    line1\n\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\n\nline2");
    }

    #[test]
    fn should_leave_multiple_indent_levels() {
        // Line 2 is indented another level beyond the
        // base indentation and should be preserved
        let s = ~"    line1\n\n        line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\n\n    line2");
    }

    #[test]
    fn should_ignore_first_line_indent() {
        // Thi first line of the first paragraph may not be indented as
        // far due to the way the doc string was written:
        //
        // #[doc = "Start way over here
        //          and continue here"]
        let s = ~"line1\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\nline2");
    }

    #[test]
    fn should_not_ignore_first_line_indent_in_a_single_line_para() {
        let s = ~"line1\n\n    line2";
        let r = unindent(s);
        assert_eq!(r, ~"line1\n\n    line2");
    }
}
<|endoftext|>