<commit_before>use parking_lot::MutexGuard;
use std::fmt;

use ctxt::get_vm;
use gc::root::Slot;
use gc::space::Space;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{CrossingEntry, CrossingMap};
use gc::swiper::large::LargeSpace;
use gc::swiper::old::{OldGen, OldGenProtected};
use gc::swiper::on_different_cards;
use gc::swiper::young::YoungGen;
use gc::swiper::{CARD_REFS, CARD_SIZE};
use gc::{Address, Region};

use mem;
use object::{offset_of_array_data, Obj};

#[derive(Copy, Clone)]
pub enum VerifierPhase {
    PreMinor,
    PostMinor,
    PreFull,
    PostFull,
}

impl VerifierPhase {
    fn is_pre(self) -> bool {
        match self {
            VerifierPhase::PreMinor => true,
            VerifierPhase::PostMinor => false,
            VerifierPhase::PreFull => true,
            VerifierPhase::PostFull => false,
        }
    }

    fn is_post_minor(self) -> bool {
        match self {
            VerifierPhase::PostMinor => true,
            _ => false,
        }
    }

    fn is_pre_full(self) -> bool {
        match self {
            VerifierPhase::PreFull => true,
            _ => false,
        }
    }
}

impl fmt::Display for VerifierPhase {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let text = match self {
            VerifierPhase::PreMinor => "pre minor",
            VerifierPhase::PostMinor => "pre full",
            VerifierPhase::PreFull => "pre full",
            VerifierPhase::PostFull => "post full",
        };

        write!(f, "{}", text)
    }
}

pub struct Verifier<'a> {
    young: &'a YoungGen,
    old: &'a OldGen,
    old_protected: MutexGuard<'a, OldGenProtected>,
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    rootset: &'a [Slot],
    large: &'a LargeSpace,
    perm_space: &'a Space,

    refs_to_young_gen: usize,
    in_old: bool,
    in_large: bool,

    young_total: Region,
    eden_active: Region,
    from_active: Region,
    to_active: Region,
    reserved_area: Region,

    phase: VerifierPhase,
    promotion_failed: bool,
}

impl<'a> Verifier<'a> {
    pub fn new(
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [Slot],
        large: &'a LargeSpace,
        perm_space: &'a Space,
        reserved_area: Region,
        phase: VerifierPhase,
        promotion_failed: bool,
    ) -> Verifier<'a> {
        let old_protected = old.protected();

        Verifier {
            young: young,
            old: old,
            old_protected: old_protected,
            card_table: card_table,
            crossing_map: crossing_map,
            rootset: rootset,
            perm_space: perm_space,
            large: large,

            refs_to_young_gen: 0,
            in_old: false,
            in_large: false,

            eden_active: young.eden_active(),
            from_active: young.from_active(),
            to_active: young.to_active(),
            young_total: young.total(),
            reserved_area: reserved_area,

            phase: phase,
            promotion_failed: promotion_failed,
        }
    }

    pub fn verify(&mut self) {
        self.verify_roots();
        self.verify_young();
        self.verify_old();
        self.verify_large();
    }

    fn verify_young(&mut self) {
        let region = self.young.eden_active();
        self.verify_objects(region, "young gen (eden)");

        let region = self.from_active.clone();
        if !self.promotion_failed {
            assert!(region.empty(), "from-space should be empty.");
        }
        self.verify_objects(region, "young gen (from)");

        let region = self.to_active.clone();
        self.verify_objects(region, "young gen (to)");
    }

    fn verify_old(&mut self) {
        self.in_old = true;
        let old_regions = self.old_protected.regions.clone();
        let mut last = self.old.total().start;
        for old_region in old_regions {
            let region_total = old_region.total_region();
            assert_eq!(region_total.start, last);
            self.verify_objects(old_region.active_region(), "old gen");
            last = region_total.end;
        }
        assert_eq!(self.old.total().end, last);
        self.in_old = false;
    }

    fn verify_large(&mut self) {
        self.in_large = true;
        self.large.visit_objects(|addr| {
            let object = addr.to_obj();
            let region = Region::new(addr, addr.offset(object.size()));
            self.verify_objects(region, "large space");
        });
        self.in_large = false;
    }

    fn verify_roots(&mut self) {
        for root in self.rootset {
            self.verify_reference(*root, Address::null(), "root set");
        }
    }

    fn verify_objects(&mut self, region: Region, name: &str) {
        let mut curr = region.start;
        let mut last_null = false;
        self.refs_to_young_gen = 0;

        while curr < region.end {
            let object = curr.to_mut_obj();

            if object.header().vtblptr().is_null() {
                let next = curr.add_ptr(1);

                if self.in_old && on_different_cards(curr, next) {
                    self.verify_card(curr, region.start);
                    self.verify_crossing(curr, next, false);
                }

                assert!(!last_null, "there should not be nulls directly after each other");

                curr = next;
                last_null = true;
                continue;
            } else {
                last_null = false;
            }

            let next = if object.is_array_ref() {
                self.verify_array_ref(object, curr, region.start, name)
            } else {
                self.verify_object(object, curr, region.start, name)
            };

            curr = next;
        }

        assert!(curr == region.end, "object doesn't end at region end");

        if (self.in_old || self.in_large) && !self.card_table.is_aligned(curr) {
            self.verify_card(curr, region.start);
        }
    }

    fn verify_array_ref(
        &mut self,
        object: &mut Obj,
        mut curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        let object_address = curr;

        object.visit_reference_fields(|element| {
            if (self.in_old || self.in_large) && on_different_cards(curr, element.address()) {
                self.verify_card(curr, start);
                curr = element.address();
            }

            self.verify_reference(element, object_address, name);
        });

        let next = object_address.offset(object.size());

        if self.in_old && on_different_cards(object_address, next) {
            self.verify_crossing(object_address, next, true);
        }

        next
    }

    fn verify_object(
        &mut self,
        object: &mut Obj,
        curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        object.visit_reference_fields(|child| {
            self.verify_reference(child, curr, name);
        });

        let next = curr.offset(object.size());

        if (self.in_old || self.in_large) && on_different_cards(curr, next) {
            self.verify_card(curr, start);
            if self.in_old {
                self.verify_crossing(curr, next, false);
            }
        }

        next
    }

    fn verify_card(&mut self, curr: Address, start: Address) {
        let curr_card = self.card_table.card_idx(curr);

        let expected_card_entry = if self.refs_to_young_gen > 0 {
            CardEntry::Dirty
        } else {
            CardEntry::Clean
        };

        let actual_card_entry = self.card_table.get(curr_card);

        // In the verify-phase before the collection the card's dirty-entry isn't
        // guaranteed to be exact. It could be `dirty` although this card doesn't
        // actually contain any references into the young generation. But it should never
        // be clean when there are actual references into the young generation.
        if self.phase.is_pre() && expected_card_entry == CardEntry::Clean {
            self.refs_to_young_gen = 0;
            return;
        }

        // The first card in an old region can't be cleaned if it is not card aligned.
        // Therefore this card could be dirty when it should actually be clean, but this is
        // allowed. Nevertheless it shouldn't be clean when it actually contains references
        // into the young generation.
        if curr_card == self.card_table.card_idx(start)
            && !start.is_card_aligned()
            && expected_card_entry == CardEntry::Clean
        {
            self.refs_to_young_gen = 0;
            return;
        }

        if actual_card_entry != expected_card_entry {
            let card_text = match actual_card_entry {
                CardEntry::Dirty => "dirty",
                CardEntry::Clean => "clean",
            };

            let card_start = self.card_table.to_address(curr_card);
            let card_end = card_start.offset(CARD_SIZE);

            println!(
                "CARD: {} ({}-{}) is marked {} but has {} reference(s).",
                curr_card.to_usize(),
                card_start,
                card_end,
                card_text,
                self.refs_to_young_gen
            );

            panic!("card table entry wrong.");
        }

        assert!(actual_card_entry == expected_card_entry);

        self.refs_to_young_gen = 0;
    }

    fn verify_crossing(&mut self, old: Address, new: Address, array_ref: bool) {
        let new_card_idx = self.card_table.card_idx(new);
        let old_card_idx = self.card_table.card_idx(old);

        if new_card_idx == old_card_idx {
            return;
        }

        let new_card_start = self.card_table.to_address(new_card_idx);
        let old_card_end = self.card_table.to_address(old_card_idx).offset(CARD_SIZE);

        let offset = new.offset_from(new_card_start);
        let offset_words = (offset / mem::ptr_width_usize()) as u8;

        let crossing_middle;
        let loop_start;

        if array_ref {
            crossing_middle = CrossingEntry::LeadingRefs(CARD_REFS as u8);

            if old.offset(offset_of_array_data() as usize) > old_card_end {
                let old_next = old_card_idx.to_usize() + 1;
                let crossing = self.crossing_map.get(old_next.into());
                let diff_words = old_card_end.offset_from(old) / mem::ptr_width_usize();
                assert!(crossing == CrossingEntry::ArrayStart(diff_words as u8));

                loop_start = old_card_idx.to_usize() + 2;
            } else {
                loop_start = old_card_idx.to_usize() + 1;
            }

            if new_card_idx.to_usize() >= loop_start {
                let crossing = self.crossing_map.get(new_card_idx);
                let expected = if offset_words > 0 {
                    CrossingEntry::LeadingRefs(offset_words)
                } else {
                    CrossingEntry::FirstObject(0)
                };
                assert!(crossing == expected, "array crossing at end not correct.");
            }
        } else {
            crossing_middle = CrossingEntry::NoRefs;
            loop_start = old_card_idx.to_usize() + 1;

            let crossing = self.crossing_map.get(new_card_idx);
            let expected = CrossingEntry::FirstObject(offset_words);
            assert!(crossing == expected, "crossing at end not correct.");
        }

        for c in loop_start..new_card_idx.to_usize() {
            assert!(
                self.crossing_map.get(c.into()) == crossing_middle,
                "middle crossing not correct."
            );
        }
    }

    fn verify_reference(&mut self, slot: Slot, container_obj: Address, name: &str) {
        let reference = slot.get();

        if reference.is_null() {
            return;
        }

        if self.old_protected.contains_slow(reference)
            || self.eden_active.contains(reference)
            || self.to_active.contains(reference)
            || self.perm_space.contains(reference)
            || self.large.contains(reference)
            || (self.from_active.contains(reference) && self.promotion_failed)
        {
            let object = reference.to_obj();

            // Verify that the address is the start of an object,
            // for this access its size.
            // To make sure this isn't optimized out by the compiler,
            // make sure that the size doesn't equal 1.
            assert!(object.size() != 1, "object size shouldn't be 1");

            if self.young_total.contains(reference) {
                self.refs_to_young_gen += 1;
            }

            return;
        }

        let perm_region = self.perm_space.used_region();

        println!(
            "PRM: {}; active: {} (size 0x{:x})",
            self.perm_space.total(),
            perm_region,
            perm_region.size(),
        );
        println!(
            "EDN: {}; active: {} (size 0x{:x})",
            self.young.eden_total(),
            self.eden_active,
            self.eden_active.size(),
        );
        println!(
            "FRM: {}; active: {} (size 0x{:x})",
            self.young.from_total(),
            self.from_active,
            self.from_active.size(),
        );
        println!(
            " TO: {}; active: {} (size 0x{:x})",
            self.young.to_total(),
            self.to_active,
            self.to_active.size(),
        );
        {
            println!(
                "OLD total: {} (size 0x{:x})",
                self.old.total(),
                self.old.total().size()
            );
            for old_region in &self.old_protected.regions {
                println!(
                    "OLD region: {}; active: {} (size 0x{:x})",
                    old_region.committed_region(),
                    old_region.active_region(),
                    old_region.active_size(),
                );
            }
        }

        println!(
            "LRG: {}-{}",
            self.large.total().start,
            self.large.total().end
        );
        println!(
            "TTL: {}-{}",
            self.reserved_area.start, self.reserved_area.end
        );
        println!(
            "found invalid reference to {} in {} (at {}, in object {}) during {} phase.",
            reference,
            name,
            slot.address(),
            container_obj,
            self.phase,
        );

        if self.young.contains(reference)
            && !self.to_active.contains(reference)
            && !self.eden_active.contains(reference)
        {
            println!("reference points into young generation but not into the active semi-space.");

            if self.young.eden_total().contains(reference) {
                println!("\treference points into eden-space");
            }

            if self.young.from_total().contains(reference) {
                println!("\treference points into from-space");
            }

            if self.young.to_total().contains(reference) {
                println!("\treference points into to-space");
            }
        }

        println!("try print object size and class:");

        let object = reference.to_obj();
        println!("\tsize {}", object.size());
        let cls = object.header().vtbl().class();
        println!("\tclass {}", cls.name(get_vm()));

        panic!("reference neither pointing into young nor old generation.");
    }
}
<commit_msg>reformat code<commit_after>use parking_lot::MutexGuard;
use std::fmt;

