<commit_before>use std::sync::Arc;
use std::io::{Read, Write};
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
use bincode::{self, serde};
use euclid::Point2D;
use calx_resource::ResourceStore;
use calx_ecs::Entity;
use calx_grid::{Dir6, HexFov};
use field::Field;
use spatial::{Place, Spatial};
use flags::Flags;
use location::{Location, Portal};
use components;
use stats;
use terrain;
use query::Query;
use command::{Command, CommandResult};
use mutate::Mutate;
use terraform::{Terraform, TerrainQuery};
use fov::SightFov;

pub const GAME_VERSION: &'static str = "0.1.0";

Ecs! {
    desc: components::Desc,
    map_memory: components::MapMemory,
    health: components::Health,
    brain: components::Brain,
    item: components::Item,
    composite_stats: components::CompositeStats,
    stats: stats::Stats,
}

/// Toplevel game state object.
#[derive(RustcEncodable, RustcDecodable)]
pub struct World {
    /// Game version. Not mutable in the slightest, but the simplest way to
    /// get versioned save files is to just drop it here.
    version: String,
    /// Entity component system.
    ecs: Ecs,
    /// Terrain data.
    terrain: Field<u8>,
    /// Optional portals between map zones.
    portals: HashMap<Location, Portal>,
    /// Spatial index for game entities.
    spatial: Spatial,
    /// Global gamestate flags.
    flags: Flags,
}

impl<'a> World {
    pub fn new(seed: u32) -> World {
        let mut ret = World {
            version: GAME_VERSION.to_string(),
            ecs: Ecs::new(),
            terrain: Field::new(0),
            portals: HashMap::new(),
            spatial: Spatial::new(),
            flags: Flags::new(seed),
        };

        ret
    }

    /* TODO: Reactivate for rustc-serialize

    pub fn load<R: Read>(reader: &mut R) -> serde::DeserializeResult<World> {
        let ret: serde::DeserializeResult<World> =
            serde::deserialize_from(reader, bincode::SizeLimit::Infinite);
        if let &Ok(ref x) = &ret {
            if &x.version != GAME_VERSION {
                panic!("Save game version {} does not match current version \
                        {}",
                       x.version,
                       GAME_VERSION);
            }
        }
        ret
    }

    pub fn save<W: Write>(&self, writer: &mut W) -> serde::SerializeResult<()> {
        serde::serialize_into(writer, self, bincode::SizeLimit::Infinite)
    }
    */

    fn default_terrain_id(&self, loc: Location) -> u8 {
        terrain::Id::Empty as u8
    }
}

impl TerrainQuery for World {
    fn is_valid_location(&self, loc: Location) -> bool {
        ::on_screen(Point2D::new(loc.x as i32, loc.y as i32))
    }

    fn terrain_id(&self, loc: Location) -> u8 {
        let idx = self.terrain.get(loc);

        if idx == 0 { self.default_terrain_id(loc) } else { idx }
    }

    fn portal(&self, loc: Location) -> Option<Location> { self.portals.get(&loc).map(|&p| loc + p) }

    fn is_untouched(&self, loc: Location) -> bool { !self.terrain.overrides(loc) }
}

impl Query for World {
    fn location(&self, e: Entity) -> Option<Location> {
        match self.spatial.get(e) {
            Some(Place::At(loc)) => Some(loc),
            Some(Place::In(container, _)) => self.location(container),
            _ => None,
        }
    }

    fn player(&self) -> Option<Entity> {
        if let Some(p) = self.flags.player {
            if self.is_alive(p) {
                return Some(p);
            }
        }

        None
    }

    fn tick(&self) -> u64 { self.flags.tick }

    fn rng_seed(&self) -> u32 { self.flags.seed }

    fn entities_at(&self, loc: Location) -> Vec<Entity> { self.spatial.entities_at(loc) }

    fn ecs<'a>(&'a self) -> &'a Ecs { &self.ecs }
}

impl Mutate for World {
    fn next_tick(&mut self) -> CommandResult {
        // TODO: Run AI
        // TODO: Clean dead
        self.flags.tick += 1;
        Ok(())
    }

    fn set_entity_location(&mut self, e: Entity, loc: Location) { self.spatial.insert_at(e, loc); }

    fn set_player(&mut self, player: Entity) { self.flags.player = Some(player); }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity {
        let e = loadout.make(&mut self.ecs);
        self.place_entity(e, loc);
        e
    }

    fn remove_entity(&mut self, e: Entity) { self.ecs.remove(e); }

    fn do_fov(&mut self, e: Entity) {
        if !self.ecs.map_memory.contains(e) {
            return;
        }

        if let Some(loc) = self.location(e) {
            const DEFAULT_FOV_RANGE: u32 = 12;

            let fov: HashSet<Location> =
                HashSet::from_iter(HexFov::new(SightFov::new(self, DEFAULT_FOV_RANGE, loc))
                                       .map(|(pos, a)| a.origin + pos));

            let ref mut memory = self.ecs.map_memory[e];
            memory.seen.clear();

            for &loc in fov.iter() {
                memory.seen.insert(loc);
                memory.remembered.insert(loc);
            }
        }
    }
}

impl Command for World {
    fn step(&mut self, dir: Dir6) -> CommandResult {
        let player = try!(self.player().ok_or(()));
        try!(self.entity_step(player, dir));
        self.next_tick()
    }

    fn melee(&mut self, dir: Dir6) -> CommandResult {
        let player = try!(self.player().ok_or(()));
        try!(self.entity_melee(player, dir));
        self.next_tick()
    }

    fn pass(&mut self) -> CommandResult { self.next_tick() }
}

impl Terraform for World {
    fn set_terrain(&mut self, loc: Location, terrain: u8) { self.terrain.set(loc, terrain); }

    fn set_portal(&mut self, loc: Location, mut portal: Portal) {
        let target_loc = loc + portal;
        // Don't create portal chains, if the target cell has another portal, just direct to its
        // destination.
        if let Some(&p) = self.portals.get(&target_loc) {
            portal = portal + p;
        }

        if portal.dx == 0 && portal.dy == 0 && portal.z == loc.z {
            self.portals.remove(&loc);
        } else {
            self.portals.insert(loc, portal);
        }
    }

    fn remove_portal(&mut self, loc: Location) { self.portals.remove(&loc); }
}
<commit_msg>Doors opening needs to show up in logic too<commit_after>use std::sync::Arc;
use std::io::{Read, Write};
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
use bincode::{self, serde};
use euclid::Point2D;
use calx_resource::ResourceStore;
use calx_ecs::Entity;
use calx_grid::{Dir6, HexFov};
use field::Field;
use spatial::{Place, Spatial};
use flags::Flags;
use location::{Location, Portal};
use components;
use stats;
use terrain;
use query::Query;
use command::{Command, CommandResult};
use mutate::Mutate;
use terraform::{Terraform, TerrainQuery};
use fov::SightFov;

pub const GAME_VERSION: &'static str = "0.1.0";

Ecs! {
    desc: components::Desc,
    map_memory: components::MapMemory,
    health: components::Health,
    brain: components::Brain,
    item: components::Item,
    composite_stats: components::CompositeStats,
    stats: stats::Stats,
}

/// Toplevel game state object.
#[derive(RustcEncodable, RustcDecodable)]
pub struct World {
    /// Game version. Not mutable in the slightest, but the simplest way to
    /// get versioned save files is to just drop it here.
    version: String,
    /// Entity component system.
    ecs: Ecs,
    /// Terrain data.
    terrain: Field<u8>,
    /// Optional portals between map zones.
    portals: HashMap<Location, Portal>,
    /// Spatial index for game entities.
    spatial: Spatial,
    /// Global gamestate flags.
    flags: Flags,
}

impl<'a> World {
    pub fn new(seed: u32) -> World {
        let mut ret = World {
            version: GAME_VERSION.to_string(),
            ecs: Ecs::new(),
            terrain: Field::new(0),
            portals: HashMap::new(),
            spatial: Spatial::new(),
            flags: Flags::new(seed),
        };

        ret
    }

    /* TODO: Reactivate for rustc-serialize

    pub fn load<R: Read>(reader: &mut R) -> serde::DeserializeResult<World> {
        let ret: serde::DeserializeResult<World> =
            serde::deserialize_from(reader, bincode::SizeLimit::Infinite);
        if let &Ok(ref x) = &ret {
            if &x.version != GAME_VERSION {
                panic!("Save game version {} does not match current version \
                        {}",
                       x.version,
                       GAME_VERSION);
            }
        }
        ret
    }

    pub fn save<W: Write>(&self, writer: &mut W) -> serde::SerializeResult<()> {
        serde::serialize_into(writer, self, bincode::SizeLimit::Infinite)
    }
    */

    fn default_terrain_id(&self, loc: Location) -> u8 {
        terrain::Id::Empty as u8
    }
}

impl TerrainQuery for World {
    fn is_valid_location(&self, loc: Location) -> bool {
        ::on_screen(Point2D::new(loc.x as i32, loc.y as i32))
    }

    fn terrain_id(&self, loc: Location) -> u8 {
        let mut idx = self.terrain.get(loc);

        if idx == terrain::Id::Door as u8 {
            // Standing in the doorway opens the door.
            if self.has_mobs(loc) {
                idx = terrain::Id::OpenDoor as u8;
            }
        }

        if idx == 0 { self.default_terrain_id(loc) } else { idx }
    }

    fn portal(&self, loc: Location) -> Option<Location> { self.portals.get(&loc).map(|&p| loc + p) }

    fn is_untouched(&self, loc: Location) -> bool { !self.terrain.overrides(loc) }
}

impl Query for World {
    fn location(&self, e: Entity) -> Option<Location> {
        match self.spatial.get(e) {
            Some(Place::At(loc)) => Some(loc),
            Some(Place::In(container, _)) => self.location(container),
            _ => None,
        }
    }

    fn player(&self) -> Option<Entity> {
        if let Some(p) = self.flags.player {
            if self.is_alive(p) {
                return Some(p);
            }
        }

        None
    }

    fn tick(&self) -> u64 { self.flags.tick }

    fn rng_seed(&self) -> u32 { self.flags.seed }

    fn entities_at(&self, loc: Location) -> Vec<Entity> { self.spatial.entities_at(loc) }

    fn ecs<'a>(&'a self) -> &'a Ecs { &self.ecs }
}

impl Mutate for World {
    fn next_tick(&mut self) -> CommandResult {
        // TODO: Run AI
        // TODO: Clean dead
        self.flags.tick += 1;
        Ok(())
    }

    fn set_entity_location(&mut self, e: Entity, loc: Location) { self.spatial.insert_at(e, loc); }

    fn set_player(&mut self, player: Entity) { self.flags.player = Some(player); }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity {
        let e = loadout.make(&mut self.ecs);
        self.place_entity(e, loc);
        e
    }

    fn remove_entity(&mut self, e: Entity) { self.ecs.remove(e); }

    fn do_fov(&mut self, e: Entity) {
        if !self.ecs.map_memory.contains(e) {
            return;
        }

        if let Some(loc) = self.location(e) {
            const DEFAULT_FOV_RANGE: u32 = 12;

            let fov: HashSet<Location> =
                HashSet::from_iter(HexFov::new(SightFov::new(self, DEFAULT_FOV_RANGE, loc))
                                       .map(|(pos, a)| a.origin + pos));

            let ref mut memory = self.ecs.map_memory[e];
            memory.seen.clear();

            for &loc in fov.iter() {
                memory.seen.insert(loc);
                memory.remembered.insert(loc);
            }
        }
    }
}

