@@ -41,6 +41,7 @@ var GETTER_MESSAGE_INDEX = 2;
4141var ARG_LOCAL_LIMIT_MESSAGE_INDEX = 3 ;
4242var OBJECT_LIMIT_MESSAGE_INDEX = 4 ;
4343var STRING_LIMIT_MESSAGE_INDEX = 5 ;
44+ var DATA_LIMIT_MESSAGE_INDEX = 6 ;
4445
4546var MESSAGE_TABLE = [ ] ;
4647MESSAGE_TABLE [ BUFFER_FULL_MESSAGE_INDEX ] =
@@ -67,6 +68,11 @@ MESSAGE_TABLE[STRING_LIMIT_MESSAGE_INDEX] =
6768 'Only first `config.capture.maxStringLength` chars' +
6869 ' were captured.' ,
6970 false ) } ;
71+ MESSAGE_TABLE [ DATA_LIMIT_MESSAGE_INDEX ] =
72+ { status : new StatusMessage ( StatusMessage . VARIABLE_VALUE ,
73+ 'Truncating the results because the cap of ' +
74+ '`config.capture.maxDataSize` has been reached.' ,
75+ false ) } ;
7076
7177/**
7278 * Captures the stack and current execution state.
@@ -140,11 +146,10 @@ function StateResolver(execState, expressions, config, v8) {
140146 * evaluatedExpressions fields
141147 */
142148StateResolver . prototype . capture_ = function ( ) {
143- // Gather the stack frames first
144149 var that = this ;
145- var frames = that . resolveFrames_ ( ) ;
146150
147151 // Evaluate the watch expressions
152+ var evalIndexSet = new Set ( ) ;
148153 if ( that . expressions_ ) {
149154 that . expressions_ . forEach ( function ( expression , index ) {
150155 var result = evaluate ( expression , that . state_ . frame ( 0 ) ) ;
@@ -157,20 +162,30 @@ StateResolver.prototype.capture_ = function() {
157162 result . error , true )
158163 } ;
159164 } else {
160- evaluated = that . resolveVariable_ ( expression , result . mirror ) ;
165+ evaluated = that . resolveVariable_ ( expression , result . mirror , true ) ;
166+ var varTableIdx = evaluated . varTableIndex ;
167+ if ( typeof varTableIdx !== 'undefined' ) {
168+ evalIndexSet . add ( varTableIdx ) ;
169+ }
161170 }
162171 that . evaluatedExpressions_ [ index ] = evaluated ;
163172 } ) ;
164173 }
165174
175+ // The frames are resolved after the evaluated expressions so that
176+ // evaluated expressions can be evaluated as much as possible within
177+ // the max data size limits
178+ var frames = that . resolveFrames_ ( ) ;
179+
166180 // Now resolve the variables
167181 var index = MESSAGE_TABLE . length ; // skip the sentinel values
168182 var noLimit = that . config_ . capture . maxDataSize === 0 ;
169183 while ( index < that . rawVariableTable_ . length && // NOTE: length changes in loop
170184 ( that . totalSize_ < that . config_ . capture . maxDataSize || noLimit ) ) {
171185 assert ( ! that . resolvedVariableTable_ [ index ] ) ; // shouldn't have it resolved yet
186+ var isEvaluated = evalIndexSet . has ( index ) ;
172187 that . resolvedVariableTable_ [ index ] =
173- that . resolveMirror_ ( that . rawVariableTable_ [ index ] ) ;
188+ that . resolveMirror_ ( that . rawVariableTable_ [ index ] , isEvaluated ) ;
174189 index ++ ;
175190 }
176191
@@ -349,7 +364,7 @@ StateResolver.prototype.extractArgumentsList_ = function (frame) {
349364StateResolver . prototype . resolveArgumentList_ = function ( args ) {
350365 var resolveVariable = this . resolveVariable_ . bind ( this ) ;
351366 return args . map ( function ( arg ) {
352- return resolveVariable ( arg . name , arg . value ) ;
367+ return resolveVariable ( arg . name , arg . value , false ) ;
353368 } ) ;
354369} ;
355370
@@ -413,12 +428,12 @@ StateResolver.prototype.resolveLocalsList_ = function (frame, args) {
413428 // locals list.
414429 remove ( args , { name : name } ) ;
415430 usedNames [ name ] = true ;
416- locals . push ( self . resolveVariable_ ( name , trg ) ) ;
431+ locals . push ( self . resolveVariable_ ( name , trg , false ) ) ;
417432 } else if ( ! usedNames [ name ] ) {
418433 // It's a valid variable that belongs in the locals list and wasn't
419434 // discovered at a lower-scope
420435 usedNames [ name ] = true ;
421- locals . push ( self . resolveVariable_ ( name , trg ) ) ;
436+ locals . push ( self . resolveVariable_ ( name , trg , false ) ) ;
422437 } // otherwise another same-named variable occured at a lower scope
423438 return locals ;
424439 } ,
@@ -432,7 +447,7 @@ StateResolver.prototype.resolveLocalsList_ = function (frame, args) {
432447 // under the name 'context' which is used by the Chrome DevTools.
433448 var ctx = frame . details ( ) . receiver ( ) ;
434449 if ( ctx ) {
435- return [ self . resolveVariable_ ( 'context' , makeMirror ( ctx ) ) ] ;
450+ return [ self . resolveVariable_ ( 'context' , makeMirror ( ctx ) , false ) ] ;
436451 }
437452 return [ ] ;
438453 } ( ) ) ) ;
@@ -445,8 +460,10 @@ StateResolver.prototype.resolveLocalsList_ = function (frame, args) {
445460 *
446461 * @param {String } name The name of the variable.
447462 * @param {Object } value A v8 debugger representation of a variable value.
463+ * @param {boolean } isEvaluated Specifies if the variable is from a watched
464+ * expression.
448465 */
449- StateResolver . prototype . resolveVariable_ = function ( name , value ) {
466+ StateResolver . prototype . resolveVariable_ = function ( name , value , isEvaluated ) {
450467 var size = name . length ;
451468
452469 var data = {
@@ -468,10 +485,11 @@ StateResolver.prototype.resolveVariable_ = function(name, value) {
468485 } else if ( value . isObject ( ) ) {
469486 data . varTableIndex = this . getVariableIndex_ ( value ) ;
470487 var maxProps = this . config_ . capture . maxProperties ;
471- if ( maxProps && maxProps < Object . keys ( value . value ( ) ) . length ) {
488+ var numKeys = Object . keys ( value . value ( ) ) . length ;
489+
490+ if ( ! isEvaluated && maxProps && maxProps < numKeys ) {
472491 data . status = MESSAGE_TABLE [ OBJECT_LIMIT_MESSAGE_INDEX ] . status ;
473492 }
474-
475493 } else {
476494 // PropertyMirror, InternalPropertyMirror, FrameMirror, ScriptMirror
477495 data . value = 'unknown mirror type' ;
@@ -510,53 +528,68 @@ StateResolver.prototype.storeObjectToVariableTable_ = function(obj) {
510528 *
511529 * See https://github.com/iojs/io.js/issues/1190.
512530 */
513- StateResolver . prototype . resolveMirror_ = function ( mirror ) {
531+ StateResolver . prototype . resolveMirror_ = function ( mirror , isEvaluated ) {
514532 if ( semver . satisfies ( process . version , '<1.6' ) ) {
515- return this . resolveMirrorSlow_ ( mirror ) ;
533+ return this . resolveMirrorSlow_ ( mirror , isEvaluated ) ;
516534 } else {
517- return this . resolveMirrorFast_ ( mirror ) ;
535+ return this . resolveMirrorFast_ ( mirror , isEvaluated ) ;
518536 }
519537} ;
520538
521539// A slower implementation of resolveMirror_ which is safe for all node versions
522- StateResolver . prototype . resolveMirrorSlow_ = function ( mirror ) {
540+ StateResolver . prototype . resolveMirrorSlow_ = function ( mirror , isEvaluated ) {
523541 // Instead, let's use Object.keys. This will only get the enumerable
524542 // properties. The other alternative would be Object.getOwnPropertyNames, but
525543 // I'm going with the former as that's what util.inspect does.
526544 var that = this ;
545+
527546 var keys = Object . keys ( mirror . value ( ) ) ;
528- if ( that . config_ . capture . maxProperties ) {
529- keys = keys . slice ( 0 , that . config_ . capture . maxProperties ) ;
547+ var maxProps = that . config_ . capture . maxProperties ;
548+
549+ if ( ! isEvaluated && maxProps ) {
550+ keys = keys . slice ( 0 , maxProps ) ;
530551 }
531552 var members = keys . map ( function ( prop ) {
532- return that . resolveMirrorProperty_ ( mirror . property ( prop ) ) ;
553+ return that . resolveMirrorProperty_ ( mirror . property ( prop ) , isEvaluated ) ;
533554 } ) ;
534555
556+ var mirrorVal = mirror . value ( ) ;
557+ var len = mirrorVal && mirrorVal . length ;
535558 return {
536- value : mirror . toText ( ) ,
559+ value : mirror . toText ( ) +
560+ ( ( typeof len === 'undefined' ) ? '' : ' of length ' + len ) ,
537561 members : members
538562 } ;
539563} ;
540564
541565// A faster implementation of resolveMirror_ which segfaults in node <1.6
542566//
543567// See https://github.com/iojs/io.js/issues/1190.
544- StateResolver . prototype . resolveMirrorFast_ = function ( mirror ) {
545- var members = this . getMirrorProperties_ ( mirror ) . map (
546- this . resolveMirrorProperty_ . bind ( this ) ) ;
568+ StateResolver . prototype . resolveMirrorFast_ = function ( mirror , isEvaluated ) {
569+ var resMirrorProp = this . resolveMirrorProperty_ . bind ( this ) ;
570+ var members = this . getMirrorProperties_ ( mirror , isEvaluated ) . map (
571+ function ( mirror ) {
572+ return resMirrorProp ( mirror , isEvaluated ) ;
573+ }
574+ ) ;
547575 return {
548576 value : mirror . toText ( ) ,
549577 members : members
550578 } ;
551579} ;
552580
553- StateResolver . prototype . getMirrorProperties_ = function ( mirror ) {
554- var numProperties = this . config_ . capture . maxProperties ;
581+ StateResolver . prototype . getMirrorProperties_ = function ( mirror , isEvaluated ) {
582+ var maxProperties = this . config_ . capture . maxProperties ;
555583 var properties = mirror . properties ( ) ;
556- return numProperties ? properties . slice ( 0 , numProperties ) : properties ;
584+
585+ if ( ! isEvaluated && maxProperties ) {
586+ return properties . slice ( 0 , maxProperties ) ;
587+ }
588+
589+ return properties ;
557590} ;
558591
559- StateResolver . prototype . resolveMirrorProperty_ = function ( property ) {
592+ StateResolver . prototype . resolveMirrorProperty_ = function ( property , isEvaluated ) {
560593 var name = String ( property . name ( ) ) ;
561594 // Array length must be special cased as it is a native property that
562595 // we know to be safe to evaluate which is not generally true.
@@ -573,5 +606,5 @@ StateResolver.prototype.resolveMirrorProperty_ = function(property) {
573606 varTableIndex : GETTER_MESSAGE_INDEX
574607 } ;
575608 }
576- return this . resolveVariable_ ( name , property . value ( ) ) ;
609+ return this . resolveVariable_ ( name , property . value ( ) , isEvaluated ) ;
577610} ;
0 commit comments