| Index: dart/runtime/vm/service.cc
 | 
| ===================================================================
 | 
| --- dart/runtime/vm/service.cc	(revision 31530)
 | 
| +++ dart/runtime/vm/service.cc	(working copy)
 | 
| @@ -14,6 +14,7 @@
 | 
|  #include "vm/object_id_ring.h"
 | 
|  #include "vm/object_store.h"
 | 
|  #include "vm/port.h"
 | 
| +#include "vm/profiler.h"
 | 
|  
 | 
|  namespace dart {
 | 
|  
 | 
| @@ -196,15 +197,15 @@
 | 
|    jsobj.AddProperty("type", "StackTrace");
 | 
|    JSONArray jsarr(&jsobj, "members");
 | 
|    intptr_t n_frames = stack->Length();
 | 
| -  String& url = String::Handle();
 | 
|    String& function = String::Handle();
 | 
| +  Script& script = Script::Handle();
 | 
|    for (int i = 0; i < n_frames; i++) {
 | 
|      ActivationFrame* frame = stack->FrameAt(i);
 | 
| -    url ^= frame->SourceUrl();
 | 
| +    script ^= frame->SourceScript();
 | 
|      function ^= frame->function().UserVisibleName();
 | 
|      JSONObject jsobj(&jsarr);
 | 
|      jsobj.AddProperty("name", function.ToCString());
 | 
| -    jsobj.AddProperty("url", url.ToCString());
 | 
| +    jsobj.AddProperty("script", script);
 | 
|      jsobj.AddProperty("line", frame->LineNumber());
 | 
|      jsobj.AddProperty("function", frame->function());
 | 
|      jsobj.AddProperty("code", frame->code());
 | 
| @@ -239,6 +240,164 @@
 | 
|    }
 | 
|  
 | 
|  
 | 
| +#define CHECK_COLLECTION_ID_BOUNDS(collection, length, arg, id, js)            \
 | 
| +  if (!GetIntegerId(arg, &id)) {                                               \
 | 
| +    PrintError(js, "Must specify collection object id: %s/id", collection);    \
 | 
| +    return;                                                                    \
 | 
| +  }                                                                            \
 | 
| +  if ((id < 0) || (id >= length)) {                                            \
 | 
| +    PrintError(js, "%s id (%" Pd ") must be in [0, %" Pd ").", collection, id, \
 | 
| +                                                               length);        \
 | 
| +    return;                                                                    \
 | 
| +  }
 | 
| +
 | 
| +
 | 
| +static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) {
 | 
| +  if ((s == NULL) || (*s == '\0')) {
 | 
| +    // Empty string.
 | 
| +    return false;
 | 
| +  }
 | 
| +  if (id == NULL) {
 | 
| +    // No id pointer.
 | 
| +    return false;
 | 
| +  }
 | 
| +  intptr_t r = 0;
 | 
| +  char* end_ptr = NULL;
 | 
| +  r = strtol(s, &end_ptr, base);
 | 
| +  if (end_ptr == s) {
 | 
| +    // String was not advanced at all, cannot be valid.
 | 
| +    return false;
 | 
| +  }
 | 
| +  *id = r;
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) {
 | 
| +  if ((s == NULL) || (*s == '\0')) {
 | 
| +    // Empty string.
 | 
| +    return false;
 | 
| +  }
 | 
| +  if (id == NULL) {
 | 
| +    // No id pointer.
 | 
| +    return false;
 | 
| +  }
 | 
| +  uintptr_t r = 0;
 | 
| +  char* end_ptr = NULL;
 | 
| +  r = strtoul(s, &end_ptr, base);
 | 
| +  if (end_ptr == s) {
 | 
| +    // String was not advanced at all, cannot be valid.
 | 
| +    return false;
 | 
| +  }
 | 
| +  *id = r;
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleClassesClosures(Isolate* isolate, const Class& cls,
 | 
| +                                  JSONStream* js) {
 | 
| +  intptr_t id;
 | 
| +  if (js->num_arguments() > 4) {
 | 
| +    PrintError(js, "Command too long");
 | 
| +    return;
 | 
| +  }
 | 
| +  if (!GetIntegerId(js->GetArgument(3), &id)) {
 | 
| +    PrintError(js, "Must specify collection object id: closures/id");
 | 
| +    return;
 | 
| +  }
 | 
| +  Function& func = Function::Handle();
 | 
| +  func ^= cls.ClosureFunctionFromIndex(id);
 | 
| +  if (func.IsNull()) {
 | 
| +    PrintError(js, "Closure function %" Pd " not found", id);
 | 
| +    return;
 | 
| +  }
 | 
| +  func.PrintToJSONStream(js, false);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleClassesDispatchers(Isolate* isolate, const Class& cls,
 | 
| +                                     JSONStream* js) {
 | 
| +  intptr_t id;
 | 
| +  if (js->num_arguments() > 4) {
 | 
| +    PrintError(js, "Command too long");
 | 
| +    return;
 | 
| +  }
 | 
| +  if (!GetIntegerId(js->GetArgument(3), &id)) {
 | 
| +    PrintError(js, "Must specify collection object id: dispatchers/id");
 | 
| +    return;
 | 
| +  }
 | 
| +  Function& func = Function::Handle();
 | 
| +  func ^= cls.InvocationDispatcherFunctionFromIndex(id);
 | 
| +  if (func.IsNull()) {
 | 
| +    PrintError(js, "Dispatcher %" Pd " not found", id);
 | 
| +    return;
 | 
| +  }
 | 
| +  func.PrintToJSONStream(js, false);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleClassesFunctions(Isolate* isolate, const Class& cls,
 | 
| +                                   JSONStream* js) {
 | 
| +  intptr_t id;
 | 
| +  if (js->num_arguments() > 4) {
 | 
| +    PrintError(js, "Command too long");
 | 
| +    return;
 | 
| +  }
 | 
| +  if (!GetIntegerId(js->GetArgument(3), &id)) {
 | 
| +    PrintError(js, "Must specify collection object id: functions/id");
 | 
| +    return;
 | 
| +  }
 | 
| +  Function& func = Function::Handle();
 | 
| +  func ^= cls.FunctionFromIndex(id);
 | 
| +  if (func.IsNull()) {
 | 
| +    PrintError(js, "Function %" Pd " not found", id);
 | 
| +    return;
 | 
| +  }
 | 
| +  func.PrintToJSONStream(js, false);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleClassesImplicitClosures(Isolate* isolate, const Class& cls,
 | 
| +                                          JSONStream* js) {
 | 
| +  intptr_t id;
 | 
| +  if (js->num_arguments() > 4) {
 | 
| +    PrintError(js, "Command too long");
 | 
| +    return;
 | 
| +  }
 | 
| +  if (!GetIntegerId(js->GetArgument(3), &id)) {
 | 
| +    PrintError(js, "Must specify collection object id: implicit_closures/id");
 | 
| +    return;
 | 
| +  }
 | 
| +  Function& func = Function::Handle();
 | 
| +  func ^= cls.ImplicitClosureFunctionFromIndex(id);
 | 
| +  if (func.IsNull()) {
 | 
| +    PrintError(js, "Implicit closure function %" Pd " not found", id);
 | 
| +    return;
 | 
| +  }
 | 
| +  func.PrintToJSONStream(js, false);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleClassesFields(Isolate* isolate, const Class& cls,
 | 
| +                                JSONStream* js) {
 | 
| +  intptr_t id;
 | 
| +  if (js->num_arguments() > 4) {
 | 
| +    PrintError(js, "Command too long");
 | 
| +    return;
 | 
| +  }
 | 
| +  if (!GetIntegerId(js->GetArgument(3), &id)) {
 | 
| +    PrintError(js, "Must specify collection object id: fields/id");
 | 
| +    return;
 | 
| +  }
 | 
| +  Field& field = Field::Handle(cls.FieldFromIndex(id));
 | 
| +  if (field.IsNull()) {
 | 
| +    PrintError(js, "Field %" Pd " not found", id);
 | 
| +    return;
 | 
| +  }
 | 
| +  field.PrintToJSONStream(js, false);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  static void HandleClasses(Isolate* isolate, JSONStream* js) {
 | 
|    if (js->num_arguments() == 1) {
 | 
|      ClassTable* table = isolate->class_table();
 | 
| @@ -246,14 +405,38 @@
 | 
|      return;
 | 
|    }
 | 
|    ASSERT(js->num_arguments() >= 2);
 | 
| -  intptr_t id = atoi(js->GetArgument(1));
 | 
| +  intptr_t id;
 | 
| +  if (!GetIntegerId(js->GetArgument(1), &id)) {
 | 
| +    PrintError(js, "Must specify collection object id: /classes/id");
 | 
| +    return;
 | 
| +  }
 | 
|    ClassTable* table = isolate->class_table();
 | 
|    if (!table->IsValidIndex(id)) {
 | 
| -    Object::null_object().PrintToJSONStream(js, false);
 | 
| -  } else {
 | 
| -    Class& cls = Class::Handle(table->At(id));
 | 
| +    PrintError(js, "%" Pd " is not a valid class id.", id);;
 | 
| +    return;
 | 
| +  }
 | 
| +  Class& cls = Class::Handle(table->At(id));
 | 
| +  if (js->num_arguments() == 2) {
 | 
|      cls.PrintToJSONStream(js, false);
 | 
| +    return;
 | 
| +  } else if (js->num_arguments() >= 3) {
 | 
| +    const char* second = js->GetArgument(2);
 | 
| +    if (!strcmp(second, "closures")) {
 | 
| +      HandleClassesClosures(isolate, cls, js);
 | 
| +    } else if (!strcmp(second, "fields")) {
 | 
| +      HandleClassesFields(isolate, cls, js);
 | 
| +    } else if (!strcmp(second, "functions")) {
 | 
| +      HandleClassesFunctions(isolate, cls, js);
 | 
| +    } else if (!strcmp(second, "implicit_closures")) {
 | 
| +      HandleClassesImplicitClosures(isolate, cls, js);
 | 
| +    } else if (!strcmp(second, "dispatchers")) {
 | 
| +      HandleClassesDispatchers(isolate, cls, js);
 | 
| +    } else {
 | 
| +      PrintError(js, "Invalid sub collection %s", second);
 | 
| +    }
 | 
| +    return;
 | 
|    }
 | 
| +  UNREACHABLE();
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -268,17 +451,109 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +static void HandleLibraries(Isolate* isolate, JSONStream* js) {
 | 
| +  // TODO(johnmccutchan): Support fields and functions on libraries.
 | 
| +  REQUIRE_COLLECTION_ID("libraries");
 | 
| +  const GrowableObjectArray& libs =
 | 
| +      GrowableObjectArray::Handle(isolate->object_store()->libraries());
 | 
| +  ASSERT(!libs.IsNull());
 | 
| +  intptr_t id = 0;
 | 
| +  CHECK_COLLECTION_ID_BOUNDS("libraries", libs.Length(), js->GetArgument(1),
 | 
| +                             id, js);
 | 
| +  Library& lib = Library::Handle();
 | 
| +  lib ^= libs.At(id);
 | 
| +  ASSERT(!lib.IsNull());
 | 
| +  lib.PrintToJSONStream(js, false);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  static void HandleObjects(Isolate* isolate, JSONStream* js) {
 | 
|    REQUIRE_COLLECTION_ID("objects");
 | 
|    ASSERT(js->num_arguments() >= 2);
 | 
|    ObjectIdRing* ring = isolate->object_id_ring();
 | 
|    ASSERT(ring != NULL);
 | 
| -  intptr_t id = atoi(js->GetArgument(1));
 | 
| +  intptr_t id = -1;
 | 
| +  if (!GetIntegerId(js->GetArgument(1), &id)) {
 | 
| +    Object::null_object().PrintToJSONStream(js, false);
 | 
| +    return;
 | 
| +  }
 | 
|    Object& obj = Object::Handle(ring->GetObjectForId(id));
 | 
|    obj.PrintToJSONStream(js, false);
 | 
|  }
 | 
|  
 | 
|  
 | 
| +
 | 
| +static void HandleScriptsEnumerate(Isolate* isolate, JSONStream* js) {
 | 
| +  JSONObject jsobj(js);
 | 
| +  jsobj.AddProperty("type", "ScriptList");
 | 
| +  {
 | 
| +    JSONArray members(&jsobj, "members");
 | 
| +    const GrowableObjectArray& libs =
 | 
| +      GrowableObjectArray::Handle(isolate->object_store()->libraries());
 | 
| +    int num_libs = libs.Length();
 | 
| +    Library &lib = Library::Handle();
 | 
| +    Script& script = Script::Handle();
 | 
| +    for (intptr_t i = 0; i < num_libs; i++) {
 | 
| +      lib ^= libs.At(i);
 | 
| +      ASSERT(!lib.IsNull());
 | 
| +      ASSERT(Smi::IsValid(lib.index()));
 | 
| +      const Array& loaded_scripts = Array::Handle(lib.LoadedScripts());
 | 
| +      ASSERT(!loaded_scripts.IsNull());
 | 
| +      intptr_t num_scripts = loaded_scripts.Length();
 | 
| +      for (intptr_t i = 0; i < num_scripts; i++) {
 | 
| +        script ^= loaded_scripts.At(i);
 | 
| +        members.AddValue(script);
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleScriptsFetch(Isolate* isolate, JSONStream* js) {
 | 
| +  const GrowableObjectArray& libs =
 | 
| +    GrowableObjectArray::Handle(isolate->object_store()->libraries());
 | 
| +  int num_libs = libs.Length();
 | 
| +  Library &lib = Library::Handle();
 | 
| +  Script& script = Script::Handle();
 | 
| +  String& url = String::Handle();
 | 
| +  const String& id = String::Handle(String::New(js->GetArgument(1)));
 | 
| +  ASSERT(!id.IsNull());
 | 
| +  // The id is the url of the script % encoded, decode it.
 | 
| +  String& requested_url = String::Handle(String::DecodeURI(id));
 | 
| +  for (intptr_t i = 0; i < num_libs; i++) {
 | 
| +    lib ^= libs.At(i);
 | 
| +    ASSERT(!lib.IsNull());
 | 
| +    ASSERT(Smi::IsValid(lib.index()));
 | 
| +    const Array& loaded_scripts = Array::Handle(lib.LoadedScripts());
 | 
| +    ASSERT(!loaded_scripts.IsNull());
 | 
| +    intptr_t num_scripts = loaded_scripts.Length();
 | 
| +    for (intptr_t i = 0; i < num_scripts; i++) {
 | 
| +      script ^= loaded_scripts.At(i);
 | 
| +      ASSERT(!script.IsNull());
 | 
| +      url ^= script.url();
 | 
| +      if (url.Equals(requested_url)) {
 | 
| +        script.PrintToJSONStream(js, false);
 | 
| +        return;
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +  PrintError(js, "Cannot find script %s\n", requested_url.ToCString());
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleScripts(Isolate* isolate, JSONStream* js) {
 | 
| +  if (js->num_arguments() == 1) {
 | 
| +    // Enumerate all scripts.
 | 
| +    HandleScriptsEnumerate(isolate, js);
 | 
| +  } else if (js->num_arguments() == 2) {
 | 
| +    // Fetch specific script.
 | 
| +    HandleScriptsFetch(isolate, js);
 | 
| +  } else {
 | 
| +    PrintError(js, "Command too long");
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
|  static void HandleDebug(Isolate* isolate, JSONStream* js) {
 | 
|    if (js->num_arguments() == 1) {
 | 
|      PrintError(js, "Must specify a subcommand");
 | 
| @@ -295,8 +570,11 @@
 | 
|  
 | 
|      } else if (js->num_arguments() == 3) {
 | 
|        // Print individual breakpoint.
 | 
| -      intptr_t id = atoi(js->GetArgument(2));
 | 
| -      SourceBreakpoint* bpt = isolate->debugger()->GetBreakpointById(id);
 | 
| +      intptr_t id = 0;
 | 
| +      SourceBreakpoint* bpt = NULL;
 | 
| +      if (GetIntegerId(js->GetArgument(2), &id)) {
 | 
| +        bpt = isolate->debugger()->GetBreakpointById(id);
 | 
| +      }
 | 
|        if (bpt != NULL) {
 | 
|          bpt->PrintToJSONStream(js);
 | 
|        } else {
 | 
| @@ -319,15 +597,40 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +static void HandleCode(Isolate* isolate, JSONStream* js) {
 | 
| +  REQUIRE_COLLECTION_ID("code");
 | 
| +  uintptr_t pc;
 | 
| +  if (!GetUnsignedIntegerId(js->GetArgument(1), &pc, 16)) {
 | 
| +    PrintError(js, "Must specify code address: code/c0deadd0.");
 | 
| +    return;
 | 
| +  }
 | 
| +  Code& code = Code::Handle(Code::LookupCode(pc));
 | 
| +  if (code.IsNull()) {
 | 
| +    PrintError(js, "Could not find code at %" Px "", pc);
 | 
| +    return;
 | 
| +  }
 | 
| +  code.PrintToJSONStream(js, false);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void HandleProfile(Isolate* isolate, JSONStream* js) {
 | 
| +  Profiler::PrintToJSONStream(isolate, js, true);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  static ServiceMessageHandlerEntry __message_handlers[] = {
 | 
|    { "_echo", HandleEcho },
 | 
|    { "classes", HandleClasses },
 | 
| +  { "code", HandleCode },
 | 
|    { "cpu", HandleCpu },
 | 
|    { "debug", HandleDebug },
 | 
| +  { "libraries", HandleLibraries },
 | 
|    { "library", HandleLibrary },
 | 
|    { "name", HandleName },
 | 
|    { "objecthistogram", HandleObjectHistogram},
 | 
|    { "objects", HandleObjects },
 | 
| +  { "profile", HandleProfile },
 | 
| +  { "scripts", HandleScripts },
 | 
|    { "stacktrace", HandleStackTrace },
 | 
|  };
 | 
|  
 | 
| 
 |