impl Command for World {
    fn step(&mut self, dir: Dir6) -> CommandResult {
        let player = try!(self.player().ok_or(()));
        try!(self.entity_step(player, dir));
        self.next_tick()
    }

    fn melee(&mut self, dir: Dir6) -> CommandResult {
        let player = try!(self.player().ok_or(()));
        try!(self.entity_melee(player, dir));
        self.next_tick()
    }

    fn pass(&mut self) -> CommandResult { self.next_tick() }
}

impl Terraform for World {
    fn set_terrain(&mut self, loc: Location, terrain: u8) { self.terrain.set(loc, terrain); }

    fn set_portal(&mut self, loc: Location, mut portal: Portal) {
        let target_loc = loc + portal;
        // Don't create portal chains, if the target cell has another portal, just direct to its
        // destination.
        if let Some(&p) = self.portals.get(&target_loc) {
            portal = portal + p;
        }

        if portal.dx == 0 && portal.dy == 0 && portal.z == loc.z {
            self.portals.remove(&loc);
        } else {
            self.portals.insert(loc, portal);
        }
    }

    fn remove_portal(&mut self, loc: Location) { self.portals.remove(&loc); }
}
<|endoftext|>


<commit_before>use alloc::boxed::{Box, FnBox};

use collections::string::String;
use collections::vec::Vec;

use core::{mem, ptr};

use common::memory;
use common::paging::Page;
use common::scheduler;

use schemes::Resource;

pub const CONTEXT_STACK_SIZE: usize = 1024 * 1024;

pub static mut contexts_ptr: *mut Vec<Box<Context>> = 0 as *mut Vec<Box<Context>>;
pub static mut context_i: usize = 0;
pub static mut context_enabled: bool = false;

/// Switch context
///
/// Unsafe due to interrupt disabling, raw pointers, and unsafe Context functions
pub unsafe fn context_switch(interrupted: bool) {
    let reenable = scheduler::start_no_ints();

    let contexts = &mut *contexts_ptr;
    if context_enabled {
        let current_i = context_i;
        context_i += 1;
        // The only garbage collection in Redox
        loop {
            if context_i >= contexts.len() {
                context_i -= contexts.len();
            }

            let mut remove = false;
            if let Some(next) = contexts.get(context_i) {
                if next.exited {
                    remove = true;
                }
            }

            if remove {
                drop(contexts.remove(context_i));
            } else {
                break;
            }
        }

        if context_i >= contexts.len() {
            context_i -= contexts.len();
        }

        if context_i != current_i {
            match contexts.get(current_i) {
                Some(current) => match contexts.get(context_i) {
                    Some(next) => {
                        let current_ptr: *mut Context = mem::transmute(current);
                        let next_ptr: *mut Context = mem::transmute(next);

                        (*current_ptr).interrupted = interrupted;
                        (*next_ptr).interrupted = false;
                        (*current_ptr).remap(&mut *next_ptr);
                        (*current_ptr).switch(&mut *next_ptr);
                    }
                    None => (),
                },
                None => (),
            }
        }
    }

    scheduler::end_no_ints(reenable);
}

/// Fork context
///
/// Unsafe due to interrupt disabling, C memory handling, and raw pointers
pub unsafe extern "cdecl" fn context_fork(parent_i: usize){
    let reenable = scheduler::start_no_ints();

    let contexts = &mut *contexts_ptr;
    let mut context_option: Option<Box<Context>> = None;
    if let Some(parent) = contexts.get(parent_i) {
        let stack = memory::alloc(CONTEXT_STACK_SIZE + 512);
        if stack > 0 {
            ::memcpy(stack as *mut u8, parent.stack as *const u8, CONTEXT_STACK_SIZE + 512);

            let mut mem: Vec<ContextMemory> = Vec::new();
            for entry in parent.memory.iter() {
                let physical_address = memory::alloc(entry.virtual_size);
                if physical_address > 0 {
                    ::memcpy(physical_address as *mut u8, entry.physical_address as *const u8, entry.virtual_size);
                    mem.push(ContextMemory {
                        physical_address: physical_address,
                        virtual_address: entry.virtual_address,
                        virtual_size: entry.virtual_size,
                    });
                }
            }

            let mut files: Vec<ContextFile> = Vec::new();
            for file in parent.files.iter() {
                if let Some(resource) = file.resource.dup() {
                    files.push(ContextFile {
                        fd: file.fd,
                        resource: resource
                    });
                }
            }

            context_option = Some(box Context {
                stack: stack,
                stack_ptr: (parent.stack_ptr - parent.stack) + stack,
                fx: stack + CONTEXT_STACK_SIZE,
                fx_enabled: parent.fx_enabled,
                memory: mem,
                cwd: parent.cwd.clone(),
                args: parent.args.clone(),
                files: files,
                interrupted: parent.interrupted,
                exited: parent.exited,
            });
        }
    }

    if let Some(context) = context_option {
        contexts.push(context);
    }

    scheduler::end_no_ints(reenable);
}

//TODO: To clean up memory leak, current must be destroyed!
/// Exit context
///
/// Unsafe due to interrupt disabling and raw pointers
pub unsafe fn context_exit() {
    let reenable = scheduler::start_no_ints();

    let contexts = &mut *contexts_ptr;
    if context_enabled && context_i > 1 {
        match contexts.get_mut(context_i) {
            Some(mut current) => current.exited = true,
            None => (),
        }
    }

    scheduler::end_no_ints(reenable);

    context_switch(false);
}

// Currently unused?
/// Reads a Boxed function and executes it
///
/// Unsafe due to raw memory handling and FnBox
pub unsafe extern "cdecl" fn context_box(box_fn_ptr: usize) {
    let box_fn = ptr::read(box_fn_ptr as *mut Box<FnBox()>);
    memory::unalloc(box_fn_ptr);
    box_fn();
}

pub struct ContextMemory {
    pub physical_address: usize,
    pub virtual_address: usize,
    pub virtual_size: usize,
}

pub struct ContextFile {
    pub fd: usize,
    pub resource: Box<Resource>,
}

pub struct Context {
    pub stack: usize,
    pub stack_ptr: usize,
    pub fx: usize,
    pub fx_enabled: bool,
    pub memory: Vec<ContextMemory>,
    pub cwd: String,
    pub args: Vec<String>,
    pub files: Vec<ContextFile>,
    pub interrupted: bool,
    pub exited: bool,
}

impl Context {
    pub unsafe fn root() -> Box<Self> {
        box Context {
            stack: 0,
            stack_ptr: 0,
            fx: memory::alloc(512),
            fx_enabled: false,
            memory: Vec::new(),
            cwd: String::new(),
            args: Vec::new(),
            files: Vec::new(),
            interrupted: false,
            exited: false,
        }
    }

    #[cfg(target_arch = "x86")]
    pub unsafe fn new(call: usize, args: &Vec<usize>) -> Box<Self> {
        let stack = memory::alloc(CONTEXT_STACK_SIZE + 512);

        let mut ret = box Context {
            stack: stack,
            stack_ptr: stack + CONTEXT_STACK_SIZE,
            fx: stack + CONTEXT_STACK_SIZE,
            fx_enabled: false,
            memory: Vec::new(),
            cwd: String::new(),
            args: Vec::new(),
            files: Vec::new(),
            interrupted: false,
            exited: false,
        };

        for arg in args.iter() {
            ret.push(*arg);
        }

        ret.push(call); //We will ret into this function call

        ret.push(1 << 9); //Flags

        ret.push(0); //EAX
        ret.push(0); //ECX
        ret.push(0); //EDX
        ret.push(0); //EBX
        ret.push(0); //ESP (ignored)
        ret.push(0); //EBP
        ret.push(0); //ESI
        ret.push(0); //EDI

        ret
    }

    #[cfg(target_arch = "x86_64")]
    pub unsafe fn new(call: usize, args: &Vec<usize>) -> Box<Self> {
        let stack = memory::alloc(CONTEXT_STACK_SIZE + 512);

        let mut ret = box Context {
            stack: stack,
            stack_ptr: stack + CONTEXT_STACK_SIZE,
            fx: stack + CONTEXT_STACK_SIZE,
            fx_enabled: false,
            memory: Vec::new(),
            cwd: String::new(),
            files: Vec::new(),
            interrupted: false,
            exited: false,
        };

        let mut args_mut = args.clone();

        while args_mut.len() >= 7 {
            if let Some(value) = args_mut.pop() {
                ret.push(value);
            }
        }

        //First six args are in regs
        let r9 = if args_mut.len() >= 6 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let r8 = if args_mut.len() >= 5 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rcx = if args_mut.len() >= 4 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rdx = if args_mut.len() >= 3 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rsi = if args_mut.len() >= 2 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rdi = if args_mut.len() >= 1 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };

        ret.push(call); //We will ret into this function call

        ret.push(1 << 9); //Flags

        ret.push(0); //RAX
        ret.push(0); //RBX
        ret.push(rcx); //RCX
        ret.push(rdx); //RDX
        ret.push(rdi); //RDI
        ret.push(rsi); //RSI
        ret.push(r8); //R8
        ret.push(r9); //R9
        ret.push(0); //R10
        ret.push(0); //R11
        ret.push(0); //R12
        ret.push(0); //R13
        ret.push(0); //R14
        ret.push(0); //R15
        ret.push(0); //RBP