use ctxt::get_vm;
use gc::root::Slot;
use gc::space::Space;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{CrossingEntry, CrossingMap};
use gc::swiper::large::LargeSpace;
use gc::swiper::old::{OldGen, OldGenProtected};
use gc::swiper::on_different_cards;
use gc::swiper::young::YoungGen;
use gc::swiper::{CARD_REFS, CARD_SIZE};
use gc::{Address, Region};

use mem;
use object::{offset_of_array_data, Obj};

#[derive(Copy, Clone)]
pub enum VerifierPhase {
    PreMinor,
    PostMinor,
    PreFull,
    PostFull,
}

impl VerifierPhase {
    fn is_pre(self) -> bool {
        match self {
            VerifierPhase::PreMinor => true,
            VerifierPhase::PostMinor => false,
            VerifierPhase::PreFull => true,
            VerifierPhase::PostFull => false,
        }
    }

    fn is_post_minor(self) -> bool {
        match self {
            VerifierPhase::PostMinor => true,
            _ => false,
        }
    }

    fn is_pre_full(self) -> bool {
        match self {
            VerifierPhase::PreFull => true,
            _ => false,
        }
    }
}

impl fmt::Display for VerifierPhase {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let text = match self {
            VerifierPhase::PreMinor => "pre minor",
            VerifierPhase::PostMinor => "pre full",
            VerifierPhase::PreFull => "pre full",
            VerifierPhase::PostFull => "post full",
        };

        write!(f, "{}", text)
    }
}

pub struct Verifier<'a> {
    young: &'a YoungGen,
    old: &'a OldGen,
    old_protected: MutexGuard<'a, OldGenProtected>,
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    rootset: &'a [Slot],
    large: &'a LargeSpace,
    perm_space: &'a Space,

    refs_to_young_gen: usize,
    in_old: bool,
    in_large: bool,

    young_total: Region,
    eden_active: Region,
    from_active: Region,
    to_active: Region,
    reserved_area: Region,

    phase: VerifierPhase,
    promotion_failed: bool,
}

impl<'a> Verifier<'a> {
    pub fn new(
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [Slot],
        large: &'a LargeSpace,
        perm_space: &'a Space,
        reserved_area: Region,
        phase: VerifierPhase,
        promotion_failed: bool,
    ) -> Verifier<'a> {
        let old_protected = old.protected();

