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