| 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/stack_frame.h" |
| 6 | #include "include/dart_api.h" |
| 7 | #include "platform/assert.h" |
| 8 | #include "vm/class_finalizer.h" |
| 9 | #include "vm/compiler/jit/compiler.h" |
| 10 | #include "vm/dart_api_impl.h" |
| 11 | #include "vm/dart_entry.h" |
| 12 | #include "vm/heap/verifier.h" |
| 13 | #include "vm/resolver.h" |
| 14 | #include "vm/unit_test.h" |
| 15 | #include "vm/zone.h" |
| 16 | |
| 17 | namespace dart { |
| 18 | |
| 19 | // Unit test for empty stack frame iteration. |
| 20 | ISOLATE_UNIT_TEST_CASE(EmptyStackFrameIteration) { |
| 21 | StackFrameIterator iterator(ValidationPolicy::kValidateFrames, |
| 22 | Thread::Current(), |
| 23 | StackFrameIterator::kNoCrossThreadIteration); |
| 24 | EXPECT(!iterator.HasNextFrame()); |
| 25 | EXPECT(iterator.NextFrame() == NULL); |
| 26 | VerifyPointersVisitor::VerifyPointers(); |
| 27 | } |
| 28 | |
| 29 | // Unit test for empty dart stack frame iteration. |
| 30 | ISOLATE_UNIT_TEST_CASE(EmptyDartStackFrameIteration) { |
| 31 | DartFrameIterator iterator(Thread::Current(), |
| 32 | StackFrameIterator::kNoCrossThreadIteration); |
| 33 | EXPECT(iterator.NextFrame() == NULL); |
| 34 | VerifyPointersVisitor::VerifyPointers(); |
| 35 | } |
| 36 | |
| 37 | #define FUNCTION_NAME(name) StackFrame_##name |
| 38 | #define REGISTER_FUNCTION(name, count) {"" #name, FUNCTION_NAME(name), count}, |
| 39 | |
| 40 | void FUNCTION_NAME(StackFrame_equals)(Dart_NativeArguments args) { |
| 41 | NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| 42 | TransitionNativeToVM transition(arguments->thread()); |
| 43 | Zone* zone = arguments->thread()->zone(); |
| 44 | const Instance& expected = |
| 45 | Instance::CheckedHandle(zone, arguments->NativeArgAt(0)); |
| 46 | const Instance& actual = |
| 47 | Instance::CheckedHandle(zone, arguments->NativeArgAt(1)); |
| 48 | if (!expected.OperatorEquals(actual)) { |
| 49 | OS::PrintErr("expected: '%s' actual: '%s'\n" , expected.ToCString(), |
| 50 | actual.ToCString()); |
| 51 | EXPECT(false); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | void FUNCTION_NAME(StackFrame_frameCount)(Dart_NativeArguments args) { |
| 56 | NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| 57 | TransitionNativeToVM transition(arguments->thread()); |
| 58 | int count = 0; |
| 59 | StackFrameIterator frames(ValidationPolicy::kValidateFrames, |
| 60 | arguments->thread(), |
| 61 | StackFrameIterator::kNoCrossThreadIteration); |
| 62 | while (frames.NextFrame() != NULL) { |
| 63 | count += 1; // Count the frame. |
| 64 | } |
| 65 | VerifyPointersVisitor::VerifyPointers(); |
| 66 | arguments->SetReturn(Object::Handle(Smi::New(count))); |
| 67 | } |
| 68 | |
| 69 | void FUNCTION_NAME(StackFrame_dartFrameCount)(Dart_NativeArguments args) { |
| 70 | TransitionNativeToVM transition(Thread::Current()); |
| 71 | int count = 0; |
| 72 | DartFrameIterator frames(Thread::Current(), |
| 73 | StackFrameIterator::kNoCrossThreadIteration); |
| 74 | while (frames.NextFrame() != NULL) { |
| 75 | count += 1; // Count the dart frame. |
| 76 | } |
| 77 | VerifyPointersVisitor::VerifyPointers(); |
| 78 | NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| 79 | arguments->SetReturn(Object::Handle(Smi::New(count))); |
| 80 | } |
| 81 | |
| 82 | void FUNCTION_NAME(StackFrame_validateFrame)(Dart_NativeArguments args) { |
| 83 | Thread* thread = Thread::Current(); |
| 84 | Zone* zone = thread->zone(); |
| 85 | |
| 86 | Dart_Handle index = Dart_GetNativeArgument(args, 0); |
| 87 | Dart_Handle name = Dart_GetNativeArgument(args, 1); |
| 88 | |
| 89 | TransitionNativeToVM transition(thread); |
| 90 | const Smi& frame_index_smi = |
| 91 | Smi::CheckedHandle(zone, Api::UnwrapHandle(index)); |
| 92 | const char* expected_name = |
| 93 | String::CheckedHandle(zone, Api::UnwrapHandle(name)).ToCString(); |
| 94 | int frame_index = frame_index_smi.Value(); |
| 95 | int count = 0; |
| 96 | DartFrameIterator frames(thread, StackFrameIterator::kNoCrossThreadIteration); |
| 97 | StackFrame* frame = frames.NextFrame(); |
| 98 | while (frame != NULL) { |
| 99 | if (count == frame_index) { |
| 100 | // Find the function corresponding to this frame and check if it |
| 101 | // matches the function name passed in. |
| 102 | const Function& function = |
| 103 | Function::Handle(zone, frame->LookupDartFunction()); |
| 104 | if (function.IsNull()) { |
| 105 | FATAL("StackFrame_validateFrame fails, invalid dart frame.\n" ); |
| 106 | } |
| 107 | const char* name = function.ToFullyQualifiedCString(); |
| 108 | // Currently all unit tests are loaded as being part of dart:core-lib. |
| 109 | String& url = String::Handle(zone, String::New(TestCase::url())); |
| 110 | const Library& lib = |
| 111 | Library::Handle(zone, Library::LookupLibrary(thread, url)); |
| 112 | ASSERT(!lib.IsNull()); |
| 113 | const char* lib_name = String::Handle(zone, lib.url()).ToCString(); |
| 114 | char* full_name = OS::SCreate(zone, "%s_%s" , lib_name, expected_name); |
| 115 | EXPECT_STREQ(full_name, name); |
| 116 | return; |
| 117 | } |
| 118 | count += 1; // Count the dart frames. |
| 119 | frame = frames.NextFrame(); |
| 120 | } |
| 121 | FATAL("StackFrame_validateFrame fails, frame count < index passed in.\n" ); |
| 122 | } |
| 123 | |
| 124 | // List all native functions implemented in the vm or core boot strap dart |
| 125 | // libraries so that we can resolve the native function to it's entry |
| 126 | // point. |
| 127 | #define STACKFRAME_NATIVE_LIST(V) \ |
| 128 | V(StackFrame_equals, 2) \ |
| 129 | V(StackFrame_frameCount, 0) \ |
| 130 | V(StackFrame_dartFrameCount, 0) \ |
| 131 | V(StackFrame_validateFrame, 2) |
| 132 | |
| 133 | static struct NativeEntries { |
| 134 | const char* name_; |
| 135 | Dart_NativeFunction function_; |
| 136 | int argument_count_; |
| 137 | } BuiltinEntries[] = {STACKFRAME_NATIVE_LIST(REGISTER_FUNCTION)}; |
| 138 | |
| 139 | static Dart_NativeFunction native_lookup(Dart_Handle name, |
| 140 | int argument_count, |
| 141 | bool* auto_setup_scope) { |
| 142 | ASSERT(auto_setup_scope != NULL); |
| 143 | *auto_setup_scope = false; |
| 144 | TransitionNativeToVM transition(Thread::Current()); |
| 145 | const Object& obj = Object::Handle(Api::UnwrapHandle(name)); |
| 146 | ASSERT(obj.IsString()); |
| 147 | const char* function_name = obj.ToCString(); |
| 148 | ASSERT(function_name != NULL); |
| 149 | int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries); |
| 150 | for (int i = 0; i < num_entries; i++) { |
| 151 | struct NativeEntries* entry = &(BuiltinEntries[i]); |
| 152 | if ((strcmp(function_name, entry->name_) == 0) && |
| 153 | (entry->argument_count_ == argument_count)) { |
| 154 | return reinterpret_cast<Dart_NativeFunction>(entry->function_); |
| 155 | } |
| 156 | } |
| 157 | return NULL; |
| 158 | } |
| 159 | |
| 160 | // Unit test case to verify stack frame iteration. |
| 161 | TEST_CASE(ValidateStackFrameIteration) { |
| 162 | const char* nullable_tag = TestCase::NullableTag(); |
| 163 | // clang-format off |
| 164 | auto kScriptChars = Utils::CStringUniquePtr( |
| 165 | OS::SCreate( |
| 166 | nullptr, |
| 167 | "class StackFrame {" |
| 168 | " static equals(var obj1, var obj2) native \"StackFrame_equals\";" |
| 169 | " static int frameCount() native \"StackFrame_frameCount\";" |
| 170 | " static int dartFrameCount() native \"StackFrame_dartFrameCount\";" |
| 171 | " static validateFrame(int index," |
| 172 | " String name) native " |
| 173 | "\"StackFrame_validateFrame\";" |
| 174 | "} " |
| 175 | "class First {" |
| 176 | " First() { }" |
| 177 | " int%s method1(int%s param) {" |
| 178 | " if (param == 1) {" |
| 179 | " param = method2(200);" |
| 180 | " } else {" |
| 181 | " param = method2(100);" |
| 182 | " }" |
| 183 | " }" |
| 184 | " int%s method2(int param) {" |
| 185 | " if (param == 200) {" |
| 186 | " First.staticmethod(this, param);" |
| 187 | " } else {" |
| 188 | " First.staticmethod(this, 10);" |
| 189 | " }" |
| 190 | " }" |
| 191 | " static int%s staticmethod(First obj, int param) {" |
| 192 | " if (param == 10) {" |
| 193 | " obj.method3(10);" |
| 194 | " } else {" |
| 195 | " obj.method3(200);" |
| 196 | " }" |
| 197 | " }" |
| 198 | " method3(int param) {" |
| 199 | " StackFrame.equals(9, StackFrame.frameCount());" |
| 200 | " StackFrame.equals(7, StackFrame.dartFrameCount());" |
| 201 | " StackFrame.validateFrame(0, \"StackFrame_validateFrame\");" |
| 202 | " StackFrame.validateFrame(1, \"First_method3\");" |
| 203 | " StackFrame.validateFrame(2, \"First_staticmethod\");" |
| 204 | " StackFrame.validateFrame(3, \"First_method2\");" |
| 205 | " StackFrame.validateFrame(4, \"First_method1\");" |
| 206 | " StackFrame.validateFrame(5, \"Second_method1\");" |
| 207 | " StackFrame.validateFrame(6, \"StackFrameTest_testMain\");" |
| 208 | " }" |
| 209 | "}" |
| 210 | "class Second {" |
| 211 | " Second() { }" |
| 212 | " int%s method1(int%s param) {" |
| 213 | " if (param == 1) {" |
| 214 | " param = method2(200);" |
| 215 | " } else {" |
| 216 | " First obj = new First();" |
| 217 | " param = obj.method1(1);" |
| 218 | " param = obj.method1(2);" |
| 219 | " }" |
| 220 | " }" |
| 221 | " int%s method2(int param) {" |
| 222 | " Second.staticmethod(this, param);" |
| 223 | " }" |
| 224 | " static int%s staticmethod(Second obj, int param) {" |
| 225 | " obj.method3(10);" |
| 226 | " }" |
| 227 | " method3(int param) {" |
| 228 | " StackFrame.equals(8, StackFrame.frameCount());" |
| 229 | " StackFrame.equals(6, StackFrame.dartFrameCount());" |
| 230 | " StackFrame.validateFrame(0, \"StackFrame_validateFrame\");" |
| 231 | " StackFrame.validateFrame(1, \"Second_method3\");" |
| 232 | " StackFrame.validateFrame(2, \"Second_staticmethod\");" |
| 233 | " StackFrame.validateFrame(3, \"Second_method2\");" |
| 234 | " StackFrame.validateFrame(4, \"Second_method1\");" |
| 235 | " StackFrame.validateFrame(5, \"StackFrameTest_testMain\");" |
| 236 | " }" |
| 237 | "}" |
| 238 | "class StackFrameTest {" |
| 239 | " static testMain() {" |
| 240 | " Second obj = new Second();" |
| 241 | " obj.method1(1);" |
| 242 | " obj.method1(2);" |
| 243 | " }" |
| 244 | "}" , |
| 245 | nullable_tag, nullable_tag, nullable_tag, nullable_tag, nullable_tag, |
| 246 | nullable_tag, nullable_tag, nullable_tag), |
| 247 | std::free); |
| 248 | // clang-format on |
| 249 | Dart_Handle lib = TestCase::LoadTestScript( |
| 250 | kScriptChars.get(), |
| 251 | reinterpret_cast<Dart_NativeEntryResolver>(native_lookup)); |
| 252 | Dart_Handle cls = Dart_GetClass(lib, NewString("StackFrameTest" )); |
| 253 | EXPECT_VALID(Dart_Invoke(cls, NewString("testMain" ), 0, NULL)); |
| 254 | } |
| 255 | |
| 256 | // Unit test case to verify stack frame iteration. |
| 257 | TEST_CASE(ValidateNoSuchMethodStackFrameIteration) { |
| 258 | const char* kScriptChars; |
| 259 | // The true stack depends on which strategy we are using for noSuchMethod. The |
| 260 | // stacktrace as seen by Dart is the same either way because dispatcher |
| 261 | // methods are marked invisible. |
| 262 | if (FLAG_enable_interpreter) { |
| 263 | kScriptChars = |
| 264 | "class StackFrame {" |
| 265 | " static equals(var obj1, var obj2) native \"StackFrame_equals\";" |
| 266 | " static int frameCount() native \"StackFrame_frameCount\";" |
| 267 | " static int dartFrameCount() native \"StackFrame_dartFrameCount\";" |
| 268 | " static validateFrame(int index," |
| 269 | " String name) native " |
| 270 | "\"StackFrame_validateFrame\";" |
| 271 | "} " |
| 272 | "class StackFrame2Test {" |
| 273 | " StackFrame2Test() {}" |
| 274 | " noSuchMethod(Invocation im) {" |
| 275 | " /* We should have 8 general frames and 4 dart frames as follows:" |
| 276 | " * exit frame" |
| 277 | " * dart frame corresponding to StackFrame.frameCount" |
| 278 | " * dart frame corresponding to StackFrame2Test.noSuchMethod" |
| 279 | " * entry frame" |
| 280 | " * exit frame" |
| 281 | " * dart frame for noSuchMethod dispatcher" |
| 282 | " * dart frame corresponding to StackFrame2Test.testMain" |
| 283 | " * entry frame" |
| 284 | " */" |
| 285 | " StackFrame.equals(8, StackFrame.frameCount());" |
| 286 | " StackFrame.equals(4, StackFrame.dartFrameCount());" |
| 287 | " StackFrame.validateFrame(0, \"StackFrame_validateFrame\");" |
| 288 | " StackFrame.validateFrame(1, \"StackFrame2Test_noSuchMethod\");" |
| 289 | " StackFrame.validateFrame(2, \"StackFrame2Test_foo\");" |
| 290 | " StackFrame.validateFrame(3, \"StackFrame2Test_testMain\");" |
| 291 | " return 5;" |
| 292 | " }" |
| 293 | " static testMain() {" |
| 294 | " /* Declare |obj| dynamic so that noSuchMethod can be" |
| 295 | " * called in strong mode. */" |
| 296 | " dynamic obj = new StackFrame2Test();" |
| 297 | " StackFrame.equals(5, obj.foo(101, 202));" |
| 298 | " }" |
| 299 | "}" ; |
| 300 | } else if (FLAG_lazy_dispatchers) { |
| 301 | kScriptChars = |
| 302 | "class StackFrame {" |
| 303 | " static equals(var obj1, var obj2) native \"StackFrame_equals\";" |
| 304 | " static int frameCount() native \"StackFrame_frameCount\";" |
| 305 | " static int dartFrameCount() native \"StackFrame_dartFrameCount\";" |
| 306 | " static validateFrame(int index," |
| 307 | " String name) native " |
| 308 | "\"StackFrame_validateFrame\";" |
| 309 | "} " |
| 310 | "class StackFrame2Test {" |
| 311 | " StackFrame2Test() {}" |
| 312 | " noSuchMethod(Invocation im) {" |
| 313 | " /* We should have 6 general frames and 4 dart frames as follows:" |
| 314 | " * exit frame" |
| 315 | " * dart frame corresponding to StackFrame.frameCount" |
| 316 | " * dart frame corresponding to StackFrame2Test.noSuchMethod" |
| 317 | " * frame for instance function invocation stub calling " |
| 318 | "noSuchMethod" |
| 319 | " * dart frame corresponding to StackFrame2Test.testMain" |
| 320 | " * entry frame" |
| 321 | " */" |
| 322 | " StackFrame.equals(6, StackFrame.frameCount());" |
| 323 | " StackFrame.equals(4, StackFrame.dartFrameCount());" |
| 324 | " StackFrame.validateFrame(0, \"StackFrame_validateFrame\");" |
| 325 | " StackFrame.validateFrame(1, \"StackFrame2Test_noSuchMethod\");" |
| 326 | " StackFrame.validateFrame(2, \"StackFrame2Test_foo\");" |
| 327 | " StackFrame.validateFrame(3, \"StackFrame2Test_testMain\");" |
| 328 | " return 5;" |
| 329 | " }" |
| 330 | " static testMain() {" |
| 331 | " /* Declare |obj| dynamic so that noSuchMethod can be" |
| 332 | " * called in strong mode. */" |
| 333 | " dynamic obj = new StackFrame2Test();" |
| 334 | " StackFrame.equals(5, obj.foo(101, 202));" |
| 335 | " }" |
| 336 | "}" ; |
| 337 | } else { |
| 338 | kScriptChars = |
| 339 | "class StackFrame {" |
| 340 | " static equals(var obj1, var obj2) native \"StackFrame_equals\";" |
| 341 | " static int frameCount() native \"StackFrame_frameCount\";" |
| 342 | " static int dartFrameCount() native \"StackFrame_dartFrameCount\";" |
| 343 | " static validateFrame(int index," |
| 344 | " String name) native " |
| 345 | "\"StackFrame_validateFrame\";" |
| 346 | "} " |
| 347 | "class StackFrame2Test {" |
| 348 | " StackFrame2Test() {}" |
| 349 | " noSuchMethod(Invocation im) {" |
| 350 | " /* We should have 8 general frames and 3 dart frames as follows:" |
| 351 | " * exit frame" |
| 352 | " * dart frame corresponding to StackFrame.frameCount" |
| 353 | " * dart frame corresponding to StackFrame2Test.noSuchMethod" |
| 354 | " * entry frame" |
| 355 | " * exit frame (call to runtime NoSuchMethodFromCallStub)" |
| 356 | " * IC stub" |
| 357 | " * dart frame corresponding to StackFrame2Test.testMain" |
| 358 | " * entry frame" |
| 359 | " */" |
| 360 | " StackFrame.equals(8, StackFrame.frameCount());" |
| 361 | " StackFrame.equals(3, StackFrame.dartFrameCount());" |
| 362 | " StackFrame.validateFrame(0, \"StackFrame_validateFrame\");" |
| 363 | " StackFrame.validateFrame(1, \"StackFrame2Test_noSuchMethod\");" |
| 364 | " StackFrame.validateFrame(2, \"StackFrame2Test_testMain\");" |
| 365 | " return 5;" |
| 366 | " }" |
| 367 | " static testMain() {" |
| 368 | " /* Declare |obj| dynamic so that noSuchMethod can be" |
| 369 | " * called in strong mode. */" |
| 370 | " dynamic obj = new StackFrame2Test();" |
| 371 | " StackFrame.equals(5, obj.foo(101, 202));" |
| 372 | " }" |
| 373 | "}" ; |
| 374 | } |
| 375 | Dart_Handle lib = TestCase::LoadTestScript( |
| 376 | kScriptChars, reinterpret_cast<Dart_NativeEntryResolver>(native_lookup)); |
| 377 | Dart_Handle cls = Dart_GetClass(lib, NewString("StackFrame2Test" )); |
| 378 | EXPECT_VALID(Dart_Invoke(cls, NewString("testMain" ), 0, NULL)); |
| 379 | } |
| 380 | |
| 381 | } // namespace dart |
| 382 | |