        ret
    }

    pub fn spawn(box_fn: Box<FnBox()>) {
        unsafe {
            let box_fn_ptr: *mut Box<FnBox()> = memory::alloc_type();
            ptr::write(box_fn_ptr, box_fn);

            let mut context_box_args: Vec<usize> = Vec::new();
            context_box_args.push(box_fn_ptr as usize);
            context_box_args.push(context_exit as usize);

            let reenable = scheduler::start_no_ints();
            if contexts_ptr as usize > 0 {
                (*contexts_ptr).push(Context::new(context_box as usize, &context_box_args));
            }
            scheduler::end_no_ints(reenable);
        }
    }

    pub unsafe fn push(&mut self, data: usize) {
        self.stack_ptr -= mem::size_of::<usize>();
        ptr::write(self.stack_ptr as *mut usize, data);
    }

    pub unsafe fn map(&mut self) {
        for entry in self.memory.iter() {
            for i in 0..(entry.virtual_size + 4095) / 4096 {
                Page::new(entry.virtual_address + i * 4096).map(entry.physical_address + i * 4096);
            }
        }
    }

    pub unsafe fn unmap(&mut self) {
        for entry in self.memory.iter() {
            for i in 0..(entry.virtual_size + 4095) / 4096 {
                Page::new(entry.virtual_address + i * 4096).map_identity();
            }
        }
    }

    pub unsafe fn remap(&mut self, other: &mut Self) {
        self.unmap();
        other.map();
    }

    //Warning: This function MUST be inspected in disassembly for correct push/pop
    //It should have exactly no extra pushes or pops
    #[cold]
    #[inline(never)]
    //#[naked]
    #[cfg(target_arch = "x86")]
    pub unsafe fn switch(&mut self, other: &mut Self) {
        asm!("pushfd
            pushad
            mov [eax], esp"
            :
            : "{eax}"(&mut self.stack_ptr)
            : "memory"
            : "intel", "volatile");

        asm!("fxsave [eax]"
            :
            : "{eax}"(self.fx)
            : "memory"
            : "intel", "volatile");

        self.fx_enabled = true;

        if other.fx_enabled {
            asm!("fxrstor [eax]"
                :
                : "{eax}"(other.fx)
                : "memory"
                : "intel", "volatile");
        }

        asm!("mov esp, [eax]
            popad
            popfd"
            :
            : "{eax}"(&mut other.stack_ptr)
            : "memory"
            : "intel", "volatile");
    }

    //Warning: This function MUST be inspected in disassembly for correct push/pop
    //It should have no extra pushes or pops
    #[cold]
    #[inline(never)]
    //#[naked]
    #[cfg(target_arch = "x86_64")]
    pub unsafe fn switch(&mut self, other: &mut Self) {
        asm!("pushfq
            push rax
            push rbx
            push rcx
            push rdx
            push rdi
            push rsi
            push r8
            push r9
            push r10
            push r11
            push r12
            push r13
            push r14
            push r15
            push rbp
            mov [rax], rsp"
            :
            : "{rax}"(&mut self.stack_ptr)
            : "memory"
            : "intel", "volatile");

        asm!("fxsave [rax]"
            :
            : "{rax}"(self.fx)
            : "memory"
            : "intel", "volatile");
        self.fx_enabled = true;

        //TODO: Clear registers
        if other.fx_enabled {
            asm!("fxrstor [rax]"
                :
                : "{rax}"(other.fx)
                : "memory"
                : "intel", "volatile");
        }

        asm!("mov rsp, [rax]
            pop rbp
            pop r15
            pop r14
            pop r13
            pop r12
            pop r11
            pop r10
            pop r9
            pop r8
            pop rsi
            pop rdi
            pop rdx
            pop rcx
            pop rbx
            pop rax
            popfq"
            :
            : "{rax}"(&mut other.stack_ptr)
            : "memory"
            : "intel", "volatile");
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        while let Some(file) = self.files.pop() {
            drop(file);
        }

        while let Some(arg) = self.args.pop() {
            drop(arg);
        }

        while let Some(entry) = self.memory.pop() {
            unsafe {
                memory::unalloc(entry.physical_address);
            }
        }

        if self.stack > 0 {
            unsafe {
                memory::unalloc(self.stack);
            }
            self.stack = 0;
        }
    }
}
<commit_msg>Fix context switch<commit_after>use alloc::boxed::{Box, FnBox};

use collections::string::String;
use collections::vec::Vec;

use core::{mem, ptr};

use common::memory;
use common::paging::Page;
use common::scheduler;

use schemes::Resource;

pub const CONTEXT_STACK_SIZE: usize = 1024 * 1024;

pub static mut contexts_ptr: *mut Vec<Box<Context>> = 0 as *mut Vec<Box<Context>>;
pub static mut context_i: usize = 0;
pub static mut context_enabled: bool = false;

/// Switch context
///
/// Unsafe due to interrupt disabling, raw pointers, and unsafe Context functions
pub unsafe fn context_switch(interrupted: bool) {
    let reenable = scheduler::start_no_ints();

    let contexts = &mut *contexts_ptr;
    if context_enabled {
        let current_i = context_i;
        context_i += 1;
        // The only garbage collection in Redox
        loop {
            if context_i >= contexts.len() {
                context_i -= contexts.len();
            }

            let mut remove = false;
            if let Some(next) = contexts.get(context_i) {
                if next.exited {
                    remove = true;
                }
            }

            if remove {
                drop(contexts.remove(context_i));
            } else {
                break;
            }
        }

        if context_i >= contexts.len() {
            context_i -= contexts.len();
        }

        if context_i != current_i {
            match contexts.get(current_i) {
                Some(current) => match contexts.get(context_i) {
                    Some(next) => {
                        let current_ptr: *mut Box<Context> = mem::transmute(current as *const Box<Context>);
                        let next_ptr: *mut Box<Context> = mem::transmute(next as *const Box<Context>);

                        (*current_ptr).interrupted = interrupted;
                        (*next_ptr).interrupted = false;
                        (*current_ptr).remap(&mut *next_ptr);
                        (*current_ptr).switch(&mut *next_ptr);
                    }
                    None => (),
                },
                None => (),
            }
        }
    }

    scheduler::end_no_ints(reenable);
}

/// Fork context
///
/// Unsafe due to interrupt disabling, C memory handling, and raw pointers
pub unsafe extern "cdecl" fn context_fork(parent_i: usize){
    let reenable = scheduler::start_no_ints();

    let contexts = &mut *contexts_ptr;
    let mut context_option: Option<Box<Context>> = None;
    if let Some(parent) = contexts.get(parent_i) {
        let stack = memory::alloc(CONTEXT_STACK_SIZE + 512);
        if stack > 0 {
            ::memcpy(stack as *mut u8, parent.stack as *const u8, CONTEXT_STACK_SIZE + 512);

            let mut mem: Vec<ContextMemory> = Vec::new();
            for entry in parent.memory.iter() {
                let physical_address = memory::alloc(entry.virtual_size);
                if physical_address > 0 {
                    ::memcpy(physical_address as *mut u8, entry.physical_address as *const u8, entry.virtual_size);
                    mem.push(ContextMemory {
                        physical_address: physical_address,
                        virtual_address: entry.virtual_address,
                        virtual_size: entry.virtual_size,
                    });
                }
            }

            let mut files: Vec<ContextFile> = Vec::new();
            for file in parent.files.iter() {
                if let Some(resource) = file.resource.dup() {
                    files.push(ContextFile {
                        fd: file.fd,
                        resource: resource
                    });
                }
            }

            context_option = Some(box Context {
                stack: stack,
                stack_ptr: (parent.stack_ptr - parent.stack) + stack,
                fx: stack + CONTEXT_STACK_SIZE,
                fx_enabled: parent.fx_enabled,
                memory: mem,
                cwd: parent.cwd.clone(),
                args: parent.args.clone(),
                files: files,
                interrupted: parent.interrupted,
                exited: parent.exited,
            });
        }
    }

    if let Some(context) = context_option {
        contexts.push(context);
    }

    scheduler::end_no_ints(reenable);
}

//TODO: To clean up memory leak, current must be destroyed!
/// Exit context
///
/// Unsafe due to interrupt disabling and raw pointers
pub unsafe fn context_exit() {
    let reenable = scheduler::start_no_ints();

    let contexts = &mut *contexts_ptr;
    if context_enabled && context_i > 1 {
        match contexts.get_mut(context_i) {
            Some(mut current) => current.exited = true,
            None => (),
        }
    }

    scheduler::end_no_ints(reenable);

    context_switch(false);
}

// Currently unused?
/// Reads a Boxed function and executes it
///
/// Unsafe due to raw memory handling and FnBox
pub unsafe extern "cdecl" fn context_box(box_fn_ptr: usize) {
    let box_fn = ptr::read(box_fn_ptr as *mut Box<FnBox()>);
    memory::unalloc(box_fn_ptr);
    box_fn();
}

pub struct ContextMemory {
    pub physical_address: usize,
    pub virtual_address: usize,
    pub virtual_size: usize,
}

pub struct ContextFile {
    pub fd: usize,
    pub resource: Box<Resource>,
}

pub struct Context {
    pub stack: usize,
    pub stack_ptr: usize,
    pub fx: usize,
    pub fx_enabled: bool,
    pub memory: Vec<ContextMemory>,
    pub cwd: String,
    pub args: Vec<String>,
    pub files: Vec<ContextFile>,
    pub interrupted: bool,
    pub exited: bool,
}

impl Context {
    pub unsafe fn root() -> Box<Self> {
        box Context {
            stack: 0,
            stack_ptr: 0,
            fx: memory::alloc(512),
            fx_enabled: false,
            memory: Vec::new(),
            cwd: String::new(),
            args: Vec::new(),
            files: Vec::new(),
            interrupted: false,
            exited: false,
        }
    }

    #[cfg(target_arch = "x86")]
    pub unsafe fn new(call: usize, args: &Vec<usize>) -> Box<Self> {
        let stack = memory::alloc(CONTEXT_STACK_SIZE + 512);

        let mut ret = box Context {
            stack: stack,
            stack_ptr: stack + CONTEXT_STACK_SIZE,
            fx: stack + CONTEXT_STACK_SIZE,
            fx_enabled: false,
            memory: Vec::new(),
            cwd: String::new(),
            args: Vec::new(),
            files: Vec::new(),
            interrupted: false,
            exited: false,
        };

        for arg in args.iter() {
            ret.push(*arg);
        }

        ret.push(call); //We will ret into this function call

        ret.push(1 << 9); //Flags

        ret.push(0); //EAX
        ret.push(0); //ECX
        ret.push(0); //EDX
        ret.push(0); //EBX
        ret.push(0); //ESP (ignored)
        ret.push(0); //EBP
        ret.push(0); //ESI
        ret.push(0); //EDI

        ret
    }

    #[cfg(target_arch = "x86_64")]
    pub unsafe fn new(call: usize, args: &Vec<usize>) -> Box<Self> {
        let stack = memory::alloc(CONTEXT_STACK_SIZE + 512);

        let mut ret = box Context {
            stack: stack,
            stack_ptr: stack + CONTEXT_STACK_SIZE,
            fx: stack + CONTEXT_STACK_SIZE,
            fx_enabled: false,
            memory: Vec::new(),
            cwd: String::new(),
            files: Vec::new(),
            interrupted: false,
            exited: false,
        };

        let mut args_mut = args.clone();

        while args_mut.len() >= 7 {
            if let Some(value) = args_mut.pop() {
                ret.push(value);
            }
        }

        //First six args are in regs
        let r9 = if args_mut.len() >= 6 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let r8 = if args_mut.len() >= 5 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rcx = if args_mut.len() >= 4 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rdx = if args_mut.len() >= 3 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rsi = if args_mut.len() >= 2 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };
        let rdi = if args_mut.len() >= 1 { if let Some(value) = args_mut.pop() { value } else { 0 } } else { 0 };

        ret.push(call); //We will ret into this function call

        ret.push(1 << 9); //Flags

        ret.push(0); //RAX
        ret.push(0); //RBX
        ret.push(rcx); //RCX
        ret.push(rdx); //RDX
        ret.push(rdi); //RDI
        ret.push(rsi); //RSI
        ret.push(r8); //R8
        ret.push(r9); //R9
        ret.push(0); //R10
        ret.push(0); //R11
        ret.push(0); //R12
        ret.push(0); //R13
        ret.push(0); //R14
        ret.push(0); //R15
        ret.push(0); //RBP

