From ee69789e078c861c2bc68ff6b4f849ce102fd3b9 Mon Sep 17 00:00:00 2001 From: "hausner@google.com" Date: Fri, 5 Apr 2013 23:45:14 +0000 Subject: [PATCH] Add line table command to debugger New command returns token information of a script. This will enable the editor/debugger to translate token offsets to line numbers. Review URL: https://codereview.chromium.org//13533016 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@21009 260f80e4-7a28-3924-810f-c04153c831b5 --- runtime/bin/dbg_message.cc | 48 +++++++++++++++++++++++++++ runtime/bin/dbg_message.h | 1 + runtime/include/dart_debugger_api.h | 22 +++++++++++++ runtime/vm/debugger_api_impl.cc | 51 +++++++++++++++++++++++++++++ tools/ddbg.dart | 17 +++++++++- 5 files changed, 138 insertions(+), 1 deletion(-) diff --git a/runtime/bin/dbg_message.cc b/runtime/bin/dbg_message.cc index d364d8745f7..df1ddac1dae 100644 --- a/runtime/bin/dbg_message.cc +++ b/runtime/bin/dbg_message.cc @@ -479,6 +479,7 @@ static JSONDebuggerCommand debugger_commands[] = { { "getGlobalVariables", DbgMessage::HandleGetGlobalsCmd }, { "getScriptURLs", DbgMessage::HandleGetScriptURLsCmd }, { "getScriptSource", DbgMessage::HandleGetSourceCmd }, + { "getLineNumberTable", DbgMessage::HandleGetLineNumbersCmd }, { "getStackTrace", DbgMessage::HandleGetStackTraceCmd }, { "setBreakpoint", DbgMessage::HandleSetBpCmd }, { "setPauseOnException", DbgMessage::HandlePauseOnExcCmd }, @@ -788,6 +789,53 @@ bool DbgMessage::HandleGetSourceCmd(DbgMessage* in_msg) { } +bool DbgMessage::HandleGetLineNumbersCmd(DbgMessage* in_msg) { + ASSERT(in_msg != NULL); + MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); + int msg_id = msg_parser.MessageId(); + dart::TextBuffer msg(64); + intptr_t lib_id = msg_parser.GetIntParam("libraryId"); + char* url_chars = msg_parser.GetStringParam("url"); + ASSERT(url_chars != NULL); + Dart_Handle url = DartUtils::NewString(url_chars); + ASSERT_NOT_ERROR(url); + free(url_chars); + url_chars = NULL; + Dart_Handle info = Dart_ScriptGetTokenInfo(lib_id, url); + if (Dart_IsError(info)) { + in_msg->SendErrorReply(msg_id, Dart_GetError(info)); + return false; + } + ASSERT(Dart_IsList(info)); + intptr_t info_len = 0; + Dart_Handle res = Dart_ListLength(info, &info_len); + ASSERT_NOT_ERROR(res); + msg.Printf("{ \"id\": %d, ", msg_id); + msg.Printf("\"result\": { \"lines\": ["); + Dart_Handle elem; + bool num_elems = 0; + for (intptr_t i = 0; i < info_len; i++) { + elem = Dart_ListGetAt(info, i); + if (Dart_IsNull(elem)) { + msg.Printf((i == 0) ? "[" : "], ["); + num_elems = 0; + } else { + ASSERT(Dart_IsInteger(elem)); + int value = GetIntValue(elem); + if (num_elems == 0) { + msg.Printf("%d", value); + } else { + msg.Printf(",%d", value); + } + num_elems++; + } + } + msg.Printf("]]}}"); + in_msg->SendReply(&msg); + return false; +} + + bool DbgMessage::HandleGetStackTraceCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); diff --git a/runtime/bin/dbg_message.h b/runtime/bin/dbg_message.h index 58d5df92d82..95bc688539d 100644 --- a/runtime/bin/dbg_message.h +++ b/runtime/bin/dbg_message.h @@ -104,6 +104,7 @@ class DbgMessage { static bool HandleGetListCmd(DbgMessage* msg); static bool HandleGetScriptURLsCmd(DbgMessage* msg); static bool HandleGetSourceCmd(DbgMessage* msg); + static bool HandleGetLineNumbersCmd(DbgMessage* msg); static bool HandleGetStackTraceCmd(DbgMessage* msg); static bool HandlePauseOnExcCmd(DbgMessage* msg); static bool HandleSetBpCmd(DbgMessage* msg); diff --git a/runtime/include/dart_debugger_api.h b/runtime/include/dart_debugger_api.h index 810623d92a9..bb1bec83e91 100755 --- a/runtime/include/dart_debugger_api.h +++ b/runtime/include/dart_debugger_api.h @@ -141,6 +141,28 @@ DART_EXPORT Dart_Handle Dart_ScriptGetSource( Dart_Handle script_url_in); +/** + * Returns an array containing line number and token offset info + * for the given script. + * + * Returns an array of numbers. Null values indicate the beginning of + * a new line. The first number after null is the line number. + * The line number is followed by pairs of numbers, with the first value + * being the "token offset" and the second value being the character offset + * of the token relative to the beginning of the script. + * The "token offset" is a value that is used to indicate a location + * in code, similarly to a "PC" address. + * Source lines with no tokens are omitted. + * + * Requires there to be a current isolate. + * + * \return A handle to an array or an error object. + */ +DART_EXPORT Dart_Handle Dart_ScriptGetTokenInfo( + intptr_t library_id, + Dart_Handle script_url_in); + + /** * Returns a string containing a generated source code of the given script * in the given library. This is essentially used to pretty print dart code diff --git a/runtime/vm/debugger_api_impl.cc b/runtime/vm/debugger_api_impl.cc index 84dc81e9f72..a3d8182793c 100644 --- a/runtime/vm/debugger_api_impl.cc +++ b/runtime/vm/debugger_api_impl.cc @@ -587,6 +587,57 @@ DART_EXPORT Dart_Handle Dart_ScriptGetSource( } +DART_EXPORT Dart_Handle Dart_ScriptGetTokenInfo( + intptr_t library_id, + Dart_Handle script_url_in) { + Isolate* isolate = Isolate::Current(); + DARTSCOPE(isolate); + const Library& lib = Library::Handle(Library::GetLibrary(library_id)); + if (lib.IsNull()) { + return Api::NewError("%s: %"Pd" is not a valid library id", + CURRENT_FUNC, library_id); + } + UNWRAP_AND_CHECK_PARAM(String, script_url, script_url_in); + const Script& script = Script::Handle(lib.LookupScript(script_url)); + if (script.IsNull()) { + return Api::NewError("%s: script '%s' not found in library '%s'", + CURRENT_FUNC, script_url.ToCString(), + String::Handle(lib.url()).ToCString()); + } + + const GrowableObjectArray& info = + GrowableObjectArray::Handle(GrowableObjectArray::New()); + const String& source = String::Handle(script.Source()); + const String& key = Symbols::Empty(); + const Object& line_separator = Object::Handle(); + const TokenStream& tkns = TokenStream::Handle(script.tokens()); + ASSERT(!tkns.IsNull()); + TokenStream::Iterator tkit(tkns, 0); + int current_line = -1; + Scanner s(source, key); + s.Scan(); + while (s.current_token().kind != Token::kEOS) { + ASSERT(tkit.IsValid()); + ASSERT(s.current_token().kind == tkit.CurrentTokenKind()); + int token_line = s.current_token().position.line; + if (token_line != current_line) { + // emit line + info.Add(line_separator); + info.Add(Smi::Handle(Smi::New(token_line))); + current_line = token_line; + } + // TODO(hausner): Could optimize here by not reporting tokens + // that will never be a location used by the debugger, e.g. + // braces, semicolons, most keywords etc. + info.Add(Smi::Handle(Smi::New(tkit.CurrentPosition()))); + info.Add(Smi::Handle(Smi::New(s.current_token().offset))); + s.Scan(); + tkit.Advance(); + } + return Api::NewHandle(isolate, Array::MakeArray(info)); +} + + DART_EXPORT Dart_Handle Dart_GenerateScriptSource(Dart_Handle library_url_in, Dart_Handle script_url_in) { Isolate* isolate = Isolate::Current(); diff --git a/tools/ddbg.dart b/tools/ddbg.dart index c92174c787b..e523a72ae42 100644 --- a/tools/ddbg.dart +++ b/tools/ddbg.dart @@ -48,6 +48,7 @@ void printHelp() { pg Print all global variables visible within given library id ls List loaded scripts in library gs Get source text of script in library + tok Get line and token table of script in library epi Set exception pause info i Interrupt execution of given isolate id h Print help @@ -181,6 +182,13 @@ void processCommand(String cmdLine) { "libraryId": int.parse(args[1]), "url": args[2] } }; sendCmd(cmd).then((result) => handleGetSourceResponse(result)); + } else if (command == "tok" && args.length == 3) { + var cmd = { "id": seqNum, + "command": "getLineNumberTable", + "params": { "isolateId" : isolate_id, + "libraryId": int.parse(args[1]), + "url": args[2] } }; + sendCmd(cmd).then((result) => handleGetLineTableResponse(result)); } else if (command == "epi" && args.length == 2) { var cmd = { "id": seqNum, "command": "setPauseOnException", @@ -324,6 +332,13 @@ handleGetSourceResponse(response) { } +handleGetLineTableResponse(response) { + Map result = response["result"]; + var info = result["lines"]; + print("Line info table:\n$info"); +} + + void handleGetLibraryResponse(response) { Map result = response["result"]; List libs = result["libraries"]; @@ -391,7 +406,7 @@ void printStackTrace(List frames) { void handlePausedEvent(msg) { assert(msg["params"] != null); var reason = msg["params"]["reason"]; - isolate_id = msg["params"]["id"]; + isolate_id = msg["params"]["isolateId"]; stackTrace = msg["params"]["callFrames"]; assert(stackTrace != null); assert(stackTrace.length >= 1);