summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-06-19 17:45:00 +0200
committerLaurenz <laurmaedje@gmail.com>2025-06-20 17:32:37 +0200
commit3602d06a155a0567fe2b2e75a4d5970578d0f14f (patch)
tree0e609071791c7ab92f4774acc6e3c775c23482ec
parent15302dbe7a6bd04125c4a56fee24bbbacfb4cc2f (diff)
Support for generating native functions at runtime
-rw-r--r--crates/typst-library/src/foundations/func.rs34
-rw-r--r--crates/typst-macros/src/func.rs8
2 files changed, 32 insertions, 10 deletions
diff --git a/crates/typst-library/src/foundations/func.rs b/crates/typst-library/src/foundations/func.rs
index 27eb34ea..9ef81289 100644
--- a/crates/typst-library/src/foundations/func.rs
+++ b/crates/typst-library/src/foundations/func.rs
@@ -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 {
diff --git a/crates/typst-macros/src/func.rs b/crates/typst-macros/src/func.rs
index b8ab7a36..e953dc37 100644
--- a/crates/typst-macros/src/func.rs
+++ b/crates/typst-macros/src/func.rs
@@ -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()),
}
}
}