        Verifier {
            young: young,
            old: old,
            old_protected: old_protected,
            card_table: card_table,
            crossing_map: crossing_map,
            rootset: rootset,
            perm_space: perm_space,
            large: large,

            refs_to_young_gen: 0,
            in_old: false,
            in_large: false,

            eden_active: young.eden_active(),
            from_active: young.from_active(),
            to_active: young.to_active(),
            young_total: young.total(),
            reserved_area: reserved_area,

            phase: phase,
            promotion_failed: promotion_failed,
        }
    }

    pub fn verify(&mut self) {
        self.verify_roots();
        self.verify_young();
        self.verify_old();
        self.verify_large();
    }

    fn verify_young(&mut self) {
        let region = self.young.eden_active();
        self.verify_objects(region, "young gen (eden)");

        let region = self.from_active.clone();
        if !self.promotion_failed {
            assert!(region.empty(), "from-space should be empty.");
        }
        self.verify_objects(region, "young gen (from)");

        let region = self.to_active.clone();
        self.verify_objects(region, "young gen (to)");
    }

    fn verify_old(&mut self) {
        self.in_old = true;
        let old_regions = self.old_protected.regions.clone();
        let mut last = self.old.total().start;
        for old_region in old_regions {
            let region_total = old_region.total_region();
            assert_eq!(region_total.start, last);
            self.verify_objects(old_region.active_region(), "old gen");
            last = region_total.end;
        }
        assert_eq!(self.old.total().end, last);
        self.in_old = false;
    }

    fn verify_large(&mut self) {
        self.in_large = true;
        self.large.visit_objects(|addr| {
            let object = addr.to_obj();
            let region = Region::new(addr, addr.offset(object.size()));
            self.verify_objects(region, "large space");
        });
        self.in_large = false;
    }

    fn verify_roots(&mut self) {
        for root in self.rootset {
            self.verify_reference(*root, Address::null(), "root set");
        }
    }

    fn verify_objects(&mut self, region: Region, name: &str) {
        let mut curr = region.start;
        let mut last_null = false;
        self.refs_to_young_gen = 0;

        while curr < region.end {
            let object = curr.to_mut_obj();

            if object.header().vtblptr().is_null() {
                let next = curr.add_ptr(1);

                if self.in_old && on_different_cards(curr, next) {
                    self.verify_card(curr, region.start);
                    self.verify_crossing(curr, next, false);
                }

                assert!(
                    !last_null,
                    "there should not be nulls directly after each other"
                );

                curr = next;
                last_null = true;
                continue;
            } else {
                last_null = false;
            }

            let next = if object.is_array_ref() {
                self.verify_array_ref(object, curr, region.start, name)
            } else {
                self.verify_object(object, curr, region.start, name)
            };

            curr = next;
        }

        assert!(curr == region.end, "object doesn't end at region end");

        if (self.in_old || self.in_large) && !self.card_table.is_aligned(curr) {
            self.verify_card(curr, region.start);
        }
    }

    fn verify_array_ref(
        &mut self,
        object: &mut Obj,
        mut curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        let object_address = curr;

        object.visit_reference_fields(|element| {
            if (self.in_old || self.in_large) && on_different_cards(curr, element.address()) {
                self.verify_card(curr, start);
                curr = element.address();
            }

            self.verify_reference(element, object_address, name);
        });

        let next = object_address.offset(object.size());

        if self.in_old && on_different_cards(object_address, next) {
            self.verify_crossing(object_address, next, true);
        }

        next
    }

    fn verify_object(
        &mut self,
        object: &mut Obj,
        curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        object.visit_reference_fields(|child| {
            self.verify_reference(child, curr, name);
        });

        let next = curr.offset(object.size());

        if (self.in_old || self.in_large) && on_different_cards(curr, next) {
            self.verify_card(curr, start);
            if self.in_old {
                self.verify_crossing(curr, next, false);
            }
        }

        next
    }

    fn verify_card(&mut self, curr: Address, start: Address) {
        let curr_card = self.card_table.card_idx(curr);

        let expected_card_entry = if self.refs_to_young_gen > 0 {
            CardEntry::Dirty
        } else {
            CardEntry::Clean
        };

        let actual_card_entry = self.card_table.get(curr_card);

        // In the verify-phase before the collection the card's dirty-entry isn't
        // guaranteed to be exact. It could be `dirty` although this card doesn't
        // actually contain any references into the young generation. But it should never
        // be clean when there are actual references into the young generation.
        if self.phase.is_pre() && expected_card_entry == CardEntry::Clean {
            self.refs_to_young_gen = 0;
            return;
        }

        // The first card in an old region can't be cleaned if it is not card aligned.
        // Therefore this card could be dirty when it should actually be clean, but this is
        // allowed. Nevertheless it shouldn't be clean when it actually contains references
        // into the young generation.
        if curr_card == self.card_table.card_idx(start)
            && !start.is_card_aligned()
            && expected_card_entry == CardEntry::Clean
        {
            self.refs_to_young_gen = 0;
            return;
        }

        if actual_card_entry != expected_card_entry {
            let card_text = match actual_card_entry {
                CardEntry::Dirty => "dirty",
                CardEntry::Clean => "clean",
            };

            let card_start = self.card_table.to_address(curr_card);
            let card_end = card_start.offset(CARD_SIZE);

            println!(
                "CARD: {} ({}-{}) is marked {} but has {} reference(s).",
                curr_card.to_usize(),
                card_start,
                card_end,
                card_text,
                self.refs_to_young_gen
            );

            panic!("card table entry wrong.");
        }

        assert!(actual_card_entry == expected_card_entry);

        self.refs_to_young_gen = 0;
    }

    fn verify_crossing(&mut self, old: Address, new: Address, array_ref: bool) {
        let new_card_idx = self.card_table.card_idx(new);
        let old_card_idx = self.card_table.card_idx(old);

        if new_card_idx == old_card_idx {
            return;
        }

        let new_card_start = self.card_table.to_address(new_card_idx);
        let old_card_end = self.card_table.to_address(old_card_idx).offset(CARD_SIZE);

        let offset = new.offset_from(new_card_start);
        let offset_words = (offset / mem::ptr_width_usize()) as u8;

        let crossing_middle;
        let loop_start;

        if array_ref {
            crossing_middle = CrossingEntry::LeadingRefs(CARD_REFS as u8);

            if old.offset(offset_of_array_data() as usize) > old_card_end {
                let old_next = old_card_idx.to_usize() + 1;
                let crossing = self.crossing_map.get(old_next.into());
                let diff_words = old_card_end.offset_from(old) / mem::ptr_width_usize();
                assert!(crossing == CrossingEntry::ArrayStart(diff_words as u8));

                loop_start = old_card_idx.to_usize() + 2;
            } else {
                loop_start = old_card_idx.to_usize() + 1;
            }

            if new_card_idx.to_usize() >= loop_start {
                let crossing = self.crossing_map.get(new_card_idx);
                let expected = if offset_words > 0 {
                    CrossingEntry::LeadingRefs(offset_words)
                } else {
                    CrossingEntry::FirstObject(0)
                };
                assert!(crossing == expected, "array crossing at end not correct.");
            }
        } else {
            crossing_middle = CrossingEntry::NoRefs;
            loop_start = old_card_idx.to_usize() + 1;

            let crossing = self.crossing_map.get(new_card_idx);
            let expected = CrossingEntry::FirstObject(offset_words);
            assert!(crossing == expected, "crossing at end not correct.");
        }

        for c in loop_start..new_card_idx.to_usize() {
            assert!(
                self.crossing_map.get(c.into()) == crossing_middle,
                "middle crossing not correct."
            );
        }
    }

    fn verify_reference(&mut self, slot: Slot, container_obj: Address, name: &str) {
        let reference = slot.get();

        if reference.is_null() {
            return;
        }

        if self.old_protected.contains_slow(reference)
            || self.eden_active.contains(reference)
            || self.to_active.contains(reference)
            || self.perm_space.contains(reference)
            || self.large.contains(reference)
            || (self.from_active.contains(reference) && self.promotion_failed)
        {
            let object = reference.to_obj();

            // Verify that the address is the start of an object,
            // for this access its size.
            // To make sure this isn't optimized out by the compiler,
            // make sure that the size doesn't equal 1.
            assert!(object.size() != 1, "object size shouldn't be 1");

            if self.young_total.contains(reference) {
                self.refs_to_young_gen += 1;
            }

            return;
        }

        let perm_region = self.perm_space.used_region();

        println!(
            "PRM: {}; active: {} (size 0x{:x})",
            self.perm_space.total(),
            perm_region,
            perm_region.size(),
        );
        println!(
            "EDN: {}; active: {} (size 0x{:x})",
            self.young.eden_total(),
            self.eden_active,
            self.eden_active.size(),
        );
        println!(
            "FRM: {}; active: {} (size 0x{:x})",
            self.young.from_total(),
            self.from_active,
            self.from_active.size(),
        );
        println!(
            " TO: {}; active: {} (size 0x{:x})",
            self.young.to_total(),
            self.to_active,
            self.to_active.size(),
        );
        {
            println!(
                "OLD total: {} (size 0x{:x})",
                self.old.total(),
                self.old.total().size()
            );
            for old_region in &self.old_protected.regions {
                println!(
                    "OLD region: {}; active: {} (size 0x{:x})",
                    old_region.committed_region(),
                    old_region.active_region(),
                    old_region.active_size(),
                );
            }
        }

        println!(
            "LRG: {}-{}",
            self.large.total().start,
            self.large.total().end
        );
        println!(
            "TTL: {}-{}",
            self.reserved_area.start, self.reserved_area.end
        );
        println!(
            "found invalid reference to {} in {} (at {}, in object {}) during {} phase.",
            reference,
            name,
            slot.address(),
            container_obj,
            self.phase,
        );

        if self.young.contains(reference)
            && !self.to_active.contains(reference)
            && !self.eden_active.contains(reference)
        {
            println!("reference points into young generation but not into the active semi-space.");

            if self.young.eden_total().contains(reference) {
                println!("\treference points into eden-space");
            }

            if self.young.from_total().contains(reference) {
                println!("\treference points into from-space");
            }

            if self.young.to_total().contains(reference) {
                println!("\treference points into to-space");
            }
        }

        println!("try print object size and class:");

        let object = reference.to_obj();
        println!("\tsize {}", object.size());
        let cls = object.header().vtbl().class();
        println!("\tclass {}", cls.name(get_vm()));

        panic!("reference neither pointing into young nor old generation.");
    }
}
<|endoftext|>


<commit_before>use binaries::BinaryInstallation;
use kernel::{
    async_trait::async_trait,
    eyre::{bail, eyre, Result},
    stencila_schema::{CodeError, Node},
    Kernel, KernelStatus, KernelTrait,
};
use serde::Serialize;
use std::{env, fs};
use tokio::{
    io::{BufReader, BufWriter},
    process::{Child, ChildStderr, ChildStdin, ChildStdout},
};

// Re-exports for the convenience of crates that implement `MicroKernelTrait`
pub use kernel;

/// The Unicode code point used as the separator between results
/// (both "outputs" on `stderr` and "messages" on `stderr`)
const RES_SEP: char = '\u{10ABBA}';

/// The Unicode code point used as the separator between individual
/// Microkernel "transactions".
const TRANS_SEP: char = '\u{10ACDC}';

#[derive(Debug, Serialize)]
pub struct MicroKernel {
    /// The language of the kernel
    ///
    /// Used to be able to return a `Kernel` spec.
    language: String,

    /// The resolved binary for the kernel
    binary: BinaryInstallation,

    /// Arguments that should be supplied to the runtime binary
    ///
    /// Use the argument `"{{script}}"` as a placeholder for the name
    /// of the script file.
    args: Vec<String>,

    /// The script that runs the kernel
    script: (String, String),

    /// Other files that the kernel uses (e.g. codec)
    others: Vec<(String, String)>,

    /// The code template for setting a variable
    set_template: String,

    /// The code template for getting a variable
    get_template: String,

    /// The current status of the kernel
    status: KernelStatus,

    /// The child process of the kernel
    #[serde(skip)]
    child: Option<Child>,

    /// The writer for the stdin stream of the child process
    #[serde(skip)]
    stdin: Option<BufWriter<ChildStdin>>,

    /// The reader for the stdout stream of the child process
    #[serde(skip)]
    stdout: Option<BufReader<ChildStdout>>,

    /// The reader for the stderr stream of the child process
    #[serde(skip)]
    stderr: Option<BufReader<ChildStderr>>,
}

impl MicroKernel {
    /// Create a new `MicroKernel`
    ///
    /// This function will error if no runtime matching the semver requirements
    /// in `runtime` is found on the `system`.
    pub async fn new(
        language: &str,
        runtime: (&str, &str, &[&str]),
        script: (&str, &str),
        others: &[(&str, &str)],
        set_template: &str,
        get_template: &str,
    ) -> Result<Self> {
        let (name, semver, args) = runtime;
        let binary = match binaries::require(name, semver).await {
            Ok(binary) => binary,
            Err(error) => bail!("Unable to find or install runtime for kernel: {}", error),
        };

        let kernel = Self {
            language: language.to_string(),
            binary,
            args: args.iter().map(|arg| arg.to_string()).collect(),
            script: (script.0.to_string(), script.1.to_string()),
            others: others
                .iter()
                .map(|file| (file.0.to_string(), file.1.to_string()))
                .collect(),
            set_template: set_template.into(),
            get_template: get_template.into(),
            status: KernelStatus::Pending,
            child: None,
            stdin: None,
            stdout: None,
            stderr: None,
        };

        Ok(kernel)
    }
}

/// Include a file as a (name, content) tuple
#[macro_export]
macro_rules! include_file {
    ($name:literal) => {
        ($name, include_str!($name))
    };
}

#[async_trait]
impl KernelTrait for MicroKernel {
    /// Get the [`Kernel`] specification
    fn spec(&self) -> Kernel {
        Kernel {
            language: self.language.clone(),
        }
    }

