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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Static function data.

use std::rc::Rc;

use super::{Chunk, Environment};
use crate::ll::{
    codegen::variables::{LocalIndex, UpvalueIndex},
    error::LanguageErrorKind,
    gc::Memory,
    value::RawValue,
};

/// The kind of an upvalue capture.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CaptureKind {
    /// Capture a local from the current scope.
    Local(LocalIndex),
    /// Capture an existing upvalue from the current closure.
    Upvalue(UpvalueIndex),
}

/// The signature of a raw foreign function.
pub type ForeignFunction =
    Box<dyn Fn(&Environment, &mut Memory, &[RawValue]) -> Result<RawValue, LanguageErrorKind>>;

/// The kind of a controlling function.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Control {
    GcCollect,
}

/// The kind of the function (bytecode or FFI).
pub enum FunctionKind {
    Bytecode { chunk: Rc<Chunk>, captured_locals: Vec<CaptureKind> },
    Foreign(ForeignFunction),
    Control(Control),
}

impl std::fmt::Debug for FunctionKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Bytecode { chunk, captured_locals } => f
                .debug_struct("Bytecode")
                .field("chunk", chunk)
                .field("captured_locals", captured_locals)
                .finish(),
            Self::Foreign(..) => f.debug_struct("Foreign").finish_non_exhaustive(),
            Self::Control(ctl) => f.debug_tuple("Control").field(ctl).finish(),
        }
    }
}

/// The number of parameters in a bare function.
///
/// Bare functions may have fixed or variable numbers of parameters, with up to 65535 arguments
/// supported per call.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum FunctionParameterCount {
    /// Accept only a specific number of arguments.
    // Implementation note: Since this count always comes from the syntactic parameter list,
    // this count never includes `self`, even in methods.
    Fixed(u16),
    /// Accept any amount of arguments.
    Varargs,
}

impl FunctionParameterCount {
    /// Attempts to get a fixed parameter count, returning `None` if the count is varargs.
    pub fn to_fixed(self) -> Option<u16> {
        if let Self::Fixed(v) = self {
            Some(v)
        } else {
            None
        }
    }
}

impl From<u16> for FunctionParameterCount {
    fn from(count: u16) -> Self {
        Self::Fixed(count)
    }
}

impl std::fmt::Debug for FunctionParameterCount {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if f.alternate() {
            match self {
                Self::Fixed(num) => write!(f, "Fixed({num})"),
                Self::Varargs => write!(f, "Varargs"),
            }
        } else {
            match self {
                Self::Fixed(num) => write!(f, "{num}"),
                Self::Varargs => write!(f, "..."),
            }
        }
    }
}

/// The number of parameters in a method.
///
/// Unlike [bare functions][FunctionParameterCount], methods can only have fixed parameter counts
/// and can accept up to 255 arguments.
///
/// Internally, this parameter count includes the implicit `self` parameter.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct MethodParameterCount(u8);

/// Converting functions from various sources of parameter counts. This usually doesn't need to be
/// used in application code, unless you're dealing with the `ll` API.
impl MethodParameterCount {
    pub fn from_count_without_self(count: impl TryInto<u8>) -> Result<Self, LanguageErrorKind> {
        count
            .try_into()
            .ok()
            .and_then(|x| x.checked_add(1))
            .map(Self)
            .ok_or(LanguageErrorKind::TooManyParameters)
    }

    pub const fn from_count_with_self(count: u8) -> Self {
        Self(count)
    }

    pub fn from_fixed_function_parameter_count(count: u16) -> Result<Self, LanguageErrorKind> {
        Self::from_count_without_self(
            u8::try_from(count).map_err(|_| LanguageErrorKind::TooManyParameters)?,
        )
    }

    pub const fn to_count_without_self(self) -> u8 {
        self.0 - 1
    }

    /// Converts the type-safe [`MethodParameterCount`] into a raw [`u8`] that includes all
    /// parameters.
    ///
    /// You usually want to use this when interfacing with the VM, which does not differentiate
    /// `self` from other arguments.
    pub const fn to_count_with_self(self) -> u8 {
        self.0
    }
}

impl From<u8> for MethodParameterCount {
    fn from(count: u8) -> Self {
        Self(count)
    }
}

impl std::fmt::Debug for MethodParameterCount {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// A function prototype.
#[derive(Debug)]
pub struct Function {
    pub name: Rc<str>,
    pub parameter_count: FunctionParameterCount,

    pub kind: FunctionKind,

    /// Set to `true` if the function is to be hidden in stack traces.
    ///
    /// This is useful for functions that are implementation details, such as trait function shims.
    pub hidden_in_stack_traces: bool,
}