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/compiler/jit/compiler.h"
6#include "platform/assert.h"
7#include "vm/class_finalizer.h"
8#include "vm/code_patcher.h"
9#include "vm/dart_api_impl.h"
10#include "vm/heap/safepoint.h"
11#include "vm/kernel_isolate.h"
12#include "vm/object.h"
13#include "vm/symbols.h"
14#include "vm/thread_pool.h"
15#include "vm/unit_test.h"
16
17namespace dart {
18
19ISOLATE_UNIT_TEST_CASE(CompileFunction) {
20 const char* kScriptChars =
21 "class A {\n"
22 " static foo() { return 42; }\n"
23 " static moo() {\n"
24 " // A.foo();\n"
25 " }\n"
26 "}\n";
27 Dart_Handle library;
28 {
29 TransitionVMToNative transition(thread);
30 library = TestCase::LoadTestScript(script: kScriptChars, resolver: nullptr);
31 }
32 const Library& lib =
33 Library::Handle(ptr: Library::RawCast(raw: Api::UnwrapHandle(object: library)));
34 EXPECT(ClassFinalizer::ProcessPendingClasses());
35 Class& cls =
36 Class::Handle(ptr: lib.LookupClass(name: String::Handle(ptr: Symbols::New(thread, cstr: "A"))));
37 EXPECT(!cls.IsNull());
38 const auto& error = cls.EnsureIsFinalized(thread);
39 EXPECT(error == Error::null());
40 String& function_foo_name = String::Handle(ptr: String::New(cstr: "foo"));
41 Function& function_foo =
42 Function::Handle(ptr: cls.LookupStaticFunction(name: function_foo_name));
43 EXPECT(!function_foo.IsNull());
44 String& function_source = String::Handle(ptr: function_foo.GetSource());
45 EXPECT_STREQ("static foo() { return 42; }", function_source.ToCString());
46 EXPECT(CompilerTest::TestCompileFunction(function_foo));
47 EXPECT(function_foo.HasCode());
48
49 String& function_moo_name = String::Handle(ptr: String::New(cstr: "moo"));
50 Function& function_moo =
51 Function::Handle(ptr: cls.LookupStaticFunction(name: function_moo_name));
52 EXPECT(!function_moo.IsNull());
53
54 EXPECT(CompilerTest::TestCompileFunction(function_moo));
55 EXPECT(function_moo.HasCode());
56 function_source = function_moo.GetSource();
57 EXPECT_STREQ("static moo() {\n // A.foo();\n }",
58 function_source.ToCString());
59}
60
61ISOLATE_UNIT_TEST_CASE(OptimizeCompileFunctionOnHelperThread) {
62 // Create a simple function and compile it without optimization.
63 const char* kScriptChars =
64 "class A {\n"
65 " static foo() { return 42; }\n"
66 "}\n";
67 Dart_Handle library;
68 {
69 TransitionVMToNative transition(thread);
70 library = TestCase::LoadTestScript(script: kScriptChars, resolver: nullptr);
71 }
72 const Library& lib =
73 Library::Handle(ptr: Library::RawCast(raw: Api::UnwrapHandle(object: library)));
74 EXPECT(ClassFinalizer::ProcessPendingClasses());
75 Class& cls =
76 Class::Handle(ptr: lib.LookupClass(name: String::Handle(ptr: Symbols::New(thread, cstr: "A"))));
77 EXPECT(!cls.IsNull());
78 String& function_foo_name = String::Handle(ptr: String::New(cstr: "foo"));
79 const auto& error = cls.EnsureIsFinalized(thread);
80 EXPECT(error == Error::null());
81 Function& func =
82 Function::Handle(ptr: cls.LookupStaticFunction(name: function_foo_name));
83 EXPECT(!func.HasCode());
84 CompilerTest::TestCompileFunction(function: func);
85 EXPECT(func.HasCode());
86 EXPECT(!func.HasOptimizedCode());
87#if !defined(PRODUCT)
88 // Constant in product mode.
89 FLAG_background_compilation = true;
90#endif
91 auto isolate_group = thread->isolate_group();
92 isolate_group->background_compiler()->EnqueueCompilation(function: func);
93 Monitor* m = new Monitor();
94 {
95 SafepointMonitorLocker ml(m);
96 while (!func.HasOptimizedCode()) {
97 ml.Wait(millis: 1);
98 }
99 }
100 delete m;
101}
102
103ISOLATE_UNIT_TEST_CASE(CompileFunctionOnHelperThread) {
104 // Create a simple function and compile it without optimization.
105 const char* kScriptChars =
106 "class A {\n"
107 " static foo() { return 42; }\n"
108 "}\n";
109 Dart_Handle library;
110 {
111 TransitionVMToNative transition(thread);
112 library = TestCase::LoadTestScript(script: kScriptChars, resolver: nullptr);
113 }
114 const Library& lib =
115 Library::Handle(ptr: Library::RawCast(raw: Api::UnwrapHandle(object: library)));
116 EXPECT(ClassFinalizer::ProcessPendingClasses());
117 Class& cls =
118 Class::Handle(ptr: lib.LookupClass(name: String::Handle(ptr: Symbols::New(thread, cstr: "A"))));
119 EXPECT(!cls.IsNull());
120 const auto& error = cls.EnsureIsFinalized(thread);
121 EXPECT(error == Error::null());
122 String& function_foo_name = String::Handle(ptr: String::New(cstr: "foo"));
123 Function& func =
124 Function::Handle(ptr: cls.LookupStaticFunction(name: function_foo_name));
125 EXPECT(!func.HasCode());
126 CompilerTest::TestCompileFunction(function: func);
127 EXPECT(func.HasCode());
128}
129
130ISOLATE_UNIT_TEST_CASE(RegenerateAllocStubs) {
131 const char* kScriptChars =
132 "class A {\n"
133 "}\n"
134 "unOpt() => new A(); \n"
135 "optIt() => new A(); \n"
136 "A main() {\n"
137 " return unOpt();\n"
138 "}\n";
139
140 Class& cls = Class::Handle();
141 TransitionVMToNative transition(thread);
142
143 Dart_Handle lib = TestCase::LoadTestScript(script: kScriptChars, resolver: nullptr);
144 Dart_Handle result = Dart_Invoke(target: lib, name: NewString(str: "main"), number_of_arguments: 0, arguments: nullptr);
145 EXPECT_VALID(result);
146
147 {
148 TransitionNativeToVM transition(thread);
149 Library& lib_handle =
150 Library::Handle(ptr: Library::RawCast(raw: Api::UnwrapHandle(object: lib)));
151 cls = lib_handle.LookupClass(name: String::Handle(ptr: Symbols::New(thread, cstr: "A")));
152 EXPECT(!cls.IsNull());
153 }
154
155 {
156 TransitionNativeToVM transition(thread);
157 cls.DisableAllocationStub();
158 }
159 result = Dart_Invoke(target: lib, name: NewString(str: "main"), number_of_arguments: 0, arguments: nullptr);
160 EXPECT_VALID(result);
161
162 {
163 TransitionNativeToVM transition(thread);
164 cls.DisableAllocationStub();
165 }
166 result = Dart_Invoke(target: lib, name: NewString(str: "main"), number_of_arguments: 0, arguments: nullptr);
167 EXPECT_VALID(result);
168
169 {
170 TransitionNativeToVM transition(thread);
171 cls.DisableAllocationStub();
172 }
173 result = Dart_Invoke(target: lib, name: NewString(str: "main"), number_of_arguments: 0, arguments: nullptr);
174 EXPECT_VALID(result);
175}
176
177TEST_CASE(EvalExpression) {
178 const char* kScriptChars =
179 "int ten = 2 * 5; \n"
180 "get dot => '.'; \n"
181 "class A { \n"
182 " var apa = 'Herr Nilsson'; \n"
183 " calc(x) => '${x*ten}'; \n"
184 "} \n"
185 "makeObj() => new A(); \n";
186
187 Dart_Handle lib = TestCase::LoadTestScript(script: kScriptChars, resolver: nullptr);
188 Dart_Handle obj_handle =
189 Dart_Invoke(target: lib, name: Dart_NewStringFromCString(str: "makeObj"), number_of_arguments: 0, arguments: nullptr);
190 EXPECT_VALID(obj_handle);
191 TransitionNativeToVM transition(thread);
192 const Object& obj = Object::Handle(ptr: Api::UnwrapHandle(object: obj_handle));
193 EXPECT(!obj.IsNull());
194 EXPECT(obj.IsInstance());
195
196 String& expr_text = String::Handle();
197 expr_text = String::New(cstr: "apa + ' ${calc(10)}' + dot");
198 Object& val = Object::Handle();
199 const Class& receiver_cls = Class::Handle(ptr: obj.clazz());
200
201 if (!KernelIsolate::IsRunning()) {
202 UNREACHABLE();
203 } else {
204 LibraryPtr raw_library = Library::RawCast(raw: Api::UnwrapHandle(object: lib));
205 Library& lib_handle = Library::ZoneHandle(ptr: raw_library);
206
207 Dart_KernelCompilationResult compilation_result =
208 KernelIsolate::CompileExpressionToKernel(
209 /*platform_kernel=*/nullptr, /*platform_kernel_size=*/0,
210 expression: expr_text.ToCString(), definitions: Array::empty_array(), definition_types: Array::empty_array(),
211 type_definitions: Array::empty_array(), type_bounds: Array::empty_array(), type_defaults: Array::empty_array(),
212 library_url: String::Handle(ptr: lib_handle.url()).ToCString(), klass: "A",
213 /* method= */ nullptr,
214 /* is_static= */ false);
215 EXPECT_EQ(Dart_KernelCompilationStatus_Ok, compilation_result.status);
216
217 const ExternalTypedData& kernel_buffer =
218 ExternalTypedData::Handle(ptr: ExternalTypedData::NewFinalizeWithFree(
219 data: const_cast<uint8_t*>(compilation_result.kernel),
220 len: compilation_result.kernel_size));
221
222 val = Instance::Cast(obj).EvaluateCompiledExpression(
223 klass: receiver_cls, kernel_buffer, type_definitions: Array::empty_array(), arguments: Array::empty_array(),
224 type_arguments: TypeArguments::null_type_arguments());
225 }
226 EXPECT(!val.IsNull());
227 EXPECT(!val.IsError());
228 EXPECT(val.IsString());
229 EXPECT_STREQ("Herr Nilsson 100.", val.ToCString());
230}
231
232ISOLATE_UNIT_TEST_CASE(EvalExpressionWithLazyCompile) {
233 { // Initialize an incremental compiler in DFE mode.
234 TransitionVMToNative transition(thread);
235 TestCase::LoadTestScript(script: "", resolver: nullptr);
236 }
237 Library& lib = Library::Handle(ptr: Library::CoreLibrary());
238 const String& expression = String::Handle(
239 ptr: String::New(cstr: "(){ return (){ return (){ return 3 + 4; }(); }(); }()"));
240 Object& val = Object::Handle();
241 val = Api::UnwrapHandle(
242 object: TestCase::EvaluateExpression(lib, expr: expression,
243 /* param_names= */ Array::empty_array(),
244 /* param_values= */ Array::empty_array()));
245
246 EXPECT(!val.IsNull());
247 EXPECT(!val.IsError());
248 EXPECT(val.IsInteger());
249 EXPECT_EQ(7, Integer::Cast(val).AsInt64Value());
250}
251
252ISOLATE_UNIT_TEST_CASE(EvalExpressionExhaustCIDs) {
253 { // Initialize an incremental compiler in DFE mode.
254 TransitionVMToNative transition(thread);
255 TestCase::LoadTestScript(script: "", resolver: nullptr);
256 }
257 Library& lib = Library::Handle(ptr: Library::CoreLibrary());
258 const String& expression = String::Handle(ptr: String::New(cstr: "3 + 4"));
259 Object& val = Object::Handle();
260 val = Api::UnwrapHandle(
261 object: TestCase::EvaluateExpression(lib, expr: expression,
262 /* param_names= */ Array::empty_array(),
263 /* param_values= */ Array::empty_array()));
264
265 EXPECT(!val.IsNull());
266 EXPECT(!val.IsError());
267 EXPECT(val.IsInteger());
268 EXPECT_EQ(7, Integer::Cast(val).AsInt64Value());
269
270 auto class_table = IsolateGroup::Current()->class_table();
271
272 intptr_t initial_class_table_size = class_table->NumCids();
273
274 val = Api::UnwrapHandle(
275 object: TestCase::EvaluateExpression(lib, expr: expression,
276 /* param_names= */ Array::empty_array(),
277 /* param_values= */ Array::empty_array()));
278 EXPECT(!val.IsNull());
279 EXPECT(!val.IsError());
280 EXPECT(val.IsInteger());
281 EXPECT_EQ(7, Integer::Cast(val).AsInt64Value());
282
283 intptr_t final_class_table_size = class_table->NumCids();
284 // Eval should not eat into this non-renewable resource.
285 EXPECT_EQ(initial_class_table_size, final_class_table_size);
286}
287
288// Too slow in debug mode.
289#if !defined(DEBUG) && !defined(TARGET_USES_THREAD_SANITIZER)
290TEST_CASE(ManyClasses) {
291 // Limit is 20 bits. Check only more than 16 bits so test completes in
292 // reasonable time.
293 const intptr_t kNumClasses = (1 << 16) + 1;
294
295 TextBuffer buffer(MB);
296 for (intptr_t i = 0; i < kNumClasses; i++) {
297 buffer.Printf("class C%" Pd " { String toString() => 'C%" Pd "'; }\n", i,
298 i);
299 }
300 buffer.Printf("main() {\n");
301 for (intptr_t i = 0; i < kNumClasses; i++) {
302 buffer.Printf(" new C%" Pd "().toString();\n", i);
303 }
304 buffer.Printf("}\n");
305
306 Dart_Handle lib = TestCase::LoadTestScript(buffer.buffer(), nullptr);
307 EXPECT_VALID(lib);
308 Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr);
309 EXPECT_VALID(result);
310
311 EXPECT(IsolateGroup::Current()->class_table()->NumCids() >= kNumClasses);
312}
313#endif // !defined(DEBUG) && !defined(TARGET_USES_THREAD_SANITIZER)
314
315} // namespace dart
316

source code of dart_sdk/runtime/vm/compiler_test.cc