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

Commit

Permalink
Add ScopeMirror traversal to state.js
Browse files Browse the repository at this point in the history
Replace the `FrameMirror.localValue` code path with `ScopeMirror`
traversal and rewrite the arguments collector to only serialize
arguments which are not matched by name with a more deeply-scoped
value.
  • Loading branch information
cristiancavalli committed Jul 25, 2016
1 parent df9276b commit d776780
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 27 deletions.
103 changes: 77 additions & 26 deletions lib/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ module.exports = {
var assert = require('assert');
var semver = require('semver');
var util = require('util');
var lodash = require('lodash');
var find = lodash.find;
var transform = lodash.transform;
var omit = lodash.omit;

var StatusMessage = require('./apiclasses.js').StatusMessage;

Expand Down Expand Up @@ -67,8 +71,8 @@ MESSAGE_TABLE[STRING_LIMIT_MESSAGE_INDEX] =
* @return an object with stackFrames, variableTable, and
* evaluatedExpressions fields
*/
function capture(execState, expressions, config) {
return (new StateResolver(execState, expressions, config)).capture_();
function capture(execState, expressions, config, v8) {
return (new StateResolver(execState, expressions, config, v8)).capture_();
}


Expand Down Expand Up @@ -112,10 +116,11 @@ function evaluate(expression, frame) {
* @param {!Object} config
* @constructor
*/
function StateResolver(execState, expressions, config) {
function StateResolver(execState, expressions, config, v8) {
this.state_ = execState;
this.expressions_ = expressions;
this.config_ = config;
this.ctx_ = v8;

this.evaluatedExpressions_ = [];
this.totalSize_ = 0;
Expand Down Expand Up @@ -277,14 +282,15 @@ StateResolver.prototype.isPathInNodeModulesDirectory_ = function(path) {
};

StateResolver.prototype.resolveFrame_ = function(frame, resolveVars) {
var args = resolveVars ? this.resolveArgumentList_(frame) : [{
name: 'arguments_not_available',
varTableIndex: ARG_LOCAL_LIMIT_MESSAGE_INDEX
}];
var args = this.extractArgumentsList_(frame);
var locals = resolveVars ? this.resolveLocalsList_(frame, args) : [{
name: 'locals_not_available',
varTableIndex: ARG_LOCAL_LIMIT_MESSAGE_INDEX
}];
args = resolveVars && (args.length > 0) ? this.resolveArgumentList_(args) : [{
name: 'arguments_not_available',
varTableIndex: ARG_LOCAL_LIMIT_MESSAGE_INDEX
}];
return {
function: this.resolveFunctionName_(frame.func()),
location: this.resolveLocation_(frame),
Expand All @@ -308,36 +314,81 @@ StateResolver.prototype.resolveLocation_ = function(frame) {
};
};

StateResolver.prototype.resolveArgumentList_ = function(frame) {
StateResolver.prototype.extractArgumentsList_ = function (frame) {
var args = [];
for (var i = 0; i < frame.argumentCount(); i++) {
// Don't resolve unnamed arguments.
if (!frame.argumentName(i)) {
continue;
}
args.push(this.resolveVariable_(
frame.argumentName(i), frame.argumentValue(i)));
args.push({name: frame.argumentName(i), value: frame.argumentValue(i)});
}
return args;
};

StateResolver.prototype.resolveLocalsList_ = function(frame,
resolvedArguments) {
var locals = [];
// Arguments may have been captured as locals in a nested closure.
// We filter them out here.
var predicate = function(localEntry, argEntry) {
return argEntry.varTableIndex === localEntry.varTableIndex;
};
for (var i = 0; i < frame.localCount(); i++) {
var localEntry = this.resolveVariable_(
frame.localName(i), frame.localValue(i));
if (!resolvedArguments.some(predicate.bind(null, localEntry))) {
locals.push(this.resolveVariable_(
frame.localName(i), frame.localValue(i)));
StateResolver.prototype.resolveArgumentList_ = function(args) {
var resolveVariable = this.resolveVariable_.bind(this);
return args.map(function (arg){
return resolveVariable(arg.name, arg.value);
});
};

/**
* Iterates and returns variable information for all scopes (excluding global)
* in a given frame. FrameMirrors should return their scope object list with
* most deeply nested scope first so variables initially encountered will take
* precedence over subsequent instance with the same name - this is tracked in
* the usedNames map. The argument list given to this function may be
* manipulated if variables with a deeper scope occur which have the same name.
* @function resolveLocalsList_
* @memberof StateResolver
* @param {FrameMirror} frame - A instance of FrameMirror
* @param {Array<Object>} args - An array of objects representing any function
* arguments the frame may list
* @returns {Array<Object>} - returns an array containing data about selected
* variables
*/
StateResolver.prototype.resolveLocalsList_ = function (frame, args) {
var usedNames = {};
var makeMirror = this.ctx_.MakeMirror;
// resolveVariable_ accesses instance properties - bind to instance
var resolveVariable = this.resolveVariable_.bind(this);
return frame.allScopes().map(
function (scope) {
if (scope.scopeType() === 0) {
// Ignore global scope
return [];
}
return transform(
scope.details().object(),
function (locals, value, name) {
var trg = makeMirror(value);
var argMatch = find(args, {name: name});
if (argMatch) {
if (argMatch.value.value() === trg.value()) {
// Argument ref is the same ref as the local ref - this is an
// argument do not push this into the locals list
return locals;
}
// There is another local/scope var with the same name and it is not
// the argument so this will take precedence. Remove the same-named
// entry from the arguments list and push the local value onto the
// locals list.
args = omit(args, {name: name});
usedNames[name] = true;
locals.push(resolveVariable(name, trg));
} else if (!usedNames[name]) {
// It's a valid variable that belongs in the locals list and wasn't
// discovered at a lower-scope
usedNames[name] = true;
locals.push(resolveVariable(name, trg));
} // otherwise another same-named variable occured at a lower scope
return locals;
},
[]
);
}
}
return locals;
);
};

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/v8debugapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ module.exports.create = function(logger_, config_, fileStats_) {
breakpoint.evaluatedExpressions = evaluatedExpressions;
}
} else {
var captured = state.capture(execState, breakpoint.expressions, config);
var captured = state.capture(execState, breakpoint.expressions, config, v8);
breakpoint.stackFrames = captured.stackFrames;
breakpoint.variableTable = captured.variableTable;
breakpoint.evaluatedExpressions =
Expand Down

0 comments on commit d776780

Please sign in to comment.