Skip to content
This repository was archived by the owner on Apr 3, 2024. It is now read-only.

Commit d370e20

Browse files
Prioritize capturing expressions (#162)
Now the collection of the contents of watched expressions is given priority over other variables such as locals. Further, the contents of watched expressions are shown completely provided that this can be done within the limits of `config.capture.maxDataSize`. Otherwise, a message is displayed describing why the entire contents of the watched expression cannot be displayed. For example, if a breakpoint is hit with a watched expression that is an array (even if that array contains more than `config.capture.maxProperties` elements), the entire contents of the array will be collected provided that the limit `config.capture.maxDataSize` is not reached. Then, other variables such as locals will be collected (provided that there is enough space to collect that information as specified by `config.capture.maxDataSize`).
1 parent 398d04a commit d370e20

File tree

3 files changed

+191
-76
lines changed

3 files changed

+191
-76
lines changed

lib/state.js

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var GETTER_MESSAGE_INDEX = 2;
4141
var ARG_LOCAL_LIMIT_MESSAGE_INDEX = 3;
4242
var OBJECT_LIMIT_MESSAGE_INDEX = 4;
4343
var STRING_LIMIT_MESSAGE_INDEX = 5;
44+
var DATA_LIMIT_MESSAGE_INDEX = 6;
4445

4546
var MESSAGE_TABLE = [];
4647
MESSAGE_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
*/
142148
StateResolver.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) {
349364
StateResolver.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
};

test/standalone/test-try-catch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ describe('v8debugapi', function() {
9191
} else {
9292
assert.deepEqual(
9393
locals[0],
94-
{name: 'e', varTableIndex: 6}
94+
{name: 'e', varTableIndex: 7}
9595
);
9696
}
9797

0 commit comments

Comments
 (0)