76
let thenableState: ThenableState | null = null;
77
let thenableIndexCounter: number = 0;
78
79
-function mergeDebugInfo(
80
- outer: ReactDebugInfo | null,
81
- inner: ReactDebugInfo | null | void,
82
-): ReactDebugInfo | null {
79
+// Server Components Meta Data
80
+let currentDebugInfo: null | ReactDebugInfo = null;
81
+
82
+function pushDebugInfo(
83
+ debugInfo: null | ReactDebugInfo,
84
+): null | ReactDebugInfo {
85
if (!__DEV__) {
86
return null;
87
}
86
- if (inner == null) {
87
- return outer;
88
- } else if (outer === null) {
89
- return inner;
88
+ const previousDebugInfo = currentDebugInfo;
89
+ if (debugInfo == null) {
90
+ // Leave inplace
91
+ } else if (previousDebugInfo === null) {
92
+ currentDebugInfo = debugInfo;
93
} else {
94
// If we have two debugInfo, we need to create a new one. This makes the array no longer
95
// live so we'll miss any future updates if we received more so ideally we should always
96
// do this after both have fully resolved/unsuspended.
94
- return outer.concat(inner);
97
+ currentDebugInfo = previousDebugInfo.concat(debugInfo);
98
}
99
+ return previousDebugInfo;
100
}
101
102
let didWarnAboutMaps;
501
current: Fiber | null,
502
textContent: string,
503
lanes: Lanes,
500
- debugInfo: ReactDebugInfo | null,
504
) {
505
if (current === null || current.tag !== HostText) {
506
// Insert
507
const created = createFiberFromText(textContent, returnFiber.mode, lanes);
508
created.return = returnFiber;
509
if (__DEV__) {
507
- created._debugInfo = debugInfo;
510
+ created._debugInfo = currentDebugInfo;
511
}
512
return created;
513
} else {
515
const existing = useFiber(current, textContent);
516
existing.return = returnFiber;
517
if (__DEV__) {
515
- existing._debugInfo = debugInfo;
518
+ existing._debugInfo = currentDebugInfo;
519
}
520
return existing;
521
}
526
current: Fiber | null,
527
element: ReactElement,
528
lanes: Lanes,
526
- debugInfo: ReactDebugInfo | null,
529
): Fiber {
530
const elementType = element.type;
531
if (elementType === REACT_FRAGMENT_TYPE) {
535
element.props.children,
536
lanes,
537
element.key,
536
- debugInfo,
538
);
539
validateFragmentProps(element, updated, returnFiber);
540
return updated;
561
existing.return = returnFiber;
562
if (__DEV__) {
563
existing._debugOwner = element._owner;
563
- existing._debugInfo = debugInfo;
564
+ existing._debugInfo = currentDebugInfo;
565
}
566
return existing;
567
}
571
coerceRef(returnFiber, current, created, element);
572
created.return = returnFiber;
573
if (__DEV__) {
573
- created._debugInfo = debugInfo;
574
+ created._debugInfo = currentDebugInfo;
575
}
576
return created;
577
}
581
current: Fiber | null,
582
portal: ReactPortal,
583
lanes: Lanes,
583
- debugInfo: ReactDebugInfo | null,
584
): Fiber {
585
if (
586
current === null ||
592
const created = createFiberFromPortal(portal, returnFiber.mode, lanes);
593
created.return = returnFiber;
594
if (__DEV__) {
595
- created._debugInfo = debugInfo;
595
+ created._debugInfo = currentDebugInfo;
596
}
597
return created;
598
} else {
600
const existing = useFiber(current, portal.children || []);
601
existing.return = returnFiber;
602
if (__DEV__) {
603
- existing._debugInfo = debugInfo;
603
+ existing._debugInfo = currentDebugInfo;
604
}
605
return existing;
606
}
612
fragment: Iterable<React$Node>,
613
lanes: Lanes,
614
key: null | string,
615
- debugInfo: null | ReactDebugInfo,
615
): Fiber {
616
if (current === null || current.tag !== Fragment) {
617
// Insert
623
);
624
created.return = returnFiber;
625
if (__DEV__) {
627
- created._debugInfo = debugInfo;
626
+ created._debugInfo = currentDebugInfo;
627
}
628
return created;
629
} else {
631
const existing = useFiber(current, fragment);
632
existing.return = returnFiber;
633
if (__DEV__) {
635
- existing._debugInfo = debugInfo;
634
+ existing._debugInfo = currentDebugInfo;
635
}
636
return existing;
637
}
641
returnFiber: Fiber,
642
newChild: any,
643
lanes: Lanes,
645
- debugInfo: ReactDebugInfo | null,
644
): Fiber | null {
645
if (
646
(typeof newChild === 'string' && newChild !== '') ||
658
);
659
created.return = returnFiber;
660
if (__DEV__) {
663
- created._debugInfo = debugInfo;
661
+ created._debugInfo = currentDebugInfo;
662
}
663
return created;
664
}
674
coerceRef(returnFiber, null, created, newChild);
675
created.return = returnFiber;
676
if (__DEV__) {
679
- created._debugInfo = mergeDebugInfo(debugInfo, newChild._debugInfo);
677
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
678
+ created._debugInfo = currentDebugInfo;
679
+ currentDebugInfo = prevDebugInfo;
680
}
681
return created;
682
}
688
);
689
created.return = returnFiber;
690
if (__DEV__) {
691
- created._debugInfo = debugInfo;
691
+ created._debugInfo = currentDebugInfo;
692
}
693
return created;
694
}
695
case REACT_LAZY_TYPE: {
696
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
697
let resolvedChild;
698
if (__DEV__) {
699
resolvedChild = callLazyInitInDEV(newChild);
702
const init = newChild._init;
703
resolvedChild = init(payload);
704
}
704
- return createChild(
705
- returnFiber,
706
- resolvedChild,
707
- lanes,
708
- mergeDebugInfo(debugInfo, newChild._debugInfo), // call merge after init
709
- );
705
+ const created = createChild(returnFiber, resolvedChild, lanes);
706
+ currentDebugInfo = prevDebugInfo;
707
+ return created;
708
}
709
}
710
722
);
723
created.return = returnFiber;
724
if (__DEV__) {
727
- created._debugInfo = mergeDebugInfo(debugInfo, newChild._debugInfo);
725
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
726
+ created._debugInfo = currentDebugInfo;
727
+ currentDebugInfo = prevDebugInfo;
728
}
729
return created;
730
}
734
// Unwrap the inner value and recursively call this function again.
735
if (typeof newChild.then === 'function') {
736
const thenable: Thenable<any> = (newChild: any);
737
- return createChild(
737
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
738
+ const created = createChild(
739
returnFiber,
740
unwrapThenable(thenable),
741
lanes,
741
- mergeDebugInfo(debugInfo, newChild._debugInfo),
742
);
743
+ currentDebugInfo = prevDebugInfo;
744
+ return created;
745
}
746
747
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
750
returnFiber,
751
readContextDuringReconciliation(returnFiber, context, lanes),
752
lanes,
751
- debugInfo,
753
);
754
}
755
773
oldFiber: Fiber | null,
774
newChild: any,
775
lanes: Lanes,
775
- debugInfo: null | ReactDebugInfo,
776
): Fiber | null {
777
// Update the fiber if the keys match, otherwise return null.
778
const key = oldFiber !== null ? oldFiber.key : null;
794
// $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint
795
'' + newChild,
796
lanes,
797
- debugInfo,
797
);
798
}
799
801
switch (newChild.$$typeof) {
802
case REACT_ELEMENT_TYPE: {
803
if (newChild.key === key) {
805
- return updateElement(
804
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
805
+ const updated = updateElement(
806
returnFiber,
807
oldFiber,
808
newChild,
809
lanes,
810
- mergeDebugInfo(debugInfo, newChild._debugInfo),
810
);
811
+ currentDebugInfo = prevDebugInfo;
812
+ return updated;
813
} else {
814
return null;
815
}
816
}
817
case REACT_PORTAL_TYPE: {
818
if (newChild.key === key) {
818
- return updatePortal(
819
- returnFiber,
820
- oldFiber,
821
- newChild,
822
- lanes,
823
- debugInfo,
824
- );
819
+ return updatePortal(returnFiber, oldFiber, newChild, lanes);
820
} else {
821
return null;
822
}
823
}
824
case REACT_LAZY_TYPE: {
825
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
826
let resolvedChild;
827
if (__DEV__) {
828
resolvedChild = callLazyInitInDEV(newChild);
831
const init = newChild._init;
832
resolvedChild = init(payload);
833
}
838
- return updateSlot(
834
+ const updated = updateSlot(
835
returnFiber,
836
oldFiber,
837
resolvedChild,
838
lanes,
843
- mergeDebugInfo(debugInfo, newChild._debugInfo),
839
);
840
+ currentDebugInfo = prevDebugInfo;
841
+ return updated;
842
}
843
}
844
852
return null;
853
}
854
858
- return updateFragment(
855
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
856
+ const updated = updateFragment(
857
returnFiber,
858
oldFiber,
859
newChild,
860
lanes,
861
null,
864
- mergeDebugInfo(debugInfo, newChild._debugInfo),
862
);
863
+ currentDebugInfo = prevDebugInfo;
864
+ return updated;
865
}
866
867
// Usable node types
869
// Unwrap the inner value and recursively call this function again.
870
if (typeof newChild.then === 'function') {
871
const thenable: Thenable<any> = (newChild: any);
873
- return updateSlot(
872
+ const prevDebugInfo = pushDebugInfo((thenable: any)._debugInfo);
873
+ const updated = updateSlot(
874
returnFiber,
875
oldFiber,
876
unwrapThenable(thenable),
877
lanes,
878
- debugInfo,
878
);
879
+ currentDebugInfo = prevDebugInfo;
880
+ return updated;
881
}
882
883
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
887
oldFiber,
888
readContextDuringReconciliation(returnFiber, context, lanes),
889
lanes,
889
- debugInfo,
890
);
891
}
892
911
newIdx: number,
912
newChild: any,
913
lanes: Lanes,
914
- debugInfo: ReactDebugInfo | null,
914
): Fiber | null {
915
if (
916
(typeof newChild === 'string' && newChild !== '') ||
926
// $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint
927
'' + newChild,
928
lanes,
930
- debugInfo,
929
);
930
}
931
936
existingChildren.get(
937
newChild.key === null ? newIdx : newChild.key,
938
) || null;
941
- return updateElement(
939
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
940
+ const updated = updateElement(
941
returnFiber,
942
matchedFiber,
943
newChild,
944
lanes,
946
- mergeDebugInfo(debugInfo, newChild._debugInfo),
945
);
946
+ currentDebugInfo = prevDebugInfo;
947
+ return updated;
948
}
949
case REACT_PORTAL_TYPE: {
950
const matchedFiber =
951
existingChildren.get(
952
newChild.key === null ? newIdx : newChild.key,
953
) || null;
954
- return updatePortal(
955
- returnFiber,
956
- matchedFiber,
957
- newChild,
958
- lanes,
959
- debugInfo,
960
- );
954
+ return updatePortal(returnFiber, matchedFiber, newChild, lanes);
955
}
956
case REACT_LAZY_TYPE: {
957
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
958
let resolvedChild;
959
if (__DEV__) {
960
resolvedChild = callLazyInitInDEV(newChild);
963
const init = newChild._init;
964
resolvedChild = init(payload);
965
}
971
- return updateFromMap(
966
+ const updated = updateFromMap(
967
existingChildren,
968
returnFiber,
969
newIdx,
970
resolvedChild,
971
lanes,
977
- mergeDebugInfo(debugInfo, newChild._debugInfo),
972
);
973
+ currentDebugInfo = prevDebugInfo;
974
+ return updated;
975
}
976
}
977
982
typeof newChild[ASYNC_ITERATOR] === 'function')
983
) {
984
const matchedFiber = existingChildren.get(newIdx) || null;
989
- return updateFragment(
985
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
986
+ const updated = updateFragment(
987
returnFiber,
988
matchedFiber,
989
newChild,
990
lanes,
991
null,
995
- mergeDebugInfo(debugInfo, newChild._debugInfo),
992
);
993
+ currentDebugInfo = prevDebugInfo;
994
+ return updated;
995
}
996
997
// Usable node types
999
// Unwrap the inner value and recursively call this function again.
1000
if (typeof newChild.then === 'function') {
1001
const thenable: Thenable<any> = (newChild: any);
1004
- return updateFromMap(
1002
+ const prevDebugInfo = pushDebugInfo((thenable: any)._debugInfo);
1003
+ const updated = updateFromMap(
1004
existingChildren,
1005
returnFiber,
1006
newIdx,
1007
unwrapThenable(thenable),
1008
lanes,
1010
- debugInfo,
1009
);
1010
+ currentDebugInfo = prevDebugInfo;
1011
+ return updated;
1012
}
1013
1014
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
1019
newIdx,
1020
readContextDuringReconciliation(returnFiber, context, lanes),
1021
lanes,
1022
- debugInfo,
1022
);
1023
}
1024
1107
currentFirstChild: Fiber | null,
1108
newChildren: Array<any>,
1109
lanes: Lanes,
1111
- debugInfo: ReactDebugInfo | null,
1110
): Fiber | null {
1111
// This algorithm can't optimize by searching from both ends since we
1112
// don't have backpointers on fibers. I'm trying to see how far we can get
1148
oldFiber,
1149
newChildren[newIdx],
1150
lanes,
1153
- debugInfo,
1151
);
1152
if (newFiber === null) {
1153
// TODO: This breaks on empty slots like null children. That's
1205
// If we don't have any more existing children we can choose a fast path
1206
// since the rest will all be insertions.
1207
for (; newIdx < newChildren.length; newIdx++) {
1211
- const newFiber = createChild(
1212
- returnFiber,
1213
- newChildren[newIdx],
1214
- lanes,
1215
- debugInfo,
1216
- );
1208
+ const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
1209
if (newFiber === null) {
1210
continue;
1211
}
1244
newIdx,
1245
newChildren[newIdx],
1246
lanes,
1255
- debugInfo,
1247
);
1248
if (newFiber !== null) {
1249
if (__DEV__) {
1293
currentFirstChild: Fiber | null,
1294
newChildrenIterable: Iterable<mixed>,
1295
lanes: Lanes,
1305
- debugInfo: ReactDebugInfo | null,
1296
): Fiber | null {
1297
// This is the same implementation as reconcileChildrenArray(),
1298
// but using the iterator instead.
1351
currentFirstChild,
1352
newChildren,
1353
lanes,
1364
- debugInfo,
1354
);
1355
}
1356
1359
currentFirstChild: Fiber | null,
1360
newChildrenIterable: AsyncIterable<mixed>,
1361
lanes: Lanes,
1373
- debugInfo: ReactDebugInfo | null,
1362
): Fiber | null {
1363
const newChildren = newChildrenIterable[ASYNC_ITERATOR]();
1364
1407
currentFirstChild,
1408
iterator,
1409
lanes,
1422
- debugInfo,
1410
);
1411
}
1412
1415
currentFirstChild: Fiber | null,
1416
newChildren: ?Iterator<mixed>,
1417
lanes: Lanes,
1431
- debugInfo: ReactDebugInfo | null,
1418
): Fiber | null {
1419
if (newChildren == null) {
1420
throw new Error('An iterable object provided no iterator.');
1442
} else {
1443
nextOldFiber = oldFiber.sibling;
1444
}
1459
- const newFiber = updateSlot(
1460
- returnFiber,
1461
- oldFiber,
1462
- step.value,
1463
- lanes,
1464
- debugInfo,
1465
- );
1445
+ const newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);
1446
if (newFiber === null) {
1447
// TODO: This breaks on empty slots like null children. That's
1448
// unfortunate because it triggers the slow path all the time. We need
1499
// If we don't have any more existing children we can choose a fast path
1500
// since the rest will all be insertions.
1501
for (; !step.done; newIdx++, step = newChildren.next()) {
1522
- const newFiber = createChild(returnFiber, step.value, lanes, debugInfo);
1502
+ const newFiber = createChild(returnFiber, step.value, lanes);
1503
if (newFiber === null) {
1504
continue;
1505
}
1538
newIdx,
1539
step.value,
1540
lanes,
1561
- debugInfo,
1541
);
1542
if (newFiber !== null) {
1543
if (__DEV__) {
1611
currentFirstChild: Fiber | null,
1612
element: ReactElement,
1613
lanes: Lanes,
1635
- debugInfo: ReactDebugInfo | null,
1614
): Fiber {
1615
const key = element.key;
1616
let child = currentFirstChild;
1626
existing.return = returnFiber;
1627
if (__DEV__) {
1628
existing._debugOwner = element._owner;
1651
- existing._debugInfo = debugInfo;
1629
+ existing._debugInfo = currentDebugInfo;
1630
}
1631
validateFragmentProps(element, existing, returnFiber);
1632
return existing;
1653
existing.return = returnFiber;
1654
if (__DEV__) {
1655
existing._debugOwner = element._owner;
1678
- existing._debugInfo = debugInfo;
1656
+ existing._debugInfo = currentDebugInfo;
1657
}
1658
return existing;
1659
}
1676
);
1677
created.return = returnFiber;
1678
if (__DEV__) {
1701
- created._debugInfo = debugInfo;
1679
+ created._debugInfo = currentDebugInfo;
1680
}
1681
validateFragmentProps(element, created, returnFiber);
1682
return created;
1685
coerceRef(returnFiber, currentFirstChild, created, element);
1686
created.return = returnFiber;
1687
if (__DEV__) {
1710
- created._debugInfo = debugInfo;
1688
+ created._debugInfo = currentDebugInfo;
1689
}
1690
return created;
1691
}
1696
currentFirstChild: Fiber | null,
1697
portal: ReactPortal,
1698
lanes: Lanes,
1721
- debugInfo: ReactDebugInfo | null,
1699
): Fiber {
1700
const key = portal.key;
1701
let child = currentFirstChild;
1735
currentFirstChild: Fiber | null,
1736
newChild: any,
1737
lanes: Lanes,
1761
- debugInfo: ReactDebugInfo | null,
1738
): Fiber | null {
1739
// This function is only recursive for Usables/Lazy and not nested arrays.
1740
// That's so that using a Lazy wrapper is unobservable to the Fragment
1761
// Handle object types
1762
if (typeof newChild === 'object' && newChild !== null) {
1763
switch (newChild.$$typeof) {
1788
- case REACT_ELEMENT_TYPE:
1789
- return placeSingleChild(
1764
+ case REACT_ELEMENT_TYPE: {
1765
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
1766
+ const firstChild = placeSingleChild(
1767
reconcileSingleElement(
1768
returnFiber,
1769
currentFirstChild,
1770
newChild,
1771
lanes,
1795
- mergeDebugInfo(debugInfo, newChild._debugInfo),
1772
),
1773
);
1774
+ currentDebugInfo = prevDebugInfo;
1775
+ return firstChild;
1776
+ }
1777
case REACT_PORTAL_TYPE:
1778
return placeSingleChild(
1779
reconcileSinglePortal(
1781
currentFirstChild,
1782
newChild,
1783
lanes,
1805
- debugInfo,
1784
),
1785
);
1808
- case REACT_LAZY_TYPE:
1809
- const payload = newChild._payload;
1810
- const init = newChild._init;
1811
- return reconcileChildFibersImpl(
1786
+ case REACT_LAZY_TYPE: {
1787
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
1788
+ let result;
1789
+ if (__DEV__) {
1790
+ result = callLazyInitInDEV(newChild);
1791
+ } else {
1792
+ const payload = newChild._payload;
1793
+ const init = newChild._init;
1794
+ result = init(payload);
1795
+ }
1796
+ const firstChild = reconcileChildFibersImpl(
1797
returnFiber,
1798
currentFirstChild,
1814
- init(payload),
1799
+ result,
1800
lanes,
1816
- mergeDebugInfo(debugInfo, newChild._debugInfo),
1801
);
1802
+ currentDebugInfo = prevDebugInfo;
1803
+ return firstChild;
1804
+ }
1805
}
1806
1807
if (isArray(newChild)) {
1821
- return reconcileChildrenArray(
1808
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
1809
+ const firstChild = reconcileChildrenArray(
1810
returnFiber,
1811
currentFirstChild,
1812
newChild,
1813
lanes,
1826
- mergeDebugInfo(debugInfo, newChild._debugInfo),
1814
);
1815
+ currentDebugInfo = prevDebugInfo;
1816
+ return firstChild;
1817
}
1818
1819
if (getIteratorFn(newChild)) {
1831
- return reconcileChildrenIteratable(
1820
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
1821
+ const firstChild = reconcileChildrenIteratable(
1822
returnFiber,
1823
currentFirstChild,
1824
newChild,
1825
lanes,
1836
- mergeDebugInfo(debugInfo, newChild._debugInfo),
1826
);
1827
+ currentDebugInfo = prevDebugInfo;
1828
+ return firstChild;
1829
}
1830
1831
if (
1832
enableAsyncIterableChildren &&
1833
typeof newChild[ASYNC_ITERATOR] === 'function'
1834
) {
1844
- return reconcileChildrenAsyncIteratable(
1835
+ const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
1836
+ const firstChild = reconcileChildrenAsyncIteratable(
1837
returnFiber,
1838
currentFirstChild,
1839
newChild,
1840
lanes,
1849
- mergeDebugInfo(debugInfo, newChild._debugInfo),
1841
);
1842
+ currentDebugInfo = prevDebugInfo;
1843
+ return firstChild;
1844
}
1845
1846
// Usables are a valid React node type. When React encounters a Usable in
1861
// consider as an future improvement.
1862
if (typeof newChild.then === 'function') {
1863
const thenable: Thenable<any> = (newChild: any);
1871
- return reconcileChildFibersImpl(
1864
+ const prevDebugInfo = pushDebugInfo((thenable: any)._debugInfo);
1865
+ const firstChild = reconcileChildFibersImpl(
1866
returnFiber,
1867
currentFirstChild,
1868
unwrapThenable(thenable),
1869
lanes,
1876
- mergeDebugInfo(debugInfo, thenable._debugInfo),
1870
);
1871
+ currentDebugInfo = prevDebugInfo;
1872
+ return firstChild;
1873
}
1874
1875
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
1879
currentFirstChild,
1880
readContextDuringReconciliation(returnFiber, context, lanes),
1881
lanes,
1887
- debugInfo,
1882
);
1883
}
1884
1920
newChild: any,
1921
lanes: Lanes,
1922
): Fiber | null {
1923
+ const prevDebugInfo = currentDebugInfo;
1924
+ currentDebugInfo = null;
1925
try {
1926
// This indirection only exists so we can reset `thenableState` at the end.
1927
// It should get inlined by Closure.
1931
currentFirstChild,
1932
newChild,
1933
lanes,
1938
- null, // debugInfo
1934
);
1935
thenableState = null;
1936
// Don't bother to reset `thenableIndexCounter` to 0 because it always gets
1958
// the error or suspending if needed.
1959
const throwFiber = createFiberFromThrow(x, returnFiber.mode, lanes);
1960
throwFiber.return = returnFiber;
1961
+ if (__DEV__) {
1962
+ throwFiber._debugInfo = currentDebugInfo;
1963
+ }
1964
return throwFiber;
1965
+ } finally {
1966
+ currentDebugInfo = prevDebugInfo;
1967
}
1968
}
1969