dart-sdk/runtime/vm/debugger_api_impl_test.cc
Vyacheslav Egorov 021f933f69 VM: Make use_osr an Isolate flag, similar to how we made use_field_guards.
In Debug mode we have assertions checking that we don't attempt OSR when
FLAG_use_osr is disabled - however app-jit snapshots are compiled with OSR
enabled and still contain countining and OSR attempting code, which causes
assertions to fail.

Refactor how getters for isolate flags are defined, consolidate all flags into
a single list.

Update test expectations and switch -c dartk configuration to use app-jit snapshot.

BUG=
R=rmacnak@google.com

Review-Url: https://codereview.chromium.org/2728163002 .
2017-03-03 19:02:23 +01:00

2367 lines
78 KiB
C++

// Copyright (c) 2012, 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 "include/dart_mirrors_api.h"
#include "include/dart_tools_api.h"
#include "platform/assert.h"
#include "vm/dart_api_impl.h"
#include "vm/lockers.h"
#include "vm/unit_test.h"
namespace dart {
DECLARE_FLAG(bool, trace_shutdown);
#ifndef PRODUCT
static bool breakpoint_hit = false;
static int breakpoint_hit_counter = 0;
static Dart_Handle script_lib = NULL;
static const bool verbose = true;
static void LoadScript(const char* source) {
script_lib = TestCase::LoadTestScript(source, NULL);
EXPECT_VALID(script_lib);
}
static void SetBreakpointAtEntry(const char* cname, const char* fname) {
ASSERT(script_lib != NULL);
ASSERT(!Dart_IsError(script_lib));
ASSERT(Dart_IsLibrary(script_lib));
Dart_Handle res =
Dart_SetBreakpointAtEntry(script_lib, NewString(cname), NewString(fname));
EXPECT(Dart_IsInteger(res));
}
static void DisableDebuggabilityOfDartColonLibraries() {
const char* dart_colon = "dart:";
const intptr_t dart_colon_length = strlen(dart_colon);
// Disable debuggability of all dart: libraries.
Dart_Handle library_ids = Dart_GetLibraryIds();
intptr_t library_ids_length;
Dart_ListLength(library_ids, &library_ids_length);
for (intptr_t i = 0; i < library_ids_length; i++) {
Dart_Handle library_id_handle = Dart_ListGetAt(library_ids, i);
int64_t library_id;
Dart_IntegerToInt64(library_id_handle, &library_id);
Dart_Handle library_url_handle = Dart_GetLibraryURL(library_id);
const char* library_url;
Dart_StringToCString(library_url_handle, &library_url);
if (strncmp(library_url, dart_colon, dart_colon_length) == 0) {
Dart_SetLibraryDebuggable(library_id, false);
} else {
Dart_SetLibraryDebuggable(library_id, true);
}
}
}
static Dart_Handle Invoke(const char* func_name) {
ASSERT(script_lib != NULL);
ASSERT(!Dart_IsError(script_lib));
ASSERT(Dart_IsLibrary(script_lib));
return Dart_Invoke(script_lib, NewString(func_name), 0, NULL);
}
static char const* ToCString(Dart_Handle str) {
EXPECT(Dart_IsString(str));
char const* c_str = NULL;
Dart_StringToCString(str, &c_str);
return c_str;
}
static int64_t ToInt64(Dart_Handle h) {
EXPECT(Dart_IsInteger(h));
int64_t i = 0;
Dart_Handle res = Dart_IntegerToInt64(h, &i);
EXPECT_VALID(res);
return i;
}
static double ToDouble(Dart_Handle h) {
EXPECT(Dart_IsDouble(h));
double d = 0.0;
Dart_Handle res = Dart_DoubleValue(h, &d);
EXPECT_VALID(res);
return d;
}
static char const* BreakpointInfo(Dart_StackTrace trace) {
static char info_str[128];
Dart_ActivationFrame frame;
Dart_Handle res = Dart_GetActivationFrame(trace, 0, &frame);
EXPECT_TRUE(res);
Dart_Handle func_name;
Dart_Handle url;
intptr_t line_number = 0;
intptr_t library_id = 0;
res = Dart_ActivationFrameInfo(frame, &func_name, &url, &line_number,
&library_id);
EXPECT_TRUE(res);
OS::SNPrint(info_str, sizeof(info_str), "function %s (%s:%" Pd ")",
ToCString(func_name), ToCString(url), line_number);
return info_str;
}
static void PrintValue(Dart_Handle value, bool expand);
static void PrintObjectList(Dart_Handle list, const char* prefix, bool expand) {
intptr_t list_length = 0;
Dart_Handle retval = Dart_ListLength(list, &list_length);
EXPECT_VALID(retval);
for (int i = 0; i + 1 < list_length; i += 2) {
Dart_Handle name_handle = Dart_ListGetAt(list, i);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
Dart_Handle value_handle = Dart_ListGetAt(list, i + 1);
OS::Print("\n %s %s = ", prefix, ToCString(name_handle));
PrintValue(value_handle, expand);
}
}
static void PrintObject(Dart_Handle obj, bool expand) {
Dart_Handle obj_class = Dart_GetObjClass(obj);
EXPECT_VALID(obj_class);
EXPECT(!Dart_IsNull(obj_class));
Dart_Handle class_name = Dart_ToString(obj_class);
EXPECT_VALID(class_name);
EXPECT(Dart_IsString(class_name));
char const* class_name_str;
Dart_StringToCString(class_name, &class_name_str);
Dart_Handle fields = Dart_GetInstanceFields(obj);
EXPECT_VALID(fields);
EXPECT(Dart_IsList(fields));
OS::Print("object of type '%s'", class_name_str);
PrintObjectList(fields, "field", false);
Dart_Handle statics = Dart_GetStaticFields(obj_class);
EXPECT_VALID(obj_class);
PrintObjectList(statics, "static field", false);
}
static void PrintValue(Dart_Handle value, bool expand) {
if (Dart_IsNull(value)) {
OS::Print("null");
} else if (Dart_IsString(value)) {
Dart_Handle str_value = Dart_ToString(value);
EXPECT_VALID(str_value);
EXPECT(Dart_IsString(str_value));
OS::Print("\"%s\"", ToCString(str_value));
} else if (Dart_IsNumber(value) || Dart_IsBoolean(value)) {
Dart_Handle str_value = Dart_ToString(value);
EXPECT_VALID(str_value);
EXPECT(Dart_IsString(str_value));
OS::Print("%s", ToCString(str_value));
} else {
PrintObject(value, expand);
}
}
static void PrintActivationFrame(Dart_ActivationFrame frame) {
Dart_Handle func_name;
Dart_Handle res;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
EXPECT_TRUE(res);
EXPECT(Dart_IsString(func_name));
const char* func_name_chars;
Dart_StringToCString(func_name, &func_name_chars);
OS::Print(" function %s\n", func_name_chars);
Dart_Handle locals = Dart_GetLocalVariables(frame);
EXPECT_VALID(locals);
intptr_t list_length = 0;
Dart_Handle ret = Dart_ListLength(locals, &list_length);
EXPECT_VALID(ret);
for (int i = 0; i + 1 < list_length; i += 2) {
Dart_Handle name_handle = Dart_ListGetAt(locals, i);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
OS::Print(" local var %s = ", ToCString(name_handle));
Dart_Handle value_handle = Dart_ListGetAt(locals, i + 1);
EXPECT_VALID(value_handle);
PrintValue(value_handle, true);
OS::Print("\n");
}
}
static Dart_Handle GetLocalVariable(Dart_ActivationFrame frame,
const char* name) {
Dart_Handle locals = Dart_GetLocalVariables(frame);
EXPECT_VALID(locals);
intptr_t list_length = 0;
Dart_Handle ret = Dart_ListLength(locals, &list_length);
EXPECT_VALID(ret);
for (int i = 0; i + 1 < list_length; i += 2) {
Dart_Handle name_handle = Dart_ListGetAt(locals, i);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
if (strcmp(ToCString(name_handle), name) == 0) {
Dart_Handle value_handle = Dart_ListGetAt(locals, i + 1);
EXPECT_VALID(value_handle);
return value_handle;
}
}
FAIL("local variable not found");
return Dart_Null();
}
static void PrintStackTrace(Dart_StackTrace trace) {
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_TRUE(res);
for (int i = 0; i < trace_len; i++) {
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, i, &frame);
EXPECT_TRUE(res);
PrintActivationFrame(frame);
}
}
static void VerifyListEquals(Dart_Handle expected,
Dart_Handle got,
bool skip_null_expects) {
EXPECT(Dart_IsList(expected));
EXPECT(Dart_IsList(got));
Dart_Handle res;
intptr_t expected_length;
res = Dart_ListLength(expected, &expected_length);
EXPECT_VALID(res);
intptr_t got_length;
res = Dart_ListLength(expected, &got_length);
EXPECT_VALID(res);
EXPECT_EQ(expected_length, got_length);
for (intptr_t i = 0; i < expected_length; i++) {
Dart_Handle expected_elem = Dart_ListGetAt(expected, i);
EXPECT_VALID(expected_elem);
Dart_Handle got_elem = Dart_ListGetAt(got, i);
EXPECT_VALID(got_elem);
bool equals;
res = Dart_ObjectEquals(expected_elem, got_elem, &equals);
EXPECT_VALID(res);
EXPECT(equals || (Dart_IsNull(expected_elem) && skip_null_expects));
}
}
static void VerifyStackFrame(Dart_ActivationFrame frame,
const char* expected_name,
Dart_Handle expected_locals,
bool skip_null_expects) {
Dart_Handle func_name;
Dart_Handle func;
Dart_Handle res;
res = Dart_ActivationFrameGetLocation(frame, &func_name, &func, NULL);
EXPECT_TRUE(res);
EXPECT(Dart_IsString(func_name));
const char* func_name_chars;
Dart_StringToCString(func_name, &func_name_chars);
if (expected_name != NULL) {
EXPECT_SUBSTRING(expected_name, func_name_chars);
}
EXPECT(Dart_IsFunction(func));
const char* func_name_chars_from_func_handle;
Dart_StringToCString(Dart_FunctionName(func),
&func_name_chars_from_func_handle);
EXPECT_SUBSTRING(func_name_chars_from_func_handle, func_name_chars);
if (!Dart_IsNull(expected_locals)) {
Dart_Handle locals = Dart_GetLocalVariables(frame);
EXPECT_VALID(locals);
VerifyListEquals(expected_locals, locals, skip_null_expects);
}
}
static void VerifyStackTrace(Dart_StackTrace trace,
const char* func_names[],
Dart_Handle local_vars[],
int expected_frames,
bool skip_null_expects) {
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_TRUE(res);
uintptr_t last_frame_pointer = 0;
uintptr_t frame_pointer;
for (int i = 0; i < trace_len; i++) {
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, i, &frame);
EXPECT_TRUE(res);
res = Dart_ActivationFrameGetFramePointer(frame, &frame_pointer);
EXPECT_TRUE(res);
if (i > 0) {
#if !defined(TARGET_ARCH_DBC)
// We expect the stack to grow from high to low addresses.
EXPECT_GT(frame_pointer, last_frame_pointer);
#else
// On DBC stack grows upwards from low to high addresses.
EXPECT_LT(frame_pointer, last_frame_pointer);
#endif
}
last_frame_pointer = frame_pointer;
if (i < expected_frames) {
VerifyStackFrame(frame, func_names[i], local_vars[i], skip_null_expects);
} else {
VerifyStackFrame(frame, NULL, Dart_Null(), skip_null_expects);
}
}
}
void TestBreakpointHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
const char* expected_trace[] = {"A.foo", "main"};
const intptr_t expected_trace_length = 2;
breakpoint_hit = true;
breakpoint_hit_counter++;
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT_EQ(expected_trace_length, trace_len);
for (int i = 0; i < trace_len; i++) {
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, i, &frame);
EXPECT_VALID(res);
Dart_Handle func_name;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
EXPECT_VALID(res);
EXPECT(Dart_IsString(func_name));
const char* name_chars;
Dart_StringToCString(func_name, &name_chars);
EXPECT_STREQ(expected_trace[i], name_chars);
if (verbose) OS::Print(" >> %d: %s\n", i, name_chars);
}
}
TEST_CASE(Debug_Breakpoint) {
const char* kScriptChars =
"void moo(s) { } \n"
"class A { \n"
" static void foo() { \n"
" moo('good news'); \n"
" } \n"
"} \n"
"void main() { \n"
" A.foo(); \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&TestBreakpointHandler);
SetBreakpointAtEntry("A", "foo");
breakpoint_hit = false;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(breakpoint_hit == true);
}
static const int stack_buffer_size = 1024;
static char stack_buffer[stack_buffer_size];
static void SaveStackTrace(Dart_StackTrace trace) {
Dart_ActivationFrame frame;
Dart_Handle func_name;
char* buffer = stack_buffer;
int buffer_size = stack_buffer_size;
intptr_t trace_len;
EXPECT_VALID(Dart_StackTraceLength(trace, &trace_len));
for (intptr_t frame_index = 0; frame_index < trace_len; frame_index++) {
EXPECT_VALID(Dart_GetActivationFrame(trace, frame_index, &frame));
EXPECT_VALID(Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL));
int pos = OS::SNPrint(buffer, buffer_size, "[%" Pd "] %s { ", frame_index,
ToCString(func_name));
buffer += pos;
buffer_size -= pos;
Dart_Handle locals = Dart_GetLocalVariables(frame);
EXPECT_VALID(locals);
intptr_t list_length = 0;
EXPECT_VALID(Dart_ListLength(locals, &list_length));
for (intptr_t i = 0; i + 1 < list_length; i += 2) {
Dart_Handle name = Dart_ListGetAt(locals, i);
EXPECT_VALID(name);
EXPECT(Dart_IsString(name));
Dart_Handle value = Dart_ListGetAt(locals, i + 1);
EXPECT_VALID(value);
Dart_Handle value_str = Dart_ToString(value);
EXPECT_VALID(value_str);
const char* name_cstr = NULL;
const char* value_cstr = NULL;
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
EXPECT_VALID(Dart_StringToCString(value_str, &value_cstr));
pos = OS::SNPrint(buffer, buffer_size, "%s = %s ", name_cstr, value_cstr);
buffer += pos;
buffer_size -= pos;
}
pos = OS::SNPrint(buffer, buffer_size, "}\n");
buffer += pos;
buffer_size -= pos;
}
}
static void InspectOptimizedStack_Breakpoint(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& loc) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
SaveStackTrace(trace);
}
static void InspectStackTest(bool optimize) {
const char* kScriptChars =
"void breakpointNow() {\n"
"}\n"
"int helper(int a, int b, bool stop) {\n"
" if (b == 99 && stop) {\n"
" breakpointNow();\n"
" }\n"
" int c = a*b;\n"
" return c;\n"
"}\n"
"int anotherMiddleMan(int one, int two, bool stop) {\n"
" return helper(one, two, stop);\n"
"}\n"
"int middleMan(int x, int limit, bool stop) {\n"
" int value = 0;\n"
" for (int i = 0; i < limit; i++) {\n"
" value += anotherMiddleMan(x, i, stop);\n"
" }\n"
" return value;\n"
"}\n"
"int test(bool stop, int limit) {\n"
" return middleMan(5, limit, stop);\n"
"}\n";
LoadScript(kScriptChars);
// Save/restore some compiler flags.
Dart_Handle dart_args[2];
int saved_threshold = FLAG_optimization_counter_threshold;
const int kLowThreshold = 100;
const int kHighThreshold = 10000;
Isolate* isolate = Isolate::Current();
const bool saved_use_osr = isolate->use_osr();
isolate->set_use_osr(false);
if (optimize) {
// Warm up the code to make sure it gets optimized. We ignore any
// breakpoints that get hit during warm-up.
FLAG_optimization_counter_threshold = kLowThreshold;
dart_args[0] = Dart_False();
dart_args[1] = Dart_NewInteger(kLowThreshold);
EXPECT_VALID(Dart_Invoke(script_lib, NewString("test"), 2, dart_args));
} else {
// Try to ensure that none of the test code gets optimized.
FLAG_optimization_counter_threshold = kHighThreshold;
}
// Set up the breakpoint.
Dart_SetPausedEventHandler(InspectOptimizedStack_Breakpoint);
SetBreakpointAtEntry("", "breakpointNow");
// Run the code and inspect the stack.
stack_buffer[0] = '\0';
dart_args[0] = Dart_True();
dart_args[1] = Dart_NewInteger(kLowThreshold);
EXPECT_VALID(Dart_Invoke(script_lib, NewString("test"), 2, dart_args));
if (optimize) {
EXPECT_STREQ(
"[0] breakpointNow { }\n"
"[1] helper { a = 5 b = 99 stop = <optimized out> }\n"
"[2] anotherMiddleMan { one = <optimized out> "
"two = <optimized out> stop = <optimized out> }\n"
"[3] middleMan { x = 5 limit = 100 stop = true value = 24255"
" i = 99 }\n"
"[4] test { stop = true limit = 100 }\n",
stack_buffer);
} else {
EXPECT_STREQ(
"[0] breakpointNow { }\n"
"[1] helper { a = 5 b = 99 stop = true }\n"
"[2] anotherMiddleMan { one = 5 two = 99 stop = true }\n"
"[3] middleMan { x = 5 limit = 100 stop = true value = 24255"
" i = 99 }\n"
"[4] test { stop = true limit = 100 }\n",
stack_buffer);
}
FLAG_optimization_counter_threshold = saved_threshold;
isolate->set_use_osr(saved_use_osr);
}
TEST_CASE(Debug_InspectStack_NotOptimized) {
InspectStackTest(false);
}
TEST_CASE(Debug_InspectStack_Optimized) {
// Ensure code gets optimized.
FLAG_background_compilation = false;
InspectStackTest(true);
}
static void InspectStackWithClosureTest(bool optimize) {
const char* kScriptChars =
"void breakpointNow() {\n"
"}\n"
"int helper(int a, int b, bool stop) {\n"
" if (b == 99 && stop) {\n"
" breakpointNow();\n"
" }\n"
" int c = a*b;\n"
" return c;\n"
"}\n"
"int anotherMiddleMan(func) {\n"
" return func(10);\n"
"}\n"
"int middleMan(int x, int limit, bool stop) {\n"
" int value = 0;\n"
" for (int i = 0; i < limit; i++) {\n"
" value += anotherMiddleMan((value) {\n"
" return helper((x * value), i, stop);\n"
" });\n"
" }\n"
" return value;\n"
"}\n"
"int test(bool stop, int limit) {\n"
" return middleMan(5, limit, stop);\n"
"}\n";
LoadScript(kScriptChars);
// Save/restore some compiler flags.
Dart_Handle dart_args[2];
int saved_threshold = FLAG_optimization_counter_threshold;
const int kLowThreshold = 100;
const int kHighThreshold = 10000;
bool saved_osr = FLAG_use_osr;
FLAG_use_osr = false;
if (optimize) {
// Warm up the code to make sure it gets optimized. We ignore any
// breakpoints that get hit during warm-up.
FLAG_optimization_counter_threshold = kLowThreshold;
dart_args[0] = Dart_False();
dart_args[1] = Dart_NewInteger(kLowThreshold);
EXPECT_VALID(Dart_Invoke(script_lib, NewString("test"), 2, dart_args));
} else {
// Try to ensure that none of the test code gets optimized.
FLAG_optimization_counter_threshold = kHighThreshold;
}
// Set up the breakpoint.
Dart_SetPausedEventHandler(InspectOptimizedStack_Breakpoint);
SetBreakpointAtEntry("", "breakpointNow");
// Run the code and inspect the stack.
stack_buffer[0] = '\0';
dart_args[0] = Dart_True();
dart_args[1] = Dart_NewInteger(kLowThreshold);
EXPECT_VALID(Dart_Invoke(script_lib, NewString("test"), 2, dart_args));
if (optimize) {
EXPECT_STREQ(
"[0] breakpointNow { }\n"
"[1] helper { a = 50 b = 99 stop = <optimized out> }\n"
"[2] <anonymous closure> { x = 5 i = 99 stop = true"
" value = <optimized out> }\n"
"[3] anotherMiddleMan { func = <optimized out> }\n"
"[4] middleMan { x = 5 limit = 100 stop = true"
" value = 242550 i = 99 }\n"
"[5] test { stop = true limit = 100 }\n",
stack_buffer);
} else {
EXPECT_STREQ(
"[0] breakpointNow { }\n"
"[1] helper { a = 50 b = 99 stop = true }\n"
"[2] <anonymous closure> { x = 5 i = 99 stop = true"
" value = 10 }\n"
"[3] anotherMiddleMan {"
" func = Closure: (dynamic) => dynamic }\n"
"[4] middleMan { x = 5 limit = 100 stop = true"
" value = 242550 i = 99 }\n"
"[5] test { stop = true limit = 100 }\n",
stack_buffer);
}
FLAG_optimization_counter_threshold = saved_threshold;
FLAG_use_osr = saved_osr;
}
TEST_CASE(Debug_InspectStackWithClosure_NotOptimized) {
InspectStackWithClosureTest(false);
}
TEST_CASE(Debug_InspectStackWithClosure_Optimized) {
// Ensure code gets optimized.
FLAG_background_compilation = false;
InspectStackWithClosureTest(true);
}
void TestStepOutHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
const char* expected_bpts[] = {"f1", "foo", "main"};
const intptr_t expected_bpts_length = ARRAY_SIZE(expected_bpts);
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT(breakpoint_hit_counter < expected_bpts_length);
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, 0, &frame);
EXPECT_VALID(res);
Dart_Handle func_name;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
EXPECT_VALID(res);
EXPECT(Dart_IsString(func_name));
const char* name_chars;
Dart_StringToCString(func_name, &name_chars);
if (breakpoint_hit_counter < expected_bpts_length) {
EXPECT_STREQ(expected_bpts[breakpoint_hit_counter], name_chars);
}
if (verbose) {
OS::Print(" >> bpt nr %d: %s\n", breakpoint_hit_counter, name_chars);
}
breakpoint_hit = true;
breakpoint_hit_counter++;
Dart_SetStepOut();
}
TEST_CASE(Debug_StepOut) {
const char* kScriptChars =
"f1() { return 1; } \n"
"f2() { return 2; } \n"
" \n"
"foo() { \n"
" f1(); \n"
" return f2(); \n"
"} \n"
" \n"
"main() { \n"
" return foo(); \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&TestStepOutHandler);
// Set a breakpoint in function f1, then repeatedly step out until
// we get to main. We should see one breakpoint each in f1,
// foo, main, but not in f2.
SetBreakpointAtEntry("", "f1");
breakpoint_hit = false;
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(Dart_IsInteger(retval));
int64_t int_value = ToInt64(retval);
EXPECT_EQ(2, int_value);
EXPECT(breakpoint_hit == true);
}
static const char* step_into_expected_bpts[] = {
"main", // entry
"main", // call foo
"foo", // entry
"foo", // call f1
"f1", // entry
"f1", // return
"foo", // call initializer
"foo", // call kvmk
"X.kvmk", // entry
"X.kvmk", // call
"f2", // entry
"f2", // return
"X.kvmk", // call +
"X.kvmk", // return
"foo", // return
"main" // return
};
void TestStepIntoHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
const intptr_t expected_bpts_length = ARRAY_SIZE(step_into_expected_bpts);
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT(breakpoint_hit_counter < expected_bpts_length);
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, 0, &frame);
EXPECT_VALID(res);
Dart_Handle func_name;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
EXPECT_VALID(res);
EXPECT(Dart_IsString(func_name));
const char* name_chars;
Dart_StringToCString(func_name, &name_chars);
if (breakpoint_hit_counter < expected_bpts_length) {
EXPECT_STREQ(step_into_expected_bpts[breakpoint_hit_counter], name_chars);
}
if (verbose) {
OS::Print(" >> bpt nr %d: %s\n", breakpoint_hit_counter, name_chars);
}
breakpoint_hit = true;
breakpoint_hit_counter++;
Dart_SetStepInto();
}
TEST_CASE(Debug_StepInto) {
const char* kScriptChars =
"f1() { return 1; } \n"
"f2() { return 2; } \n"
" \n"
"class X { \n"
" kvmk(a, {b, c}) { \n"
" return c + f2(); \n"
" } \n"
"} \n"
" \n"
"foo() { \n"
" f1(); \n"
" var o = new X(); \n"
" return o.kvmk(3, c:5); \n"
"} \n"
" \n"
"main() { \n"
" return foo(); \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&TestStepIntoHandler);
DisableDebuggabilityOfDartColonLibraries();
SetBreakpointAtEntry("", "main");
breakpoint_hit = false;
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(Dart_IsInteger(retval));
int64_t int_value = ToInt64(retval);
EXPECT_EQ(7, int_value);
EXPECT(breakpoint_hit == true);
EXPECT(breakpoint_hit_counter == ARRAY_SIZE(step_into_expected_bpts));
}
static void StepIntoHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
if (verbose) {
OS::Print(">>> Breakpoint nr. %d in %s <<<\n", breakpoint_hit_counter,
BreakpointInfo(trace));
PrintStackTrace(trace);
}
breakpoint_hit = true;
breakpoint_hit_counter++;
Dart_SetStepInto();
}
TEST_CASE(Debug_IgnoreBP) {
const char* kScriptChars =
"class B { \n"
" static var z = 0; \n"
" var i = 100; \n"
" var d = 3.14; \n"
" var s = 'Dr Seuss'; \n"
"} \n"
" \n"
"main() { \n"
" var x = new B(); \n"
" return x.i + 1; \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&StepIntoHandler);
SetBreakpointAtEntry("", "main");
breakpoint_hit = false;
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(Dart_IsInteger(retval));
int64_t int_value = ToInt64(retval);
EXPECT_EQ(101, int_value);
EXPECT(breakpoint_hit == true);
}
TEST_CASE(Debug_DeoptimizeFunction) {
const char* kScriptChars =
"foo(x) => 2 * x; \n"
" \n"
"warmup() { \n"
" for (int i = 0; i < 5000; i++) { \n"
" foo(i); \n"
" } \n"
"} \n"
" \n"
"main() { \n"
" return foo(99); \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&StepIntoHandler);
// Cause function foo to be optimized before we set a BP.
Dart_Handle res = Invoke("warmup");
EXPECT_VALID(res);
// Now set breakpoint in main and then step into optimized function foo.
SetBreakpointAtEntry("", "main");
breakpoint_hit = false;
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(Dart_IsInteger(retval));
int64_t int_value = ToInt64(retval);
EXPECT_EQ(2 * 99, int_value);
EXPECT(breakpoint_hit == true);
}
void TestSingleStepHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
const char* expected_bpts[] = {"moo", "moo", "foo", "moo", "moo",
"foo", "moo", "moo", "foo", "main"};
const intptr_t expected_bpts_length = ARRAY_SIZE(expected_bpts);
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT(breakpoint_hit_counter < expected_bpts_length);
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, 0, &frame);
EXPECT_VALID(res);
Dart_Handle func_name;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
EXPECT_VALID(res);
EXPECT(Dart_IsString(func_name));
const char* name_chars;
Dart_StringToCString(func_name, &name_chars);
if (verbose) {
OS::Print(" >> bpt nr %d: %s\n", breakpoint_hit_counter, name_chars);
}
if (breakpoint_hit_counter < expected_bpts_length) {
EXPECT_STREQ(expected_bpts[breakpoint_hit_counter], name_chars);
}
breakpoint_hit = true;
breakpoint_hit_counter++;
Dart_SetStepOver();
}
TEST_CASE(Debug_SingleStep) {
const char* kScriptChars =
"moo(s) { return 1; } \n"
" \n"
"void foo() { \n"
" moo('step one'); \n"
" moo('step two'); \n"
" moo('step three'); \n"
"} \n"
" \n"
"void main() { \n"
" foo(); \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&TestSingleStepHandler);
SetBreakpointAtEntry("", "moo");
breakpoint_hit = false;
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(breakpoint_hit == true);
}
static void ClosureBreakpointHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
const char* expected_trace[] = {"callback", "main"};
const intptr_t expected_trace_length = 2;
breakpoint_hit_counter++;
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT_EQ(expected_trace_length, trace_len);
for (int i = 0; i < trace_len; i++) {
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, i, &frame);
EXPECT_VALID(res);
Dart_Handle func_name;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
EXPECT_VALID(res);
EXPECT(Dart_IsString(func_name));
const char* name_chars;
Dart_StringToCString(func_name, &name_chars);
EXPECT_STREQ(expected_trace[i], name_chars);
if (verbose) OS::Print(" >> %d: %s\n", i, name_chars);
}
}
TEST_CASE(Debug_ClosureBreakpoint) {
const char* kScriptChars =
"callback(s) { \n"
" return 111; \n"
"} \n"
" \n"
"main() { \n"
" var h = callback; \n"
" h('bla'); \n"
" callback('jada'); \n"
" return 442; \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&ClosureBreakpointHandler);
SetBreakpointAtEntry("", "callback");
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
int64_t int_value = ToInt64(retval);
EXPECT_EQ(442, int_value);
EXPECT_EQ(2, breakpoint_hit_counter);
}
static void ExprClosureBreakpointHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
static const char* expected_trace[] = {"<anonymous closure>", "main"};
Dart_Handle add_locals = Dart_NewList(4);
Dart_ListSetAt(add_locals, 0, NewString("a"));
Dart_ListSetAt(add_locals, 1, Dart_NewInteger(10));
Dart_ListSetAt(add_locals, 2, NewString("b"));
Dart_ListSetAt(add_locals, 3, Dart_NewInteger(20));
Dart_Handle expected_locals[] = {add_locals, Dart_Null()};
breakpoint_hit_counter++;
PrintStackTrace(trace);
VerifyStackTrace(trace, expected_trace, expected_locals, 2, false);
}
TEST_CASE(Debug_ExprClosureBreakpoint) {
const char* kScriptChars =
"var c; \n"
" \n"
"main() { \n"
" c = (a, b) { \n"
" return a + b; \n"
" }; \n"
" return c(10, 20); \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&ExprClosureBreakpointHandler);
Dart_Handle script_url = NewString(TestCase::url());
intptr_t line_no = 5; // In closure 'add'.
Dart_Handle res = Dart_SetBreakpoint(script_url, line_no);
EXPECT_VALID(res);
EXPECT(Dart_IsInteger(res));
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
int64_t int_value = ToInt64(retval);
EXPECT_EQ(30, int_value);
EXPECT_EQ(1, breakpoint_hit_counter);
}
void TestBreakpointHandlerWithVerify(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
breakpoint_hit = true;
breakpoint_hit_counter++;
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
Dart_ActivationFrame frame;
Dart_Handle res = Dart_GetActivationFrame(trace, 0, &frame);
EXPECT_VALID(res);
Dart_Handle func_name;
intptr_t line_number = -1;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, &line_number, NULL);
EXPECT_NE(-1, line_number);
if (verbose) OS::Print("Hit line %" Pd "\n", line_number);
VerifyPointersVisitor::VerifyPointers();
}
static void NoopNativeFunction(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_SetReturnValue(args, Dart_True());
Dart_ExitScope();
}
static Dart_NativeFunction NoopNativeResolver(Dart_Handle name,
int arg_count,
bool* auto_setup_scope) {
ASSERT(auto_setup_scope != NULL);
*auto_setup_scope = false;
return &NoopNativeFunction;
}
TEST_CASE(Debug_BreakpointStubPatching) {
// Note changes to this script may require changes to the breakpoint line
// numbers below.
const char* kScriptChars =
"bar(i) {} \n"
"nat() native 'a'; \n"
"foo(n) { \n"
" for(var i = 0; i < n; i++) { \n"
" bar(i); \n" // Static call.
" i++; \n" // Instance call.
" i == null; \n" // Equality.
" var x = i; \n" // Debug step check.
" i is int; \n" // Subtype test.
" y(z) => () => z + i; \n" // Allocate context.
" y(i)(); \n" // Closure call.
" nat(); \n" // Runtime call.
" return y; \n" // Return.
" } \n"
"} \n"
" \n"
"main() { \n"
" var i = 3; \n"
" foo(i); \n"
"} \n";
LoadScript(kScriptChars);
Dart_Handle result =
Dart_SetNativeResolver(script_lib, &NoopNativeResolver, NULL);
EXPECT_VALID(result);
Dart_SetPausedEventHandler(&TestBreakpointHandlerWithVerify);
Dart_Handle script_url = NewString(TestCase::url());
const intptr_t num_breakpoints = 9;
intptr_t breakpoint_lines[num_breakpoints] = {5, 6, 7, 8, 9, 10, 11, 12, 13};
for (intptr_t i = 0; i < num_breakpoints; i++) {
result = Dart_SetBreakpoint(script_url, breakpoint_lines[i]);
EXPECT_VALID(result);
EXPECT(Dart_IsInteger(result));
}
breakpoint_hit = false;
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(breakpoint_hit == true);
EXPECT_EQ(num_breakpoints, breakpoint_hit_counter);
}
static intptr_t bp_id_to_be_deleted;
static void DeleteBreakpointHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
const char* expected_trace[] = {"foo", "main"};
const intptr_t expected_trace_length = 2;
breakpoint_hit_counter++;
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT_EQ(expected_trace_length, trace_len);
for (int i = 0; i < trace_len; i++) {
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, i, &frame);
EXPECT_VALID(res);
Dart_Handle func_name;
res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
EXPECT_VALID(res);
EXPECT(Dart_IsString(func_name));
const char* name_chars;
Dart_StringToCString(func_name, &name_chars);
EXPECT_STREQ(expected_trace[i], name_chars);
if (verbose) OS::Print(" >> %d: %s\n", i, name_chars);
}
// Remove the breakpoint after we've hit it twice
if (breakpoint_hit_counter == 2) {
if (verbose) OS::Print("uninstalling breakpoint\n");
EXPECT_EQ(bp_id_to_be_deleted, bp_id);
Dart_Handle res = Dart_RemoveBreakpoint(bp_id);
EXPECT_VALID(res);
}
}
TEST_CASE(Debug_DeleteBreakpoint) {
const char* kScriptChars =
"moo(s) { } \n"
" \n"
"foo() { \n"
" moo('good news'); \n"
"} \n"
" \n"
"void main() { \n"
" foo(); \n"
" foo(); \n"
" foo(); \n"
"} \n";
LoadScript(kScriptChars);
Dart_Handle script_url = NewString(TestCase::url());
intptr_t line_no = 4; // In function 'foo'.
Dart_SetPausedEventHandler(&DeleteBreakpointHandler);
Dart_Handle res = Dart_SetBreakpoint(script_url, line_no);
EXPECT_VALID(res);
EXPECT(Dart_IsInteger(res));
int64_t bp_id = ToInt64(res);
// Function main() calls foo() 3 times. On the second iteration, the
// breakpoint is removed by the handler, so we expect the breakpoint
// to fire twice only.
bp_id_to_be_deleted = bp_id;
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT_EQ(2, breakpoint_hit_counter);
}
static void InspectStaticFieldHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
ASSERT(script_lib != NULL);
ASSERT(!Dart_IsError(script_lib));
ASSERT(Dart_IsLibrary(script_lib));
Dart_Handle class_A = Dart_GetClass(script_lib, NewString("A"));
EXPECT_VALID(class_A);
const int expected_num_fields = 2;
struct {
const char* field_name;
const char* field_value;
} expected[] = {// Expected values at first breakpoint.
{"bla", "yada yada yada"},
{"u", "null"},
// Expected values at second breakpoint.
{"bla", "silence is golden"},
{"u", "442"}};
ASSERT(breakpoint_hit_counter < 2);
int expected_idx = breakpoint_hit_counter * expected_num_fields;
breakpoint_hit_counter++;
Dart_Handle fields = Dart_GetStaticFields(class_A);
ASSERT(!Dart_IsError(fields));
ASSERT(Dart_IsList(fields));
intptr_t list_length = 0;
Dart_Handle retval = Dart_ListLength(fields, &list_length);
EXPECT_VALID(retval);
int num_fields = list_length / 2;
OS::Print("Class A has %d fields:\n", num_fields);
ASSERT(expected_num_fields == num_fields);
for (int i = 0; i + 1 < list_length; i += 2) {
Dart_Handle name_handle = Dart_ListGetAt(fields, i);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
char const* name;
Dart_StringToCString(name_handle, &name);
EXPECT_STREQ(expected[expected_idx].field_name, name);
Dart_Handle value_handle = Dart_ListGetAt(fields, i + 1);
EXPECT_VALID(value_handle);
value_handle = Dart_ToString(value_handle);
EXPECT_VALID(value_handle);
EXPECT(Dart_IsString(value_handle));
char const* value;
Dart_StringToCString(value_handle, &value);
EXPECT_STREQ(expected[expected_idx].field_value, value);
OS::Print(" %s: %s\n", name, value);
expected_idx++;
}
}
TEST_CASE(Debug_InspectStaticField) {
const char* kScriptChars =
" class A { \n"
" static var bla = 'yada yada yada'; \n"
" static var u; \n"
" } \n"
" \n"
" debugBreak() { } \n"
" main() { \n"
" var a = new A(); \n"
" debugBreak(); \n"
" A.u = 442; \n"
" A.bla = 'silence is golden'; \n"
" debugBreak(); \n"
" } \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&InspectStaticFieldHandler);
SetBreakpointAtEntry("", "debugBreak");
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
}
TEST_CASE(Debug_InspectObject) {
const char* kScriptChars =
" class A { \n"
" var a_field = 'a'; \n"
" static var bla = 'yada yada yada'; \n"
" static var error = unresolvedName(); \n"
" var d = 42.1; \n"
" } \n"
" class B extends A { \n"
" var oneDay = const Duration(hours: 24); \n"
" static var bla = 'blah blah'; \n"
" } \n"
" get_b() { return new B(); } \n"
" get_int() { return 666; } \n";
// Number of instance fields in an object of class B.
const intptr_t kNumObjectFields = 3;
LoadScript(kScriptChars);
Dart_Handle object_b = Invoke("get_b");
EXPECT_VALID(object_b);
Dart_Handle fields = Dart_GetInstanceFields(object_b);
EXPECT_VALID(fields);
EXPECT(Dart_IsList(fields));
intptr_t list_length = 0;
Dart_Handle retval = Dart_ListLength(fields, &list_length);
EXPECT_VALID(retval);
int num_fields = list_length / 2;
EXPECT_EQ(kNumObjectFields, num_fields);
OS::Print("Object has %d fields:\n", num_fields);
for (int i = 0; i + 1 < list_length; i += 2) {
Dart_Handle name_handle = Dart_ListGetAt(fields, i);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
char const* name;
Dart_StringToCString(name_handle, &name);
Dart_Handle value_handle = Dart_ListGetAt(fields, i + 1);
EXPECT_VALID(value_handle);
value_handle = Dart_ToString(value_handle);
EXPECT_VALID(value_handle);
EXPECT(Dart_IsString(value_handle));
char const* value;
Dart_StringToCString(value_handle, &value);
OS::Print(" %s: %s\n", name, value);
}
// Check that an integer value returns an empty list of fields.
Dart_Handle triple_six = Invoke("get_int");
EXPECT_VALID(triple_six);
EXPECT(Dart_IsInteger(triple_six));
int64_t int_value = ToInt64(triple_six);
EXPECT_EQ(666, int_value);
fields = Dart_GetInstanceFields(triple_six);
EXPECT_VALID(fields);
EXPECT(Dart_IsList(fields));
retval = Dart_ListLength(fields, &list_length);
EXPECT_EQ(0, list_length);
// Check static field of class B (one field named 'bla')
Dart_Handle class_B = Dart_GetObjClass(object_b);
EXPECT_VALID(class_B);
EXPECT(!Dart_IsNull(class_B));
fields = Dart_GetStaticFields(class_B);
EXPECT_VALID(fields);
EXPECT(Dart_IsList(fields));
list_length = 0;
retval = Dart_ListLength(fields, &list_length);
EXPECT_VALID(retval);
EXPECT_EQ(2, list_length);
Dart_Handle name_handle = Dart_ListGetAt(fields, 0);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
char const* name;
Dart_StringToCString(name_handle, &name);
EXPECT_STREQ("bla", name);
Dart_Handle value_handle = Dart_ListGetAt(fields, 1);
EXPECT_VALID(value_handle);
value_handle = Dart_ToString(value_handle);
EXPECT_VALID(value_handle);
EXPECT(Dart_IsString(value_handle));
char const* value;
Dart_StringToCString(value_handle, &value);
EXPECT_STREQ("blah blah", value);
// Check static field of B's superclass.
Dart_Handle class_A = Dart_GetSupertype(class_B);
EXPECT_VALID(class_A);
EXPECT(!Dart_IsNull(class_A));
fields = Dart_GetStaticFields(class_A);
EXPECT_VALID(fields);
EXPECT(Dart_IsList(fields));
list_length = 0;
retval = Dart_ListLength(fields, &list_length);
EXPECT_VALID(retval);
EXPECT_EQ(4, list_length);
// Static field "bla" should have value "yada yada yada".
name_handle = Dart_ListGetAt(fields, 0);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
Dart_StringToCString(name_handle, &name);
EXPECT_STREQ("bla", name);
value_handle = Dart_ListGetAt(fields, 1);
EXPECT_VALID(value_handle);
value_handle = Dart_ToString(value_handle);
EXPECT_VALID(value_handle);
EXPECT(Dart_IsString(value_handle));
Dart_StringToCString(value_handle, &value);
EXPECT_STREQ("yada yada yada", value);
// The static field "error" should result in a compile error.
name_handle = Dart_ListGetAt(fields, 2);
EXPECT_VALID(name_handle);
EXPECT(Dart_IsString(name_handle));
Dart_StringToCString(name_handle, &name);
EXPECT_STREQ("error", name);
value_handle = Dart_ListGetAt(fields, 3);
EXPECT(Dart_IsError(value_handle));
}
static Dart_IsolateId test_isolate_id = ILLEGAL_ISOLATE_ID;
static int verify_callback = 0;
static void TestIsolateID(Dart_IsolateId isolate_id, Dart_IsolateEvent kind) {
if (kind == kCreated) {
EXPECT(test_isolate_id == ILLEGAL_ISOLATE_ID);
test_isolate_id = isolate_id;
Dart_Isolate isolate = Dart_GetIsolate(isolate_id);
EXPECT(isolate == Dart_CurrentIsolate());
verify_callback |= 0x1; // Register create callback.
} else if (kind == kInterrupted) {
EXPECT(test_isolate_id == isolate_id);
Dart_Isolate isolate = Dart_GetIsolate(isolate_id);
EXPECT(isolate == Dart_CurrentIsolate());
verify_callback |= 0x2; // Register interrupt callback.
} else if (kind == kShutdown) {
EXPECT(test_isolate_id == isolate_id);
Dart_Isolate isolate = Dart_GetIsolate(isolate_id);
EXPECT(isolate == Dart_CurrentIsolate());
verify_callback |= 0x4; // Register shutdown callback.
}
}
VM_UNIT_TEST_CASE(Debug_IsolateID) {
const char* kScriptChars =
"void moo(s) { } \n"
"class A { \n"
" static void foo() { \n"
" moo('good news'); \n"
" } \n"
"} \n"
"void main() { \n"
" A.foo(); \n"
"} \n";
Dart_SetIsolateEventHandler(&TestIsolateID);
Dart_Isolate isolate = TestCase::CreateTestIsolate();
ASSERT(isolate != NULL);
Dart_EnterScope();
LoadScript(kScriptChars);
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
EXPECT(test_isolate_id != ILLEGAL_ISOLATE_ID);
EXPECT(Dart_GetIsolate(test_isolate_id) == isolate);
EXPECT(Dart_GetIsolateId(isolate) == test_isolate_id);
Dart_ExitScope();
Dart_ShutdownIsolate();
Dart_SetIsolateEventHandler(NULL);
EXPECT(verify_callback == 0x5); // Only created and shutdown events.
}
static Monitor* sync = NULL;
static bool isolate_interrupted = false;
static bool pause_event_handled = false;
static bool interrupt_thread_stopped = false;
static Dart_IsolateId interrupt_isolate_id = ILLEGAL_ISOLATE_ID;
static volatile bool continue_isolate_loop = true;
static void InterruptIsolateHandler(Dart_IsolateId isolateId,
intptr_t breakpointId,
const Dart_CodeLocation& location) {
MonitorLocker ml(sync);
pause_event_handled = true;
ml.Notify();
}
static void TestInterruptIsolate(Dart_IsolateId isolate_id,
Dart_IsolateEvent kind) {
if (kind == kCreated) {
EXPECT(interrupt_isolate_id == ILLEGAL_ISOLATE_ID);
// Indicate that the isolate has been created.
{
MonitorLocker ml(sync);
interrupt_isolate_id = isolate_id;
ml.Notify();
}
} else if (kind == kInterrupted) {
// Indicate that isolate has been interrupted.
{
MonitorLocker ml(sync);
isolate_interrupted = true;
continue_isolate_loop = false;
Dart_SetStepInto();
}
} else if (kind == kShutdown) {
if (interrupt_isolate_id == isolate_id) {
MonitorLocker ml(sync);
interrupt_isolate_id = ILLEGAL_ISOLATE_ID;
ml.Notify();
}
}
}
static void InterruptNativeFunction(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle val = Dart_NewBoolean(continue_isolate_loop);
Dart_SetReturnValue(args, val);
Dart_ExitScope();
}
static Dart_NativeFunction InterruptNativeResolver(Dart_Handle name,
int arg_count,
bool* auto_setup_scope) {
ASSERT(auto_setup_scope != NULL);
*auto_setup_scope = false;
return &InterruptNativeFunction;
}
static void InterruptIsolateRun(uword unused) {
const char* kScriptChars =
"void moo(s) { } \n"
"class A { \n"
" static check() native 'a'; \n"
" static void foo() { \n"
" var loop = true; \n"
" while (loop) { \n"
" moo('good news'); \n"
" loop = check(); \n"
" } \n"
" } \n"
"} \n"
"void main() { \n"
" A.foo(); \n"
"} \n";
Dart_Isolate isolate = TestCase::CreateTestIsolate();
ASSERT(isolate != NULL);
Dart_EnterScope();
LoadScript(kScriptChars);
Dart_Handle result =
Dart_SetNativeResolver(script_lib, &InterruptNativeResolver, NULL);
EXPECT_VALID(result);
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
Dart_ExitScope();
Dart_ShutdownIsolate();
{
// Notify the waiting thread that we are done.
MonitorLocker ml(sync);
interrupt_thread_stopped = true;
ml.Notify();
}
}
TEST_CASE(Debug_InterruptIsolate) {
bool saved_flag = FLAG_trace_shutdown;
FLAG_trace_shutdown = true;
sync = new Monitor();
Dart_SetIsolateEventHandler(&TestInterruptIsolate);
EXPECT(interrupt_isolate_id == ILLEGAL_ISOLATE_ID);
Dart_SetPausedEventHandler(InterruptIsolateHandler);
{
MonitorLocker ml(sync);
interrupt_thread_stopped = false;
}
int result = OSThread::Start("DebugInterruptIsolate", InterruptIsolateRun, 0);
EXPECT_EQ(0, result);
// Wait for the test isolate to be created.
{
OS::PrintErr("Waiting for isolate to be created\n");
MonitorLocker ml(sync);
while (interrupt_isolate_id == ILLEGAL_ISOLATE_ID) {
ml.Wait();
}
}
EXPECT(interrupt_isolate_id != ILLEGAL_ISOLATE_ID);
Dart_Isolate isolate = Dart_GetIsolate(interrupt_isolate_id);
EXPECT(isolate != NULL);
Dart_InterruptIsolate(isolate);
// Wait for the test isolate to be interrupted.
{
OS::PrintErr("Waiting for isolate to be interrupted\n");
MonitorLocker ml(sync);
while (!isolate_interrupted || !pause_event_handled) {
ml.Wait();
}
}
EXPECT(isolate_interrupted);
EXPECT(pause_event_handled);
// Wait for the test isolate to shutdown.
{
OS::PrintErr("Waiting for isolate to be shut down\n");
MonitorLocker ml(sync);
while (interrupt_isolate_id != ILLEGAL_ISOLATE_ID) {
ml.Wait();
}
}
EXPECT(interrupt_isolate_id == ILLEGAL_ISOLATE_ID);
// Wait for the OSThread that we started above, if we do
// not wait we end up with a race between the process
// exiting and cleaning up while the thread above is cleaning
// up stuff from the isolate leading to flaky crashes.
{
MonitorLocker ml(sync);
while (!interrupt_thread_stopped) {
ml.Wait();
}
}
OS::PrintErr("Complete\n");
FLAG_trace_shutdown = saved_flag;
}
static void StackTraceDump1BreakpointHandler(
Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
const int kStackTraceLen = 4;
static const char* expected_trace[kStackTraceLen] = {
"local_to_main", "Test.local1_to_func1", "Test.func1", "main"};
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT_EQ(kStackTraceLen, trace_len);
// Frame 0 corresponding to "local_to_main".
Dart_Handle frame0_locals = Dart_NewList(8);
Dart_ListSetAt(frame0_locals, 0, NewString("i"));
Dart_ListSetAt(frame0_locals, 1, Dart_NewInteger(76));
Dart_ListSetAt(frame0_locals, 2, NewString("j"));
Dart_ListSetAt(frame0_locals, 3, Dart_NewInteger(119));
Dart_ListSetAt(frame0_locals, 4, NewString("k"));
Dart_ListSetAt(frame0_locals, 5, Dart_NewInteger(66));
Dart_ListSetAt(frame0_locals, 6, NewString("l"));
Dart_ListSetAt(frame0_locals, 7, Dart_NewInteger(99));
// Frame 1 corresponding to "Test.local1_to_func1".
Dart_Handle frame1_locals = Dart_NewList(12);
Dart_ListSetAt(frame1_locals, 0, NewString("i"));
Dart_ListSetAt(frame1_locals, 1, Dart_NewInteger(11));
Dart_ListSetAt(frame1_locals, 2, NewString("j"));
Dart_ListSetAt(frame1_locals, 3, Dart_NewInteger(22));
Dart_ListSetAt(frame1_locals, 4, NewString("k"));
Dart_ListSetAt(frame1_locals, 5, Dart_NewInteger(33));
Dart_ListSetAt(frame1_locals, 6, NewString("l"));
Dart_ListSetAt(frame1_locals, 7, Dart_NewInteger(44));
Dart_ListSetAt(frame1_locals, 8, NewString("m"));
Dart_ListSetAt(frame1_locals, 9, Dart_NewInteger(55));
Dart_ListSetAt(frame1_locals, 10, NewString("func"));
Dart_ListSetAt(frame1_locals, 11, Dart_Null());
// Frame 2 corresponding to "Test.func1".
Dart_Handle frame2_locals = Dart_NewList(18);
Dart_ListSetAt(frame2_locals, 0, NewString("this"));
Dart_ListSetAt(frame2_locals, 1, Dart_Null());
Dart_ListSetAt(frame2_locals, 2, NewString("func"));
Dart_ListSetAt(frame2_locals, 3, Dart_Null());
Dart_ListSetAt(frame2_locals, 4, NewString("i"));
Dart_ListSetAt(frame2_locals, 5, Dart_NewInteger(11));
Dart_ListSetAt(frame2_locals, 6, NewString("j"));
Dart_ListSetAt(frame2_locals, 7, Dart_NewInteger(22));
Dart_ListSetAt(frame2_locals, 8, NewString("k"));
Dart_ListSetAt(frame2_locals, 9, Dart_NewInteger(33));
Dart_ListSetAt(frame2_locals, 10, NewString("l"));
Dart_ListSetAt(frame2_locals, 11, Dart_NewInteger(44));
Dart_ListSetAt(frame2_locals, 12, NewString("m"));
Dart_ListSetAt(frame2_locals, 13, Dart_NewInteger(55));
Dart_ListSetAt(frame2_locals, 14, NewString("local1_to_func1"));
Dart_ListSetAt(frame2_locals, 15, Dart_Null());
Dart_ListSetAt(frame2_locals, 16, NewString("sum"));
Dart_ListSetAt(frame2_locals, 17, Dart_NewInteger(0));
// Frame 3 corresponding to "main".
Dart_Handle frame3_locals = Dart_NewList(14);
Dart_ListSetAt(frame3_locals, 0, NewString("i"));
Dart_ListSetAt(frame3_locals, 1, Dart_NewInteger(76));
Dart_ListSetAt(frame3_locals, 2, NewString("j"));
Dart_ListSetAt(frame3_locals, 3, Dart_NewInteger(119));
Dart_ListSetAt(frame3_locals, 4, NewString("local_to_main"));
Dart_ListSetAt(frame3_locals, 5, Dart_Null());
Dart_ListSetAt(frame3_locals, 6, NewString("sum"));
Dart_ListSetAt(frame3_locals, 7, Dart_NewInteger(0));
Dart_ListSetAt(frame3_locals, 8, NewString("value"));
Dart_ListSetAt(frame3_locals, 9, Dart_Null());
Dart_ListSetAt(frame3_locals, 10, NewString("func1"));
Dart_ListSetAt(frame3_locals, 11, Dart_Null());
Dart_ListSetAt(frame3_locals, 12, NewString("main_local"));
Dart_ListSetAt(frame3_locals, 13, Dart_Null());
Dart_Handle expected_locals[] = {frame0_locals, frame1_locals, frame2_locals,
frame3_locals};
breakpoint_hit_counter++;
VerifyStackTrace(trace, expected_trace, expected_locals, kStackTraceLen,
true);
}
TEST_CASE(Debug_StackTraceDump1) {
const char* kScriptChars =
"class Test {\n"
" Test(int local);\n"
"\n"
" int func1(int func(int i, int j)) {\n"
" var i = 0;\n"
" var j = 0;\n"
" var k = 0;\n"
" var l = 0;\n"
" var m = 0;\n"
" int local1_to_func1(int func(int i, int j)) {\n"
" // Capture i and j here.\n"
" i = 11;\n"
" j = 22;\n"
" k = 33;\n"
" l = 44;\n"
" m = 55;\n"
" var n = func(i + j + k, l + m);\n"
" return n;\n"
" }\n"
" var sum = 0;\n"
" return local1_to_func1(func);\n"
" }\n"
"\n"
" int local;\n"
"}\n"
"\n"
"int main() {\n"
" var i = 10;\n"
" var j = 20;\n"
" int local_to_main(int k, int l) {\n"
" // Capture i and j here.\n"
" i = i + k;\n"
" j = j + l;\n"
" return i + j;\n"
" }\n"
" var sum = 0;\n"
" Test value = new Test(10);\n"
" var func1 = value.func1;\n"
" var main_local = local_to_main;\n"
" return func1(main_local);\n"
"}\n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&StackTraceDump1BreakpointHandler);
Dart_Handle script_url = NewString(TestCase::url());
intptr_t line_no = 34; // In closure 'local_to_main'.
Dart_Handle res = Dart_SetBreakpoint(script_url, line_no);
EXPECT_VALID(res);
EXPECT(Dart_IsInteger(res));
breakpoint_hit_counter = 0;
Dart_Handle retval = Invoke("main");
EXPECT_VALID(retval);
int64_t int_value = ToInt64(retval);
EXPECT_EQ(195, int_value);
EXPECT_EQ(1, breakpoint_hit_counter);
}
static void StackTraceDump2ExceptionHandler(Dart_IsolateId isolate_id,
Dart_Handle exception_object,
Dart_StackTrace trace) {
const int kStackTraceLen = 5;
static const char* expected_trace[kStackTraceLen] = {
"Object._noSuchMethod", "Object.noSuchMethod", "Test.local1_to_func1",
"Test.func1", "main"};
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT_EQ(kStackTraceLen, trace_len);
// Frame 0 corresponding to "Object._noSuchMethod".
Dart_Handle frame0_locals = Dart_NewList(12);
Dart_ListSetAt(frame0_locals, 0, NewString("this"));
Dart_ListSetAt(frame0_locals, 1, Dart_Null());
Dart_ListSetAt(frame0_locals, 2, NewString("isMethod"));
Dart_ListSetAt(frame0_locals, 3, Dart_Null());
Dart_ListSetAt(frame0_locals, 4, NewString("memberName"));
Dart_ListSetAt(frame0_locals, 5, Dart_Null());
Dart_ListSetAt(frame0_locals, 6, NewString("type"));
Dart_ListSetAt(frame0_locals, 7, Dart_Null());
Dart_ListSetAt(frame0_locals, 8, NewString("arguments"));
Dart_ListSetAt(frame0_locals, 9, Dart_Null());
Dart_ListSetAt(frame0_locals, 10, NewString("namedArguments"));
Dart_ListSetAt(frame0_locals, 11, Dart_Null());
// Frame 1 corresponding to "Object.noSuchMethod".
Dart_Handle frame1_locals = Dart_NewList(4);
Dart_ListSetAt(frame1_locals, 0, NewString("this"));
Dart_ListSetAt(frame1_locals, 1, Dart_Null());
Dart_ListSetAt(frame1_locals, 2, NewString("invocation"));
Dart_ListSetAt(frame1_locals, 3, Dart_Null());
// Frame 2 corresponding to "Test.local1_to_func1".
Dart_Handle frame2_locals = Dart_NewList(14);
Dart_ListSetAt(frame2_locals, 0, NewString("i"));
Dart_ListSetAt(frame2_locals, 1, Dart_NewInteger(11));
Dart_ListSetAt(frame2_locals, 2, NewString("j"));
Dart_ListSetAt(frame2_locals, 3, Dart_NewInteger(22));
Dart_ListSetAt(frame2_locals, 4, NewString("k"));
Dart_ListSetAt(frame2_locals, 5, Dart_NewInteger(33));
Dart_ListSetAt(frame2_locals, 6, NewString("l"));
Dart_ListSetAt(frame2_locals, 7, Dart_NewInteger(44));
Dart_ListSetAt(frame2_locals, 8, NewString("m"));
Dart_ListSetAt(frame2_locals, 9, Dart_NewInteger(55));
Dart_ListSetAt(frame2_locals, 10, NewString("this"));
Dart_ListSetAt(frame2_locals, 11, Dart_Null());
Dart_ListSetAt(frame2_locals, 12, NewString("func"));
Dart_ListSetAt(frame2_locals, 13, Dart_Null());
// Frame 3 corresponding to "Test.func1".
Dart_Handle frame3_locals = Dart_NewList(18);
Dart_ListSetAt(frame3_locals, 0, NewString("this"));
Dart_ListSetAt(frame3_locals, 1, Dart_Null());
Dart_ListSetAt(frame3_locals, 2, NewString("func"));
Dart_ListSetAt(frame3_locals, 3, Dart_Null());
Dart_ListSetAt(frame3_locals, 4, NewString("i"));
Dart_ListSetAt(frame3_locals, 5, Dart_NewInteger(11));
Dart_ListSetAt(frame3_locals, 6, NewString("j"));
Dart_ListSetAt(frame3_locals, 7, Dart_NewInteger(22));
Dart_ListSetAt(frame3_locals, 8, NewString("k"));
Dart_ListSetAt(frame3_locals, 9, Dart_NewInteger(33));
Dart_ListSetAt(frame3_locals, 10, NewString("l"));
Dart_ListSetAt(frame3_locals, 11, Dart_NewInteger(44));
Dart_ListSetAt(frame3_locals, 12, NewString("m"));
Dart_ListSetAt(frame3_locals, 13, Dart_NewInteger(55));
Dart_ListSetAt(frame3_locals, 14, NewString("local1_to_func1"));
Dart_ListSetAt(frame3_locals, 15, Dart_Null());
Dart_ListSetAt(frame3_locals, 16, NewString("sum"));
Dart_ListSetAt(frame3_locals, 17, Dart_NewInteger(0));
// Frame 4 corresponding to "main".
Dart_Handle frame4_locals = Dart_NewList(12);
Dart_ListSetAt(frame4_locals, 0, NewString("i"));
Dart_ListSetAt(frame4_locals, 1, Dart_NewInteger(10));
Dart_ListSetAt(frame4_locals, 2, NewString("j"));
Dart_ListSetAt(frame4_locals, 3, Dart_NewInteger(20));
Dart_ListSetAt(frame4_locals, 4, NewString("local_to_main"));
Dart_ListSetAt(frame4_locals, 5, Dart_Null());
Dart_ListSetAt(frame4_locals, 6, NewString("sum"));
Dart_ListSetAt(frame4_locals, 7, Dart_NewInteger(0));
Dart_ListSetAt(frame4_locals, 8, NewString("value"));
Dart_ListSetAt(frame4_locals, 9, Dart_Null());
Dart_ListSetAt(frame4_locals, 10, NewString("func1"));
Dart_ListSetAt(frame4_locals, 11, Dart_Null());
Dart_Handle expected_locals[] = {frame0_locals, frame1_locals, frame2_locals,
frame3_locals, frame4_locals};
breakpoint_hit_counter++;
VerifyStackTrace(trace, expected_trace, expected_locals, kStackTraceLen,
true);
}
TEST_CASE(Debug_StackTraceDump2) {
const char* kScriptChars =
"class Test {\n"
" Test(int local);\n"
"\n"
" int func1(int func(int i, int j)) {\n"
" var i = 0;\n"
" var j = 0;\n"
" var k = 0;\n"
" var l = 0;\n"
" var m = 0;\n"
" int local1_to_func1(int func(int i, int j)) {\n"
" // Capture i and j here.\n"
" i = 11;\n"
" j = 22;\n"
" k = 33;\n"
" l = 44;\n"
" m = 55;\n"
" var n = junk(i + j + k, l + m);\n"
" return n;\n"
" }\n"
" var sum = 0;\n"
" return local1_to_func1(func);\n"
" }\n"
"\n"
" int local;\n"
"}\n"
"\n"
"int main() {\n"
" var i = 10;\n"
" var j = 20;\n"
" int local_to_main(int k, int l) {\n"
" // Capture i and j here.\n"
" return i + j;\n"
" }\n"
" var sum = 0;\n"
" Test value = new Test(10);\n"
" var func1 = value.func1;\n"
" return func1(local_to_main);\n"
"}\n";
LoadScript(kScriptChars);
Dart_SetExceptionThrownHandler(&StackTraceDump2ExceptionHandler);
breakpoint_hit_counter = 0;
Dart_SetExceptionPauseInfo(kPauseOnAllExceptions);
Dart_Handle retval = Invoke("main");
EXPECT(Dart_IsError(retval));
EXPECT(Dart_IsUnhandledExceptionError(retval));
EXPECT_EQ(1, breakpoint_hit_counter);
}
void TestEvaluateHandler(Dart_IsolateId isolate_id,
intptr_t bp_id,
const Dart_CodeLocation& location) {
Dart_StackTrace trace;
Dart_GetStackTrace(&trace);
intptr_t trace_len;
Dart_Handle res = Dart_StackTraceLength(trace, &trace_len);
EXPECT_VALID(res);
EXPECT_EQ(1, trace_len);
Dart_ActivationFrame frame;
res = Dart_GetActivationFrame(trace, 0, &frame);
EXPECT_VALID(res);
// Get the local variable p and evaluate an expression in the
// context of p.
Dart_Handle p = GetLocalVariable(frame, "p");
EXPECT_VALID(p);
EXPECT(!Dart_IsNull(p));
Dart_Handle r = Dart_EvaluateExpr(p, NewString("sqrt(x*x + y*this.y)"));
EXPECT_VALID(r);
EXPECT(Dart_IsNumber(r));
EXPECT_EQ(5.0, ToDouble(r));
// Set top-level variable to a new value.
Dart_Handle h = Dart_EvaluateExpr(p, NewString("_factor = 10"));
EXPECT_VALID(h);
EXPECT(Dart_IsInteger(h));
EXPECT_EQ(10, ToInt64(h));
// Check that the side effect of the previous expression is
// persistent.
h = Dart_EvaluateExpr(p, NewString("_factor"));
EXPECT_VALID(h);
EXPECT(Dart_IsInteger(h));
EXPECT_EQ(10, ToInt64(h));
breakpoint_hit = true;
breakpoint_hit_counter++;
}
TEST_CASE(Debug_EvaluateExpr) {
const char* kScriptChars =
"import 'dart:math'; \n"
"main() { \n"
" var p = new Point(3, 4); \n"
" l = [1, 2, 3]; /*BP*/ \n"
" m = {'\"': 'quote' , \n"
" \"\t\": 'tab' }; \n"
" return p; \n"
"} \n"
"var _factor = 2; \n"
"var l; \n"
"var m; \n"
"class Point { \n"
" var x, y; \n"
" Point(this.x, this.y); \n"
"} \n";
LoadScript(kScriptChars);
Dart_SetPausedEventHandler(&TestEvaluateHandler);
Dart_Handle script_url = NewString(TestCase::url());
intptr_t line_no = 4;
Dart_Handle res = Dart_SetBreakpoint(script_url, line_no);
EXPECT_VALID(res);
breakpoint_hit = false;
Dart_Handle point = Invoke("main");
EXPECT_VALID(point);
EXPECT(breakpoint_hit == true);
Dart_Handle r =
Dart_EvaluateExpr(point, NewString("_factor * sqrt(x*x + y*y)"));
EXPECT_VALID(r);
EXPECT(Dart_IsDouble(r));
EXPECT_EQ(50.0, ToDouble(r));
Dart_Handle closure =
Dart_EvaluateExpr(point, NewString("() => _factor * sqrt(x*x + y*y))"));
EXPECT_VALID(closure);
EXPECT(Dart_IsClosure(closure));
r = Dart_InvokeClosure(closure, 0, NULL);
EXPECT_EQ(50.0, ToDouble(r));
r = Dart_EvaluateExpr(point,
NewString("(() => _factor * sqrt(x*x + y*y))()"));
EXPECT_VALID(r);
EXPECT(Dart_IsDouble(r));
EXPECT_EQ(50.0, ToDouble(r));
Dart_Handle len = Dart_EvaluateExpr(point, NewString("l.length"));
EXPECT_VALID(len);
EXPECT(Dart_IsNumber(len));
EXPECT_EQ(3, ToInt64(len));
Dart_Handle point_type =
Dart_GetType(script_lib, NewString("Point"), 0, NULL);
EXPECT_VALID(point_type);
EXPECT(Dart_IsType(point_type));
Dart_Handle elem = Dart_EvaluateExpr(point_type, NewString("m['\"']"));
EXPECT_VALID(elem);
EXPECT(Dart_IsString(elem));
EXPECT_STREQ("quote", ToCString(elem));
elem = Dart_EvaluateExpr(point_type, NewString("m[\"\\t\"]"));
EXPECT_VALID(elem);
EXPECT(Dart_IsString(elem));
EXPECT_STREQ("tab", ToCString(elem));
res = Dart_EvaluateExpr(script_lib, NewString("l..add(11)..add(-5)"));
EXPECT_VALID(res);
// List l now has 5 elements.
len = Dart_EvaluateExpr(script_lib, NewString("l.length + 1"));
EXPECT_VALID(len);
EXPECT(Dart_IsNumber(len));
EXPECT_EQ(6, ToInt64(len));
closure = Dart_EvaluateExpr(script_lib, NewString("(x) => l.length + x"));
EXPECT_VALID(closure);
EXPECT(Dart_IsClosure(closure));
Dart_Handle args[1] = {Dart_NewInteger(1)};
len = Dart_InvokeClosure(closure, 1, args);
EXPECT_VALID(len);
EXPECT(Dart_IsNumber(len));
EXPECT_EQ(6, ToInt64(len));
len = Dart_EvaluateExpr(script_lib, NewString("((x) => l.length + x)(1)"));
EXPECT_VALID(len);
EXPECT(Dart_IsNumber(len));
EXPECT_EQ(6, ToInt64(len));
Dart_Handle error =
Dart_EvaluateExpr(script_lib, NewString("new NonexistingType()"));
EXPECT(Dart_IsError(error));
}
static void EvaluateInActivationOfEvaluateHandler(Dart_IsolateId isolate_id,
Dart_Handle exception_object,
Dart_StackTrace trace) {
breakpoint_hit_counter++;
Dart_ActivationFrame top_frame = 0;
Dart_Handle result = Dart_GetActivationFrame(trace, 0, &top_frame);
EXPECT_VALID(result);
result = Dart_ActivationFrameEvaluate(top_frame, NewString("p.r"));
EXPECT_VALID(result);
EXPECT_EQ(5.0, ToDouble(result));
}
TEST_CASE(Debug_EvaluateInActivationOfEvaluate) {
// This library deliberately declares no top-level variables or methods. This
// exercises a path in eval where a library may have no top-level anonymous
// classes.
const char* kScriptChars =
"import 'dart:math'; \n"
"class Point { \n"
" var x, y; \n"
" Point(this.x, this.y); \n"
" get r => sqrt(x*x + y*y); \n"
"} \n";
LoadScript(kScriptChars);
Dart_FinalizeLoading(false);
Dart_SetExceptionThrownHandler(&EvaluateInActivationOfEvaluateHandler);
Dart_SetExceptionPauseInfo(kPauseOnAllExceptions);
breakpoint_hit_counter = 0;
Dart_Handle result = Dart_EvaluateExpr(
script_lib, NewString("() { var p = new Point(3, 4); throw p; } ())"));
EXPECT(Dart_IsError(result));
EXPECT_EQ(1, breakpoint_hit_counter);
}
static void UnhandledExceptionHandler(Dart_IsolateId isolate_id,
Dart_Handle exception_object,
Dart_StackTrace trace) {
breakpoint_hit_counter++;
}
// Check that the debugger is not called when an exception is
// caught by Dart code.
TEST_CASE(Debug_BreakOnUnhandledException) {
const char* kScriptChars =
"main() { \n"
" try { \n"
" throw 'broccoli'; \n"
" } catch (e) { \n"
" return 'carrots'; \n"
" } \n"
" return 'zucchini'; \n"
"} \n";
LoadScript(kScriptChars);
Dart_FinalizeLoading(false);
Dart_SetExceptionThrownHandler(&UnhandledExceptionHandler);
breakpoint_hit_counter = 0;
// Check that the debugger is not called since the exception
// is handled.
Dart_SetExceptionPauseInfo(kPauseOnUnhandledExceptions);
Dart_Handle res = Invoke("main");
EXPECT_VALID(res);
EXPECT(Dart_IsString(res));
EXPECT_STREQ("carrots", ToCString(res));
EXPECT_EQ(0, breakpoint_hit_counter);
// Check that the debugger is called when "break on all
// exceptions" is turned on.
Dart_SetExceptionPauseInfo(kPauseOnAllExceptions);
Dart_Handle res2 = Invoke("main");
EXPECT_VALID(res2);
EXPECT(Dart_IsString(res2));
EXPECT_STREQ("carrots", ToCString(res2));
EXPECT_EQ(1, breakpoint_hit_counter);
}
TEST_CASE(Debug_GetClosureInfo) {
const char* kScriptChars =
"void foo() { return 43; } \n"
" \n"
"main() { \n"
" return foo; \n"
"} \n";
LoadScript(kScriptChars);
Dart_Handle clo = Invoke("main");
EXPECT_VALID(clo);
EXPECT(Dart_IsClosure(clo));
Dart_Handle name, sig;
Dart_CodeLocation loc;
loc.script_url = Dart_Null();
loc.library_id = -1;
loc.token_pos = -1;
Dart_Handle res = Dart_GetClosureInfo(clo, &name, &sig, &loc);
EXPECT_VALID(res);
EXPECT_TRUE(res);
EXPECT_VALID(name);
EXPECT(Dart_IsString(name));
EXPECT_STREQ("foo", ToCString(name));
EXPECT(Dart_IsString(sig));
EXPECT_STREQ("() => void", ToCString(sig));
EXPECT(Dart_IsString(loc.script_url));
EXPECT_STREQ("test-lib", ToCString(loc.script_url));
EXPECT_EQ(0, loc.token_pos);
EXPECT(loc.library_id > 0);
}
TEST_CASE(Debug_GetSupertype) {
const char* kScriptChars =
"class Test {\n"
"}\n"
"class Test1 extends Test {\n"
"}\n"
"class Test2<T> {\n"
"}\n"
"class Test3 extends Test2<int> {\n"
"}\n"
"class Test4<A, B> extends Test2<A> {\n"
"}\n"
"class Test5<A, B, C> extends Test4<A, B> {\n"
"}\n"
"var s = new Set();\n"
"var l = new List();\n"
"int main() {\n"
"}\n";
Zone* zone = thread->zone();
LoadScript(kScriptChars);
ASSERT(script_lib != NULL);
ASSERT(Dart_IsLibrary(script_lib));
Dart_Handle core_lib = Dart_LookupLibrary(NewString("dart:core"));
Dart_Handle Test_name = Dart_NewStringFromCString("Test");
Dart_Handle Test1_name = Dart_NewStringFromCString("Test1");
Dart_Handle Test2_name = Dart_NewStringFromCString("Test2");
Dart_Handle Test3_name = Dart_NewStringFromCString("Test3");
Dart_Handle Test4_name = Dart_NewStringFromCString("Test4");
Dart_Handle Test5_name = Dart_NewStringFromCString("Test5");
Dart_Handle object_name = Dart_NewStringFromCString("Object");
Dart_Handle int_name = Dart_NewStringFromCString("int");
Dart_Handle set_name = Dart_NewStringFromCString("Set");
Dart_Handle iterable_name = Dart_NewStringFromCString("Iterable");
Dart_Handle list_name = Dart_NewStringFromCString("List");
Dart_Handle object_type = Dart_GetType(core_lib, object_name, 0, NULL);
Dart_Handle int_type = Dart_GetType(core_lib, int_name, 0, NULL);
EXPECT_VALID(int_type);
Dart_Handle Test_type = Dart_GetType(script_lib, Test_name, 0, NULL);
Dart_Handle Test1_type = Dart_GetType(script_lib, Test1_name, 0, NULL);
Dart_Handle type_args = Dart_NewList(1);
Dart_ListSetAt(type_args, 0, int_type);
Dart_Handle Test2_int_type =
Dart_GetType(script_lib, Test2_name, 1, &type_args);
Dart_Handle Test3_type = Dart_GetType(script_lib, Test3_name, 0, NULL);
type_args = Dart_NewList(2);
Dart_ListSetAt(type_args, 0, int_type);
Dart_ListSetAt(type_args, 1, Test_type);
Dart_Handle Test4_int_type =
Dart_GetType(script_lib, Test4_name, 2, &type_args);
type_args = Dart_NewList(3);
Dart_ListSetAt(type_args, 0, int_type);
Dart_ListSetAt(type_args, 1, Test_type);
Dart_ListSetAt(type_args, 2, int_type);
Dart_Handle Test5_int_type =
Dart_GetType(script_lib, Test5_name, 3, &type_args);
{
Dart_Handle super_type = Dart_GetSupertype(object_type);
EXPECT(super_type == Dart_Null());
}
{
Dart_Handle super_type = Dart_GetSupertype(Test1_type);
const Type& expected_type = Api::UnwrapTypeHandle(zone, Test_type);
const Type& actual_type = Api::UnwrapTypeHandle(zone, super_type);
EXPECT(expected_type.raw() == actual_type.raw());
}
{
Dart_Handle super_type = Dart_GetSupertype(Test3_type);
const Type& expected_type = Api::UnwrapTypeHandle(zone, Test2_int_type);
const Type& actual_type = Api::UnwrapTypeHandle(zone, super_type);
EXPECT(expected_type.raw() == actual_type.raw());
}
{
Dart_Handle super_type = Dart_GetSupertype(Test4_int_type);
const Type& expected_type = Api::UnwrapTypeHandle(zone, Test2_int_type);
const Type& actual_type = Api::UnwrapTypeHandle(zone, super_type);
EXPECT(expected_type.raw() == actual_type.raw());
}
{
Dart_Handle super_type = Dart_GetSupertype(Test5_int_type);
const Type& expected_type = Api::UnwrapTypeHandle(zone, Test4_int_type);
const Type& actual_type = Api::UnwrapTypeHandle(zone, super_type);
EXPECT(expected_type.raw() == actual_type.raw());
}
{
Dart_Handle set_type = Dart_GetType(core_lib, set_name, 0, NULL);
Dart_Handle super_type = Dart_GetSupertype(set_type);
Dart_Handle super2_type = Dart_GetSupertype(super_type);
Dart_Handle iterable_type = Dart_GetType(core_lib, iterable_name, 0, NULL);
const Type& expected_type = Api::UnwrapTypeHandle(zone, iterable_type);
const Type& actual_type = Api::UnwrapTypeHandle(zone, super2_type);
EXPECT(expected_type.raw() == actual_type.raw());
}
{
Dart_Handle list_type = Dart_GetType(core_lib, list_name, 0, NULL);
Dart_Handle super_type = Dart_GetSupertype(list_type);
EXPECT(!Dart_IsError(super_type));
super_type = Dart_GetSupertype(super_type);
EXPECT(!Dart_IsError(super_type));
EXPECT(super_type == Dart_Null());
}
}
TEST_CASE(Debug_ListSuperType) {
const char* kScriptChars =
"List testMain() {"
" List a = new List();"
" a.add(10);"
" a.add(20);"
" a.add(30);"
" return a;"
"}"
""
"List immutable() {"
" return const [0, 1, 2];"
"}";
Dart_Handle result;
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Invoke a function which returns an object of type List.
result = Dart_Invoke(lib, NewString("testMain"), 0, NULL);
EXPECT_VALID(result);
// First ensure that the returned object is a list.
Dart_Handle list_access_test_obj = result;
EXPECT(Dart_IsList(list_access_test_obj));
Dart_Handle list_type = Dart_InstanceGetType(list_access_test_obj);
Dart_Handle super_type = Dart_GetSupertype(list_type);
EXPECT(!Dart_IsError(super_type));
}
TEST_CASE(Debug_ScriptGetTokenInfo_Basic) {
const char* kScriptChars =
"var foo;\n"
"\n"
"main() {\n"
"}";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
intptr_t libId = -1;
EXPECT_VALID(Dart_LibraryId(lib, &libId));
Dart_Handle scriptUrl = NewString(TestCase::url());
Dart_Handle tokens = Dart_ScriptGetTokenInfo(libId, scriptUrl);
EXPECT_VALID(tokens);
Dart_Handle tokens_string = Dart_ToString(tokens);
EXPECT_VALID(tokens_string);
const char* tokens_cstr = "";
EXPECT_VALID(Dart_StringToCString(tokens_string, &tokens_cstr));
EXPECT_STREQ(
"[null, 1, 0, 1, 1, 5, 2, 8,"
" null, 3, 5, 1, 6, 5, 7, 6, 8, 8,"
" null, 4, 10, 1]",
tokens_cstr);
}
TEST_CASE(Debug_ScriptGetTokenInfo_MultiLineInterpolation) {
const char* kScriptChars =
"var foo = 'hello world';\n"
"\n"
"void test() {\n"
" return '''\n"
"foo=$foo"
"''';\n"
"}\n"
"\n"
"main() {\n"
"}";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
intptr_t libId = -1;
EXPECT_VALID(Dart_LibraryId(lib, &libId));
Dart_Handle scriptUrl = NewString(TestCase::url());
Dart_Handle tokens = Dart_ScriptGetTokenInfo(libId, scriptUrl);
EXPECT_VALID(tokens);
Dart_Handle tokens_string = Dart_ToString(tokens);
EXPECT_VALID(tokens_string);
const char* tokens_cstr = "";
EXPECT_VALID(Dart_StringToCString(tokens_string, &tokens_cstr));
EXPECT_STREQ(
"[null, 1, 0, 1, 1, 5, 2, 9, 3, 11, 4, 24,"
" null, 3, 7, 1, 8, 6, 9, 10, 10, 11, 11, 13,"
" null, 4, 13, 3, 14, 10,"
" null, 5, 17, 5, 18, 9, 19, 12,"
" null, 6, 21, 1,"
" null, 8, 24, 1, 25, 5, 26, 6, 27, 8,"
" null, 9, 29, 1]",
tokens_cstr);
}
#endif
} // namespace dart