Don't materialize typed array until the transfer
Dan Abramov committed
Apr 15, 2019 at 16:27 UTC
6b9b80faace37046e1abf10f4a41f4cfa654c5ba
1 file changed
+84
-68
src/backend/renderer.js
+84
-68
@@ -577,22 +577,34 @@ export function attach(
577
);
578
}
579
580
- let pendingOperations: Uint32Array = new Uint32Array(0);
581
- let pendingOperationsQueue: Array<Uint32Array> | null = [];
582
-
583
- function addOperation(
584
- newAction: Uint32Array,
585
- addToStartOfQueue: boolean = false
586
- ): void {
587
- const oldActions = pendingOperations;
588
- pendingOperations = new Uint32Array(oldActions.length + newAction.length);
580
+ let pendingOperations: Array<number> = [];
581
+ let pendingOperationsQueue: Array<Array<number>> | null = [];
582
+
583
+ let nextOperation: Array<number> = [];
584
+ function beginNextOperation(size: number): void {
585
+ nextOperation.length = size;
586
+ }
587
+ function endNextOperation(addToStartOfQueue: boolean): void {
588
+ if (__DEV__) {
589
+ for (let i = 0; i < nextOperation.length; i++) {
590
+ if (!Number.isInteger(nextOperation[i])) {
591
+ console.error(
592
+ 'endNextOperation() was called but some values are not integers.',
593
+ nextOperation
594
+ );
595
+ }
596
+ }
597
+ }
598
+
599
if (addToStartOfQueue) {
590
- pendingOperations.set(newAction);
591
- pendingOperations.set(oldActions, newAction.length);
600
+ pendingOperations.splice.apply(
601
+ pendingOperations,
602
+ [0, 0].concat(nextOperation)
603
+ );
604
} else {
593
- pendingOperations.set(oldActions);
594
- pendingOperations.set(newAction, oldActions.length);
605
+ pendingOperations.push.apply(pendingOperations, nextOperation);
606
}
607
+ nextOperation.length = 0;
608
}
609
610
function flushPendingEvents(root: Object): void {
@@ -608,10 +620,10 @@ export function attach(
620
// Identify which renderer this update is coming from.
621
// This enables roots to be mapped to renderers,
622
// Which in turn enables fiber props, states, and hooks to be inspected.
611
- const idArray = new Uint32Array(2);
612
- idArray[0] = rendererID;
613
- idArray[1] = getFiberID(getPrimaryFiber(root.current));
614
- addOperation(idArray, true);
623
+ beginNextOperation(2);
624
+ nextOperation[0] = rendererID;
625
+ nextOperation[1] = getFiberID(getPrimaryFiber(root.current));
626
+ endNextOperation(true);
627
628
// Let the frontend know about tree operations.
629
// The first value in this array will identify which root it corresponds to,
@@ -623,10 +635,10 @@ export function attach(
635
pendingOperationsQueue.push(pendingOperations);
636
} else {
637
// If we've already connected to the frontend, just pass the operations through.
626
- hook.emit('operations', pendingOperations);
638
+ hook.emit('operations', Uint32Array.from(pendingOperations));
639
}
640
629
- pendingOperations = new Uint32Array(0);
641
+ pendingOperations = [];
642
}
643
644
function recordMount(fiber: Fiber, parentFiber: Fiber | null) {
@@ -642,13 +654,13 @@ export function attach(
654
const hasOwnerMetadata = fiber.hasOwnProperty('_debugOwner');
655
656
if (isRoot) {
645
- const operation = new Uint32Array(5);
646
- operation[0] = TREE_OPERATION_ADD;
647
- operation[1] = id;
648
- operation[2] = ElementTypeRoot;
649
- operation[3] = isProfilingSupported ? 1 : 0;
650
- operation[4] = hasOwnerMetadata ? 1 : 0;
651
- addOperation(operation);
657
+ beginNextOperation(5);
658
+ nextOperation[0] = TREE_OPERATION_ADD;
659
+ nextOperation[1] = id;
660
+ nextOperation[2] = ElementTypeRoot;
661
+ nextOperation[3] = isProfilingSupported ? 1 : 0;
662
+ nextOperation[4] = hasOwnerMetadata ? 1 : 0;
663
+ endNextOperation(false);
664
} else {
665
const { displayName, key, type } = getDataForFiber(fiber);
666
const { _debugOwner } = fiber;
@@ -675,23 +687,25 @@ export function attach(
687
displayName === null ? 0 : encodedDisplayName.length;
688
const encodedKeySize = key === null ? 0 : encodedKey.length;
689
678
- const operation = new Uint32Array(
679
- 7 + encodedDisplayNameSize + encodedKeySize
680
- );
681
- operation[0] = TREE_OPERATION_ADD;
682
- operation[1] = id;
683
- operation[2] = type;
684
- operation[3] = parentID;
685
- operation[4] = ownerID;
686
- operation[5] = encodedDisplayNameSize;
690
+ beginNextOperation(7 + encodedDisplayNameSize + encodedKeySize);
691
+ nextOperation[0] = TREE_OPERATION_ADD;
692
+ nextOperation[1] = id;
693
+ nextOperation[2] = type;
694
+ nextOperation[3] = parentID;
695
+ nextOperation[4] = ownerID;
696
+ nextOperation[5] = encodedDisplayNameSize;
697
if (displayName !== null) {
688
- operation.set(encodedDisplayName, 6);
698
+ for (let i = 0; i < encodedDisplayName.length; i++) {
699
+ nextOperation[6 + i] = encodedDisplayName[i];
700
+ }
701
}
690
- operation[6 + encodedDisplayNameSize] = encodedKeySize;
702
+ nextOperation[6 + encodedDisplayNameSize] = encodedKeySize;
703
if (key !== null) {
692
- operation.set(encodedKey, 6 + encodedDisplayNameSize + 1);
704
+ for (let i = 0; i < encodedKey.length; i++) {
705
+ nextOperation[6 + encodedDisplayNameSize + 1 + i] = encodedKey[i];
706
+ }
707
}
694
- addOperation(operation);
708
+ endNextOperation(false);
709
}
710
711
if (isProfiling) {
@@ -699,11 +713,11 @@ export function attach(
713
// So we have to convert them from milliseconds to microseconds so we can send them as ints.
714
const treeBaseDuration = Math.floor(fiber.treeBaseDuration * 1000);
715
702
- const operation = new Uint32Array(3);
703
- operation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
704
- operation[1] = id;
705
- operation[2] = treeBaseDuration;
706
- addOperation(operation);
716
+ beginNextOperation(3);
717
+ nextOperation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
718
+ nextOperation[1] = id;
719
+ nextOperation[2] = treeBaseDuration;
720
+ endNextOperation(false);
721
722
const { actualDuration } = fiber;
723
if (actualDuration > 0) {
@@ -733,19 +747,19 @@ export function attach(
747
}
748
const id = getFiberID(primaryFiber);
749
if (isRoot) {
736
- const operation = new Uint32Array(2);
737
- operation[0] = TREE_OPERATION_REMOVE;
738
- operation[1] = id;
739
- addOperation(operation);
750
+ beginNextOperation(2);
751
+ nextOperation[0] = TREE_OPERATION_REMOVE;
752
+ nextOperation[1] = id;
753
+ endNextOperation(false);
754
} else if (!shouldFilterFiber(fiber)) {
755
// Non-root fibers are deleted during the commit phase.
756
// They are deleted in the child-first order. However
757
// DevTools currently expects deletions to be parent-first.
758
// This is why we unshift deletions rather tha
745
- const operation = new Uint32Array(2);
746
- operation[0] = TREE_OPERATION_REMOVE;
747
- operation[1] = id;
748
- addOperation(operation, true);
759
+ beginNextOperation(2);
760
+ nextOperation[0] = TREE_OPERATION_REMOVE;
761
+ nextOperation[1] = id;
762
+ endNextOperation(true);
763
}
764
fiberToIDMap.delete(primaryFiber);
765
idToFiberMap.delete(id);
@@ -761,10 +775,10 @@ export function attach(
775
function recordRecursiveRemoveChildren(fiber) {
776
const primaryFiber = getPrimaryFiber(fiber);
777
const id = getFiberID(primaryFiber);
764
- const operation = new Uint32Array(2);
765
- operation[0] = TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN;
766
- operation[1] = id;
767
- addOperation(operation, false);
778
+ beginNextOperation(2);
779
+ nextOperation[0] = TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN;
780
+ nextOperation[1] = id;
781
+ endNextOperation(false);
782
}
783
784
function mountFiberRecursively(
@@ -832,11 +846,11 @@ export function attach(
846
// So we have to convert them from milliseconds to microseconds so we can send them as ints.
847
const treeBaseDuration = Math.floor(fiber.treeBaseDuration * 1000);
848
835
- const operation = new Uint32Array(3);
836
- operation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
837
- operation[1] = getFiberID(getPrimaryFiber(fiber));
838
- operation[2] = treeBaseDuration;
839
- addOperation(operation);
849
+ beginNextOperation(3);
850
+ nextOperation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
851
+ nextOperation[1] = getFiberID(getPrimaryFiber(fiber));
852
+ nextOperation[2] = treeBaseDuration;
853
+ endNextOperation(false);
854
}
855
856
if (haveProfilerTimesChanged(fiber.alternate, fiber)) {
@@ -868,12 +882,14 @@ export function attach(
882
}
883
884
const numChildren = nextChildren.length;
871
- const operation = new Uint32Array(3 + numChildren);
872
- operation[0] = TREE_OPERATION_RESET_CHILDREN;
873
- operation[1] = getFiberID(getPrimaryFiber(fiber));
874
- operation[2] = numChildren;
875
- operation.set(nextChildren, 3);
876
- addOperation(operation);
885
+ beginNextOperation(3 + numChildren);
886
+ nextOperation[0] = TREE_OPERATION_RESET_CHILDREN;
887
+ nextOperation[1] = getFiberID(getPrimaryFiber(fiber));
888
+ nextOperation[2] = numChildren;
889
+ for (let i = 0; i < nextChildren.length; i++) {
890
+ nextOperation[3 + i] = nextChildren[i];
891
+ }
892
+ endNextOperation(false);
893
}
894
}
895
@@ -1029,7 +1045,7 @@ export function attach(
1045
// We may have already queued up some operations before the frontend connected
1046
// If so, let the frontend know about them.
1047
localPendingOperationsQueue.forEach(pendingOperations => {
1032
- hook.emit('operations', pendingOperations);
1048
+ hook.emit('operations', Uint32Array.from(pendingOperations));
1049
});
1050
} else {
1051
// If we have not been profiling, then we can just walk the tree and build up its current state as-is.