scopes.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. use crate::{
  2. any_props::AnyProps,
  3. any_props::VProps,
  4. bump_frame::BumpFrame,
  5. innerlude::ErrorBoundary,
  6. innerlude::{DynamicNode, EventHandler, VComponent, VText},
  7. lazynodes::LazyNodes,
  8. nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
  9. runtime::Runtime,
  10. scope_context::ScopeContext,
  11. AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
  12. };
  13. use bumpalo::{boxed::Box as BumpBox, Bump};
  14. use std::{
  15. any::Any,
  16. cell::{Cell, Ref, RefCell, UnsafeCell},
  17. fmt::{Arguments, Debug},
  18. future::Future,
  19. rc::Rc,
  20. sync::Arc,
  21. };
  22. /// A wrapper around the [`Scoped`] object that contains a reference to the [`ScopeState`] and properties for a given
  23. /// component.
  24. ///
  25. /// The [`Scope`] is your handle to the [`crate::VirtualDom`] and the component state. Every component is given its own
  26. /// [`ScopeState`] and merged with its properties to create a [`Scoped`].
  27. ///
  28. /// The [`Scope`] handle specifically exists to provide a stable reference to these items for the lifetime of the
  29. /// component render.
  30. pub type Scope<'a, T = ()> = &'a Scoped<'a, T>;
  31. // This ScopedType exists because we want to limit the amount of monomorphization that occurs when making inner
  32. // state type generic over props. When the state is generic, it causes every method to be monomorphized for every
  33. // instance of Scope<T> in the codebase.
  34. //
  35. //
  36. /// A wrapper around a component's [`ScopeState`] and properties. The [`ScopeState`] provides the majority of methods
  37. /// for the VirtualDom and component state.
  38. pub struct Scoped<'a, T = ()> {
  39. /// The component's state and handle to the scheduler.
  40. ///
  41. /// Stores things like the custom bump arena, spawn functions, hooks, and the scheduler.
  42. pub scope: &'a ScopeState,
  43. /// The component's properties.
  44. pub props: &'a T,
  45. }
  46. impl<'a, T> std::ops::Deref for Scoped<'a, T> {
  47. type Target = &'a ScopeState;
  48. fn deref(&self) -> &Self::Target {
  49. &self.scope
  50. }
  51. }
  52. /// A component's unique identifier.
  53. ///
  54. /// `ScopeId` is a `usize` that acts a key for the internal slab of Scopes. This means that the key is not unqiue across
  55. /// time. We do try and guarantee that between calls to `wait_for_work`, no ScopeIds will be recycled in order to give
  56. /// time for any logic that relies on these IDs to properly update.
  57. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  58. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
  59. pub struct ScopeId(pub usize);
  60. impl ScopeId {
  61. /// The root ScopeId.
  62. ///
  63. /// This scope will last for the entire duration of your app, making it convenient for long-lived state
  64. /// that is created dynamically somewhere down the component tree.
  65. ///
  66. /// # Example
  67. ///
  68. /// ```rust, ignore
  69. /// use dioxus_signals::*;
  70. /// let my_persistent_state = Signal::new_in_scope(ScopeId::ROOT, String::new());
  71. /// ```
  72. pub const ROOT: ScopeId = ScopeId(0);
  73. }
  74. /// A component's state separate from its props.
  75. ///
  76. /// This struct exists to provide a common interface for all scopes without relying on generics.
  77. pub struct ScopeState {
  78. pub(crate) runtime: Rc<Runtime>,
  79. pub(crate) context_id: ScopeId,
  80. pub(crate) render_cnt: Cell<usize>,
  81. pub(crate) node_arena_1: BumpFrame,
  82. pub(crate) node_arena_2: BumpFrame,
  83. pub(crate) hooks: RefCell<Vec<Box<UnsafeCell<dyn Any>>>>,
  84. pub(crate) hook_idx: Cell<usize>,
  85. pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
  86. pub(crate) attributes_to_drop_before_render: RefCell<Vec<*const Attribute<'static>>>,
  87. pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
  88. }
  89. impl Drop for ScopeState {
  90. fn drop(&mut self) {
  91. self.runtime.remove_context(self.context_id);
  92. }
  93. }
  94. impl<'src> ScopeState {
  95. pub(crate) fn context(&self) -> Ref<'_, ScopeContext> {
  96. self.runtime.get_context(self.context_id).unwrap()
  97. }
  98. pub(crate) fn current_frame(&self) -> &BumpFrame {
  99. match self.render_cnt.get() % 2 {
  100. 0 => &self.node_arena_1,
  101. 1 => &self.node_arena_2,
  102. _ => unreachable!(),
  103. }
  104. }
  105. pub(crate) fn previous_frame(&self) -> &BumpFrame {
  106. match self.render_cnt.get() % 2 {
  107. 1 => &self.node_arena_1,
  108. 0 => &self.node_arena_2,
  109. _ => unreachable!(),
  110. }
  111. }
  112. /// Get the name of this component
  113. pub fn name(&self) -> &str {
  114. self.context().name
  115. }
  116. /// Get the current render since the inception of this component
  117. ///
  118. /// This can be used as a helpful diagnostic when debugging hooks/renders, etc
  119. pub fn generation(&self) -> usize {
  120. self.render_cnt.get()
  121. }
  122. /// Get a handle to the currently active bump arena for this Scope
  123. ///
  124. /// This is a bump memory allocator. Be careful using this directly since the contents will be wiped on the next render.
  125. /// It's easy to leak memory here since the drop implementation will not be called for any objects allocated in this arena.
  126. ///
  127. /// If you need to allocate items that need to be dropped, use bumpalo's box.
  128. pub fn bump(&self) -> &Bump {
  129. // note that this is actually the previous frame since we use that as scratch space while the component is rendering
  130. self.previous_frame().bump()
  131. }
  132. /// Get a handle to the currently active head node arena for this Scope
  133. ///
  134. /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
  135. ///
  136. /// Panics if the tree has not been built yet.
  137. pub fn root_node(&self) -> &RenderReturn {
  138. self.try_root_node()
  139. .expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
  140. }
  141. /// Try to get a handle to the currently active head node arena for this Scope
  142. ///
  143. /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
  144. ///
  145. /// Returns [`None`] if the tree has not been built yet.
  146. pub fn try_root_node(&self) -> Option<&RenderReturn> {
  147. let ptr = self.current_frame().node.get();
  148. if ptr.is_null() {
  149. return None;
  150. }
  151. let r: &RenderReturn = unsafe { &*ptr };
  152. unsafe { std::mem::transmute(r) }
  153. }
  154. /// Get the height of this Scope - IE the number of scopes above it.
  155. ///
  156. /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
  157. ///
  158. /// # Example
  159. ///
  160. /// ```rust, ignore
  161. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  162. /// dom.rebuild();
  163. ///
  164. /// let base = dom.base_scope();
  165. ///
  166. /// assert_eq!(base.height(), 0);
  167. /// ```
  168. pub fn height(&self) -> u32 {
  169. self.context().height
  170. }
  171. /// Get the Parent of this [`Scope`] within this Dioxus [`crate::VirtualDom`].
  172. ///
  173. /// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
  174. ///
  175. /// The base component will not have a parent, and will return `None`.
  176. ///
  177. /// # Example
  178. ///
  179. /// ```rust, ignore
  180. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  181. /// dom.rebuild();
  182. ///
  183. /// let base = dom.base_scope();
  184. ///
  185. /// assert_eq!(base.parent(), None);
  186. /// ```
  187. pub fn parent(&self) -> Option<ScopeId> {
  188. // safety: the pointer to our parent is *always* valid thanks to the bump arena
  189. self.context().parent_id()
  190. }
  191. /// Get the ID of this Scope within this Dioxus [`crate::VirtualDom`].
  192. ///
  193. /// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
  194. ///
  195. /// # Example
  196. ///
  197. /// ```rust, ignore
  198. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  199. /// dom.rebuild();
  200. /// let base = dom.base_scope();
  201. ///
  202. /// assert_eq!(base.scope_id(), 0);
  203. /// ```
  204. pub fn scope_id(&self) -> ScopeId {
  205. self.context().scope_id()
  206. }
  207. /// Create a subscription that schedules a future render for the reference component
  208. ///
  209. /// ## Notice: you should prefer using [`Self::schedule_update_any`] and [`Self::scope_id`]
  210. pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
  211. self.context().schedule_update()
  212. }
  213. /// Schedule an update for any component given its [`ScopeId`].
  214. ///
  215. /// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
  216. ///
  217. /// This method should be used when you want to schedule an update for a component
  218. pub fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
  219. self.context().schedule_update_any()
  220. }
  221. /// Mark this scope as dirty, and schedule a render for it.
  222. pub fn needs_update(&self) {
  223. self.context().needs_update()
  224. }
  225. /// Get the [`ScopeId`] of a mounted component.
  226. ///
  227. /// `ScopeId` is not unique for the lifetime of the [`crate::VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
  228. pub fn needs_update_any(&self, id: ScopeId) {
  229. self.context().needs_update_any(id)
  230. }
  231. /// Return any context of type T if it exists on this scope
  232. pub fn has_context<T: 'static + Clone>(&self) -> Option<T> {
  233. self.context().has_context()
  234. }
  235. /// Try to retrieve a shared state with type `T` from any parent scope.
  236. ///
  237. /// Clones the state if it exists.
  238. pub fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
  239. self.context().consume_context()
  240. }
  241. /// Expose state to children further down the [`crate::VirtualDom`] Tree. Requires `Clone` on the context to allow getting values down the tree.
  242. ///
  243. /// This is a "fundamental" operation and should only be called during initialization of a hook.
  244. ///
  245. /// For a hook that provides the same functionality, use `use_provide_context` and `use_context` instead.
  246. ///
  247. /// # Example
  248. ///
  249. /// ```rust, ignore
  250. /// struct SharedState(&'static str);
  251. ///
  252. /// static App: Component = |cx| {
  253. /// cx.use_hook(|| cx.provide_context(SharedState("world")));
  254. /// render!(Child {})
  255. /// }
  256. ///
  257. /// static Child: Component = |cx| {
  258. /// let state = cx.consume_state::<SharedState>();
  259. /// render!(div { "hello {state.0}" })
  260. /// }
  261. /// ```
  262. pub fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
  263. self.context().provide_context(value)
  264. }
  265. /// Provide a context to the root and then consume it
  266. ///
  267. /// This is intended for "global" state management solutions that would rather be implicit for the entire app.
  268. /// Things like signal runtimes and routers are examples of "singletons" that would benefit from lazy initialization.
  269. ///
  270. /// Note that you should be checking if the context existed before trying to provide a new one. Providing a context
  271. /// when a context already exists will swap the context out for the new one, which may not be what you want.
  272. pub fn provide_root_context<T: 'static + Clone>(&self, context: T) -> T {
  273. self.context().provide_root_context(context)
  274. }
  275. /// Pushes the future onto the poll queue to be polled after the component renders.
  276. pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
  277. self.context().push_future(fut)
  278. }
  279. /// Spawns the future but does not return the [`TaskId`]
  280. pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) {
  281. self.context().spawn(fut);
  282. }
  283. /// Spawn a future that Dioxus won't clean up when this component is unmounted
  284. ///
  285. /// This is good for tasks that need to be run after the component has been dropped.
  286. pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
  287. self.context().spawn_forever(fut)
  288. }
  289. /// Informs the scheduler that this task is no longer needed and should be removed.
  290. ///
  291. /// This drops the task immediately.
  292. pub fn remove_future(&self, id: TaskId) {
  293. self.context().remove_future(id);
  294. }
  295. /// Take a lazy [`crate::VNode`] structure and actually build it with the context of the efficient [`bumpalo::Bump`] allocator.
  296. ///
  297. /// ## Example
  298. ///
  299. /// ```ignore
  300. /// fn Component(cx: Scope<Props>) -> Element {
  301. /// // Lazy assemble the VNode tree
  302. /// let lazy_nodes = rsx!("hello world");
  303. ///
  304. /// // Actually build the tree and allocate it
  305. /// cx.render(lazy_tree)
  306. /// }
  307. ///```
  308. pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
  309. let element = rsx.call(self);
  310. let mut listeners = self.attributes_to_drop_before_render.borrow_mut();
  311. for attr in element.dynamic_attrs {
  312. match attr.value {
  313. // We need to drop listeners before the next render because they may borrow data from the borrowed props which will be dropped
  314. AttributeValue::Listener(_) => {
  315. let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
  316. listeners.push(unbounded);
  317. }
  318. // We need to drop any values manually to make sure that their drop implementation is called before the next render
  319. AttributeValue::Any(_) => {
  320. let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
  321. self.previous_frame().add_attribute_to_drop(unbounded);
  322. }
  323. _ => (),
  324. }
  325. }
  326. let mut props = self.borrowed_props.borrow_mut();
  327. let mut drop_props = self
  328. .previous_frame()
  329. .props_to_drop_before_reset
  330. .borrow_mut();
  331. for node in element.dynamic_nodes {
  332. if let DynamicNode::Component(comp) = node {
  333. let unbounded = unsafe { std::mem::transmute(comp as *const VComponent) };
  334. if !comp.static_props {
  335. props.push(unbounded);
  336. }
  337. drop_props.push(unbounded);
  338. }
  339. }
  340. Some(element)
  341. }
  342. /// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
  343. pub fn text_node(&'src self, args: Arguments) -> DynamicNode<'src> {
  344. DynamicNode::Text(VText {
  345. value: self.raw_text(args),
  346. id: Default::default(),
  347. })
  348. }
  349. /// Allocate some text inside the [`ScopeState`] from [`Arguments`]
  350. ///
  351. /// Uses the currently active [`Bump`] allocator
  352. pub fn raw_text(&'src self, args: Arguments) -> &'src str {
  353. args.as_str().unwrap_or_else(|| {
  354. use bumpalo::core_alloc::fmt::Write;
  355. let mut str_buf = bumpalo::collections::String::new_in(self.bump());
  356. str_buf.write_fmt(args).unwrap();
  357. str_buf.into_bump_str()
  358. })
  359. }
  360. /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] using the internal [`Bump`] allocator
  361. pub fn make_node<'c, I>(&'src self, into: impl IntoDynNode<'src, I> + 'c) -> DynamicNode {
  362. into.into_vnode(self)
  363. }
  364. /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
  365. ///
  366. /// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
  367. /// some renderers stay in sync with the VirtualDom's understanding of the world
  368. pub fn attr(
  369. &'src self,
  370. name: &'static str,
  371. value: impl IntoAttributeValue<'src>,
  372. namespace: Option<&'static str>,
  373. volatile: bool,
  374. ) -> Attribute<'src> {
  375. Attribute {
  376. name,
  377. namespace,
  378. volatile,
  379. mounted_element: Default::default(),
  380. value: value.into_value(self.bump()),
  381. }
  382. }
  383. /// Create a new [`DynamicNode::Component`] variant
  384. ///
  385. ///
  386. /// The given component can be any of four signatures. Remember that an [`Element`] is really a [`Result<VNode>`].
  387. ///
  388. /// ```rust, ignore
  389. /// // Without explicit props
  390. /// fn(Scope) -> Element;
  391. /// async fn(Scope<'_>) -> Element;
  392. ///
  393. /// // With explicit props
  394. /// fn(Scope<Props>) -> Element;
  395. /// async fn(Scope<Props<'_>>) -> Element;
  396. /// ```
  397. pub fn component<'child, P>(
  398. &'src self,
  399. component: fn(Scope<'child, P>) -> Element<'child>,
  400. props: P,
  401. fn_name: &'static str,
  402. ) -> DynamicNode<'src>
  403. where
  404. // The properties must be valid until the next bump frame
  405. P: Properties + 'src,
  406. // The current bump allocator frame must outlive the child's borrowed props
  407. 'src: 'child,
  408. {
  409. let vcomp = VProps::new(component, P::memoize, props);
  410. // cast off the lifetime of the render return
  411. let as_dyn: Box<dyn AnyProps<'child> + '_> = Box::new(vcomp);
  412. let extended: Box<dyn AnyProps<'src> + 'src> = unsafe { std::mem::transmute(as_dyn) };
  413. DynamicNode::Component(VComponent {
  414. name: fn_name,
  415. render_fn: component as *const (),
  416. static_props: P::IS_STATIC,
  417. props: RefCell::new(Some(extended)),
  418. scope: Cell::new(None),
  419. })
  420. }
  421. /// Create a new [`EventHandler`] from an [`FnMut`]
  422. pub fn event_handler<T>(&'src self, f: impl FnMut(T) + 'src) -> EventHandler<'src, T> {
  423. let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
  424. let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
  425. let callback = RefCell::new(Some(caller));
  426. EventHandler {
  427. callback,
  428. origin: self.context().id,
  429. }
  430. }
  431. /// Create a new [`AttributeValue`] with the listener variant from a callback
  432. ///
  433. /// The callback must be confined to the lifetime of the ScopeState
  434. pub fn listener<T: 'static>(
  435. &'src self,
  436. mut callback: impl FnMut(Event<T>) + 'src,
  437. ) -> AttributeValue<'src> {
  438. // safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
  439. // This is the suggested way to build a bumpbox
  440. //
  441. // In theory, we could just use regular boxes
  442. let boxed: BumpBox<'src, dyn FnMut(_) + 'src> = unsafe {
  443. BumpBox::from_raw(self.bump().alloc(move |event: Event<dyn Any>| {
  444. if let Ok(data) = event.data.downcast::<T>() {
  445. callback(Event {
  446. propagates: event.propagates,
  447. data,
  448. });
  449. }
  450. }))
  451. };
  452. AttributeValue::Listener(RefCell::new(Some(boxed)))
  453. }
  454. /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
  455. pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue<'src> {
  456. // safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
  457. // This is the suggested way to build a bumpbox
  458. //
  459. // In theory, we could just use regular boxes
  460. let boxed: BumpBox<'src, dyn AnyValue> =
  461. unsafe { BumpBox::from_raw(self.bump().alloc(value)) };
  462. AttributeValue::Any(RefCell::new(Some(boxed)))
  463. }
  464. /// Inject an error into the nearest error boundary and quit rendering
  465. ///
  466. /// The error doesn't need to implement Error or any specific traits since the boundary
  467. /// itself will downcast the error into a trait object.
  468. pub fn throw(&self, error: impl Debug + 'static) -> Option<()> {
  469. if let Some(cx) = self.consume_context::<Rc<ErrorBoundary>>() {
  470. cx.insert_error(self.scope_id(), Box::new(error));
  471. }
  472. // Always return none during a throw
  473. None
  474. }
  475. /// Mark this component as suspended and then return None
  476. pub fn suspend(&self) -> Option<Element> {
  477. let cx = self.context();
  478. cx.suspend();
  479. None
  480. }
  481. /// Store a value between renders. The foundational hook for all other hooks.
  482. ///
  483. /// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
  484. ///
  485. /// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait
  486. ///
  487. /// # Example
  488. ///
  489. /// ```
  490. /// use dioxus_core::ScopeState;
  491. ///
  492. /// // prints a greeting on the initial render
  493. /// pub fn use_hello_world(cx: &ScopeState) {
  494. /// cx.use_hook(|| println!("Hello, world!"));
  495. /// }
  496. /// ```
  497. #[allow(clippy::mut_from_ref)]
  498. pub fn use_hook<State: 'static>(&self, initializer: impl FnOnce() -> State) -> &mut State {
  499. let cur_hook = self.hook_idx.get();
  500. let mut hooks = self.hooks.try_borrow_mut().expect("The hook list is already borrowed: This error is likely caused by trying to use a hook inside a hook which violates the rules of hooks.");
  501. if cur_hook >= hooks.len() {
  502. hooks.push(Box::new(UnsafeCell::new(initializer())));
  503. }
  504. hooks
  505. .get(cur_hook)
  506. .and_then(|inn| {
  507. self.hook_idx.set(cur_hook + 1);
  508. let raw_ref = unsafe { &mut *inn.get() };
  509. raw_ref.downcast_mut::<State>()
  510. })
  511. .expect(
  512. r#"
  513. Unable to retrieve the hook that was initialized at this index.
  514. Consult the `rules of hooks` to understand how to use hooks properly.
  515. You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
  516. Functions prefixed with "use" should never be called conditionally.
  517. "#,
  518. )
  519. }
  520. }