    /// Start the kernel
    ///
    /// An override of `KernelTrait::start` that searches for the preferred executable
    /// and runs it using specified commands, including the kernel script file if specified
    /// in the arguments.
    async fn start(&mut self) -> Result<()> {
        // Resolve the directory where kernels ar run
        let user_data_dir = dirs::data_dir().unwrap_or_else(|| env::current_dir().unwrap());
        let dir = match env::consts::OS {
            "macos" | "windows" => user_data_dir.join("Stencila").join("Microkernels"),
            _ => user_data_dir.join("stencila").join("microkernels"),
        };
        fs::create_dir_all(&dir)?;

        // Copy over script file
        let script_path = dir.join(&self.script.0);
        fs::write(&script_path, &self.script.1)?;

        // Copy over other files
        for other in &self.others {
            fs::write(dir.join(&other.0), &other.1)?;
        }

        // Construct args array, inserting script path where appropriate
        let args: Vec<String> = self
            .args
            .iter()
            .map(|arg| match arg.as_str() {
                "{{script}}" => script_path.display().to_string(),
                _ => arg.to_string(),
            })
            .collect();

        // Start child process
        let mut child = self.binary.interact(&args)?;

        let stdin = child
            .stdin
            .take()
            .ok_or_else(|| eyre!("Child has no stdin handle"))?;
        let stdout = child
            .stdout
            .take()
            .ok_or_else(|| eyre!("Child has no stdout handle"))?;
        let stderr = child
            .stderr
            .take()
            .ok_or_else(|| eyre!("Child has no stderr handle"))?;

        self.child = Some(child);
        self.stdin = Some(BufWriter::new(stdin));
        self.stdout = Some(BufReader::new(stdout));
        self.stderr = Some(BufReader::new(stderr));
        self.status = KernelStatus::Idle;

        Ok(())
    }

    /// Stop the kernel
    ///
    /// An override of `KernelTrait::stop` that kills the child process.
    async fn stop(&mut self) -> Result<()> {
        if let Some(child) = self.child.as_mut() {
            self.status = KernelStatus::Stopping;
            child.kill().await?;
            self.child = None;
            self.status = KernelStatus::Finished;
        }
        Ok(())
    }

    /// Get the status of the kernel
    async fn status(&self) -> Result<KernelStatus> {
        Ok(self.status.clone())
    }

    /// Get a symbol from the kernel
    async fn get(&mut self, name: &str) -> Result<Node> {
        let code = self.get_template.replace("{{name}}", name);

        let (outputs, messages) = self.exec(&code).await?;

        if let Some(output) = outputs.first() {
            Ok(output.clone())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to get symbol `{}`: {}", name, message)
        }
    }

    /// Set a symbol in the kernel
    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let json = serde_json::to_string(&value)?;
        let code = self
            .set_template
            .replace("{{name}}", name)
            .replace("{{json}}", &json);

        let (.., messages) = self.exec(&code).await?;

        if messages.is_empty() {
            Ok(())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to set symbol `{}`: {}", name, message)
        }
    }

    /// Execute some code in the kernel
    async fn exec(&mut self, code: &str) -> Result<(Vec<Node>, Vec<CodeError>)> {
        use tokio::io::AsyncBufReadExt;
        use tokio::io::AsyncWriteExt;

        let stdin = self
            .stdin
            .as_mut()
            .expect("Kernel should have started and have stdin");
        let stdout = self
            .stdout
            .as_mut()
            .expect("Kernel should have started and have stdout");
        let stderr = self
            .stderr
            .as_mut()
            .expect("Kernel should have started and have stderr");

        // Send code to the kernel
        tracing::debug!("Sending on stdin");
        stdin
            .write_all([&code.replace("\n", "\\n"), "\n"].concat().as_bytes())
            .await?;
        stdin.flush().await?;

        // Capture outputs separating them as we go
        let mut output = String::new();
        let mut outputs = Vec::new();
        while let Some(line) = stdout.lines().next_line().await? {
            tracing::debug!("Received on stdout: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                    output.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                }
                break;
            } else {
                output.push_str(&line);
                output.push('\n');
            }
        }

        // Attempt to parse each output as JSON into a `Node`, falling back to a string.
        let outputs: Vec<Node> = outputs
            .iter()
            .map(|output| -> Node {
                serde_json::from_str(output).unwrap_or_else(|_| Node::String(output.clone()))
            })
            .collect();

        // Capture messages separating them as we go
        let mut message = String::new();
        let mut messages = Vec::new();
        while let Some(line) = stderr.lines().next_line().await? {
            tracing::debug!("Received on sterr: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                    message.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                }
                break;
            } else {
                message.push_str(&line);
                message.push('\n');
            }
        }

        // Attempt to parse each message as JSON into a `CodeMessage`.
        let messages: Vec<CodeError> = messages
            .iter()
            .map(|message| -> CodeError {
                serde_json::from_str(message).unwrap_or_else(|_| CodeError {
                    error_message: message.into(),
                    ..Default::default()
                })
            })
            .collect();

        Ok((outputs, messages))
    }
}
<commit_msg>fix(Microkernels): Update status of pipe failures<commit_after>use binaries::BinaryInstallation;
use kernel::{
    async_trait::async_trait,
    eyre::{bail, eyre, Result},
    stencila_schema::{CodeError, Node},
    Kernel, KernelStatus, KernelTrait,
};
use serde::Serialize;
use std::{env, fs};
use tokio::{
    io::{BufReader, BufWriter},
    process::{Child, ChildStderr, ChildStdin, ChildStdout},
};

// Re-exports for the convenience of crates that implement `MicroKernelTrait`
pub use kernel;

/// The Unicode code point used as the separator between results
/// (both "outputs" on `stderr` and "messages" on `stderr`)
const RES_SEP: char = '\u{10ABBA}';

/// The Unicode code point used as the separator between individual
/// Microkernel "transactions".
const TRANS_SEP: char = '\u{10ACDC}';

#[derive(Debug, Serialize)]
pub struct MicroKernel {
    /// The language of the kernel
    ///
    /// Used to be able to return a `Kernel` spec.
    language: String,

    /// The resolved binary for the kernel
    binary: BinaryInstallation,

    /// Arguments that should be supplied to the runtime binary
    ///
    /// Use the argument `"{{script}}"` as a placeholder for the name
    /// of the script file.
    args: Vec<String>,

    /// The script that runs the kernel
    script: (String, String),

    /// Other files that the kernel uses (e.g. codec)
    others: Vec<(String, String)>,

    /// The code template for setting a variable
    set_template: String,

    /// The code template for getting a variable
    get_template: String,

    /// The current status of the kernel
    status: KernelStatus,

    /// The child process of the kernel
    #[serde(skip)]
    child: Option<Child>,

    /// The writer for the stdin stream of the child process
    #[serde(skip)]
    stdin: Option<BufWriter<ChildStdin>>,

    /// The reader for the stdout stream of the child process
    #[serde(skip)]
    stdout: Option<BufReader<ChildStdout>>,

    /// The reader for the stderr stream of the child process
    #[serde(skip)]
    stderr: Option<BufReader<ChildStderr>>,
}

impl MicroKernel {
    /// Create a new `MicroKernel`
    ///
    /// This function will error if no runtime matching the semver requirements
    /// in `runtime` is found on the `system`.
    pub async fn new(
        language: &str,
        runtime: (&str, &str, &[&str]),
        script: (&str, &str),
        others: &[(&str, &str)],
        set_template: &str,
        get_template: &str,
    ) -> Result<Self> {
        let (name, semver, args) = runtime;
        let binary = match binaries::require(name, semver).await {
            Ok(binary) => binary,
            Err(error) => bail!("Unable to find or install runtime for kernel: {}", error),
        };

        let kernel = Self {
            language: language.to_string(),
            binary,
            args: args.iter().map(|arg| arg.to_string()).collect(),
            script: (script.0.to_string(), script.1.to_string()),
            others: others
                .iter()
                .map(|file| (file.0.to_string(), file.1.to_string()))
                .collect(),
            set_template: set_template.into(),
            get_template: get_template.into(),
            status: KernelStatus::Pending,
            child: None,
            stdin: None,
            stdout: None,
            stderr: None,
        };

        Ok(kernel)
    }
}

/// Include a file as a (name, content) tuple
#[macro_export]
macro_rules! include_file {
    ($name:literal) => {
        ($name, include_str!($name))
    };
}

#[async_trait]
impl KernelTrait for MicroKernel {
    /// Get the [`Kernel`] specification
    fn spec(&self) -> Kernel {
        Kernel {
            language: self.language.clone(),
        }
    }

    /// Start the kernel
    ///
    /// An override of `KernelTrait::start` that searches for the preferred executable
    /// and runs it using specified commands, including the kernel script file if specified
    /// in the arguments.
    async fn start(&mut self) -> Result<()> {
        // Resolve the directory where kernels ar run
        let user_data_dir = dirs::data_dir().unwrap_or_else(|| env::current_dir().unwrap());
        let dir = match env::consts::OS {
            "macos" | "windows" => user_data_dir.join("Stencila").join("Microkernels"),
            _ => user_data_dir.join("stencila").join("microkernels"),
        };
        fs::create_dir_all(&dir)?;

        // Copy over script file
        let script_path = dir.join(&self.script.0);
        fs::write(&script_path, &self.script.1)?;

        // Copy over other files
        for other in &self.others {
            fs::write(dir.join(&other.0), &other.1)?;
        }

        // Construct args array, inserting script path where appropriate
        let args: Vec<String> = self
            .args
            .iter()
            .map(|arg| match arg.as_str() {
                "{{script}}" => script_path.display().to_string(),
                _ => arg.to_string(),
            })
            .collect();

        // Start child process
        let mut child = self.binary.interact(&args)?;

        let stdin = child
            .stdin
            .take()
            .ok_or_else(|| eyre!("Child has no stdin handle"))?;
        let stdout = child
            .stdout
            .take()
            .ok_or_else(|| eyre!("Child has no stdout handle"))?;
        let stderr = child
            .stderr
            .take()
            .ok_or_else(|| eyre!("Child has no stderr handle"))?;

        self.child = Some(child);
        self.stdin = Some(BufWriter::new(stdin));
        self.stdout = Some(BufReader::new(stdout));
        self.stderr = Some(BufReader::new(stderr));
        self.status = KernelStatus::Idle;

        Ok(())
    }

