blob: 20863b07525134efa8da3b3e7a9a281805344122 [file] [log] [blame]
// 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_debugger_api.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/debugger.h"
#include "vm/globals.h"
#include "vm/message_handler.h"
#include "vm/object_id_ring.h"
#include "vm/os.h"
#include "vm/port.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");
class ServiceTestMessageHandler : public MessageHandler {
public:
ServiceTestMessageHandler() : _msg(NULL) {}
~ServiceTestMessageHandler() {
free(_msg);
}
bool HandleMessage(Message* message) {
if (_msg != NULL) {
free(_msg);
}
// Parse the message.
SnapshotReader reader(message->data(), message->len(),
Snapshot::kMessage, Isolate::Current());
const Object& response_obj = Object::Handle(reader.ReadObject());
String& response = String::Handle();
response ^= response_obj.raw();
_msg = strdup(response.ToCString());
return true;
}
// Removes a given json key:value from _msg.
void filterMsg(const char* key) {
int key_len = strlen(key);
int old_len = strlen(_msg);
char* new_msg = reinterpret_cast<char*>(malloc(old_len + 1));
int old_pos = 0;
int new_pos = 0;
while (_msg[old_pos] != '\0') {
if (_msg[old_pos] == '\"') {
old_pos++;
if ((old_len - old_pos) > key_len &&
strncmp(&_msg[old_pos], key, key_len) == 0 &&
_msg[old_pos + key_len] == '\"') {
old_pos += (key_len + 2);
// Skip until next , or }.
while (_msg[old_pos] != '\0' &&
_msg[old_pos] != ',' &&
_msg[old_pos] != '}') {
old_pos++;
}
if (_msg[old_pos] == ',') {
old_pos++;
}
} else {
new_msg[new_pos] = '\"';;
new_pos++;
}
} else {
new_msg[new_pos] = _msg[old_pos];
new_pos++;
old_pos++;
}
}
new_msg[new_pos] = '\0';
free(_msg);
_msg = new_msg;
}
const char* msg() const { return _msg; }
private:
char* _msg;
};
static RawArray* Eval(Dart_Handle lib, const char* expr) {
Dart_Handle expr_val = Dart_EvaluateExpr(lib, NewString(expr));
EXPECT_VALID(expr_val);
Isolate* isolate = Isolate::Current();
const GrowableObjectArray& value =
Api::UnwrapGrowableObjectArrayHandle(isolate, expr_val);
const Array& result = Array::Handle(Array::MakeArray(value));
GrowableObjectArray& growable = GrowableObjectArray::Handle();
growable ^= result.At(3);
Array& array = Array::Handle(Array::MakeArray(growable));
result.SetAt(3, array);
growable ^= result.At(4);
array = Array::MakeArray(growable);
result.SetAt(4, array);
return result.raw();
}
static RawArray* 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);
}
// Search for the formatted string in buffer.
//
// TODO(turnidge): This function obscures the line number of failing
// EXPECTs. Rework this.
static void ExpectSubstringF(const char* buff, 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);
EXPECT_SUBSTRING(buffer, buff);
}
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(name))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
return cls.raw();
}
TEST_CASE(Service_Isolate) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Get the isolate summary.
service_msg = Eval(lib, "[0, port, [], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
JSONReader reader(handler.msg());
const int kBufferSize = 128;
char buffer[kBufferSize];
// Check that the response string is somewhat sane.
// type
EXPECT(reader.Seek("type"));
EXPECT_EQ(reader.Type(), JSONReader::kString);
reader.GetDecodedValueChars(buffer, kBufferSize);
EXPECT_STREQ("Isolate", buffer);
// id
EXPECT(reader.Seek("id"));
EXPECT_EQ(reader.Type(), JSONReader::kString);
reader.GetDecodedValueChars(buffer, kBufferSize);
EXPECT_SUBSTRING("isolates/", buffer);
// heap
EXPECT(reader.Seek("heaps"));
EXPECT_EQ(reader.Type(), JSONReader::kObject);
}
TEST_CASE(Service_StackTrace) {
// TODO(turnidge): Extend this test to cover a non-trivial stack trace.
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Get the stacktrace.
service_msg = Eval(lib, "[0, port, ['stacktrace'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"StackTrace\",\"id\":\"stacktrace\",\"members\":[]}",
handler.msg());
// Malformed request.
service_msg = Eval(lib, "[0, port, ['stacktrace', 'jamboree'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Command too long\","
"\"request\":{\"arguments\":[\"stacktrace\",\"jamboree\"],"
"\"option_keys\":[],\"option_values\":[]}}",
handler.msg());
}
TEST_CASE(Service_DebugBreakpoints) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n" // We set breakpoint here.
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
// 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, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Add a breakpoint.
service_msg = EvalF(lib,
"[0, port, ['libraries', '%" Pd "', "
"'scripts', 'test-lib', 'setBreakpoint'], "
"['line'], ['3']]",
vmlib.index());
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"Breakpoint\",\"id\":\"debug\\/breakpoints\\/1\","
"\"breakpointNumber\":1,\"enabled\":true,\"resolved\":false,"
"\"location\":{\"type\":\"Location\","
"\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\",\"kind\":\"script\"},"
"\"tokenPos\":5}}",
vmlib.index());
// Get the breakpoint list.
service_msg = Eval(lib, "[0, port, ['debug', 'breakpoints'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"BreakpointList\",\"id\":\"debug\\/breakpoints\","
"\"breakpoints\":["
"{\"type\":\"Breakpoint\",\"id\":\"debug\\/breakpoints\\/1\","
"\"breakpointNumber\":1,\"enabled\":true,\"resolved\":false,"
"\"location\":{\"type\":\"Location\","
"\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\",\"kind\":\"script\"},"
"\"tokenPos\":5}}]}",
vmlib.index());
// Lookup individual breakpoint.
service_msg = Eval(lib, "[0, port, ['debug', 'breakpoints', '1'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"Breakpoint\",\"id\":\"debug\\/breakpoints\\/1\","
"\"breakpointNumber\":1,\"enabled\":true,\"resolved\":false,"
"\"location\":{\"type\":\"Location\","
"\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\",\"kind\":\"script\"},"
"\"tokenPos\":5}}",
vmlib.index());
// Unrecognized breakpoint subcommand.
service_msg =
Eval(lib, "[0, port, ['debug', 'breakpoints', '1', 'green'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"Unrecognized subcommand: green\","
"\"request\":{\"arguments\":[\"debug\",\"breakpoints\","
"\"1\",\"green\"],"
"\"option_keys\":[],\"option_values\":[]}}",
handler.msg());
// Clear breakpoint.
service_msg =
Eval(lib, "[0, port, ['debug', 'breakpoints', '1', 'clear'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("{\"type\":\"Success\",\"id\":\"\"}",
handler.msg());
// Get the breakpoint list.
service_msg = Eval(lib, "[0, port, ['debug', 'breakpoints'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"BreakpointList\",\"id\":\"debug\\/breakpoints\","
"\"breakpoints\":[]}",
handler.msg());
// Missing sub-command.
service_msg = Eval(lib, "[0, port, ['debug'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"Must specify a subcommand\","
"\"request\":{\"arguments\":[\"debug\"],\"option_keys\":[],"
"\"option_values\":[]}}",
handler.msg());
// Unrecognized breakpoint.
service_msg = Eval(lib,
"[0, port, ['debug', 'breakpoints', '1111'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"Unrecognized breakpoint id: 1111\","
"\"request\":{"
"\"arguments\":[\"debug\",\"breakpoints\",\"1111\"],"
"\"option_keys\":[],\"option_values\":[]}}",
handler.msg());
// Unrecognized subcommand.
service_msg = Eval(lib, "[0, port, ['debug', 'nosferatu'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"Unrecognized subcommand 'nosferatu'\","
"\"request\":{\"arguments\":[\"debug\",\"nosferatu\"],"
"\"option_keys\":[],\"option_values\":[]}}",
handler.msg());
}
// Globals used to communicate with HandlerPausedEvent.
static intptr_t pause_line_number = 0;
static Dart_Handle saved_lib;
static ServiceTestMessageHandler* saved_handler = NULL;
static void HandlePausedEvent(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& loc) {
Isolate* isolate = Isolate::Current();
Debugger* debugger = isolate->debugger();
// The debugger knows that it is paused, and why.
EXPECT(debugger->IsPaused());
const DebuggerEvent* event = debugger->PauseEvent();
EXPECT(event != NULL);
EXPECT(event->type() == DebuggerEvent::kBreakpointReached);
// Save the last line number seen by this handler.
pause_line_number = event->top_frame()->LineNumber();
// Single step
Array& service_msg = Array::Handle();
service_msg = Eval(saved_lib,
"[0, port, ['debug', 'resume'], "
"['step'], ['into']]");
Service::HandleIsolateMessage(isolate, service_msg);
saved_handler->HandleNextMessage();
EXPECT_STREQ("{\"type\":\"Success\",\"id\":\"\"}",
saved_handler->msg());
}
TEST_CASE(Service_Stepping) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"var value = 0;\n"
"\n"
"main() {\n" // We set breakpoint here.
" value++;\n"
" value++;\n"
" value++;\n"
"}"; // We step up to here.
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
saved_lib = lib;
// Build a mock message handler and wrap it in a dart port.
ServiceTestMessageHandler handler;
saved_handler = &handler;
Dart_Port port_id = PortMap::CreatePort(&handler);
Dart_Handle port = Api::NewHandle(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Add a breakpoint.
service_msg = EvalF(lib,
"[0, port, ['libraries', '%" Pd "', "
"'scripts', 'test-lib', 'setBreakpoint'], "
"['line'], ['4']]",
vmlib.index());
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"Breakpoint\",\"id\":\"debug\\/breakpoints\\/1\","
"\"breakpointNumber\":1,\"enabled\":true,\"resolved\":false,"
"\"location\":{\"type\":\"Location\","
"\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\",\"kind\":\"script\"},"
"\"tokenPos\":11}}",
vmlib.index());
pause_line_number = -1;
Dart_SetPausedEventHandler(HandlePausedEvent);
// Run the program.
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(result);
// We were able to step to the last line in main.
EXPECT_EQ(8, pause_line_number);
}
TEST_CASE(Service_Objects) {
// TODO(turnidge): Extend this test to cover a non-trivial stack trace.
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"var validId;\n" // Set to a valid object id by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
ObjectIdRing* ring = isolate->object_id_ring();
const Array& arr = Array::Handle(Array::New(1, Heap::kOld));
{
HANDLESCOPE(isolate);
const String& str = String::Handle(String::New("value", Heap::kOld));
arr.SetAt(0, str);
}
intptr_t arr_id = ring->GetIdForObject(arr.raw());
Dart_Handle valid_id = Dart_NewInteger(arr_id);
EXPECT_VALID(valid_id);
EXPECT_VALID(Dart_SetField(lib, NewString("validId"), valid_id));
Array& service_msg = Array::Handle();
// null
service_msg = Eval(lib, "[0, port, ['objects', 'null'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_SUBSTRING(
"{\"type\":\"null\",\"id\":\"objects\\/null\","
"\"valueAsString\":\"null\",\"class\":",
handler.msg());
// not initialized
service_msg = Eval(lib, "[0, port, ['objects', 'not-initialized'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Sentinel\",\"id\":\"objects\\/not-initialized\","
"\"valueAsString\":\"<not initialized>\"}",
handler.msg());
// being initialized
service_msg = Eval(lib,
"[0, port, ['objects', 'being-initialized'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Sentinel\",\"id\":\"objects\\/being-initialized\","
"\"valueAsString\":\"<being initialized>\"}",
handler.msg());
// optimized out
service_msg = Eval(lib, "[0, port, ['objects', 'optimized-out'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Sentinel\",\"id\":\"objects\\/optimized-out\","
"\"valueAsString\":\"<optimized out>\"}",
handler.msg());
// collected
service_msg = Eval(lib, "[0, port, ['objects', 'collected'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Sentinel\",\"id\":\"objects\\/collected\","
"\"valueAsString\":\"<collected>\"}",
handler.msg());
// expired
service_msg = Eval(lib, "[0, port, ['objects', 'expired'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Sentinel\",\"id\":\"objects\\/expired\","
"\"valueAsString\":\"<expired>\"}",
handler.msg());
// bool
service_msg = Eval(lib, "[0, port, ['objects', 'bool-true'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
handler.filterMsg("size");
EXPECT_STREQ(
"{\"type\":\"bool\","
"\"class\":{\"type\":\"@Class\",\"id\":\"classes\\/46\","
"\"name\":\"bool\"},"
"\"fields\":[],\"id\":\"objects\\/bool-true\","
"\"valueAsString\":\"true\"}",
handler.msg());
// int
service_msg = Eval(lib, "[0, port, ['objects', 'int-123'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"int\",\"_vmType\":\"Smi\","
"\"class\":{\"type\":\"@Class\",\"id\":\"classes\\/42\","
"\"name\":\"_Smi\",},"
"\"fields\":[],"
"\"id\":\"objects\\/int-123\","
"\"valueAsString\":\"123\"}",
handler.msg());
// object id ring / valid
service_msg = Eval(lib, "[0, port, ['objects', '$validId'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
handler.filterMsg("size");
handler.filterMsg("id");
EXPECT_STREQ(
"{\"type\":\"List\",\"_vmType\":\"Array\","
"\"class\":{\"type\":\"@Class\",\"name\":\"_List\",},"
"\"fields\":[],"
"\"length\":1,"
"\"elements\":[{"
"\"index\":0,"
"\"value\":{\"type\":\"@String\","
"\"class\":{\"type\":\"@Class\",\"name\":\"_OneByteString\",},"
"\"valueAsString\":\"value\"}}]}",
handler.msg());
// object id ring / invalid => expired
service_msg = Eval(lib, "[0, port, ['objects', '99999999'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Sentinel\",\"id\":\"objects\\/expired\","
"\"valueAsString\":\"<expired>\"}",
handler.msg());
// expired/eval => error
service_msg = Eval(lib, "[0, port, ['objects', 'expired', 'eval'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"expected at most 2 arguments but found 3\\n\","
"\"request\":{\"arguments\":[\"objects\",\"expired\",\"eval\"],"
"\"option_keys\":[],\"option_values\":[]}}",
handler.msg());
// int/eval => good
service_msg = Eval(lib,
"[0, port, ['objects', 'int-123', 'eval'], "
"['expr'], ['this+99']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"@int\",\"_vmType\":\"@Smi\","
"\"class\":{\"type\":\"@Class\",\"id\":\"classes\\/42\","
"\"name\":\"_Smi\",},"
"\"id\":\"objects\\/int-222\","
"\"valueAsString\":\"222\"}",
handler.msg());
// eval returning null works
service_msg = Eval(lib,
"[0, port, ['objects', 'int-123', 'eval'], "
"['expr'], ['null']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"@null\",\"id\":\"objects\\/null\","
"\"valueAsString\":\"null\"}",
handler.msg());
// object id ring / invalid => expired
service_msg = Eval(lib, "[0, port, ['objects', '99999999', 'eval'], "
"['expr'], ['this']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Error\",\"id\":\"\",\"kind\":\"EvalExpired\","
"\"message\":\"attempt to evaluate against expired object\\n\","
"\"request\":{\"arguments\":[\"objects\",\"99999999\",\"eval\"],"
"\"option_keys\":[\"expr\"],\"option_values\":[\"this\"]}}",
handler.msg());
// Extra arg to eval.
service_msg = Eval(lib,
"[0, port, ['objects', 'int-123', 'eval', 'foo'], "
"['expr'], ['this+99']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"expected at most 3 arguments but found 4\\n\","
"\"request\":{\"arguments\":[\"objects\",\"int-123\",\"eval\",\"foo\"],"
"\"option_keys\":[\"expr\"],\"option_values\":[\"this+99\"]}}",
handler.msg());
// Retained by single instance.
service_msg = Eval(lib,
"[0, port, ['objects', '$validId', 'retained'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
ExpectSubstringF(handler.msg(),
"\"id\":\"objects\\/int-%" Pd "\"",
arr.raw()->Size() + arr.At(0)->Size());
// Retaining path to 'arr', limit 1.
service_msg = Eval(
lib,
"[0, port, ['objects', '$validId', 'retaining_path'], ['limit'], ['1']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"RetainingPath\",\"id\":\"retaining_path\",\"length\":1,"
"\"elements\":[{\"index\":0,\"value\":{\"type\":\"@List\"");
// Retaining path missing limit.
service_msg = Eval(
lib,
"[0, port, ['objects', '$validId', 'retaining_path'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(), "{\"type\":\"Error\"");
// eval against list containing an internal object.
Object& internal_object = Object::Handle();
internal_object = LiteralToken::New();
arr.SetAt(0, internal_object);
service_msg = Eval(lib,
"[0, port, ['objects', '$validId', 'eval'], "
"['expr'], ['toString()']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(), "\"type\":\"Error\"");
ExpectSubstringF(
handler.msg(),
"\"message\":\"attempt to evaluate against internal VM object\\n\"");
}
TEST_CASE(Service_RetainingPath) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"var id0;\n" // Set to an object id by C++.
"var id1;\n" // Ditto.
"var idElem;\n" // Ditto.
"class Foo {\n"
" String f0;\n"
" String f1;\n"
"}\n"
"Foo foo;\n"
"List<String> lst;\n"
"main() {\n"
" foo = new Foo();\n"
" lst = new List<String>(100);\n"
"}\n";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(result);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
const Class& class_foo = Class::Handle(GetClass(vmlib, "Foo"));
EXPECT(!class_foo.IsNull());
Dart_Handle foo = Dart_GetField(lib, NewString("foo"));
Dart_Handle lst = Dart_GetField(lib, NewString("lst"));
const intptr_t kElemIndex = 42;
{
Dart_EnterScope();
ObjectIdRing* ring = isolate->object_id_ring();
{
const String& foo0 = String::Handle(String::New("foo0", Heap::kOld));
Dart_Handle h_foo0 = Api::NewHandle(isolate, foo0.raw());
EXPECT_VALID(Dart_SetField(foo, NewString("f0"), h_foo0));
Dart_Handle id0 = Dart_NewInteger(ring->GetIdForObject(foo0.raw()));
EXPECT_VALID(id0);
EXPECT_VALID(Dart_SetField(lib, NewString("id0"), id0));
}
{
const String& foo1 = String::Handle(String::New("foo1", Heap::kOld));
Dart_Handle h_foo1 = Api::NewHandle(isolate, foo1.raw());
EXPECT_VALID(Dart_SetField(foo, NewString("f1"), h_foo1));
Dart_Handle id1 = Dart_NewInteger(ring->GetIdForObject(foo1.raw()));
EXPECT_VALID(id1);
EXPECT_VALID(Dart_SetField(lib, NewString("id1"), id1));
}
{
const String& elem = String::Handle(String::New("elem", Heap::kOld));
Dart_Handle h_elem = Api::NewHandle(isolate, elem.raw());
EXPECT_VALID(Dart_ListSetAt(lst, kElemIndex, h_elem));
Dart_Handle idElem = Dart_NewInteger(ring->GetIdForObject(elem.raw()));
EXPECT_VALID(idElem);
EXPECT_VALID(Dart_SetField(lib, NewString("idElem"), idElem));
}
Dart_ExitScope();
}
// 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, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Retaining path to 'foo0', limit 2.
service_msg = Eval(
lib,
"[0, port, ['objects', '$id0', 'retaining_path'], ['limit'], ['2']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"RetainingPath\",\"id\":\"retaining_path\",\"length\":2,"
"\"elements\":[{\"index\":0,\"value\":{\"type\":\"@String\"");
ExpectSubstringF(handler.msg(), "\"parentField\":{\"type\":\"@Field\"");
ExpectSubstringF(handler.msg(), "\"name\":\"f0\"");
ExpectSubstringF(handler.msg(),
"{\"index\":1,\"value\":{\"type\":\"@Instance\"");
// Retaining path to 'foo1', limit 2.
service_msg = Eval(
lib,
"[0, port, ['objects', '$id1', 'retaining_path'], ['limit'], ['2']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"RetainingPath\",\"id\":\"retaining_path\",\"length\":2,"
"\"elements\":[{\"index\":0,\"value\":{\"type\":\"@String\"");
ExpectSubstringF(handler.msg(), "\"parentField\":{\"type\":\"@Field\"");
ExpectSubstringF(handler.msg(), "\"name\":\"f1\"");
ExpectSubstringF(handler.msg(),
"{\"index\":1,\"value\":{\"type\":\"@Instance\"");
// Retaining path to 'elem', limit 2.
service_msg = Eval(
lib,
"[0, port, ['objects', '$idElem', 'retaining_path'], ['limit'], ['2']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(
handler.msg(),
"{\"type\":\"RetainingPath\",\"id\":\"retaining_path\",\"length\":2,"
"\"elements\":[{\"index\":0,\"value\":{\"type\":\"@String\"");
ExpectSubstringF(handler.msg(), "\"parentListIndex\":%" Pd, kElemIndex);
ExpectSubstringF(handler.msg(),
"{\"index\":1,\"value\":{\"type\":\"@List\"");
}
TEST_CASE(Service_Libraries) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"var libVar = 54321;\n"
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
// 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, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Request library.
service_msg = EvalF(lib,
"[0, port, ['libraries', '%" Pd "'], [], []]",
vmlib.index());
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Library\"", handler.msg());
EXPECT_SUBSTRING("\"url\":\"test-lib\"", handler.msg());
// Evaluate an expression from a library.
service_msg = EvalF(lib,
"[0, port, ['libraries', '%" Pd "', 'eval'], "
"['expr'], ['libVar - 1']]",
vmlib.index());
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"@int\",\"_vmType\":\"@Smi\","
"\"class\":{\"type\":\"@Class\",\"id\":\"classes\\/42\","
"\"name\":\"_Smi\",},\"id\":\"objects\\/int-54320\","
"\"valueAsString\":\"54320\"}",
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"
" static var cobra = 11235;\n"
" dynamic b() {}\n"
" dynamic c() {\n"
" var d = () { b(); };\n"
" return d;\n"
" }\n"
"}\n"
"class B { static int i = 42; }\n"
"main() {\n"
" var z = new A();\n"
" var x = z.c();\n"
" x();\n"
" ++B.i;\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
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());
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, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Request an invalid class id.
service_msg = Eval(lib, "[0, port, ['classes', '999999'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"999999 is not a valid class id.\","
"\"request\":{\"arguments\":[\"classes\",\"999999\"],"
"\"option_keys\":[],\"option_values\":[]}}", handler.msg());
// Request the class A over the service.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "'], [], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Class\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\",\"name\":\"A\",", cid);
ExpectSubstringF(handler.msg(), "\"allocationStats\":");
ExpectSubstringF(handler.msg(), "\"tokenPos\":");
ExpectSubstringF(handler.msg(), "\"endTokenPos\":");
// Evaluate an expression from class A.
service_msg = EvalF(lib,
"[0, port, ['classes', '%" Pd "', 'eval'], "
"['expr'], ['cobra + 100000']]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
handler.filterMsg("_vmName");
EXPECT_STREQ(
"{\"type\":\"@int\",\"_vmType\":\"@Smi\","
"\"class\":{\"type\":\"@Class\",\"id\":\"classes\\/42\","
"\"name\":\"_Smi\",},"
"\"id\":\"objects\\/int-111235\","
"\"valueAsString\":\"111235\"}",
handler.msg());
// Request function 'b' from class A.
service_msg = EvalF(lib,
"[0, port, ['classes', '%" Pd "', 'functions', 'b'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Function\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\\/functions\\/b\","
"\"name\":\"b\",", cid);
// Request field 0 from class A.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'fields', '0'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Field\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\\/fields\\/0\","
"\"name\":\"a\",", cid);
// Invalid sub command.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'huh', '0'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Invalid sub collection huh\""
",\"request\":"
"{\"arguments\":[\"classes\",\"%" Pd "\",\"huh\",\"0\"],\"option_keys\":[],"
"\"option_values\":[]}}", cid);
// Invalid field request.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'fields', '9'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Field 9 not found\","
"\"request\":{\"arguments\":[\"classes\",\"%" Pd "\",\"fields\",\"9\"],"
"\"option_keys\":[],\"option_values\":[]}}", cid);
// Invalid function request.
service_msg = EvalF(lib,
"[0, port, ['classes', '%" Pd "', 'functions', '9'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Function 9 not found\","
"\"request\":{\"arguments\":[\"classes\",\"%" Pd "\",\"functions\",\"9\"],"
"\"option_keys\":[],\"option_values\":[]}}", cid);
// Invalid field subcommand.
service_msg = EvalF(lib,
"[0, port, ['classes', '%" Pd "', 'fields', '9', 'x']"
",[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Command too long\","
"\"request\":"
"{\"arguments\":[\"classes\",\"%" Pd "\",\"fields\",\"9\",\"x\"],"
"\"option_keys\":[],\"option_values\":[]}}", cid);
// Invalid function command.
service_msg = EvalF(lib,
"[0, port, ['classes', '%" Pd "', 'functions', '0',"
"'x', 'y'], [], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"Command should have 4 or 5 arguments\","
"\"request\":"
"{\"arguments\":[\"classes\",\"%" Pd "\",\"functions\",\"0\",\"x\",\"y\"],"
"\"option_keys\":[],\"option_values\":[]}}", cid);
// Invalid function subcommand with valid function id.
service_msg = EvalF(lib,
"[0, port, ['classes', '%" Pd "', 'functions', 'b',"
"'x'], [], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Invalid sub collection x\","
"\"request\":"
"{\"arguments\":[\"classes\",\"%" Pd "\",\"functions\",\"b\",\"x\"],"
"\"option_keys\":[],\"option_values\":[]}}", cid);
// Retained size of all instances of class B.
const Class& class_b = Class::Handle(GetClass(vmlib, "B"));
EXPECT(!class_b.IsNull());
const Instance& b0 = Instance::Handle(Instance::New(class_b));
const Instance& b1 = Instance::Handle(Instance::New(class_b));
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'retained'],"
"[], []]", class_b.id());
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"\"id\":\"objects\\/int-%" Pd "\"",
b0.raw()->Size() + b1.raw()->Size());
// ... and list the instances of class B.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'instances'],"
"['limit'], ['3']]", class_b.id());
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(), "\"type\":\"InstanceSet\"");
ExpectSubstringF(handler.msg(), "\"totalCount\":2");
ExpectSubstringF(handler.msg(), "\"sampleCount\":2");
// TODO(koda): Actually parse the response.
static const intptr_t kInstanceListId = 0;
ExpectSubstringF(handler.msg(), "\"id\":\"objects\\/%" Pd "\",\"length\":2",
kInstanceListId);
Array& list = Array::Handle();
ObjectIdRing::LookupResult kind;
list ^= isolate->object_id_ring()->GetObjectForId(kInstanceListId, &kind);
EXPECT_EQ(2, list.Length());
// The list should contain {b0, b1}.
EXPECT((list.At(0) == b0.raw() && list.At(1) == b1.raw()) ||
(list.At(0) == b1.raw() && list.At(1) == b0.raw()));
// ... and if limit is 1, we one get one of them.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'instances'],"
"['limit'], ['1']]", class_b.id());
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(), "\"totalCount\":2");
ExpectSubstringF(handler.msg(), "\"sampleCount\":1");
}
TEST_CASE(Service_Types) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"class A<T> { }\n"
"\n"
"main() {\n"
" new A<A<bool>>();\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
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());
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, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Request the class A over the service.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "'], [], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Class\"", handler.msg());
EXPECT_SUBSTRING("\"name\":\"A\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\"", cid);
// Request canonical type 0 from class A.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'types', '0'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Type\"", handler.msg());
EXPECT_SUBSTRING("\"name\":\"A<bool>\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\\/types\\/0\"", cid);
// Request canonical type 1 from class A.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'types', '1'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Type\"", handler.msg());
EXPECT_SUBSTRING("\"name\":\"A<A<bool>>\"", handler.msg());
ExpectSubstringF(handler.msg(),
"\"id\":\"classes\\/%" Pd "\\/types\\/1\"", cid);
// Request for non-existent canonical type from class A.
service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "', 'types', '42'],"
"[], []]", cid);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
ExpectSubstringF(handler.msg(),
"{\"type\":\"Error\",\"id\":\"\","
"\"message\":\"Canonical type 42 not found\""
",\"request\":"
"{\"arguments\":[\"classes\",\"%" Pd "\",\"types\",\"42\"],"
"\"option_keys\":[],\"option_values\":[]}}", cid);
// Request canonical type arguments. Expect <A<bool>> to be listed.
service_msg = EvalF(lib, "[0, port, ['typearguments'],"
"[], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"TypeArgumentsList\"", handler.msg());
ExpectSubstringF(handler.msg(), "\"name\":\"<A<bool>>\",");
// Request canonical type arguments with instantiations.
service_msg = EvalF(lib,
"[0, port, ['typearguments', 'withinstantiations'],"
"[], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"TypeArgumentsList\"", handler.msg());
}
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 = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
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.EntryPoint();
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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Request an invalid code object.
service_msg = Eval(lib, "[0, port, ['code', '0'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ(
"{\"type\":\"Error\",\"id\":\"\",\"message\":\"Malformed code id: 0\","
"\"request\":{\"arguments\":[\"code\",\"0\"],"
"\"option_keys\":[],\"option_values\":[]}}", 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, ['code', '%" Px64"-%" Px "'], [], []]",
compile_timestamp,
entry);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
{
// Only perform a partial match.
const intptr_t kBufferSize = 512;
char buffer[kBufferSize];
OS::SNPrint(buffer, kBufferSize-1,
"{\"type\":\"Code\",\"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, ['code', '%" Px64"-%" Px "'], [], []]",
compile_timestamp,
address);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
{
// Only perform a partial match.
const intptr_t kBufferSize = 512;
char buffer[kBufferSize];
OS::SNPrint(buffer, kBufferSize-1,
"Could not find code with id: %" Px64 "-%" Px "",
compile_timestamp,
address);
EXPECT_SUBSTRING(buffer, 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, ['code', '%" Px64"-%" Px "'], [], []]",
compile_timestamp - 1,
address);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
{
// Only perform a partial match.
const intptr_t kBufferSize = 512;
char buffer[kBufferSize];
OS::SNPrint(buffer, kBufferSize-1,
"Could not find code with id: %" Px64 "-%" Px "",
compile_timestamp - 1,
address);
EXPECT_SUBSTRING(buffer, handler.msg());
}
// Request native code at address. Expect the null code object back.
address = last;
service_msg = EvalF(lib, "[0, port, ['code', 'native-%" Px "'], [], []]",
address);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("{\"type\":\"null\",\"id\":\"objects\\/null\","
"\"valueAsString\":\"null\"",
handler.msg());
// Request malformed native code.
service_msg = EvalF(lib, "[0, port, ['code', 'native%" Px "'], [], []]",
address);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"message\":\"Malformed code id:", handler.msg());
}
TEST_CASE(Service_TokenStream) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
const String& script_name = String::Handle(String::New("test-lib"));
EXPECT(!script_name.IsNull());
const Script& script = Script::Handle(vmlib.LookupScript(script_name));
EXPECT(!script.IsNull());
const TokenStream& token_stream = TokenStream::Handle(script.tokens());
EXPECT(!token_stream.IsNull());
ObjectIdRing* ring = isolate->object_id_ring();
intptr_t id = ring->GetIdForObject(token_stream.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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Fetch object.
service_msg = EvalF(lib, "[0, port, ['objects', '%" Pd "'], [], []]", id);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
// Check type.
EXPECT_SUBSTRING("\"type\":\"Object\"", handler.msg());
EXPECT_SUBSTRING("\"_vmType\":\"TokenStream\"", handler.msg());
// Check for members array.
EXPECT_SUBSTRING("\"members\":[", handler.msg());
}
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 = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Fetch object.
service_msg = EvalF(lib, "[0, port, ['objects', '%" Pd "'], [], []]", id);
Service::HandleIsolateMessage(isolate, service_msg);
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());
}
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 = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
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.var_descriptors());
// 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
// Fetch object.
service_msg = EvalF(lib, "[0, port, ['objects', '%" Pd "'], [], []]", id);
Service::HandleIsolateMessage(isolate, service_msg);
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());
}
TEST_CASE(Service_VM) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['vm'], [], []]");
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"VM\",\"id\":\"vm\"", handler.msg());
EXPECT_SUBSTRING("\"targetCPU\"", handler.msg());
EXPECT_SUBSTRING("\"hostCPU\"", handler.msg());
EXPECT_SUBSTRING("\"version\"", handler.msg());
EXPECT_SUBSTRING("\"uptime\"", handler.msg());
EXPECT_SUBSTRING("\"isolates\"", handler.msg());
}
TEST_CASE(Service_Flags) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['flags'], [], []]");
// Make sure we can get the FlagList.
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"FlagList\",\"id\":\"flags\"", handler.msg());
EXPECT_SUBSTRING(
"\"name\":\"service_testing_flag\",\"comment\":\"Comment\","
"\"flagType\":\"bool\",\"valueAsString\":\"false\"",
handler.msg());
// Modify a flag through the vm service.
service_msg = Eval(lib,
"[0, port, ['flags', 'set'], "
"['name', 'value'], ['service_testing_flag', 'true']]");
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("Success", handler.msg());
// Make sure that the flag changed.
service_msg = Eval(lib, "[0, port, ['flags'], [], []]");
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING(
"\"name\":\"service_testing_flag\",\"comment\":\"Comment\","
"\"flagType\":\"bool\",\"valueAsString\":\"true\"",
handler.msg());
}
TEST_CASE(Service_Scripts) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
// 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, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
char buf[1024];
OS::SNPrint(buf, sizeof(buf),
"[0, port, ['libraries', '%" Pd "', 'scripts', 'test-lib'], [], []]",
vmlib.index());
service_msg = Eval(lib, buf);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
OS::SNPrint(buf, sizeof(buf),
"{\"type\":\"Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\","
"\"kind\":\"script\","
"\"owningLibrary\":{\"type\":\"@Library\","
"\"id\":\"libraries\\/%" Pd "\",\"name\":\"\","
"\"url\":\"test-lib\"},"
"\"source\":\"var port;\\n\\nmain() {\\n}\","
"\"tokenPosTable\":[[1,0,1,1,5,2,9],[3,5,1,6,5,7,6,8,8],[4,10,1]]}",
vmlib.index(), vmlib.index());
EXPECT_STREQ(buf, handler.msg());
}
// TODO(zra): Remove when tests are ready to enable.
#if !defined(TARGET_ARCH_ARM64)
TEST_CASE(Service_Coverage) {
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;\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['coverage'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
char buf[1024];
OS::SNPrint(buf, sizeof(buf),
"{\"source\":\"test-lib\",\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\","
"\"kind\":\"script\"},\"hits\":"
"[5,1,6,1]}",
vmlib.index());
EXPECT_SUBSTRING(buf, handler.msg());
}
TEST_CASE(Service_LibrariesScriptsCoverage) {
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;\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
char buf[1024];
OS::SNPrint(buf, sizeof(buf),
"[0, port, ['libraries', '%" Pd "', 'scripts', 'test-lib', 'coverage'], "
"[], []]",
vmlib.index());
service_msg = Eval(lib, buf);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
OS::SNPrint(buf, sizeof(buf),
"{\"type\":\"CodeCoverage\",\"id\":\"coverage\",\"coverage\":["
"{\"source\":\"test-lib\",\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\","
"\"kind\":\"script\"},\"hits\":[5,1,6,1]}]}", vmlib.index());
EXPECT_STREQ(buf, handler.msg());
}
TEST_CASE(Service_LibrariesCoverage) {
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;\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
char buf[1024];
OS::SNPrint(buf, sizeof(buf),
"[0, port, ['libraries', '%" Pd "', 'coverage'], [], []]",
vmlib.index());
Array& service_msg = Array::Handle();
service_msg = Eval(lib, buf);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
OS::SNPrint(buf, sizeof(buf),
"{\"type\":\"CodeCoverage\",\"id\":\"coverage\",\"coverage\":["
"{\"source\":\"test-lib\",\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\","
"\"kind\":\"script\"},\"hits\":[5,1,6,1]}]}",
vmlib.index());
EXPECT_STREQ(buf, handler.msg());
}
TEST_CASE(Service_ClassesCoverage) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"class Foo {\n"
" var x;\n"
" Foo(this.x);\n"
" bar() {\n"
" x = x * x;\n"
" x = x / 13;\n"
" }\n"
"}\n"
"main() {\n"
" var foo = new Foo(7);\n"
" foo.bar();\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
// Look up the service id of Foo.
const Class& cls = Class::Handle(
vmlib.LookupClass(String::Handle(String::New("Foo"))));
ASSERT(!cls.IsNull());
ClassTable* table = isolate->class_table();
intptr_t i;
for (i = 1; i < table->NumCids(); i++) {
if (table->HasValidClassAt(i) && table->At(i) == cls.raw()) {
break;
}
}
ASSERT(i != table->NumCids());
char buf[1024];
OS::SNPrint(buf, sizeof(buf),
"[0, port, ['classes', '%" Pd "', 'coverage'], [], []]", i);
Array& service_msg = Array::Handle();
service_msg = Eval(lib, buf);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
OS::SNPrint(buf, sizeof(buf),
"{\"type\":\"CodeCoverage\",\"id\":\"coverage\",\"coverage\":["
"{\"source\":\"test-lib\",\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\","
"\"kind\":\"script\"},\"hits\":[5,1,7,4,8,3]}]}",
vmlib.index());
EXPECT_STREQ(buf, handler.msg());
}
TEST_CASE(Service_ClassesFunctionsCoverage) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"class Foo {\n"
" var x;\n"
" Foo(this.x);\n"
" bar() {\n"
" x = x * x;\n"
" x = x / 13;\n"
" }\n"
" badum(var a) => 4 + a;\n"
"}\n"
"main() {\n"
" var foo = new Foo(7);\n"
" foo.bar();\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
EXPECT_VALID(lib);
Library& vmlib = Library::Handle();
vmlib ^= Api::UnwrapHandle(lib);
EXPECT(!vmlib.IsNull());
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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
// Look up the service id of Foo.
const Class& cls = Class::Handle(
vmlib.LookupClass(String::Handle(String::New("Foo"))));
ASSERT(!cls.IsNull());
ClassTable* table = isolate->class_table();
intptr_t i;
for (i = 1; i < table->NumCids(); i++) {
if (table->HasValidClassAt(i) && table->At(i) == cls.raw()) {
break;
}
}
ASSERT(i != table->NumCids());
// Look up the service if of the function Foo.bar.
const Function& func = Function::Handle(
cls.LookupFunction(String::Handle(String::New("bar"))));
ASSERT(!func.IsNull());
char buf[1024];
OS::SNPrint(buf, sizeof(buf),
"[0, port, ['classes', '%" Pd "', 'functions',"
"'bar', 'coverage'], [], []]", i);
Array& service_msg = Array::Handle();
service_msg = Eval(lib, buf);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
OS::SNPrint(buf, sizeof(buf),
"{\"type\":\"CodeCoverage\",\"id\":\"coverage\",\"coverage\":["
"{\"source\":\"test-lib\",\"script\":{\"type\":\"@Script\","
"\"id\":\"libraries\\/%" Pd "\\/scripts\\/test-lib\","
"\"name\":\"test-lib\","
"\"kind\":\"script\"},\"hits\":[7,4,8,3]}]}", vmlib.index());
EXPECT_STREQ(buf, handler.msg());
}
#endif
TEST_CASE(Service_AllocationProfile) {
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;\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['allocationprofile'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"AllocationProfile\"", handler.msg());
// Too long.
service_msg = Eval(lib, "[0, port, ['allocationprofile', 'foo'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg());
// Bad gc option.
service_msg = Eval(lib,
"[0, port, ['allocationprofile'], ['gc'], ['cat']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg());
// Bad reset option.
service_msg = Eval(lib,
"[0, port, ['allocationprofile'], ['reset'], ['ff']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg());
// Good reset.
service_msg =
Eval(lib, "[0, port, ['allocationprofile'], ['reset'], ['true']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"AllocationProfile\"", handler.msg());
// Good GC.
service_msg =
Eval(lib, "[0, port, ['allocationprofile'], ['gc'], ['full']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"AllocationProfile\"", handler.msg());
// Good GC and reset.
service_msg = Eval(lib,
"[0, port, ['allocationprofile'], ['gc', 'reset'], ['full', 'true']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"AllocationProfile\"", handler.msg());
}
TEST_CASE(Service_HeapMap) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['heapmap'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"HeapMap\"", handler.msg());
EXPECT_SUBSTRING("\"pages\":[", handler.msg());
}
TEST_CASE(Service_Address) {
const char* kScript =
"var port;\n" // Set to our mock port by C++.
"\n"
"main() {\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
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];
OS::SNPrint(buf, sizeof(buf),
"[0, port, ['address', '%" Px "'], [], []]", addr);
service_msg = Eval(lib, buf);
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_SUBSTRING("\"type\":\"@String\"", handler.msg());
EXPECT_SUBSTRING("foobar", handler.msg());
}
// Expect null when no object is found.
service_msg = Eval(lib, "[0, port, ['address', '7'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
// TODO(turnidge): Should this be a ServiceException instead?
EXPECT_STREQ("{\"type\":\"@null\",\"id\":\"objects\\/null\","
"\"valueAsString\":\"null\"}",
handler.msg());
}
static const char* alpha_callback(
const char* name,
const char** arguments,
intptr_t num_arguments,
const char** option_keys,
const char** option_values,
intptr_t num_options,
void* user_data) {
return strdup("alpha");
}
static const char* beta_callback(
const char* name,
const char** arguments,
intptr_t num_arguments,
const char** option_keys,
const char** option_values,
intptr_t num_options,
void* user_data) {
return strdup("beta");
}
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;\n"
"}";
Dart_RegisterRootServiceRequestCallback("alpha", alpha_callback, NULL);
Dart_RegisterRootServiceRequestCallback("beta", beta_callback, NULL);
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['alpha'], [], []]");
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("alpha", handler.msg());
service_msg = Eval(lib, "[0, port, ['beta'], [], []]");
Service::HandleRootMessage(service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("beta", handler.msg());
}
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;\n"
"}";
Dart_RegisterIsolateServiceRequestCallback("alpha", alpha_callback, NULL);
Dart_RegisterIsolateServiceRequestCallback("beta", beta_callback, NULL);
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['alpha'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("alpha", handler.msg());
service_msg = Eval(lib, "[0, port, ['beta'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
EXPECT_STREQ("beta", handler.msg());
}
// TODO(zra): Remove when tests are ready to enable.
#if !defined(TARGET_ARCH_ARM64)
TEST_CASE(Service_Profile) {
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;\n"
"}";
Isolate* isolate = Isolate::Current();
Dart_Handle 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(isolate, SendPort::New(port_id));
EXPECT_VALID(port);
EXPECT_VALID(Dart_SetField(lib, NewString("port"), port));
Array& service_msg = Array::Handle();
service_msg = Eval(lib, "[0, port, ['profile'], [], []]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
// Expect profile
EXPECT_SUBSTRING("\"type\":\"Profile\"", handler.msg());
service_msg = Eval(lib, "[0, port, ['profile'], ['tags'], ['hide']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
// Expect profile
EXPECT_SUBSTRING("\"type\":\"Profile\"", handler.msg());
service_msg = Eval(lib, "[0, port, ['profile'], ['tags'], ['hidden']]");
Service::HandleIsolateMessage(isolate, service_msg);
handler.HandleNextMessage();
// Expect error.
EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg());
}
#endif // !defined(TARGET_ARCH_ARM64)
} // namespace dart