        ret
    }

    pub fn spawn(box_fn: Box<FnBox()>) {
        unsafe {
            let box_fn_ptr: *mut Box<FnBox()> = memory::alloc_type();
            ptr::write(box_fn_ptr, box_fn);

            let mut context_box_args: Vec<usize> = Vec::new();
            context_box_args.push(box_fn_ptr as usize);
            context_box_args.push(context_exit as usize);

            let reenable = scheduler::start_no_ints();
            if contexts_ptr as usize > 0 {
                (*contexts_ptr).push(Context::new(context_box as usize, &context_box_args));
            }
            scheduler::end_no_ints(reenable);
        }
    }

    pub unsafe fn push(&mut self, data: usize) {
        self.stack_ptr -= mem::size_of::<usize>();
        ptr::write(self.stack_ptr as *mut usize, data);
    }

    pub unsafe fn map(&mut self) {
        for entry in self.memory.iter() {
            for i in 0..(entry.virtual_size + 4095) / 4096 {
                Page::new(entry.virtual_address + i * 4096).map(entry.physical_address + i * 4096);
            }
        }
    }

    pub unsafe fn unmap(&mut self) {
        for entry in self.memory.iter() {
            for i in 0..(entry.virtual_size + 4095) / 4096 {
                Page::new(entry.virtual_address + i * 4096).map_identity();
            }
        }
    }

    pub unsafe fn remap(&mut self, other: &mut Self) {
        self.unmap();
        other.map();
    }

    //Warning: This function MUST be inspected in disassembly for correct push/pop
    //It should have exactly no extra pushes or pops
    #[cold]
    #[inline(never)]
    //#[naked]
    #[cfg(target_arch = "x86")]
    pub unsafe fn switch(&mut self, other: &mut Self) {
        asm!("pushfd
            pushad
            mov [eax], esp"
            :
            : "{eax}"(&mut self.stack_ptr)
            : "memory"
            : "intel", "volatile");

        asm!("fxsave [eax]"
            :
            : "{eax}"(self.fx)
            : "memory"
            : "intel", "volatile");

        self.fx_enabled = true;

        if other.fx_enabled {
            asm!("fxrstor [eax]"
                :
                : "{eax}"(other.fx)
                : "memory"
                : "intel", "volatile");
        }

        asm!("mov esp, [eax]
            popad
            popfd"
            :
            : "{eax}"(&mut other.stack_ptr)
            : "memory"
            : "intel", "volatile");
    }

    //Warning: This function MUST be inspected in disassembly for correct push/pop
    //It should have no extra pushes or pops
    #[cold]
    #[inline(never)]
    //#[naked]
    #[cfg(target_arch = "x86_64")]
    pub unsafe fn switch(&mut self, other: &mut Self) {
        asm!("pushfq
            push rax
            push rbx
            push rcx
            push rdx
            push rdi
            push rsi
            push r8
            push r9
            push r10
            push r11
            push r12
            push r13
            push r14
            push r15
            push rbp
            mov [rax], rsp"
            :
            : "{rax}"(&mut self.stack_ptr)
            : "memory"
            : "intel", "volatile");

        asm!("fxsave [rax]"
            :
            : "{rax}"(self.fx)
            : "memory"
            : "intel", "volatile");
        self.fx_enabled = true;

        //TODO: Clear registers
        if other.fx_enabled {
            asm!("fxrstor [rax]"
                :
                : "{rax}"(other.fx)
                : "memory"
                : "intel", "volatile");
        }

        asm!("mov rsp, [rax]
            pop rbp
            pop r15
            pop r14
            pop r13
            pop r12
            pop r11
            pop r10
            pop r9
            pop r8
            pop rsi
            pop rdi
            pop rdx
            pop rcx
            pop rbx
            pop rax
            popfq"
            :
            : "{rax}"(&mut other.stack_ptr)
            : "memory"
            : "intel", "volatile");
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        while let Some(file) = self.files.pop() {
            drop(file);
        }

        while let Some(arg) = self.args.pop() {
            drop(arg);
        }

        while let Some(entry) = self.memory.pop() {
            unsafe {
                memory::unalloc(entry.physical_address);
            }
        }

        if self.stack > 0 {
            unsafe {
                memory::unalloc(self.stack);
            }
            self.stack = 0;
        }
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::io::Write;
use std::io::stderr;

/// Print an Error type and its cause recursively
///
/// The error is printed with "Error NNNN :" as prefix, where "NNNN" is a number which increases
/// which each recursion into the errors cause. The error description is used to visualize what
/// failed and if there is a cause "-- caused by:" is appended, and the cause is printed on the next
/// line.
///
/// Example output:
///
/// ```ignore
/// Error    1 : Some error -- caused by:
/// Error    2 : Some other error -- caused by:
/// Error    3 : Yet another Error -- caused by:
/// ...
///
/// Error <NNNN> : <Error description>
/// ```
pub fn trace_error(e: &Error) {
    print_trace_maxdepth(count_error_causes(e), e, ::std::u64::MAX);
    write!(stderr(), "");
}

/// Print an Error type and its cause recursively, but only `max` levels
///
/// Output is the same as for `trace_error()`, though there are only `max` levels printed.
pub fn trace_error_maxdepth(e: &Error, max: u64) {
    let n = count_error_causes(e);
    write!(stderr(), "{}/{} Levels of errors will be printed\n", (if max > n { n } else { max }), n);
    print_trace_maxdepth(n, e, max);
    write!(stderr(), "");
}

/// Print an Error type and its cause recursively with the debug!() macro
///
/// Output is the same as for `trace_error()`.
pub fn trace_error_dbg(e: &Error) {
    print_trace_dbg(0, e);
}

/// Helper function for `trace_error()` and `trace_error_maxdepth()`.
///
/// Returns the cause of the last processed error in the recursion, so `None` if all errors where
/// processed.
fn print_trace_maxdepth(idx: u64, e: &Error, max: u64) -> Option<&Error> {
    if e.cause().is_some() && idx > 0 {
        print_trace_maxdepth(idx - 1, e.cause().unwrap(), max);
        write!(stderr(), " -- caused:\n");
    }
    write!(stderr(), "Error {:>4} : {}", idx, e.description());
    e.cause()
}

/// Count errors in Error::cause() recursively
fn count_error_causes(e: &Error) -> u64 {
    1 + if e.cause().is_some() { count_error_causes(e.cause().unwrap()) } else { 0 }
}

fn print_trace_dbg(idx: u64, e: &Error) {
    debug!("Error {:>4} : {}", idx, e.description());
    if e.cause().is_some() {
        debug!(" -- caused by:");
        print_trace_dbg(idx + 1, e.cause().unwrap());
    }
}

<commit_msg>Add another missing newline character<commit_after>use std::error::Error;
use std::io::Write;
use std::io::stderr;

/// Print an Error type and its cause recursively
///
/// The error is printed with "Error NNNN :" as prefix, where "NNNN" is a number which increases
/// which each recursion into the errors cause. The error description is used to visualize what
/// failed and if there is a cause "-- caused by:" is appended, and the cause is printed on the next
/// line.
///
/// Example output:
///
/// ```ignore
/// Error    1 : Some error -- caused by:
/// Error    2 : Some other error -- caused by:
/// Error    3 : Yet another Error -- caused by:
/// ...
///
/// Error <NNNN> : <Error description>
/// ```
pub fn trace_error(e: &Error) {
    print_trace_maxdepth(count_error_causes(e), e, ::std::u64::MAX);
    write!(stderr(), "\n");
}

/// Print an Error type and its cause recursively, but only `max` levels
///
/// Output is the same as for `trace_error()`, though there are only `max` levels printed.
pub fn trace_error_maxdepth(e: &Error, max: u64) {
    let n = count_error_causes(e);
    write!(stderr(), "{}/{} Levels of errors will be printed\n", (if max > n { n } else { max }), n);
    print_trace_maxdepth(n, e, max);
    write!(stderr(), "");
}

/// Print an Error type and its cause recursively with the debug!() macro
///
/// Output is the same as for `trace_error()`.
pub fn trace_error_dbg(e: &Error) {
    print_trace_dbg(0, e);
}

/// Helper function for `trace_error()` and `trace_error_maxdepth()`.
///
/// Returns the cause of the last processed error in the recursion, so `None` if all errors where
/// processed.
fn print_trace_maxdepth(idx: u64, e: &Error, max: u64) -> Option<&Error> {
    if e.cause().is_some() && idx > 0 {
        match print_trace_maxdepth(idx - 1, e.cause().unwrap(), max) {
            None => write!(stderr(), "\n"),
            Some(_) => write!(stderr(), " -- caused:\n"),
        };
    } else {
        write!(stderr(), "\n");
    }
    write!(stderr(), "Error {:>4} : {}", idx, e.description());
    e.cause()
}

/// Count errors in Error::cause() recursively
fn count_error_causes(e: &Error) -> u64 {
    1 + if e.cause().is_some() { count_error_causes(e.cause().unwrap()) } else { 0 }
}

fn print_trace_dbg(idx: u64, e: &Error) {
    debug!("Error {:>4} : {}", idx, e.description());
    if e.cause().is_some() {
        debug!(" -- caused by:");
        print_trace_dbg(idx + 1, e.cause().unwrap());
    }
}

<|endoftext|>


<commit_before>// Copyright 2012-2013 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.

// The classification code for the x86_64 ABI is taken from the clay language
// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp

#![allow(non_upper_case_globals)]
use self::RegClass::*;

use llvm::{Integer, Pointer, Float, Double};
use llvm::{Struct, Array, Attribute, Vector};
use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute};
use trans::cabi::{ArgType, FnType};
use trans::context::CrateContext;
use trans::type_::Type;

use std::cmp;
use std::iter::repeat;

#[derive(Clone, Copy, PartialEq)]
enum RegClass {
    NoClass,
    Int,
    SSEFs,
    SSEFv,
    SSEDs,
    SSEDv,
    SSEInt,
    /// Data that can appear in the upper half of an SSE register.
    SSEUp,
    X87,
    X87Up,
    ComplexX87,
    Memory
}

trait TypeMethods {
    fn is_reg_ty(&self) -> bool;
}

impl TypeMethods for Type {
    fn is_reg_ty(&self) -> bool {
        match self.kind() {
            Integer | Pointer | Float | Double => true,
            _ => false
        }
    }
}

impl RegClass {
    fn is_sse(&self) -> bool {
        match *self {
            SSEFs | SSEFv | SSEDs | SSEDv => true,
            _ => false
        }
    }
}

trait ClassList {
    fn is_pass_byval(&self) -> bool;
    fn is_ret_bysret(&self) -> bool;
}

impl ClassList for [RegClass] {
    fn is_pass_byval(&self) -> bool {
        if self.len() == 0 { return false; }

        let class = self[0];
           class == Memory
        || class == X87
        || class == ComplexX87
    }

    fn is_ret_bysret(&self) -> bool {
        if self.len() == 0 { return false; }

        self[0] == Memory
    }
}

