-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Function Components & Hooks V2 (#2401)
* Make a use_hook hook with the new Hook trait. * Implement Lifetime. * Rewrites function signature. * Only apply lifetime if there're other lifetimes. * Cleanup signature rewrite logic. * Rewrite hook body. * Port some built-in hooks. * Finish porting all built-in hooks. * Port tests. * Fix tests. * Migrate to macro-based hooks. * Fix HookContext, add tests on non-possible locations. * Fix stderr for trybuild. * Add 1 more test case. * Adjust doc location. * Pretty print hook signature. * Fix Items & std::ops::Fn*. * Add use_memo. * Optimise Implementation of hooks. * Use Box to capture function value only. * Detect whether needs boxing. * Add args if boxing not needed. * Enforce hook number. * Deduplicate use_effect. * Optimise Implementation. * Update documentation. * Fix website test. Strip BoxedHook implementation from it. * Allow doc string. * Workaround doc tests. * Optimise codebase & documentation. * Fix website test. * Reduce implementation complexity. * Destructor is no more. * Documentation and macros. * Reduce heap allocation and hook complexity. * Remove Queue as well. * Prefer Generics. * Fix typo. * Remove more allocations. * Add comments. * Remove outdated comment. * Bare Function Pointer for better code size.
- Loading branch information
Showing
39 changed files
with
1,749 additions
and
917 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
use proc_macro2::Span; | ||
use proc_macro_error::emit_error; | ||
use std::sync::{Arc, Mutex}; | ||
use syn::spanned::Spanned; | ||
use syn::visit_mut::VisitMut; | ||
use syn::{ | ||
parse_quote_spanned, visit_mut, Expr, ExprCall, ExprClosure, ExprForLoop, ExprIf, ExprLoop, | ||
ExprMatch, ExprWhile, Ident, Item, | ||
}; | ||
|
||
#[derive(Debug, Default)] | ||
pub struct BodyRewriter { | ||
branch_lock: Arc<Mutex<()>>, | ||
} | ||
|
||
impl BodyRewriter { | ||
fn is_branched(&self) -> bool { | ||
self.branch_lock.try_lock().is_err() | ||
} | ||
|
||
fn with_branch<F, O>(&mut self, f: F) -> O | ||
where | ||
F: FnOnce(&mut BodyRewriter) -> O, | ||
{ | ||
let branch_lock = self.branch_lock.clone(); | ||
let _branched = branch_lock.try_lock(); | ||
f(self) | ||
} | ||
} | ||
|
||
impl VisitMut for BodyRewriter { | ||
fn visit_expr_call_mut(&mut self, i: &mut ExprCall) { | ||
let ctx_ident = Ident::new("ctx", Span::mixed_site()); | ||
|
||
// Only rewrite hook calls. | ||
if let Expr::Path(ref m) = &*i.func { | ||
if let Some(m) = m.path.segments.last().as_ref().map(|m| &m.ident) { | ||
if m.to_string().starts_with("use_") { | ||
if self.is_branched() { | ||
emit_error!( | ||
m, | ||
"hooks cannot be called at this position."; | ||
help = "move hooks to the top-level of your function."; | ||
note = "see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks" | ||
); | ||
} else { | ||
*i = parse_quote_spanned! { i.span() => ::yew::functional::Hook::run(#i, #ctx_ident) }; | ||
} | ||
|
||
return; | ||
} | ||
} | ||
} | ||
|
||
visit_mut::visit_expr_call_mut(self, i); | ||
} | ||
|
||
fn visit_expr_closure_mut(&mut self, i: &mut ExprClosure) { | ||
self.with_branch(move |m| visit_mut::visit_expr_closure_mut(m, i)) | ||
} | ||
|
||
fn visit_expr_if_mut(&mut self, i: &mut ExprIf) { | ||
for it in &mut i.attrs { | ||
visit_mut::visit_attribute_mut(self, it); | ||
} | ||
|
||
visit_mut::visit_expr_mut(self, &mut *i.cond); | ||
|
||
self.with_branch(|m| visit_mut::visit_block_mut(m, &mut i.then_branch)); | ||
|
||
if let Some(it) = &mut i.else_branch { | ||
self.with_branch(|m| visit_mut::visit_expr_mut(m, &mut *(it).1)); | ||
} | ||
} | ||
|
||
fn visit_expr_loop_mut(&mut self, i: &mut ExprLoop) { | ||
self.with_branch(|m| visit_mut::visit_expr_loop_mut(m, i)); | ||
} | ||
|
||
fn visit_expr_for_loop_mut(&mut self, i: &mut ExprForLoop) { | ||
for it in &mut i.attrs { | ||
visit_mut::visit_attribute_mut(self, it); | ||
} | ||
if let Some(it) = &mut i.label { | ||
visit_mut::visit_label_mut(self, it); | ||
} | ||
visit_mut::visit_pat_mut(self, &mut i.pat); | ||
visit_mut::visit_expr_mut(self, &mut *i.expr); | ||
|
||
self.with_branch(|m| visit_mut::visit_block_mut(m, &mut i.body)); | ||
} | ||
|
||
fn visit_expr_match_mut(&mut self, i: &mut ExprMatch) { | ||
for it in &mut i.attrs { | ||
visit_mut::visit_attribute_mut(self, it); | ||
} | ||
|
||
visit_mut::visit_expr_mut(self, &mut *i.expr); | ||
|
||
self.with_branch(|m| { | ||
for it in &mut i.arms { | ||
visit_mut::visit_arm_mut(m, it); | ||
} | ||
}); | ||
} | ||
|
||
fn visit_expr_while_mut(&mut self, i: &mut ExprWhile) { | ||
for it in &mut i.attrs { | ||
visit_mut::visit_attribute_mut(self, it); | ||
} | ||
if let Some(it) = &mut i.label { | ||
visit_mut::visit_label_mut(self, it); | ||
} | ||
|
||
self.with_branch(|m| visit_mut::visit_expr_mut(m, &mut i.cond)); | ||
self.with_branch(|m| visit_mut::visit_block_mut(m, &mut i.body)); | ||
} | ||
|
||
fn visit_item_mut(&mut self, _i: &mut Item) { | ||
// We don't do anything for items. | ||
// for components / hooks in other components / hooks, apply the attribute again. | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use proc_macro2::Span; | ||
use std::sync::{Arc, Mutex}; | ||
use syn::visit_mut::{self, VisitMut}; | ||
use syn::{ | ||
GenericArgument, Lifetime, ParenthesizedGenericArguments, Receiver, TypeBareFn, TypeImplTrait, | ||
TypeParamBound, TypeReference, | ||
}; | ||
|
||
// borrowed from the awesome async-trait crate. | ||
pub struct CollectLifetimes { | ||
pub elided: Vec<Lifetime>, | ||
pub explicit: Vec<Lifetime>, | ||
pub name: &'static str, | ||
pub default_span: Span, | ||
|
||
pub impl_trait_lock: Arc<Mutex<()>>, | ||
pub impl_fn_lock: Arc<Mutex<()>>, | ||
} | ||
|
||
impl CollectLifetimes { | ||
pub fn new(name: &'static str, default_span: Span) -> Self { | ||
CollectLifetimes { | ||
elided: Vec::new(), | ||
explicit: Vec::new(), | ||
name, | ||
default_span, | ||
|
||
impl_trait_lock: Arc::default(), | ||
impl_fn_lock: Arc::default(), | ||
} | ||
} | ||
|
||
fn is_impl_trait(&self) -> bool { | ||
self.impl_trait_lock.try_lock().is_err() | ||
} | ||
|
||
fn is_impl_fn(&self) -> bool { | ||
self.impl_fn_lock.try_lock().is_err() | ||
} | ||
|
||
fn visit_opt_lifetime(&mut self, lifetime: &mut Option<Lifetime>) { | ||
match lifetime { | ||
None => *lifetime = Some(self.next_lifetime(None)), | ||
Some(lifetime) => self.visit_lifetime(lifetime), | ||
} | ||
} | ||
|
||
fn visit_lifetime(&mut self, lifetime: &mut Lifetime) { | ||
if lifetime.ident == "_" { | ||
*lifetime = self.next_lifetime(lifetime.span()); | ||
} else { | ||
self.explicit.push(lifetime.clone()); | ||
} | ||
} | ||
|
||
fn next_lifetime<S: Into<Option<Span>>>(&mut self, span: S) -> Lifetime { | ||
let name = format!("{}{}", self.name, self.elided.len()); | ||
let span = span.into().unwrap_or(self.default_span); | ||
let life = Lifetime::new(&name, span); | ||
self.elided.push(life.clone()); | ||
life | ||
} | ||
} | ||
|
||
impl VisitMut for CollectLifetimes { | ||
fn visit_receiver_mut(&mut self, arg: &mut Receiver) { | ||
if let Some((_, lifetime)) = &mut arg.reference { | ||
self.visit_opt_lifetime(lifetime); | ||
} | ||
} | ||
|
||
fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { | ||
// We don't rewrite references in the impl FnOnce(&arg) or fn(&arg) | ||
if self.is_impl_fn() { | ||
return; | ||
} | ||
|
||
self.visit_opt_lifetime(&mut ty.lifetime); | ||
visit_mut::visit_type_reference_mut(self, ty); | ||
} | ||
|
||
fn visit_generic_argument_mut(&mut self, gen: &mut GenericArgument) { | ||
// We don't rewrite types in the impl FnOnce(&arg) -> Type<'_> | ||
if self.is_impl_fn() { | ||
return; | ||
} | ||
|
||
if let GenericArgument::Lifetime(lifetime) = gen { | ||
self.visit_lifetime(lifetime); | ||
} | ||
visit_mut::visit_generic_argument_mut(self, gen); | ||
} | ||
|
||
fn visit_type_impl_trait_mut(&mut self, impl_trait: &mut TypeImplTrait) { | ||
let impl_trait_lock = self.impl_trait_lock.clone(); | ||
let _locked = impl_trait_lock.try_lock(); | ||
|
||
impl_trait | ||
.bounds | ||
.insert(0, TypeParamBound::Lifetime(self.next_lifetime(None))); | ||
|
||
visit_mut::visit_type_impl_trait_mut(self, impl_trait); | ||
} | ||
|
||
fn visit_parenthesized_generic_arguments_mut( | ||
&mut self, | ||
generic_args: &mut ParenthesizedGenericArguments, | ||
) { | ||
let impl_fn_lock = self.impl_fn_lock.clone(); | ||
let _maybe_locked = self.is_impl_trait().then(|| impl_fn_lock.try_lock()); | ||
|
||
visit_mut::visit_parenthesized_generic_arguments_mut(self, generic_args); | ||
} | ||
|
||
fn visit_type_bare_fn_mut(&mut self, i: &mut TypeBareFn) { | ||
let impl_fn_lock = self.impl_fn_lock.clone(); | ||
let _locked = impl_fn_lock.try_lock(); | ||
|
||
visit_mut::visit_type_bare_fn_mut(self, i); | ||
} | ||
} |
Oops, something went wrong.
485a1b8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yew master branch benchmarks (Lower is better)
yew-struct-keyed 01_run1k
276.214
172.9725
1.60
yew-struct-keyed 02_replace1k
253.064
186.5145
1.36
yew-struct-keyed 03_update10th1k_x16
446.37
342.0505
1.30
yew-struct-keyed 04_select1k
79.6895
67.3735
1.18
yew-struct-keyed 05_swap1k
95.8995
86.578
1.11
yew-struct-keyed 06_remove-one-1k
32.419
30.5455
1.06
yew-struct-keyed 07_create10k
2735.821
2002.607
1.37
yew-struct-keyed 08_create1k-after1k_x2
560.5615
383.9154999999999
1.46
yew-struct-keyed 09_clear1k_x8
250.0145
199.415
1.25
yew-struct-keyed 21_ready-memory
0.9634513854980468
0.9634513854980468
1
yew-struct-keyed 22_run-memory
1.4578094482421875
1.4578094482421875
1
yew-struct-keyed 23_update5-memory
1.5065078735351562
1.4615478515625
1.03
yew-struct-keyed 24_run5-memory
1.5065422058105469
1.5065422058105469
1
yew-struct-keyed 25_run-clear-memory
1.1290855407714844
1.1287879943847656
1.00
yew-struct-keyed 31_startup-ci
1743.1039999999998
1896.1325
0.92
yew-struct-keyed 32_startup-bt
35.06999999999999
34.44199999999999
1.02
yew-struct-keyed 34_startup-totalbytes
364.2373046875
364.2373046875
1
This comment was automatically generated by workflow using github-action-benchmark.