diff --git a/config.js b/config.js index 682ea96b..204f09a3 100644 --- a/config.js +++ b/config.js @@ -53,7 +53,7 @@ module.exports = { // To reduce the overall capture time, limit the number of properties // gathered on large object. A value of 0 disables the limit. - maxProperties: 0, + maxProperties: 10, // Total 'size' of data to gather. This is NOT the number of bytes of data // that are sent over the wire, but instead a very very coarse approximation @@ -65,7 +65,7 @@ module.exports = { // To limit the size of the buffer, we truncate long strings. // A value of 0 disables truncation. - maxStringLength: 0 + maxStringLength: 100 }, // These configuration options are for internal experimentation only. diff --git a/lib/state.js b/lib/state.js index c762595e..ab17e547 100644 --- a/lib/state.js +++ b/lib/state.js @@ -32,6 +32,8 @@ var BUFFER_FULL_MESSAGE_INDEX = 0; var NATIVE_PROPERTY_MESSAGE_INDEX = 1; var GETTER_MESSAGE_INDEX = 2; var ARG_LOCAL_LIMIT_MESSAGE_INDEX = 3; +var OBJECT_LIMIT_MESSAGE_INDEX = 4; +var STRING_LIMIT_MESSAGE_INDEX = 5; var MESSAGE_TABLE = []; MESSAGE_TABLE[BUFFER_FULL_MESSAGE_INDEX] = @@ -48,6 +50,16 @@ MESSAGE_TABLE[ARG_LOCAL_LIMIT_MESSAGE_INDEX] = 'Locals and arguments are only displayed for the ' + 'top `config.capture.maxExpandFrames` stack frames.', true) }; +MESSAGE_TABLE[OBJECT_LIMIT_MESSAGE_INDEX] = + { status: new StatusMessage(StatusMessage.VARIABLE_VALUE, + 'Only first `config.capture.maxProperties` elements' + + ' were captured.', + false) }; +MESSAGE_TABLE[STRING_LIMIT_MESSAGE_INDEX] = + { status: new StatusMessage(StatusMessage.VARIABLE_VALUE, + 'Only first `config.capture.maxStringLength` chars' + + ' were captured.', + false) }; /** * Captures the stack and current execution state. @@ -347,8 +359,9 @@ StateResolver.prototype.resolveVariable_ = function(name, value) { // primitives: undefined, null, boolean, number, string, symbol data.value = value.toText(); var maxLength = this.config_.capture.maxStringLength; - if (maxLength) { + if (maxLength && maxLength < data.value.length) { data.value = data.value.substring(0, maxLength) + '...'; + data.status = MESSAGE_TABLE[STRING_LIMIT_MESSAGE_INDEX]; } } else if (value.isFunction()) { @@ -356,6 +369,10 @@ StateResolver.prototype.resolveVariable_ = function(name, value) { } else if (value.isObject()) { data.varTableIndex = this.getVariableIndex_(value); + var maxProps = this.config_.capture.maxProperties; + if (maxProps && maxProps < Object.keys(value.value()).length) { + data.status = MESSAGE_TABLE[OBJECT_LIMIT_MESSAGE_INDEX]; + } } else { // PropertyMirror, InternalPropertyMirror, FrameMirror, ScriptMirror @@ -437,9 +454,8 @@ StateResolver.prototype.resolveMirrorFast_ = function(mirror) { StateResolver.prototype.getMirrorProperties_ = function(mirror) { var numProperties = this.config_.capture.maxProperties; - var namedProperties = mirror.properties(1, numProperties); - var indexedProperties = mirror.properties(2, numProperties); - return namedProperties.concat(indexedProperties); + var properties = mirror.properties(); + return numProperties ? properties.slice(0, numProperties) : properties; }; StateResolver.prototype.resolveMirrorProperty_ = function(property) { diff --git a/test/test-v8debugapi.js b/test/test-v8debugapi.js index 9208f93c..0967715d 100644 --- a/test/test-v8debugapi.js +++ b/test/test-v8debugapi.js @@ -1,12 +1,13 @@ /*1* KEEP THIS CODE AT THE TOP TO AVOID LINE NUMBER CHANGES */ /*2*/'use strict'; /*3*/function foo(n) { -/*4*/ var A = new Array(3); return n+42+A[0]; -/*5*/} -/*6*/function getterObject() { -/*7*/ var hasGetter = { _a: 5, get a() { return this._a; }, b: 'hello world' }; -/*8*/ return hasGetter.a; -/*9*/} +/*4*/ var A = [1, 2, 3]; var B = { a: 5, b: 6, c: 7 }; +/*5*/ return n+42+A[0]+B.b; +/*6*/} +/*7*/function getterObject() { +/*8*/ var hasGetter = { _a: 5, get a() { return this._a; }, b: 'hello world' }; +/*9*/ return hasGetter.a; +/*10*/} /** * Copyright 2015 Google Inc. All Rights Reserved. * @@ -449,6 +450,8 @@ describe('v8debugapi', function() { location: breakpointInFoo.location, expressions: ['process'] }; + var oldMax = config.capture.maxProperties; + config.capture.maxProperties = 0; api.set(bp, function(err) { assert.ifError(err); api.wait(bp, function(err) { @@ -477,6 +480,7 @@ describe('v8debugapi', function() { })); api.clear(bp); + config.capture.maxProperties = oldMax; done(); }); process.nextTick(function() {foo(3);}); @@ -486,7 +490,7 @@ describe('v8debugapi', function() { it('should report error for native prop or getter', function(done) { var bp = { id: 'fake-id-124', - location: { path: 'test-v8debugapi.js', line: 8 }, + location: { path: 'test-v8debugapi.js', line: 9 }, expressions: ['process.env', 'hasGetter'] }; api.set(bp, function(err) { @@ -520,7 +524,7 @@ describe('v8debugapi', function() { it('should limit string length', function(done) { var bp = { id: 'fake-id-124', - location: { path: 'test-v8debugapi.js', line: 8 }, + location: { path: 'test-v8debugapi.js', line: 9 }, expressions: ['hasGetter'] }; var oldMax = config.capture.maxStringLength; @@ -543,6 +547,60 @@ describe('v8debugapi', function() { }); }); + it('should limit array length', function(done) { + var bp = { + id: 'fake-id-124', + location: { path: 'test-v8debugapi.js', line: 5 }, + expressions: ['A'] + }; + var oldMax = config.capture.maxProperties; + config.capture.maxProperties = 1; + api.set(bp, function(err) { + assert.ifError(err); + api.wait(bp, function(err) { + assert.ifError(err); + var foo = bp.evaluatedExpressions[0]; + var fooVal = bp.variableTable[foo.varTableIndex]; + assert.equal(fooVal.members.length, 1); + assert(foo.status.status.description.format.indexOf( + 'Only first') !== -1); + assert(!foo.status.isError); + + api.clear(bp); + config.capture.maxProperties = oldMax; + done(); + }); + process.nextTick(function() {foo(2);}); + }); + }); + + it('should limit object length', function(done) { + var bp = { + id: 'fake-id-124', + location: { path: 'test-v8debugapi.js', line: 5 }, + expressions: ['B'] + }; + var oldMax = config.capture.maxProperties; + config.capture.maxProperties = 1; + api.set(bp, function(err) { + assert.ifError(err); + api.wait(bp, function(err) { + assert.ifError(err); + var foo = bp.evaluatedExpressions[0]; + var fooVal = bp.variableTable[foo.varTableIndex]; + assert.equal(fooVal.members.length, 1); + assert(foo.status.status.description.format.indexOf( + 'Only first') !== -1); + assert(!foo.status.isError); + + api.clear(bp); + config.capture.maxProperties = oldMax; + done(); + }); + process.nextTick(function() {foo(2);}); + }); + }); + it('should capture without values for invalid watch expressions', function(done) { // clone a clean breakpointInFoo var bp = {