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