    /// Stop the kernel
    ///
    /// An override of `KernelTrait::stop` that kills the child process.
    async fn stop(&mut self) -> Result<()> {
        if let Some(child) = self.child.as_mut() {
            self.status = KernelStatus::Stopping;
            child.kill().await?;
            self.child = None;
            self.status = KernelStatus::Finished;
        }
        Ok(())
    }

    /// Get the status of the kernel
    async fn status(&self) -> Result<KernelStatus> {
        Ok(self.status.clone())
    }

    /// Get a symbol from the kernel
    async fn get(&mut self, name: &str) -> Result<Node> {
        let code = self.get_template.replace("{{name}}", name);

        let (outputs, messages) = self.exec(&code).await?;

        if let Some(output) = outputs.first() {
            Ok(output.clone())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to get symbol `{}`: {}", name, message)
        }
    }

    /// Set a symbol in the kernel
    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let json = serde_json::to_string(&value)?;
        let code = self
            .set_template
            .replace("{{name}}", name)
            .replace("{{json}}", &json);

        let (.., messages) = self.exec(&code).await?;

        if messages.is_empty() {
            Ok(())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to set symbol `{}`: {}", name, message)
        }
    }

    /// Execute some code in the kernel
    async fn exec(&mut self, code: &str) -> Result<(Vec<Node>, Vec<CodeError>)> {
        use tokio::io::AsyncBufReadExt;
        use tokio::io::AsyncWriteExt;

        let stdin = self
            .stdin
            .as_mut()
            .expect("Kernel should have started and have stdin");
        let stdout = self
            .stdout
            .as_mut()
            .expect("Kernel should have started and have stdout");
        let stderr = self
            .stderr
            .as_mut()
            .expect("Kernel should have started and have stderr");

        // Send code to the kernel
        tracing::debug!("Sending on stdin");
        if let Err(error) = stdin
            .write_all([&code.replace("\n", "\\n"), "\n"].concat().as_bytes())
            .await
        {
            self.status = KernelStatus::Failed;
            bail!("When writing code to kernel: {}", error)
        }
        if let Err(error) = stdin.flush().await {
            self.status = KernelStatus::Failed;
            bail!("When flushing code to kernel: {}", error)
        }

        // Capture outputs separating them as we go
        let mut output = String::new();
        let mut outputs = Vec::new();
        loop {
            let line = match stdout.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving outputs from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stdout: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                    output.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                }
                break;
            } else {
                output.push_str(&line);
                output.push('\n');
            }
        }

        // Attempt to parse each output as JSON into a `Node`, falling back to a string.
        let outputs: Vec<Node> = outputs
            .iter()
            .map(|output| -> Node {
                serde_json::from_str(output).unwrap_or_else(|_| Node::String(output.clone()))
            })
            .collect();

        // Capture messages separating them as we go
        let mut message = String::new();
        let mut messages = Vec::new();
        loop {
            let line = match stderr.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving messages from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stderr: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                    message.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                }
                break;
            } else {
                message.push_str(&line);
                message.push('\n');
            }
        }

        // Attempt to parse each message as JSON into a `CodeMessage`.
        let messages: Vec<CodeError> = messages
            .iter()
            .map(|message| -> CodeError {
                serde_json::from_str(message).unwrap_or_else(|_| CodeError {
                    error_message: message.into(),
                    ..Default::default()
                })
            })
            .collect();

        Ok((outputs, messages))
    }
}
<|endoftext|>


<commit_before>use storage::file::FileHeaderSpec as FHS;

pub fn get_spec() -> FHS {
    FHS::Map { keys: vec![ url_key(), tags_key() ] }
}

fn url_key() -> FHS {
    FHS::Key { name: "URL", value_type: Box::new(FHS::Text) }
}

fn tags_key() -> FHS {
    FHS::Key { name: "TAGS", value_type: Box::new(text_array()) }
}

fn text_array() -> FHS {
    FHS::Array { allowed_types: vec![FHS::Text] }
}

<commit_msg>Add function to build header content<commit_after>use storage::file::FileHeaderSpec as FHS;
use storage::file::FileHeaderData as FHD;

pub fn get_spec() -> FHS {
    FHS::Map { keys: vec![ url_key(), tags_key() ] }
}

fn url_key() -> FHS {
    FHS::Key { name: "URL", value_type: Box::new(FHS::Text) }
}

fn tags_key() -> FHS {
    FHS::Key { name: "TAGS", value_type: Box::new(text_array()) }
}

fn text_array() -> FHS {
    FHS::Array { allowed_types: vec![FHS::Text] }
}

pub fn build_header(url: &String, tags: &Vec<String>) -> FHD {
    FHD::Map {
        keys: vec![
            FHD::Key {
                name: "URL",
                value: Box::new(FHD::Text(url.clone()))
            },
            FHD::Key {
                name: "TAGS",
                value: Box::new(FHD::Text(tags.connect(",")))
            }
        ]
    }
}

<|endoftext|>


<commit_before>use crate::task::{waker_ref, ArcWake};
use futures_core::future::{FusedFuture, Future};
use futures_core::task::{Context, Poll, Waker};
use slab::Slab;
use std::cell::UnsafeCell;
use std::fmt;
use std::pin::Pin;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{Acquire, SeqCst};
use std::sync::{Arc, Mutex, Weak};

/// Future for the [`shared`](super::FutureExt::shared) method.
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Shared<Fut: Future> {
    inner: Option<Arc<Inner<Fut>>>,
    waker_key: usize,
}

struct Inner<Fut: Future> {
    future_or_output: UnsafeCell<FutureOrOutput<Fut>>,
    notifier: Arc<Notifier>,
}

struct Notifier {
    state: AtomicUsize,
    wakers: Mutex<Option<Slab<Option<Waker>>>>,
}

/// A weak reference to a [`Shared`] that can be upgraded much like an `Arc`.
pub struct WeakShared<Fut: Future>(Weak<Inner<Fut>>);

// The future itself is polled behind the `Arc`, so it won't be moved
// when `Shared` is moved.
impl<Fut: Future> Unpin for Shared<Fut> {}

impl<Fut: Future> fmt::Debug for Shared<Fut> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Shared")
            .field("inner", &self.inner)
            .field("waker_key", &self.waker_key)
            .finish()
    }
}

impl<Fut: Future> fmt::Debug for Inner<Fut> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Inner").finish()
    }
}

impl<Fut: Future> fmt::Debug for WeakShared<Fut> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("WeakShared").finish()
    }
}

enum FutureOrOutput<Fut: Future> {
    Future(Fut),
    Output(Fut::Output),
}

unsafe impl<Fut> Send for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{
}

unsafe impl<Fut> Sync for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{
}

const IDLE: usize = 0;
const POLLING: usize = 1;
const COMPLETE: usize = 2;
const POISONED: usize = 3;

const NULL_WAKER_KEY: usize = usize::max_value();

impl<Fut: Future> Shared<Fut> {
    pub(super) fn new(future: Fut) -> Self {
        let inner = Inner {
            future_or_output: UnsafeCell::new(FutureOrOutput::Future(future)),
            notifier: Arc::new(Notifier {
                state: AtomicUsize::new(IDLE),
                wakers: Mutex::new(Some(Slab::new())),
            }),
        };

        Self {
            inner: Some(Arc::new(inner)),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// Returns [`Some`] containing a reference to this [`Shared`]'s output if
    /// it has already been computed by a clone or [`None`] if it hasn't been
    /// computed yet or this [`Shared`] already returned its output from
    /// [`poll`](Future::poll).
    pub fn peek(&self) -> Option<&Fut::Output> {
        if let Some(inner) = self.inner.as_ref() {
            match inner.notifier.state.load(SeqCst) {
                COMPLETE => unsafe { return Some(inner.output()) },
                POISONED => panic!("inner future panicked during poll"),
                _ => {}
            }
        }
        None
    }

    /// Creates a new [`WeakShared`] for this [`Shared`].
    ///
    /// Returns [`None`] if it has already been polled to completion.
    pub fn downgrade(&self) -> Option<WeakShared<Fut>> {
        if let Some(inner) = self.inner.as_ref() {
            return Some(WeakShared(Arc::downgrade(inner)));
        }
        None
    }

    /// Gets the number of strong pointers to this allocation.
    ///
    /// Returns [`None`] if it has already been polled to completion.
    ///
    /// # Safety
    ///
    /// This method by itself is safe, but using it correctly requires extra care. Another thread
    /// can change the strong count at any time, including potentially between calling this method
    /// and acting on the result.
    pub fn strong_count(&self) -> Option<usize> {
        self.inner.as_ref().map(|arc| Arc::strong_count(arc))
    }

    /// Gets the number of weak pointers to this allocation.
    ///
    /// Returns [`None`] if it has already been polled to completion.
    ///
    /// # Safety
    ///
    /// This method by itself is safe, but using it correctly requires extra care. Another thread
    /// can change the weak count at any time, including potentially between calling this method
    /// and acting on the result.
    pub fn weak_count(&self) -> Option<usize> {
        self.inner.as_ref().map(|arc| Arc::weak_count(arc))
    }
}

impl<Fut> Inner<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn output(&self) -> &Fut::Output {
        match &*self.future_or_output.get() {
            FutureOrOutput::Output(ref item) => item,
            FutureOrOutput::Future(_) => unreachable!(),
        }
    }
    /// Registers the current task to receive a wakeup when we are awoken.
    fn record_waker(&self, waker_key: &mut usize, cx: &mut Context<'_>) {
        let mut wakers_guard = self.notifier.wakers.lock().unwrap();

        let wakers = match wakers_guard.as_mut() {
            Some(wakers) => wakers,
            None => return,
        };

        let new_waker = cx.waker();

        if *waker_key == NULL_WAKER_KEY {
            *waker_key = wakers.insert(Some(new_waker.clone()));
        } else {
            match wakers[*waker_key] {
                Some(ref old_waker) if new_waker.will_wake(old_waker) => {}
                // Could use clone_from here, but Waker doesn't specialize it.
                ref mut slot => *slot = Some(new_waker.clone()),
            }
        }
        debug_assert!(*waker_key != NULL_WAKER_KEY);
    }

    /// Safety: callers must first ensure that `inner.state`
    /// is `COMPLETE`
    unsafe fn take_or_clone_output(self: Arc<Self>) -> Fut::Output {
        match Arc::try_unwrap(self) {
            Ok(inner) => match inner.future_or_output.into_inner() {
                FutureOrOutput::Output(item) => item,
                FutureOrOutput::Future(_) => unreachable!(),
            },
            Err(inner) => inner.output().clone(),
        }
    }
}

impl<Fut> FusedFuture for Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    fn is_terminated(&self) -> bool {
        self.inner.is_none()
    }
}

