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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
pub use crate::ll::bytecode::{FunctionParameterCount, MethodParameterCount};
use crate::{
ll::{bytecode::Environment, gc::Memory, value::RawValue},
Error, IntoValue, LanguageErrorKind, RawForeignFunction, TryFromValue, Value,
};
fn create_rawff(
f: impl Fn(&Environment, &mut Memory, &[RawValue]) -> Result<RawValue, LanguageErrorKind> + 'static,
) -> RawForeignFunction {
Box::new(f)
}
/// Arguments passed to a varargs function.
///
/// This is a wrapper that does things like type-checking and arity-checking.
#[derive(Debug)]
pub struct Arguments<'a> {
this: RawValue,
inner: &'a [RawValue],
env: &'a Environment,
}
impl<'a> Arguments<'a> {
/// Creates a new [`Arguments`] from a raw argument list, as is passed into a
/// [raw function][RawForeignFunction].
pub fn new(raw_arguments: &'a [RawValue], env: &'a Environment) -> Self {
// Skip the first argument, which is `self` (or the currently called function).
Self { this: raw_arguments[0], inner: &raw_arguments[1..], env }
}
/// Returns the number of arguments passed to the function.
pub fn count(&self) -> usize {
self.inner.len()
}
/// Raises an error if there weren't exactly `n` arguments passed to the function.
pub fn expect_exactly(&self, n: usize) -> Result<(), Error> {
if self.count() != n {
Err(Error::ArgumentCount { expected: n, got: self.count() })
} else {
Ok(())
}
}
/// Raises an error if there weren't at least `n` arguments passed to the function.
pub fn expect_at_least(&self, n: usize) -> Result<(), Error> {
if self.count() < n {
Err(Error::ArgumentCount { expected: n, got: self.count() })
} else {
Ok(())
}
}
/// Returns the `self` parameter as a value.
pub fn raw_self(&self) -> &RawValue {
&self.this
}
/// Returns the `n`th argument, or `None` if the argument is not present.
pub fn nth(&self, n: usize) -> Option<&RawValue> {
self.inner.get(n)
}
/// Returns the `n`th argument converted into the given type. Returns an error if there aren't
/// enough arguments.
pub fn get<T>(&self, n: usize) -> Result<T, Error>
where
T: TryFromValue,
{
let value = self.inner.get(n).cloned().unwrap_or(RawValue::from(()));
T::try_from_value(&Value::from_raw(value), self.env).map_err(|error| {
if let Error::TypeMismatch { expected, got } = error {
Error::ArgumentTypeMismatch { index: n, expected, got }
} else {
error
}
})
}
/// Returns the raw array of arguments.
pub fn array(&self) -> &[RawValue] {
self.inner
}
}
/// Wrapper struct for marking functions that use the with-raw-self calling convention.
///
/// This `Deref`s to the inner value.
#[derive(Debug)]
#[repr(transparent)]
pub struct RawSelf<'a>(pub(crate) &'a RawValue);
impl std::ops::Deref for RawSelf<'_> {
type Target = RawValue;
fn deref(&self) -> &Self::Target {
self.0
}
}
/// A Rust function that can be called from Mica.
///
/// To spare you the time needed to decipher the absolutely unreadable autogenerated
/// implementations of this trait, functions with the following signatures are supported:
/// - Constant number of type checked arguments
/// - `fn (A, B, C, ...) -> R` where
/// - Each argument: [`TryFromValue`]
/// - `R`: [`Into`]`<`[`Value`]`>`
/// - `fn (A, B, C, ...) -> Result<R, E>`
/// - Each argument: [`TryFromValue`]
/// - `R`: [`Into`]`<`[`Value`]`>`
/// - `fn (Self, A, B, C, ...) -> R` where
/// - `Self`: [`SelfFromRawValue`][`crate::SelfFromRawValue`] or
/// [`MutSelfFromRawValue`][`crate::MutSelfFromRawValue`]
/// - Each argument after `Self`: [`TryFromValue`]
/// - `R`: [`Into`]`<`[`Value`]`>`
/// - `fn (Self, A, B, C, ...) -> Result<R, E>`
/// - `Self`: [`SelfFromRawValue`][`crate::SelfFromRawValue`] or
/// [`MutSelfFromRawValue`][`crate::MutSelfFromRawValue`]
/// - Each argument after `Self`: [`TryFromValue`]
/// - `R`: [`Into`]`<`[`Value`]`>`
/// - Due to a limitation in Rust's type system, a maximum of 8 arguments is supported now. If
/// more is needed, use the varargs versions described below.
/// - Variable number of dynamically typed arguments
/// - `fn (`[`Arguments`]`) -> R` where `R`: [`Into`]`<`[`Value`]`>`
/// - `fn (`[`Arguments`]`) -> Result<R, E>` where
/// - `R`: [`Into`]`<`[`Value`]`>`
/// - `E`: [`std::error::Error`]
///
/// The generic parameter `V` is not used inside the trait. Its only purpose is to allow for
/// multiple overlapping implementations of a trait for the same type. See [`ffvariants`] for more
/// information.
pub trait ForeignFunction<V> {
/// The type used for parameter counts in this kind of foreign functions.
///
/// This is either [`FunctionParameterCount`] or [`MethodParameterCount`], depending on whether
/// a function is usable as a bare function or a method.
type ParameterCount;
/// The number of parameters this function has, or `None` if the function accepts a variable
/// number of arguments.
///
/// The default implementation returns `None`.
const PARAMETER_COUNT: Self::ParameterCount;
/// Converts the function to a `RawForeignFunction`.
fn into_raw_foreign_function(self) -> RawForeignFunction;
}
/// Variants of `ForeignFunction`.
///
/// This is a bit of a hack around Rust's type system not supporting disjoint generic
/// implementations. Each implementation has a corresponding marker type in this module, such that
/// eg. the traits `ForeignFunction<ffvariants::Fallible>` and
/// `ForeignFunction<ffvariants::Infallible>` are different, but can both be matched by using a
/// generic parameter.
///
/// As long as all implementations are disjoint, Rust will happily infer all the types. If any
/// implementations end up not being disjoint, that's bad - Rust will complain about types being
/// ambiguous, but that should be pretty easy to notice.
#[allow(missing_debug_implementations)]
pub mod ffvariants {
use std::marker::PhantomData;
// For the type system to accept our implemenations, the types of the function's parameters must
// appear somewhere in the trait or the implemented self type. We can't control the self type,
// so we put them into a generic parameter here.
// The PhantomData inside suppresses an "unused generic parameter" error and prevents
// construction of the two structs (because it's a private field).
/// A bare fallible function.
pub struct Fallible<Args>(PhantomData<Args>);
/// A bare infallible function.
pub struct Infallible<Args>(PhantomData<Args>);
/// A bare varargs fallible function.
pub enum VarargsFallible {}
/// A bare varargs infallible function.
pub enum VarargsInfallible {}
mod bare {
pub trait Sealed {}
impl<Args> Sealed for super::Fallible<Args> {}
impl<Args> Sealed for super::Infallible<Args> {}
impl Sealed for super::VarargsFallible {}
impl Sealed for super::VarargsInfallible {}
}
/// Marker trait for all functions that _don't_ accept a `self` reference as the first
/// parameter, and may accept a variable number of arguments.
///
/// See also [`Method`] and [`BareExactArgs`].
pub trait BareMaybeVarargs: bare::Sealed {}
impl<Args> BareMaybeVarargs for Fallible<Args> {}
impl<Args> BareMaybeVarargs for Infallible<Args> {}
impl BareMaybeVarargs for VarargsFallible {}
impl BareMaybeVarargs for VarargsInfallible {}
/// Marker trait for all functions that don't accept a `self` reference as the first
/// parameter and do not accept a variable number of arguments.
///
/// See also [`Method`] and [`BareMaybeVarargs`].
pub trait BareExactArgs: bare::Sealed {}
impl<Args> BareExactArgs for Fallible<Args> {}
impl<Args> BareExactArgs for Infallible<Args> {}
/// A fallible function with `RawSelf`.
pub struct FallibleRawSelf<Args>(PhantomData<Args>);
/// An infallible function with `RawSelf`.
pub struct InfallibleRawSelf<Args>(PhantomData<Args>);
// S is the self type (`ImmutableSelf` or `MutableSelf`).
/// A fallible function with typed `self`.
pub struct FallibleSelf<S, Args>(PhantomData<(S, Args)>);
/// An infallible function with typed `self`.
pub struct InfallibleSelf<S, Args>(PhantomData<(S, Args)>);
mod method {
pub trait Sealed {}
impl<Args> Sealed for super::FallibleRawSelf<Args> {}
impl<Args> Sealed for super::InfallibleRawSelf<Args> {}
impl<S, Args> Sealed for super::FallibleSelf<S, Args> {}
impl<S, Args> Sealed for super::InfallibleSelf<S, Args> {}
}
/// Defines the common [`Receiver`][`Self::Receiver`] type of [`ImmutableSelf`]
/// and [`MutableSelf`].
pub trait Receiver {
/// The type that receives the method call.
type Receiver;
}
/// Marker for a method that has an immutable `self` (`&self`).
pub struct ImmutableSelf<S>(PhantomData<S>);
/// Marker for a method that has a mutable `self` (`&mut self`).
pub struct MutableSelf<S>(PhantomData<S>);
impl<S> Receiver for ImmutableSelf<S> {
type Receiver = S;
}
impl<S> Receiver for MutableSelf<S> {
type Receiver = S;
}
/// Marker trait for all functions that accept a `self` reference as the first parameter.
///
/// See also [`BareMaybeVarargs`] and [`BareExactArgs`].
pub trait Method<S>: method::Sealed
where
S: ?Sized,
{
}
impl<'s, Args> Method<super::RawSelf<'s>> for FallibleRawSelf<Args> {}
impl<'s, Args> Method<super::RawSelf<'s>> for InfallibleRawSelf<Args> {}
impl<S, Args> Method<S::Receiver> for FallibleSelf<S, Args> where S: Receiver {}
impl<S, Args> Method<S::Receiver> for InfallibleSelf<S, Args> where S: Receiver {}
}
impl<Ret, Err, F> ForeignFunction<ffvariants::VarargsFallible> for F
where
Ret: IntoValue + 'static,
Err: std::error::Error + 'static,
F: Fn(Arguments) -> Result<Ret, Err> + 'static,
{
type ParameterCount = FunctionParameterCount;
const PARAMETER_COUNT: Self::ParameterCount = FunctionParameterCount::Varargs;
fn into_raw_foreign_function(self) -> RawForeignFunction {
create_rawff(move |env, gc, args| {
self(Arguments::new(args, env))
.map(|value| value.into_value_with_environment(env).to_raw(gc))
.map_err(|error| LanguageErrorKind::User(Box::new(error)))
})
}
}
impl<Ret, F> ForeignFunction<ffvariants::VarargsInfallible> for F
where
Ret: IntoValue + 'static,
F: Fn(Arguments) -> Ret + 'static,
{
type ParameterCount = FunctionParameterCount;
const PARAMETER_COUNT: Self::ParameterCount = FunctionParameterCount::Varargs;
fn into_raw_foreign_function(self) -> RawForeignFunction {
create_rawff(move |env, gc, args| {
Ok(self(Arguments::new(args, env)).into_value_with_environment(env).to_raw(gc))
})
}
}