Version 1.9.0-dev.10.8
svn merge -c 44268 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44269 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44353 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44355 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44382 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44417 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44419 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44440 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44443 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44459 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44461 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44465 https://dart.googlecode.com/svn/branches/bleeding_edge trunk (mc resolve conflict)
svn merge -c 44466 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44480 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44499 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44505 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44520 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44523 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44525 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44526 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44528 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
Merge cl from:
https://codereview.chromium.org/1010753002
git-svn-id: http://dart.googlecode.com/svn/trunk@44530 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/compiler/lib/src/js/rewrite_async.dart b/pkg/compiler/lib/src/js/rewrite_async.dart
index 0f1f433..8e900c0 100644
--- a/pkg/compiler/lib/src/js/rewrite_async.dart
+++ b/pkg/compiler/lib/src/js/rewrite_async.dart
@@ -996,18 +996,10 @@
addStatement(node);
}
- void visitExpressionInStatementContext(js.Expression node) {
- if (node is js.VariableDeclarationList) {
- // Treat js.VariableDeclarationList as a statement.
- visitVariableDeclarationList(node);
- } else {
- visitExpressionIgnoreResult(node);
- }
- }
@override
void visitExpressionStatement(js.ExpressionStatement node) {
- visitExpressionInStatementContext(node.expression);
+ visitExpressionIgnoreResult(node.expression);
}
@override
@@ -1030,7 +1022,7 @@
}
if (node.init != null) {
- visitExpressionInStatementContext(node.init);
+ addExpressionStatement(visitExpression(node.init));
}
int startLabel = newLabel("for condition");
// If there is no update, continuing the loop is the same as going to the
@@ -1154,12 +1146,12 @@
new js.LabeledStatement(node.label, translateInBlock(node.body)));
return;
}
+ // `continue label` is really continuing the nested loop.
+ // This is set up in [PreTranslationAnalysis.visitContinue].
+ // Here we only need a breakLabel:
int breakLabel = newLabel("break ${node.label}");
- int continueLabel = newLabel("continue ${node.label}");
breakLabels[node] = breakLabel;
- continueLabels[node] = continueLabel;
- beginLabel(continueLabel);
jumpTargets.add(node);
visitStatement(node.body);
jumpTargets.removeLast();
@@ -1535,7 +1527,9 @@
}
@override
- void visitVariableDeclarationList(js.VariableDeclarationList node) {
+ js.Expression visitVariableDeclarationList(js.VariableDeclarationList node) {
+ List<js.Assignment> initializations = new List<js.Assignment>();
+
// Declaration of local variables is hoisted outside the helper but the
// initialization is done here.
for (js.VariableInitialization initialization in node.declarations) {
@@ -1543,11 +1537,20 @@
localVariables.add(declaration);
if (initialization.value != null) {
withExpression(initialization.value, (js.Expression value) {
- addStatement(new js.ExpressionStatement(
- new js.Assignment(new js.VariableUse(declaration.name), value)));
+ initializations.add(
+ new js.Assignment(new js.VariableUse(declaration.name), value));
}, store: false);
}
}
+ if (initializations.isEmpty) {
+ // Dummy expression. Will be dropped by [visitExpressionIgnoreResult].
+ return js.number(0);
+ } else {
+ return initializations.reduce(
+ (js.Expression first, js.Expression second) {
+ return new js.Binary(",", first, second);
+ });
+ }
}
@override
@@ -2251,8 +2254,10 @@
@override
bool visitContinue(js.Continue node) {
if (node.targetLabel != null) {
- targets[node] = labelledStatements.lastWhere(
+ js.LabeledStatement targetLabel = labelledStatements.lastWhere(
(js.LabeledStatement stm) => stm.label == node.targetLabel);
+ js.Loop targetStatement = targetLabel.body;
+ targets[node] = targetStatement;
} else {
targets[node] =
loopsAndSwitches.lastWhere((js.Node node) => node is! js.Switch);
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 2db55a0..93b0896 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -6034,6 +6034,7 @@
// body-block will be used, but for loops we will add (unnecessary) phis
// that will reference the body variables. This makes it look as if the
// variables were used in a non-dominated block.
+ LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
HBasicBlock enterBlock = openNewBlock();
HTry tryInstruction = new HTry();
close(tryInstruction);
@@ -6056,6 +6057,7 @@
SubGraph finallyGraph = null;
+ localsHandler = new LocalsHandler.from(savedLocals);
startFinallyBlock = graph.addNewBlock();
open(startFinallyBlock);
buildFinally();
@@ -6099,6 +6101,11 @@
// block and the finally block.
addExitTrySuccessor(startFinallyBlock);
+ // Use the locals handler not altered by the catch and finally
+ // blocks.
+ // TODO(sigurdm): We can probably do this, because try-variables are boxed.
+ // Need to verify.
+ localsHandler = savedLocals;
open(exitBlock);
enterBlock.setBlockFlow(
new HTryBlockInformation(
diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart
index d8c586c..27d751a 100644
--- a/runtime/bin/builtin.dart
+++ b/runtime/bin/builtin.dart
@@ -350,7 +350,7 @@
_finishedOneLoadRequest(uri);
}
-void _asyncLoadError(tag, uri, libraryUri, error) {
+void _asyncLoadError(int tag, String uri, String libraryUri, LoadError error) {
if (_logBuiltin) {
_print("_asyncLoadError($uri), error: $error");
}
@@ -359,7 +359,7 @@
// uri.
libraryUri = uri;
}
- _asyncLoadErrorCallback(uri, libraryUri, new LoadError(error.toString()));
+ _asyncLoadErrorCallback(uri, libraryUri, error);
_finishedOneLoadRequest(uri);
}
@@ -373,10 +373,15 @@
if (dataOrError is List<int>) {
_loadScript(tag, uri, libraryUri, dataOrError);
} else {
- _asyncLoadError(tag, uri, libraryUri, dataOrError);
+ assert(dataOrError is String);
+ var error = new LoadError(dataOrError.toString());
+ _asyncLoadError(tag, uri, libraryUri, error);
}
}).catchError((e) {
- _asyncLoadError(tag, uri, libraryUri, e.toString());
+ // Wrap inside a LoadError unless we are already propagating a previously
+ // seen LoadError.
+ var error = (e is LoadError) ? e : new LoadError(e.toString);
+ _asyncLoadError(tag, uri, libraryUri, error);
});
try {
@@ -387,7 +392,10 @@
if (_logBuiltin) {
_print("Exception when communicating with service isolate: $e");
}
- _asyncLoadError(tag, uri, libraryUri, e.toString());
+ // Wrap inside a LoadError unless we are already propagating a previously
+ // seen LoadError.
+ var error = (e is LoadError) ? e : new LoadError(e.toString);
+ _asyncLoadError(tag, uri, libraryUri, error);
receivePort.close();
}
}
diff --git a/runtime/bin/dbg_message.cc b/runtime/bin/dbg_message.cc
index 91558bf..1d8c22c 100644
--- a/runtime/bin/dbg_message.cc
+++ b/runtime/bin/dbg_message.cc
@@ -1098,10 +1098,6 @@
// request.
bool resume = false;
while (!resume && Dart_HasServiceMessages()) {
- // Release the message queue lock before handling service
- // messages. This allows notifications to come in while we are
- // processing long requests and avoids deadlock with the PortMap
- // lock in the vm.
msg_queue_lock_.Exit();
resume = Dart_HandleServiceMessages();
msg_queue_lock_.Enter();
diff --git a/runtime/bin/file_patch.dart b/runtime/bin/file_patch.dart
index 14499af..479bb9f 100644
--- a/runtime/bin/file_patch.dart
+++ b/runtime/bin/file_patch.dart
@@ -193,6 +193,10 @@
if (event == null) continue;
eventCount++;
int pathId = event[4];
+ if (!_idMap.containsKey(pathId)) {
+ // Path is no longer being wathed.
+ continue;
+ }
bool isDir = getIsDir(event);
var path = getPath(event);
if ((event[0] & FileSystemEvent.CREATE) != 0) {
diff --git a/runtime/bin/vmservice/server.dart b/runtime/bin/vmservice/server.dart
index 55f36e0..c6e7ce4 100644
--- a/runtime/bin/vmservice/server.dart
+++ b/runtime/bin/vmservice/server.dart
@@ -31,8 +31,7 @@
return;
}
var serial = map['id'];
-
- onMessage(serial, new Message.fromJsonRpc(map['method'], map['params']));
+ onMessage(serial, new Message.fromJsonRpc(map));
} else {
socket.close(BINARY_MESSAGE_ERROR_CODE, 'Message must be a string.');
}
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index acea977..2d2dbc3 100755
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -1108,6 +1108,9 @@
* This function may be used by an embedder at a breakpoint to avoid
* pausing the vm service.
*
+ * This function can indirectly cause the message notify callback to
+ * be called.
+ *
* \return true if the vm service requests the program resume
* execution, false otherwise
*/
diff --git a/runtime/observatory/lib/service_common.dart b/runtime/observatory/lib/service_common.dart
index 7aebc81..3add115 100644
--- a/runtime/observatory/lib/service_common.dart
+++ b/runtime/observatory/lib/service_common.dart
@@ -203,6 +203,12 @@
Logger.root.severe('Received unexpected message: ${map}');
return;
}
+ if (request.method != 'getTagProfile' &&
+ request.method != 'getIsolateMetric' &&
+ request.method != 'getVMMetric') {
+ Logger.root.info(
+ 'RESPONSE [${serial}] ${request.method}');
+ }
request.completer.complete(response);
}
@@ -260,11 +266,6 @@
/// Send the request over WebSocket.
void _sendRequest(String serial, _WebSocketRequest request) {
assert (_webSocket.isOpen);
- if (request.method != 'getTagProfile' &&
- request.method != 'getIsolateMetric' &&
- request.method != 'getVMMetric') {
- Logger.root.info('GET ${request.method} from ${target.networkAddress}');
- }
// Mark request as pending.
assert(_pendingRequests.containsKey(serial) == false);
_pendingRequests[serial] = request;
@@ -284,6 +285,12 @@
'method': request.method,
'params': request.params});
}
+ if (request.method != 'getTagProfile' &&
+ request.method != 'getIsolateMetric' &&
+ request.method != 'getVMMetric') {
+ Logger.root.info(
+ 'GET [${serial}] ${request.method} from ${target.networkAddress}');
+ }
// Send message.
_webSocket.send(message);
}
diff --git a/runtime/observatory/lib/src/app/application.dart b/runtime/observatory/lib/src/app/application.dart
index c5413c9..d06c6e1 100644
--- a/runtime/observatory/lib/src/app/application.dart
+++ b/runtime/observatory/lib/src/app/application.dart
@@ -51,9 +51,11 @@
void removePauseEvents(Isolate isolate) {
bool isPauseEvent(var event) {
- return (event.eventType == 'IsolateInterrupted' ||
- event.eventType == 'BreakpointReached' ||
- event.eventType == 'ExceptionThrown');
+ return (event.eventType == ServiceEvent.kPauseStart ||
+ event.eventType == ServiceEvent.kPauseExit ||
+ event.eventType == ServiceEvent.kPauseBreakpoint ||
+ event.eventType == ServiceEvent.kPauseInterrupted ||
+ event.eventType == ServiceEvent.kPauseException);
}
notifications.removeWhere((oldEvent) {
@@ -64,46 +66,29 @@
void _onEvent(ServiceEvent event) {
switch(event.eventType) {
- case 'IsolateCreated':
+ case ServiceEvent.kIsolateStart:
+ case ServiceEvent.kGraph:
+ case ServiceEvent.kBreakpointAdded:
+ case ServiceEvent.kBreakpointResolved:
+ case ServiceEvent.kBreakpointRemoved:
+ case ServiceEvent.kGC:
// Ignore for now.
break;
- case 'IsolateResumed':
- event.isolate.pauseEvent = null;
- break;
-
- case 'IsolateShutdown':
- // TODO(turnidge): Should we show the user isolate shutdown events?
- // What if there are hundreds of them? Coalesce multiple
- // shutdown events into one notification?
+ case ServiceEvent.kIsolateExit:
+ case ServiceEvent.kResume:
removePauseEvents(event.isolate);
-
- // TODO(turnidge): Reload the isolate for now in case it is
- // paused. We may need to distinguish an IsolateShutdown
- // event from a "paused at isolate shutdown" event.
- event.isolate.reload();
break;
- case 'BreakpointResolved':
- event.isolate.reloadBreakpoints();
- break;
-
- case 'BreakpointReached':
- case 'IsolateInterrupted':
- case 'ExceptionThrown':
- event.isolate.pauseEvent = event;
+ case ServiceEvent.kPauseStart:
+ case ServiceEvent.kPauseExit:
+ case ServiceEvent.kPauseBreakpoint:
+ case ServiceEvent.kPauseInterrupted:
+ case ServiceEvent.kPauseException:
removePauseEvents(event.isolate);
notifications.add(event);
break;
- case '_Graph':
- event.isolate.loadHeapSnapshot(event);
- break;
-
- case 'GC':
- // Ignore GC events for now.
- break;
-
default:
// Ignore unrecognized events.
Logger.root.severe('Unrecognized event: $event');
@@ -217,7 +202,7 @@
void _removeDisconnectEvents() {
notifications.removeWhere((oldEvent) {
- return (oldEvent.eventType == 'VMDisconnected');
+ return (oldEvent.eventType == ServiceEvent.kVMDisconnected);
});
}
diff --git a/runtime/observatory/lib/src/cli/command.dart b/runtime/observatory/lib/src/cli/command.dart
index 30a7874..67b21c2 100644
--- a/runtime/observatory/lib/src/cli/command.dart
+++ b/runtime/observatory/lib/src/cli/command.dart
@@ -4,11 +4,26 @@
part of cli;
-// Splits a line into a list of string args.
+// Splits a line into a list of string args. Each arg retains any
+// trailing whitespace so that we can reconstruct the original command
+// line from the pieces.
List<String> _splitLine(String line) {
- var args = line.split(' ').where((arg) {
- return arg != ' ' && arg != '';
- }).toList();
+ line = line.trimLeft();
+ var args = [];
+ var codes = line.codeUnits;
+
+ int pos = 0;
+ while (pos < line.length) {
+ int startPos = pos;
+
+ // Advance to end of word.
+ for (; pos < line.length && line[pos] != ' '; pos++);
+
+ // Advance to end of spaces.
+ for (; pos < line.length && line[pos] == ' '; pos++);
+
+ args.add(line.substring(startPos, pos));
+ }
return args;
}
@@ -17,7 +32,7 @@
if (count == 0) {
return '';
}
- return '${args.sublist(0, count).join(" ")} ';
+ return '${args.sublist(0, count).join('')}';
}
// Shared functionality for RootCommand and Command.
@@ -46,11 +61,12 @@
Future run(List<String> args);
// Returns a list of local subcommands which match the args.
- List<Command> _matchLocal(String arg, bool preferExact) {
+ List<Command> _matchLocal(String argWithSpace, bool preferExact) {
var matches = new List<Command>();
+ var arg = argWithSpace.trimRight();
for (var child in _children) {
if (child.name.startsWith(arg)) {
- if (preferExact && child.name == arg) {
+ if (preferExact && ((child.name == arg) || (child.alias == arg))) {
return [child];
}
matches.add(child);
@@ -220,6 +236,8 @@
Command(this.name, List<Command> children) : super(children);
final String name;
+ String alias;
+
String get fullName {
if (_parent is RootCommand) {
return name;
diff --git a/runtime/observatory/lib/src/debugger/debugger.dart b/runtime/observatory/lib/src/debugger/debugger.dart
index d028c36..97a6ff5 100644
--- a/runtime/observatory/lib/src/debugger/debugger.dart
+++ b/runtime/observatory/lib/src/debugger/debugger.dart
@@ -8,4 +8,5 @@
abstract class Debugger {
Isolate get isolate;
ServiceMap get stack;
+ int get currentFrame;
}
diff --git a/runtime/observatory/lib/src/debugger/source_location.dart b/runtime/observatory/lib/src/debugger/source_location.dart
index 07ab3f6..f14d87b 100644
--- a/runtime/observatory/lib/src/debugger/source_location.dart
+++ b/runtime/observatory/lib/src/debugger/source_location.dart
@@ -48,7 +48,7 @@
return new Future.value(new SourceLocation.error(
'A script must be provided when the stack is empty'));
}
- var frame = stack['frames'][0];
+ var frame = stack['frames'][debugger.currentFrame];
Script script = frame['script'];
return script.load().then((_) {
var line = script.tokenToLine(frame['tokenPos']);
diff --git a/runtime/observatory/lib/src/elements/cpu_profile.html b/runtime/observatory/lib/src/elements/cpu_profile.html
index c5e5cf7..4893f99 100644
--- a/runtime/observatory/lib/src/elements/cpu_profile.html
+++ b/runtime/observatory/lib/src/elements/cpu_profile.html
@@ -153,15 +153,17 @@
</select>
</div>
</div>
+ <!--- Experimental
<div class="memberItem">
<div class="memberName">Call Tree Direction</div>
<div class="memberValue">
<select value="{{directionSelector}}">
- <!--- Experimental <option value="Down">Top down</option> --->
+ <option value="Down">Top down</option>
<option value="Up">Bottom up</option>
</select>
</div>
</div>
+ --->
</div>
</template>
<template if="{{ state == 'Loaded' && directionSelector == 'Down' }}">
diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart
index e9d1623..4a9bdf8 100644
--- a/runtime/observatory/lib/src/elements/debugger.dart
+++ b/runtime/observatory/lib/src/elements/debugger.dart
@@ -28,6 +28,14 @@
class HelpCommand extends DebuggerCommand {
HelpCommand(Debugger debugger) : super(debugger, 'help', []);
+ String _nameAndAlias(Command cmd) {
+ if (cmd.alias == null) {
+ return cmd.name;
+ } else {
+ return '${cmd.name}, ${cmd.alias}';
+ }
+ }
+
Future run(List<String> args) {
var con = debugger.console;
if (args.length == 0) {
@@ -36,14 +44,16 @@
commands.sort((a, b) => a.name.compareTo(b.name));
con.print('List of commands:\n');
for (var command in commands) {
- con.print('${command.name.padRight(12)} - ${command.helpShort}');
+ con.print('${_nameAndAlias(command).padRight(12)} '
+ '- ${command.helpShort}');
}
con.print(
"\nFor more information on a specific command type 'help <command>'\n"
"\n"
"Command prefixes are accepted (e.g. 'h' for 'help')\n"
"Hit [TAB] to complete a command (try 'i[TAB][TAB]')\n"
- "Hit [ENTER] to repeat the last command\n");
+ "Hit [ENTER] to repeat the last command\n"
+ "Use up/down arrow for command history\n");
return new Future.value(null);
} else {
// Print any matching commands.
@@ -56,7 +66,7 @@
}
con.print('');
for (var command in commands) {
- con.printBold(command.fullName);
+ con.printBold(_nameAndAlias(command));
con.print(command.helpLong);
var newArgs = [];
@@ -94,6 +104,138 @@
' help <command> - Help for a specific command\n';
}
+class PrintCommand extends DebuggerCommand {
+ PrintCommand(Debugger debugger) : super(debugger, 'print', []) {
+ alias = 'p';
+ }
+
+ Future run(List<String> args) {
+ if (args.length < 1) {
+ debugger.console.print('print expects arguments');
+ return new Future.value(null);
+ }
+ var expr = args.join('');
+ return debugger.isolate.evalFrame(debugger.currentFrame, expr)
+ .then((response) {
+ if (response is DartError) {
+ debugger.console.print(response.message);
+ } else {
+ debugger.console.print('= ', newline:false);
+ debugger.console.printRef(response);
+ }
+ });
+ }
+
+ String helpShort = 'Evaluate and print an expression in the current frame';
+
+ String helpLong =
+ 'Evaluate and print an expression in the current frame.\n'
+ '\n'
+ 'Syntax: print <expression>\n'
+ ' p <expression>\n';
+}
+
+class DownCommand extends DebuggerCommand {
+ DownCommand(Debugger debugger) : super(debugger, 'down', []);
+
+ Future run(List<String> args) {
+ int count = 1;
+ if (args.length == 1) {
+ count = int.parse(args[0]);
+ } else if (args.length > 1) {
+ debugger.console.print('down expects 0 or 1 argument');
+ return new Future.value(null);
+ }
+ if (debugger.currentFrame == null) {
+ debugger.console.print('No stack');
+ return new Future.value(null);
+ }
+ try {
+ debugger.currentFrame -= count;
+ debugger.console.print('frame = ${debugger.currentFrame}');
+ } catch (e) {
+ debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
+ }
+ return new Future.value(null);
+ }
+
+ String helpShort = 'Move down one or more frames';
+
+ String helpLong =
+ 'Move down one or more frames.\n'
+ '\n'
+ 'Syntax: down\n'
+ ' down <count>\n';
+}
+
+class UpCommand extends DebuggerCommand {
+ UpCommand(Debugger debugger) : super(debugger, 'up', []);
+
+ Future run(List<String> args) {
+ int count = 1;
+ if (args.length == 1) {
+ count = int.parse(args[0]);
+ } else if (args.length > 1) {
+ debugger.console.print('up expects 0 or 1 argument');
+ return new Future.value(null);
+ }
+ if (debugger.currentFrame == null) {
+ debugger.console.print('No stack');
+ return new Future.value(null);
+ }
+ try {
+ debugger.currentFrame += count;
+ debugger.console.print('frame = ${debugger.currentFrame}');
+ } on RangeError catch (e) {
+ debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
+ }
+ return new Future.value(null);
+ }
+
+ String helpShort = 'Move up one or more frames';
+
+ String helpLong =
+ 'Move up one or more frames.\n'
+ '\n'
+ 'Syntax: up\n'
+ ' up <count>\n';
+}
+
+class FrameCommand extends DebuggerCommand {
+ FrameCommand(Debugger debugger) : super(debugger, 'frame', []) {
+ alias = 'f';
+ }
+
+ Future run(List<String> args) {
+ int frame = 1;
+ if (args.length == 1) {
+ frame = int.parse(args[0]);
+ } else {
+ debugger.console.print('frame expects 1 argument');
+ return new Future.value(null);
+ }
+ if (debugger.currentFrame == null) {
+ debugger.console.print('No stack');
+ return new Future.value(null);
+ }
+ try {
+ debugger.currentFrame = frame;
+ debugger.console.print('frame = ${debugger.currentFrame}');
+ } on RangeError catch (e) {
+ debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
+ }
+ return new Future.value(null);
+ }
+
+ String helpShort = 'Set the current frame';
+
+ String helpLong =
+ 'Set the current frame.\n'
+ '\n'
+ 'Syntax: frame <number>\n'
+ ' f <count>\n';
+}
+
class PauseCommand extends DebuggerCommand {
PauseCommand(Debugger debugger) : super(debugger, 'pause', []);
@@ -115,7 +257,9 @@
}
class ContinueCommand extends DebuggerCommand {
- ContinueCommand(Debugger debugger) : super(debugger, 'continue', []);
+ ContinueCommand(Debugger debugger) : super(debugger, 'continue', []) {
+ alias = 'c';
+ }
Future run(List<String> args) {
if (debugger.isolatePaused()) {
@@ -133,7 +277,8 @@
String helpLong =
'Continue running the isolate.\n'
'\n'
- 'Syntax: continue\n';
+ 'Syntax: continue\n'
+ ' c\n';
}
class NextCommand extends DebuggerCommand {
@@ -142,11 +287,11 @@
Future run(List<String> args) {
if (debugger.isolatePaused()) {
var event = debugger.isolate.pauseEvent;
- if (event.eventType == 'IsolateCreated') {
+ if (event.eventType == ServiceEvent.kPauseStart) {
debugger.console.print("Type 'continue' to start the isolate");
return new Future.value(null);
}
- if (event.eventType == 'IsolateShutdown') {
+ if (event.eventType == ServiceEvent.kPauseExit) {
debugger.console.print("Type 'continue' to exit the isolate");
return new Future.value(null);
}
@@ -174,11 +319,11 @@
Future run(List<String> args) {
if (debugger.isolatePaused()) {
var event = debugger.isolate.pauseEvent;
- if (event.eventType == 'IsolateCreated') {
+ if (event.eventType == ServiceEvent.kPauseStart) {
debugger.console.print("Type 'continue' to start the isolate");
return new Future.value(null);
}
- if (event.eventType == 'IsolateShutdown') {
+ if (event.eventType == ServiceEvent.kPauseExit) {
debugger.console.print("Type 'continue' to exit the isolate");
return new Future.value(null);
}
@@ -190,7 +335,7 @@
}
String helpShort =
- 'Continue running the isolate until it reaches the next source location';
+ 'Continue running the isolate until it reaches the next source location';
String helpLong =
'Continue running the isolate until it reaches the next source '
@@ -258,13 +403,6 @@
// TODO(turnidge): Adding a duplicate breakpoint is
// currently ignored. May want to change the protocol to
// inform us when this happens.
-
- // The BreakpointResolved event prints resolved
- // breakpoints already. Just print the unresolved ones here.
- Breakpoint bpt = result;
- if (!bpt.resolved) {
- return debugger._reportBreakpointAdded(bpt);
- }
}
return new Future.value(null);
}
@@ -330,7 +468,7 @@
'clearing breakpoint at a specific column not yet implemented');
}
- for (var bpt in debugger.isolate.breakpoints) {
+ for (var bpt in debugger.isolate.breakpoints.values) {
var script = bpt.script;
if (script.id == loc.script.id) {
assert(script.loaded);
@@ -341,12 +479,6 @@
debugger.console.print(
'Unable to clear breakpoint at ${loc}: ${result.message}');
return;
- } else {
- // TODO(turnidge): Add a BreakpointRemoved event to
- // the service instead of printing here.
- var bpId = bpt.number;
- debugger.console.print('Breakpoint ${bpId} removed at ${loc}');
- return;
}
});
}
@@ -408,7 +540,7 @@
for (var arg in args) {
int id = int.parse(arg);
var bptToRemove = null;
- for (var bpt in debugger.isolate.breakpoints) {
+ for (var bpt in debugger.isolate.breakpoints.values) {
if (bpt.number == id) {
bptToRemove = bpt;
break;
@@ -422,10 +554,7 @@
}
List pending = [];
for (var bpt in toRemove) {
- pending.add(debugger.isolate.removeBreakpoint(bpt).then((_) {
- var id = bpt.number;
- debugger.console.print("Removed breakpoint $id");
- }));
+ pending.add(debugger.isolate.removeBreakpoint(bpt));
}
return Future.wait(pending);
}
@@ -444,27 +573,26 @@
: super(debugger, 'breakpoints', []);
Future run(List<String> args) {
- return debugger.isolate.reloadBreakpoints().then((_) {
- if (debugger.isolate.breakpoints.isEmpty) {
- debugger.console.print('No breakpoints');
- }
- for (var bpt in debugger.isolate.breakpoints) {
- var bpId = bpt.number;
- var script = bpt.script;
- var tokenPos = bpt.tokenPos;
- var line = script.tokenToLine(tokenPos);
- var col = script.tokenToCol(tokenPos);
- var extras = new StringBuffer();
- if (!bpt.resolved) {
- extras.write(' unresolved');
- }
- if (!bpt.enabled) {
- extras.write(' disabled');
- }
+ if (debugger.isolate.breakpoints.isEmpty) {
+ debugger.console.print('No breakpoints');
+ }
+ List bpts = debugger.isolate.breakpoints.values.toList();
+ bpts.sort((a, b) => a.number - b.number);
+ for (var bpt in bpts) {
+ var bpId = bpt.number;
+ var script = bpt.script;
+ var tokenPos = bpt.tokenPos;
+ var line = script.tokenToLine(tokenPos);
+ var col = script.tokenToCol(tokenPos);
+ if (!bpt.resolved) {
debugger.console.print(
- 'Breakpoint ${bpId} at ${script.name}:${line}:${col}${extras}');
+ 'Future breakpoint ${bpId} at ${script.name}:${line}:${col}');
+ } else {
+ debugger.console.print(
+ 'Breakpoint ${bpId} at ${script.name}:${line}:${col}');
}
- });
+ }
+ return new Future.value(null);
}
String helpShort = 'List all breakpoints';
@@ -495,10 +623,31 @@
'Syntax: info isolates\n';
}
+class InfoFrameCommand extends DebuggerCommand {
+ InfoFrameCommand(Debugger debugger) : super(debugger, 'frame', []);
+
+ Future run(List<String> args) {
+ if (args.length > 0) {
+ debugger.console.print('info frame expects 1 argument');
+ return new Future.value(null);
+ }
+ debugger.console.print('frame = ${debugger.currentFrame}');
+ return new Future.value(null);
+ }
+
+ String helpShort = 'Show current frame';
+
+ String helpLong =
+ 'Show current frame.\n'
+ '\n'
+ 'Syntax: info frame\n';
+}
+
class InfoCommand extends DebuggerCommand {
InfoCommand(Debugger debugger) : super(debugger, 'info', [
new InfoBreakpointsCommand(debugger),
new InfoIsolatesCommand(debugger),
+ new InfoFrameCommand(debugger),
]);
Future run(List<String> args) {
@@ -578,11 +727,28 @@
DebuggerConsoleElement console;
DebuggerStackElement stackElement;
ServiceMap stack;
- int currentFrame = 0;
+
+ int get currentFrame => _currentFrame;
+ void set currentFrame(int value) {
+ if (value != null && (value < 0 || value >= stackDepth)) {
+ throw new RangeError.range(value, 0, stackDepth);
+ }
+ _currentFrame = value;
+ if (stackElement != null) {
+ stackElement.setCurrentFrame(value);
+ }
+ }
+ int _currentFrame = null;
+
+ int get stackDepth => stack['frames'].length;
ObservatoryDebugger() {
cmd = new RootCommand([
new HelpCommand(this),
+ new PrintCommand(this),
+ new DownCommand(this),
+ new UpCommand(this),
+ new FrameCommand(this),
new PauseCommand(this),
new ContinueCommand(this),
new NextCommand(this),
@@ -635,7 +801,9 @@
// TODO(turnidge): Stop relying on the isolate to track the last
// pause event. Since we listen to events directly in the
// debugger, this could introduce a race.
- return isolate.pauseEvent != null;
+ return (isolate != null &&
+ isolate.pauseEvent != null &&
+ isolate.pauseEvent.eventType != ServiceEvent.kResume);
}
void warnOutOfDate() {
@@ -653,6 +821,11 @@
// TODO(turnidge): Replace only the changed part of the stack to
// reduce flicker.
stackElement.updateStack(stack, pauseEvent);
+ if (stack['frames'].length > 0) {
+ currentFrame = 0;
+ } else {
+ currentFrame = null;
+ }
});
}
@@ -669,10 +842,10 @@
}
void _reportPause(ServiceEvent event) {
- if (event.eventType == 'IsolateCreated') {
+ if (event.eventType == ServiceEvent.kPauseStart) {
console.print(
"Paused at isolate start (type 'continue' to start the isolate')");
- } else if (event.eventType == 'IsolateShutdown') {
+ } else if (event.eventType == ServiceEvent.kPauseExit) {
console.print(
"Paused at isolate exit (type 'continue' to exit the isolate')");
}
@@ -696,7 +869,22 @@
}
}
- Future _reportBreakpointAdded(Breakpoint bpt) {
+ Future _reportBreakpointEvent(ServiceEvent event) {
+ var bpt = event.breakpoint;
+ var verb = null;
+ switch (event.eventType) {
+ case ServiceEvent.kBreakpointAdded:
+ verb = 'added';
+ break;
+ case ServiceEvent.kBreakpointResolved:
+ verb = 'resolved';
+ break;
+ case ServiceEvent.kBreakpointRemoved:
+ verb = 'removed';
+ break;
+ default:
+ break;
+ }
var script = bpt.script;
return script.load().then((_) {
var bpId = bpt.number;
@@ -704,14 +892,11 @@
var line = script.tokenToLine(tokenPos);
var col = script.tokenToCol(tokenPos);
if (bpt.resolved) {
- // TODO(turnidge): If this was a future breakpoint before, we
- // should change the message to say that the breakpoint was 'resolved',
- // rather than 'added'.
console.print(
- 'Breakpoint ${bpId} added at ${script.name}:${line}:${col}');
+ 'Breakpoint ${bpId} ${verb} at ${script.name}:${line}:${col}');
} else {
console.print(
- 'Future breakpoint ${bpId} added at ${script.name}:${line}:${col}');
+ 'Future breakpoint ${bpId} ${verb} at ${script.name}:${line}:${col}');
}
});
}
@@ -721,30 +906,34 @@
return;
}
switch(event.eventType) {
- case 'IsolateShutdown':
+ case ServiceEvent.kIsolateExit:
console.print('Isolate shutdown');
isolate = null;
break;
- case 'BreakpointReached':
- case 'IsolateInterrupted':
- case 'ExceptionThrown':
+ case ServiceEvent.kPauseStart:
+ case ServiceEvent.kPauseExit:
+ case ServiceEvent.kPauseBreakpoint:
+ case ServiceEvent.kPauseInterrupted:
+ case ServiceEvent.kPauseException:
_refreshStack(event).then((_) {
_reportPause(event);
});
break;
- case 'IsolateResumed':
+ case ServiceEvent.kResume:
console.print('Continuing...');
break;
- case 'BreakpointResolved':
- _reportBreakpointAdded(event.breakpoint);
+ case ServiceEvent.kBreakpointAdded:
+ case ServiceEvent.kBreakpointResolved:
+ case ServiceEvent.kBreakpointRemoved:
+ _reportBreakpointEvent(event);
break;
- case '_Graph':
- case 'IsolateCreated':
- case 'GC':
+ case ServiceEvent.kIsolateStart:
+ case ServiceEvent.kGraph:
+ case ServiceEvent.kGC:
// Ignore these events for now.
break;
@@ -866,13 +1055,19 @@
@published Isolate isolate;
@observable bool hasStack = false;
@observable bool isSampled = false;
+ @observable int currentFrame;
ObservatoryDebugger debugger;
- _addFrame(List frameList, ObservableMap frameInfo, bool expand) {
+ _addFrame(List frameList, ObservableMap frameInfo) {
DebuggerFrameElement frameElement = new Element.tag('debugger-frame');
- frameElement.expand = expand;
frameElement.frame = frameInfo;
+ if (frameInfo['depth'] == currentFrame) {
+ frameElement.setCurrent(true);
+ } else {
+ frameElement.setCurrent(false);
+ }
+
var li = new LIElement();
li.classes.add('list-group-item');
li.children.insert(0, frameElement);
@@ -916,13 +1111,12 @@
// Add new frames to the top of stack.
newCount = newFrames.length - frameElements.length;
for (int i = newCount-1; i >= 0; i--) {
- _addFrame(frameElements, newFrames[i], i == 0);
+ _addFrame(frameElements, newFrames[i]);
}
}
assert(frameElements.length == newFrames.length);
if (frameElements.isNotEmpty) {
- frameElements[0].children[0].expand = true;
for (int i = newCount; i < frameElements.length; i++) {
frameElements[i].children[0].updateFrame(newFrames[i]);
}
@@ -932,6 +1126,19 @@
hasStack = frameElements.isNotEmpty;
}
+ void setCurrentFrame(int value) {
+ currentFrame = value;
+ List frameElements = $['frameList'].children;
+ for (var frameElement in frameElements) {
+ var dbgFrameElement = frameElement.children[0];
+ if (dbgFrameElement.frame['depth'] == currentFrame) {
+ dbgFrameElement.setCurrent(true);
+ } else {
+ dbgFrameElement.setCurrent(false);
+ }
+ }
+ }
+
Set<Script> activeScripts() {
var s = new Set<Script>();
List frameElements = $['frameList'].children;
@@ -964,8 +1171,35 @@
class DebuggerFrameElement extends ObservatoryElement {
@published ObservableMap frame;
- // When true, the frame will start out expanded.
- @published bool expand = false;
+ // Is this the current frame?
+ bool _current = false;
+
+ // Has this frame been pinned open?
+ bool _pinned = false;
+
+ void setCurrent(bool value) {
+ busy = true;
+ frame['function'].load().then((func) {
+ _current = value;
+ var frameOuter = $['frameOuter'];
+ if (_current) {
+ frameOuter.classes.add('current');
+ expanded = true;
+ frameOuter.classes.add('shadow');
+ scrollIntoView();
+ } else {
+ frameOuter.classes.remove('current');
+ if (_pinned) {
+ expanded = true;
+ frameOuter.classes.add('shadow');
+ } else {
+ expanded = false;
+ frameOuter.classes.remove('shadow');
+ }
+ }
+ busy = false;
+ });
+ }
@observable String scriptHeight;
@observable bool expanded = false;
@@ -993,23 +1227,19 @@
scriptHeight = '${windowHeight ~/ 1.6}px';
}
- void expandChanged(oldValue) {
- if (expand != expanded) {
- toggleExpand(null, null, null);
- }
- }
-
void toggleExpand(var a, var b, var c) {
if (busy) {
return;
}
busy = true;
frame['function'].load().then((func) {
- expanded = !expanded;
+ _pinned = !_pinned;
var frameOuter = $['frameOuter'];
- if (expanded) {
+ if (_pinned) {
+ expanded = true;
frameOuter.classes.add('shadow');
} else {
+ expanded = false;
frameOuter.classes.remove('shadow');
}
busy = false;
@@ -1038,11 +1268,23 @@
var span = new SpanElement();
span.classes.add('bold');
span.appendText(line);
- span.appendText('\n');
+ if (newline) {
+ span.appendText('\n');
+ }
$['consoleText'].children.add(span);
span.scrollIntoView();
}
+ void printRef(Instance ref, { bool newline:true }) {
+ var refElement = new Element.tag('instance-ref');
+ refElement.ref = ref;
+ $['consoleText'].children.add(refElement);
+ if (newline) {
+ this.newline();
+ }
+ refElement.scrollIntoView();
+ }
+
void newline() {
var br = new BRElement();
$['consoleText'].children.add(br);
diff --git a/runtime/observatory/lib/src/elements/debugger.html b/runtime/observatory/lib/src/elements/debugger.html
index 06731c5..59ccda8 100644
--- a/runtime/observatory/lib/src/elements/debugger.html
+++ b/runtime/observatory/lib/src/elements/debugger.html
@@ -191,6 +191,11 @@
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.16),
0 2px 5px 0 rgba(0, 0, 0, 0.26);
}
+ .current {
+ box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.26),
+ 0 2px 5px 0 rgba(0, 0, 0, 0.46);
+ border: 1px solid #444;
+ }
.frameSummaryText {
display: inline-block;
padding: 5px;
diff --git a/runtime/observatory/lib/src/elements/isolate_summary.dart b/runtime/observatory/lib/src/elements/isolate_summary.dart
index 07d647a..cefc373 100644
--- a/runtime/observatory/lib/src/elements/isolate_summary.dart
+++ b/runtime/observatory/lib/src/elements/isolate_summary.dart
@@ -22,26 +22,6 @@
IsolateRunStateElement.created() : super.created();
@published Isolate isolate;
-
- Future pause(_) {
- return isolate.pause();
- }
- Future resume(_) {
- app.removePauseEvents(isolate);
- return isolate.resume();
- }
- Future stepInto(_) {
- app.removePauseEvents(isolate);
- return isolate.stepInto();
- }
- Future stepOver(_) {
- app.removePauseEvents(isolate);
- return isolate.stepOver();
- }
- Future stepOut(_) {
- app.removePauseEvents(isolate);
- return isolate.stepOut();
- }
}
@CustomTag('isolate-location')
diff --git a/runtime/observatory/lib/src/elements/isolate_summary.html b/runtime/observatory/lib/src/elements/isolate_summary.html
index cd29e17..89ea848 100644
--- a/runtime/observatory/lib/src/elements/isolate_summary.html
+++ b/runtime/observatory/lib/src/elements/isolate_summary.html
@@ -41,7 +41,7 @@
<polymer-element name="isolate-run-state" extends="observatory-element">
<template>
- <template if="{{ isolate.pauseEvent != null }}">
+ <template if="{{ isolate.paused }}">
<strong>paused</strong>
</template>
@@ -62,21 +62,21 @@
<polymer-element name="isolate-location" extends="observatory-element">
<template>
<template if="{{ isolate.pauseEvent != null }}">
- <template if="{{ isolate.pauseEvent.eventType == 'IsolateCreated' }}">
+ <template if="{{ isolate.pauseEvent.eventType == 'PauseStart' }}">
at isolate start
</template>
- <template if="{{ isolate.pauseEvent.eventType == 'IsolateShutdown' }}">
+ <template if="{{ isolate.pauseEvent.eventType == 'PauseExit' }}">
at isolate exit
</template>
- <template if="{{ isolate.pauseEvent.eventType == 'IsolateInterrupted' ||
- isolate.pauseEvent.eventType == 'BreakpointReached' ||
- isolate.pauseEvent.eventType == 'ExceptionThrown' }}">
+ <template if="{{ isolate.pauseEvent.eventType == 'PauseInterrupted' ||
+ isolate.pauseEvent.eventType == 'PauseBreakpoint' ||
+ isolate.pauseEvent.eventType == 'PauseException' }}">
<template if="{{ isolate.pauseEvent.breakpoint != null }}">
by breakpoint
</template>
- <template if="{{ isolate.pauseEvent.eventType == 'ExceptionThrown' }}">
+ <template if="{{ isolate.pauseEvent.eventType == 'PauseException' }}">
by exception
</template>
at
diff --git a/runtime/observatory/lib/src/elements/script_inset.dart b/runtime/observatory/lib/src/elements/script_inset.dart
index cc4787a..73ee457 100644
--- a/runtime/observatory/lib/src/elements/script_inset.dart
+++ b/runtime/observatory/lib/src/elements/script_inset.dart
@@ -284,15 +284,19 @@
return;
}
busy = true;
- if (line.bpt == null) {
+ if (line.breakpoints == null) {
// No breakpoint. Add it.
line.script.isolate.addBreakpoint(line.script, line.line).then((_) {
busy = false;
});
} else {
// Existing breakpoint. Remove it.
- line.script.isolate.removeBreakpoint(line.bpt).then((_) {
- busy = false;
+ List pending = [];
+ for (var bpt in line.breakpoints) {
+ pending.add(line.script.isolate.removeBreakpoint(bpt));
+ }
+ Future.wait(pending).then((_) {
+ busy = false;
});
}
}
diff --git a/runtime/observatory/lib/src/elements/script_inset.html b/runtime/observatory/lib/src/elements/script_inset.html
index a8fb001..ac7262c 100644
--- a/runtime/observatory/lib/src/elements/script_inset.html
+++ b/runtime/observatory/lib/src/elements/script_inset.html
@@ -90,23 +90,23 @@
<div class="busyBreakpoint">B</div>
</template>
- <template if="{{ line.bpt == null && !line.possibleBpt }}">
+ <template if="{{ line.breakpoints == null && !line.possibleBpt }}">
<div class="emptyBreakpoint"> </div>
</template>
- <template if="{{ line.bpt == null && line.possibleBpt && !busy}}">
+ <template if="{{ line.breakpoints == null && line.possibleBpt && !busy}}">
<div class="possibleBreakpoint">
<a on-click="{{ toggleBreakpoint }}">B</a>
</div>
</template>
- <template if="{{ line.bpt != null && !line.bpt.resolved && !busy}}">
+ <template if="{{ line.breakpoints != null && !line.breakpointResolved && !busy}}">
<div class="unresolvedBreakpoint">
<a on-click="{{ toggleBreakpoint }}">B</a>
</div>
</template>
- <template if="{{ line.bpt != null && line.bpt.resolved && !busy}}">
+ <template if="{{ line.breakpoints != null && line.breakpointResolved && !busy}}">
<div class="resolvedBreakpoint">
<a on-click="{{ toggleBreakpoint }}">B</a>
</div>
diff --git a/runtime/observatory/lib/src/elements/script_ref.dart b/runtime/observatory/lib/src/elements/script_ref.dart
index ab31c8e..2f30e26 100644
--- a/runtime/observatory/lib/src/elements/script_ref.dart
+++ b/runtime/observatory/lib/src/elements/script_ref.dart
@@ -38,7 +38,8 @@
if (ref.loaded) {
// Script is loaded, get the line number.
Script script = ref;
- return '${super.name}:${script.tokenToLine(pos)}';
+ return '${super.name}:${script.tokenToLine(pos)}:'
+ '${script.tokenToCol(pos)}';
} else {
ref.load().then(_updateProperties);
}
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 925e0e7..2bd63a2 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -364,16 +364,16 @@
new StreamController.broadcast();
bool _isIsolateLifecycleEvent(String eventType) {
- return _isIsolateShutdownEvent(eventType) ||
- _isIsolateCreatedEvent(eventType);
+ return _isIsolateExitEvent(eventType) ||
+ _isIsolateStartEvent(eventType);
}
- bool _isIsolateShutdownEvent(String eventType) {
- return (eventType == 'IsolateShutdown');
+ bool _isIsolateExitEvent(String eventType) {
+ return (eventType == ServiceEvent.kIsolateExit);
}
- bool _isIsolateCreatedEvent(String eventType) {
- return (eventType == 'IsolateCreated');
+ bool _isIsolateStartEvent(String eventType) {
+ return (eventType == ServiceEvent.kIsolateStart);
}
void postServiceEvent(String response, ByteData data) {
@@ -399,17 +399,17 @@
if (_isIsolateLifecycleEvent(eventType)) {
String isolateId = map['isolate']['id'];
var event;
- if (_isIsolateCreatedEvent(eventType)) {
- _onIsolateCreated(map['isolate']);
+ if (_isIsolateStartEvent(eventType)) {
+ _onIsolateStart(map['isolate']);
// By constructing the event *after* adding the isolate to the
// isolate cache, the call to getFromMap will use the cached Isolate.
event = new ServiceObject._fromMap(this, map);
} else {
- assert(_isIsolateShutdownEvent(eventType));
+ assert(_isIsolateExitEvent(eventType));
// By constructing the event *before* removing the isolate from the
// isolate cache, the call to getFromMap will use the cached Isolate.
event = new ServiceObject._fromMap(this, map);
- _onIsolateShutdown(isolateId);
+ _onIsolateExit(isolateId);
}
assert(event != null);
events.add(event);
@@ -426,11 +426,12 @@
return;
}
var event = new ServiceObject._fromMap(owningIsolate, map);
+ owningIsolate._onEvent(event);
events.add(event);
});
}
- Isolate _onIsolateCreated(Map isolateMap) {
+ Isolate _onIsolateStart(Map isolateMap) {
var isolateId = isolateMap['id'];
assert(!_isolateCache.containsKey(isolateId));
Isolate isolate = new ServiceObject._fromMap(this, isolateMap);
@@ -443,7 +444,7 @@
return isolate;
}
- void _onIsolateShutdown(String isolateId) {
+ void _onIsolateExit(String isolateId) {
assert(_isolateCache.containsKey(isolateId));
_isolateCache.remove(isolateId);
notifyPropertyChange(#isolates, true, false);
@@ -478,12 +479,12 @@
// Process shutdown.
for (var isolateId in shutdownIsolates) {
- _onIsolateShutdown(isolateId);
+ _onIsolateExit(isolateId);
}
// Process creation.
for (var isolateMap in createdIsolates) {
- _onIsolateCreated(isolateMap);
+ _onIsolateStart(isolateMap);
}
}
@@ -505,6 +506,10 @@
// We should never see an unknown isolate here.
throw new UnimplementedError();
}
+ var mapIsRef = _hasRef(map['type']);
+ if (!mapIsRef) {
+ isolate.update(map);
+ }
return isolate;
}
@@ -774,11 +779,24 @@
@observable ObservableMap counters = new ObservableMap();
@observable ServiceEvent pauseEvent = null;
- bool get _isPaused => pauseEvent != null;
+ void _updateRunState() {
+ topFrame = (pauseEvent != null ? pauseEvent.topFrame : null);
+ paused = (pauseEvent != null &&
+ pauseEvent.eventType != ServiceEvent.kResume);
+ running = (!paused && topFrame != null);
+ idle = (!paused && topFrame == null);
+ notifyPropertyChange(#topFrame, 0, 1);
+ notifyPropertyChange(#paused, 0, 1);
+ notifyPropertyChange(#running, 0, 1);
+ notifyPropertyChange(#idle, 0, 1);
+ }
+
+ @observable bool paused = false;
@observable bool running = false;
@observable bool idle = false;
@observable bool loading = true;
+
@observable bool ioEnabled = false;
Map<String,ServiceObject> _cache = new Map<String,ServiceObject>();
@@ -844,7 +862,10 @@
String mapId = map['id'];
var obj = (mapId != null) ? _cache[mapId] : null;
if (obj != null) {
- // Consider calling update when map is not a reference.
+ var mapIsRef = _hasRef(map['type']);
+ if (!mapIsRef) {
+ obj.update(map);
+ }
return obj;
}
// Build the object from the map directly.
@@ -862,12 +883,7 @@
Future<ServiceObject> invokeRpc(String method, Map params) {
return invokeRpcNoUpgrade(method, params).then((ObservableMap response) {
- var obj = new ServiceObject._fromMap(this, response);
- if ((obj != null) && obj.canCache) {
- String objId = obj.id;
- _cache.putIfAbsent(objId, () => obj);
- }
- return obj;
+ return getFromMap(response);
});
}
@@ -912,9 +928,11 @@
@observable HeapSnapshot latestSnapshot;
Completer<HeapSnapshot> _snapshotFetch;
- void loadHeapSnapshot(ServiceEvent event) {
+ void _loadHeapSnapshot(ServiceEvent event) {
latestSnapshot = new HeapSnapshot(this, event.data);
- _snapshotFetch.complete(latestSnapshot);
+ if (_snapshotFetch != null) {
+ _snapshotFetch.complete(latestSnapshot);
+ }
}
Future<HeapSnapshot> fetchHeapSnapshot() {
@@ -940,7 +958,6 @@
_loaded = true;
loading = false;
- reloadBreakpoints();
_upgradeCollection(map, isolate);
if (map['rootLib'] == null ||
map['timers'] == null ||
@@ -952,11 +969,6 @@
if (map['entry'] != null) {
entry = map['entry'];
}
- if (map['topFrame'] != null) {
- topFrame = map['topFrame'];
- } else {
- topFrame = null ;
- }
var countersMap = map['tagCounters'];
if (countersMap != null) {
@@ -994,6 +1006,7 @@
timers['dart'] = timerMap['time_dart_execution'];
updateHeapsFromMap(map['heaps']);
+ _updateBreakpoints(map['breakpoints']);
List features = map['features'];
if (features != null) {
@@ -1003,10 +1016,8 @@
}
}
}
- // Isolate status
pauseEvent = map['pauseEvent'];
- running = (!_isPaused && map['topFrame'] != null);
- idle = (!_isPaused && map['topFrame'] == null);
+ _updateRunState();
error = map['error'];
libraries.clear();
@@ -1023,71 +1034,74 @@
});
}
- ObservableList<Breakpoint> breakpoints = new ObservableList();
+ ObservableMap<int, Breakpoint> breakpoints = new ObservableMap();
- void _removeBreakpoint(Breakpoint bpt) {
- var script = bpt.script;
- var tokenPos = bpt.tokenPos;
- assert(tokenPos != null);
- if (script.loaded) {
- var line = script.tokenToLine(tokenPos);
- assert(line != null);
- if (script.lines[line - 1] != null) {
- assert(script.lines[line - 1].bpt == bpt);
- script.lines[line - 1].bpt = null;
+ void _updateBreakpoints(List newBpts) {
+ // Build a map of new breakpoints.
+ var newBptMap = {};
+ newBpts.forEach((bpt) => (newBptMap[bpt.number] = bpt));
+
+ // Remove any old breakpoints which no longer exist.
+ List toRemove = [];
+ breakpoints.forEach((key, _) {
+ if (!newBptMap.containsKey(key)) {
+ toRemove.add(key);
}
- }
+ });
+ toRemove.forEach((key) => breakpoints.remove(key));
+
+ // Add all new breakpoints.
+ breakpoints.addAll(newBptMap);
}
void _addBreakpoint(Breakpoint bpt) {
- var script = bpt.script;
- var tokenPos = bpt.tokenPos;
- assert(tokenPos != null);
- if (script.loaded) {
- var line = script.tokenToLine(tokenPos);
- assert(line != null);
- assert(script.lines[line - 1].bpt == null);
- script.lines[line - 1].bpt = bpt;
- } else {
- // Load the script and then plop in the breakpoint.
- script.load().then((_) {
- _addBreakpoint(bpt);
- });
- }
+ breakpoints[bpt.number] = bpt;
}
- void _updateBreakpoints(ServiceMap newBreakpoints) {
- // Remove all of the old breakpoints from the Script lines.
- if (breakpoints != null) {
- for (var bpt in breakpoints) {
- _removeBreakpoint(bpt);
- }
- }
- // Add all of the new breakpoints to the Script lines.
- for (var bpt in newBreakpoints['breakpoints']) {
- _addBreakpoint(bpt);
- }
- breakpoints.clear();
- breakpoints.addAll(newBreakpoints['breakpoints']);
-
- // Sort the breakpoints by breakpointNumber.
- breakpoints.sort((a, b) => (a.number - b.number));
+ void _removeBreakpoint(Breakpoint bpt) {
+ breakpoints.remove(bpt.number);
+ bpt.remove();
}
- Future<ServiceObject> _inProgressReloadBpts;
+ void _onEvent(ServiceEvent event) {
+ assert(event.eventType != ServiceEvent.kIsolateStart &&
+ event.eventType != ServiceEvent.kIsolateExit);
+ switch(event.eventType) {
+ case ServiceEvent.kBreakpointAdded:
+ _addBreakpoint(event.breakpoint);
+ break;
- Future reloadBreakpoints() {
- // TODO(turnidge): Can reusing the Future here ever cause us to
- // get stale breakpoints?
- if (_inProgressReloadBpts == null) {
- _inProgressReloadBpts =
- invokeRpc('getBreakpoints', {}).then((newBpts) {
- _updateBreakpoints(newBpts);
- }).whenComplete(() {
- _inProgressReloadBpts = null;
- });
+ case ServiceEvent.kBreakpointResolved:
+ // Update occurs as side-effect of caching.
+ break;
+
+ case ServiceEvent.kBreakpointRemoved:
+ _removeBreakpoint(event.breakpoint);
+ break;
+
+ case ServiceEvent.kPauseStart:
+ case ServiceEvent.kPauseExit:
+ case ServiceEvent.kPauseBreakpoint:
+ case ServiceEvent.kPauseInterrupted:
+ case ServiceEvent.kPauseException:
+ case ServiceEvent.kResume:
+ pauseEvent = event;
+ _updateRunState();
+ break;
+
+ case ServiceEvent.kGraph:
+ _loadHeapSnapshot(event);
+ break;
+
+ case ServiceEvent.kGC:
+ // Ignore GC events for now.
+ break;
+
+ default:
+ // Log unrecognized events.
+ Logger.root.severe('Unrecognized event: $event');
+ break;
}
- return _inProgressReloadBpts;
}
Future<ServiceObject> addBreakpoint(Script script, int line) {
@@ -1107,53 +1121,26 @@
// Unable to set a breakpoint at desired line.
script.lines[line - 1].possibleBpt = false;
}
- // TODO(turnidge): Instead of reloading all of the breakpoints,
- // rely on events to update the breakpoint list.
- return reloadBreakpoints().then((_) {
- return result;
- });
+ return result;
});
}
Future<ServiceObject> addBreakpointAtEntry(ServiceFunction function) {
return invokeRpc('addBreakpointAtEntry',
- { 'functionId': function.id }).then((result) {
- // TODO(turnidge): Instead of reloading all of the breakpoints,
- // rely on events to update the breakpoint list.
- return reloadBreakpoints().then((_) {
- return result;
- });
- });
+ { 'functionId': function.id });
}
Future removeBreakpoint(Breakpoint bpt) {
return invokeRpc('removeBreakpoint',
- { 'breakpointId': bpt.id }).then((result) {
- if (result is DartError) {
- // TODO(turnidge): Handle this more gracefully.
- Logger.root.severe(result.message);
- return result;
- }
- if (pauseEvent != null &&
- pauseEvent.breakpoint != null &&
- (pauseEvent.breakpoint.id == bpt.id)) {
- return isolate.reload();
- } else {
- return reloadBreakpoints();
- }
- });
+ { 'breakpointId': bpt.id });
}
- // TODO(turnidge): If the user invokes pause (or other rpcs) twice,
- // they could get a race. Consider returning an "in progress"
- // future to avoid this.
Future pause() {
return invokeRpc('pause', {}).then((result) {
if (result is DartError) {
// TODO(turnidge): Handle this more gracefully.
Logger.root.severe(result.message);
}
- return isolate.reload();
});
}
@@ -1163,7 +1150,6 @@
// TODO(turnidge): Handle this more gracefully.
Logger.root.severe(result.message);
}
- return isolate.reload();
});
}
@@ -1173,7 +1159,6 @@
// TODO(turnidge): Handle this more gracefully.
Logger.root.severe(result.message);
}
- return isolate.reload();
});
}
@@ -1183,7 +1168,6 @@
// TODO(turnidge): Handle this more gracefully.
Logger.root.severe(result.message);
}
- return isolate.reload();
});
}
@@ -1193,7 +1177,6 @@
// TODO(turnidge): Handle this more gracefully.
Logger.root.severe(result.message);
}
- return isolate.reload();
});
}
@@ -1216,6 +1199,15 @@
return invokeRpc('eval', params);
}
+ Future<ServiceObject> evalFrame(int framePos,
+ String expression) {
+ Map params = {
+ 'frame': framePos,
+ 'expression': expression,
+ };
+ return invokeRpc('evalFrame', params);
+ }
+
Future<ServiceObject> getRetainedSize(ServiceObject target) {
Map params = {
'targetId': target.id,
@@ -1319,14 +1311,10 @@
ServiceMap._empty(ServiceObjectOwner owner) : super._empty(owner);
- void _upgradeValues() {
- assert(owner != null);
- _upgradeCollection(_map, owner);
- }
-
void _update(ObservableMap map, bool mapIsRef) {
_loaded = !mapIsRef;
+ _upgradeCollection(map, owner);
// TODO(turnidge): Currently _map.clear() prevents us from
// upgrading an already upgraded submap. Is clearing really the
// right thing to do here?
@@ -1335,7 +1323,6 @@
name = _map['name'];
vmName = (_map.containsKey('vmName') ? _map['vmName'] : name);
- _upgradeValues();
}
// Forward Map interface calls.
@@ -1432,14 +1419,31 @@
/// A [ServiceEvent] is an asynchronous event notification from the vm.
class ServiceEvent extends ServiceObject {
+ /// The possible 'eventType' values.
+ static const kIsolateStart = 'IsolateStart';
+ static const kIsolateExit = 'IsolateExit';
+ static const kPauseStart = 'PauseStart';
+ static const kPauseExit = 'PauseExit';
+ static const kPauseBreakpoint = 'PauseBreakpoint';
+ static const kPauseInterrupted = 'PauseInterrupted';
+ static const kPauseException = 'PauseException';
+ static const kResume = 'Resume';
+ static const kBreakpointAdded = 'BreakpointAdded';
+ static const kBreakpointResolved = 'BreakpointResolved';
+ static const kBreakpointRemoved = 'BreakpointRemoved';
+ static const kGraph = '_Graph';
+ static const kGC = 'GC';
+ static const kVMDisconnected = 'VMDisconnected';
+
ServiceEvent._empty(ServiceObjectOwner owner) : super._empty(owner);
ServiceEvent.vmDisconencted() : super._empty(null) {
- eventType = 'VMDisconnected';
+ eventType = kVMDisconnected;
}
@observable String eventType;
@observable Breakpoint breakpoint;
+ @observable ServiceMap topFrame;
@observable ServiceMap exception;
@observable ByteData data;
@observable int count;
@@ -1453,6 +1457,9 @@
if (map['breakpoint'] != null) {
breakpoint = map['breakpoint'];
}
+ if (map['topFrame'] != null) {
+ topFrame = map['topFrame'];
+ }
if (map['exception'] != null) {
exception = map['exception'];
}
@@ -1465,8 +1472,12 @@
}
String toString() {
- return 'ServiceEvent of type $eventType with '
- '${data == null ? 0 : data.lengthInBytes} bytes of binary data';
+ if (data == null) {
+ return "ServiceEvent(owner='${owner.id}', type='${eventType}')";
+ } else {
+ return "ServiceEvent(owner='${owner.id}', type='${eventType}', "
+ "data.lengthInBytes=${data.lengthInBytes})";
+ }
}
}
@@ -1488,19 +1499,44 @@
// The breakpoint has been assigned to a final source location.
@observable bool resolved;
- // The breakpoint is active.
- @observable bool enabled;
-
void _update(ObservableMap map, bool mapIsRef) {
_loaded = true;
_upgradeCollection(map, owner);
+ var newNumber = map['breakpointNumber'];
+ var newScript = map['location']['script'];
+ var newTokenPos = map['location']['tokenPos'];
+
+ // number and script never change.
+ assert((number == null) || (number == newNumber));
+ assert((script == null) || (script == newScript));
+
number = map['breakpointNumber'];
script = map['location']['script'];
- tokenPos = map['location']['tokenPos'];
-
resolved = map['resolved'];
- enabled = map['enabled'];
+ bool tokenPosChanged = tokenPos != newTokenPos;
+
+ if (script.loaded &&
+ (tokenPos != null) &&
+ tokenPosChanged) {
+ // The breakpoint has moved. Remove it and add it later.
+ script._removeBreakpoint(this);
+ }
+
+ tokenPos = newTokenPos;
+ if (script.loaded && tokenPosChanged) {
+ script._addBreakpoint(this);
+ }
+ }
+
+ void remove() {
+ // Remove any references to this breakpoint. It has been removed.
+ script._removeBreakpoint(this);
+ if ((isolate.pauseEvent != null) &&
+ (isolate.pauseEvent.breakpoint != null) &&
+ (isolate.pauseEvent.breakpoint.id == id)) {
+ isolate.pauseEvent.breakpoint = null;
+ }
}
String toString() {
@@ -1900,6 +1936,7 @@
return;
}
+ _loaded = true;
isStatic = map['static'];
isConst = map['const'];
parent = map['parent'];
@@ -1969,8 +2006,9 @@
final int line;
final String text;
@observable int hits;
- @observable Breakpoint bpt;
@observable bool possibleBpt = true;
+ @observable bool breakpointResolved = false;
+ @observable Set<Breakpoint> breakpoints;
bool get isBlank {
// Compute isBlank on demand.
@@ -2015,13 +2053,22 @@
ScriptLine(this.script, this.line, this.text) {
possibleBpt = !_isTrivialLine(text);
+ }
- // TODO(turnidge): This is not so efficient. Consider improving.
- for (var bpt in this.script.isolate.breakpoints) {
- if (bpt.script == this.script &&
- bpt.script.tokenToLine(bpt.tokenPos) == line) {
- this.bpt = bpt;
- }
+ void addBreakpoint(Breakpoint bpt) {
+ if (breakpoints == null) {
+ breakpoints = new Set<Breakpoint>();
+ }
+ breakpoints.add(bpt);
+ breakpointResolved = breakpointResolved || bpt.resolved;
+ }
+
+ void removeBreakpoint(Breakpoint bpt) {
+ assert(breakpoints != null && breakpoints.contains(bpt));
+ breakpoints.remove(bpt);
+ if (breakpoints.isEmpty) {
+ breakpoints = null;
+ breakpointResolved = false;
}
}
}
@@ -2064,8 +2111,8 @@
if (mapIsRef) {
return;
}
- _processSource(map['source']);
_parseTokenPosTable(map['tokenPosTable']);
+ _processSource(map['source']);
owningLibrary = map['owningLibrary'];
}
@@ -2145,6 +2192,12 @@
for (var i = 0; i < sourceLines.length; i++) {
lines.add(new ScriptLine(this, i + 1, sourceLines[i]));
}
+ for (var bpt in isolate.breakpoints.values) {
+ if (bpt.script == this) {
+ _addBreakpoint(bpt);
+ }
+ }
+
_applyHitsToLines();
// Notify any Observers that this Script's state has changed.
notifyChange(null);
@@ -2156,6 +2209,18 @@
line.hits = hits;
}
}
+
+ void _addBreakpoint(Breakpoint bpt) {
+ var line = tokenToLine(bpt.tokenPos);
+ getLine(line).addBreakpoint(bpt);
+ }
+
+ void _removeBreakpoint(Breakpoint bpt) {
+ var line = tokenToLine(bpt.tokenPos);
+ if (line != null) {
+ getLine(line).removeBreakpoint(bpt);
+ }
+ }
}
class PcDescriptor extends Observable {
diff --git a/runtime/observatory/test/code_test.dart b/runtime/observatory/test/code_test.dart
index c12b6f3..16806ee 100644
--- a/runtime/observatory/test/code_test.dart
+++ b/runtime/observatory/test/code_test.dart
@@ -35,7 +35,7 @@
Completer completer = new Completer();
List events = [];
isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType == 'BreakpointReached') {
+ if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print('Breakpoint reached');
completer.complete();
}
diff --git a/runtime/observatory/test/command_test.dart b/runtime/observatory/test/command_test.dart
index 8159981..260684e 100644
--- a/runtime/observatory/test/command_test.dart
+++ b/runtime/observatory/test/command_test.dart
@@ -124,7 +124,7 @@
});
// Locals + subcommands, single local match.
- cmd.completeCommand('count th ').then((result) {
+ cmd.completeCommand('count th').then((result) {
expect(result, equals(['count three ']));
});
@@ -138,9 +138,9 @@
expect(result, equals(['co chocula ']));
});
- // We gobble spare spaces, even in the prefix.
+ // We gobble spare spaces in the prefix but not elsewhere.
cmd.completeCommand(' game chec').then((result) {
- expect(result, equals(['game checkers ']));
+ expect(result, equals(['game checkers ']));
});
}
@@ -155,7 +155,7 @@
out.clear();
// Substring dispatch works.
cmd.runCommand('al cat mouse').then(expectAsync((_) {
- expect(out.toString(), contains('executing alpha([cat, mouse])\n'));
+ expect(out.toString(), contains('executing alpha([cat , mouse])\n'));
}));
}));
}
@@ -203,11 +203,25 @@
}));
}
+void testCommandRunAlias() {
+ // Run a simple command.
+ StringBuffer out = new StringBuffer();
+ var aliasCmd = new TestCommand(out, 'alpha', []);
+ aliasCmd.alias = 'a';
+ RootCommand cmd = new RootCommand([aliasCmd,
+ new TestCommand(out, 'ankle', [])]);
+
+ cmd.runCommand('a 55').then(expectAsync((_) {
+ expect(out.toString(), equals('executing alpha([55])\n'));
+ }));
+}
+
main() {
test('command completion test suite', testCommandComplete);
test('run a simple command', testCommandRunSimple);
test('run a subcommand', testCommandRunSubcommand);
test('run a command which is not found', testCommandRunNotFound);
test('run a command which is ambiguous', testCommandRunAmbiguous);
+ test('run a command using an alias', testCommandRunAlias);
}
diff --git a/runtime/observatory/test/coverage_test.dart b/runtime/observatory/test/coverage_test.dart
index 67d0504..207b67d 100644
--- a/runtime/observatory/test/coverage_test.dart
+++ b/runtime/observatory/test/coverage_test.dart
@@ -58,7 +58,7 @@
Completer completer = new Completer();
List events = [];
isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType == 'BreakpointReached') {
+ if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print('Breakpoint reached');
completer.complete();
}
diff --git a/runtime/observatory/test/debugging_test.dart b/runtime/observatory/test/debugging_test.dart
index 51204e7..751ec00 100644
--- a/runtime/observatory/test/debugging_test.dart
+++ b/runtime/observatory/test/debugging_test.dart
@@ -25,8 +25,10 @@
// Pause
(Isolate isolate) {
Completer completer = new Completer();
- isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType == 'IsolateInterrupted') {
+ var subscription;
+ subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
+ if (event.eventType == ServiceEvent.kPauseInterrupted) {
+ subscription.cancel();
completer.complete();
}
});
@@ -36,9 +38,16 @@
// Resume
(Isolate isolate) {
- return isolate.resume().then((_) {
- expect(isolate.pauseEvent == null, isTrue);
+ Completer completer = new Completer();
+ var subscription;
+ subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
+ if (event.eventType == ServiceEvent.kResume) {
+ subscription.cancel();
+ completer.complete();
+ }
});
+ isolate.resume();
+ return completer.future;
},
// Add breakpoint
@@ -49,15 +58,10 @@
List events = [];
var subscription;
subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType.startsWith('Breakpoint')) {
- events.add(event);
- if (events.length == 2) {
- expect(events[0].eventType, equals('BreakpointResolved'));
- expect(events[1].eventType, equals('BreakpointReached'));
- print('Breakpoint reached');
- subscription.cancel();
- completer.complete();
- }
+ if (event.eventType == ServiceEvent.kPauseBreakpoint) {
+ print('Breakpoint reached');
+ subscription.cancel();
+ completer.complete();
}
});
@@ -91,7 +95,7 @@
List events = [];
var subscription;
subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType.startsWith('BreakpointReached')) {
+ if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print('Breakpoint reached');
subscription.cancel();
completer.complete();
@@ -114,18 +118,38 @@
// Remove breakpoint
(Isolate isolate) {
- expect(isolate.breakpoints.length, equals(1));
- var bpt = isolate.breakpoints[0];
- return isolate.removeBreakpoint(bpt).then((_) {
+ // Set up a listener to wait for breakpoint events.
+ Completer completer = new Completer();
+ List events = [];
+ var subscription;
+ subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
+ if (event.eventType == ServiceEvent.kBreakpointRemoved) {
+ print('Breakpoint removed');
expect(isolate.breakpoints.length, equals(0));
+ subscription.cancel();
+ completer.complete();
+ }
+ });
+
+ expect(isolate.breakpoints.length, equals(1));
+ var bpt = isolate.breakpoints.values.first;
+ return isolate.removeBreakpoint(bpt).then((_) {
+ return completer.future;
});
},
// Resume
(Isolate isolate) {
- return isolate.resume().then((_) {
- expect(isolate.pauseEvent == null, isTrue);
+ Completer completer = new Completer();
+ var subscription;
+ subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
+ if (event.eventType == ServiceEvent.kResume) {
+ subscription.cancel();
+ completer.complete();
+ }
});
+ isolate.resume();
+ return completer.future;
},
// Add breakpoint at function entry
@@ -135,7 +159,7 @@
List events = [];
var subscription;
subscription = isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType.startsWith('BreakpointReached')) {
+ if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print('Breakpoint reached');
subscription.cancel();
completer.complete();
diff --git a/runtime/observatory/test/eval_test.dart b/runtime/observatory/test/eval_test.dart
index 7c5d77c..8355f7e 100644
--- a/runtime/observatory/test/eval_test.dart
+++ b/runtime/observatory/test/eval_test.dart
@@ -35,7 +35,7 @@
Completer completer = new Completer();
List events = [];
isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType == 'BreakpointReached') {
+ if (event.eventType == ServiceEvent.kPauseBreakpoint) {
print('Breakpoint reached');
completer.complete();
}
diff --git a/runtime/observatory/test/gc_test.dart b/runtime/observatory/test/gc_test.dart
index 7f2c441..11ba8c5 100644
--- a/runtime/observatory/test/gc_test.dart
+++ b/runtime/observatory/test/gc_test.dart
@@ -27,7 +27,7 @@
// Expect at least this many GC events.
int gcCountdown = 3;
isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType == 'GC' && --gcCountdown == 0) {
+ if (event.eventType == ServiceEvent.kGC && --gcCountdown == 0) {
completer.complete();
}
});
diff --git a/runtime/observatory/test/graph_test.dart b/runtime/observatory/test/graph_test.dart
index db16681..a496088 100644
--- a/runtime/observatory/test/graph_test.dart
+++ b/runtime/observatory/test/graph_test.dart
@@ -39,7 +39,7 @@
(Isolate isolate) {
Completer completer = new Completer();
isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType == '_Graph') {
+ if (event.eventType == ServiceEvent.kGraph) {
ReadStream reader = new ReadStream(event.data);
ObjectGraph graph = new ObjectGraph(reader);
expect(fooId, isNotNull);
diff --git a/runtime/observatory/test/isolate_lifecycle_test.dart b/runtime/observatory/test/isolate_lifecycle_test.dart
index 97411d2..c814ca9 100644
--- a/runtime/observatory/test/isolate_lifecycle_test.dart
+++ b/runtime/observatory/test/isolate_lifecycle_test.dart
@@ -44,7 +44,7 @@
var pausedCount = 0;
var runningCount = 0;
for (var isolate in vm.isolates) {
- if (isolate.pauseEvent != null) {
+ if (isolate.paused) {
pausedCount++;
} else {
runningCount++;
@@ -56,7 +56,7 @@
(VM vm) async {
var resumedReceived = 0;
var eventsDone = processServiceEvents(vm, (event, sub, completer) {
- if (event.eventType.startsWith('IsolateShutdown')) {
+ if (event.eventType == ServiceEvent.kIsolateExit) {
resumedReceived++;
if (resumedReceived == resumeCount) {
sub.cancel();
@@ -84,7 +84,7 @@
var pausedCount = 0;
var runningCount = 0;
for (var isolate in vm.isolates) {
- if (isolate.pauseEvent != null) {
+ if (isolate.paused) {
pausedCount++;
} else {
runningCount++;
diff --git a/runtime/observatory/test/source_location_test.dart b/runtime/observatory/test/source_location_test.dart
index 5c24d32..e0cdd21 100644
--- a/runtime/observatory/test/source_location_test.dart
+++ b/runtime/observatory/test/source_location_test.dart
@@ -22,6 +22,7 @@
Isolate isolate;
ServiceMap stack;
+ int currentFrame = 0;
}
void source_location_dummy_function() {
@@ -54,7 +55,7 @@
// Listen for breakpoint event.
Completer completer = new Completer();
isolate.vm.events.stream.listen((ServiceEvent event) {
- if (event.eventType == 'BreakpointReached') {
+ if (event.eventType == ServiceEvent.kPauseBreakpoint) {
completer.complete();
}
});
diff --git a/runtime/vm/assembler_x64.cc b/runtime/vm/assembler_x64.cc
index a3e6c26..dcf41c2 100644
--- a/runtime/vm/assembler_x64.cc
+++ b/runtime/vm/assembler_x64.cc
@@ -722,21 +722,21 @@
void Assembler::addpl(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
- EmitREX_RB(dst, src);
EmitUint8(0x66);
+ EmitREX_RB(dst, src);
EmitUint8(0x0F);
EmitUint8(0xFE);
- EmitXmmRegisterOperand(dst, src);
+ EmitXmmRegisterOperand(dst & 7, src);
}
void Assembler::subpl(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
- EmitREX_RB(dst, src);
EmitUint8(0x66);
+ EmitREX_RB(dst, src);
EmitUint8(0x0F);
EmitUint8(0xFA);
- EmitXmmRegisterOperand(dst, src);
+ EmitXmmRegisterOperand(dst & 7, src);
}
diff --git a/runtime/vm/assembler_x64_test.cc b/runtime/vm/assembler_x64_test.cc
index eafb765..3a35086 100644
--- a/runtime/vm/assembler_x64_test.cc
+++ b/runtime/vm/assembler_x64_test.cc
@@ -2298,6 +2298,36 @@
}
+ASSEMBLER_TEST_GENERATE(PackedIntOperations2, assembler) {
+ // Note: on Windows 64 XMM6-XMM15 are callee save.
+ const intptr_t cpu_register_set = 0;
+ const intptr_t fpu_register_set =
+ ((1 << XMM10) | (1 << XMM11)) & CallingConventions::kVolatileXmmRegisters;
+ __ PushRegisters(cpu_register_set, fpu_register_set);
+ __ movl(RAX, Immediate(0x2));
+ __ movd(XMM10, RAX);
+ __ shufps(XMM10, XMM10, Immediate(0x0));
+ __ movl(RAX, Immediate(0x1));
+ __ movd(XMM11, RAX);
+ __ shufps(XMM11, XMM11, Immediate(0x0));
+ __ addpl(XMM10, XMM11); // 0x3
+ __ addpl(XMM10, XMM10); // 0x6
+ __ subpl(XMM10, XMM11); // 0x5
+ __ pushq(RAX);
+ __ movss(Address(RSP, 0), XMM10);
+ __ popq(RAX);
+ __ PopRegisters(cpu_register_set, fpu_register_set);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(PackedIntOperations2, test) {
+ typedef uint32_t (*PackedIntOperationsCode)();
+ uint32_t res = reinterpret_cast<PackedIntOperationsCode>(test->entry())();
+ EXPECT_EQ(static_cast<uword>(0x5), res);
+}
+
+
ASSEMBLER_TEST_GENERATE(PackedFPOperations2, assembler) {
__ movq(RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
__ movd(XMM0, RAX);
diff --git a/runtime/vm/ast_transformer.cc b/runtime/vm/ast_transformer.cc
index 3acd1d0..dacabbf 100644
--- a/runtime/vm/ast_transformer.cc
+++ b/runtime/vm/ast_transformer.cc
@@ -66,10 +66,10 @@
Z, String::NewFormatted("%s%d", await_temp_prefix, temp_cnt_));
const String& symbol = String::ZoneHandle(Z, Symbols::New(cnt_str));
ASSERT(!symbol.IsNull());
- // Look up the variable through the preamble scope.
- LocalVariable* await_tmp = preamble_->scope()->LookupVariable(symbol, false);
+ // Look up the variable in the scope used for async temp variables.
+ LocalVariable* await_tmp = function_top_->LocalLookupVariable(symbol);
if (await_tmp == NULL) {
- // If we need a new temp variable, we add it to the function's top scope.
+ // We need a new temp variable; add it to the function's top scope.
await_tmp = new (Z) LocalVariable(
Scanner::kNoSourcePos, symbol, Type::ZoneHandle(Type::DynamicType()));
function_top_->AddVariable(await_tmp);
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index 1757c13..233d1b6 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -84,4 +84,42 @@
return map.raw();
}
+
+RawExceptionHandlers* ExceptionHandlerList::FinalizeExceptionHandlers(
+ uword entry_point) const {
+ intptr_t num_handlers = Length();
+ if (num_handlers == 0) {
+ return Object::empty_exception_handlers().raw();
+ }
+ const ExceptionHandlers& handlers =
+ ExceptionHandlers::Handle(ExceptionHandlers::New(num_handlers));
+ for (intptr_t i = 0; i < num_handlers; i++) {
+ // Assert that every element in the array has been initialized.
+ if (list_[i].handler_types == NULL) {
+ // Unreachable handler, entry not computed.
+ // Initialize it to some meaningful value.
+ const bool has_catch_all = false;
+ // Check it is uninitialized.
+ ASSERT((list_[i].outer_try_index == -1) &&
+ (list_[i].pc_offset == ExceptionHandlers::kInvalidPcOffset));
+ handlers.SetHandlerInfo(i,
+ list_[i].outer_try_index,
+ list_[i].pc_offset,
+ list_[i].needs_stacktrace,
+ has_catch_all);
+ handlers.SetHandledTypes(i, Array::null_array());
+ } else {
+ const bool has_catch_all = ContainsDynamic(*list_[i].handler_types);
+ handlers.SetHandlerInfo(i,
+ list_[i].outer_try_index,
+ list_[i].pc_offset,
+ list_[i].needs_stacktrace,
+ has_catch_all);
+ handlers.SetHandledTypes(i, *list_[i].handler_types);
+ }
+ }
+ return handlers.raw();
+}
+
+
} // namespace dart
diff --git a/runtime/vm/code_descriptors.h b/runtime/vm/code_descriptors.h
index d5ea730..796f290 100644
--- a/runtime/vm/code_descriptors.h
+++ b/runtime/vm/code_descriptors.h
@@ -116,7 +116,7 @@
void AddPlaceHolder() {
struct HandlerDesc data;
data.outer_try_index = -1;
- data.pc_offset = -1;
+ data.pc_offset = ExceptionHandlers::kInvalidPcOffset;
data.handler_types = NULL;
data.needs_stacktrace = false;
list_.Add(data);
@@ -162,27 +162,7 @@
return false;
}
-
- RawExceptionHandlers* FinalizeExceptionHandlers(uword entry_point) {
- intptr_t num_handlers = Length();
- if (num_handlers == 0) {
- return Object::empty_exception_handlers().raw();
- }
- const ExceptionHandlers& handlers =
- ExceptionHandlers::Handle(ExceptionHandlers::New(num_handlers));
- for (intptr_t i = 0; i < num_handlers; i++) {
- // Assert that every element in the array has been initialized.
- ASSERT(list_[i].handler_types != NULL);
- bool has_catch_all = ContainsDynamic(*list_[i].handler_types);
- handlers.SetHandlerInfo(i,
- list_[i].outer_try_index,
- list_[i].pc_offset,
- list_[i].needs_stacktrace,
- has_catch_all);
- handlers.SetHandledTypes(i, *list_[i].handler_types);
- }
- return handlers.raw();
- }
+ RawExceptionHandlers* FinalizeExceptionHandlers(uword entry_point) const;
private:
GrowableArray<struct HandlerDesc> list_;
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 6c45e4e..c087c0e 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -30,6 +30,7 @@
#include "vm/profiler.h"
#include "vm/resolver.h"
#include "vm/reusable_handles.h"
+#include "vm/service_event.h"
#include "vm/service_isolate.h"
#include "vm/service.h"
#include "vm/stack_frame.h"
@@ -1566,12 +1567,7 @@
ASSERT(isolate->GetAndClearResumeRequest() == false);
isolate->message_handler()->HandleOOBMessages();
- bool resume = isolate->GetAndClearResumeRequest();
- if (resume && Service::NeedsDebuggerEvents()) {
- DebuggerEvent resumeEvent(isolate, DebuggerEvent::kIsolateResumed);
- Service::HandleDebuggerEvent(&resumeEvent);
- }
- return resume;
+ return isolate->GetAndClearResumeRequest();
}
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index d9a8d97..1834063 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -19,6 +19,7 @@
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/port.h"
+#include "vm/service_event.h"
#include "vm/service_isolate.h"
#include "vm/service.h"
#include "vm/stack_frame.h"
@@ -116,7 +117,7 @@
function_ = func.raw();
token_pos_ = token_pos;
end_token_pos_ = token_pos;
- line_number_ = -1; // Recalcualte lazily.
+ line_number_ = -1; // Recalculate lazily.
is_resolved_ = true;
}
@@ -173,7 +174,6 @@
jsobj.AddPropertyF("id", "breakpoints/%" Pd "", id());
jsobj.AddProperty("breakpointNumber", id());
- jsobj.AddProperty("enabled", IsEnabled());
jsobj.AddProperty("resolved", IsResolved());
Library& library = Library::Handle(isolate);
@@ -230,13 +230,25 @@
// Give the event to the Service first, as the debugger event handler
// may go into a message loop and the Service will not.
- if (Service::NeedsDebuggerEvents()) {
- Service::HandleDebuggerEvent(event);
+ //
+ // kBreakpointResolved events are handled differently in the vm
+ // service, so suppress them here.
+ if (Service::NeedsDebuggerEvents() &&
+ (event->type() != DebuggerEvent::kBreakpointResolved)) {
+ ServiceEvent service_event(event);
+ Service::HandleEvent(&service_event);
}
if (event_handler_ != NULL) {
(*event_handler_)(event);
}
+
+ if (Service::NeedsDebuggerEvents() && event->IsPauseEvent()) {
+ // If we were paused, notify the service that we have resumed.
+ ServiceEvent service_event(event->isolate(), ServiceEvent::kResume);
+ service_event.set_top_frame(event->top_frame());
+ Service::HandleEvent(&service_event);
+ }
}
@@ -247,6 +259,7 @@
if (type == DebuggerEvent::kIsolateInterrupted) {
DebuggerStackTrace* trace = CollectStackTrace();
ASSERT(trace->Length() > 0);
+ event.set_top_frame(trace->FrameAt(0));
ASSERT(stack_trace_ == NULL);
stack_trace_ = trace;
resume_action_ = kContinue;
@@ -269,6 +282,18 @@
}
+// The vm service handles breakpoint notifications in a different way
+// than the regular debugger breakpoint notifications.
+static void SendServiceBreakpointEvent(ServiceEvent::EventType type,
+ SourceBreakpoint* bpt) {
+ if (Service::NeedsDebuggerEvents() /*&& !bpt->IsOneShot()*/) {
+ ServiceEvent service_event(Isolate::Current(), type);
+ service_event.set_breakpoint(bpt);
+ Service::HandleEvent(&service_event);
+ }
+}
+
+
const char* Debugger::QualifiedFunctionName(const Function& func) {
const String& func_name = String::Handle(func.name());
Class& func_class = Class::Handle(func.Owner());
@@ -509,47 +534,6 @@
}
-const char* DebuggerEvent::EventTypeToCString(EventType type) {
- switch (type) {
- case kBreakpointReached:
- return "BreakpointReached";
- case kBreakpointResolved:
- return "BreakpointResolved";
- case kExceptionThrown:
- return "ExceptionThrown";
- case kIsolateCreated:
- return "IsolateCreated";
- case kIsolateShutdown:
- return "IsolateShutdown";
- case kIsolateInterrupted:
- return "IsolateInterrupted";
- case kIsolateResumed:
- return "IsolateResumed";
- default:
- UNREACHABLE();
- return "Unknown";
- }
-}
-
-
-void DebuggerEvent::PrintJSON(JSONStream* js) const {
- JSONObject jsobj(js);
- jsobj.AddProperty("type", "ServiceEvent");
- // TODO(turnidge): Drop the 'id' for things like DebuggerEvent.
- jsobj.AddProperty("id", "");
- jsobj.AddProperty("eventType", EventTypeToCString(type()));
- jsobj.AddProperty("isolate", isolate());
- if ((type() == kBreakpointResolved || type() == kBreakpointReached) &&
- breakpoint() != NULL) {
- // TODO(turnidge): Make this a breakpoint ref.
- jsobj.AddProperty("breakpoint", breakpoint());
- }
- if (type() == kExceptionThrown) {
- jsobj.AddProperty("exception", *(exception()));
- }
-}
-
-
ActivationFrame* DebuggerStackTrace::GetHandlerFrame(
const Instance& exc_obj) const {
ExceptionHandlers& handlers = ExceptionHandlers::Handle();
@@ -744,7 +728,10 @@
ASSERT(i < desc_indices_.length());
intptr_t desc_index = desc_indices_[i];
ASSERT(name != NULL);
- *name ^= var_descriptors_.GetName(desc_index);
+
+ const String& tmp = String::Handle(var_descriptors_.GetName(desc_index));
+ *name ^= String::IdentifierPrettyName(tmp);
+
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(desc_index, &var_info);
ASSERT(token_pos != NULL);
@@ -888,6 +875,7 @@
void ActivationFrame::PrintToJSONObject(JSONObject* jsobj) {
const Script& script = Script::Handle(SourceScript());
+ jsobj->AddProperty("type", "Frame");
jsobj->AddProperty("script", script);
jsobj->AddProperty("tokenPos", TokenPos());
jsobj->AddProperty("function", function());
@@ -1411,6 +1399,8 @@
}
DebuggerEvent event(isolate_, DebuggerEvent::kExceptionThrown);
event.set_exception(&exc);
+ ASSERT(stack_trace->Length() > 0);
+ event.set_top_frame(stack_trace->FrameAt(0));
ASSERT(stack_trace_ == NULL);
stack_trace_ = stack_trace;
Pause(&event);
@@ -1732,6 +1722,7 @@
line_number);
}
SignalBpResolved(bpt);
+ SendServiceBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
return bpt;
}
}
@@ -1749,6 +1740,7 @@
if (bpt == NULL) {
bpt = new SourceBreakpoint(nextId(), script, token_pos, last_token_pos);
RegisterSourceBreakpoint(bpt);
+ SendServiceBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
}
bpt->Enable();
return bpt;
@@ -2373,6 +2365,7 @@
requested_end_pos);
}
SignalBpResolved(bpt);
+ SendServiceBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt);
}
ASSERT(bpt->IsResolved());
if (FLAG_verbose_debug) {
@@ -2530,6 +2523,7 @@
} else {
prev_bpt->set_next(curr_bpt->next());
}
+ SendServiceBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt);
// Remove references from code breakpoints to this source breakpoint,
// and disable the code breakpoints.
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 3f9c207..69b03d1 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -277,8 +277,6 @@
DebuggerStackTrace* stack);
-// TODO(turnidge): At some point we may want to turn this into a class
-// hierarchy.
class DebuggerEvent {
public:
enum EventType {
@@ -288,7 +286,6 @@
kIsolateCreated = 4,
kIsolateShutdown = 5,
kIsolateInterrupted = 6,
- kIsolateResumed = 7,
};
explicit DebuggerEvent(Isolate* isolate, EventType event_type)
@@ -302,12 +299,18 @@
EventType type() const { return type_; }
+ bool IsPauseEvent() const {
+ return (type_ == kBreakpointReached ||
+ type_ == kIsolateInterrupted ||
+ type_ == kExceptionThrown);
+ }
+
ActivationFrame* top_frame() const {
- ASSERT(type_ == kBreakpointReached);
+ ASSERT(IsPauseEvent());
return top_frame_;
}
void set_top_frame(ActivationFrame* frame) {
- ASSERT(type_ == kBreakpointReached);
+ ASSERT(IsPauseEvent());
top_frame_ = frame;
}
@@ -333,10 +336,6 @@
return isolate_->main_port();
}
- void PrintJSON(JSONStream* js) const;
-
- static const char* EventTypeToCString(EventType type);
-
private:
Isolate* isolate_;
EventType type_;
diff --git a/runtime/vm/debugger_test.cc b/runtime/vm/debugger_test.cc
index 4c3afa6..5545700 100644
--- a/runtime/vm/debugger_test.cc
+++ b/runtime/vm/debugger_test.cc
@@ -70,14 +70,14 @@
ExpectSubstringF(
js.ToCString(),
"[{\"type\":\"Breakpoint\",\"id\":\"breakpoints\\/2\","
- "\"breakpointNumber\":2,\"enabled\":true,\"resolved\":false,"
+ "\"breakpointNumber\":2,\"resolved\":false,"
"\"location\":{\"type\":\"Location\","
"\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\","
"\"kind\":\"script\"},\"tokenPos\":14}},"
"{\"type\":\"Breakpoint\",\"id\":\"breakpoints\\/1\","
- "\"breakpointNumber\":1,\"enabled\":true,\"resolved\":false,"
+ "\"breakpointNumber\":1,\"resolved\":false,"
"\"location\":{\"type\":\"Location\","
"\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index 6ece008..f3cbb88 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -231,7 +231,7 @@
StubCode::JumpToExceptionHandlerEntryPoint());
// Unpoison the stack before we tear it down in the generated stub code.
- uword current_sp = reinterpret_cast<uword>(&program_counter) - 1024;
+ uword current_sp = Isolate::GetCurrentStackPointer() - 1024;
ASAN_UNPOISON(reinterpret_cast<void*>(current_sp),
stack_pointer - current_sp);
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 86f27e0..e175da5 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -4292,7 +4292,10 @@
intptr_t outer_try_index = node->try_index();
owner()->set_try_index(outer_try_index);
}
- BuildRestoreContext(node->context_var());
+
+ // Note: do not restore the saved_try_context here since the inlined
+ // code is running at he context level of the return or jump instruction
+ // that follows the inlined code. See issue 22822.
JoinEntryInstr* finally_entry =
new(I) JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
diff --git a/runtime/vm/il_printer.cc b/runtime/vm/il_printer.cc
index c94497a..0d79149 100644
--- a/runtime/vm/il_printer.cc
+++ b/runtime/vm/il_printer.cc
@@ -485,12 +485,12 @@
void LoadLocalInstr::PrintOperandsTo(BufferFormatter* f) const {
- f->Print("%s", local().name().ToCString());
+ f->Print("%s @%d", local().name().ToCString(), local().index());
}
void StoreLocalInstr::PrintOperandsTo(BufferFormatter* f) const {
- f->Print("%s, ", local().name().ToCString());
+ f->Print("%s @%d, ", local().name().ToCString(), local().index());
value()->PrintTo(f);
}
@@ -1146,9 +1146,9 @@
f->Print(" ");
}
if (GetDeoptId() != Isolate::kNoDeoptId) {
- f->Print("goto:%" Pd " %" Pd "", GetDeoptId(), successor()->block_id());
+ f->Print("goto:%" Pd " B%" Pd "", GetDeoptId(), successor()->block_id());
} else {
- f->Print("goto: %" Pd "", successor()->block_id());
+ f->Print("goto: B%" Pd "", successor()->block_id());
}
}
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 214956a..d2a5f54 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -27,6 +27,7 @@
#include "vm/profiler.h"
#include "vm/reusable_handles.h"
#include "vm/service.h"
+#include "vm/service_event.h"
#include "vm/service_isolate.h"
#include "vm/simulator.h"
#include "vm/stack_frame.h"
@@ -114,6 +115,8 @@
const char* name() const;
void MessageNotify(Message::Priority priority);
bool HandleMessage(Message* message);
+ void NotifyPauseOnStart();
+ void NotifyPauseOnExit();
#if defined(DEBUG)
// Check that it is safe to access this handler.
@@ -431,6 +434,24 @@
}
+void IsolateMessageHandler::NotifyPauseOnStart() {
+ StartIsolateScope start_isolate(isolate());
+ StackZone zone(I);
+ HandleScope handle_scope(I);
+ ServiceEvent pause_event(isolate(), ServiceEvent::kPauseStart);
+ Service::HandleEvent(&pause_event);
+}
+
+
+void IsolateMessageHandler::NotifyPauseOnExit() {
+ StartIsolateScope start_isolate(isolate());
+ StackZone zone(I);
+ HandleScope handle_scope(I);
+ ServiceEvent pause_event(isolate(), ServiceEvent::kPauseExit);
+ Service::HandleEvent(&pause_event);
+}
+
+
#if defined(DEBUG)
void IsolateMessageHandler::CheckAccess() {
ASSERT(IsCurrentIsolate());
@@ -1541,32 +1562,29 @@
heap()->PrintToJSONObject(Heap::kOld, &jsheap);
}
- // TODO(turnidge): Don't compute a full stack trace every time we
- // request an isolate's info.
- DebuggerStackTrace* stack = debugger()->StackTrace();
- if (stack->Length() > 0) {
- JSONObject jsframe(&jsobj, "topFrame");
-
- ActivationFrame* frame = stack->FrameAt(0);
- frame->PrintToJSONObject(&jsobj);
- // TODO(turnidge): Implement depth differently -- differentiate
- // inlined frames.
- jsobj.AddProperty("depth", (intptr_t)0);
- }
jsobj.AddProperty("livePorts", message_handler()->live_ports());
jsobj.AddProperty("pauseOnExit", message_handler()->pause_on_exit());
- // TODO(turnidge): Make the debugger support paused_on_start/exit.
if (message_handler()->paused_on_start()) {
ASSERT(debugger()->PauseEvent() == NULL);
- DebuggerEvent pauseEvent(this, DebuggerEvent::kIsolateCreated);
- jsobj.AddProperty("pauseEvent", &pauseEvent);
+ ServiceEvent pause_event(this, ServiceEvent::kPauseStart);
+ jsobj.AddProperty("pauseEvent", &pause_event);
} else if (message_handler()->paused_on_exit()) {
ASSERT(debugger()->PauseEvent() == NULL);
- DebuggerEvent pauseEvent(this, DebuggerEvent::kIsolateShutdown);
- jsobj.AddProperty("pauseEvent", &pauseEvent);
+ ServiceEvent pause_event(this, ServiceEvent::kPauseExit);
+ jsobj.AddProperty("pauseEvent", &pause_event);
} else if (debugger()->PauseEvent() != NULL) {
- jsobj.AddProperty("pauseEvent", debugger()->PauseEvent());
+ ServiceEvent pause_event(debugger()->PauseEvent());
+ jsobj.AddProperty("pauseEvent", &pause_event);
+ } else {
+ ServiceEvent pause_event(this, ServiceEvent::kResume);
+
+ // TODO(turnidge): Don't compute a full stack trace.
+ DebuggerStackTrace* stack = debugger()->StackTrace();
+ if (stack->Length() > 0) {
+ pause_event.set_top_frame(stack->FrameAt(0));
+ }
+ jsobj.AddProperty("pauseEvent", &pause_event);
}
const Library& lib =
@@ -1610,6 +1628,10 @@
}
}
{
+ JSONArray breakpoints(&jsobj, "breakpoints");
+ debugger()->PrintBreakpointsToJSONArray(&breakpoints);
+ }
+ {
JSONArray features_array(&jsobj, "features");
if (is_io_enabled) {
features_array.AddValue("io");
diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc
index d4ae6a1..388c0d3 100644
--- a/runtime/vm/json_stream.cc
+++ b/runtime/vm/json_stream.cc
@@ -10,6 +10,7 @@
#include "vm/message.h"
#include "vm/metrics.h"
#include "vm/object.h"
+#include "vm/service_event.h"
#include "vm/unicode.h"
@@ -88,15 +89,20 @@
uint8_t* data = NULL;
MessageWriter writer(&data, &allocator, false);
writer.WriteMessage(reply);
- PortMap::PostMessage(new Message(port, data,
- writer.BytesWritten(),
- Message::kNormalPriority));
+ bool result = PortMap::PostMessage(new Message(port, data,
+ writer.BytesWritten(),
+ Message::kNormalPriority));
if (FLAG_trace_service) {
Isolate* isolate = Isolate::Current();
ASSERT(isolate != NULL);
const char* isolate_name = isolate->name();
- OS::Print("Isolate %s processed service request %s in %" Pd64" us.\n",
- isolate_name, method_, process_delta_micros);
+ if (result) {
+ OS::Print("Isolate %s processed service request %s in %" Pd64" us.\n",
+ isolate_name, method_, process_delta_micros);
+ } else {
+ OS::Print("Isolate %s FAILED to post response for service request %s.\n",
+ isolate_name, method_);
+ }
}
}
@@ -243,7 +249,7 @@
}
-void JSONStream::PrintValue(const DebuggerEvent* event) {
+void JSONStream::PrintValue(const ServiceEvent* event) {
PrintCommaIfNeeded();
event->PrintJSON(this);
}
@@ -305,7 +311,7 @@
}
-void JSONStream::PrintProperty(const char* name, const DebuggerEvent* event) {
+void JSONStream::PrintProperty(const char* name, const ServiceEvent* event) {
PrintPropertyName(name);
PrintValue(event);
}
diff --git a/runtime/vm/json_stream.h b/runtime/vm/json_stream.h
index b15c3d5..17792cc 100644
--- a/runtime/vm/json_stream.h
+++ b/runtime/vm/json_stream.h
@@ -11,17 +11,17 @@
namespace dart {
-class DebuggerEvent;
-class Field;
class Array;
+class Field;
class GrowableObjectArray;
class Instance;
class JSONArray;
class JSONObject;
+class Metric;
class Object;
+class ServiceEvent;
class SourceBreakpoint;
class String;
-class Metric;
class Zone;
class JSONStream : ValueObject {
@@ -86,7 +86,7 @@
void PrintfValue(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
void PrintValue(const Object& o, bool ref = true);
void PrintValue(SourceBreakpoint* bpt);
- void PrintValue(const DebuggerEvent* event);
+ void PrintValue(const ServiceEvent* event);
void PrintValue(Metric* metric);
void PrintValue(Isolate* isolate, bool ref = true);
bool PrintValueStr(const String& s, intptr_t limit);
@@ -102,7 +102,7 @@
PRINTF_ATTRIBUTE(3, 4);
void PrintProperty(const char* name, const Object& o, bool ref = true);
- void PrintProperty(const char* name, const DebuggerEvent* event);
+ void PrintProperty(const char* name, const ServiceEvent* event);
void PrintProperty(const char* name, SourceBreakpoint* bpt);
void PrintProperty(const char* name, Metric* metric);
void PrintProperty(const char* name, Isolate* isolate);
@@ -170,7 +170,7 @@
void AddProperty(const char* name, const Object& obj, bool ref = true) const {
stream_->PrintProperty(name, obj, ref);
}
- void AddProperty(const char* name, const DebuggerEvent* event) const {
+ void AddProperty(const char* name, const ServiceEvent* event) const {
stream_->PrintProperty(name, event);
}
void AddProperty(const char* name, SourceBreakpoint* bpt) const {
@@ -224,7 +224,7 @@
void AddValue(SourceBreakpoint* bpt) const {
stream_->PrintValue(bpt);
}
- void AddValue(const DebuggerEvent* event) const {
+ void AddValue(const ServiceEvent* event) const {
stream_->PrintValue(event);
}
void AddValue(Metric* metric) const {
diff --git a/runtime/vm/message_handler.cc b/runtime/vm/message_handler.cc
index 6ca2fb1..68344cb 100644
--- a/runtime/vm/message_handler.cc
+++ b/runtime/vm/message_handler.cc
@@ -38,6 +38,7 @@
paused_(0),
pause_on_start_(false),
pause_on_exit_(false),
+ paused_on_start_(false),
paused_on_exit_(false),
pool_(NULL),
task_(NULL),
@@ -93,33 +94,36 @@
void MessageHandler::PostMessage(Message* message, bool before_events) {
- MonitorLocker ml(&monitor_);
- if (FLAG_trace_isolates) {
- const char* source_name = "<native code>";
- Isolate* source_isolate = Isolate::Current();
- if (source_isolate) {
- source_name = source_isolate->name();
+ Message::Priority saved_priority;
+ {
+ MonitorLocker ml(&monitor_);
+ if (FLAG_trace_isolates) {
+ const char* source_name = "<native code>";
+ Isolate* source_isolate = Isolate::Current();
+ if (source_isolate) {
+ source_name = source_isolate->name();
+ }
+ OS::Print("[>] Posting message:\n"
+ "\tlen: %" Pd "\n"
+ "\tsource: %s\n"
+ "\tdest: %s\n"
+ "\tdest_port: %" Pd64 "\n",
+ message->len(), source_name, name(), message->dest_port());
}
- OS::Print("[>] Posting message:\n"
- "\tsource: %s\n"
- "\tdest: %s\n"
- "\tdest_port: %" Pd64 "\n",
- source_name, name(), message->dest_port());
- }
- Message::Priority saved_priority = message->priority();
- if (message->IsOOB()) {
- oob_queue_->Enqueue(message, before_events);
- } else {
- queue_->Enqueue(message, before_events);
- }
- message = NULL; // Do not access message. May have been deleted.
+ saved_priority = message->priority();
+ if (message->IsOOB()) {
+ oob_queue_->Enqueue(message, before_events);
+ } else {
+ queue_->Enqueue(message, before_events);
+ }
+ message = NULL; // Do not access message. May have been deleted.
- if (pool_ != NULL && task_ == NULL) {
- task_ = new MessageHandlerTask(this);
- pool_->Run(task_);
+ if (pool_ != NULL && task_ == NULL) {
+ task_ = new MessageHandlerTask(this);
+ pool_->Run(task_);
+ }
}
-
// Invoke any custom message notification.
MessageNotify(saved_priority);
}
@@ -146,11 +150,13 @@
Message::kNormalPriority : Message::kOOBPriority;
Message* message = DequeueMessage(min_priority);
while (message != NULL) {
+ intptr_t message_len = message->len();
if (FLAG_trace_isolates) {
OS::Print("[<] Handling message:\n"
+ "\tlen: %" Pd "\n"
"\thandler: %s\n"
"\tport: %" Pd64 "\n",
- name(), message->dest_port());
+ message_len, name(), message->dest_port());
}
// Release the monitor_ temporarily while we handle the message.
@@ -163,9 +169,10 @@
monitor_.Enter();
if (FLAG_trace_isolates) {
OS::Print("[.] Message handled:\n"
+ "\tlen: %" Pd "\n"
"\thandler: %s\n"
"\tport: %" Pd64 "\n",
- name(), saved_dest_port);
+ message_len, name(), saved_dest_port);
}
if (!result) {
// If we hit an error, we're done processing messages.
@@ -225,11 +232,17 @@
// if we have one. For an isolate, this will run the isolate's
// main() function.
if (pause_on_start()) {
+ if (!paused_on_start_) {
+ NotifyPauseOnStart();
+ paused_on_start_ = true;
+ }
HandleMessages(false, false);
if (pause_on_start()) {
// Still paused.
task_ = NULL; // No task in queue.
return;
+ } else {
+ paused_on_start_ = false;
}
}
@@ -251,11 +264,14 @@
if (!ok || !HasLivePorts()) {
if (pause_on_exit()) {
- if (FLAG_trace_service_pause_events && !paused_on_exit_) {
- OS::PrintErr("Isolate %s paused before exiting. "
+ if (!paused_on_exit_) {
+ if (FLAG_trace_service_pause_events) {
+ OS::PrintErr("Isolate %s paused before exiting. "
"Use the Observatory to release it.\n", name());
+ }
+ NotifyPauseOnExit();
+ paused_on_exit_ = true;
}
- paused_on_exit_ = true;
} else {
if (FLAG_trace_isolates) {
OS::Print("[-] Stopping message handler (%s):\n"
diff --git a/runtime/vm/message_handler.h b/runtime/vm/message_handler.h
index 0e08377..a2a07bf 100644
--- a/runtime/vm/message_handler.h
+++ b/runtime/vm/message_handler.h
@@ -81,7 +81,7 @@
bool paused_on_start() const {
// If pause_on_start_ is still set, tell the user we are paused,
// even if we haven't hit the pause point yet.
- return pause_on_start_;
+ return paused_on_start_;
}
bool pause_on_exit() const {
@@ -144,6 +144,9 @@
// Returns true on success.
virtual bool HandleMessage(Message* message) = 0;
+ virtual void NotifyPauseOnStart() {}
+ virtual void NotifyPauseOnExit() {}
+
private:
friend class PortMap;
friend class MessageHandlerTestPeer;
@@ -167,6 +170,7 @@
intptr_t paused_; // The number of pause messages received.
bool pause_on_start_;
bool pause_on_exit_;
+ bool paused_on_start_;
bool paused_on_exit_;
ThreadPool* pool_;
ThreadPool::Task* task_;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index ea3c6d9..dd85273 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1542,6 +1542,10 @@
cls = Class::New<MirrorReference>();
cls = Class::New<UserTag>();
+ const Context& context = Context::Handle(isolate,
+ Context::New(0, Heap::kOld));
+ object_store->set_empty_context(context);
+
StubCode::InitBootstrapStubs(isolate);
}
@@ -13461,20 +13465,21 @@
const char* UnhandledException::ToErrorCString() const {
Isolate* isolate = Isolate::Current();
- if (exception() == isolate->object_store()->out_of_memory()) {
- return "Unhandled exception:\nOut of memory";
- }
- if (exception() == isolate->object_store()->stack_overflow()) {
- return "Unhandled exception:\nStack overflow";
- }
HANDLESCOPE(isolate);
Object& strtmp = Object::Handle();
- const Instance& exc = Instance::Handle(exception());
- strtmp = DartLibraryCalls::ToString(exc);
- const char* exc_str =
- "<Received error while converting exception to string>";
- if (!strtmp.IsError()) {
- exc_str = strtmp.ToCString();
+ const char* exc_str;
+ if (exception() == isolate->object_store()->out_of_memory()) {
+ exc_str = "Out of Memory";
+ } else if (exception() == isolate->object_store()->stack_overflow()) {
+ exc_str = "Stack Overflow";
+ } else {
+ const Instance& exc = Instance::Handle(exception());
+ strtmp = DartLibraryCalls::ToString(exc);
+ if (!strtmp.IsError()) {
+ exc_str = strtmp.ToCString();
+ } else {
+ exc_str = "<Received error while converting exception to string>";
+ }
}
const Instance& stack = Instance::Handle(stacktrace());
strtmp = DartLibraryCalls::ToString(stack);
@@ -13483,11 +13488,8 @@
if (!strtmp.IsError()) {
stack_str = strtmp.ToCString();
}
-
const char* format = "Unhandled exception:\n%s\n%s";
- int len = (strlen(exc_str) + strlen(stack_str) + strlen(format)
- - 4 // Two '%s'
- + 1); // '\0'
+ intptr_t len = OS::SNPrint(NULL, 0, format, exc_str, stack_str);
char* chars = isolate->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, exc_str, stack_str);
return chars;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 0b0eb5e..7981b2b 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -3490,6 +3490,8 @@
class ExceptionHandlers : public Object {
public:
+ static const intptr_t kInvalidPcOffset = 0;
+
intptr_t num_entries() const;
void GetHandlerInfo(intptr_t try_index,
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index f79e6d4..05334bc 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -8157,12 +8157,15 @@
ExpectToken(Token::kWHILE);
ExpectToken(Token::kLPAREN);
SequenceNode* await_preamble = NULL;
- AstNode* cond_expr = ParseAwaitableExpr(
- kAllowConst, kConsumeCascades, &await_preamble);
- // No need for special handling of the await preamble as we can just append o
- // it to the loop body.
+ intptr_t expr_pos = TokenPos();
+ AstNode* cond_expr =
+ ParseAwaitableExpr(kAllowConst, kConsumeCascades, &await_preamble);
if (await_preamble != NULL) {
- dowhile_body->Add(await_preamble);
+ // Prepend the preamble to the condition.
+ LetNode* await_cond = new(Z) LetNode(expr_pos);
+ await_cond->AddNode(await_preamble);
+ await_cond->AddNode(cond_expr);
+ cond_expr = await_cond;
}
ExpectToken(Token::kRPAREN);
ExpectSemicolon();
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index b6e6018..dd3d124 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -174,7 +174,9 @@
intptr_t inclusive_ticks() const {
return inclusive_ticks_;
}
-
+ void inc_inclusive_ticks() {
+ inclusive_ticks_++;
+ }
intptr_t exclusive_ticks() const {
return exclusive_ticks_;
}
@@ -436,6 +438,9 @@
void set_inclusive_ticks(intptr_t inclusive_ticks) {
inclusive_ticks_ = inclusive_ticks;
}
+ void inc_inclusive_ticks() {
+ inclusive_ticks_++;
+ }
intptr_t exclusive_ticks() const { return exclusive_ticks_; }
void set_exclusive_ticks(intptr_t exclusive_ticks) {
@@ -1412,7 +1417,7 @@
dead_code_table_(dead_code_table),
tag_code_table_(tag_code_table),
function_table_(function_table),
- inclusive_(false),
+ inclusive_tree_(false),
trace_(false),
trace_code_filter_(NULL) {
ASSERT(live_code_table_ != NULL);
@@ -1439,9 +1444,9 @@
}
void VisitSample(Sample* sample) {
- inclusive_ = false;
+ inclusive_tree_ = false;
ProcessSampleExclusive(sample);
- inclusive_ = true;
+ inclusive_tree_ = true;
ProcessSampleInclusive(sample);
}
@@ -1457,6 +1462,12 @@
return tag_order_;
}
+ bool vm_tags_emitted() const {
+ return (tag_order_ == ProfilerService::kUserVM) ||
+ (tag_order_ == ProfilerService::kVMUser) ||
+ (tag_order_ == ProfilerService::kVM);
+ }
+
void set_tag_order(ProfilerService::TagOrder tag_order) {
tag_order_ = tag_order;
}
@@ -1468,6 +1479,7 @@
ProfileFunctionTrieNode* current = inclusive_root_;
current = AppendTags(sample, current);
if (sample->truncated_trace()) {
+ InclusiveTickTruncatedTag();
current = AppendTruncatedTag(current);
}
// Walk the sampled PCs.
@@ -1475,11 +1487,12 @@
if (sample->At(i) == 0) {
continue;
}
- // If we aren't sampled out of an exit frame and this is the top
- // frame.
- bool exclusive_tick = (i == 0) && !sample->exit_frame_sample();
- current = ProcessPC(sample->At(i), sample->timestamp(), current,
- visited(), exclusive_tick,
+ current = ProcessPC(sample->At(i),
+ sample->timestamp(),
+ current,
+ visited(),
+ (i == 0),
+ sample->exit_frame_sample() && (i == 0),
sample->missing_frame_inserted());
}
}
@@ -1494,11 +1507,12 @@
if (sample->At(i) == 0) {
break;
}
- // If we aren't sampled out of an exit frame and this is the top
- // frame.
- bool exclusive_tick = (i == 0) && !sample->exit_frame_sample();
- current = ProcessPC(sample->At(i), sample->timestamp(), current,
- visited(), exclusive_tick,
+ current = ProcessPC(sample->At(i),
+ sample->timestamp(),
+ current,
+ visited(),
+ (i == 0),
+ sample->exit_frame_sample() && (i == 0),
sample->missing_frame_inserted());
}
if (sample->truncated_trace()) {
@@ -1527,6 +1541,12 @@
return current;
}
+ void InclusiveTickTruncatedTag() {
+ intptr_t index = tag_code_table_->FindIndex(VMTag::kTruncatedTagId);
+ CodeRegion* region = tag_code_table_->At(index);
+ ProfileFunction* function = region->function();
+ function->inc_inclusive_ticks();
+ }
ProfileFunctionTrieNode* AppendVMTag(Sample* sample,
ProfileFunctionTrieNode* current) {
@@ -1624,10 +1644,12 @@
OS::Print("\n");
}
- ProfileFunctionTrieNode* ProcessPC(uword pc, int64_t timestamp,
+ ProfileFunctionTrieNode* ProcessPC(uword pc,
+ int64_t timestamp,
ProfileFunctionTrieNode* current,
intptr_t inclusive_serial,
- bool exclusive,
+ bool top_frame,
+ bool exit_frame,
bool missing_frame_inserted) {
CodeRegion* region = FindCodeObject(pc, timestamp);
if (region == NULL) {
@@ -1652,12 +1674,12 @@
OS::Print("[%" Px "] X - %s (%s)\n",
pc, function->name(), region_name);
}
- if (!inclusive_) {
- function->Tick(exclusive, exclusive ? -1 : inclusive_serial);
- }
- current = current->GetChild(function->index());
- current->AddCodeObjectIndex(code_index);
- current->Tick();
+ current = ProcessFunction(function,
+ current,
+ inclusive_serial,
+ top_frame,
+ exit_frame,
+ code_index);
if ((trace_code_filter_ != NULL) &&
(strstr(region_name, trace_code_filter_) != NULL)) {
trace_ = true;
@@ -1668,14 +1690,18 @@
return current;
}
- if (inclusive_) {
+ if (inclusive_tree_) {
for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) {
Function* inlined_function = inlined_functions[i];
ASSERT(inlined_function != NULL);
ASSERT(!inlined_function->IsNull());
- current = ProcessInlinedFunction(
- inlined_function, current, inclusive_serial, exclusive, code_index);
- exclusive = false;
+ current = ProcessInlinedFunction(inlined_function,
+ current,
+ inclusive_serial,
+ top_frame,
+ exit_frame,
+ code_index);
+ top_frame = false;
}
} else {
for (intptr_t i = 0; i < inlined_functions.length(); i++) {
@@ -1687,9 +1713,13 @@
OS::Print("[%" Px "] %" Pd " - %s (%s)\n",
pc, i, inline_name, region_name);
}
- current = ProcessInlinedFunction(
- inlined_function, current, inclusive_serial, exclusive, code_index);
- exclusive = false;
+ current = ProcessInlinedFunction(inlined_function,
+ current,
+ inclusive_serial,
+ top_frame,
+ exit_frame,
+ code_index);
+ top_frame = false;
if ((trace_code_filter_ != NULL) &&
(strstr(region_name, trace_code_filter_) != NULL)) {
trace_ = true;
@@ -1707,16 +1737,44 @@
Function* inlined_function,
ProfileFunctionTrieNode* current,
intptr_t inclusive_serial,
- bool exclusive,
+ bool top_frame,
+ bool exit_frame,
intptr_t code_index) {
ProfileFunction* function =
function_table_->LookupOrAdd(*inlined_function);
ASSERT(function != NULL);
+ return ProcessFunction(function,
+ current,
+ inclusive_serial,
+ top_frame,
+ exit_frame,
+ code_index);
+ }
+
+ ProfileFunctionTrieNode* ProcessFunction(ProfileFunction* function,
+ ProfileFunctionTrieNode* current,
+ intptr_t inclusive_serial,
+ bool top_frame,
+ bool exit_frame,
+ intptr_t code_index) {
+ const bool exclusive = top_frame && !exit_frame;
+ if (!inclusive_tree_) {
+ // We process functions for the inclusive and exclusive trees.
+ // Only tick the function for the exclusive tree.
+ function->Tick(exclusive, exclusive ? -1 : inclusive_serial);
+ }
function->AddCodeObjectIndex(code_index);
- function->Tick(exclusive, exclusive ? -1 : inclusive_serial);
+
current = current->GetChild(function->index());
current->AddCodeObjectIndex(code_index);
- current->Tick();
+ if (top_frame) {
+ if (!exit_frame || vm_tags_emitted()) {
+ // Only tick if this isn't an exit frame or VM tags are emitted.
+ current->Tick();
+ }
+ } else {
+ current->Tick();
+ }
return current;
}
@@ -1749,7 +1807,7 @@
CodeRegionTable* dead_code_table_;
CodeRegionTable* tag_code_table_;
ProfileFunctionTable* function_table_;
- bool inclusive_;
+ bool inclusive_tree_;
bool trace_;
const char* trace_code_filter_;
};
@@ -1892,6 +1950,12 @@
return tag_order_;
}
+ bool vm_tags_emitted() const {
+ return (tag_order_ == ProfilerService::kUserVM) ||
+ (tag_order_ == ProfilerService::kVMUser) ||
+ (tag_order_ == ProfilerService::kVM);
+ }
+
void set_tag_order(ProfilerService::TagOrder tag_order) {
tag_order_ = tag_order;
}
@@ -1934,7 +1998,16 @@
continue;
}
current = current->GetChild(index);
- current->Tick();
+ if (i == 0) {
+ // Executing PC.
+ if (!sample->exit_frame_sample() || vm_tags_emitted()) {
+ // Only tick if this isn't an exit frame or VM tags are emitted.
+ current->Tick();
+ }
+ } else {
+ // Caller PCs.
+ current->Tick();
+ }
}
if (sample->truncated_trace()) {
current = AppendTruncatedTag(current);
@@ -2136,7 +2209,8 @@
intptr_t total_live_code_objects = live_code_table.Length();
intptr_t total_dead_code_objects = dead_code_table.Length();
intptr_t total_tag_code_objects = tag_code_table.Length();
- OS::Print("Processed %" Pd " frames\n", frames);
+ OS::Print(
+ "Processed %" Pd " samples with %" Pd " frames\n", samples, frames);
OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n",
total_live_code_objects,
total_dead_code_objects,
@@ -2174,6 +2248,11 @@
code_trie_builder.exclusive_root()->SortByCount();
code_trie_builder.inclusive_root()->SortByCount();
}
+ if (FLAG_trace_profiler) {
+ OS::Print("Code Trie Root Count: E: %" Pd " I: %" Pd "\n",
+ code_trie_builder.exclusive_root()->count(),
+ code_trie_builder.inclusive_root()->count());
+ }
ProfileFunctionTrieBuilder function_trie_builder(isolate,
&live_code_table,
&dead_code_table,
@@ -2188,6 +2267,11 @@
function_trie_builder.exclusive_root()->SortByCount();
function_trie_builder.inclusive_root()->SortByCount();
}
+ if (FLAG_trace_profiler) {
+ OS::Print("Function Trie Root Count: E: %" Pd " I: %" Pd "\n",
+ function_trie_builder.exclusive_root()->count(),
+ function_trie_builder.inclusive_root()->count());
+ }
{
ScopeTimer sw("CpuProfileJSONStream", FLAG_trace_profiler);
// Serialize to JSON.
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 49d25ec..232ed6f 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -1366,23 +1366,26 @@
// Allocate context object.
int32_t num_vars = reader->Read<int32_t>();
- Context& context = Context::ZoneHandle(
- reader->isolate(), NEW_OBJECT_WITH_LEN(Context, num_vars));
+ Context& context = Context::ZoneHandle(reader->isolate());
reader->AddBackRef(object_id, &context, kIsDeserialized);
+ if (num_vars == 0) {
+ context ^= reader->object_store()->empty_context();
+ } else {
+ context ^= NEW_OBJECT_WITH_LEN(Context, num_vars);
- // Set the object tags.
- context.set_tags(tags);
+ // Set the object tags.
+ context.set_tags(tags);
- // Set all the object fields.
- // TODO(5411462): Need to assert No GC can happen here, even though
- // allocations may happen.
- intptr_t num_flds = (context.raw()->to(num_vars) - context.raw()->from());
- for (intptr_t i = 0; i <= num_flds; i++) {
- (*reader->PassiveObjectHandle()) = reader->ReadObjectRef();
- context.StorePointer((context.raw()->from() + i),
- reader->PassiveObjectHandle()->raw());
+ // Set all the object fields.
+ // TODO(5411462): Need to assert No GC can happen here, even though
+ // allocations may happen.
+ intptr_t num_flds = (context.raw()->to(num_vars) - context.raw()->from());
+ for (intptr_t i = 0; i <= num_flds; i++) {
+ (*reader->PassiveObjectHandle()) = reader->ReadObjectRef();
+ context.StorePointer((context.raw()->from() + i),
+ reader->PassiveObjectHandle()->raw());
+ }
}
-
return context.raw();
}
@@ -1400,11 +1403,13 @@
writer->WriteTags(writer->GetObjectTags(this));
// Write out num of variables in the context.
- writer->Write<int32_t>(ptr()->num_variables_);
-
- // Write out all the object pointer fields.
- SnapshotWriterVisitor visitor(writer);
- visitor.VisitPointers(from(), to(ptr()->num_variables_));
+ int32_t num_variables = ptr()->num_variables_;
+ writer->Write<int32_t>(num_variables);
+ if (num_variables != 0) {
+ // Write out all the object pointer fields.
+ SnapshotWriterVisitor visitor(writer);
+ visitor.VisitPointers(from(), to(num_variables));
+ }
}
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index f62e38f..b8cea45 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -27,6 +27,7 @@
#include "vm/port.h"
#include "vm/profiler_service.h"
#include "vm/reusable_handles.h"
+#include "vm/service_event.h"
#include "vm/service_isolate.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
@@ -462,6 +463,10 @@
}
if (method->entry(isolate, &js)) {
js.PostReply();
+ } else {
+ // NOTE(turnidge): All message handlers currently return true,
+ // so this case shouldn't be reached, at present.
+ UNIMPLEMENTED();
}
return;
}
@@ -501,13 +506,18 @@
}
+bool Service::NeedsEvents() {
+ return ServiceIsolate::IsRunning();
+}
+
+
bool Service::NeedsDebuggerEvents() {
- return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyDebugMask);
+ return NeedsEvents() && EventMaskHas(kEventFamilyDebugMask);
}
bool Service::NeedsGCEvents() {
- return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyGCMask);
+ return NeedsEvents() && EventMaskHas(kEventFamilyGCMask);
}
@@ -516,7 +526,9 @@
}
-void Service::SendEvent(intptr_t eventId, const Object& eventMessage) {
+void Service::SendEvent(intptr_t eventFamilyId,
+ intptr_t eventType,
+ const Object& eventMessage) {
if (!ServiceIsolate::IsRunning()) {
return;
}
@@ -524,10 +536,12 @@
ASSERT(isolate != NULL);
HANDLESCOPE(isolate);
- // Construct a list of the form [eventId, eventMessage].
+ // Construct a list of the form [eventFamilyId, eventMessage].
+ //
+ // TODO(turnidge): Revisit passing the eventFamilyId here at all.
const Array& list = Array::Handle(Array::New(2));
ASSERT(!list.IsNull());
- list.SetAt(0, Integer::Handle(Integer::New(eventId)));
+ list.SetAt(0, Integer::Handle(Integer::New(eventFamilyId)));
list.SetAt(1, eventMessage);
// Push the event to port_.
@@ -537,7 +551,7 @@
intptr_t len = writer.BytesWritten();
if (FLAG_trace_service) {
OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n",
- eventId, len);
+ eventType, len);
}
// TODO(turnidge): For now we ignore failure to send an event. Revisit?
PortMap::PostMessage(
@@ -545,7 +559,7 @@
}
-void Service::SendEvent(intptr_t eventId,
+void Service::SendEvent(intptr_t eventFamilyId,
const String& meta,
const uint8_t* data,
intptr_t size) {
@@ -571,7 +585,8 @@
offset += size;
}
ASSERT(offset == total_bytes);
- SendEvent(eventId, message);
+ // TODO(turnidge): Pass the real eventType here.
+ SendEvent(eventFamilyId, 0, message);
}
@@ -579,15 +594,16 @@
JSONStream js;
event->PrintJSON(&js);
const String& message = String::Handle(String::New(js.ToCString()));
- SendEvent(kEventFamilyGC, message);
+ // TODO(turnidge): Pass the real eventType here.
+ SendEvent(kEventFamilyGC, 0, message);
}
-void Service::HandleDebuggerEvent(DebuggerEvent* event) {
+void Service::HandleEvent(ServiceEvent* event) {
JSONStream js;
event->PrintJSON(&js);
const String& message = String::Handle(String::New(js.ToCString()));
- SendEvent(kEventFamilyDebug, message);
+ SendEvent(kEventFamilyDebug, event->type(), message);
}
@@ -1513,6 +1529,32 @@
}
+static const MethodParameter* eval_frame_params[] = {
+ ISOLATE_PARAMETER,
+ new UIntParameter("frame", true),
+ new MethodParameter("expression", true),
+ NULL,
+};
+
+
+static bool EvalFrame(Isolate* isolate, JSONStream* js) {
+ DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
+ intptr_t framePos = UIntParameter::Parse(js->LookupParam("frame"));
+ if (framePos > stack->Length()) {
+ PrintInvalidParamError(js, "frame");
+ return true;
+ }
+ ActivationFrame* frame = stack->FrameAt(framePos);
+
+ const char* expr = js->LookupParam("expression");
+ const String& expr_str = String::Handle(isolate, String::New(expr));
+
+ const Object& result = Object::Handle(frame->Evaluate(expr_str));
+ result.PrintJSON(js, true);
+ return true;
+}
+
+
static const MethodParameter* get_call_site_data_params[] = {
ISOLATE_PARAMETER,
new IdParameter("targetId", true),
@@ -2016,8 +2058,8 @@
jsobj.AddProperty("type", "Success");
jsobj.AddProperty("id", "");
{
- DebuggerEvent resumeEvent(isolate, DebuggerEvent::kIsolateResumed);
- Service::HandleDebuggerEvent(&resumeEvent);
+ ServiceEvent event(isolate, ServiceEvent::kResume);
+ Service::HandleEvent(&event);
}
return true;
}
@@ -2054,21 +2096,6 @@
}
-static const MethodParameter* get_breakpoints_params[] = {
- ISOLATE_PARAMETER,
- NULL,
-};
-
-
-static bool GetBreakpoints(Isolate* isolate, JSONStream* js) {
- JSONObject jsobj(js);
- jsobj.AddProperty("type", "BreakpointList");
- JSONArray jsarr(&jsobj, "breakpoints");
- isolate->debugger()->PrintBreakpointsToJSONArray(&jsarr);
- return true;
-}
-
-
static const MethodParameter* pause_params[] = {
ISOLATE_PARAMETER,
NULL,
@@ -2515,10 +2542,10 @@
clear_cpu_profile_params },
{ "eval", Eval,
eval_params },
+ { "evalFrame", EvalFrame,
+ eval_frame_params },
{ "getAllocationProfile", GetAllocationProfile,
get_allocation_profile_params },
- { "getBreakpoints", GetBreakpoints,
- get_breakpoints_params },
{ "getCallSiteData", GetCallSiteData,
get_call_site_data_params },
{ "getClassList", GetClassList,
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 2a9078f..c63f15d 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -13,7 +13,6 @@
namespace dart {
class Array;
-class DebuggerEvent;
class EmbedderServiceHandler;
class GCEvent;
class Instance;
@@ -21,6 +20,7 @@
class JSONStream;
class Object;
class RawInstance;
+class ServiceEvent;
class String;
class Service : public AllStatic {
@@ -33,10 +33,11 @@
static bool EventMaskHas(uint32_t mask);
static void SetEventMask(uint32_t mask);
+ static bool NeedsEvents();
static bool NeedsDebuggerEvents();
static bool NeedsGCEvents();
- static void HandleDebuggerEvent(DebuggerEvent* event);
+ static void HandleEvent(ServiceEvent* event);
static void HandleGCEvent(GCEvent* event);
static void RegisterIsolateEmbedderCallback(
@@ -67,7 +68,9 @@
static EmbedderServiceHandler* FindIsolateEmbedderHandler(const char* name);
static EmbedderServiceHandler* FindRootEmbedderHandler(const char* name);
- static void SendEvent(intptr_t eventId, const Object& eventMessage);
+ static void SendEvent(intptr_t eventFamilyId,
+ intptr_t eventType,
+ const Object& eventMessage);
// Does not take ownership of 'data'.
static void SendEvent(intptr_t eventId,
const String& meta,
diff --git a/runtime/vm/service/client.dart b/runtime/vm/service/client.dart
index a9cbd38..1fd4f44 100644
--- a/runtime/vm/service/client.dart
+++ b/runtime/vm/service/client.dart
@@ -25,12 +25,16 @@
/// Call to process a message. Response will be posted with 'seq'.
void onMessage(var seq, Message message) {
- // Call post when the response arrives.
- message.response.then((response) {
- post(seq, response);
- });
- // Send message to service.
- service.route(message);
+ try {
+ // Send message to service.
+ service.route(message).then((response) {
+ // Call post when the response arrives.
+ post(seq, response);
+ });
+ } catch (e, st) {
+ message.setErrorResponse('Internal error: $e');
+ post(seq, message.response);
+ }
}
/// When implementing, responsible for sending [response] to the client.
diff --git a/runtime/vm/service/message.dart b/runtime/vm/service/message.dart
index 2b3f038..b465499 100644
--- a/runtime/vm/service/message.dart
+++ b/runtime/vm/service/message.dart
@@ -10,6 +10,9 @@
/// Future of response.
Future<String> get response => _completer.future;
+ // Client-side identifier for this message.
+ final serial;
+
// In new messages.
final String method;
@@ -30,8 +33,8 @@
});
}
- Message.fromJsonRpc(this.method, Map rpcParams) {
- params.addAll(rpcParams);
+ Message.fromJsonRpc(Map map) : serial = map['id'], method = map['method'] {
+ params.addAll(map['params']);
}
static String _methodNameFromUri(Uri uri) {
@@ -76,11 +79,8 @@
final receivePort = new RawReceivePort();
receivePort.handler = (value) {
receivePort.close();
- if (value is Exception) {
- _completer.completeError(value);
- } else {
- _completer.complete(value);
- }
+ assert(value is String);
+ _completer.complete(value);
};
var keys = _makeAllString(params.keys.toList(growable:false));
var values = _makeAllString(params.values.toList(growable:false));
@@ -90,7 +90,14 @@
..[2] = method
..[3] = keys
..[4] = values;
- sendIsolateServiceMessage(sendPort, request);
+ if (!sendIsolateServiceMessage(sendPort, request)) {
+ _completer.complete(JSON.encode({
+ 'type': 'ServiceError',
+ 'id': '',
+ 'kind': 'InternalError',
+ 'message': 'could not send message [${serial}] to isolate',
+ }));
+ }
return _completer.future;
}
@@ -98,11 +105,8 @@
final receivePort = new RawReceivePort();
receivePort.handler = (value) {
receivePort.close();
- if (value is Exception) {
- _completer.completeError(value);
- } else {
- _completer.complete(value);
- }
+ assert(value is String);
+ _completer.complete(value);
};
var keys = _makeAllString(params.keys.toList(growable:false));
var values = _makeAllString(params.values.toList(growable:false));
@@ -132,7 +136,7 @@
}
}
-void sendIsolateServiceMessage(SendPort sp, List m)
+bool sendIsolateServiceMessage(SendPort sp, List m)
native "VMService_SendIsolateServiceMessage";
void sendRootServiceMessage(List m)
diff --git a/runtime/vm/service/service.idl b/runtime/vm/service/service.idl
index 2764e23..1b825a19 100644
--- a/runtime/vm/service/service.idl
+++ b/runtime/vm/service/service.idl
@@ -14,9 +14,6 @@
// The response is a subtype of Object or ObjectRef.
getObjectByAddress(address string, ref bool) Response
- // Returns the list of breakpoints for an isolate.
- getBreakpoints(isolateId string) BreakpointList
-
// Adds a breakpoint at the specified line.
//
// TODO(turnidge): Make line an int instead of a string.
@@ -177,7 +174,6 @@
// A <code>Breakpoint</code> describes a debugger breakpoint.
struct Breakpoint extends Object {
breakpointNumber int
- enabled bool
resolved bool
location Location
}
@@ -308,12 +304,6 @@
}
-// A list of <code>Breakpoint</code>
-struct BreakpointList extends Response {
- breakpoints []Breakpoint
-}
-
-
// An <code>AllocationProfile</code> encodes an allocation profile.
struct AllocationProfile extends Response {
todo int
diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc
new file mode 100644
index 0000000..9f7c63d
--- /dev/null
+++ b/runtime/vm/service_event.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "vm/service_event.h"
+
+namespace dart {
+
+// Translate from the legacy DebugEvent to a ServiceEvent.
+static ServiceEvent::EventType TranslateEventType(
+ DebuggerEvent::EventType type) {
+ switch (type) {
+ case DebuggerEvent::kIsolateCreated:
+ return ServiceEvent::kIsolateStart;
+
+ case DebuggerEvent::kIsolateShutdown:
+ return ServiceEvent::kIsolateExit;
+
+ case DebuggerEvent::kBreakpointReached:
+ return ServiceEvent::kPauseBreakpoint;
+
+ case DebuggerEvent::kIsolateInterrupted:
+ return ServiceEvent::kPauseInterrupted;
+
+ case DebuggerEvent::kExceptionThrown:
+ return ServiceEvent::kPauseException;
+
+ default:
+ UNREACHABLE();
+ return ServiceEvent::kIllegal;
+ }
+}
+
+ServiceEvent::ServiceEvent(const DebuggerEvent* debugger_event)
+ : isolate_(debugger_event->isolate()),
+ type_(TranslateEventType(debugger_event->type())),
+ breakpoint_(NULL),
+ top_frame_(NULL),
+ exception_(NULL) {
+ DebuggerEvent::EventType type = debugger_event->type();
+ if (type == DebuggerEvent::kBreakpointReached) {
+ set_breakpoint(debugger_event->breakpoint());
+ }
+ if (type == DebuggerEvent::kExceptionThrown) {
+ set_exception(debugger_event->exception());
+ }
+ if (type == DebuggerEvent::kBreakpointReached ||
+ type == DebuggerEvent::kIsolateInterrupted ||
+ type == DebuggerEvent::kExceptionThrown) {
+ set_top_frame(debugger_event->top_frame());
+ }
+}
+
+
+const char* ServiceEvent::EventTypeToCString(EventType type) {
+ switch (type) {
+ case kIsolateStart:
+ return "IsolateStart";
+ case kIsolateExit:
+ return "IsolateExit";
+ case kPauseStart:
+ return "PauseStart";
+ case kPauseExit:
+ return "PauseExit";
+ case kPauseBreakpoint:
+ return "PauseBreakpoint";
+ case kPauseInterrupted:
+ return "PauseInterrupted";
+ case kPauseException:
+ return "PauseException";
+ case kResume:
+ return "Resume";
+ case kBreakpointAdded:
+ return "BreakpointAdded";
+ case kBreakpointResolved:
+ return "BreakpointResolved";
+ case kBreakpointRemoved:
+ return "BreakpointRemoved";
+ default:
+ UNREACHABLE();
+ return "Unknown";
+ }
+}
+
+
+void ServiceEvent::PrintJSON(JSONStream* js) const {
+ JSONObject jsobj(js);
+ jsobj.AddProperty("type", "ServiceEvent");
+ jsobj.AddProperty("eventType", EventTypeToCString(type()));
+ jsobj.AddProperty("isolate", isolate());
+ if (breakpoint() != NULL) {
+ jsobj.AddProperty("breakpoint", breakpoint());
+ }
+ if (top_frame() != NULL) {
+ JSONObject jsFrame(&jsobj, "topFrame");
+ top_frame()->PrintToJSONObject(&jsFrame);
+ }
+ if (exception() != NULL) {
+ jsobj.AddProperty("exception", *(exception()));
+ }
+}
+
+} // namespace dart
diff --git a/runtime/vm/service_event.h b/runtime/vm/service_event.h
new file mode 100644
index 0000000..b55d0a8
--- /dev/null
+++ b/runtime/vm/service_event.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef VM_SERVICE_EVENT_H_
+#define VM_SERVICE_EVENT_H_
+
+#include "vm/debugger.h"
+
+class DebuggerEvent;
+
+namespace dart {
+
+class ServiceEvent {
+ public:
+ enum EventType {
+ kIsolateStart, // New isolate has started
+ kIsolateExit, // Isolate has exited
+
+ kPauseStart, // --pause-isolates-on-start
+ kPauseExit, // --pause-isolates-on-exit
+ kPauseBreakpoint,
+ kPauseInterrupted,
+ kPauseException,
+ kResume,
+
+ kBreakpointAdded,
+ kBreakpointResolved,
+ kBreakpointRemoved,
+
+ kIllegal,
+ };
+
+ ServiceEvent(Isolate* isolate, EventType event_type)
+ : isolate_(isolate),
+ type_(event_type),
+ breakpoint_(NULL),
+ top_frame_(NULL),
+ exception_(NULL) {}
+
+ explicit ServiceEvent(const DebuggerEvent* debugger_event);
+
+ Isolate* isolate() const { return isolate_; }
+
+ EventType type() const { return type_; }
+
+ SourceBreakpoint* breakpoint() const {
+ return breakpoint_;
+ }
+ void set_breakpoint(SourceBreakpoint* bpt) {
+ ASSERT(type() == kPauseBreakpoint ||
+ type() == kBreakpointAdded ||
+ type() == kBreakpointResolved ||
+ type() == kBreakpointRemoved);
+ breakpoint_ = bpt;
+ }
+
+ ActivationFrame* top_frame() const {
+ return top_frame_;
+ }
+ void set_top_frame(ActivationFrame* frame) {
+ ASSERT(type() == kPauseBreakpoint ||
+ type() == kPauseInterrupted ||
+ type() == kPauseException ||
+ type() == kResume);
+ top_frame_ = frame;
+ }
+
+ const Object* exception() const {
+ return exception_;
+ }
+ void set_exception(const Object* exception) {
+ ASSERT(type_ == kPauseException);
+ exception_ = exception;
+ }
+
+ void PrintJSON(JSONStream* js) const;
+
+ static const char* EventTypeToCString(EventType type);
+
+ private:
+ Isolate* isolate_;
+ EventType type_;
+ SourceBreakpoint* breakpoint_;
+ ActivationFrame* top_frame_;
+ const Object* exception_;
+};
+
+} // namespace dart
+
+#endif // VM_SERVICE_EVENT_H_
diff --git a/runtime/vm/service_isolate.cc b/runtime/vm/service_isolate.cc
index fd82c5a9..650b040 100644
--- a/runtime/vm/service_isolate.cc
+++ b/runtime/vm/service_isolate.cc
@@ -219,8 +219,10 @@
writer.WriteMessage(message);
// TODO(turnidge): Throw an exception when the return value is false?
- PortMap::PostMessage(new Message(sp.Id(), data, writer.BytesWritten(),
- Message::kOOBPriority));
+ bool result = PortMap::PostMessage(
+ new Message(sp.Id(), data, writer.BytesWritten(),
+ Message::kOOBPriority));
+ arguments->SetReturn(Bool::Get(result));
}
static void SendRootServiceMessage(Dart_NativeArguments args) {
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index a3544ad..8ef91b4 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -243,8 +243,9 @@
// First create a function object and associate it with the specified
// 'object_id'.
- Function& func = Function::ZoneHandle(isolate(), Function::null());
- AddBackRef(object_id, &func, kIsDeserialized);
+ Function& func = Function::Handle(isolate(), Function::null());
+ Instance& obj = Instance::ZoneHandle(isolate(), Instance::null());
+ AddBackRef(object_id, &obj, kIsDeserialized);
// Read the library/class/function information and lookup the function.
str_ ^= ReadObjectImpl();
@@ -273,7 +274,8 @@
ASSERT(!func.IsNull());
// Return the associated implicit static closure.
- return func.ImplicitStaticClosure();
+ obj = func.ImplicitStaticClosure();
+ return obj.raw();
}
diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi
index 5eaba92..d2dc6b5 100644
--- a/runtime/vm/vm_sources.gypi
+++ b/runtime/vm/vm_sources.gypi
@@ -384,6 +384,8 @@
'scopes_test.cc',
'service.cc',
'service.h',
+ 'service_event.cc',
+ 'service_event.h',
'service_isolate.cc',
'service_isolate.h',
'service_test.cc',
diff --git a/sdk/lib/_internal/compiler/js_lib/js_helper.dart b/sdk/lib/_internal/compiler/js_lib/js_helper.dart
index 89766d1..4b5a2ad 100644
--- a/sdk/lib/_internal/compiler/js_lib/js_helper.dart
+++ b/sdk/lib/_internal/compiler/js_lib/js_helper.dart
@@ -3734,7 +3734,7 @@
Stream get stream => controller.stream;
bool stopRunning = false;
bool isAdding = false;
- bool get isPaused => controller.isPaused;
+ bool isPaused = false;
add(event) => controller.add(event);
addStream(Stream stream) {
return controller.addStream(stream, cancelOnError: false);
@@ -3751,12 +3751,16 @@
wrapped(null);
});
},
- onResume: () {
+ onPause: () {
+ isPaused = true;
+ }, onResume: () {
+ isPaused = false;
if (!isAdding) {
asyncStarHelper(null, body, this);
}
}, onCancel: () {
stopRunning = true;
+ if (isPaused) asyncStarHelper(null, body, this);
});
}
}
diff --git a/tests/compiler/dart2js/async_await_js_transform_test.dart b/tests/compiler/dart2js/async_await_js_transform_test.dart
index be164df6..a193616 100644
--- a/tests/compiler/dart2js/async_await_js_transform_test.dart
+++ b/tests/compiler/dart2js/async_await_js_transform_test.dart
@@ -95,42 +95,40 @@
// Function start
__handler = 3;
case 7:
- // continue __outer
- case 8:
// while condition
- __handler = 10;
+ __handler = 9;
inner:
while (true) {
__next = [6];
// goto finally
- __goto = 11;
+ __goto = 10;
break __outer1;
break;
}
while (true) {
__next = [1, 4];
// goto finally
- __goto = 11;
+ __goto = 10;
break __outer1;
}
- __goto = 13;
+ __goto = 12;
return thenHelper(foo(), __body, __completer);
- case 13:
+ case 12:
// returning from await.
__helper = __result;
- __next.push(12);
+ __next.push(11);
// goto finally
- __goto = 11;
+ __goto = 10;
break;
- case 10:
+ case 9:
// uncaught
__next = [3];
- case 11:
+ case 10:
// finally
__handler = 3;
foo();
// goto while condition
- __goto = 8;
+ __goto = 7;
break;
__returnValue = 2;
__next = [1];
@@ -140,12 +138,12 @@
// goto the next finally handler
__goto = __next.pop();
break;
- case 12:
+ case 11:
// after finally
// goto while condition
- __goto = 8;
+ __goto = 7;
break;
- case 9:
+ case 8:
// after while
case 6:
// break __outer
@@ -923,45 +921,55 @@
break;
}
case 7:
- // continue lab
- case 8:
// switch
__temp1 = y;
if (__temp1 === 0) {
// goto case
+ __goto = 9;
+ break;
+ }
+ if (__temp1 === 0) {
+ // goto case
__goto = 10;
break;
}
- if (__temp1 === 0) {
+ __goto = 12;
+ return thenHelper(bar(), __body, __completer);
+ case 12:
+ // returning from await.
+ if (__temp1 === __result) {
// goto case
__goto = 11;
break;
}
- __goto = 13;
- return thenHelper(bar(), __body, __completer);
- case 13:
- // returning from await.
- if (__temp1 === __result) {
- // goto case
- __goto = 12;
- break;
- }
if (__temp1 === x) {
// goto case
- __goto = 14;
+ __goto = 13;
break;
}
// goto default
- __goto = 15;
+ __goto = 14;
break;
- case 10:
+ case 9:
// case
foo();
+ case 10:
+ // case
+ __temp1 = print;
+ __goto = 15;
+ return thenHelper(foo1(x), __body, __completer);
+ case 15:
+ // returning from await.
+ __temp1(__result);
+ __returnValue = y;
+ // goto return
+ __goto = 1;
+ break;
case 11:
// case
__temp1 = print;
__goto = 16;
- return thenHelper(foo1(x), __body, __completer);
+ return thenHelper(foobar(x), __body, __completer);
case 16:
// returning from await.
__temp1(__result);
@@ -969,19 +977,7 @@
// goto return
__goto = 1;
break;
- case 12:
- // case
- __temp1 = print;
- __goto = 17;
- return thenHelper(foobar(x), __body, __completer);
- case 17:
- // returning from await.
- __temp1(__result);
- __returnValue = y;
- // goto return
- __goto = 1;
- break;
- case 14:
+ case 13:
// case
if (a)
throw new Error();
@@ -990,12 +986,12 @@
__goto = 3;
break;
}
- case 15:
+ case 14:
// default
// goto break lab
__goto = 6;
break;
- case 9:
+ case 8:
// after switch
foo();
case 6:
diff --git a/tests/isolate/issue_22778_test.dart b/tests/isolate/issue_22778_test.dart
new file mode 100644
index 0000000..d9e3973
--- /dev/null
+++ b/tests/isolate/issue_22778_test.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:isolate";
+import "package:expect/expect.dart";
+
+func(){}
+
+main() {
+ var r = new RawReceivePort();
+ r.handler = (v) {
+ Expect.isTrue(v[0] == v[1]);
+ r.close();
+ };
+ r.sendPort.send([func, func]);
+}
diff --git a/tests/language/async_and_or_test.dart b/tests/language/async_and_or_test.dart
index 78a3758..15955f0 100644
--- a/tests/language/async_and_or_test.dart
+++ b/tests/language/async_and_or_test.dart
@@ -4,8 +4,8 @@
import "package:expect/expect.dart";
import "package:async_helper/async_helper.dart";
-@NoInline
-@AssumeDynamic
+@NoInline()
+@AssumeDynamic()
confuse(x) {
return x;
}
diff --git a/tests/language/async_continue_label_test.dart b/tests/language/async_continue_label_test.dart
new file mode 100644
index 0000000..d2d7cc8
--- /dev/null
+++ b/tests/language/async_continue_label_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+// Two loop variables
+test1() async {
+ var r = 0;
+ label:
+ for(var i = 1, j =
+ await /// await_in_init: ok
+ 10; i < 10 && j >
+ await /// await_in_condition: ok
+ -5; j--, i +=
+ await /// await_in_update: ok
+ 1) {
+ if (i <
+ await /// await_in_body: ok
+ 5 || j < -5) {
+ continue label;
+ }
+ r++;
+ }
+ Expect.equals(5, r);
+}
+
+// One loop variable
+test2() async {
+ var r = 0;
+ label:
+ for(var i =
+ await /// await_in_init: ok
+ 0; i <
+ await /// await_in_condition: ok
+ 10; i +=
+ await /// await_in_update: ok
+ 1) {
+ if (i <
+ await /// await_in_body: ok
+ 5) {
+ continue label;
+ }
+ r++;
+ }
+ Expect.equals(5, r);
+}
+
+// Variable not declared in initializer;
+test3() async {
+ var r = 0, i, j;
+ label:
+ for(i =
+ await /// await_in_init: ok
+ 0; i <
+ await /// await_in_condition: ok
+ 10; i +=
+ await /// await_in_update: ok
+ 1) {
+ if (i <
+ await /// await_in_body: ok
+ 5) {
+ continue label;
+ }
+ r++;
+ }
+ Expect.equals(5, r);
+}
+
+// Nested loop
+test4() async {
+ var r = 0;
+ label:
+ for(var i =
+ await /// await_in_init: ok
+ 0; i <
+ await /// await_in_condition: ok
+ 10; i+=
+ await /// await_in_update: ok
+ 1) {
+ if (i <
+ await /// await_in_body: ok
+ 5) {
+ for (int i = 0; i < 10; i++) {
+ continue label;
+ }
+ }
+ r++;
+ }
+ Expect.equals(5, r);
+}
+
+test() async {
+ await test1();
+ await test2();
+ await test3();
+ await test4();
+}
+
+main() {
+ asyncStart();
+ test().then((_) => asyncEnd());
+}
diff --git a/tests/language/issue22800_test.dart b/tests/language/issue22800_test.dart
new file mode 100644
index 0000000..035aa31
--- /dev/null
+++ b/tests/language/issue22800_test.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Check proper exception handler finalization, even for unreachable handlers.
+
+void main() {
+ try {
+ print("Starting here");
+ throw 0;
+ try {
+ } catch (e) {
+ }
+ } catch (e) {
+ print("Caught in here: $e");
+ }
+ try {
+ } catch (e) {
+ }
+}
diff --git a/tests/language/issue_22780_test.dart b/tests/language/issue_22780_test.dart
new file mode 100644
index 0000000..6e54dcf
--- /dev/null
+++ b/tests/language/issue_22780_test.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+main() {
+ f() => "Oh, the joy of ${f()}"; print(f()); /// 01: runtime error
+}
diff --git a/tests/language/language.status b/tests/language/language.status
index 194f526..a4b2459 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -45,7 +45,6 @@
async_throw_in_catch_test: Crash, RuntimeError # Issue 21404 or it could be a test error
[ $compiler == none && ($runtime == drt || $runtime == dartium|| $runtime == ContentShellOnAndroid) ]
-async_throw_in_catch_test: Timeout, Fail # Issue 21404 or it could be a test error
[ $compiler == none && $runtime == vm ]
class_keyword_test/02: MissingCompileTimeError # Issue 13627
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 122beb3..5ccdf36 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -7,6 +7,9 @@
sync_generator2_test/08: MissingCompileTimeError # Issue 22324
sync_generator2_test/10: MissingCompileTimeError # Issue 22324
+async_star_test/02: RuntimeError # 22853
+async_star_test/05: RuntimeError, Timeout
+
[ $compiler == dart2js && $runtime == jsshell ]
await_for_test: Skip # Jsshell does not provide periodic timers, Issue 7728
diff --git a/tests/language/regress_22777_test.dart b/tests/language/regress_22777_test.dart
new file mode 100644
index 0000000..74f0849
--- /dev/null
+++ b/tests/language/regress_22777_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+var a = 0;
+
+testSync () {
+ do {
+ continue;
+ } while (throw "Error");
+ a = 100;
+}
+
+testAsync() async {
+ do {
+ continue;
+ } while (await (throw "Error"));
+ a = 100;
+}
+
+test() async {
+ try {
+ testSync();
+ } catch (e) {
+ Expect.equals(e, "Error");
+ }
+ Expect.equals(a, 0);
+
+ try {
+ await testAsync();
+ } catch (e) {
+ Expect.equals(e, "Error");
+ }
+ Expect.equals(a, 0);
+}
+
+main() {
+ asyncStart();
+ test().then((_) => asyncEnd());
+}
\ No newline at end of file
diff --git a/tests/language/regress_22822_test.dart b/tests/language/regress_22822_test.dart
new file mode 100644
index 0000000..ad990c7
--- /dev/null
+++ b/tests/language/regress_22822_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for issue 22822. The assignment in the finally block
+// used to crash because it was executed at context level 1 instead of
+// context level 2.
+
+import 'package:expect/expect.dart';
+
+test(b) {
+ try {
+ for (int i = 0; i < 10; i++) {
+ // Closurizing i and b, thus the return statement
+ // executes at context level 2, and the code in
+ // the finally block runs at context level 1.
+ return () => i + b;
+ }
+ } finally {
+ b = 10;
+ }
+}
+
+main() {
+ var c = test(0);
+ Expect.equals(10, c());
+}
diff --git a/tools/VERSION b/tools/VERSION
index f71127e..db3bd8d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 9
PATCH 0
PRERELEASE 10
-PRERELEASE_PATCH 7
+PRERELEASE_PATCH 8