impl<Fut> Future for Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = &mut *self;

        let inner = this
            .inner
            .take()
            .expect("Shared future polled again after completion");

        // Fast path for when the wrapped future has already completed
        if inner.notifier.state.load(Acquire) == COMPLETE {
            // Safety: We're in the COMPLETE state
            return unsafe { Poll::Ready(inner.take_or_clone_output()) };
        }

        inner.record_waker(&mut this.waker_key, cx);

        match inner
            .notifier
            .state
            .compare_exchange(IDLE, POLLING, SeqCst, SeqCst)
            .unwrap_or_else(|x| x)
        {
            IDLE => {
                // Lock acquired, fall through
            }
            POLLING => {
                // Another task is currently polling, at this point we just want
                // to ensure that the waker for this task is registered
                this.inner = Some(inner);
                return Poll::Pending;
            }
            COMPLETE => {
                // Safety: We're in the COMPLETE state
                return unsafe { Poll::Ready(inner.take_or_clone_output()) };
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => unreachable!(),
        }

        let waker = waker_ref(&inner.notifier);
        let mut cx = Context::from_waker(&waker);

        struct Reset<'a>(&'a AtomicUsize);

        impl Drop for Reset<'_> {
            fn drop(&mut self) {
                use std::thread;

                if thread::panicking() {
                    self.0.store(POISONED, SeqCst);
                }
            }
        }

        let _reset = Reset(&inner.notifier.state);

        let output = {
            let future = unsafe {
                match &mut *inner.future_or_output.get() {
                    FutureOrOutput::Future(fut) => Pin::new_unchecked(fut),
                    _ => unreachable!(),
                }
            };

            match future.poll(&mut cx) {
                Poll::Pending => {
                    if inner
                        .notifier
                        .state
                        .compare_exchange(POLLING, IDLE, SeqCst, SeqCst)
                        .is_ok()
                    {
                        // Success
                        drop(_reset);
                        this.inner = Some(inner);
                        return Poll::Pending;
                    } else {
                        unreachable!()
                    }
                }
                Poll::Ready(output) => output,
            }
        };

        unsafe {
            *inner.future_or_output.get() = FutureOrOutput::Output(output);
        }

        inner.notifier.state.store(COMPLETE, SeqCst);

        // Wake all tasks and drop the slab
        let mut wakers_guard = inner.notifier.wakers.lock().unwrap();
        let mut wakers = wakers_guard.take().unwrap();
        for opt_waker in wakers.drain() {
            if let Some(waker) = opt_waker {
                waker.wake();
            }
        }

        drop(_reset); // Make borrow checker happy
        drop(wakers_guard);

        // Safety: We're in the COMPLETE state
        unsafe { Poll::Ready(inner.take_or_clone_output()) }
    }
}

impl<Fut> Clone for Shared<Fut>
where
    Fut: Future,
{
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Drop for Shared<Fut>
where
    Fut: Future,
{
    fn drop(&mut self) {
        if self.waker_key != NULL_WAKER_KEY {
            if let Some(ref inner) = self.inner {
                if let Ok(mut wakers) = inner.notifier.wakers.lock() {
                    if let Some(wakers) = wakers.as_mut() {
                        wakers.remove(self.waker_key);
                    }
                }
            }
        }
    }
}

impl ArcWake for Notifier {
    fn wake_by_ref(arc_self: &Arc<Self>) {
        let wakers = &mut *arc_self.wakers.lock().unwrap();
        if let Some(wakers) = wakers.as_mut() {
            for (_key, opt_waker) in wakers {
                if let Some(waker) = opt_waker.take() {
                    waker.wake();
                }
            }
        }
    }
}

impl<Fut: Future> WeakShared<Fut>
{
    /// Attempts to upgrade this [`WeakShared`] into a [`Shared`].
    ///
    /// Returns [`None`] if all clones of the [`Shared`] have been dropped or polled
    /// to completion.
    pub fn upgrade(&self) -> Option<Shared<Fut>> {
        Some(Shared {
            inner: Some(self.0.upgrade()?),
            waker_key: NULL_WAKER_KEY,
        })
    }
}
<commit_msg>Fix clippy::manual_flatten warning (#2347)<commit_after>use crate::task::{waker_ref, ArcWake};
use futures_core::future::{FusedFuture, Future};
use futures_core::task::{Context, Poll, Waker};
use slab::Slab;
use std::cell::UnsafeCell;
use std::fmt;
use std::pin::Pin;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{Acquire, SeqCst};
use std::sync::{Arc, Mutex, Weak};

/// Future for the [`shared`](super::FutureExt::shared) method.
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Shared<Fut: Future> {
    inner: Option<Arc<Inner<Fut>>>,
    waker_key: usize,
}

struct Inner<Fut: Future> {
    future_or_output: UnsafeCell<FutureOrOutput<Fut>>,
    notifier: Arc<Notifier>,
}

struct Notifier {
    state: AtomicUsize,
    wakers: Mutex<Option<Slab<Option<Waker>>>>,
}

/// A weak reference to a [`Shared`] that can be upgraded much like an `Arc`.
pub struct WeakShared<Fut: Future>(Weak<Inner<Fut>>);

// The future itself is polled behind the `Arc`, so it won't be moved
// when `Shared` is moved.
impl<Fut: Future> Unpin for Shared<Fut> {}

impl<Fut: Future> fmt::Debug for Shared<Fut> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Shared")
            .field("inner", &self.inner)
            .field("waker_key", &self.waker_key)
            .finish()
    }
}

impl<Fut: Future> fmt::Debug for Inner<Fut> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Inner").finish()
    }
}

impl<Fut: Future> fmt::Debug for WeakShared<Fut> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("WeakShared").finish()
    }
}

enum FutureOrOutput<Fut: Future> {
    Future(Fut),
    Output(Fut::Output),
}

unsafe impl<Fut> Send for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{
}

unsafe impl<Fut> Sync for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{
}

const IDLE: usize = 0;
const POLLING: usize = 1;
const COMPLETE: usize = 2;
const POISONED: usize = 3;

const NULL_WAKER_KEY: usize = usize::max_value();

impl<Fut: Future> Shared<Fut> {
    pub(super) fn new(future: Fut) -> Self {
        let inner = Inner {
            future_or_output: UnsafeCell::new(FutureOrOutput::Future(future)),
            notifier: Arc::new(Notifier {
                state: AtomicUsize::new(IDLE),
                wakers: Mutex::new(Some(Slab::new())),
            }),
        };

        Self {
            inner: Some(Arc::new(inner)),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// Returns [`Some`] containing a reference to this [`Shared`]'s output if
    /// it has already been computed by a clone or [`None`] if it hasn't been
    /// computed yet or this [`Shared`] already returned its output from
    /// [`poll`](Future::poll).
    pub fn peek(&self) -> Option<&Fut::Output> {
        if let Some(inner) = self.inner.as_ref() {
            match inner.notifier.state.load(SeqCst) {
                COMPLETE => unsafe { return Some(inner.output()) },
                POISONED => panic!("inner future panicked during poll"),
                _ => {}
            }
        }
        None
    }

    /// Creates a new [`WeakShared`] for this [`Shared`].
    ///
    /// Returns [`None`] if it has already been polled to completion.
    pub fn downgrade(&self) -> Option<WeakShared<Fut>> {
        if let Some(inner) = self.inner.as_ref() {
            return Some(WeakShared(Arc::downgrade(inner)));
        }
        None
    }

    /// Gets the number of strong pointers to this allocation.
    ///
    /// Returns [`None`] if it has already been polled to completion.
    ///
    /// # Safety
    ///
    /// This method by itself is safe, but using it correctly requires extra care. Another thread
    /// can change the strong count at any time, including potentially between calling this method
    /// and acting on the result.
    pub fn strong_count(&self) -> Option<usize> {
        self.inner.as_ref().map(|arc| Arc::strong_count(arc))
    }

    /// Gets the number of weak pointers to this allocation.
    ///
    /// Returns [`None`] if it has already been polled to completion.
    ///
    /// # Safety
    ///
    /// This method by itself is safe, but using it correctly requires extra care. Another thread
    /// can change the weak count at any time, including potentially between calling this method
    /// and acting on the result.
    pub fn weak_count(&self) -> Option<usize> {
        self.inner.as_ref().map(|arc| Arc::weak_count(arc))
    }
}

impl<Fut> Inner<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn output(&self) -> &Fut::Output {
        match &*self.future_or_output.get() {
            FutureOrOutput::Output(ref item) => item,
            FutureOrOutput::Future(_) => unreachable!(),
        }
    }
    /// Registers the current task to receive a wakeup when we are awoken.
    fn record_waker(&self, waker_key: &mut usize, cx: &mut Context<'_>) {
        let mut wakers_guard = self.notifier.wakers.lock().unwrap();

        let wakers = match wakers_guard.as_mut() {
            Some(wakers) => wakers,
            None => return,
        };

        let new_waker = cx.waker();

        if *waker_key == NULL_WAKER_KEY {
            *waker_key = wakers.insert(Some(new_waker.clone()));
        } else {
            match wakers[*waker_key] {
                Some(ref old_waker) if new_waker.will_wake(old_waker) => {}
                // Could use clone_from here, but Waker doesn't specialize it.
                ref mut slot => *slot = Some(new_waker.clone()),
            }
        }
        debug_assert!(*waker_key != NULL_WAKER_KEY);
    }

    /// Safety: callers must first ensure that `inner.state`
    /// is `COMPLETE`
    unsafe fn take_or_clone_output(self: Arc<Self>) -> Fut::Output {
        match Arc::try_unwrap(self) {
            Ok(inner) => match inner.future_or_output.into_inner() {
                FutureOrOutput::Output(item) => item,
                FutureOrOutput::Future(_) => unreachable!(),
            },
            Err(inner) => inner.output().clone(),
        }
    }
}

impl<Fut> FusedFuture for Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    fn is_terminated(&self) -> bool {
        self.inner.is_none()
    }
}

