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:
+250
-21
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user