@@ -597,8 +597,12 @@ class ReadableStream {
597597 ! controller [ kState ] . queue . length ) {
598598 readableStreamDefaultControllerClearAlgorithms ( controller ) ;
599599 readableStreamClose ( stream ) ;
600- } else {
601- readableStreamDefaultControllerCallPullIfNeeded ( controller ) ;
600+ } else if ( ! controller [ kState ] . closeRequested &&
601+ controller [ kState ] . started &&
602+ controller [ kState ] . highWaterMark -
603+ controller [ kState ] . queueTotalSize > 0 ) {
604+ // Reduced ShouldCallPull, as in the read() fast path.
605+ readableStreamDefaultControllerPull ( controller ) ;
602606 }
603607
604608 return PromiseResolve ( { done : false , value : chunk } ) ;
@@ -947,8 +951,14 @@ class ReadableStreamDefaultReader {
947951 if ( controller [ kState ] . closeRequested && ! controller [ kState ] . queue . length ) {
948952 readableStreamDefaultControllerClearAlgorithms ( controller ) ;
949953 readableStreamClose ( stream ) ;
950- } else {
951- readableStreamDefaultControllerCallPullIfNeeded ( controller ) ;
954+ } else if ( ! controller [ kState ] . closeRequested &&
955+ controller [ kState ] . started &&
956+ controller [ kState ] . highWaterMark -
957+ controller [ kState ] . queueTotalSize > 0 ) {
958+ // ShouldCallPull reduced to the conditions not already
959+ // established on this path: the state is readable and the
960+ // queue was non-empty, so no read requests can be parked.
961+ readableStreamDefaultControllerPull ( controller ) ;
952962 }
953963
954964 return PromiseResolve ( { done : false , value : chunk } ) ;
@@ -2522,8 +2532,27 @@ function readableStreamDefaultControllerEnqueue(controller, chunk) {
25222532 reader [ kState ] !== undefined &&
25232533 reader [ kType ] === 'ReadableStreamDefaultReader' &&
25242534 reader [ kState ] . readRequests . length ) {
2535+ // Fulfilling a read request can run user code synchronously (the
2536+ // pipeTo read request invokes the sink's write algorithm), so the
2537+ // full ShouldCallPull predicate has to be re-evaluated afterwards.
25252538 readableStreamFulfillReadRequest ( stream , chunk , false ) ;
2539+ } else if ( controllerState . sizeAlgorithm === defaultSizeAlgorithm ) {
2540+ // The default size algorithm always returns 1, so the call and the
2541+ // size validation in enqueueValueWithSize can be skipped entirely.
2542+ // No user code runs between the guards at the top of this function
2543+ // and this point, so ShouldCallPull reduces to the started flag and
2544+ // the desired size (this branch implies no parked read requests,
2545+ // ruling out the reader arm of the predicate).
2546+ ArrayPrototypePush ( controllerState . queue , { value : chunk , size : 1 } ) ;
2547+ controllerState . queueTotalSize ++ ;
2548+ if ( controllerState . started &&
2549+ controllerState . highWaterMark - controllerState . queueTotalSize > 0 ) {
2550+ readableStreamDefaultControllerPull ( controller ) ;
2551+ }
2552+ return ;
25262553 } else {
2554+ // The user-supplied size algorithm may run arbitrary code, so the
2555+ // full ShouldCallPull predicate has to be re-evaluated afterwards.
25272556 try {
25282557 const chunkSize =
25292558 FunctionPrototypeCall (
@@ -2592,6 +2621,13 @@ function readableStreamDefaultControllerShouldCallPull(controller) {
25922621function readableStreamDefaultControllerCallPullIfNeeded ( controller ) {
25932622 if ( ! readableStreamDefaultControllerShouldCallPull ( controller ) )
25942623 return ;
2624+ readableStreamDefaultControllerPull ( controller ) ;
2625+ }
2626+
2627+ // The ShouldCallPull half of CallPullIfNeeded, split out so that callers
2628+ // that have already established the predicate from state in scope (the
2629+ // enqueue path above) can skip re-running it.
2630+ function readableStreamDefaultControllerPull ( controller ) {
25952631 if ( controller [ kState ] . pulling ) {
25962632 controller [ kState ] . pullAgain = true ;
25972633 return ;
@@ -2652,14 +2688,24 @@ function readableStreamDefaultControllerPullSteps(controller, readRequest) {
26522688 if ( controller [ kState ] . closeRequested && ! queue . length ) {
26532689 readableStreamDefaultControllerClearAlgorithms ( controller ) ;
26542690 readableStreamClose ( stream ) ;
2655- } else {
2656- readableStreamDefaultControllerCallPullIfNeeded ( controller ) ;
2691+ } else if ( ! controller [ kState ] . closeRequested &&
2692+ controller [ kState ] . started &&
2693+ controller [ kState ] . highWaterMark -
2694+ controller [ kState ] . queueTotalSize > 0 ) {
2695+ // Reduced ShouldCallPull: the state is known to be readable and the
2696+ // queue was non-empty, so no read requests can be parked.
2697+ readableStreamDefaultControllerPull ( controller ) ;
26572698 }
26582699 readRequest [ kChunk ] ( chunk ) ;
26592700 return ;
26602701 }
26612702 readableStreamAddReadRequest ( stream , readRequest ) ;
2662- readableStreamDefaultControllerCallPullIfNeeded ( controller ) ;
2703+ // Reduced ShouldCallPull: the state is known to be readable, an empty
2704+ // queue in that state implies close has not been requested (close with
2705+ // an empty queue closes the stream immediately), and the read request
2706+ // parked above already satisfies the reader-with-pending-reads arm.
2707+ if ( controller [ kState ] . started )
2708+ readableStreamDefaultControllerPull ( controller ) ;
26632709}
26642710
26652711function setupReadableStreamDefaultController (
0 commit comments