@@ -18,7 +33,7 @@
diff --git a/debugger/assets/ui/code/code-tab.js b/debugger/assets/ui/code/code-tab.js
index 5e746e5be..08bc3b374 100644
--- a/debugger/assets/ui/code/code-tab.js
+++ b/debugger/assets/ui/code/code-tab.js
@@ -61,6 +61,10 @@ module.controller('CodeTabController', function(
});
};
+ $scope.showLocation = function() {
+ //
+ };
+
if (app.session.dataSource) {
refresh();
}
diff --git a/debugger/assets/ui/code/function-view.js b/debugger/assets/ui/code/function-view.js
index 284066bc2..1b848b388 100644
--- a/debugger/assets/ui/code/function-view.js
+++ b/debugger/assets/ui/code/function-view.js
@@ -16,8 +16,9 @@ var module = angular.module('xe.ui.code.functionView', [
module.controller('FunctionViewController', function(
- $rootScope, $scope, app, log, Breakpoint) {
+ $rootScope, $scope, $location, app, log, Breakpoint) {
$scope.codeType = 'source';
+ $scope.highlightInfo = null;
function refresh() {
if (!app.session || !app.session.dataSource) {
@@ -36,6 +37,46 @@ module.controller('FunctionViewController', function(
$rootScope.$on('refresh', refresh);
$scope.$watch('functionAddress', refresh);
+ function updateHighlight(address) {
+ if (!$scope.sourceLines || $scope.codeType != 'source') {
+ return;
+ }
+ if ($scope.highlightInfo) {
+ if ($scope.highlightInfo.address == address) {
+ return;
+ }
+ var oldLine = $scope.highlightInfo.line;
+ if ($scope.highlightInfo.widget) {
+ $scope.highlightInfo.widget.clear();
+ }
+ $scope.highlightInfo = null;
+ updateLine(oldLine);
+ }
+ // TODO(benvanik): a better mapping.
+ var line = -1;
+ for (var n = 0; n < $scope.sourceLines.length; n++) {
+ var sourceLine = $scope.sourceLines[n];
+ if (sourceLine[0] == 'i' &&
+ sourceLine[1] == address) {
+ line = n;
+ break;
+ }
+ }
+ if (line != -1) {
+ $scope.highlightInfo = {
+ address: address,
+ line: line,
+ widget: null
+ };
+ updateLine(line);
+ }
+ };
+ $scope.$watch(function() {
+ return $location.search();
+ }, function(search) {
+ updateHighlight(parseInt(search.a, 16));
+ });
+
var textArea = document.querySelector('.debugger-fnview-textarea');
$scope.codeMirror = CodeMirror.fromTextArea(textArea, {
mode: 'javascript',
@@ -94,7 +135,7 @@ module.controller('FunctionViewController', function(
el.innerText = hex32(line[2]);
cm.setGutterMarker(n, 'debugger-fnview-gutter-code', el);
- updateLineIcon(n, line);
+ updateLine(n);
}
}
};
@@ -140,7 +181,8 @@ module.controller('FunctionViewController', function(
};
$scope.$watch('codeType', updateCode);
- function updateLineIcon(line, sourceLine) {
+ function updateLine(line) {
+ var sourceLine = $scope.sourceLines[line];
var cm = $scope.codeMirror;
if (sourceLine[0] != 'i') {
return;
@@ -160,6 +202,26 @@ module.controller('FunctionViewController', function(
el = null;
}
cm.setGutterMarker(line, 'debugger-fnview-gutter-icon', el);
+
+ var highlightInfo = $scope.highlightInfo;
+ if (highlightInfo && highlightInfo.line == line) {
+ /*
+ if (!highlightInfo.widget) {
+ el = document.createElement('div');
+ el.style.width = '100%';
+ el.style.height = '20px';
+ el.style.backgroundColor = 'red';
+ el.innerHTML = 'hi!';
+ highlightInfo.widget = cm.addLineWidget(line, el, {
+ coverGutter: false
+ });
+ cm.scrollIntoView(line, 50);
+ }
+ */
+ cm.addLineClass(line, 'background', 'debugger-fnview-line-highlight-bg');
+ } else {
+ cm.removeLineClass(line, 'background');
+ }
};
function toggleBreakpoint(line, sourceLine, shiftKey) {
@@ -174,10 +236,11 @@ module.controller('FunctionViewController', function(
}
} else {
// New breakpoint needed.
- breakpoint = app.session.addCodeBreakpoint(address);
+ breakpoint = app.session.addCodeBreakpoint(
+ $scope.functionAddress, address);
}
- updateLineIcon(line, sourceLine);
+ updateLine(line);
};
$scope.codeMirror.on('gutterClick', function(
diff --git a/debugger/src/datasources.js b/debugger/src/datasources.js
index 3119f0514..79296ee90 100644
--- a/debugger/src/datasources.js
+++ b/debugger/src/datasources.js
@@ -25,6 +25,7 @@ module.service('Breakpoint', function() {
var Breakpoint = function(opt_id) {
this.id = opt_id || uuid4();
this.type = Breakpoint.Type.TEMP;
+ this.fnAddress = 0;
this.address = 0;
this.enabled = true;
};
@@ -35,6 +36,7 @@ module.service('Breakpoint', function() {
Breakpoint.fromJSON = function(json) {
var breakpoint = new Breakpoint(json.id);
breakpoint.type = json.type;
+ breakpoint.fnAddress = json.fnAddress;
breakpoint.address = json.address;
breakpoint.enabled = json.enabled;
return breakpoint;
@@ -43,6 +45,7 @@ module.service('Breakpoint', function() {
return {
'id': this.id,
'type': this.type,
+ 'fnAddress': this.fnAddress,
'address': this.address,
'enabled': this.enabled
};
@@ -52,8 +55,9 @@ module.service('Breakpoint', function() {
module.service('DataSource', function($q) {
- var DataSource = function(source) {
+ var DataSource = function(source, delegate) {
this.source = source;
+ this.delegate = delegate;
this.online = false;
this.status = 'disconnected';
};
@@ -129,12 +133,31 @@ module.service('DataSource', function($q) {
});
};
+ DataSource.prototype.continueExecution = function() {
+ return this.issue({
+ command: 'cpu.continue'
+ });
+ };
+
+ DataSource.prototype.breakExecution = function() {
+ return this.issue({
+ command: 'cpu.break'
+ });
+ };
+
+ DataSource.prototype.stepNext = function() {
+ return this.issue({
+ command: 'cpu.step'
+ });
+ };
+
return DataSource;
});
-module.service('RemoteDataSource', function($q, log, DataSource) {
- var RemoteDataSource = function(url) {
- DataSource.call(this, url);
+module.service('RemoteDataSource', function(
+ $rootScope, $q, log, DataSource) {
+ var RemoteDataSource = function(url, delegate) {
+ DataSource.call(this, url, delegate);
this.url = url;
this.socket = null;
this.nextRequestId_ = 1;
@@ -152,22 +175,26 @@ module.service('RemoteDataSource', function($q, log, DataSource) {
this.socket = new WebSocket(url, []);
this.socket.onopen = (function() {
- // TODO(benvanik): handshake
+ $rootScope.$apply((function() {
+ // TODO(benvanik): handshake
- this.online = true;
- this.status = 'connected';
- d.resolve();
+ this.online = true;
+ this.status = 'connected';
+ d.resolve();
+ }).bind(this));
}).bind(this);
this.socket.onclose = (function(e) {
- this.online = false;
- if (this.status == 'connecting') {
- this.status = 'disconnected';
- d.reject(e.code + ' ' + e.reason);
- } else {
- this.status = 'disconnected';
- log.info('Disconnected');
- }
+ $rootScope.$apply((function() {
+ this.online = false;
+ if (this.status == 'connecting') {
+ this.status = 'disconnected';
+ d.reject(e.code + ' ' + e.reason);
+ } else {
+ this.status = 'disconnected';
+ log.info('Disconnected');
+ }
+ }).bind(this));
}).bind(this);
this.socket.onerror = (function(e) {
@@ -175,27 +202,42 @@ module.service('RemoteDataSource', function($q, log, DataSource) {
}).bind(this);
this.socket.onmessage = (function(e) {
- console.log('message', e.data);
- var json = JSON.parse(e.data);
- if (json.requestId) {
- // Response to a previous request.
- var request = this.pendingRequests_[json.requestId];
- if (request) {
- delete this.pendingRequests_[json.requestId];
- if (json.status) {
- request.deferred.resolve(json.result);
- } else {
- request.deferred.reject(json.result);
- }
- }
- } else {
- // Notification.
- }
+ $rootScope.$apply((function() {
+ this.socketMessage(e);
+ }).bind(this));
}).bind(this);
return d.promise;
};
+ RemoteDataSource.prototype.socketMessage = function(e) {
+ console.log('message', e.data);
+ var json = JSON.parse(e.data);
+ if (json.requestId) {
+ // Response to a previous request.
+ var request = this.pendingRequests_[json.requestId];
+ if (request) {
+ delete this.pendingRequests_[json.requestId];
+ if (json.status) {
+ request.deferred.resolve(json.result);
+ } else {
+ request.deferred.reject(json.result);
+ }
+ }
+ } else {
+ // Notification.
+ switch (json.type) {
+ case 'breakpoint':
+ this.delegate.onBreakpointHit(
+ json.breakpointId, json.threadId);
+ break;
+ default:
+ log.error('Unknown notification type: ' + json.type);
+ break;
+ }
+ }
+ };
+
RemoteDataSource.prototype.dispose = function() {
this.pendingRequests_ = {};
this.online = false;
@@ -221,8 +263,8 @@ module.service('RemoteDataSource', function($q, log, DataSource) {
module.service('FileDataSource', function($q, DataSource) {
- var FileDataSource = function(file) {
- DataSource.call(this, file.name);
+ var FileDataSource = function(file, delegate) {
+ DataSource.call(this, file.name, delegate);
this.file = file;
};
inherits(FileDataSource, DataSource);
diff --git a/debugger/src/router.js b/debugger/src/router.js
index c47405504..e5427ab3b 100644
--- a/debugger/src/router.js
+++ b/debugger/src/router.js
@@ -98,13 +98,11 @@ module.config(function($stateProvider, $urlRouterProvider) {
onExit: function() {}
});
$stateProvider.state('session.code.function', {
- url: '/:module/:function',
+ url: '/:function?a',
templateUrl: 'assets/ui/code/function-view.html',
controller: function($scope, $stateParams) {
- $scope.moduleName = $stateParams.module;
$scope.functionAddress = parseInt($stateParams.function, 16);
- $scope.$emit('xxx');
- $scope.$broadcast('yyy');
+ $scope.highlightAddress = parseInt($stateParams.a, 16);
},
onEnter: function() {},
onExit: function() {}
diff --git a/debugger/src/session.js b/debugger/src/session.js
index ed92fdf00..3a73c56b2 100644
--- a/debugger/src/session.js
+++ b/debugger/src/session.js
@@ -13,15 +13,18 @@ var module = angular.module('xe.session', []);
module.service('Session', function(
- $rootScope, $q, $http, log,
+ $rootScope, $q, $http, $state, log,
Breakpoint, FileDataSource, RemoteDataSource) {
var Session = function(id, opt_dataSource) {
this.id = id;
this.breakpoints = {};
+ this.breakpointsById = {};
this.dataSource = opt_dataSource || null;
+ this.paused = false;
+
this.loadState();
};
@@ -31,7 +34,11 @@ module.service('Session', function(
};
Session.prototype.loadState = function() {
- var json = JSON.parse(window.localStorage[this.id]);
+ var raw = window.localStorage[this.id];
+ if (!raw) {
+ return;
+ }
+ var json = JSON.parse(raw);
if (!json) {
return;
}
@@ -40,8 +47,9 @@ module.service('Session', function(
this.breakpoints = {};
for (var n = 0; n < breakpointList.length; n++) {
var breakpointJson = breakpointList[n];
- this.breakpoints[breakpointJson.address] =
- Breakpoint.fromJSON(breakpointJson);
+ var breakpoint = Breakpoint.fromJSON(breakpointJson);
+ this.breakpoints[breakpointJson.address] = breakpoint;
+ this.breakpointsById[breakpoint.id] = breakpoint;
}
};
@@ -50,8 +58,8 @@ module.service('Session', function(
id: this.id,
breakpoints: []
};
- for (var key in this.breakpoints) {
- var breakpoint = this.breakpoints[key];
+ for (var key in this.breakpointsById) {
+ var breakpoint = this.breakpointsById[key];
if (breakpoint.type != Breakpoint.TEMP) {
json.breakpoints.push(breakpoint.toJSON());
}
@@ -97,7 +105,7 @@ module.service('Session', function(
var d = $q.defer();
- var dataSource = new RemoteDataSource(url);
+ var dataSource = new RemoteDataSource(url, this);
var p = dataSource.open();
p.then((function() {
log.info('Connected!');
@@ -167,6 +175,7 @@ module.service('Session', function(
Session.prototype.addBreakpoint = function(breakpoint) {
this.breakpoints[breakpoint.address] = breakpoint;
+ this.breakpointsById[breakpoint.id] = breakpoint;
if (this.dataSource) {
this.dataSource.addBreakpoint(breakpoint);
}
@@ -174,17 +183,19 @@ module.service('Session', function(
return breakpoint;
};
- Session.prototype.addTempBreakpoint = function(address) {
+ Session.prototype.addTempBreakpoint = function(fnAddress, address) {
var breakpoint = new Breakpoint();
breakpoint.type = Breakpoint.Type.TEMP;
+ breakpoint.fnAddress = fnAddress;
breakpoint.address = address;
breakpoint.enabled = true;
return this.addBreakpoint(breakpoint);
};
- Session.prototype.addCodeBreakpoint = function(address) {
+ Session.prototype.addCodeBreakpoint = function(fnAddress, address) {
var breakpoint = new Breakpoint();
breakpoint.type = Breakpoint.Type.CODE;
+ breakpoint.fnAddress = fnAddress;
breakpoint.address = address;
breakpoint.enabled = true;
return this.addBreakpoint(breakpoint);
@@ -192,6 +203,7 @@ module.service('Session', function(
Session.prototype.removeBreakpoint = function(breakpoint) {
delete this.breakpoints[breakpoint.address];
+ delete this.breakpointsById[breakpoint.id];
if (this.dataSource) {
this.dataSource.removeBreakpoint(breakpoint.id);
}
@@ -211,5 +223,60 @@ module.service('Session', function(
this.saveState();
};
+ Session.prototype.onBreakpointHit = function(breakpointId, threadId) {
+ var breakpoint = this.breakpointsById[breakpointId];
+ var thread = null; // TODO
+ if (!breakpoint) {
+ log.error('Breakpoint hit but not found');
+ return;
+ }
+
+ // Now paused!
+ this.paused = true;
+
+ $state.go('session.code.function', {
+ 'function': breakpoint.fnAddress.toString(16).toUpperCase(),
+ 'a': breakpoint.address.toString(16).toUpperCase()
+ }, {
+ notify: false,
+ reloadOnSearch: false
+ });
+
+ //
+ log.info('breakpoint!!');
+ };
+
+ Session.prototype.continueExecution = function() {
+ if (!this.dataSource) {
+ return;
+ }
+ this.paused = false;
+ this.dataSource.continueExecution().then(function() {
+ }, function(e) {
+ log.error('Unable to continue: ' + e);
+ });
+ };
+
+ Session.prototype.breakExecution = function() {
+ if (!this.dataSource) {
+ return;
+ }
+ this.dataSource.breakExecution().then(function() {
+ }, function(e) {
+ log.error('Unable to break: ' + e);
+ });
+ };
+
+ Session.prototype.stepNext = function() {
+ if (!this.dataSource) {
+ return;
+ }
+ this.paused = false;
+ this.dataSource.breakExecution().then(function() {
+ }, function(e) {
+ log.error('Unable to step: ' + e);
+ });
+ };
+
return Session;
});