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 | |