From 30d6c070c1de3da708cacf459f4cc016c374563a Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 19 Mar 2023 23:49:35 +0100 Subject: [PATCH] More efficient function representation --- macros/src/func.rs | 7 +++-- src/eval/func.rs | 72 +++++++++++++++++++++----------------------- src/model/element.rs | 12 ++++---- 3 files changed, 45 insertions(+), 46 deletions(-) diff --git a/macros/src/func.rs b/macros/src/func.rs index 87324120f..07ecdd78c 100644 --- a/macros/src/func.rs +++ b/macros/src/func.rs @@ -115,8 +115,8 @@ fn create(func: &Func) -> TokenStream { let params = params.iter().map(create_param_info); quote! { #[doc = #docs] - #vis fn #ident() -> ::typst::eval::NativeFunc { - ::typst::eval::NativeFunc { + #vis fn #ident() -> &'static ::typst::eval::NativeFunc { + static FUNC: ::typst::eval::NativeFunc = ::typst::eval::NativeFunc { func: |vm, args| { #(#handlers)* #[allow(unreachable_code)] @@ -130,7 +130,8 @@ fn create(func: &Func) -> TokenStream { returns: ::std::vec![#(#returns),*], category: #category, }), - } + }; + &FUNC } } } diff --git a/src/eval/func.rs b/src/eval/func.rs index ef042d6d0..082963200 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -20,42 +20,42 @@ use crate::World; #[derive(Clone, Hash)] pub struct Func { /// The internal representation. - repr: Arc>, + repr: Repr, /// The span with which errors are reported when this function is called. span: Span, } /// The different kinds of function representations. -#[derive(Hash)] +#[derive(Clone, PartialEq, Hash)] enum Repr { /// A native Rust function. - Native(NativeFunc), + Native(&'static NativeFunc), /// A function for an element. Elem(ElemFunc), /// A user-defined closure. - Closure(Closure), + Closure(Arc>), /// A nested function with pre-applied arguments. - With(Func, Args), + With(Arc<(Func, Args)>), } impl Func { /// The name of the function. pub fn name(&self) -> Option<&str> { - match &**self.repr { + match &self.repr { Repr::Native(native) => Some(native.info.name), Repr::Elem(func) => Some(func.info().name), Repr::Closure(closure) => closure.name.as_deref(), - Repr::With(func, _) => func.name(), + Repr::With(arc) => arc.0.name(), } } /// Extract details the function. pub fn info(&self) -> Option<&FuncInfo> { - match &**self.repr { + match &self.repr { Repr::Native(native) => Some(&native.info), Repr::Elem(func) => Some(func.info()), - Repr::With(func, _) => func.info(), - _ => None, + Repr::Closure(_) => None, + Repr::With(arc) => arc.0.info(), } } @@ -74,10 +74,10 @@ impl Func { /// The number of positional arguments this function takes, if known. pub fn argc(&self) -> Option { - match &**self.repr { + match &self.repr { Repr::Closure(closure) => closure.argc(), - Repr::With(wrapped, applied) => Some(wrapped.argc()?.saturating_sub( - applied.items.iter().filter(|arg| arg.name.is_none()).count(), + Repr::With(arc) => Some(arc.0.argc()?.saturating_sub( + arc.1.items.iter().filter(|arg| arg.name.is_none()).count(), )), _ => None, } @@ -85,7 +85,7 @@ impl Func { /// Call the function with the given arguments. pub fn call_vm(&self, vm: &mut Vm, mut args: Args) -> SourceResult { - match &**self.repr { + match &self.repr { Repr::Native(native) => { let value = (native.func)(vm, &mut args)?; args.finish()?; @@ -113,9 +113,9 @@ impl Func { args, ) } - Repr::With(wrapped, applied) => { - args.items = applied.items.iter().cloned().chain(args.items).collect(); - return wrapped.call_vm(vm, args); + Repr::With(arc) => { + args.items = arc.1.items.iter().cloned().chain(args.items).collect(); + return arc.0.call_vm(vm, args); } } } @@ -137,15 +137,12 @@ impl Func { /// Apply the given arguments to the function. pub fn with(self, args: Args) -> Self { let span = self.span; - Self { - repr: Arc::new(Prehashed::new(Repr::With(self, args))), - span, - } + Self { repr: Repr::With(Arc::new((self, args))), span } } /// Extract the element function, if it is one. pub fn element(&self) -> Option { - match **self.repr { + match self.repr { Repr::Elem(func) => Some(func), _ => None, } @@ -169,10 +166,7 @@ impl PartialEq for Func { impl From for Func { fn from(repr: Repr) -> Self { - Self { - repr: Arc::new(Prehashed::new(repr)), - span: Span::detached(), - } + Self { repr, span: Span::detached() } } } @@ -190,28 +184,32 @@ pub struct NativeFunc { pub info: Lazy, } +impl PartialEq for NativeFunc { + fn eq(&self, other: &Self) -> bool { + self.func as usize == other.func as usize + } +} + +impl Eq for NativeFunc {} + impl Hash for NativeFunc { fn hash(&self, state: &mut H) { (self.func as usize).hash(state); } } -impl From for Func { - fn from(native: NativeFunc) -> Self { +impl From<&'static NativeFunc> for Func { + fn from(native: &'static NativeFunc) -> Self { Repr::Native(native).into() } } -cast_to_value! { - v: NativeFunc => Value::Func(v.into()) -} - impl From for Value where - F: Fn() -> NativeFunc, + F: Fn() -> &'static NativeFunc, { fn from(f: F) -> Self { - f().into() + Value::Func(f().into()) } } @@ -294,7 +292,7 @@ impl Closure { depth: usize, mut args: Args, ) -> SourceResult { - let closure = match &**this.repr { + let closure = match &this.repr { Repr::Closure(closure) => closure, _ => panic!("`this` must be a closure"), }; @@ -347,7 +345,7 @@ impl Closure { result } - /// The number of positional arguments this function takes, if known. + /// The number of positional arguments this closure takes, if known. fn argc(&self) -> Option { if self.sink.is_some() { return None; @@ -359,7 +357,7 @@ impl Closure { impl From for Func { fn from(closure: Closure) -> Self { - Repr::Closure(closure).into() + Repr::Closure(Arc::new(Prehashed::new(closure))).into() } } diff --git a/src/model/element.rs b/src/model/element.rs index e25b22b4c..c6738582a 100644 --- a/src/model/element.rs +++ b/src/model/element.rs @@ -88,12 +88,6 @@ impl Debug for ElemFunc { } } -impl Hash for ElemFunc { - fn hash(&self, state: &mut H) { - state.write_usize(self.0 as *const _ as usize); - } -} - impl Eq for ElemFunc {} impl PartialEq for ElemFunc { @@ -102,6 +96,12 @@ impl PartialEq for ElemFunc { } } +impl Hash for ElemFunc { + fn hash(&self, state: &mut H) { + state.write_usize(self.0 as *const _ as usize); + } +} + cast_from_value! { ElemFunc, v: Func => v.element().ok_or("expected element function")?,