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 "platform/assert.h"
6#include "vm/globals.h"
7
8#include "vm/code_descriptors.h"
9#include "vm/compiler/assembler/assembler.h"
10#include "vm/compiler/jit/compiler.h"
11#include "vm/dart_entry.h"
12#include "vm/native_entry.h"
13#include "vm/parser.h"
14#include "vm/symbols.h"
15#include "vm/thread.h"
16#include "vm/unit_test.h"
17
18namespace dart {
19
20static void NativeFunc(Dart_NativeArguments args) {
21 Dart_Handle i = Dart_GetNativeArgument(args, 0);
22 Dart_Handle k = Dart_GetNativeArgument(args, 1);
23 int64_t value = -1;
24 EXPECT_VALID(Dart_IntegerToInt64(i, &value));
25 EXPECT_EQ(10, value);
26 EXPECT_VALID(Dart_IntegerToInt64(k, &value));
27 EXPECT_EQ(20, value);
28 {
29 TransitionNativeToVM transition(Thread::Current());
30 Isolate::Current()->heap()->CollectAllGarbage();
31 }
32}
33
34static Dart_NativeFunction native_resolver(Dart_Handle name,
35 int argument_count,
36 bool* auto_setup_scope) {
37 ASSERT(auto_setup_scope);
38 *auto_setup_scope = false;
39 return reinterpret_cast<Dart_NativeFunction>(&NativeFunc);
40}
41
42TEST_CASE(StackMapGC) {
43 const char* kScriptChars =
44 "class A {"
45 " static void func(var i, var k) native 'NativeFunc';"
46 " static foo() {"
47 " var i;"
48 " var s1;"
49 " var k;"
50 " var s2;"
51 " var s3;"
52 " i = 10; s1 = 'abcd'; k = 20; s2 = 'B'; s3 = 'C';"
53 " func(i, k);"
54 " return i + k; }"
55 " static void moo() {"
56 " var i = A.foo();"
57 " if (i != 30) throw '$i != 30';"
58 " }\n"
59 "}\n";
60 // First setup the script and compile the script.
61 TestCase::LoadTestScript(kScriptChars, native_resolver);
62 TransitionNativeToVM transition(thread);
63
64 EXPECT(ClassFinalizer::ProcessPendingClasses());
65 const String& name = String::Handle(String::New(TestCase::url()));
66 const Library& lib = Library::Handle(Library::LookupLibrary(thread, name));
67 EXPECT(!lib.IsNull());
68 Class& cls =
69 Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A"))));
70 EXPECT(!cls.IsNull());
71
72 // Now compile the two functions 'A.foo' and 'A.moo'
73 String& function_moo_name = String::Handle(String::New("moo"));
74 Function& function_moo =
75 Function::Handle(cls.LookupStaticFunction(function_moo_name));
76 EXPECT(CompilerTest::TestCompileFunction(function_moo));
77 EXPECT(function_moo.HasCode());
78
79 String& function_foo_name = String::Handle(String::New("foo"));
80 Function& function_foo =
81 Function::Handle(cls.LookupStaticFunction(function_foo_name));
82 EXPECT(CompilerTest::TestCompileFunction(function_foo));
83 EXPECT(function_foo.HasCode());
84
85 // Build and setup a stackmap for the call to 'func' in 'A.foo' in order
86 // to test the traversal of stack maps when a GC happens.
87 BitmapBuilder* stack_bitmap = new BitmapBuilder();
88 EXPECT(stack_bitmap != nullptr);
89 stack_bitmap->Set(0, false); // var i.
90 stack_bitmap->Set(1, true); // var s1.
91 stack_bitmap->Set(2, false); // var k.
92 stack_bitmap->Set(3, true); // var s2.
93 stack_bitmap->Set(4, true); // var s3.
94 const Code& code = Code::Handle(function_foo.unoptimized_code());
95 // Search for the pc of the call to 'func'.
96 const PcDescriptors& descriptors =
97 PcDescriptors::Handle(code.pc_descriptors());
98 int call_count = 0;
99 PcDescriptors::Iterator iter(descriptors,
100 PcDescriptorsLayout::kUnoptStaticCall);
101 CompressedStackMapsBuilder compressed_maps_builder;
102 while (iter.MoveNext()) {
103 compressed_maps_builder.AddEntry(iter.PcOffset(), stack_bitmap, 0);
104 ++call_count;
105 }
106 // We can't easily check that we put the stackmap at the correct pc, but
107 // we did if there was exactly one call seen.
108 EXPECT(call_count == 1);
109 const auto& compressed_maps =
110 CompressedStackMaps::Handle(compressed_maps_builder.Finalize());
111 code.set_compressed_stackmaps(compressed_maps);
112
113 // Now invoke 'A.moo' and it will trigger a GC when the native function
114 // is called, this should then cause the stack map of function 'A.foo'
115 // to be traversed and the appropriate objects visited.
116 const Object& result = Object::Handle(
117 DartEntry::InvokeFunction(function_foo, Object::empty_array()));
118 EXPECT(!result.IsError());
119}
120
121ISOLATE_UNIT_TEST_CASE(DescriptorList_TokenPositions) {
122 DescriptorList* descriptors = new DescriptorList(64);
123 ASSERT(descriptors != NULL);
124 const intptr_t token_positions[] = {
125 kMinInt32,
126 5,
127 13,
128 13,
129 13,
130 13,
131 31,
132 23,
133 23,
134 23,
135 33,
136 33,
137 5,
138 5,
139 TokenPosition::kMinSourcePos,
140 TokenPosition::kMaxSourcePos,
141 };
142 const intptr_t num_token_positions =
143 sizeof(token_positions) / sizeof(token_positions[0]);
144
145 for (intptr_t i = 0; i < num_token_positions; i++) {
146 descriptors->AddDescriptor(PcDescriptorsLayout::kRuntimeCall, 0, 0,
147 TokenPosition(token_positions[i]), 0, 1);
148 }
149
150 const PcDescriptors& finalized_descriptors =
151 PcDescriptors::Handle(descriptors->FinalizePcDescriptors(0));
152
153 ASSERT(!finalized_descriptors.IsNull());
154 PcDescriptors::Iterator it(finalized_descriptors,
155 PcDescriptorsLayout::kRuntimeCall);
156
157 intptr_t i = 0;
158 while (it.MoveNext()) {
159 if (token_positions[i] != it.TokenPos().value()) {
160 OS::PrintErr("[%" Pd "]: Expected: %" Pd " != %" Pd "\n", i,
161 token_positions[i], it.TokenPos().value());
162 }
163 EXPECT(token_positions[i] == it.TokenPos().value());
164 i++;
165 }
166}
167
168} // namespace dart
169