fn classify_ty(ty: Type) -> Vec<RegClass> {
    fn align(off: uint, ty: Type) -> uint {
        let a = ty_align(ty);
        return (off + a - 1u) / a * a;
    }

    fn ty_align(ty: Type) -> uint {
        match ty.kind() {
            Integer => ((ty.int_width() as uint) + 7) / 8,
            Pointer => 8,
            Float => 4,
            Double => 8,
            Struct => {
              if ty.is_packed() {
                1
              } else {
                let str_tys = ty.field_types();
                str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t)))
              }
            }
            Array => {
                let elt = ty.element_type();
                ty_align(elt)
            }
            Vector => {
                let len = ty.vector_length();
                let elt = ty.element_type();
                ty_align(elt) * len
            }
            _ => panic!("ty_align: unhandled type")
        }
    }

    fn ty_size(ty: Type) -> uint {
        match ty.kind() {
            Integer => (ty.int_width() as uint + 7) / 8,
            Pointer => 8,
            Float => 4,
            Double => 8,
            Struct => {
                let str_tys = ty.field_types();
                if ty.is_packed() {
                    str_tys.iter().fold(0, |s, t| s + ty_size(*t))
                } else {
                    let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
                    align(size, ty)
                }
            }
            Array => {
                let len = ty.array_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                len * eltsz
            }
            Vector => {
                let len = ty.vector_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                len * eltsz
            }

            _ => panic!("ty_size: unhandled type")
        }
    }

    fn all_mem(cls: &mut [RegClass]) {
        for elt in cls.iter_mut() {
            *elt = Memory;
        }
    }

    fn unify(cls: &mut [RegClass],
             i: uint,
             newv: RegClass) {
        if cls[i] == newv { return }

        let to_write = match (cls[i], newv) {
            (NoClass,     _) => newv,
            (_,           NoClass) => return,

            (Memory,      _) |
            (_,           Memory) => Memory,

            (Int,         _) |
            (_,           Int) => Int,

            (X87,         _) |
            (X87Up,       _) |
            (ComplexX87,  _) |
            (_,           X87) |
            (_,           X87Up) |
            (_,           ComplexX87) => Memory,

            (SSEFv,       SSEUp) |
            (SSEFs,       SSEUp) |
            (SSEDv,       SSEUp) |
            (SSEDs,       SSEUp) |
            (SSEInt(_),   SSEUp) => return,

            (_,           _) => newv
        };
        cls[i] = to_write;
    }

    fn classify_struct(tys: &[Type],
                       cls: &mut [RegClass],
                       i: uint,
                       off: uint,
                       packed: bool) {
        let mut field_off = off;
        for ty in tys.iter() {
            if !packed {
                field_off = align(field_off, *ty);
            }
            classify(*ty, cls, i, field_off);
            field_off += ty_size(*ty);
        }
    }

    fn classify(ty: Type,
                cls: &mut [RegClass], ix: uint,
                off: uint) {
        let t_align = ty_align(ty);
        let t_size = ty_size(ty);

        let misalign = off % t_align;
        if misalign != 0u {
            let mut i = off / 8u;
            let e = (off + t_size + 7u) / 8u;
            while i < e {
                unify(cls, ix + i, Memory);
                i += 1u;
            }
            return;
        }

        match ty.kind() {
            Integer |
            Pointer => {
                unify(cls, ix + off / 8u, Int);
            }
            Float => {
                if off % 8u == 4u {
                    unify(cls, ix + off / 8u, SSEFv);
                } else {
                    unify(cls, ix + off / 8u, SSEFs);
                }
            }
            Double => {
                unify(cls, ix + off / 8u, SSEDs);
            }
            Struct => {
                classify_struct(ty.field_types().as_slice(), cls, ix, off, ty.is_packed());
            }
            Array => {
                let len = ty.array_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                let mut i = 0u;
                while i < len {
                    classify(elt, cls, ix, off + i * eltsz);
                    i += 1u;
                }
            }
            Vector => {
                let len = ty.vector_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                let mut reg = match elt.kind() {
                    Integer => SSEInt,
                    Float => SSEFv,
                    Double => SSEDv,
                    _ => panic!("classify: unhandled vector element type")
                };

                let mut i = 0u;
                while i < len {
                    unify(cls, ix + (off + i * eltsz) / 8, reg);

                    // everything after the first one is the upper
                    // half of a register.
                    reg = SSEUp;
                    i += 1u;
                }
            }
            _ => panic!("classify: unhandled type")
        }
    }

    fn fixup(ty: Type, cls: &mut [RegClass]) {
        let mut i = 0u;
        let ty_kind = ty.kind();
        let e = cls.len();
        if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array || ty_kind == Vector) {
            if cls[i].is_sse() {
                i += 1u;
                while i < e {
                    if cls[i] != SSEUp {
                        all_mem(cls);
                        return;
                    }
                    i += 1u;
                }
            } else {
                all_mem(cls);
                return
            }
        } else {
            while i < e {
                if cls[i] == Memory {
                    all_mem(cls);
                    return;
                }
                if cls[i] == X87Up {
                    // for darwin
                    // cls[i] = SSEDs;
                    all_mem(cls);
                    return;
                }
                if cls[i] == SSEUp {
                    cls[i] = SSEDv;
                } else if cls[i].is_sse() {
                    i += 1;
                    while i != e && cls[i] == SSEUp { i += 1u; }
                } else if cls[i] == X87 {
                    i += 1;
                    while i != e && cls[i] == X87Up { i += 1u; }
                } else {
                    i += 1;
                }
            }
        }
    }

    let words = (ty_size(ty) + 7) / 8;
    let mut cls: Vec<_> = repeat(NoClass).take(words).collect();
    if words > 4 {
        all_mem(cls.as_mut_slice());
        return cls;
    }
    classify(ty, cls.as_mut_slice(), 0, 0);
    fixup(ty, cls.as_mut_slice());
    return cls;
}

fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type {
    fn llvec_len(cls: &[RegClass]) -> uint {
        let mut len = 1u;
        for c in cls.iter() {
            if *c != SSEUp {
                break;
            }
            len += 1u;
        }
        return len;
    }

    let mut tys = Vec::new();
    let mut i = 0u;
    let e = cls.len();
    while i < e {
        match cls[i] {
            Int => {
                tys.push(Type::i64(ccx));
            }
            SSEFv | SSEDv | SSEInt => {
                let (elts_per_word, elt_ty) = match cls[i] {
                    SSEFv => (2, Type::f32(ccx)),
                    SSEDv => (1, Type::f64(ccx)),
                    // FIXME: need to handle the element types, since
                    // C doesn't distinguish between the contained
                    // types of the vector at all; normalise to u8,
                    // maybe?
                    SSEInt => panic!("llregtype: SSEInt not yet supported"),
                    _ => unreachable!(),
                };
                let vec_len = llvec_len(&cls[(i + 1u)..]);
                let vec_ty = Type::vector(&elt_ty, (vec_len * elts_per_word) as u64);
                tys.push(vec_ty);
                i += vec_len;
                continue;
            }
            SSEFs => {
                tys.push(Type::f32(ccx));
            }
            SSEDs => {
                tys.push(Type::f64(ccx));
            }
            _ => panic!("llregtype: unhandled class")
        }
        i += 1u;
    }
    if tys.len() == 1 && tys[0].kind() == Vector {
        // if the type contains only a vector, pass it as that vector.
        tys[0]
    } else {
        Type::struct_(ccx, tys.as_slice(), false)
    }
}

pub fn compute_abi_info(ccx: &CrateContext,
                        atys: &[Type],
                        rty: Type,
                        ret_def: bool) -> FnType {
    fn x86_64_ty<F>(ccx: &CrateContext,
                    ty: Type,
                    is_mem_cls: F,
                    ind_attr: Attribute)
                    -> ArgType where
        F: FnOnce(&[RegClass]) -> bool,
    {
        if !ty.is_reg_ty() {
            let cls = classify_ty(ty);
            if is_mem_cls(cls.as_slice()) {
                ArgType::indirect(ty, Some(ind_attr))
            } else {
                ArgType::direct(ty,
                                Some(llreg_ty(ccx, cls.as_slice())),
                                None,
                                None)
            }
        } else {
            let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
            ArgType::direct(ty, None, None, attr)
        }
    }

    let mut arg_tys = Vec::new();
    for t in atys.iter() {
        let ty = x86_64_ty(ccx, *t, |cls| cls.is_pass_byval(), ByValAttribute);
        arg_tys.push(ty);
    }

    let ret_ty = if ret_def {
        x86_64_ty(ccx, rty, |cls| cls.is_ret_bysret(), StructRetAttribute)
    } else {
        ArgType::direct(Type::void(ccx), None, None, None)
    };

    return FnType {
        arg_tys: arg_tys,
        ret_ty: ret_ty,
    };
}
<commit_msg>Support SSE with integer types in x86-64 FFI.<commit_after>// Copyright 2012-2013 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.

// The classification code for the x86_64 ABI is taken from the clay language
// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp

#![allow(non_upper_case_globals)]
use self::RegClass::*;

use llvm::{Integer, Pointer, Float, Double};
use llvm::{Struct, Array, Attribute, Vector};
use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute};
use trans::cabi::{ArgType, FnType};
use trans::context::CrateContext;
use trans::type_::Type;

use std::cmp;
use std::iter::repeat;

#[derive(Clone, Copy, PartialEq)]
enum RegClass {
    NoClass,
    Int,
    SSEFs,
    SSEFv,
    SSEDs,
    SSEDv,
    SSEInt(/* bitwidth */ u64),
    /// Data that can appear in the upper half of an SSE register.
    SSEUp,
    X87,
    X87Up,
    ComplexX87,
    Memory
}

trait TypeMethods {
    fn is_reg_ty(&self) -> bool;
}

impl TypeMethods for Type {
    fn is_reg_ty(&self) -> bool {
        match self.kind() {
            Integer | Pointer | Float | Double => true,
            _ => false
        }
    }
}

impl RegClass {
    fn is_sse(&self) -> bool {
        match *self {
            SSEFs | SSEFv | SSEDs | SSEDv | SSEInt(_) => true,
            _ => false
        }
    }
}

trait ClassList {
    fn is_pass_byval(&self) -> bool;
    fn is_ret_bysret(&self) -> bool;
}

impl ClassList for [RegClass] {
    fn is_pass_byval(&self) -> bool {
        if self.len() == 0 { return false; }

        let class = self[0];
           class == Memory
        || class == X87
        || class == ComplexX87
    }

    fn is_ret_bysret(&self) -> bool {
        if self.len() == 0 { return false; }

        self[0] == Memory
    }
}

