Support for generating native functions at runtime

This commit is contained in:
Laurenz 2025-06-19 17:45:00 +02:00
parent 15302dbe7a
commit 3602d06a15
2 changed files with 32 additions and 10 deletions

View File

@ -307,7 +307,7 @@ impl Func {
) -> SourceResult<Value> {
match &self.repr {
Repr::Native(native) => {
let value = (native.function)(engine, context, &mut args)?;
let value = (native.function.0)(engine, context, &mut args)?;
args.finish()?;
Ok(value)
}
@ -491,8 +491,8 @@ pub trait NativeFunc {
/// Defines a native function.
#[derive(Debug)]
pub struct NativeFuncData {
/// Invokes the function from Typst.
pub function: fn(&mut Engine, Tracked<Context>, &mut Args) -> SourceResult<Value>,
/// The implementation of the function.
pub function: NativeFuncPtr,
/// The function's normal name (e.g. `align`), as exposed to Typst.
pub name: &'static str,
/// The function's title case name (e.g. `Align`).
@ -504,11 +504,11 @@ pub struct NativeFuncData {
/// Whether this function makes use of context.
pub contextual: bool,
/// Definitions in the scope of the function.
pub scope: LazyLock<Scope>,
pub scope: DynLazyLock<Scope>,
/// A list of parameter information for each parameter.
pub params: LazyLock<Vec<ParamInfo>>,
pub params: DynLazyLock<Vec<ParamInfo>>,
/// Information about the return value of this function.
pub returns: LazyLock<CastInfo>,
pub returns: DynLazyLock<CastInfo>,
}
cast! {
@ -516,6 +516,28 @@ cast! {
self => Func::from(self).into_value(),
}
/// A pointer to a native function's implementation.
pub struct NativeFuncPtr(pub &'static NativeFuncSignature);
/// The signature of a native function's implementation.
type NativeFuncSignature =
dyn Fn(&mut Engine, Tracked<Context>, &mut Args) -> SourceResult<Value> + Send + Sync;
impl Debug for NativeFuncPtr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad("NativeFuncPtr(..)")
}
}
/// A `LazyLock` that uses a static closure for initialization instead of only
/// working with function pointers.
///
/// Can be created from a normal function or closure by prepending with a `&`,
/// e.g. `LazyLock::new(&|| "hello")`. Can be created from a dynamic closure
/// by allocating and then leaking it. This is equivalent to having it
/// statically allocated, but allows for it to be generated at runtime.
type DynLazyLock<T> = LazyLock<T, &'static (dyn Fn() -> T + Send + Sync)>;
/// Describes a function parameter.
#[derive(Debug, Clone)]
pub struct ParamInfo {

View File

@ -315,15 +315,15 @@ fn create_func_data(func: &Func) -> TokenStream {
quote! {
#foundations::NativeFuncData {
function: #closure,
function: #foundations::NativeFuncPtr(&#closure),
name: #name,
title: #title,
docs: #docs,
keywords: &[#(#keywords),*],
contextual: #contextual,
scope: ::std::sync::LazyLock::new(|| #scope),
params: ::std::sync::LazyLock::new(|| ::std::vec![#(#params),*]),
returns: ::std::sync::LazyLock::new(|| <#returns as #foundations::Reflect>::output()),
scope: ::std::sync::LazyLock::new(&|| #scope),
params: ::std::sync::LazyLock::new(&|| ::std::vec![#(#params),*]),
returns: ::std::sync::LazyLock::new(&|| <#returns as #foundations::Reflect>::output()),
}
}
}