impl<Fut> Future for Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = &mut *self;

        let inner = this
            .inner
            .take()
            .expect("Shared future polled again after completion");

        // Fast path for when the wrapped future has already completed
        if inner.notifier.state.load(Acquire) == COMPLETE {
            // Safety: We're in the COMPLETE state
            return unsafe { Poll::Ready(inner.take_or_clone_output()) };
        }

        inner.record_waker(&mut this.waker_key, cx);

        match inner
            .notifier
            .state
            .compare_exchange(IDLE, POLLING, SeqCst, SeqCst)
            .unwrap_or_else(|x| x)
        {
            IDLE => {
                // Lock acquired, fall through
            }
            POLLING => {
                // Another task is currently polling, at this point we just want
                // to ensure that the waker for this task is registered
                this.inner = Some(inner);
                return Poll::Pending;
            }
            COMPLETE => {
                // Safety: We're in the COMPLETE state
                return unsafe { Poll::Ready(inner.take_or_clone_output()) };
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => unreachable!(),
        }

        let waker = waker_ref(&inner.notifier);
        let mut cx = Context::from_waker(&waker);

        struct Reset<'a>(&'a AtomicUsize);

        impl Drop for Reset<'_> {
            fn drop(&mut self) {
                use std::thread;

                if thread::panicking() {
                    self.0.store(POISONED, SeqCst);
                }
            }
        }

        let _reset = Reset(&inner.notifier.state);

        let output = {
            let future = unsafe {
                match &mut *inner.future_or_output.get() {
                    FutureOrOutput::Future(fut) => Pin::new_unchecked(fut),
                    _ => unreachable!(),
                }
            };

            match future.poll(&mut cx) {
                Poll::Pending => {
                    if inner
                        .notifier
                        .state
                        .compare_exchange(POLLING, IDLE, SeqCst, SeqCst)
                        .is_ok()
                    {
                        // Success
                        drop(_reset);
                        this.inner = Some(inner);
                        return Poll::Pending;
                    } else {
                        unreachable!()
                    }
                }
                Poll::Ready(output) => output,
            }
        };

        unsafe {
            *inner.future_or_output.get() = FutureOrOutput::Output(output);
        }

        inner.notifier.state.store(COMPLETE, SeqCst);

        // Wake all tasks and drop the slab
        let mut wakers_guard = inner.notifier.wakers.lock().unwrap();
        let mut wakers = wakers_guard.take().unwrap();
        for waker in wakers.drain().flatten() {
            waker.wake();
        }

        drop(_reset); // Make borrow checker happy
        drop(wakers_guard);

        // Safety: We're in the COMPLETE state
        unsafe { Poll::Ready(inner.take_or_clone_output()) }
    }
}

impl<Fut> Clone for Shared<Fut>
where
    Fut: Future,
{
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Drop for Shared<Fut>
where
    Fut: Future,
{
    fn drop(&mut self) {
        if self.waker_key != NULL_WAKER_KEY {
            if let Some(ref inner) = self.inner {
                if let Ok(mut wakers) = inner.notifier.wakers.lock() {
                    if let Some(wakers) = wakers.as_mut() {
                        wakers.remove(self.waker_key);
                    }
                }
            }
        }
    }
}

impl ArcWake for Notifier {
    fn wake_by_ref(arc_self: &Arc<Self>) {
        let wakers = &mut *arc_self.wakers.lock().unwrap();
        if let Some(wakers) = wakers.as_mut() {
            for (_key, opt_waker) in wakers {
                if let Some(waker) = opt_waker.take() {
                    waker.wake();
                }
            }
        }
    }
}

impl<Fut: Future> WeakShared<Fut>
{
    /// Attempts to upgrade this [`WeakShared`] into a [`Shared`].
    ///
    /// Returns [`None`] if all clones of the [`Shared`] have been dropped or polled
    /// to completion.
    pub fn upgrade(&self) -> Option<Shared<Fut>> {
        Some(Shared {
            inner: Some(self.0.upgrade()?),
            waker_key: NULL_WAKER_KEY,
        })
    }
}
<|endoftext|>


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

use llvm::ValueRef;
use rustc::middle::ty::{self, Ty, TypeFoldable};
use rustc::mir::repr as mir;
use rustc::mir::tcx::LvalueTy;
use trans::abi;
use trans::adt;
use trans::base;
use trans::builder::Builder;
use trans::common::{self, BlockAndBuilder, C_uint};
use trans::consts;
use trans::machine;
use trans::mir::drop;
use llvm;
use trans::Disr;

use std::ptr;

use super::{MirContext, TempRef};

#[derive(Copy, Clone)]
pub struct LvalueRef<'tcx> {
    /// Pointer to the contents of the lvalue
    pub llval: ValueRef,

    /// This lvalue's extra data if it is unsized, or null
    pub llextra: ValueRef,

    /// Monomorphized type of this lvalue, including variant information
    pub ty: LvalueTy<'tcx>,
}

impl<'tcx> LvalueRef<'tcx> {
    pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> {
        LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty }
    }

    pub fn alloca<'bcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                        ty: Ty<'tcx>,
                        name: &str)
                        -> LvalueRef<'tcx>
    {
        assert!(!ty.has_erasable_regions());
        let lltemp = bcx.with_block(|bcx| base::alloc_ty(bcx, ty, name));
        drop::drop_fill(bcx, lltemp, ty);
        LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
    }
}

pub fn get_meta(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
    b.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA)
}

pub fn get_dataptr(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
    b.struct_gep(fat_ptr, abi::FAT_PTR_ADDR)
}

pub fn load_fat_ptr(b: &Builder, fat_ptr: ValueRef) -> (ValueRef, ValueRef) {
    (b.load(get_dataptr(b, fat_ptr)), b.load(get_meta(b, fat_ptr)))
}

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn lvalue_len(&mut self,
                      bcx: &BlockAndBuilder<'bcx, 'tcx>,
                      lvalue: LvalueRef<'tcx>)
                      -> ValueRef {
        match lvalue.ty.to_ty(bcx.tcx()).sty {
            ty::TyArray(_, n) => common::C_uint(bcx.ccx(), n),
            ty::TySlice(_) | ty::TyStr => {
                assert!(lvalue.llextra != ptr::null_mut());
                lvalue.llextra
            }
            _ => bcx.sess().bug("unexpected type in lvalue_len"),
        }
    }

    pub fn trans_lvalue(&mut self,
                        bcx: &BlockAndBuilder<'bcx, 'tcx>,
                        lvalue: &mir::Lvalue<'tcx>)
                        -> LvalueRef<'tcx> {
        debug!("trans_lvalue(lvalue={:?})", lvalue);

        let fcx = bcx.fcx();
        let ccx = bcx.ccx();
        let tcx = bcx.tcx();
        match *lvalue {
            mir::Lvalue::Var(index) => self.vars[index as usize],
            mir::Lvalue::Temp(index) => match self.temps[index as usize] {
                TempRef::Lvalue(lvalue) =>
                    lvalue,
                TempRef::Operand(..) =>
                    tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)),
            },
            mir::Lvalue::Arg(index) => self.args[index as usize],
            mir::Lvalue::Static(def_id) => {
                let const_ty = self.mir.lvalue_ty(tcx, lvalue);
                LvalueRef::new_sized(consts::get_static(ccx, def_id).val, const_ty)
            },
            mir::Lvalue::ReturnPointer => {
                let llval = if !fcx.fn_ty.ret.is_ignore() {
                    bcx.with_block(|bcx| {
                        fcx.get_ret_slot(bcx, "")
                    })
                } else {
                    // This is a void return; that is, there’s no place to store the value and
                    // there cannot really be one (or storing into it doesn’t make sense, anyway).
                    // Ergo, we return an undef ValueRef, so we do not have to special-case every
                    // place using lvalues, and could use it the same way you use a regular
                    // ReturnPointer LValue (i.e. store into it, load from it etc).
                    let llty = fcx.fn_ty.ret.original_ty.ptr_to();
                    unsafe {
                        llvm::LLVMGetUndef(llty.to_ref())
                    }
                };
                let fn_return_ty = bcx.monomorphize(&self.mir.return_ty);
                let return_ty = fn_return_ty.unwrap();
                LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty))
            },
            mir::Lvalue::Projection(ref projection) => {
                let tr_base = self.trans_lvalue(bcx, &projection.base);
                let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem);
                let projected_ty = bcx.monomorphize(&projected_ty);

                let project_index = |llindex| {
                    let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty {
                        // Slices already point to the array element type.
                        bcx.inbounds_gep(tr_base.llval, &[llindex])
                    } else {
                        let zero = common::C_uint(bcx.ccx(), 0u64);
                        bcx.inbounds_gep(tr_base.llval, &[zero, llindex])
                    };
                    (element, ptr::null_mut())
                };

                let (llprojected, llextra) = match projection.elem {
                    mir::ProjectionElem::Deref => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
                            (base::load_ty_builder(bcx, tr_base.llval, base_ty),
                             ptr::null_mut())
                        } else {
                            load_fat_ptr(bcx, tr_base.llval)
                        }
                    }
                    mir::ProjectionElem::Field(ref field, _) => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        let base_repr = adt::represent_type(ccx, base_ty);
                        let discr = match tr_base.ty {
                            LvalueTy::Ty { .. } => 0,
                            LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
                        };
                        let discr = discr as u64;
                        let is_sized = common::type_is_sized(tcx, projected_ty.to_ty(tcx));
                        let base = if is_sized {
                            adt::MaybeSizedValue::sized(tr_base.llval)
                        } else {
                            adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra)
                        };
                        let llprojected = adt::trans_field_ptr_builder(bcx, &base_repr, base,
                                                                       Disr(discr), field.index());
                        let llextra = if is_sized {
                            ptr::null_mut()
                        } else {
                            tr_base.llextra
                        };
                        (llprojected, llextra)
                    }
                    mir::ProjectionElem::Index(ref index) => {
                        let index = self.trans_operand(bcx, index);
                        project_index(self.prepare_index(bcx, index.immediate()))
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: false,
                                                         min_length: _ } => {
                        let lloffset = C_uint(bcx.ccx(), offset);
                        project_index(self.prepare_index(bcx, lloffset))
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: true,
                                                         min_length: _ } => {
                        let lloffset = C_uint(bcx.ccx(), offset);
                        let lllen = self.lvalue_len(bcx, tr_base);
                        let llindex = bcx.sub(lllen, lloffset);
                        project_index(self.prepare_index(bcx, llindex))
                    }
                    mir::ProjectionElem::Downcast(..) => {
                        (tr_base.llval, tr_base.llextra)
                    }
                };
                LvalueRef {
                    llval: llprojected,
                    llextra: llextra,
                    ty: projected_ty,
                }
            }
        }
    }

    /// Adjust the bitwidth of an index since LLVM is less forgiving
    /// than we are.
    ///
    /// nmatsakis: is this still necessary? Not sure.
    fn prepare_index(&mut self,
                     bcx: &BlockAndBuilder<'bcx, 'tcx>,
                     llindex: ValueRef)
                     -> ValueRef
    {
        let ccx = bcx.ccx();
        let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex));
        let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type());
        if index_size < int_size {
            bcx.zext(llindex, ccx.int_type())
        } else if index_size > int_size {
            bcx.trunc(llindex, ccx.int_type())
        } else {
            llindex
        }
    }
}
<commit_msg>mir: Don't memset allocas of types that do not require drop.<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm::ValueRef;
use rustc::middle::ty::{self, Ty, TypeFoldable};
use rustc::mir::repr as mir;
use rustc::mir::tcx::LvalueTy;
use trans::abi;
use trans::adt;
use trans::base;
use trans::builder::Builder;
use trans::common::{self, BlockAndBuilder, C_uint};
use trans::consts;
use trans::machine;
use trans::mir::drop;
use llvm;
use trans::Disr;