fn classify_ty(ty: Type) -> Vec<RegClass> {
    fn align(off: uint, ty: Type) -> uint {
        let a = ty_align(ty);
        return (off + a - 1u) / a * a;
    }

    fn ty_align(ty: Type) -> uint {
        match ty.kind() {
            Integer => ((ty.int_width() as uint) + 7) / 8,
            Pointer => 8,
            Float => 4,
            Double => 8,
            Struct => {
              if ty.is_packed() {
                1
              } else {
                let str_tys = ty.field_types();
                str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t)))
              }
            }
            Array => {
                let elt = ty.element_type();
                ty_align(elt)
            }
            Vector => {
                let len = ty.vector_length();
                let elt = ty.element_type();
                ty_align(elt) * len
            }
            _ => panic!("ty_align: unhandled type")
        }
    }

    fn ty_size(ty: Type) -> uint {
        match ty.kind() {
            Integer => (ty.int_width() as uint + 7) / 8,
            Pointer => 8,
            Float => 4,
            Double => 8,
            Struct => {
                let str_tys = ty.field_types();
                if ty.is_packed() {
                    str_tys.iter().fold(0, |s, t| s + ty_size(*t))
                } else {
                    let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
                    align(size, ty)
                }
            }
            Array => {
                let len = ty.array_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                len * eltsz
            }
            Vector => {
                let len = ty.vector_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                len * eltsz
            }

            _ => panic!("ty_size: unhandled type")
        }
    }

    fn all_mem(cls: &mut [RegClass]) {
        for elt in cls.iter_mut() {
            *elt = Memory;
        }
    }

    fn unify(cls: &mut [RegClass],
             i: uint,
             newv: RegClass) {
        if cls[i] == newv { return }

        let to_write = match (cls[i], newv) {
            (NoClass,     _) => newv,
            (_,           NoClass) => return,

            (Memory,      _) |
            (_,           Memory) => Memory,

            (Int,         _) |
            (_,           Int) => Int,

            (X87,         _) |
            (X87Up,       _) |
            (ComplexX87,  _) |
            (_,           X87) |
            (_,           X87Up) |
            (_,           ComplexX87) => Memory,

            (SSEFv,       SSEUp) |
            (SSEFs,       SSEUp) |
            (SSEDv,       SSEUp) |
            (SSEDs,       SSEUp) |
            (SSEInt(_),   SSEUp) => return,

            (_,           _) => newv
        };
        cls[i] = to_write;
    }

    fn classify_struct(tys: &[Type],
                       cls: &mut [RegClass],
                       i: uint,
                       off: uint,
                       packed: bool) {
        let mut field_off = off;
        for ty in tys.iter() {
            if !packed {
                field_off = align(field_off, *ty);
            }
            classify(*ty, cls, i, field_off);
            field_off += ty_size(*ty);
        }
    }

    fn classify(ty: Type,
                cls: &mut [RegClass], ix: uint,
                off: uint) {
        let t_align = ty_align(ty);
        let t_size = ty_size(ty);

        let misalign = off % t_align;
        if misalign != 0u {
            let mut i = off / 8u;
            let e = (off + t_size + 7u) / 8u;
            while i < e {
                unify(cls, ix + i, Memory);
                i += 1u;
            }
            return;
        }

        match ty.kind() {
            Integer |
            Pointer => {
                unify(cls, ix + off / 8u, Int);
            }
            Float => {
                if off % 8u == 4u {
                    unify(cls, ix + off / 8u, SSEFv);
                } else {
                    unify(cls, ix + off / 8u, SSEFs);
                }
            }
            Double => {
                unify(cls, ix + off / 8u, SSEDs);
            }
            Struct => {
                classify_struct(ty.field_types().as_slice(), cls, ix, off, ty.is_packed());
            }
            Array => {
                let len = ty.array_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                let mut i = 0u;
                while i < len {
                    classify(elt, cls, ix, off + i * eltsz);
                    i += 1u;
                }
            }
            Vector => {
                let len = ty.vector_length();
                let elt = ty.element_type();
                let eltsz = ty_size(elt);
                let mut reg = match elt.kind() {
                    Integer => SSEInt(elt.int_width()),
                    Float => SSEFv,
                    Double => SSEDv,
                    _ => panic!("classify: unhandled vector element type")
                };

                let mut i = 0u;
                while i < len {
                    unify(cls, ix + (off + i * eltsz) / 8, reg);

                    // everything after the first one is the upper
                    // half of a register.
                    reg = SSEUp;
                    i += 1u;
                }
            }
            _ => panic!("classify: unhandled type")
        }
    }

    fn fixup(ty: Type, cls: &mut [RegClass]) {
        let mut i = 0u;
        let ty_kind = ty.kind();
        let e = cls.len();
        if cls.len() > 2u && (ty_kind == Struct || ty_kind == Array || ty_kind == Vector) {
            if cls[i].is_sse() {
                i += 1u;
                while i < e {
                    if cls[i] != SSEUp {
                        all_mem(cls);
                        return;
                    }
                    i += 1u;
                }
            } else {
                all_mem(cls);
                return
            }
        } else {
            while i < e {
                if cls[i] == Memory {
                    all_mem(cls);
                    return;
                }
                if cls[i] == X87Up {
                    // for darwin
                    // cls[i] = SSEDs;
                    all_mem(cls);
                    return;
                }
                if cls[i] == SSEUp {
                    cls[i] = SSEDv;
                } else if cls[i].is_sse() {
                    i += 1;
                    while i != e && cls[i] == SSEUp { i += 1u; }
                } else if cls[i] == X87 {
                    i += 1;
                    while i != e && cls[i] == X87Up { i += 1u; }
                } else {
                    i += 1;
                }
            }
        }
    }

    let words = (ty_size(ty) + 7) / 8;
    let mut cls: Vec<_> = repeat(NoClass).take(words).collect();
    if words > 4 {
        all_mem(cls.as_mut_slice());
        return cls;
    }
    classify(ty, cls.as_mut_slice(), 0, 0);
    fixup(ty, cls.as_mut_slice());
    return cls;
}

fn llreg_ty(ccx: &CrateContext, cls: &[RegClass]) -> Type {
    fn llvec_len(cls: &[RegClass]) -> uint {
        let mut len = 1u;
        for c in cls.iter() {
            if *c != SSEUp {
                break;
            }
            len += 1u;
        }
        return len;
    }

    let mut tys = Vec::new();
    let mut i = 0u;
    let e = cls.len();
    while i < e {
        match cls[i] {
            Int => {
                tys.push(Type::i64(ccx));
            }
            SSEFv | SSEDv | SSEInt(_) => {
                let (elts_per_word, elt_ty) = match cls[i] {
                    SSEFv => (2, Type::f32(ccx)),
                    SSEDv => (1, Type::f64(ccx)),
                    SSEInt(bits) => {
                        assert!(bits == 8 || bits == 16 || bits == 32 || bits == 64,
                                "llreg_ty: unsupported SSEInt width {}", bits);
                        (64 / bits, Type::ix(ccx, bits))
                    }
                    _ => unreachable!(),
                };
                let vec_len = llvec_len(&cls[(i + 1u)..]);
                let vec_ty = Type::vector(&elt_ty, vec_len as u64 * elts_per_word);
                tys.push(vec_ty);
                i += vec_len;
                continue;
            }
            SSEFs => {
                tys.push(Type::f32(ccx));
            }
            SSEDs => {
                tys.push(Type::f64(ccx));
            }
            _ => panic!("llregtype: unhandled class")
        }
        i += 1u;
    }
    if tys.len() == 1 && tys[0].kind() == Vector {
        // if the type contains only a vector, pass it as that vector.
        tys[0]
    } else {
        Type::struct_(ccx, tys.as_slice(), false)
    }
}

pub fn compute_abi_info(ccx: &CrateContext,
                        atys: &[Type],
                        rty: Type,
                        ret_def: bool) -> FnType {
    fn x86_64_ty<F>(ccx: &CrateContext,
                    ty: Type,
                    is_mem_cls: F,
                    ind_attr: Attribute)
                    -> ArgType where
        F: FnOnce(&[RegClass]) -> bool,
    {
        if !ty.is_reg_ty() {
            let cls = classify_ty(ty);
            if is_mem_cls(cls.as_slice()) {
                ArgType::indirect(ty, Some(ind_attr))
            } else {
                ArgType::direct(ty,
                                Some(llreg_ty(ccx, cls.as_slice())),
                                None,
                                None)
            }
        } else {
            let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None };
            ArgType::direct(ty, None, None, attr)
        }
    }

    let mut arg_tys = Vec::new();
    for t in atys.iter() {
        let ty = x86_64_ty(ccx, *t, |cls| cls.is_pass_byval(), ByValAttribute);
        arg_tys.push(ty);
    }

    let ret_ty = if ret_def {
        x86_64_ty(ccx, rty, |cls| cls.is_ret_bysret(), StructRetAttribute)
    } else {
        ArgType::direct(Type::void(ccx), None, None, None)
    };

    return FnType {
        arg_tys: arg_tys,
        ret_ty: ret_ty,
    };
}
<|endoftext|>


<commit_before>use qt_gui::{qt_core::QPoint, QGuiApplication, QWindow};

#[test]
fn window1() {
    QGuiApplication::init(|_| unsafe {
        let mut a = QWindow::new();
        let mut b = QWindow::new_from_parent(a.as_mut_ptr());
        let mut c = QWindow::new_from_parent(b.as_mut_ptr());
        a.set_geometry_4a(10, 10, 300, 300);
        b.set_geometry_4a(20, 20, 200, 200);
        c.set_geometry_4a(40, 40, 100, 100);

        let point = QPoint::new_2a(100, 100);
        let r1 = a.map_to_global(point.as_ref());
        assert_eq!(r1.x(), 110);
        assert_eq!(r1.y(), 110);
        0
    })
}
<commit_msg>update qt_gui test<commit_after>use qt_gui::{qt_core::QPoint, QGuiApplication, QWindow};

#[test]
fn window1() {
    QGuiApplication::init(|_| unsafe {
        let mut a = QWindow::new();
        let mut b = QWindow::from_q_window(&mut a);
        let mut c = QWindow::from_q_window(&mut b);
        a.set_geometry_4a(10, 10, 300, 300);
        b.set_geometry_4a(20, 20, 200, 200);
        c.set_geometry_4a(40, 40, 100, 100);

        let point = QPoint::new_2a(100, 100);
        let r1 = a.map_to_global(&point);
        assert_eq!(r1.x(), 110);
        assert_eq!(r1.y(), 110);
        0
    })
}
<|endoftext|>


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

// `data` comes from components/style/properties.mako.rs; see build.rs for more details.

<%!
    from data import to_rust_ident
%>

use app_units::Au;
% for style_struct in data.style_structs:
%if style_struct.gecko_ffi_name:
use gecko_style_structs::${style_struct.gecko_ffi_name};
use bindings::Gecko_Construct_${style_struct.gecko_ffi_name};
use bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
use bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
% endif
% endfor
use gecko_style_structs;
use heapsize::HeapSizeOf;
use std::fmt::{self, Debug};
use std::mem::zeroed;
use std::sync::Arc;
use style::custom_properties::ComputedValuesMap;
use style::logical_geometry::WritingMode;
use style::properties::{CascadePropertyFn, ServoComputedValues, ComputedValues};
use style::properties::longhands;
use style::properties::make_cascade_vec;
use style::properties::style_struct_traits::*;

#[derive(Clone)]
pub struct GeckoComputedValues {
    % for style_struct in data.style_structs:
    ${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
    % endfor

    custom_properties: Option<Arc<ComputedValuesMap>>,
    shareable: bool,
    pub writing_mode: WritingMode,
    pub root_font_size: Au,
}

impl ComputedValues for GeckoComputedValues {
% for style_struct in data.style_structs:
    type Concrete${style_struct.trait_name} = ${style_struct.gecko_struct_name};
% endfor

