1use std::{
2 borrow::{Borrow, Cow},
3 error::Error,
4 fmt::{self, Debug, Display},
5 future::Future,
6 hash::{BuildHasher, BuildHasherDefault, Hash},
7 ops::Deref,
8 pin::Pin,
9 sync::Arc,
10};
11
12use anyhow::{Result, anyhow};
13use auto_hash_map::AutoMap;
14use bincode::{
15 Decode, Encode,
16 de::Decoder,
17 enc::Encoder,
18 error::{DecodeError, EncodeError},
19 impl_borrow_decode,
20};
21use rustc_hash::FxHasher;
22use smallvec::SmallVec;
23use tracing::Span;
24use turbo_bincode::{
25 TurboBincodeDecode, TurboBincodeDecoder, TurboBincodeEncode, TurboBincodeEncoder,
26 impl_decode_for_turbo_bincode_decode, impl_encode_for_turbo_bincode_encode, new_hash_encoder,
27};
28use turbo_rcstr::RcStr;
29use turbo_tasks_hash::DeterministicHasher;
30
31use crate::{
32 CellId, RawVc, ReadCellOptions, ReadOutcome, ReadOutputOptions, ReadRef, SharedReference,
33 TaskId, TaskIdSet, TaskPriority, TraitRef, TraitTypeId, TurboTasksCallApi, TurboTasksPanic,
34 ValueTypeId, ValueTypePersistence, VcValueTrait, VcValueType,
35 dyn_task_inputs::{DynTaskInputs, DynTaskInputsStorage},
36 macro_helpers::NativeFunction,
37 manager::{TaskPersistence, TurboTasks},
38 registry,
39 task::shared_reference::TypedSharedReference,
40 task_statistics::TaskStatisticsApi,
41 turbo_tasks,
42};
43
44pub type TransientTaskRoot =
45 Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<RawVc>> + Send>> + Send + Sync>;
46
47pub enum TransientTaskType {
48 Root(TransientTaskRoot),
54
55 Once(Pin<Box<dyn Future<Output = Result<RawVc>> + Send + 'static>>),
65}
66
67impl Debug for TransientTaskType {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 match self {
70 Self::Root(_) => f.debug_tuple("Root").finish(),
71 Self::Once(_) => f.debug_tuple("Once").finish(),
72 }
73 }
74}
75
76#[derive(Debug, Eq)]
79pub struct CachedTaskType {
80 pub native_fn: &'static NativeFunction,
81 pub this: Option<RawVc>,
82 pub arg: Box<dyn DynTaskInputs>,
83}
84
85impl CachedTaskType {
86 pub fn get_name(&self) -> &'static str {
89 self.native_fn.ty.name
90 }
91
92 pub fn hash_encode<H: DeterministicHasher>(&self, hasher: &mut H) {
97 Self::hash_encode_components(self.native_fn, self.this, &*self.arg, hasher);
98 }
99}
100
101impl TurboBincodeEncode for CachedTaskType {
102 fn encode(&self, encoder: &mut TurboBincodeEncoder) -> Result<(), EncodeError> {
103 Encode::encode(®istry::get_function_id(self.native_fn), encoder)?;
104
105 let (encode_arg_any, _) = self.native_fn.arg_meta.bincode;
106 Encode::encode(&self.this, encoder)?;
107 encode_arg_any(&*self.arg, encoder)?;
108
109 Ok(())
110 }
111}
112
113impl<Context> TurboBincodeDecode<Context> for CachedTaskType {
114 fn decode(decoder: &mut TurboBincodeDecoder) -> Result<Self, DecodeError> {
115 let native_fn = registry::get_native_function(Decode::decode(decoder)?);
116
117 let (_, decode_arg_any) = native_fn.arg_meta.bincode;
118 let this = Decode::decode(decoder)?;
119 let arg = decode_arg_any(decoder)?;
120
121 Ok(Self {
122 native_fn,
123 this,
124 arg,
125 })
126 }
127}
128
129impl_encode_for_turbo_bincode_encode!(CachedTaskType);
130impl_decode_for_turbo_bincode_decode!(CachedTaskType);
131impl_borrow_decode!(CachedTaskType);
132
133#[derive(Clone, Debug, Hash, PartialEq, Eq)]
139pub struct CachedTaskTypeArc(pub triomphe::Arc<CachedTaskType>);
140
141impl CachedTaskTypeArc {
142 pub fn new(value: CachedTaskType) -> Self {
143 Self(triomphe::Arc::new(value))
144 }
145
146 pub fn count(&self) -> usize {
147 triomphe::Arc::count(&self.0)
148 }
149}
150
151impl AsRef<CachedTaskType> for CachedTaskTypeArc {
152 fn as_ref(&self) -> &CachedTaskType {
153 &self.0
154 }
155}
156
157impl Deref for CachedTaskTypeArc {
158 type Target = CachedTaskType;
159 #[inline]
160 fn deref(&self) -> &CachedTaskType {
161 &self.0
162 }
163}
164
165impl Borrow<CachedTaskType> for CachedTaskTypeArc {
166 #[inline]
167 fn borrow(&self) -> &CachedTaskType {
168 &self.0
169 }
170}
171
172impl Display for CachedTaskTypeArc {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 Display::fmt(&**self, f)
175 }
176}
177
178impl Encode for CachedTaskTypeArc {
179 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
180 <CachedTaskType as Encode>::encode(self, encoder)
181 }
182}
183
184impl<Context> Decode<Context> for CachedTaskTypeArc {
185 fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
186 Ok(Self::new(<CachedTaskType as Decode<Context>>::decode(
187 decoder,
188 )?))
189 }
190}
191
192impl<'de, Context> bincode::BorrowDecode<'de, Context> for CachedTaskTypeArc {
193 fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
194 decoder: &mut D,
195 ) -> Result<Self, DecodeError> {
196 Ok(Self::new(<CachedTaskType as bincode::BorrowDecode<
197 'de,
198 Context,
199 >>::borrow_decode(decoder)?))
200 }
201}
202
203impl PartialEq for CachedTaskType {
206 #[expect(clippy::op_ref)]
207 fn eq(&self, other: &Self) -> bool {
208 self.native_fn == other.native_fn && self.this == other.this && &self.arg == &other.arg
209 }
210}
211
212impl Hash for CachedTaskType {
215 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
216 self.native_fn.hash(state);
217 self.this.hash(state);
218 self.arg.hash(state);
219 }
220}
221
222impl Display for CachedTaskType {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 f.write_str(self.get_name())
225 }
226}
227
228impl CachedTaskType {
229 pub fn hash_from_components(
232 hasher: &impl BuildHasher,
233 native_fn: &'static NativeFunction,
234 this: Option<RawVc>,
235 arg: &dyn DynTaskInputs,
236 ) -> u64 {
237 use std::hash::Hasher;
238 let mut state = hasher.build_hasher();
239 native_fn.hash(&mut state);
240 this.hash(&mut state);
241 arg.hash(&mut state);
242 state.finish()
243 }
244
245 pub fn hash_encode_components<H: DeterministicHasher>(
250 native_fn: &'static NativeFunction,
251 this: Option<RawVc>,
252 arg: &dyn DynTaskInputs,
253 hasher: &mut H,
254 ) {
255 let fn_id = registry::get_function_id(native_fn);
256 {
257 let mut encoder = new_hash_encoder(hasher);
258 Encode::encode(&fn_id, &mut encoder).expect("fn_id encoding should not fail");
259 Encode::encode(&this, &mut encoder).expect("this encoding should not fail");
260 }
261 (native_fn.arg_meta.hash_encode)(arg, hasher);
262 }
263
264 pub fn eq_components(
266 &self,
267 native_fn: &'static NativeFunction,
268 this: Option<RawVc>,
269 arg: &dyn DynTaskInputs,
270 ) -> bool {
271 std::ptr::eq(self.native_fn, native_fn) && self.this == this && &*self.arg == arg
272 }
273}
274
275pub struct TaskExecutionSpec<'a> {
276 pub future: Pin<Box<dyn Future<Output = Result<RawVc>> + Send + 'a>>,
277 pub span: Span,
278}
279
280#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
281pub struct CellContent(pub Option<SharedReference>);
282#[derive(Clone, Debug, PartialEq, Eq, Hash)]
283pub struct TypedCellContent(pub ValueTypeId, pub CellContent);
284
285impl Display for CellContent {
286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 match &self.0 {
288 None => write!(f, "empty"),
289 Some(content) => Display::fmt(content, f),
290 }
291 }
292}
293
294impl TypedCellContent {
295 pub fn cast<T: VcValueType>(self) -> Result<ReadRef<T>> {
296 let data = self.1.0.ok_or_else(|| anyhow!("Cell is empty"))?;
297 let data = data
298 .downcast::<T>()
299 .map_err(|_err| anyhow!("Unexpected type in cell"))?;
300 Ok(ReadRef::new_arc(data))
301 }
302
303 pub fn cast_trait<T>(self) -> Result<TraitRef<T>>
308 where
309 T: VcValueTrait + ?Sized,
310 {
311 let shared_reference = self
312 .1
313 .0
314 .ok_or_else(|| anyhow!("Cell is empty"))?
315 .into_typed(self.0);
316 Ok(
317 TraitRef::new(shared_reference),
319 )
320 }
321
322 pub fn into_untyped(self) -> CellContent {
323 self.1
324 }
325
326 pub fn encode(&self, enc: &mut TurboBincodeEncoder) -> Result<(), EncodeError> {
327 let Self(type_id, content) = self;
328 let value_type = registry::get_value_type(*type_id);
329 type_id.encode(enc)?;
330 if let ValueTypePersistence::Persistable(encode_fn, _) = value_type.persistence {
331 if let Some(reference) = &content.0 {
332 true.encode(enc)?;
333 encode_fn(&*reference.0, enc)?;
334 Ok(())
335 } else {
336 false.encode(enc)?;
337 Ok(())
338 }
339 } else {
340 Ok(())
341 }
342 }
343
344 pub fn decode(dec: &mut TurboBincodeDecoder) -> Result<Self, DecodeError> {
345 let type_id = ValueTypeId::decode(dec)?;
346 let value_type = registry::get_value_type(type_id);
347 if let ValueTypePersistence::Persistable(_, decode_fn) = value_type.persistence {
348 let is_some = bool::decode(dec)?;
349 if is_some {
350 let reference = decode_fn(dec)?;
351 return Ok(TypedCellContent(type_id, CellContent(Some(reference))));
352 }
353 }
354 Ok(TypedCellContent(type_id, CellContent(None)))
355 }
356}
357
358impl From<TypedSharedReference> for TypedCellContent {
359 fn from(value: TypedSharedReference) -> Self {
360 TypedCellContent(value.type_id, CellContent(Some(value.reference)))
361 }
362}
363
364impl TryFrom<TypedCellContent> for TypedSharedReference {
365 type Error = TypedCellContent;
366
367 fn try_from(content: TypedCellContent) -> Result<Self, TypedCellContent> {
368 if let TypedCellContent(type_id, CellContent(Some(reference))) = content {
369 Ok(TypedSharedReference { type_id, reference })
370 } else {
371 Err(content)
372 }
373 }
374}
375
376impl CellContent {
377 pub fn into_typed(self, type_id: ValueTypeId) -> TypedCellContent {
378 TypedCellContent(type_id, self)
379 }
380}
381
382impl From<SharedReference> for CellContent {
383 fn from(value: SharedReference) -> Self {
384 CellContent(Some(value))
385 }
386}
387
388impl From<Option<SharedReference>> for CellContent {
389 fn from(value: Option<SharedReference>) -> Self {
390 CellContent(value)
391 }
392}
393
394impl TryFrom<CellContent> for SharedReference {
395 type Error = CellContent;
396
397 fn try_from(content: CellContent) -> Result<Self, CellContent> {
398 if let CellContent(Some(shared_reference)) = content {
399 Ok(shared_reference)
400 } else {
401 Err(content)
402 }
403 }
404}
405
406pub type TaskCollectiblesMap = AutoMap<RawVc, i32, BuildHasherDefault<FxHasher>, 1>;
407
408pub type CellHash = [u8; 16];
414
415#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
418pub enum TurboTasksExecutionErrorMessage {
419 PIISafe(#[bincode(with = "turbo_bincode::owned_cow")] Cow<'static, str>),
420 NonPIISafe(String),
421}
422
423impl Display for TurboTasksExecutionErrorMessage {
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425 match self {
426 TurboTasksExecutionErrorMessage::PIISafe(msg) => write!(f, "{msg}"),
427 TurboTasksExecutionErrorMessage::NonPIISafe(msg) => write!(f, "{msg}"),
428 }
429 }
430}
431
432#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
433pub struct TurboTasksError {
434 pub message: TurboTasksExecutionErrorMessage,
435 pub source: Option<TurboTasksExecutionError>,
436}
437
438#[derive(Clone)]
442pub struct TurboTaskContextError {
443 pub turbo_tasks: Arc<dyn TurboTasksCallApi>,
444 pub task_id: TaskId,
445 pub source: Option<TurboTasksExecutionError>,
446}
447
448impl PartialEq for TurboTaskContextError {
449 fn eq(&self, other: &Self) -> bool {
450 self.task_id == other.task_id && self.source == other.source
451 }
452}
453impl Eq for TurboTaskContextError {}
454
455impl Encode for TurboTaskContextError {
456 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
457 Encode::encode(&self.task_id, encoder)?;
458 Encode::encode(&self.source, encoder)?;
459 Ok(())
460 }
461}
462
463impl<Context> Decode<Context> for TurboTaskContextError {
464 fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
465 let task_id = Decode::decode(decoder)?;
466 let source = Decode::decode(decoder)?;
467 let turbo_tasks = turbo_tasks();
468 Ok(Self {
469 turbo_tasks,
470 task_id,
471 source,
472 })
473 }
474}
475
476impl_borrow_decode!(TurboTaskContextError);
477
478impl Debug for TurboTaskContextError {
479 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
480 f.debug_struct("TurboTaskContextError")
481 .field("task_id", &self.task_id)
482 .field("source", &self.source)
483 .finish()
484 }
485}
486
487#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
490pub struct TurboTaskLocalContextError {
491 pub name: RcStr,
492 pub source: Option<TurboTasksExecutionError>,
493}
494
495#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
496pub enum TurboTasksExecutionError {
497 Panic(Arc<TurboTasksPanic>),
498 Error(Arc<TurboTasksError>),
499 TaskContext(Arc<TurboTaskContextError>),
500 LocalTaskContext(Arc<TurboTaskLocalContextError>),
501}
502
503impl TurboTasksExecutionError {
504 pub fn with_task_context(
507 self,
508 task_id: TaskId,
509 turbo_tasks: Arc<dyn TurboTasksCallApi>,
510 ) -> Self {
511 TurboTasksExecutionError::TaskContext(Arc::new(TurboTaskContextError {
512 task_id,
513 turbo_tasks,
514 source: Some(self),
515 }))
516 }
517
518 pub fn with_local_task_context(self, name: String) -> Self {
521 TurboTasksExecutionError::LocalTaskContext(Arc::new(TurboTaskLocalContextError {
522 name: RcStr::from(name),
523 source: Some(self),
524 }))
525 }
526}
527
528impl Error for TurboTasksExecutionError {
529 fn source(&self) -> Option<&(dyn Error + 'static)> {
530 match self {
531 TurboTasksExecutionError::Panic(_panic) => None,
532 TurboTasksExecutionError::Error(error) => {
533 error.source.as_ref().map(|s| s as &dyn Error)
534 }
535 TurboTasksExecutionError::TaskContext(context_error) => {
536 context_error.source.as_ref().map(|s| s as &dyn Error)
537 }
538 TurboTasksExecutionError::LocalTaskContext(context_error) => {
539 context_error.source.as_ref().map(|s| s as &dyn Error)
540 }
541 }
542 }
543}
544
545impl Display for TurboTasksExecutionError {
546 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
547 match self {
548 TurboTasksExecutionError::Panic(panic) => write!(f, "{}", panic),
549 TurboTasksExecutionError::Error(error) => {
550 write!(f, "{}", error.message)
551 }
552 TurboTasksExecutionError::TaskContext(context_error) => {
553 let task_id = context_error.task_id;
554 let name = context_error.turbo_tasks.get_task_name(task_id);
555 if cfg!(feature = "task_id_details") {
556 write!(f, "Execution of {name} ({}) failed", task_id)
557 } else {
558 write!(f, "Execution of {name} failed")
559 }
560 }
561 TurboTasksExecutionError::LocalTaskContext(context_error) => {
562 write!(f, "Execution of {} failed", context_error.name)
563 }
564 }
565 }
566}
567
568impl<'l> From<&'l (dyn std::error::Error + 'static)> for TurboTasksExecutionError {
569 fn from(err: &'l (dyn std::error::Error + 'static)) -> Self {
570 if let Some(err) = err.downcast_ref::<TurboTasksExecutionError>() {
571 return err.clone();
572 }
573 let message = err.to_string();
574 let source = err.source().map(|source| source.into());
575
576 TurboTasksExecutionError::Error(Arc::new(TurboTasksError {
577 message: TurboTasksExecutionErrorMessage::NonPIISafe(message),
578 source,
579 }))
580 }
581}
582
583impl From<anyhow::Error> for TurboTasksExecutionError {
584 fn from(err: anyhow::Error) -> Self {
585 let current: &(dyn std::error::Error + 'static) = err.as_ref();
586 current.into()
587 }
588}
589
590pub enum VerificationMode {
591 EqualityCheck,
592 Skip,
593}
594
595pub trait Backend: Sized + Sync + Send {
596 #[allow(unused_variables)]
597 fn startup(&self, turbo_tasks: &TurboTasks<Self>) {}
598
599 #[allow(unused_variables)]
600 fn stop(&self, turbo_tasks: &TurboTasks<Self>) {}
601 #[allow(unused_variables)]
602 fn stopping(&self, turbo_tasks: &TurboTasks<Self>) {}
603
604 #[allow(unused_variables)]
605 fn idle_start(&self, turbo_tasks: &TurboTasks<Self>) {}
606 #[allow(unused_variables)]
607 fn idle_end(&self, turbo_tasks: &TurboTasks<Self>) {}
608
609 fn invalidate_task(&self, task: TaskId, turbo_tasks: &TurboTasks<Self>);
610
611 fn invalidate_tasks(&self, tasks: &[TaskId], turbo_tasks: &TurboTasks<Self>);
612 fn invalidate_tasks_set(&self, tasks: &TaskIdSet, turbo_tasks: &TurboTasks<Self>);
613
614 fn invalidate_serialization(&self, _task: TaskId, _turbo_tasks: &TurboTasks<Self>) {}
615
616 fn try_start_task_execution<'a>(
617 &'a self,
618 task: TaskId,
619 priority: TaskPriority,
620 turbo_tasks: &TurboTasks<Self>,
621 ) -> Option<TaskExecutionSpec<'a>>;
622
623 fn task_execution_canceled(&self, task: TaskId, turbo_tasks: &TurboTasks<Self>);
624
625 fn task_execution_completed(
631 &self,
632 task: TaskId,
633 result: Result<RawVc, TurboTasksExecutionError>,
634 cell_counters: &AutoMap<ValueTypeId, u32, BuildHasherDefault<FxHasher>, 8>,
635 #[cfg(feature = "verify_determinism")] stateful: bool,
636 has_invalidator: bool,
637 turbo_tasks: &TurboTasks<Self>,
638 ) -> Option<TaskPriority>;
639
640 type BackendJob: Send + 'static;
641
642 fn run_backend_job<'a>(
643 &'a self,
644 job: Self::BackendJob,
645 turbo_tasks: &'a TurboTasks<Self>,
646 ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
647
648 fn try_read_task_output(
651 &self,
652 task: TaskId,
653 reader: Option<TaskId>,
654 options: ReadOutputOptions,
655 turbo_tasks: &TurboTasks<Self>,
656 ) -> Result<ReadOutcome<RawVc>>;
657
658 fn try_read_task_cell(
661 &self,
662 task: TaskId,
663 index: CellId,
664 reader: Option<TaskId>,
665 options: ReadCellOptions,
666 turbo_tasks: &TurboTasks<Self>,
667 ) -> Result<ReadOutcome<TypedCellContent>>;
668
669 fn try_read_own_task_cell(
672 &self,
673 current_task: TaskId,
674 index: CellId,
675 turbo_tasks: &TurboTasks<Self>,
676 ) -> Result<TypedCellContent>;
677
678 fn read_task_collectibles(
681 &self,
682 task: TaskId,
683 trait_id: TraitTypeId,
684 reader: Option<TaskId>,
685 turbo_tasks: &TurboTasks<Self>,
686 ) -> TaskCollectiblesMap;
687
688 fn emit_collectible(
689 &self,
690 trait_type: TraitTypeId,
691 collectible: RawVc,
692 task: TaskId,
693 turbo_tasks: &TurboTasks<Self>,
694 );
695
696 fn unemit_collectible(
697 &self,
698 trait_type: TraitTypeId,
699 collectible: RawVc,
700 count: u32,
701 task: TaskId,
702 turbo_tasks: &TurboTasks<Self>,
703 );
704
705 fn update_task_cell(
706 &self,
707 task: TaskId,
708 index: CellId,
709 content: CellContent,
710 updated_key_hashes: Option<SmallVec<[u64; 2]>>,
711 content_hash: Option<CellHash>,
712 verification_mode: VerificationMode,
713 turbo_tasks: &TurboTasks<Self>,
714 );
715
716 fn get_or_create_task(
717 &self,
718 native_fn: &'static NativeFunction,
719 this: Option<RawVc>,
720 arg: &mut dyn DynTaskInputsStorage,
721 parent_task: Option<TaskId>,
722 persistence: TaskPersistence,
723 turbo_tasks: &TurboTasks<Self>,
724 ) -> TaskId;
725
726 fn connect_task(
727 &self,
728 task: TaskId,
729 parent_task: Option<TaskId>,
730 turbo_tasks: &TurboTasks<Self>,
731 );
732
733 fn mark_own_task_as_finished(&self, _task: TaskId, _turbo_tasks: &TurboTasks<Self>) {
734 }
736
737 fn pin_task_for_gc(&self, _task: TaskId, _turbo_tasks: &TurboTasks<Self>);
741
742 fn unpin_task_for_gc(&self, _task: TaskId, _turbo_tasks: &TurboTasks<Self>);
744
745 fn create_transient_task(
746 &self,
747 task_type: TransientTaskType,
748 turbo_tasks: &TurboTasks<Self>,
749 ) -> TaskId;
750
751 fn dispose_root_task(&self, task: TaskId, turbo_tasks: &TurboTasks<Self>);
752
753 fn task_statistics(&self) -> &TaskStatisticsApi;
754
755 fn is_tracking_dependencies(&self) -> bool;
756
757 fn get_task_name(&self, task: TaskId, turbo_tasks: &TurboTasks<Self>) -> String;
760}
761
762#[cfg(test)]
763mod cached_task_type_tests {
764 use std::{collections::hash_map::RandomState, hash::BuildHasher};
765
766 use crate::{
767 RawVc, TaskId,
768 backend::CachedTaskType,
769 dyn_task_inputs::DynTaskInputs,
770 macro_helpers::{ArgMeta, NativeFunction, into_task_fn},
771 };
772
773 fn dummy_fn_a() {}
778 fn dummy_fn_b() {}
779
780 static FN_A: NativeFunction = NativeFunction::new(
781 "dummy_fn_a",
782 "dummy_fn_a",
783 ArgMeta::new::<(i32,)>(),
784 &into_task_fn(dummy_fn_a),
785 false,
786 false,
787 );
788
789 static FN_B: NativeFunction = NativeFunction::new(
790 "dummy_fn_b",
791 "dummy_fn_b",
792 ArgMeta::new::<(i32,)>(),
793 &into_task_fn(dummy_fn_b),
794 false,
795 false,
796 );
797
798 fn hash_task(rs: &RandomState, task: &CachedTaskType) -> u64 {
800 rs.hash_one(task)
801 }
802
803 fn make_arg(value: i32) -> Box<dyn DynTaskInputs> {
805 Box::new((value,))
806 }
807
808 fn make_this(id: u32) -> Option<RawVc> {
810 Some(RawVc::task_output(
811 TaskId::new(id).expect("non-zero task id"),
812 ))
813 }
814
815 #[test]
820 fn hash_from_components_matches_hash_impl_no_this() {
821 let rs = RandomState::new();
822 let arg = make_arg(42);
823 let task = CachedTaskType {
824 native_fn: &FN_A,
825 this: None,
826 arg: make_arg(42),
827 };
828 let expected = hash_task(&rs, &task);
829 let actual = CachedTaskType::hash_from_components(&rs, &FN_A, None, &*arg);
830 assert_eq!(actual, expected);
831 }
832
833 #[test]
834 fn hash_from_components_matches_hash_impl_with_this() {
835 let rs = RandomState::new();
836 let this = make_this(1);
837 let arg = make_arg(99);
838 let task = CachedTaskType {
839 native_fn: &FN_A,
840 this,
841 arg: make_arg(99),
842 };
843 let expected = hash_task(&rs, &task);
844 let actual = CachedTaskType::hash_from_components(&rs, &FN_A, this, &*arg);
845 assert_eq!(actual, expected);
846 }
847
848 #[test]
853 fn eq_components_returns_true_when_all_match() {
854 let task = CachedTaskType {
855 native_fn: &FN_A,
856 this: None,
857 arg: make_arg(7),
858 };
859 assert!(task.eq_components(&FN_A, None, &(7i32,)));
860 }
861
862 #[test]
863 fn eq_components_returns_true_with_matching_this() {
864 let this = make_this(1);
865 let task = CachedTaskType {
866 native_fn: &FN_A,
867 this,
868 arg: make_arg(7),
869 };
870 assert!(task.eq_components(&FN_A, this, &(7i32,)));
871 }
872
873 #[test]
878 fn eq_components_returns_false_when_native_fn_differs() {
879 let task = CachedTaskType {
880 native_fn: &FN_A,
881 this: None,
882 arg: make_arg(7),
883 };
884 assert!(!task.eq_components(&FN_B, None, &(7i32,)));
886 }
887
888 #[test]
893 fn eq_components_returns_false_when_this_differs() {
894 let task = CachedTaskType {
895 native_fn: &FN_A,
896 this: None,
897 arg: make_arg(7),
898 };
899 assert!(!task.eq_components(&FN_A, make_this(1), &(7i32,)));
901 }
902
903 #[test]
904 fn eq_components_returns_false_when_this_has_different_task_id() {
905 let task = CachedTaskType {
906 native_fn: &FN_A,
907 this: make_this(1),
908 arg: make_arg(7),
909 };
910 assert!(!task.eq_components(&FN_A, make_this(2), &(7i32,)));
911 }
912
913 #[test]
918 fn eq_components_returns_false_when_arg_differs() {
919 let task = CachedTaskType {
920 native_fn: &FN_A,
921 this: None,
922 arg: make_arg(1),
923 };
924 assert!(!task.eq_components(&FN_A, None, &(2i32,)));
926 }
927}