use std::ptr;

use super::{MirContext, TempRef};

#[derive(Copy, Clone)]
pub struct LvalueRef<'tcx> {
    /// Pointer to the contents of the lvalue
    pub llval: ValueRef,

    /// This lvalue's extra data if it is unsized, or null
    pub llextra: ValueRef,

    /// Monomorphized type of this lvalue, including variant information
    pub ty: LvalueTy<'tcx>,
}

impl<'tcx> LvalueRef<'tcx> {
    pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> {
        LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty }
    }

    pub fn alloca<'bcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                        ty: Ty<'tcx>,
                        name: &str)
                        -> LvalueRef<'tcx>
    {
        assert!(!ty.has_erasable_regions());
        let lltemp = bcx.with_block(|bcx| base::alloc_ty(bcx, ty, name));
        if bcx.fcx().type_needs_drop(ty) {
            drop::drop_fill(bcx, lltemp, ty);
        }
        LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
    }
}

pub fn get_meta(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
    b.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA)
}

pub fn get_dataptr(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
    b.struct_gep(fat_ptr, abi::FAT_PTR_ADDR)
}

pub fn load_fat_ptr(b: &Builder, fat_ptr: ValueRef) -> (ValueRef, ValueRef) {
    (b.load(get_dataptr(b, fat_ptr)), b.load(get_meta(b, fat_ptr)))
}

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn lvalue_len(&mut self,
                      bcx: &BlockAndBuilder<'bcx, 'tcx>,
                      lvalue: LvalueRef<'tcx>)
                      -> ValueRef {
        match lvalue.ty.to_ty(bcx.tcx()).sty {
            ty::TyArray(_, n) => common::C_uint(bcx.ccx(), n),
            ty::TySlice(_) | ty::TyStr => {
                assert!(lvalue.llextra != ptr::null_mut());
                lvalue.llextra
            }
            _ => bcx.sess().bug("unexpected type in lvalue_len"),
        }
    }

    pub fn trans_lvalue(&mut self,
                        bcx: &BlockAndBuilder<'bcx, 'tcx>,
                        lvalue: &mir::Lvalue<'tcx>)
                        -> LvalueRef<'tcx> {
        debug!("trans_lvalue(lvalue={:?})", lvalue);

        let fcx = bcx.fcx();
        let ccx = bcx.ccx();
        let tcx = bcx.tcx();
        match *lvalue {
            mir::Lvalue::Var(index) => self.vars[index as usize],
            mir::Lvalue::Temp(index) => match self.temps[index as usize] {
                TempRef::Lvalue(lvalue) =>
                    lvalue,
                TempRef::Operand(..) =>
                    tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)),
            },
            mir::Lvalue::Arg(index) => self.args[index as usize],
            mir::Lvalue::Static(def_id) => {
                let const_ty = self.mir.lvalue_ty(tcx, lvalue);
                LvalueRef::new_sized(consts::get_static(ccx, def_id).val, const_ty)
            },
            mir::Lvalue::ReturnPointer => {
                let llval = if !fcx.fn_ty.ret.is_ignore() {
                    bcx.with_block(|bcx| {
                        fcx.get_ret_slot(bcx, "")
                    })
                } else {
                    // This is a void return; that is, there’s no place to store the value and
                    // there cannot really be one (or storing into it doesn’t make sense, anyway).
                    // Ergo, we return an undef ValueRef, so we do not have to special-case every
                    // place using lvalues, and could use it the same way you use a regular
                    // ReturnPointer LValue (i.e. store into it, load from it etc).
                    let llty = fcx.fn_ty.ret.original_ty.ptr_to();
                    unsafe {
                        llvm::LLVMGetUndef(llty.to_ref())
                    }
                };
                let fn_return_ty = bcx.monomorphize(&self.mir.return_ty);
                let return_ty = fn_return_ty.unwrap();
                LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty))
            },
            mir::Lvalue::Projection(ref projection) => {
                let tr_base = self.trans_lvalue(bcx, &projection.base);
                let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem);
                let projected_ty = bcx.monomorphize(&projected_ty);

                let project_index = |llindex| {
                    let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty {
                        // Slices already point to the array element type.
                        bcx.inbounds_gep(tr_base.llval, &[llindex])
                    } else {
                        let zero = common::C_uint(bcx.ccx(), 0u64);
                        bcx.inbounds_gep(tr_base.llval, &[zero, llindex])
                    };
                    (element, ptr::null_mut())
                };

                let (llprojected, llextra) = match projection.elem {
                    mir::ProjectionElem::Deref => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
                            (base::load_ty_builder(bcx, tr_base.llval, base_ty),
                             ptr::null_mut())
                        } else {
                            load_fat_ptr(bcx, tr_base.llval)
                        }
                    }
                    mir::ProjectionElem::Field(ref field, _) => {
                        let base_ty = tr_base.ty.to_ty(tcx);
                        let base_repr = adt::represent_type(ccx, base_ty);
                        let discr = match tr_base.ty {
                            LvalueTy::Ty { .. } => 0,
                            LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
                        };
                        let discr = discr as u64;
                        let is_sized = common::type_is_sized(tcx, projected_ty.to_ty(tcx));
                        let base = if is_sized {
                            adt::MaybeSizedValue::sized(tr_base.llval)
                        } else {
                            adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra)
                        };
                        let llprojected = adt::trans_field_ptr_builder(bcx, &base_repr, base,
                                                                       Disr(discr), field.index());
                        let llextra = if is_sized {
                            ptr::null_mut()
                        } else {
                            tr_base.llextra
                        };
                        (llprojected, llextra)
                    }
                    mir::ProjectionElem::Index(ref index) => {
                        let index = self.trans_operand(bcx, index);
                        project_index(self.prepare_index(bcx, index.immediate()))
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: false,
                                                         min_length: _ } => {
                        let lloffset = C_uint(bcx.ccx(), offset);
                        project_index(self.prepare_index(bcx, lloffset))
                    }
                    mir::ProjectionElem::ConstantIndex { offset,
                                                         from_end: true,
                                                         min_length: _ } => {
                        let lloffset = C_uint(bcx.ccx(), offset);
                        let lllen = self.lvalue_len(bcx, tr_base);
                        let llindex = bcx.sub(lllen, lloffset);
                        project_index(self.prepare_index(bcx, llindex))
                    }
                    mir::ProjectionElem::Downcast(..) => {
                        (tr_base.llval, tr_base.llextra)
                    }
                };
                LvalueRef {
                    llval: llprojected,
                    llextra: llextra,
                    ty: projected_ty,
                }
            }
        }
    }

    /// Adjust the bitwidth of an index since LLVM is less forgiving
    /// than we are.
    ///
    /// nmatsakis: is this still necessary? Not sure.
    fn prepare_index(&mut self,
                     bcx: &BlockAndBuilder<'bcx, 'tcx>,
                     llindex: ValueRef)
                     -> ValueRef
    {
        let ccx = bcx.ccx();
        let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex));
        let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type());
        if index_size < int_size {
            bcx.zext(llindex, ccx.int_type())
        } else if index_size > int_size {
            bcx.trunc(llindex, ccx.int_type())
        } else {
            llindex
        }
    }
}
<|endoftext|>