    // These will go away, and we will never implement them.
    fn as_servo<'a>(&'a self) -> &'a ServoComputedValues { unimplemented!() }
    fn as_servo_mut<'a>(&'a mut self) -> &'a mut ServoComputedValues { unimplemented!() }

    fn new(custom_properties: Option<Arc<ComputedValuesMap>>,
           shareable: bool,
           writing_mode: WritingMode,
           root_font_size: Au,
            % for style_struct in data.style_structs:
           ${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
            % endfor
    ) -> Self {
        GeckoComputedValues {
            custom_properties: custom_properties,
            shareable: shareable,
            writing_mode: writing_mode,
            root_font_size: root_font_size,
            % for style_struct in data.style_structs:
            ${style_struct.ident}: ${style_struct.ident},
            % endfor
        }
    }

    fn initial_values() -> &'static Self { &*INITIAL_GECKO_VALUES }

    fn do_cascade_property<F: FnOnce(&Vec<Option<CascadePropertyFn<Self>>>)>(f: F) {
        CASCADE_PROPERTY.with(|x| f(x));
    }

    % for style_struct in data.style_structs:
    #[inline]
    fn clone_${style_struct.trait_name_lower}(&self) -> Arc<Self::Concrete${style_struct.trait_name}> {
        self.${style_struct.ident}.clone()
    }
    #[inline]
    fn get_${style_struct.trait_name_lower}<'a>(&'a self) -> &'a Self::Concrete${style_struct.trait_name} {
        &self.${style_struct.ident}
    }
    #[inline]
    fn mutate_${style_struct.trait_name_lower}<'a>(&'a mut self) -> &'a mut Self::Concrete${style_struct.trait_name} {
        Arc::make_mut(&mut self.${style_struct.ident})
    }
    % endfor

    fn custom_properties(&self) -> Option<Arc<ComputedValuesMap>> { self.custom_properties.as_ref().map(|x| x.clone())}
    fn root_font_size(&self) -> Au { self.root_font_size }
    fn set_root_font_size(&mut self, s: Au) { self.root_font_size = s; }
    fn set_writing_mode(&mut self, mode: WritingMode) { self.writing_mode = mode; }

    #[inline]
    fn is_multicol(&self) -> bool { unimplemented!() }
}

<%def name="declare_style_struct(style_struct)">
#[derive(Clone, HeapSizeOf, Debug)]
#[no_move]
% if style_struct.gecko_ffi_name:
pub struct ${style_struct.gecko_struct_name} {
    gecko: ${style_struct.gecko_ffi_name},
}
% else:
pub struct ${style_struct.gecko_struct_name};
% endif
</%def>

<%def name="impl_style_struct(style_struct)">
impl ${style_struct.gecko_struct_name} {
    #[allow(dead_code, unused_variables)]
    fn initial() -> Arc<Self> {
        // Some Gecko style structs have AutoTArray members, which have internal pointers and are
        // thus MOZ_NON_MEMMOVABLE. Since Rust is generally a very move-happy language, we need to
        // be very careful that nsStyle* structs are never moved after they are constructed.
        //
        // By annotating the structs [no_move], we can get the |rust-tenacious| linter to trigger
        // an error on any semantic moves. But we don't have a great way of telling LLVM to
        // allocate our new object directly on the heap without using a temporary. So to do that
        // (and also please tenacious), we pass zeroed memory into the Arc constructor, and _then_
        // use make_mut to get a reference to pass to the Gecko constructor. Since the refcount is
        // guaranteed to be 1, make_mut will always pass us a direct reference instead of taking
        // the copy-on-write path.
        let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: unsafe { zeroed() } });
        unsafe {
            Gecko_Construct_${style_struct.gecko_ffi_name}(&mut Arc::make_mut(&mut result).gecko);
        }
        result
    }
}
%if style_struct.gecko_ffi_name:
impl Drop for ${style_struct.gecko_struct_name} {
    fn drop(&mut self) {
        unsafe {
            Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut self.gecko);
        }
    }
}
impl Clone for ${style_struct.gecko_ffi_name} {
    fn clone(&self) -> Self {
        unsafe {
            let mut result: Self = zeroed();
            Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(&mut result, self);
            result
        }
    }
}
unsafe impl Send for ${style_struct.gecko_ffi_name} {}
unsafe impl Sync for ${style_struct.gecko_ffi_name} {}
impl HeapSizeOf for ${style_struct.gecko_ffi_name} {
    // Not entirely accurate, but good enough for now.
    fn heap_size_of_children(&self) -> usize { 0 }
}
impl Debug for ${style_struct.gecko_ffi_name} {
    // FIXME(bholley): Generate this.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "GECKO STYLE STRUCT")
    }
}
%endif
</%def>

<%def name="raw_impl_trait(style_struct, skip_longhands=None, skip_additionals=None)">
<%
   longhands = [x for x in style_struct.longhands
                if not (skip_longhands and x.name in skip_longhands)]

   #
   # Make a list of types we can't auto-generate.
   #
   force_stub = [];
   # These are currently being shuffled to a different style struct on the gecko side.
   force_stub += ["backface-visibility", "transform-box", "transform-style"]
   # These live in nsStyleImageLayers in gecko. Need to figure out what to do about that.
   force_stub += ["background-repeat", "background-attachment", "background-clip", "background-origin"];
   # These live in an nsFont member in Gecko. Should be straightforward to do manually.
   force_stub += ["font-kerning", "font-stretch", "font-style", "font-variant"]
   # These have unusual representations in gecko.
   force_stub += ["list-style-type", "text-overflow"]
   # Enum class instead of NS_STYLE_...
   force_stub += ["box-sizing"]
   # Inconsistent constant naming in gecko
   force_stub += ["unicode-bidi", "text-transform"]
   # Need to figure out why servo has sideways-left computed value and gecko doesn't
   force_stub += ["text-orientation"]
   # Automatic mapping generates NS_STYLE_TEXT_DECORATION_STYLE__MOZ_NONE instead of
   # NS_STYLE_TEXT_DECORATION_STYLE__NONE
   force_stub += ["text-decoration-style"]
   # These are booleans.
   force_stub += ["page-break-after", "page-break-before"]

   keyword_longhands = [x for x in longhands if x.keyword and not x.name in force_stub]
   stub_longhands = [x for x in longhands if x not in keyword_longhands]
%>
impl ${style_struct.trait_name} for ${style_struct.gecko_struct_name} {
    /*
     * Manually-Implemented Methods.
     */
    ${caller.body().strip()}

    /*
     * Auto-Generated Methods.
     */
    % for longhand in keyword_longhands:
    fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
        use gecko_style_structs as gss;
        use style::properties::longhands::${longhand.ident}::computed_value::T as Keyword;
        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
        self.gecko.${longhand.gecko_ffi_name} = match v {
            % for value in longhand.keyword.values_for('gecko'):
                Keyword::${to_rust_ident(value)} => gss::${longhand.keyword.gecko_constant(value)} as u8,
            % endfor
        };
    }
    fn copy_${longhand.ident}_from(&mut self, other: &Self) {
        self.gecko.${longhand.gecko_ffi_name} = other.gecko.${longhand.gecko_ffi_name};
    }

    %if longhand.need_clone:
    fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
        use gecko_style_structs as gss;
        use style::properties::longhands::${longhand.ident}::computed_value::T as Keyword;
        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
        match self.gecko.${longhand.gecko_ffi_name} as u32 {
            % for value in longhand.keyword.values_for('gecko'):
            gss::${longhand.keyword.gecko_constant(value)} => Keyword::${to_rust_ident(value)},
            % endfor
            x => panic!("Found unexpected value in style struct for ${longhand.name} property: {}", x),
        }
    }

    % endif
    % endfor

    /*
     * Stubs.
     */
    % for longhand in stub_longhands:
    fn set_${longhand.ident}(&mut self, _: longhands::${longhand.ident}::computed_value::T) {
        unimplemented!()
    }
    fn copy_${longhand.ident}_from(&mut self, _: &Self) {
        unimplemented!()
    }
    % if longhand.need_clone:
    fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
        unimplemented!()
    }
    % endif
    % endfor
    <% additionals = [x for x in style_struct.additional_methods
                      if not (skip_additionals and x.name in skip_additionals)] %>
    % for additional in additionals:
    ${additional.stub()}
    % endfor
}
</%def>

<% data.manual_style_structs = [] %>
<%def name="impl_trait(style_struct_name, skip_longhands=None, skip_additionals=None)">
<%self:raw_impl_trait style_struct="${next(x for x in data.style_structs if x.trait_name == style_struct_name)}"
                      skip_longhands="${skip_longhands}" skip_additionals="${skip_additionals}">
${caller.body()}
</%self:raw_impl_trait>
<% data.manual_style_structs.append(style_struct_name) %>
</%def>

// Proof-of-concept for a style struct with some manually-implemented methods. We add
// the manually-implemented methods to skip_longhands and skip_additionals, and the
// infrastructure auto-generates everything not in those lists. This allows us to
// iteratively implement more and more methods.
<%self:impl_trait style_struct_name="Border"
                  skip_longhands="${['border-left-color', 'border-left-style']}"
                  skip_additionals="${['border_bottom_has_nonzero_width']}">
    fn set_border_left_color(&mut self, _: longhands::border_left_color::computed_value::T) {
        unimplemented!()
    }
    fn copy_border_left_color_from(&mut self, _: &Self) {
        unimplemented!()
    }
    fn set_border_left_style(&mut self, _: longhands::border_left_style::computed_value::T) {
        unimplemented!()
    }
    fn copy_border_left_style_from(&mut self, _: &Self) {
        unimplemented!()
    }
    fn clone_border_left_style(&self) -> longhands::border_left_style::computed_value::T {
        unimplemented!()
    }
    fn border_bottom_has_nonzero_width(&self) -> bool {
        unimplemented!()
    }
</%self:impl_trait>

% for style_struct in data.style_structs:
${declare_style_struct(style_struct)}
${impl_style_struct(style_struct)}
% if not style_struct.trait_name in data.manual_style_structs:
<%self:raw_impl_trait style_struct="${style_struct}"></%self:raw_impl_trait>
% endif
% endfor

lazy_static! {
    pub static ref INITIAL_GECKO_VALUES: GeckoComputedValues = GeckoComputedValues {
        % for style_struct in data.style_structs:
           ${style_struct.ident}: ${style_struct.gecko_struct_name}::initial(),
        % endfor
        custom_properties: None,
        shareable: true,
        writing_mode: WritingMode::empty(),
        root_font_size: longhands::font_size::get_initial_value(),
    };
}

// This is a thread-local rather than a lazy static to avoid atomic operations when cascading
// properties.
thread_local!(static CASCADE_PROPERTY: Vec<Option<CascadePropertyFn<GeckoComputedValues>>> = {
    make_cascade_vec::<GeckoComputedValues>()
});
<commit_msg>Use strings rather than arrays for skip_longhands and skip_additionals.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// `data` comes from components/style/properties.mako.rs; see build.rs for more details.

<%!
    from data import to_rust_ident
%>

use app_units::Au;
% for style_struct in data.style_structs:
%if style_struct.gecko_ffi_name:
use gecko_style_structs::${style_struct.gecko_ffi_name};
use bindings::Gecko_Construct_${style_struct.gecko_ffi_name};
use bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
use bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
% endif
% endfor
use gecko_style_structs;
use heapsize::HeapSizeOf;
use std::fmt::{self, Debug};
use std::mem::zeroed;
use std::sync::Arc;
use style::custom_properties::ComputedValuesMap;
use style::logical_geometry::WritingMode;
use style::properties::{CascadePropertyFn, ServoComputedValues, ComputedValues};
use style::properties::longhands;
use style::properties::make_cascade_vec;
use style::properties::style_struct_traits::*;

