From c39f48340d6cd1607b2d2f7cb450504dc159a604 Mon Sep 17 00:00:00 2001 From: "hausner@google.com" Date: Sat, 2 Jun 2012 00:07:12 +0000 Subject: [PATCH] Add string encoding to wire protocol Also, add command to retrieve the source text of a given script. Review URL: https://chromiumcodereview.appspot.com//10496006 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@8220 260f80e4-7a28-3924-810f-c04153c831b5 --- runtime/bin/dbg_connection.cc | 145 +++++++++++++++++++++------- runtime/bin/dbg_connection.h | 1 + runtime/include/dart_debugger_api.h | 16 +++ runtime/platform/json.cc | 51 +++++++++- runtime/platform/json.h | 1 + runtime/vm/debugger_api_impl.cc | 22 +++++ tools/ddbg.dart | 13 +++ 7 files changed, 211 insertions(+), 38 deletions(-) diff --git a/runtime/bin/dbg_connection.cc b/runtime/bin/dbg_connection.cc index 6aa947b16b1..68867044241 100644 --- a/runtime/bin/dbg_connection.cc +++ b/runtime/bin/dbg_connection.cc @@ -55,6 +55,8 @@ class MessageBuffer { int MessageId() const; const char* Params() const; intptr_t GetIntParam(const char* name) const; + // GetStringParam mallocs the buffer that it returns. Caller must free. + char* GetStringParam(const char* name) const; char* buf() const { return buf_; } bool Alive() const { return connection_is_alive_; } @@ -133,6 +135,20 @@ intptr_t MessageBuffer::GetIntParam(const char* name) const { return strtol(r.ValueChars(), NULL, 10); } +char* MessageBuffer::GetStringParam(const char* name) const { + const char* params = Params(); + ASSERT(params != NULL); + dart::JSONReader pr(params); + pr.Seek(name); + if (pr.Type() != dart::JSONReader::kString) { + return NULL; + } + intptr_t buflen = pr.ValueLen() + 1; + char* param_chars = reinterpret_cast(malloc(buflen)); + pr.GetValueChars(param_chars, buflen); + // TODO(hausner): Decode escape sequences. + return param_chars; +} void MessageBuffer::ReadData() { ASSERT(data_length_ >= 0); @@ -226,7 +242,7 @@ static int GetIntValue(Dart_Handle int_handle) { int64_t int64_val = -1; ASSERT(Dart_IsInteger(int_handle)); Dart_Handle res = Dart_IntegerToInt64(int_handle, &int64_val); - ASSERT(!Dart_IsError(res)); + ASSERT_NOT_ERROR(res); // TODO(hausner): Range check. return int64_val; } @@ -262,6 +278,32 @@ void DebuggerConnectionHandler::HandleStepOverCmd(const char* json_msg) { } +static void FormatEncodedString(dart::TextBuffer* buf, Dart_Handle str) { + ASSERT(Dart_IsString8(str)); + intptr_t str_len = 0; + Dart_Handle res = Dart_StringLength(str, &str_len); + ASSERT_NOT_ERROR(res); + uint8_t* codepoints = reinterpret_cast(malloc(str_len)); + ASSERT(codepoints != NULL); + intptr_t actual_len = str_len; + res = Dart_StringGet8(str, codepoints, &actual_len); + ASSERT(str_len == actual_len); + buf->Printf("\""); + buf->PrintJsonString8(codepoints, str_len); + buf->Printf("\""); + free(codepoints); +} + + +static void FormatErrorMsg(dart::TextBuffer* buf, Dart_Handle err) { + // TODO(hausner): Turn message into Dart string and + // properly encode the message. + ASSERT(Dart_IsError(err)); + const char* msg = Dart_GetError(err); + buf->Printf("\"%s\"", msg); +} + + void DebuggerConnectionHandler::HandleGetScriptURLsCmd(const char* json_msg) { int msg_id = msgbuf_->MessageId(); dart::TextBuffer msg(64); @@ -280,9 +322,35 @@ void DebuggerConnectionHandler::HandleGetScriptURLsCmd(const char* json_msg) { msg.Printf("\"result\": { \"urls\": ["); for (int i = 0; i < num_urls; i++) { Dart_Handle script_url = Dart_ListGetAt(urls, i); - msg.Printf("%s\"%s\"", (i == 0) ? "" : ", ", GetStringChars(script_url)); + if (i > 0) { + msg.Printf(","); + } + FormatEncodedString(&msg, script_url); } - msg.Printf("] }}"); + msg.Printf("]}}"); + SendMsg(&msg); +} + + +void DebuggerConnectionHandler::HandleGetSourceCmd(const char* json_msg) { + int msg_id = msgbuf_->MessageId(); + dart::TextBuffer msg(64); + intptr_t lib_id = msgbuf_->GetIntParam("libraryId"); + char* url_chars = msgbuf_->GetStringParam("url"); + ASSERT(url_chars != NULL); + Dart_Handle url = Dart_NewString(url_chars); + ASSERT_NOT_ERROR(url); + free(url_chars); + url_chars = NULL; + Dart_Handle source = Dart_ScriptGetSource(lib_id, url); + if (Dart_IsError(source)) { + SendError(msg_id, Dart_GetError(source)); + return; + } + msg.Printf("{ \"id\": %d, ", msg_id); + msg.Printf("\"result\": { \"text\": "); + FormatEncodedString(&msg, source); + msg.Printf("}}"); SendMsg(&msg); } @@ -302,12 +370,10 @@ void DebuggerConnectionHandler::HandleGetLibrariesCmd(const char* json_msg) { int lib_id = GetIntValue(lib_id_handle); Dart_Handle lib_url = Dart_GetLibraryURL(lib_id); ASSERT_NOT_ERROR(lib_url); - ASSERT(!Dart_IsNull(lib_url)); ASSERT(Dart_IsString(lib_url)); - char const* chars = NULL; - Dart_StringToCString(lib_url, &chars); - msg.Printf("%s{\"id\":%d,\"url\":\"%s\"}", - (i == 0) ? "" : ", ", lib_id, chars); + msg.Printf("%s{\"id\":%d,\"url\":", (i == 0) ? "" : ", ", lib_id); + FormatEncodedString(&msg, lib_url); + msg.Printf("}"); } msg.Printf("]}}"); SendMsg(&msg); @@ -331,8 +397,21 @@ static void FormatField(dart::TextBuffer* buf, kind = "boolean"; } buf->Printf("\"kind\":\"%s\",", kind); - Dart_Handle text = Dart_ToString(object); - buf->Printf("\"text\":\"%s\"}}", GetStringChars(text)); + buf->Printf("\"text\":"); + Dart_Handle text; + if (Dart_IsNull(object)) { + text = Dart_Null(); + } else { + text = Dart_ToString(object); + } + if (Dart_IsNull(text)) { + buf->Printf("null"); + } else if (Dart_IsError(text)) { + FormatErrorMsg(buf, text); + } else { + FormatEncodedString(buf, text); + } + buf->Printf("}}"); } @@ -384,7 +463,8 @@ static const char* FormatLibraryProps(dart::TextBuffer* buf, intptr_t lib_id) { Dart_Handle url = Dart_GetLibraryURL(lib_id); RETURN_IF_ERROR(url); - buf->Printf("{\"url\":\"%s\",", GetStringChars(url)); + buf->Printf("{\"url\":"); + FormatEncodedString(buf, url); // Imports and prefixes. Dart_Handle import_list = Dart_GetLibraryImports(lib_id); @@ -393,7 +473,7 @@ static const char* FormatLibraryProps(dart::TextBuffer* buf, intptr_t list_length = 0; Dart_Handle res = Dart_ListLength(import_list, &list_length); RETURN_IF_ERROR(res); - buf->Printf("\"imports\":["); + buf->Printf(",\"imports\":["); for (int i = 0; i + 1 < list_length; i += 2) { Dart_Handle lib_id = Dart_ListGetAt(import_list, i + 1); ASSERT_NOT_ERROR(lib_id); @@ -450,18 +530,17 @@ static void FormatCallFrames(dart::TextBuffer* msg, Dart_StackTrace trace) { intptr_t line_number = 0; res = Dart_ActivationFrameInfo( frame, &func_name, &script_url, &line_number); + ASSERT_NOT_ERROR(res); ASSERT(Dart_IsString(func_name)); - const char* func_name_chars; - Dart_StringToCString(func_name, &func_name_chars); - msg->Printf("%s { \"functionName\": \"%s\" , ", - i > 0 ? "," : "", - func_name_chars); + msg->Printf("%s{\"functionName\":", (i > 0) ? "," : ""); + FormatEncodedString(msg, func_name); + ASSERT(Dart_IsString(script_url)); - const char* script_url_chars; - Dart_StringToCString(script_url, &script_url_chars); - msg->Printf("\"location\": { \"url\": \"%s\", \"lineNumber\":%d},", - script_url_chars, line_number); + msg->Printf(",\"location\": { \"url\":"); + FormatEncodedString(msg, script_url); + msg->Printf(",\"lineNumber\":%d},", line_number); + Dart_Handle locals = Dart_GetLocalVariables(frame); ASSERT_NOT_ERROR(locals); msg->Printf("\"locals\":"); @@ -487,18 +566,13 @@ void DebuggerConnectionHandler::HandleGetStackTraceCmd(const char* json_msg) { void DebuggerConnectionHandler::HandleSetBpCmd(const char* json_msg) { int msg_id = msgbuf_->MessageId(); - const char* params = msgbuf_->Params(); - ASSERT(params != NULL); - dart::JSONReader pr(params); - pr.Seek("url"); - ASSERT(pr.Type() == dart::JSONReader::kString); - char url_chars[128]; - pr.GetValueChars(url_chars, sizeof(url_chars)); + char* url_chars = msgbuf_->GetStringParam("url"); + ASSERT(url_chars != NULL); Dart_Handle url = Dart_NewString(url_chars); ASSERT_NOT_ERROR(url); - pr.Seek("line"); - ASSERT(pr.Type() == dart::JSONReader::kInteger); - intptr_t line_number = atoi(pr.ValueChars()); + free(url_chars); + url_chars = NULL; + intptr_t line_number = msgbuf_->GetIntParam("line"); Dart_Handle bp_id = Dart_SetBreakpoint(url, line_number); if (Dart_IsError(bp_id)) { SendError(msg_id, Dart_GetError(bp_id)); @@ -594,6 +668,7 @@ void DebuggerConnectionHandler::HandleMessages() { { "getLibraryProperties", HandleGetLibPropsCmd }, { "getObjectProperties", HandleGetObjPropsCmd }, { "getScriptURLs", HandleGetScriptURLsCmd }, + { "getScriptSource", HandleGetSourceCmd }, { "getStackTrace", HandleGetStackTraceCmd }, { "setBreakpoint", HandleSetBpCmd }, { "removeBreakpoint", HandleRemBpCmd }, @@ -682,11 +757,9 @@ void DebuggerConnectionHandler::BptResolvedHandler(intptr_t bp_id, Dart_EnterScope(); dart::TextBuffer msg(128); msg.Printf("{ \"event\": \"breakpointResolved\", \"params\": {"); - msg.Printf("\"breakpointId\": %d, ", bp_id); - char const* url_chars; - Dart_StringToCString(url, &url_chars); - msg.Printf("\"url\": \"%s\", ", url_chars); - msg.Printf("\"line\": %d }}", line_number); + msg.Printf("\"breakpointId\": %d, \"url\":", bp_id); + FormatEncodedString(&msg, url); + msg.Printf(",\"line\": %d }}", line_number); QueueMsg(&msg); Dart_ExitScope(); } diff --git a/runtime/bin/dbg_connection.h b/runtime/bin/dbg_connection.h index 45624fcc73f..ace0525d12d 100644 --- a/runtime/bin/dbg_connection.h +++ b/runtime/bin/dbg_connection.h @@ -60,6 +60,7 @@ class DebuggerConnectionHandler { static void HandleGetLibPropsCmd(const char* json_msg); static void HandleGetObjPropsCmd(const char* json_msg); static void HandleGetScriptURLsCmd(const char* json_msg); + static void HandleGetSourceCmd(const char* json_msg); static void HandleGetStackTraceCmd(const char* json_msg); static void HandleSetBpCmd(const char* json_msg); static void HandleRemBpCmd(const char* json_msg); diff --git a/runtime/include/dart_debugger_api.h b/runtime/include/dart_debugger_api.h index 50c8ae936ed..b9463a683fa 100755 --- a/runtime/include/dart_debugger_api.h +++ b/runtime/include/dart_debugger_api.h @@ -82,6 +82,8 @@ DART_EXPORT Dart_Handle Dart_GetScriptURLs(Dart_Handle library_url); /** + * DEPRECATED --- use Dart_ScriptGetSource + * * Returns a string containing the source code of the given script * in the given library. * @@ -95,6 +97,20 @@ DART_EXPORT Dart_Handle Dart_GetScriptSource( Dart_Handle script_url_in); +/** + * Returns a string containing the source code of the given script + * in the given library. + * + * Requires there to be a current isolate. + * + * \return A handle to string containing the source text if no error + * occurs. + */ +DART_EXPORT Dart_Handle Dart_ScriptGetSource( + intptr_t library_id, + Dart_Handle script_url_in); + + /** * Sets a breakpoint at line \line_number in \script_url, or the closest * following line (within the same function) where a breakpoint can be set. diff --git a/runtime/platform/json.cc b/runtime/platform/json.cc index 06dbd29e953..474e326e61e 100644 --- a/runtime/platform/json.cc +++ b/runtime/platform/json.cc @@ -92,8 +92,11 @@ void JSONScanner::ScanString() { token_ = TokenIllegal; return; } else if (*current_pos_ == '\\') { - // TODO(hausner): Implement escape sequence. - UNIMPLEMENTED(); + ++current_pos_; + if (*current_pos_ == '"') { + // Consume escaped double quote. + ++current_pos_; + } } else if (*current_pos_ < 0) { // UTF-8 not supported. token_length_ = 0; @@ -373,6 +376,50 @@ intptr_t TextBuffer::Printf(const char* format, ...) { } +void TextBuffer::PrintJsonString8(const uint8_t* codepoints, intptr_t length) { + for (intptr_t i = 0; i < length; i++) { + uint8_t cp = codepoints[i]; + switch (cp) { + case '"': + Printf("%s", "\\\""); + break; + case '\\': + Printf("%s", "\\\\"); + break; + case '/': + Printf("%s", "\\/"); + break; + case '\b': + Printf("%s", "\\b"); + break; + case '\f': + Printf("%s", "\\f"); + break; + case '\n': + Printf("%s", "\\n"); + break; + case '\r': + Printf("%s", "\\r"); + break; + case '\t': + Printf("%s", "\\t"); + break; + default: + if ((0x20 <= cp) && (cp <= 0x7e)) { + Printf("%c", cp); + } else { + // Encode character as \u00HH. + uint8_t digit2 = (cp & 0xf0) >> 4; + uint8_t digit3 = cp & 0xf; + Printf("\\u00%c%c", + digit2 > 9 ? 'A' + (digit2 - 10) : '0' + digit2, + digit3 > 9 ? 'A' + (digit3 - 10) : '0' + digit3); + } + } + } +} + + void TextBuffer::GrowBuffer(intptr_t len) { intptr_t new_size = buf_size_ + len; char* new_buf = reinterpret_cast(realloc(buf_, new_size)); diff --git a/runtime/platform/json.h b/runtime/platform/json.h index d2a0f2625c4..3bb9d0719d1 100644 --- a/runtime/platform/json.h +++ b/runtime/platform/json.h @@ -117,6 +117,7 @@ class TextBuffer : ValueObject { ~TextBuffer(); intptr_t Printf(const char* format, ...); + void PrintJsonString8(const uint8_t* codepoints, intptr_t length); void Clear(); diff --git a/runtime/vm/debugger_api_impl.cc b/runtime/vm/debugger_api_impl.cc index c83efb2833c..95ef9aa73bc 100644 --- a/runtime/vm/debugger_api_impl.cc +++ b/runtime/vm/debugger_api_impl.cc @@ -454,6 +454,28 @@ DART_EXPORT Dart_Handle Dart_GetClassInfo( } +DART_EXPORT Dart_Handle Dart_ScriptGetSource( + 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: %d is not a valid library id", + CURRENT_FUNC, library_id); + } + String& script_url = String::Handle(); + 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()); + } + return Api::NewHandle(isolate, script.source()); +} + + DART_EXPORT Dart_Handle Dart_GetScriptSource( Dart_Handle library_url_in, Dart_Handle script_url_in) { diff --git a/tools/ddbg.dart b/tools/ddbg.dart index d6d7f985b02..36d794d6baf 100644 --- a/tools/ddbg.dart +++ b/tools/ddbg.dart @@ -39,6 +39,7 @@ void printHelp() { ll List loaded libraries pl Print dlibrary info for given id ls List loaded scripts in library + gs Get source text of script in library h Print help """); } @@ -117,6 +118,11 @@ void processCommand(String cmdLine) { var cmd = { "id": seqNum, "command": "getLibraryProperties", "params": {"libraryId": Math.parseInt(args[1]) }}; sendCmd(cmd).then((result) => handleGetLibraryPropsResponse(result)); + } else if (command == "gs" && args.length == 3) { + var cmd = { "id": seqNum, "command": "getScriptSource", + "params": { "libraryId": Math.parseInt(args[1]), + "url": args[2] }}; + sendCmd(cmd).then((result) => handleGetSourceResponse(result)); } else if (command == "q") { quitShell(); } else if (command == "h") { @@ -197,6 +203,13 @@ handleGetLibraryPropsResponse(response) { } +handleGetSourceResponse(response) { + Map result = response["result"]; + String source = result["text"]; + print("Source text:\n$source\n--------"); +} + + void handleGetLibraryResponse(response) { Map result = response["result"]; List libs = result["libraries"];