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
//! Frames ensure Julia's garbage collector is properly managed.
//!
//! Julia data is freed by the GC when it's not in use. You will need to use frames to do things
//! like calling Julia functions and creating new values; this ensures the values created with a
//! specific frame are protected from garbage collection until that frame goes out of scope.
//!
//! Frames can be nested, the two frame types that currently exist can be freely mixed. The main
//! difference between the two is that a [`StaticFrame`] is created with a definite capacity,
//! while a [`DynamicFrame`] will dynamically grow its capacity whenever a value is created or a
//! function is called. A [`StaticFrame`] is more efficient, a [`DynamicFrame`] is easier to use.
//! Creating a nested frame takes no space in the current frame.
//!
//! Frames have a lifetime, `'frame`. This lifetime ensures that a [`Value`] can only be used as
//! long as the frame that protects it has not been dropped.
//!
//! Most functionality that frames implement is defined in the [`Frame`] trait.
//!
//! [`StaticFrame`]: struct.StaticFrame.html
//! [`DynamicFrame`]: struct.DynamicFrame.html
//! [`Julia::frame`]: ../struct.Julia.html#method.frame
//! [`Julia::dynamic_frame`]: ../struct.Julia.html#method.dynamic_frame
//! [`Value`]: ../value/struct.Value.html
//! [`Frame`]: ../traits/trait.Frame.html

use crate::error::JlrsResult;
use crate::stack::{Dynamic, FrameIdx, StackView, Static};
use std::marker::PhantomData;

/// A `StaticFrame` is a frame that has a definite number of slots on the GC stack. With some
/// exceptions, creating new `Value`s and calling them require one slot each. Rather than using
/// new slots on the GC stack when a slot is needed, a `StaticFrame` uses the slots it acquired on
/// creation. See the documentation in the [`value`] module for more information about the costs.
/// You get access to a `StaticFrame` by calling [`Julia::frame`] or [`Frame::frame`], most of
/// their functionality is defined in the [`Frame`] trait.
///
/// [`value`]: ../value/index.html
/// [`Julia::frame`]: ../struct.Julia.html#method.frame
/// [`Frame::frame`]: ../traits/trait.Frame.html#method.frame
/// [`Frame`]: ../traits/trait.Frame.html
pub struct StaticFrame<'frame> {
    pub(crate) idx: FrameIdx,
    pub(crate) memory: StackView<'frame, Static>,
    pub(crate) capacity: usize,
    pub(crate) len: usize,
}

impl<'frame> StaticFrame<'frame> {
    pub(crate) unsafe fn with_capacity(
        idx: FrameIdx,
        capacity: usize,
        memory: StackView<'frame, Static>,
    ) -> StaticFrame<'frame> {
        StaticFrame {
            idx,
            memory,
            capacity,
            len: 0,
        }
    }

    pub(crate) unsafe fn nested_frame<'nested>(
        &'nested mut self,
        capacity: usize,
    ) -> JlrsResult<StaticFrame<'nested>> {
        let idx = self.memory.new_frame(capacity)?;
        Ok(StaticFrame {
            idx,
            memory: self.memory.nest_static(),
            capacity,
            len: 0,
        })
    }

    /// Returns the total number of slots.
    pub fn capacity(&self) -> usize {
        self.capacity
    }
}

impl<'frame> Drop for StaticFrame<'frame> {
    fn drop(&mut self) {
        unsafe {
            self.memory.pop_frame(self.idx);
        }
    }
}

/// A `DynamicFrame` is a frame that has a dynamic number of slots on the GC stack. With some
/// exceptions, creating new `Value`s and calling them require one slot each. A `DynamicFrame`
/// acquires a new slot every time one is needed. See the documentation in the [`value`] module
/// for more information about the costs. You get access to a `DynamicFrame` by calling
/// [`Julia::dynamic_frame`] or [`Frame::dynamic_frame`], most of
/// their functionality is defined in the [`Frame`] trait.
///
/// [`value`]: ../value/index.html
/// [`Julia::dynamic_frame`]: ../struct.Julia.html#method.dynamic_frame
/// [`Frame::dynamic_frame`]: ../traits/trait.Frame.html#method.dynamic_frame
/// [`Frame`]: ../traits/trait.Frame.html
pub struct DynamicFrame<'frame> {
    pub(crate) idx: FrameIdx,
    pub(crate) memory: StackView<'frame, Dynamic>,
    pub(crate) len: usize,
}

impl<'frame> DynamicFrame<'frame> {
    pub(crate) unsafe fn new(idx: FrameIdx, memory: StackView<'frame, Dynamic>) -> Self {
        DynamicFrame {
            idx,
            memory,
            len: 0,
        }
    }

    pub(crate) unsafe fn nested_frame<'nested>(
        &'nested mut self,
    ) -> JlrsResult<DynamicFrame<'nested>> {
        let idx = self.memory.new_frame()?;
        Ok(DynamicFrame {
            idx,
            memory: self.memory.nest_dynamic(),
            len: 0,
        })
    }
}

impl<'frame> Drop for DynamicFrame<'frame> {
    fn drop(&mut self) {
        unsafe {
            self.memory.pop_frame(self.idx);
        }
    }
}

/// An `Output` is a slot of a frame that has been reserved for later use. It can be used to
/// extend the lifetime of the result of a function call to the `Output`'s lifetime. You can
/// create an output by calling [`Frame::output`].
///
/// [`Frame::output`]: ../traits/trait.Frame.html#method.output
pub struct Output<'frame> {
    pub(crate) offset: usize,
    _marker: PhantomData<&'frame ()>,
}

impl<'frame> Output<'frame> {
    pub(crate) unsafe fn new(offset: usize) -> Self {
        Output {
            offset,
            _marker: PhantomData,
        }
    }
}