1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::CStr;
use std::fmt;
use super::address::SBAddress;
use super::filespec::SBFileSpec;
use super::stream::SBStream;
use sys;

/// A lexical block.
pub struct SBBlock {
    /// The underlying raw `SBBlockRef`.
    pub raw: sys::SBBlockRef,
}

impl SBBlock {
    /// Construct a new `SBBlock`.
    pub fn wrap(raw: sys::SBBlockRef) -> SBBlock {
        SBBlock { raw: raw }
    }

    /// Construct a new `Some(SBBlock)` or `None`.
    pub fn maybe_wrap(raw: sys::SBBlockRef) -> Option<SBBlock> {
        if unsafe { sys::SBBlockIsValid(raw) != 0 } {
            Some(SBBlock { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBBlock` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBBlockIsValid(self.raw) != 0 }
    }

    /// Does this block represent an inlined function?
    pub fn is_inlined(&self) -> bool {
        unsafe { sys::SBBlockIsInlined(self.raw) != 0 }
    }

    /// Get the function name if this block represents an inlined function.
    pub fn inlined_name(&self) -> &str {
        unsafe {
            match CStr::from_ptr(sys::SBBlockGetInlinedName(self.raw)).to_str() {
                Ok(s) => s,
                _ => panic!("Invalid string?"),
            }
        }
    }

    /// Get the call site file if this block represents an inlined function.
    pub fn inlined_call_site_file(&self) -> Option<SBFileSpec> {
        SBFileSpec::maybe_wrap(unsafe { sys::SBBlockGetInlinedCallSiteFile(self.raw) })
    }

    /// Get the call site line number if this block represents an inlined function.
    pub fn inlined_call_site_line(&self) -> Option<u32> {
        let line = unsafe { sys::SBBlockGetInlinedCallSiteLine(self.raw) };
        if line > 0 { Some(line) } else { None }
    }

    /// Get the call site column number if this block represents an inlined function.
    pub fn inlined_call_site_column(&self) -> Option<u32> {
        let column = unsafe { sys::SBBlockGetInlinedCallSiteColumn(self.raw) };
        if column > 0 { Some(column) } else { None }
    }

    /// Get the parent block
    pub fn parent(&self) -> Option<SBBlock> {
        SBBlock::maybe_wrap(unsafe { sys::SBBlockGetParent(self.raw) })
    }

    /// Get the inlined block that is or contains this block.
    pub fn containing_inlined_block(&self) -> Option<SBBlock> {
        SBBlock::maybe_wrap(unsafe { sys::SBBlockGetContainingInlinedBlock(self.raw) })
    }

    /// Get the sibling block for this block.
    pub fn sibling(&self) -> Option<SBBlock> {
        SBBlock::maybe_wrap(unsafe { sys::SBBlockGetSibling(self.raw) })
    }

    /// Get the first child block for this block.
    pub fn first_child(&self) -> Option<SBBlock> {
        SBBlock::maybe_wrap(unsafe { sys::SBBlockGetFirstChild(self.raw) })
    }

    /// The number of address ranges associated with this block.
    pub fn num_ranges(&self) -> u32 {
        unsafe { sys::SBBlockGetNumRanges(self.raw) }
    }

    /// Get the start address of an address range.
    pub fn range_start_address(&self, idx: u32) -> SBAddress {
        SBAddress { raw: unsafe { sys::SBBlockGetRangeStartAddress(self.raw, idx) } }
    }

    /// Get the end address of an address range.
    pub fn range_end_address(&self, idx: u32) -> SBAddress {
        SBAddress { raw: unsafe { sys::SBBlockGetRangeEndAddress(self.raw, idx) } }
    }

    /// Given an address, find out which address range it is part of.
    pub fn range_index_for_block_address(&self, block_address: &SBAddress) -> u32 {
        unsafe { sys::SBBlockGetRangeIndexForBlockAddress(self.raw, block_address.raw) }
    }
}

impl fmt::Debug for SBBlock {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBBlockGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBBlock {{ {} }}", stream.data())
    }
}

impl Drop for SBBlock {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBBlock(self.raw) };
    }
}

#[cfg(feature = "graphql")]
graphql_object!(SBBlock: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    field is_inlined() -> bool {
        self.is_inlined()
    }

    field inlined_name() -> &str {
        self.inlined_name()
    }

    field inlined_call_site_file() -> Option<SBFileSpec> {
        self.inlined_call_site_file()
    }

    // TODO(bm) This should be u32
    field inlined_call_site_line() -> Option<i64> {
        self.inlined_call_site_line().map(|i| i as i64)
    }

    // TODO(bm) This should be u32
    field inlined_call_site_column() -> Option<i64> {
        self.inlined_call_site_column().map(|i| i as i64)
    }
});