| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "platform/globals.h" |
| |
| #include "include/dart_tools_api.h" |
| #include "vm/dart_api_impl.h" |
| #include "vm/dart_entry.h" |
| #include "vm/debugger.h" |
| #include "vm/debugger_api_impl_test.h" |
| #include "vm/globals.h" |
| #include "vm/heap/safepoint.h" |
| #include "vm/message_handler.h" |
| #include "vm/object_id_ring.h" |
| #include "vm/os.h" |
| #include "vm/port.h" |
| #include "vm/profiler.h" |
| #include "vm/service.h" |
| #include "vm/unit_test.h" |
| |
| namespace dart { |
| |
| // This flag is used in the Service_Flags test below. |
| DEFINE_FLAG(bool, service_testing_flag, false, "Comment"); |
| |
| #ifndef PRODUCT |
| |
| class ServiceTestMessageHandler : public MessageHandler { |
| public: |
| ServiceTestMessageHandler() : _msg(NULL) {} |
| |
| ~ServiceTestMessageHandler() { |
| PortMap::ClosePorts(this); |
| free(_msg); |
| } |
| |
| MessageStatus HandleMessage(Message* message) { |
| if (_msg != NULL) { |
| free(_msg); |
| _msg = NULL; |
| } |
| |
| // Parse the message. |
| Object& response_obj = Object::Handle(); |
| if (message->IsRaw()) { |
| response_obj = message->raw_obj(); |
| } else { |
| Thread* thread = Thread::Current(); |
| MessageSnapshotReader reader(message, thread); |
| response_obj = reader.ReadObject(); |
| } |
| if (response_obj.IsString()) { |
| String& response = String::Handle(); |
| response ^= response_obj.raw(); |
| _msg = strdup(response.ToCString()); |
| } else { |
| ASSERT(response_obj.IsArray()); |
| Array& response_array = Array::Handle(); |
| response_array ^= response_obj.raw(); |
| ASSERT(response_array.Length() == 1); |
| ExternalTypedData& response = ExternalTypedData::Handle(); |
| response ^= response_array.At(0); |
| _msg = strdup(reinterpret_cast<char*>(response.DataAddr(0))); |
| } |
| |
| delete message; |
| |
| return kOK; |
| } |
| |
| const char* msg() const { return _msg; } |
| |
| virtual Isolate* isolate() const { return Isolate::Current(); } |
| |
| private: |
| char* _msg; |
| }; |
| |
| static RawArray* Eval(Dart_Handle lib, const char* expr) { |
| const String& dummy_isolate_id = String::Handle(String::New("isolateId")); |
| Dart_Handle expr_val; |
| { |
| TransitionVMToNative transiton(Thread::Current()); |
| expr_val = Dart_EvaluateStaticExpr(lib, NewString(expr)); |
| EXPECT_VALID(expr_val); |
| } |
| Zone* zone = Thread::Current()->zone(); |
| const GrowableObjectArray& value = |
| Api::UnwrapGrowableObjectArrayHandle(zone, expr_val); |
| const Array& result = Array::Handle(Array::MakeFixedLength(value)); |
| GrowableObjectArray& growable = GrowableObjectArray::Handle(); |
| growable ^= result.At(4); |
| // Append dummy isolate id to parameter values. |
| growable.Add(dummy_isolate_id); |
| Array& array = Array::Handle(Array::MakeFixedLength(growable)); |
| result.SetAt(4, array); |
| growable ^= result.At(5); |
| // Append dummy isolate id to parameter values. |
| growable.Add(dummy_isolate_id); |
| array = Array::MakeFixedLength(growable); |
| result.SetAt(5, array); |
| return result.raw(); |
| } |
| |
| static RawArray* EvalF(Dart_Handle lib, const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| intptr_t len = Utils::VSNPrint(NULL, 0, fmt, args); |
| va_end(args); |
| |
| char* buffer = Thread::Current()->zone()->Alloc<char>(len + 1); |
| va_list args2; |
| va_start(args2, fmt); |
| Utils::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 RawClass* GetClass(const Library& lib, const char* name) { |
| const Class& cls = Class::Handle( |
| lib.LookupClass(String::Handle(Symbols::New(Thread::Current(), name)))); |
| EXPECT(!cls.IsNull()); // No ambiguity error expected. |
| return cls.raw(); |
| } |
| |
| static void HandleIsolateMessage(Isolate* isolate, const Array& msg) { |
| Service::HandleIsolateMessage(isolate, msg); |
| } |
| |
| static void HandleRootMessage(const Array& message) { |
| Service::HandleRootMessage(message); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_IsolateStickyError) { |
| const char* kScript = "main() => throw 'HI THERE STICKY';\n"; |
| |
| Isolate* isolate = thread->isolate(); |
| isolate->set_is_runnable(true); |
| Dart_Handle result; |
| { |
| TransitionVMToNative transition(thread); |
| Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT(Dart_IsUnhandledExceptionError(result)); |
| EXPECT(!Dart_HasStickyError()); |
| } |
| EXPECT(Thread::Current()->sticky_error() == Error::null()); |
| |
| { |
| JSONStream js; |
| isolate->PrintJSON(&js, false); |
| // No error property and no PauseExit state. |
| EXPECT_NOTSUBSTRING("\"error\":", js.ToCString()); |
| EXPECT_NOTSUBSTRING("HI THERE STICKY", js.ToCString()); |
| EXPECT_NOTSUBSTRING("PauseExit", js.ToCString()); |
| } |
| |
| { |
| // Set the sticky error. |
| TransitionVMToNative transition(thread); |
| Dart_SetStickyError(result); |
| Dart_SetPausedOnExit(true); |
| EXPECT(Dart_HasStickyError()); |
| } |
| |
| { |
| JSONStream js; |
| isolate->PrintJSON(&js, false); |
| // Error and PauseExit set. |
| EXPECT_SUBSTRING("\"error\":", js.ToCString()); |
| EXPECT_SUBSTRING("HI THERE STICKY", js.ToCString()); |
| EXPECT_SUBSTRING("PauseExit", js.ToCString()); |
| } |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_IdZones) { |
| Zone* zone = thread->zone(); |
| Isolate* isolate = thread->isolate(); |
| ObjectIdRing* ring = isolate->object_id_ring(); |
| |
| const String& test_a = String::Handle(zone, String::New("a")); |
| const String& test_b = String::Handle(zone, String::New("b")); |
| const String& test_c = String::Handle(zone, String::New("c")); |
| const String& test_d = String::Handle(zone, String::New("d")); |
| |
| // Both RingServiceIdZones share the same backing store and id space. |
| |
| // Always allocate a new id. |
| RingServiceIdZone always_new_zone; |
| always_new_zone.Init(ring, ObjectIdRing::kAllocateId); |
| EXPECT_STREQ("objects/0", always_new_zone.GetServiceId(test_a)); |
| EXPECT_STREQ("objects/1", always_new_zone.GetServiceId(test_a)); |
| EXPECT_STREQ("objects/2", always_new_zone.GetServiceId(test_a)); |
| EXPECT_STREQ("objects/3", always_new_zone.GetServiceId(test_b)); |
| EXPECT_STREQ("objects/4", always_new_zone.GetServiceId(test_c)); |
| |
| // Reuse an existing id or allocate a new id. |
| RingServiceIdZone reuse_zone; |
| reuse_zone.Init(ring, ObjectIdRing::kReuseId); |
| EXPECT_STREQ("objects/0", reuse_zone.GetServiceId(test_a)); |
| EXPECT_STREQ("objects/0", reuse_zone.GetServiceId(test_a)); |
| EXPECT_STREQ("objects/3", reuse_zone.GetServiceId(test_b)); |
| EXPECT_STREQ("objects/3", reuse_zone.GetServiceId(test_b)); |
| EXPECT_STREQ("objects/4", reuse_zone.GetServiceId(test_c)); |
| EXPECT_STREQ("objects/4", reuse_zone.GetServiceId(test_c)); |
| EXPECT_STREQ("objects/5", reuse_zone.GetServiceId(test_d)); |
| EXPECT_STREQ("objects/5", reuse_zone.GetServiceId(test_d)); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_Code) { |
| 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 = thread->isolate(); |
| isolate->set_is_runnable(true); |
| Dart_Handle lib; |
| Library& vmlib = Library::Handle(); |
| { |
| TransitionVMToNative transition(thread); |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| vmlib ^= Api::UnwrapHandle(lib); |
| EXPECT(!vmlib.IsNull()); |
| Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| } |
| const Class& class_a = Class::Handle(GetClass(vmlib, "A")); |
| EXPECT(!class_a.IsNull()); |
| const Function& function_c = Function::Handle(GetFunction(class_a, "c")); |
| EXPECT(!function_c.IsNull()); |
| const Code& code_c = Code::Handle(function_c.CurrentCode()); |
| EXPECT(!code_c.IsNull()); |
| // Use the entry of the code object as it's reference. |
| uword entry = code_c.PayloadStart(); |
| int64_t compile_timestamp = code_c.compile_timestamp(); |
| EXPECT_GT(code_c.Size(), 16); |
| uword last = entry + code_c.Size(); |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| Array& service_msg = Array::Handle(); |
| |
| // Request an invalid code object. |
| service_msg = |
| Eval(lib, "[0, port, '0', 'getObject', ['objectId'], ['code/0']]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_SUBSTRING("\"error\"", handler.msg()); |
| |
| // The following test checks that a code object can be found only |
| // at compile_timestamp()-code.EntryPoint(). |
| service_msg = EvalF(lib, |
| "[0, port, '0', 'getObject', " |
| "['objectId'], ['code/%" Px64 "-%" Px "']]", |
| compile_timestamp, entry); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_SUBSTRING("\"type\":\"Code\"", handler.msg()); |
| { |
| // Only perform a partial match. |
| const intptr_t kBufferSize = 512; |
| char buffer[kBufferSize]; |
| Utils::SNPrint(buffer, kBufferSize - 1, |
| "\"fixedId\":true,\"id\":\"code\\/%" Px64 "-%" Px "\",", |
| compile_timestamp, entry); |
| EXPECT_SUBSTRING(buffer, handler.msg()); |
| } |
| |
| // Request code object at compile_timestamp-code.EntryPoint() + 16 |
| // Expect this to fail because the address is not the entry point. |
| uintptr_t address = entry + 16; |
| service_msg = EvalF(lib, |
| "[0, port, '0', 'getObject', " |
| "['objectId'], ['code/%" Px64 "-%" Px "']]", |
| compile_timestamp, address); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_SUBSTRING("\"error\"", handler.msg()); |
| |
| // Request code object at (compile_timestamp - 1)-code.EntryPoint() |
| // Expect this to fail because the timestamp is wrong. |
| address = entry; |
| service_msg = EvalF(lib, |
| "[0, port, '0', 'getObject', " |
| "['objectId'], ['code/%" Px64 "-%" Px "']]", |
| compile_timestamp - 1, address); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_SUBSTRING("\"error\"", handler.msg()); |
| |
| // Request native code at address. Expect the null code object back. |
| address = last; |
| service_msg = EvalF(lib, |
| "[0, port, '0', 'getObject', " |
| "['objectId'], ['code/native-%" Px "']]", |
| address); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // TODO(turnidge): It is pretty broken to return an Instance here. Fix. |
| EXPECT_SUBSTRING("\"kind\":\"Null\"", handler.msg()); |
| |
| // Request malformed native code. |
| service_msg = EvalF(lib, |
| "[0, port, '0', 'getObject', ['objectId'], " |
| "['code/native%" Px "']]", |
| address); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_SUBSTRING("\"error\"", handler.msg()); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_PcDescriptors) { |
| 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 = thread->isolate(); |
| isolate->set_is_runnable(true); |
| Dart_Handle lib; |
| Library& vmlib = Library::Handle(); |
| { |
| TransitionVMToNative transition(thread); |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| vmlib ^= Api::UnwrapHandle(lib); |
| EXPECT(!vmlib.IsNull()); |
| Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| } |
| const Class& class_a = Class::Handle(GetClass(vmlib, "A")); |
| EXPECT(!class_a.IsNull()); |
| const Function& function_c = Function::Handle(GetFunction(class_a, "c")); |
| EXPECT(!function_c.IsNull()); |
| const Code& code_c = Code::Handle(function_c.CurrentCode()); |
| EXPECT(!code_c.IsNull()); |
| |
| const PcDescriptors& descriptors = |
| PcDescriptors::Handle(code_c.pc_descriptors()); |
| EXPECT(!descriptors.IsNull()); |
| ObjectIdRing* ring = isolate->object_id_ring(); |
| intptr_t id = ring->GetIdForObject(descriptors.raw()); |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| Array& service_msg = Array::Handle(); |
| |
| // Fetch object. |
| service_msg = EvalF(lib, |
| "[0, port, '0', 'getObject', " |
| "['objectId'], ['objects/%" Pd "']]", |
| id); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // Check type. |
| EXPECT_SUBSTRING("\"type\":\"Object\"", handler.msg()); |
| EXPECT_SUBSTRING("\"_vmType\":\"PcDescriptors\"", handler.msg()); |
| // Check for members array. |
| EXPECT_SUBSTRING("\"members\":[", handler.msg()); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_LocalVarDescriptors) { |
| 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 = thread->isolate(); |
| isolate->set_is_runnable(true); |
| Dart_Handle lib; |
| Library& vmlib = Library::Handle(); |
| { |
| TransitionVMToNative transition(thread); |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| vmlib ^= Api::UnwrapHandle(lib); |
| EXPECT(!vmlib.IsNull()); |
| Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| } |
| const Class& class_a = Class::Handle(GetClass(vmlib, "A")); |
| EXPECT(!class_a.IsNull()); |
| const Function& function_c = Function::Handle(GetFunction(class_a, "c")); |
| EXPECT(!function_c.IsNull()); |
| const Code& code_c = Code::Handle(function_c.CurrentCode()); |
| EXPECT(!code_c.IsNull()); |
| |
| const LocalVarDescriptors& descriptors = |
| LocalVarDescriptors::Handle(code_c.GetLocalVarDescriptors()); |
| // Generate an ID for this object. |
| ObjectIdRing* ring = isolate->object_id_ring(); |
| intptr_t id = ring->GetIdForObject(descriptors.raw()); |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| Array& service_msg = Array::Handle(); |
| |
| // Fetch object. |
| service_msg = EvalF(lib, |
| "[0, port, '0', 'getObject', " |
| "['objectId'], ['objects/%" Pd "']]", |
| id); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // Check type. |
| EXPECT_SUBSTRING("\"type\":\"Object\"", handler.msg()); |
| EXPECT_SUBSTRING("\"_vmType\":\"LocalVarDescriptors\"", handler.msg()); |
| // Check for members array. |
| EXPECT_SUBSTRING("\"members\":[", handler.msg()); |
| } |
| |
| static void WeakHandleFinalizer(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) {} |
| |
| ISOLATE_UNIT_TEST_CASE(Service_PersistentHandles) { |
| const char* kScript = |
| "var port;\n" // Set to our mock port by C++. |
| "\n" |
| "class A {\n" |
| " var a;\n" |
| "}\n" |
| "var global = new A();\n" |
| "main() {\n" |
| " return global;\n" |
| "}"; |
| |
| Isolate* isolate = thread->isolate(); |
| isolate->set_is_runnable(true); |
| |
| Dart_Handle lib; |
| Dart_PersistentHandle persistent_handle; |
| Dart_WeakPersistentHandle weak_persistent_handle; |
| { |
| TransitionVMToNative transition(thread); |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| |
| // Create a persistent handle to global. |
| persistent_handle = Dart_NewPersistentHandle(result); |
| |
| // Create a weak persistent handle to global. |
| weak_persistent_handle = Dart_NewWeakPersistentHandle( |
| result, reinterpret_cast<void*>(0xdeadbeef), 128, WeakHandleFinalizer); |
| } |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| Array& service_msg = Array::Handle(); |
| |
| // Get persistent handles. |
| service_msg = Eval(lib, "[0, port, '0', '_getPersistentHandles', [], []]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // Look for a heart beat. |
| EXPECT_SUBSTRING("\"type\":\"_PersistentHandles\"", handler.msg()); |
| EXPECT_SUBSTRING("\"peer\":\"0xdeadbeef\"", handler.msg()); |
| EXPECT_SUBSTRING("\"name\":\"A\"", handler.msg()); |
| EXPECT_SUBSTRING("\"externalSize\":\"128\"", handler.msg()); |
| |
| // Delete persistent handles. |
| { |
| TransitionVMToNative transition(thread); |
| Dart_DeletePersistentHandle(persistent_handle); |
| Dart_DeleteWeakPersistentHandle(Dart_CurrentIsolate(), |
| weak_persistent_handle); |
| } |
| |
| // Get persistent handles (again). |
| service_msg = Eval(lib, "[0, port, '0', '_getPersistentHandles', [], []]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_SUBSTRING("\"type\":\"_PersistentHandles\"", handler.msg()); |
| // Verify that old persistent handles are not present. |
| EXPECT_NOTSUBSTRING("\"peer\":\"0xdeadbeef\"", handler.msg()); |
| EXPECT_NOTSUBSTRING("\"name\":\"A\"", handler.msg()); |
| EXPECT_NOTSUBSTRING("\"externalSize\":\"128\"", handler.msg()); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_Address) { |
| const char* kScript = |
| "var port;\n" // Set to our mock port by C++. |
| "\n" |
| "main() {\n" |
| "}"; |
| |
| Isolate* isolate = thread->isolate(); |
| isolate->set_is_runnable(true); |
| Dart_Handle lib; |
| { |
| TransitionVMToNative transition(thread); |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| } |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| const String& str = String::Handle(String::New("foobar", Heap::kOld)); |
| Array& service_msg = Array::Handle(); |
| // Note: If we ever introduce old space compaction, this test might fail. |
| uword start_addr = RawObject::ToAddr(str.raw()); |
| // Expect to find 'str', also from internal addresses. |
| for (int offset = 0; offset < kObjectAlignment; ++offset) { |
| uword addr = start_addr + offset; |
| char buf[1024]; |
| bool ref = offset % 2 == 0; |
| Utils::SNPrint(buf, sizeof(buf), |
| (ref ? "[0, port, '0', '_getObjectByAddress', " |
| "['address', 'ref'], ['%" Px "', 'true']]" |
| : "[0, port, '0', '_getObjectByAddress', " |
| "['address'], ['%" Px "']]"), |
| addr); |
| service_msg = Eval(lib, buf); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_SUBSTRING(ref ? "\"type\":\"@Instance\"" : "\"type\":\"Instance\"", |
| handler.msg()); |
| EXPECT_SUBSTRING("\"kind\":\"String\"", handler.msg()); |
| EXPECT_SUBSTRING("foobar", handler.msg()); |
| } |
| // Expect null when no object is found. |
| service_msg = Eval(lib, |
| "[0, port, '0', '_getObjectByAddress', " |
| "['address'], ['7']]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // TODO(turnidge): Should this be a ServiceException instead? |
| EXPECT_SUBSTRING( |
| "{\"type\":\"Sentinel\",\"kind\":\"Free\"," |
| "\"valueAsString\":\"<free>\"", |
| handler.msg()); |
| } |
| |
| static bool alpha_callback(const char* name, |
| const char** option_keys, |
| const char** option_values, |
| intptr_t num_options, |
| void* user_data, |
| const char** result) { |
| *result = strdup("alpha"); |
| return true; |
| } |
| |
| static bool beta_callback(const char* name, |
| const char** option_keys, |
| const char** option_values, |
| intptr_t num_options, |
| void* user_data, |
| const char** result) { |
| *result = strdup("beta"); |
| return false; |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_EmbedderRootHandler) { |
| const char* kScript = |
| "var port;\n" // Set to our mock port by C++. |
| "\n" |
| "var x = 7;\n" |
| "main() {\n" |
| " x = x * x;\n" |
| " x = (x / 13).floor();\n" |
| "}"; |
| |
| Dart_Handle lib; |
| { |
| TransitionVMToNative transition(thread); |
| |
| Dart_RegisterRootServiceRequestCallback("alpha", alpha_callback, NULL); |
| Dart_RegisterRootServiceRequestCallback("beta", beta_callback, NULL); |
| |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| } |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| Array& service_msg = Array::Handle(); |
| service_msg = Eval(lib, "[0, port, '\"', 'alpha', [], []]"); |
| HandleRootMessage(service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"result\":alpha,\"id\":\"\\\"\"}", |
| handler.msg()); |
| service_msg = Eval(lib, "[0, port, 1, 'beta', [], []]"); |
| HandleRootMessage(service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"error\":beta,\"id\":1}", handler.msg()); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_EmbedderIsolateHandler) { |
| const char* kScript = |
| "var port;\n" // Set to our mock port by C++. |
| "\n" |
| "var x = 7;\n" |
| "main() {\n" |
| " x = x * x;\n" |
| " x = (x / 13).floor();\n" |
| "}"; |
| |
| Dart_Handle lib; |
| { |
| TransitionVMToNative transition(thread); |
| |
| Dart_RegisterIsolateServiceRequestCallback("alpha", alpha_callback, NULL); |
| Dart_RegisterIsolateServiceRequestCallback("beta", beta_callback, NULL); |
| |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| } |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| Isolate* isolate = thread->isolate(); |
| Array& service_msg = Array::Handle(); |
| service_msg = Eval(lib, "[0, port, '0', 'alpha', [], []]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"result\":alpha,\"id\":\"0\"}", |
| handler.msg()); |
| service_msg = Eval(lib, "[0, port, '0', 'beta', [], []]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"error\":beta,\"id\":\"0\"}", |
| handler.msg()); |
| } |
| |
| // TODO(zra): Remove when tests are ready to enable. |
| #if !defined(TARGET_ARCH_ARM64) |
| |
| static void EnableProfiler() { |
| if (!FLAG_profiler) { |
| FLAG_profiler = true; |
| Profiler::Init(); |
| } |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(Service_Profile) { |
| EnableProfiler(); |
| const char* kScript = |
| "var port;\n" // Set to our mock port by C++. |
| "\n" |
| "var x = 7;\n" |
| "main() {\n" |
| " x = x * x;\n" |
| " x = (x / 13).floor();\n" |
| "}"; |
| |
| Isolate* isolate = thread->isolate(); |
| isolate->set_is_runnable(true); |
| Dart_Handle lib; |
| { |
| TransitionVMToNative transition(thread); |
| |
| lib = TestCase::LoadTestScript(kScript, NULL); |
| EXPECT_VALID(lib); |
| Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| } |
| |
| // 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(thread, SendPort::New(port_id)); |
| EXPECT_VALID(port); |
| { |
| TransitionVMToNative transition(thread); |
| EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
| } |
| |
| Array& service_msg = Array::Handle(); |
| service_msg = Eval(lib, "[0, port, '0', '_getCpuProfile', [], []]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // Expect error (tags required). |
| EXPECT_SUBSTRING("\"error\"", handler.msg()); |
| |
| service_msg = |
| Eval(lib, "[0, port, '0', '_getCpuProfile', ['tags'], ['None']]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // Expect profile |
| EXPECT_SUBSTRING("\"type\":\"_CpuProfile\"", handler.msg()); |
| |
| service_msg = |
| Eval(lib, "[0, port, '0', '_getCpuProfile', ['tags'], ['Bogus']]"); |
| HandleIsolateMessage(isolate, service_msg); |
| EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
| // Expect error. |
| EXPECT_SUBSTRING("\"error\"", handler.msg()); |
| } |
| |
| #endif // !defined(TARGET_ARCH_ARM64) |
| |
| #endif // !PRODUCT |
| |
| } // namespace dart |