Refactor VM service IDs:

- IDs are strings which can be concatenated together like paths.
- Isolate ID is now "/isolates/XXX"
- Function, field, class, library, and script IDs are now stable.
- Introduce <type-ref> tags.

R=turnidge@google.com

Review URL: https://codereview.chromium.org//98253009

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@31358 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
johnmccutchan@google.com
2013-12-20 22:35:55 +00:00
parent 383468782e
commit 727d29d6b6
54 changed files with 39393 additions and 34058 deletions
+250 -21
View File
@@ -1808,6 +1808,32 @@ void Class::AddFunction(const Function& function) const {
}
intptr_t Class::FindFunctionIndex(const Function& needle) const {
Isolate* isolate = Isolate::Current();
if (EnsureIsFinalized(isolate) != Error::null()) {
return -1;
}
ReusableHandleScope reused_handles(isolate);
Array& funcs = reused_handles.ArrayHandle();
funcs ^= functions();
ASSERT(!funcs.IsNull());
Function& function = reused_handles.FunctionHandle();
String& function_name = reused_handles.StringHandle();
String& needle_name = String::Handle(isolate);
needle_name ^= needle.name();
const intptr_t len = funcs.Length();
for (intptr_t i = 0; i < len; i++) {
function ^= funcs.At(i);
function_name ^= function.name();
if (function_name.Equals(needle_name)) {
return i;
}
}
// No function found.
return -1;
}
void Class::AddClosureFunction(const Function& function) const {
GrowableObjectArray& closures =
GrowableObjectArray::Handle(raw_ptr()->closure_functions_);
@@ -1848,6 +1874,31 @@ RawFunction* Class::LookupClosureFunction(intptr_t token_pos) const {
return closure.raw();
}
intptr_t Class::FindClosureIndex(intptr_t token_pos) const {
if (raw_ptr()->closure_functions_ == GrowableObjectArray::null()) {
return -1;
}
Isolate* isolate = Isolate::Current();
ReusableHandleScope reused_handles(isolate);
const GrowableObjectArray& closures =
GrowableObjectArray::Handle(isolate, raw_ptr()->closure_functions_);
Function& closure = reused_handles.FunctionHandle();
intptr_t num_closures = closures.Length();
intptr_t best_fit_token_pos = -1;
intptr_t best_fit_index = -1;
for (intptr_t i = 0; i < num_closures; i++) {
closure ^= closures.At(i);
ASSERT(!closure.IsNull());
if ((closure.token_pos() <= token_pos) &&
(token_pos <= closure.end_token_pos()) &&
(best_fit_token_pos < closure.token_pos())) {
best_fit_index = i;
best_fit_token_pos = closure.token_pos();
}
}
return best_fit_index;
}
void Class::set_signature_function(const Function& value) const {
ASSERT(value.IsClosureFunction() || value.IsSignatureFunction());
@@ -2400,6 +2451,32 @@ void Class::SetFields(const Array& value) const {
}
intptr_t Class::FindFieldIndex(const Field& needle) const {
Isolate* isolate = Isolate::Current();
if (EnsureIsFinalized(isolate) != Error::null()) {
return -1;
}
ReusableHandleScope reused_handles(isolate);
Array& fields_array = reused_handles.ArrayHandle();
fields_array ^= fields();
ASSERT(!fields_array.IsNull());
Field& field = reused_handles.FieldHandle();
String& field_name = reused_handles.StringHandle();
String& needle_name = String::Handle(isolate);
needle_name ^= needle.name();
const intptr_t len = fields_array.Length();
for (intptr_t i = 0; i < len; i++) {
field ^= fields_array.At(i);
field_name ^= field.name();
if (field_name.Equals(needle_name)) {
return i;
}
}
// No field found found.
return -1;
}
template <class FakeInstance>
RawClass* Class::New(intptr_t index) {
ASSERT(Object::class_class() != Class::null());
@@ -3249,7 +3326,7 @@ void Class::PrintToJSONStream(JSONStream* stream, bool ref) const {
const char* user_visible_class_name =
String::Handle(UserVisibleName()).ToCString();
jsobj.AddProperty("type", JSONType(ref));
jsobj.AddProperty("id", id());
jsobj.AddPropertyF("id", "classes/%" Pd "", id());
jsobj.AddProperty("name", internal_class_name);
jsobj.AddProperty("user_name", user_visible_class_name);
if (!ref) {
@@ -5633,11 +5710,26 @@ void Function::PrintToJSONStream(JSONStream* stream, bool ref) const {
const char* internal_function_name = String::Handle(name()).ToCString();
const char* function_name =
String::Handle(QualifiedUserVisibleName()).ToCString();
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
Class& cls = Class::Handle(Owner());
Error& err = Error::Handle();
err ^= cls.EnsureIsFinalized(Isolate::Current());
ASSERT(err.IsNull());
const Function& func = *this;
intptr_t id;
if (IsNonImplicitClosureFunction()) {
id = cls.FindClosureIndex(token_pos());
} else {
id = cls.FindFunctionIndex(func);
}
ASSERT(id >= 0);
intptr_t cid = cls.id();
JSONObject jsobj(stream);
jsobj.AddProperty("type", JSONType(ref));
jsobj.AddProperty("id", id);
if (IsNonImplicitClosureFunction()) {
jsobj.AddPropertyF("id", "classes/%" Pd "/closures/%" Pd "", cid, id);
} else {
jsobj.AddPropertyF("id", "classes/%" Pd "/functions/%" Pd "", cid, id);
}
jsobj.AddProperty("name", internal_function_name);
jsobj.AddProperty("user_name", function_name);
if (ref) return;
@@ -5966,10 +6058,12 @@ void Field::PrintToJSONStreamWithInstance(JSONStream* stream,
JSONObject jsobj(stream);
const char* internal_field_name = String::Handle(name()).ToCString();
const char* field_name = String::Handle(UserVisibleName()).ToCString();
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
Class& cls = Class::Handle(owner());
intptr_t id = cls.FindFieldIndex(*this);
ASSERT(id >= 0);
intptr_t cid = cls.id();
jsobj.AddProperty("type", JSONType(ref));
jsobj.AddProperty("id", id);
jsobj.AddPropertyF("id", "classes/%" Pd "/fields/%" Pd "", cid, id);
jsobj.AddProperty("name", internal_field_name);
jsobj.AddProperty("user_name", field_name);
if (is_static()) {
@@ -5979,7 +6073,7 @@ void Field::PrintToJSONStreamWithInstance(JSONStream* stream,
const Object& valueObj = Object::Handle(instance.GetField(*this));
jsobj.AddProperty("value", valueObj);
}
Class& cls = Class::Handle(owner());
jsobj.AddProperty("owner", cls);
AbstractType& declared_type = AbstractType::Handle(type());
cls = declared_type.type_class();
@@ -7133,12 +7227,14 @@ const char* Script::ToCString() const {
void Script::PrintToJSONStream(JSONStream* stream, bool ref) const {
JSONObject jsobj(stream);
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
jsobj.AddProperty("type", JSONType(ref));
jsobj.AddProperty("id", id);
const String& name = String::Handle(url());
ASSERT(!name.IsNull());
const String& encoded_url = String::Handle(String::EncodeURI(name));
ASSERT(!encoded_url.IsNull());
jsobj.AddPropertyF("id", "scripts/%s", encoded_url.ToCString());
jsobj.AddProperty("name", name.ToCString());
jsobj.AddProperty("user_name", name.ToCString());
jsobj.AddProperty("kind", GetKindAsCString());
if (ref) {
return;
@@ -8207,13 +8303,13 @@ const char* Library::ToCString() const {
void Library::PrintToJSONStream(JSONStream* stream, bool ref) const {
const char* library_name = String::Handle(name()).ToCString();
const char* library_url = String::Handle(url()).ToCString();
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
intptr_t id = index();
ASSERT(id >= 0);
JSONObject jsobj(stream);
jsobj.AddProperty("type", JSONType(ref));
jsobj.AddProperty("id", id);
jsobj.AddPropertyF("id", "libraries/%" Pd "", id);
jsobj.AddProperty("name", library_name);
jsobj.AddProperty("url", library_url);
jsobj.AddProperty("user_name", library_url);
if (ref) return;
{
JSONArray jsarr(&jsobj, "classes");
@@ -9780,16 +9876,27 @@ const char* Code::ToCString() const {
void Code::PrintToJSONStream(JSONStream* stream, bool ref) const {
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
Isolate* isolate = Isolate::Current();
ObjectIdRing* ring = isolate->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
JSONObject jsobj(stream);
jsobj.AddProperty("type", JSONType(ref));
jsobj.AddPropertyF("id", "objects/%" Pd "", id);
Function& func = Function::Handle();
String& name = String::Handle();
func ^= function();
ASSERT(!func.IsNull());
name ^= func.name();
const char* internal_function_name = name.ToCString();
jsobj.AddPropertyF("name", "%s%s", is_optimized() ? "*" : "",
internal_function_name);
name ^= func.QualifiedUserVisibleName();
const char* function_name = name.ToCString();
jsobj.AddPropertyF("user_name", "%s%s", is_optimized() ? "*" : "",
function_name);
if (ref) {
jsobj.AddProperty("type", "@Code");
jsobj.AddProperty("id", id);
return;
}
jsobj.AddProperty("type", "Code");
jsobj.AddProperty("id", id);
jsobj.AddProperty("is_optimized", is_optimized());
jsobj.AddProperty("is_alive", is_alive());
jsobj.AddProperty("function", Object::Handle(function()));
@@ -11491,7 +11598,7 @@ void Instance::PrintToJSONStream(JSONStream* stream, bool ref) const {
JSONObject jsobj(stream);
jsobj.AddProperty("type", JSONType(ref));
jsobj.AddProperty("id", id);
jsobj.AddPropertyF("id", "objects/%" Pd "", id);
Class& cls = Class::Handle(this->clazz());
jsobj.AddProperty("class", cls);
@@ -14350,6 +14457,128 @@ RawString* String::EscapeSpecialCharacters(const String& str) {
}
static bool IsPercent(int32_t c) {
return c == '%';
}
static bool IsURISafeCharacter(int32_t c) {
if ((c >= '0') && (c <= '9')) {
return true;
}
if ((c >= 'a') && (c <= 'z')) {
return true;
}
if ((c >= 'A') && (c <= 'Z')) {
return true;
}
return (c == '-') || (c == '_') || (c == '.') || (c == '~');
}
static int32_t GetHexCharacter(int32_t c) {
ASSERT(c >= 0);
ASSERT(c < 16);
const char* hex = "0123456789ABCDEF";
return hex[c];
}
static int32_t GetHexValue(int32_t c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
UNREACHABLE();
return 0;
}
static int32_t MergeHexCharacters(int32_t c1, int32_t c2) {
return GetHexValue(c1) << 4 | GetHexValue(c2);
}
RawString* String::EncodeURI(const String& str) {
// URI encoding is only specified for one byte strings.
ASSERT(str.IsOneByteString() || str.IsExternalOneByteString());
intptr_t num_escapes = 0;
intptr_t len = str.Length();
{
CodePointIterator cpi(str);
while (cpi.Next()) {
int32_t code_point = cpi.Current();
if (!IsURISafeCharacter(code_point)) {
num_escapes += 2;
}
}
}
const String& dststr = String::Handle(
OneByteString::New(len + num_escapes, Heap::kNew));
{
intptr_t index = 0;
CodePointIterator cpi(str);
while (cpi.Next()) {
int32_t code_point = cpi.Current();
if (!IsURISafeCharacter(code_point)) {
OneByteString::SetCharAt(dststr, index, '%');
OneByteString::SetCharAt(dststr, index + 1,
GetHexCharacter(code_point >> 4));
OneByteString::SetCharAt(dststr, index + 2,
GetHexCharacter(code_point & 0xF));
index += 3;
} else {
OneByteString::SetCharAt(dststr, index, code_point);
index += 1;
}
}
}
return dststr.raw();
}
RawString* String::DecodeURI(const String& str) {
// URI encoding is only specified for one byte strings.
ASSERT(str.IsOneByteString() || str.IsExternalOneByteString());
CodePointIterator cpi(str);
intptr_t num_escapes = 0;
intptr_t len = str.Length();
{
CodePointIterator cpi(str);
while (cpi.Next()) {
int32_t code_point = cpi.Current();
if (IsPercent(code_point)) {
num_escapes += 2;
}
}
}
ASSERT(len - num_escapes > 0);
const String& dststr = String::Handle(
OneByteString::New(len - num_escapes, Heap::kNew));
{
intptr_t index = 0;
CodePointIterator cpi(str);
while (cpi.Next()) {
int32_t code_point = cpi.Current();
if (IsPercent(code_point)) {
ASSERT(cpi.Next());
int32_t ch1 = cpi.Current();
cpi.Next();
int32_t ch2 = cpi.Current();
int32_t merged = MergeHexCharacters(ch1, ch2);
OneByteString::SetCharAt(dststr, index, merged);
} else {
OneByteString::SetCharAt(dststr, index, code_point);
}
index++;
}
}
return dststr.raw();
}
RawString* String::NewFormatted(const char* format, ...) {
va_list args;
va_start(args, format);
+7 -2
View File
@@ -878,6 +878,7 @@ class Class : public Object {
RawArray* fields() const { return raw_ptr()->fields_; }
void SetFields(const Array& value) const;
intptr_t FindFieldIndex(const Field& field) const;
// Returns an array of all fields of this class and its superclasses indexed
// by offset in words.
@@ -889,12 +890,14 @@ class Class : public Object {
RawArray* functions() const { return raw_ptr()->functions_; }
void SetFunctions(const Array& value) const;
void AddFunction(const Function& function) const;
intptr_t FindFunctionIndex(const Function& function) const;
RawGrowableObjectArray* closures() const {
return raw_ptr()->closure_functions_;
}
void AddClosureFunction(const Function& function) const;
RawFunction* LookupClosureFunction(intptr_t token_pos) const;
intptr_t FindClosureIndex(intptr_t token_pos) const;
RawFunction* LookupDynamicFunction(const String& name) const;
RawFunction* LookupDynamicFunctionAllowPrivate(const String& name) const;
@@ -5072,7 +5075,8 @@ class String : public Instance {
intptr_t len);
static RawString* EscapeSpecialCharacters(const String& str);
static RawString* EncodeURI(const String& str);
static RawString* DecodeURI(const String& str);
static RawString* Concat(const String& str1,
const String& str2,
Heap::Space space = Heap::kNew);
@@ -5163,7 +5167,6 @@ class OneByteString : public AllStatic {
*CharAddr(str, index) = code_point;
}
static RawOneByteString* EscapeSpecialCharacters(const String& str);
// We use the same maximum elements for all strings.
static const intptr_t kBytesPerElement = 1;
static const intptr_t kMaxElements = String::kMaxElements;
@@ -5387,6 +5390,8 @@ class ExternalOneByteString : public AllStatic {
}
static RawOneByteString* EscapeSpecialCharacters(const String& str);
static RawOneByteString* EncodeURI(const String& str);
static RawOneByteString* DecodeURI(const String& str);
static const ClassId kClassId = kExternalOneByteStringCid;
+162
View File
@@ -332,6 +332,32 @@ TEST_CASE(StringCompareTo) {
}
TEST_CASE(StringEncodeURI) {
const char* kInput =
"file:///usr/local/johnmccutchan/workspace/dart-repo/dart/test.dart";
const char* kOutput =
"file%3A%2F%2F%2Fusr%2Flocal%2Fjohnmccutchan%2Fworkspace%2F"
"dart-repo%2Fdart%2Ftest.dart";
const String& input = String::Handle(String::New(kInput));
const String& output = String::Handle(String::New(kOutput));
const String& encoded = String::Handle(String::EncodeURI(input));
EXPECT(output.Equals(encoded));
}
TEST_CASE(StringDecodeURI) {
const char* kOutput =
"file:///usr/local/johnmccutchan/workspace/dart-repo/dart/test.dart";
const char* kInput =
"file%3A%2F%2F%2Fusr%2Flocal%2Fjohnmccutchan%2Fworkspace%2F"
"dart-repo%2Fdart%2Ftest.dart";
const String& input = String::Handle(String::New(kInput));
const String& output = String::Handle(String::New(kOutput));
const String& decoded = String::Handle(String::DecodeURI(input));
EXPECT(output.Equals(decoded));
}
TEST_CASE(Mint) {
// On 64-bit architectures a Smi is stored in a 64 bit word. A Midint cannot
// be allocated if it does fit into a Smi.
@@ -2749,6 +2775,8 @@ TEST_CASE(FieldTests) {
}
// Expose helper function from object.cc for testing.
bool EqualsIgnoringPrivate(const String& name, const String& private_name);
@@ -3391,6 +3419,140 @@ static RawClass* GetClass(const Library& lib, const char* name) {
}
TEST_CASE(FindFieldIndex) {
const char* kScriptChars =
"class A {\n"
" var a;\n"
" var b;\n"
"}\n"
"class B {\n"
" var d;\n"
"}\n"
"test() {\n"
" new A();\n"
" new B();\n"
"}";
Dart_Handle h_lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(h_lib);
Dart_Handle result = Dart_Invoke(h_lib, NewString("test"), 0, NULL);
EXPECT_VALID(result);
Library& lib = Library::Handle();
lib ^= Api::UnwrapHandle(h_lib);
EXPECT(!lib.IsNull());
const Class& class_a = Class::Handle(GetClass(lib, "A"));
const Array& class_a_fields = Array::Handle(class_a.fields());
const Class& class_b = Class::Handle(GetClass(lib, "B"));
const Field& field_a = Field::Handle(GetField(class_a, "a"));
const Field& field_b = Field::Handle(GetField(class_a, "b"));
const Field& field_d = Field::Handle(GetField(class_b, "d"));
intptr_t field_a_index = class_a.FindFieldIndex(field_a);
intptr_t field_b_index = class_a.FindFieldIndex(field_b);
intptr_t field_d_index = class_a.FindFieldIndex(field_d);
// Valid index.
EXPECT_GE(field_a_index, 0);
// Valid index.
EXPECT_GE(field_b_index, 0);
// Invalid index.
EXPECT_EQ(field_d_index, -1);
Field& field_a_from_index = Field::Handle();
field_a_from_index ^= class_a_fields.At(field_a_index);
ASSERT(!field_a_from_index.IsNull());
// Same field.
EXPECT_EQ(field_a.raw(), field_a_from_index.raw());
Field& field_b_from_index = Field::Handle();
field_b_from_index ^= class_a_fields.At(field_b_index);
ASSERT(!field_b_from_index.IsNull());
// Same field.
EXPECT_EQ(field_b.raw(), field_b_from_index.raw());
}
TEST_CASE(FindFunctionIndex) {
const char* kScriptChars =
"class A {\n"
" void a() {}\n"
" void b() {}\n"
"}\n"
"class B {\n"
" dynamic d() {}\n"
"}\n"
"test() {\n"
" new A();\n"
" new B();\n"
"}";
Dart_Handle h_lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(h_lib);
Dart_Handle result = Dart_Invoke(h_lib, NewString("test"), 0, NULL);
EXPECT_VALID(result);
Library& lib = Library::Handle();
lib ^= Api::UnwrapHandle(h_lib);
EXPECT(!lib.IsNull());
const Class& class_a = Class::Handle(GetClass(lib, "A"));
const Array& class_a_funcs = Array::Handle(class_a.functions());
const Class& class_b = Class::Handle(GetClass(lib, "B"));
const Function& func_a = Function::Handle(GetFunction(class_a, "a"));
const Function& func_b = Function::Handle(GetFunction(class_a, "b"));
const Function& func_d = Function::Handle(GetFunction(class_b, "d"));
intptr_t func_a_index = class_a.FindFunctionIndex(func_a);
intptr_t func_b_index = class_a.FindFunctionIndex(func_b);
intptr_t func_d_index = class_a.FindFunctionIndex(func_d);
// Valid index.
EXPECT_GE(func_a_index, 0);
// Valid index.
EXPECT_GE(func_b_index, 0);
// Invalid index.
EXPECT_EQ(func_d_index, -1);
Function& func_a_from_index = Function::Handle();
func_a_from_index ^= class_a_funcs.At(func_a_index);
ASSERT(!func_a_from_index.IsNull());
// Same function.
EXPECT_EQ(func_a.raw(), func_a_from_index.raw());
Function& func_b_from_index = Function::Handle();
func_b_from_index ^= class_a_funcs.At(func_b_index);
ASSERT(!func_b_from_index.IsNull());
// Same function.
EXPECT_EQ(func_b.raw(), func_b_from_index.raw());
}
TEST_CASE(FindClosureIndex) {
// Allocate the class first.
const String& class_name = String::Handle(Symbols::New("MyClass"));
const Script& script = Script::Handle();
const Class& cls = Class::Handle(CreateDummyClass(class_name, script));
const Array& functions = Array::Handle(Array::New(1));
Function& parent = Function::Handle();
const String& parent_name = String::Handle(Symbols::New("foo_papa"));
parent = Function::New(parent_name, RawFunction::kRegularFunction,
false, false, false, false, false, cls, 0);
functions.SetAt(0, parent);
cls.SetFunctions(functions);
Function& function = Function::Handle();
const String& function_name = String::Handle(Symbols::New("foo"));
function = Function::NewClosureFunction(function_name, parent, 0);
// Add closure function to class.
cls.AddClosureFunction(function);
// Token position 0 should return a valid index.
intptr_t good_closure_index = cls.FindClosureIndex(0);
EXPECT_GE(good_closure_index, 0);
// Token position 1 should return an invalid index.
intptr_t bad_closure_index = cls.FindClosureIndex(1);
EXPECT_EQ(bad_closure_index, -1);
// Retrieve closure function via index.
const GrowableObjectArray& closures = GrowableObjectArray::Handle(
cls.closures());
Function& func_from_index = Function::Handle();
func_from_index ^= closures.At(good_closure_index);
// Same closure function.
EXPECT_EQ(func_from_index.raw(), function.raw());
}
static void PrintMetadata(const char* name, const Object& data) {
if (data.IsError()) {
OS::Print("Error in metadata evaluation for %s: '%s'\n",
+215 -11
View File
@@ -196,15 +196,15 @@ static void HandleStackTrace(Isolate* isolate, JSONStream* js) {
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 +239,93 @@ static void HandleEcho(Isolate* isolate, JSONStream* js) {
}
#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) {
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, 10);
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) {
const GrowableObjectArray& closures =
GrowableObjectArray::Handle(cls.closures());
intptr_t id;
if (js->num_arguments() > 4) {
PrintError(js, "Command too long");
return;
}
CHECK_COLLECTION_ID_BOUNDS("closures", closures.Length(), js->GetArgument(3),
id, js);
Function& function = Function::Handle();
function ^= closures.At(id);
ASSERT(!function.IsNull());
function.PrintToJSONStream(js, false);
}
static void HandleClassesFunctions(Isolate* isolate, const Class& cls,
JSONStream* js) {
const Array& functions =
Array::Handle(cls.functions());
intptr_t id;
if (js->num_arguments() > 4) {
PrintError(js, "Command too long");
return;
}
CHECK_COLLECTION_ID_BOUNDS("functions", functions.Length(),
js->GetArgument(3), id, js);
Function& function = Function::Handle();
function ^= functions.At(id);
ASSERT(!function.IsNull());
function.PrintToJSONStream(js, false);
}
static void HandleClassesFields(Isolate* isolate, const Class& cls,
JSONStream* js) {
const Array& fields =
Array::Handle(cls.fields());
intptr_t id;
if (js->num_arguments() > 4) {
PrintError(js, "Command too long");
return;
}
CHECK_COLLECTION_ID_BOUNDS("fields", fields.Length(), js->GetArgument(3),
id, js);
Field& field = Field::Handle();
field ^= fields.At(id);
ASSERT(!field.IsNull());
field.PrintToJSONStream(js, false);
}
static void HandleClasses(Isolate* isolate, JSONStream* js) {
if (js->num_arguments() == 1) {
ClassTable* table = isolate->class_table();
@@ -246,14 +333,34 @@ static void HandleClasses(Isolate* isolate, JSONStream* js) {
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));
cls.PrintToJSONStream(js, false);
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 {
PrintError(js, "Invalid sub collection %s", second);
}
return;
}
UNREACHABLE();
}
@@ -268,17 +375,109 @@ static void HandleLibrary(Isolate* isolate, JSONStream* js) {
}
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 +494,11 @@ static void HandleDebug(Isolate* isolate, JSONStream* js) {
} 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 {
@@ -324,10 +526,12 @@ static ServiceMessageHandlerEntry __message_handlers[] = {
{ "classes", HandleClasses },
{ "cpu", HandleCpu },
{ "debug", HandleDebug },
{ "libraries", HandleLibraries },
{ "library", HandleLibrary },
{ "name", HandleName },
{ "objecthistogram", HandleObjectHistogram},
{ "objects", HandleObjects },
{ "scripts", HandleScripts },
{ "stacktrace", HandleStackTrace },
};
+212
View File
@@ -54,6 +54,58 @@ static RawInstance* Eval(Dart_Handle lib, const char* expr) {
}
static RawInstance* EvalF(Dart_Handle lib, const char* fmt, ...) {
Isolate* isolate = Isolate::Current();
va_list args;
va_start(args, fmt);
intptr_t len = OS::VSNPrint(NULL, 0, fmt, args);
va_end(args);
char* buffer = isolate->current_zone()->Alloc<char>(len + 1);
va_list args2;
va_start(args2, fmt);
OS::VSNPrint(buffer, (len + 1), fmt, args2);
va_end(args2);
return Eval(lib, buffer);
}
/*
static RawFunction* GetFunction(const Class& cls, const char* name) {
const Function& result = Function::Handle(cls.LookupDynamicFunction(
String::Handle(String::New(name))));
EXPECT(!result.IsNull());
return result.raw();
}
static RawFunction* GetStaticFunction(const Class& cls, const char* name) {
const Function& result = Function::Handle(cls.LookupStaticFunction(
String::Handle(String::New(name))));
EXPECT(!result.IsNull());
return result.raw();
}
static RawField* GetField(const Class& cls, const char* name) {
const Field& field =
Field::Handle(cls.LookupField(String::Handle(String::New(name))));
EXPECT(!field.IsNull());
return field.raw();
}
*/
static RawClass* GetClass(const Library& lib, const char* name) {
const Class& cls = Class::Handle(
lib.LookupClass(String::Handle(Symbols::New(name))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
return cls.raw();
}
TEST_CASE(Service_DebugBreakpoints) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
@@ -146,4 +198,164 @@ TEST_CASE(Service_DebugBreakpoints) {
handler.msg());
}
TEST_CASE(Service_Classes) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"class A {\n"
" var a;\n"
" dynamic b() {}\n"
" dynamic c() {\n"
" var d = () { b(); };\n"
" return d;\n"
" }\n"
"}\n"
"main() {\n"
" var z = new A();\n"
" var x = z.c();\n"
" x();\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle h_lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(h_lib);
Library& lib = Library::Handle();
lib ^= Api::UnwrapHandle(h_lib);
EXPECT(!lib.IsNull());
Dart_Handle result = Dart_Invoke(h_lib, NewString("main"), 0, NULL);
EXPECT_VALID(result);
const Class& class_a = Class::Handle(GetClass(lib, "A"));
EXPECT(!class_a.IsNull());
intptr_t cid = class_a.id();
// Build a mock message handler and wrap it in a dart port.
ServiceTestMessageHandler handler;
Dart_Port port_id = PortMap::CreatePort(&handler);
Dart_Handle port =
Api::NewHandle(isolate, DartLibraryCalls::NewSendPort(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(h_lib, NewString("port"), port));
Instance& service_msg = Instance::Handle();
// Request an invalid class id.
service_msg = Eval(h_lib, "[port, ['classes', '999999'], [], []]");
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"text\":\"999999 is not a valid class id.\","
"\"message\":{\"arguments\":[\"classes\",\"999999\"],"
"\"option_keys\":[],\"option_values\":[]}}", handler.msg());
// Request the class A over the service.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "'], [], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Class\",\"id\":\"classes\\/1009\",\"name\":\"A\","
"\"user_name\":\"A\",\"implemented\":false,\"abstract\":false,"
"\"patch\":false,\"finalized\":true,\"const\":false,\"super\":"
"{\"type\":\"@Class\",\"id\":\"classes\\/35\",\"name\":\"Object\","
"\"user_name\":\"Object\"},\"library\":{\"type\":\"@Library\",\"id\":"
"\"libraries\\/12\",\"name\":\"\",\"user_name\":\"dart:test-lib\"},"
"\"fields\":[{\"type\":\"@Field\",\"id\":\"classes\\/1009\\/fields\\/0\","
"\"name\":\"a\",\"user_name\":\"a\",\"owner\":{\"type\":\"@Class\","
"\"id\":\"classes\\/1009\",\"name\":\"A\",\"user_name\":\"A\"},"
"\"declared_type\":{\"type\":\"@Class\",\"id\":\"classes\\/106\","
"\"name\":\"dynamic\",\"user_name\":\"dynamic\"},\"static\":false,"
"\"final\":false,\"const\":false}],\"functions\":["
"{\"type\":\"@Function\",\"id\":\"classes\\/1009\\/functions\\/0\","
"\"name\":\"get:a\",\"user_name\":\"A.a\"},{\"type\":\"@Function\","
"\"id\":\"classes\\/1009\\/functions\\/1\",\"name\":\"set:a\","
"\"user_name\":\"A.a=\"},{\"type\":\"@Function\",\"id\":"
"\"classes\\/1009\\/functions\\/2\",\"name\":\"b\",\"user_name\":\"A.b\"}"
",{\"type\":\"@Function\",\"id\":\"classes\\/1009\\/functions\\/3\","
"\"name\":\"c\",\"user_name\":\"A.c\"},{\"type\":\"@Function\",\"id\":"
"\"classes\\/1009\\/functions\\/4\",\"name\":\"A.\",\"user_name\":"
"\"A.A\"}]}",
handler.msg());
// Request function 0 from class A.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "', 'functions', '0'],"
"[], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Function\",\"id\":\"classes\\/1009\\/functions\\/0\",\"name\":"
"\"get:a\",\"user_name\":\"A.a\",\"is_static\":false,\"is_const\":false,"
"\"is_optimizable\":true,\"is_inlinable\":false,\"kind\":"
"\"implicit getter\",\"unoptimized_code\":{\"type\":\"null\"},"
"\"usage_counter\":0,\"optimized_call_site_count\":0,\"code\":"
"{\"type\":\"null\"},\"deoptimizations\":0}", handler.msg());
// Request field 0 from class A.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "', 'fields', '0'],"
"[], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Field\",\"id\":\"classes\\/1009\\/fields\\/0\",\"name\":\"a\","
"\"user_name\":\"a\",\"owner\":{\"type\":\"@Class\",\"id\":"
"\"classes\\/1009\",\"name\":\"A\",\"user_name\":\"A\"},\"declared_type\":"
"{\"type\":\"@Class\",\"id\":\"classes\\/106\",\"name\":\"dynamic\","
"\"user_name\":\"dynamic\"},\"static\":false,\"final\":false,\"const\":"
"false,\"guard_nullable\":true,\"guard_class\":{\"type\":\"@Class\","
"\"id\":\"classes\\/105\",\"name\":\"Null\",\"user_name\":\"Null\"},"
"\"guard_length\":\"variable\"}", handler.msg());
// Invalid sub command.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "', 'huh', '0'],"
"[], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"text\":\"Invalid sub collection huh\",\"message\":"
"{\"arguments\":[\"classes\",\"1009\",\"huh\",\"0\"],\"option_keys\":[],"
"\"option_values\":[]}}", handler.msg());
// Invalid field request.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "', 'fields', '9'],"
"[], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"text\":\"fields id (9) must be in [0, 1).\","
"\"message\":{\"arguments\":[\"classes\",\"1009\",\"fields\",\"9\"],"
"\"option_keys\":[],\"option_values\":[]}}", handler.msg());
// Invalid function request.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "', 'functions', '9'],"
"[], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"text\":\"functions id (9) must be in [0, 5).\","
"\"message\":{\"arguments\":[\"classes\",\"1009\",\"functions\",\"9\"],"
"\"option_keys\":[],\"option_values\":[]}}", handler.msg());
// Invalid field subcommand.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "', 'fields', '9', 'x']"
",[], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"text\":\"Command too long\",\"message\":"
"{\"arguments\":[\"classes\",\"1009\",\"fields\",\"9\",\"x\"],"
"\"option_keys\":[],\"option_values\":[]}}",
handler.msg());
// Invalid function request.
service_msg = EvalF(h_lib, "[port, ['classes', '%" Pd "', 'functions', '9',"
"'x'], [], []]", cid);
Service::HandleServiceMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"text\":\"Command too long\",\"message\":"
"{\"arguments\":[\"classes\",\"1009\",\"functions\",\"9\",\"x\"],"
"\"option_keys\":[],\"option_values\":[]}}",
handler.msg());
}
} // namespace dart