Update list of scripts on afterCompile event.

Added afterCompile listener that registers
newly parsed/loaded script. Includes

- refactoring of script registration
  (extracted class ScriptManager).

- fix of script path shortening, where windows paths
  containing '\' instead of '/' were not shortened
  at all.
This commit is contained in:
Miroslav Bajtoš 2013-04-30 10:36:50 +02:00 коммит произвёл Miroslav Bajtos
Родитель 87c71616ff
Коммит de14ca3951
3 изменённых файлов: 133 добавлений и 43 удалений

91
lib/ScriptManager.js Normal file
Просмотреть файл

@ -0,0 +1,91 @@
var events = require('events');
/**
* @param hiddenScripts List of scripts that are not displayed to the user.
* @returns {ScriptManager}
* @constructor
*/
function ScriptManager(hiddenScripts) {
hiddenScripts = hiddenScripts || [];
return Object.create(ScriptManager.prototype, {
_sources: { value: {} },
_hiddenScripts: { value: hiddenScripts }
});
}
ScriptManager.prototype = Object.create(events.EventEmitter.prototype, {
_isHidden: {
value: function(scriptUrl) {
return this._hiddenScripts.some(function fnHiddenScriptMatchesUrl(r) {
return r.test(scriptUrl);
});
}
},
_listAllSources: {
value: function() {
var self = this;
return Object.keys(this._sources).map(function fnSelectValue(key) {
return self._sources[key];
});
}
},
findScriptByID: {
/**
* @param id script id
* @returns {{hidden: boolean, path: string, url: string}}
*/
value: function(id) {
return this._sources[id];
}
},
_shortenPath: {
value: function(pathArray) {
var p;
var scripts = this._listAllSources();
for (var i = pathArray.length - 1; i > 0; i--) {
p = pathArray.slice(i).join('/');
if (!scripts.some(function fnScriptUrlMatchesP(s) {
return s.url === p;
})) {
return p;
}
}
return pathArray.join('/');
}
},
addScript: {
value: function(v8data) {
var script = {
sourceID: String(v8data.id),
url: v8data.name,
data: v8data.source,
firstLine: v8data.lineOffset,
scriptWorldType: 0,
},
path = String(v8data.name).split(/[\/\\]/);
var hidden = this._isHidden(script.url),
item = {
hidden: hidden,
path: script.url,
url: script.url,
};
if (path.length > 1) {
item.url = script.url = this._shortenPath(path);
}
this._sources[script.sourceID] = item;
if (!hidden) {
this.emit('scriptLoaded', script);
}
}
}
});
exports.ScriptManager = ScriptManager;

Просмотреть файл

@ -1,5 +1,6 @@
var events = require('events'),
debugr = require('./debugger'),
ScriptManager = require('./ScriptManager').ScriptManager;
Breakpoint = require('./Breakpoint').Breakpoint;
///////////////////////////////////////////////////////////
@ -11,8 +12,7 @@ exports.create = function(debuggerPort, config) {
attachedToDebugger = false,
//map from sourceID:lineNumber to breakpoint
breakpoints = {},
//map from sourceID to filename
sourceIDs = {},
scriptManager = new ScriptManager(config.hidden),
//milliseconds to wait for a lookup
LOOKUP_TIMEOUT = 2500,
//node function wrapper
@ -20,6 +20,10 @@ exports.create = function(debuggerPort, config) {
//
cpuProfileCount = 0;
scriptManager.on('scriptLoaded', function onScriptLoaded(script) {
sendEvent('parsedScriptSource', script);
});
function wrapperObject(type, description, hasChildren, frame, scope, ref) {
return {
type: type,
@ -147,7 +151,7 @@ exports.create = function(debuggerPort, config) {
function breakEvent(obj) {
var data = {},
source = sourceIDs[obj.body.script.id],
source = scriptManager.findScriptByID(obj.body.script.id),
args;
if (!source) {
args = {
@ -165,43 +169,14 @@ exports.create = function(debuggerPort, config) {
sendBacktrace();
}
function onAfterCompile(event) {
if (!event.success) return;
scriptManager.addScript(event.body.script);
}
function parsedScripts(msg) {
var scripts = msg.body.map(function(s) {
return {
sourceID: String(s.id),
url: s.name,
data: s.source,
firstLine: s.lineOffset,
scriptWorldType: 0,
path: String(s.name).split('/')
};
});
scripts.sort(function(a, b) {
return a.path.length - b.path.length;
});
var paths = [];
function shorten(s) {
var p;
for (var i = s.length - 1; i > 0; i--) {
p = s.slice(i).join('/');
if (paths.indexOf(p) === -1) {
paths.push(p);
return p;
}
}
return s.join('/');
}
scripts.forEach(function(s) {
var hidden = config.hidden &&
config.hidden.some(function(r) { return r.test(s.url); }),
item = { hidden: hidden, path: s.url };
if (s.path.length > 1) s.url = shorten(s.path);
item.url = s.url;
sourceIDs[s.sourceID] = item;
delete s.path;
if (!hidden) {
sendEvent('parsedScriptSource', s);
}
msg.body.forEach(function(s) {
scriptManager.addScript(s);
});
}
@ -263,7 +238,7 @@ exports.create = function(debuggerPort, config) {
if (bp.type === 'scriptId') {
data.sourceID = bp.script_id;
data.url = sourceIDs[bp.script_id].url;
data.url = scriptManager.findScriptByID(bp.script_id).url;
} else if (bp.type == 'scriptName') {
data.url = bp.script_name;
if (bp.actual_locations && bp.actual_locations.length > 0)
@ -331,6 +306,7 @@ exports.create = function(debuggerPort, config) {
var self = this;
debug = debugr.attachDebugger(debuggerPort);
debug.on('break', breakEvent);
debug.on('afterCompile', onAfterCompile)
debug.on('close', function() {
//TODO determine proper close behavior
debug = {
@ -626,7 +602,7 @@ exports.create = function(debuggerPort, config) {
var b = msg.body;
var breakpoint = new Breakpoint({
sourceID: b.script_id,
url: sourceIDs[b.script_id].url,
url: scriptManager.findScriptByID(b.script_id).url,
line: b.line + 1,
enabled: enabled,
condition: condition,
@ -799,9 +775,10 @@ exports.create = function(debuggerPort, config) {
var fs = require('fs'),
match = FUNC_WRAP.exec(newContent),
newSource;
if (match && sourceIDs[sourceID] && sourceIDs[sourceID].path) {
var source = scriptManager.findScriptByID(sourceID);
if (match && source && source.path) {
newSource = match[1];
fs.writeFile(sourceIDs[sourceID].path, newSource, function(e) {
fs.writeFile(source.path, newSource, function(e) {
if (e) {
var err = e.toString(),
data = {

22
test/ScriptManager.js Normal file
Просмотреть файл

@ -0,0 +1,22 @@
var expect = require('chai').expect,
ScriptManager = require('../lib/ScriptManager').ScriptManager;
describe('ScriptManager', function() {
describe('findSourceByID()', function() {
var manager;
beforeEach(function() {
manager = new ScriptManager();
});
it('returns stored source', function() {
manager._sources['id'] = 'a-source';
expect(manager.findScriptByID('id')).to.equal('a-source');
});
it('returns undefined for unknown id', function() {
expect(manager.findScriptByID('unknown-id')).to.be.undefined;
});
});
});