@@ -362,8 +362,8 @@ export class CodeQLCliServer implements Disposable {
362362 silent ?: boolean ,
363363 ) : Promise < string > {
364364 const stderrBuffers : Buffer [ ] = [ ] ;
365- // The array of fragments of stderr of this line. To be used for logging.
366- let currentLineStderrBuffers : Buffer [ ] = [ ] ;
365+ // The current buffer of stderr of a single line. To be used for logging.
366+ let currentLineStderrBuffer : Buffer = Buffer . alloc ( 0 ) ;
367367 if ( this . commandInProcess ) {
368368 throw new Error ( "runCodeQlCliInternal called while cli was running" ) ;
369369 }
@@ -423,28 +423,34 @@ export class CodeQLCliServer implements Disposable {
423423 stderrBuffers . push ( newData ) ;
424424
425425 if ( ! silent ) {
426- currentLineStderrBuffers . push ( newData ) ;
426+ currentLineStderrBuffer = Buffer . concat ( [
427+ currentLineStderrBuffer ,
428+ newData ,
429+ ] ) ;
427430
428431 // Print the stderr to the logger as it comes in. We need to ensure that
429432 // we don't split messages on the same line, so we buffer the stderr and
430433 // split it on EOLs.
431- let currentLineBuffer = Buffer . concat ( currentLineStderrBuffers ) ;
432434 const eolBuffer = Buffer . from ( EOL ) ;
433- if ( currentLineBuffer . includes ( eolBuffer ) ) {
434- while ( currentLineBuffer . includes ( eolBuffer ) ) {
435- const line = currentLineBuffer . subarray (
436- 0 ,
437- currentLineBuffer . indexOf ( eolBuffer ) ,
438- ) ;
439-
440- void this . logger . log ( line . toString ( "utf-8" ) ) ;
441-
442- currentLineBuffer = currentLineBuffer . subarray (
443- currentLineBuffer . indexOf ( eolBuffer ) + eolBuffer . length ,
444- ) ;
445- }
446435
447- currentLineStderrBuffers = [ currentLineBuffer ] ;
436+ let hasCreatedSubarray = false ;
437+
438+ let eolIndex ;
439+ while (
440+ ( eolIndex = currentLineStderrBuffer . indexOf ( eolBuffer ) ) !== - 1
441+ ) {
442+ const line = currentLineStderrBuffer . subarray ( 0 , eolIndex ) ;
443+ void this . logger . log ( line . toString ( "utf-8" ) ) ;
444+ currentLineStderrBuffer = currentLineStderrBuffer . subarray (
445+ eolIndex + eolBuffer . length ,
446+ ) ;
447+ hasCreatedSubarray = true ;
448+ }
449+
450+ // We have created a subarray, which means that the complete original buffer is now referenced
451+ // by the subarray. We need to create a new buffer to avoid memory leaks.
452+ if ( hasCreatedSubarray ) {
453+ currentLineStderrBuffer = Buffer . from ( currentLineStderrBuffer ) ;
448454 }
449455 }
450456 } ) ;
@@ -461,10 +467,8 @@ export class CodeQLCliServer implements Disposable {
461467 // Make sure we remove the terminator;
462468 const data = fullBuffer . toString ( "utf8" , 0 , fullBuffer . length - 1 ) ;
463469 if ( ! silent ) {
464- void this . logger . log (
465- Buffer . concat ( currentLineStderrBuffers ) . toString ( "utf8" ) ,
466- ) ;
467- currentLineStderrBuffers = [ ] ;
470+ void this . logger . log ( currentLineStderrBuffer . toString ( "utf8" ) ) ;
471+ currentLineStderrBuffer = Buffer . alloc ( 0 ) ;
468472 void this . logger . log ( "CLI command succeeded." ) ;
469473 }
470474 return data ;
@@ -484,10 +488,8 @@ export class CodeQLCliServer implements Disposable {
484488 cliError . stack += getErrorStack ( err ) ;
485489 throw cliError ;
486490 } finally {
487- if ( ! silent && currentLineStderrBuffers . length > 0 ) {
488- void this . logger . log (
489- Buffer . concat ( currentLineStderrBuffers ) . toString ( "utf8" ) ,
490- ) ;
491+ if ( ! silent && currentLineStderrBuffer . length > 0 ) {
492+ void this . logger . log ( currentLineStderrBuffer . toString ( "utf8" ) ) ;
491493 }
492494 // Remove the listeners we set up.
493495 process . stdout . removeAllListeners ( "data" ) ;
0 commit comments