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
17namespace dart {
18
19// Unit test for empty stack frame iteration.
20ISOLATE_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.
30ISOLATE_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
40void 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
55void 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
69void 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
82void 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
133static struct NativeEntries {
134 const char* name_;
135 Dart_NativeFunction function_;
136 int argument_count_;
137} BuiltinEntries[] = {STACKFRAME_NATIVE_LIST(REGISTER_FUNCTION)};
138
139static 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.
161TEST_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.
257TEST_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