#[derive(Clone)]
pub struct GeckoComputedValues {
    % for style_struct in data.style_structs:
    ${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
    % endfor

    custom_properties: Option<Arc<ComputedValuesMap>>,
    shareable: bool,
    pub writing_mode: WritingMode,
    pub root_font_size: Au,
}

impl ComputedValues for GeckoComputedValues {
% for style_struct in data.style_structs:
    type Concrete${style_struct.trait_name} = ${style_struct.gecko_struct_name};
% endfor

    // These will go away, and we will never implement them.
    fn as_servo<'a>(&'a self) -> &'a ServoComputedValues { unimplemented!() }
    fn as_servo_mut<'a>(&'a mut self) -> &'a mut ServoComputedValues { unimplemented!() }

    fn new(custom_properties: Option<Arc<ComputedValuesMap>>,
           shareable: bool,
           writing_mode: WritingMode,
           root_font_size: Au,
            % for style_struct in data.style_structs:
           ${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
            % endfor
    ) -> Self {
        GeckoComputedValues {
            custom_properties: custom_properties,
            shareable: shareable,
            writing_mode: writing_mode,
            root_font_size: root_font_size,
            % for style_struct in data.style_structs:
            ${style_struct.ident}: ${style_struct.ident},
            % endfor
        }
    }

    fn initial_values() -> &'static Self { &*INITIAL_GECKO_VALUES }

    fn do_cascade_property<F: FnOnce(&Vec<Option<CascadePropertyFn<Self>>>)>(f: F) {
        CASCADE_PROPERTY.with(|x| f(x));
    }

    % for style_struct in data.style_structs:
    #[inline]
    fn clone_${style_struct.trait_name_lower}(&self) -> Arc<Self::Concrete${style_struct.trait_name}> {
        self.${style_struct.ident}.clone()
    }
    #[inline]
    fn get_${style_struct.trait_name_lower}<'a>(&'a self) -> &'a Self::Concrete${style_struct.trait_name} {
        &self.${style_struct.ident}
    }
    #[inline]
    fn mutate_${style_struct.trait_name_lower}<'a>(&'a mut self) -> &'a mut Self::Concrete${style_struct.trait_name} {
        Arc::make_mut(&mut self.${style_struct.ident})
    }
    % endfor

    fn custom_properties(&self) -> Option<Arc<ComputedValuesMap>> { self.custom_properties.as_ref().map(|x| x.clone())}
    fn root_font_size(&self) -> Au { self.root_font_size }
    fn set_root_font_size(&mut self, s: Au) { self.root_font_size = s; }
    fn set_writing_mode(&mut self, mode: WritingMode) { self.writing_mode = mode; }

    #[inline]
    fn is_multicol(&self) -> bool { unimplemented!() }
}

<%def name="declare_style_struct(style_struct)">
#[derive(Clone, HeapSizeOf, Debug)]
#[no_move]
% if style_struct.gecko_ffi_name:
pub struct ${style_struct.gecko_struct_name} {
    gecko: ${style_struct.gecko_ffi_name},
}
% else:
pub struct ${style_struct.gecko_struct_name};
% endif
</%def>

<%def name="impl_style_struct(style_struct)">
impl ${style_struct.gecko_struct_name} {
    #[allow(dead_code, unused_variables)]
    fn initial() -> Arc<Self> {
        // Some Gecko style structs have AutoTArray members, which have internal pointers and are
        // thus MOZ_NON_MEMMOVABLE. Since Rust is generally a very move-happy language, we need to
        // be very careful that nsStyle* structs are never moved after they are constructed.
        //
        // By annotating the structs [no_move], we can get the |rust-tenacious| linter to trigger
        // an error on any semantic moves. But we don't have a great way of telling LLVM to
        // allocate our new object directly on the heap without using a temporary. So to do that
        // (and also please tenacious), we pass zeroed memory into the Arc constructor, and _then_
        // use make_mut to get a reference to pass to the Gecko constructor. Since the refcount is
        // guaranteed to be 1, make_mut will always pass us a direct reference instead of taking
        // the copy-on-write path.
        let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: unsafe { zeroed() } });
        unsafe {
            Gecko_Construct_${style_struct.gecko_ffi_name}(&mut Arc::make_mut(&mut result).gecko);
        }
        result
    }
}
%if style_struct.gecko_ffi_name:
impl Drop for ${style_struct.gecko_struct_name} {
    fn drop(&mut self) {
        unsafe {
            Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut self.gecko);
        }
    }
}
impl Clone for ${style_struct.gecko_ffi_name} {
    fn clone(&self) -> Self {
        unsafe {
            let mut result: Self = zeroed();
            Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(&mut result, self);
            result
        }
    }
}
unsafe impl Send for ${style_struct.gecko_ffi_name} {}
unsafe impl Sync for ${style_struct.gecko_ffi_name} {}
impl HeapSizeOf for ${style_struct.gecko_ffi_name} {
    // Not entirely accurate, but good enough for now.
    fn heap_size_of_children(&self) -> usize { 0 }
}
impl Debug for ${style_struct.gecko_ffi_name} {
    // FIXME(bholley): Generate this.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "GECKO STYLE STRUCT")
    }
}
%endif
</%def>

<%def name="raw_impl_trait(style_struct, skip_longhands='', skip_additionals='')">
<%
   longhands = [x for x in style_struct.longhands if not x.name in skip_longhands.split()]

   #
   # Make a list of types we can't auto-generate.
   #
   force_stub = [];
   # These are currently being shuffled to a different style struct on the gecko side.
   force_stub += ["backface-visibility", "transform-box", "transform-style"]
   # These live in nsStyleImageLayers in gecko. Need to figure out what to do about that.
   force_stub += ["background-repeat", "background-attachment", "background-clip", "background-origin"];
   # These live in an nsFont member in Gecko. Should be straightforward to do manually.
   force_stub += ["font-kerning", "font-stretch", "font-style", "font-variant"]
   # These have unusual representations in gecko.
   force_stub += ["list-style-type", "text-overflow"]
   # Enum class instead of NS_STYLE_...
   force_stub += ["box-sizing"]
   # Inconsistent constant naming in gecko
   force_stub += ["unicode-bidi", "text-transform"]
   # Need to figure out why servo has sideways-left computed value and gecko doesn't
   force_stub += ["text-orientation"]
   # Automatic mapping generates NS_STYLE_TEXT_DECORATION_STYLE__MOZ_NONE instead of
   # NS_STYLE_TEXT_DECORATION_STYLE__NONE
   force_stub += ["text-decoration-style"]
   # These are booleans.
   force_stub += ["page-break-after", "page-break-before"]

   keyword_longhands = [x for x in longhands if x.keyword and not x.name in force_stub]
   stub_longhands = [x for x in longhands if x not in keyword_longhands]
%>
impl ${style_struct.trait_name} for ${style_struct.gecko_struct_name} {
    /*
     * Manually-Implemented Methods.
     */
    ${caller.body().strip()}

    /*
     * Auto-Generated Methods.
     */
    % for longhand in keyword_longhands:
    fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
        use gecko_style_structs as gss;
        use style::properties::longhands::${longhand.ident}::computed_value::T as Keyword;
        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
        self.gecko.${longhand.gecko_ffi_name} = match v {
            % for value in longhand.keyword.values_for('gecko'):
                Keyword::${to_rust_ident(value)} => gss::${longhand.keyword.gecko_constant(value)} as u8,
            % endfor
        };
    }
    fn copy_${longhand.ident}_from(&mut self, other: &Self) {
        self.gecko.${longhand.gecko_ffi_name} = other.gecko.${longhand.gecko_ffi_name};
    }

    %if longhand.need_clone:
    fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
        use gecko_style_structs as gss;
        use style::properties::longhands::${longhand.ident}::computed_value::T as Keyword;
        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
        match self.gecko.${longhand.gecko_ffi_name} as u32 {
            % for value in longhand.keyword.values_for('gecko'):
            gss::${longhand.keyword.gecko_constant(value)} => Keyword::${to_rust_ident(value)},
            % endfor
            x => panic!("Found unexpected value in style struct for ${longhand.name} property: {}", x),
        }
    }

    % endif
    % endfor

    /*
     * Stubs.
     */
    % for longhand in stub_longhands:
    fn set_${longhand.ident}(&mut self, _: longhands::${longhand.ident}::computed_value::T) {
        unimplemented!()
    }
    fn copy_${longhand.ident}_from(&mut self, _: &Self) {
        unimplemented!()
    }
    % if longhand.need_clone:
    fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
        unimplemented!()
    }
    % endif
    % endfor
    <% additionals = [x for x in style_struct.additional_methods
                      if not x.name in skip_additionals.split()] %>
    % for additional in additionals:
    ${additional.stub()}
    % endfor
}
</%def>

<% data.manual_style_structs = [] %>
<%def name="impl_trait(style_struct_name, skip_longhands='', skip_additionals='')">
<%self:raw_impl_trait style_struct="${next(x for x in data.style_structs if x.trait_name == style_struct_name)}"
                      skip_longhands="${skip_longhands}" skip_additionals="${skip_additionals}">
${caller.body()}
</%self:raw_impl_trait>
<% data.manual_style_structs.append(style_struct_name) %>
</%def>

// Proof-of-concept for a style struct with some manually-implemented methods. We add
// the manually-implemented methods to skip_longhands and skip_additionals, and the
// infrastructure auto-generates everything not in those lists. This allows us to
// iteratively implement more and more methods.
<%self:impl_trait style_struct_name="Border"
                  skip_longhands="border-left-color border-left-style"
                  skip_additionals="border_bottom_has_nonzero_width">
    fn set_border_left_color(&mut self, _: longhands::border_left_color::computed_value::T) {
        unimplemented!()
    }
    fn copy_border_left_color_from(&mut self, _: &Self) {
        unimplemented!()
    }
    fn set_border_left_style(&mut self, _: longhands::border_left_style::computed_value::T) {
        unimplemented!()
    }
    fn copy_border_left_style_from(&mut self, _: &Self) {
        unimplemented!()
    }
    fn clone_border_left_style(&self) -> longhands::border_left_style::computed_value::T {
        unimplemented!()
    }
    fn border_bottom_has_nonzero_width(&self) -> bool {
        unimplemented!()
    }
</%self:impl_trait>

% for style_struct in data.style_structs:
${declare_style_struct(style_struct)}
${impl_style_struct(style_struct)}
% if not style_struct.trait_name in data.manual_style_structs:
<%self:raw_impl_trait style_struct="${style_struct}"></%self:raw_impl_trait>
% endif
% endfor

lazy_static! {
    pub static ref INITIAL_GECKO_VALUES: GeckoComputedValues = GeckoComputedValues {
        % for style_struct in data.style_structs:
           ${style_struct.ident}: ${style_struct.gecko_struct_name}::initial(),
        % endfor
        custom_properties: None,
        shareable: true,
        writing_mode: WritingMode::empty(),
        root_font_size: longhands::font_size::get_initial_value(),
    };
}

// This is a thread-local rather than a lazy static to avoid atomic operations when cascading
// properties.
thread_local!(static CASCADE_PROPERTY: Vec<Option<CascadePropertyFn<GeckoComputedValues>>> = {
    make_cascade_vec::<GeckoComputedValues>()
});
<|endoftext|>