1 | // Copyright (c) 2011, 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 | #ifndef RUNTIME_VM_UNIT_TEST_H_ |
6 | #define RUNTIME_VM_UNIT_TEST_H_ |
7 | |
8 | #include "include/dart_native_api.h" |
9 | |
10 | #include "platform/globals.h" |
11 | |
12 | #include "vm/dart.h" |
13 | #include "vm/dart_api_state.h" |
14 | #include "vm/dart_entry.h" |
15 | #include "vm/globals.h" |
16 | #include "vm/heap/heap.h" |
17 | #include "vm/isolate.h" |
18 | #include "vm/longjump.h" |
19 | #include "vm/object.h" |
20 | #include "vm/object_store.h" |
21 | #include "vm/simulator.h" |
22 | #include "vm/zone.h" |
23 | |
24 | // The VM_UNIT_TEST_CASE macro is used for tests that do not need any |
25 | // default isolate or zone functionality. |
26 | #define VM_UNIT_TEST_CASE_WITH_EXPECTATION(name, expectation) \ |
27 | void Dart_Test##name(); \ |
28 | static const dart::TestCase kRegister##name(Dart_Test##name, #name, \ |
29 | expectation); \ |
30 | void Dart_Test##name() |
31 | |
32 | #define VM_UNIT_TEST_CASE(name) VM_UNIT_TEST_CASE_WITH_EXPECTATION(name, "Pass") |
33 | |
34 | // The UNIT_TEST_CASE macro is used for tests that do not require any |
35 | // functionality provided by the VM. Tests declared using this macro will be run |
36 | // after the VM is cleaned up. |
37 | #define UNIT_TEST_CASE_WITH_EXPECTATION(name, expectation) \ |
38 | void Dart_Test##name(); \ |
39 | static const dart::RawTestCase kRegister##name(Dart_Test##name, #name, \ |
40 | expectation); \ |
41 | void Dart_Test##name() |
42 | |
43 | #define UNIT_TEST_CASE(name) UNIT_TEST_CASE_WITH_EXPECTATION(name, "Pass") |
44 | |
45 | // The ISOLATE_UNIT_TEST_CASE macro is used for tests that need an isolate and |
46 | // zone in order to test its functionality. This macro is used for tests that |
47 | // are implemented using the VM code directly and do not use the Dart API |
48 | // for calling into the VM. The safepoint execution state of threads using |
49 | // this macro is transitioned from kThreadInNative to kThreadInVM. |
50 | #define ISOLATE_UNIT_TEST_CASE_WITH_EXPECTATION(name, expectation) \ |
51 | static void Dart_TestHelper##name(Thread* thread); \ |
52 | VM_UNIT_TEST_CASE_WITH_EXPECTATION(name, expectation) { \ |
53 | TestIsolateScope __test_isolate__; \ |
54 | Thread* __thread__ = Thread::Current(); \ |
55 | ASSERT(__thread__->isolate() == __test_isolate__.isolate()); \ |
56 | TransitionNativeToVM transition(__thread__); \ |
57 | StackZone __zone__(__thread__); \ |
58 | HandleScope __hs__(__thread__); \ |
59 | Dart_TestHelper##name(__thread__); \ |
60 | } \ |
61 | static void Dart_TestHelper##name(Thread* thread) |
62 | |
63 | #define ISOLATE_UNIT_TEST_CASE(name) \ |
64 | ISOLATE_UNIT_TEST_CASE_WITH_EXPECTATION(name, "Pass") |
65 | |
66 | // The TEST_CASE macro is used for tests that need an isolate and zone |
67 | // in order to test its functionality. This macro is used for tests that |
68 | // are implemented using the Dart API for calling into the VM. The safepoint |
69 | // execution state of threads using this macro remains kThreadNative. |
70 | #define TEST_CASE_WITH_EXPECTATION(name, expectation) \ |
71 | static void Dart_TestHelper##name(Thread* thread); \ |
72 | VM_UNIT_TEST_CASE_WITH_EXPECTATION(name, expectation) { \ |
73 | TestIsolateScope __test_isolate__; \ |
74 | Thread* __thread__ = Thread::Current(); \ |
75 | ASSERT(__thread__->isolate() == __test_isolate__.isolate()); \ |
76 | TransitionNativeToVM transition1(__thread__); \ |
77 | StackZone __zone__(__thread__); \ |
78 | HandleScope __hs__(__thread__); \ |
79 | TransitionVMToNative transition2(__thread__); \ |
80 | Dart_TestHelper##name(__thread__); \ |
81 | } \ |
82 | static void Dart_TestHelper##name(Thread* thread) |
83 | |
84 | #define TEST_CASE(name) TEST_CASE_WITH_EXPECTATION(name, "Pass") |
85 | |
86 | // The ASSEMBLER_TEST_GENERATE macro is used to generate a unit test |
87 | // for the assembler. |
88 | #define ASSEMBLER_TEST_GENERATE(name, assembler) \ |
89 | void AssemblerTestGenerate##name(compiler::Assembler* assembler) |
90 | |
91 | // The ASSEMBLER_TEST_EXTERN macro is used to declare a unit test |
92 | // for the assembler. |
93 | #define ASSEMBLER_TEST_EXTERN(name) \ |
94 | extern void AssemblerTestGenerate##name(compiler::Assembler* assembler); |
95 | |
96 | // The ASSEMBLER_TEST_RUN macro is used to execute the assembler unit |
97 | // test generated using the ASSEMBLER_TEST_GENERATE macro. |
98 | // C++ callee-saved registers are not preserved. Arguments may be passed in. |
99 | #define ASSEMBLER_TEST_RUN_WITH_EXPECTATION(name, test, expectation) \ |
100 | static void AssemblerTestRun##name(AssemblerTest* test); \ |
101 | ISOLATE_UNIT_TEST_CASE_WITH_EXPECTATION(name, expectation) { \ |
102 | { \ |
103 | bool use_far_branches = false; \ |
104 | LongJumpScope jump; \ |
105 | if (setjmp(*jump.Set()) == 0) { \ |
106 | compiler::ObjectPoolBuilder object_pool_builder; \ |
107 | compiler::Assembler assembler(&object_pool_builder, use_far_branches); \ |
108 | AssemblerTest test("" #name, &assembler); \ |
109 | AssemblerTestGenerate##name(test.assembler()); \ |
110 | test.Assemble(); \ |
111 | AssemblerTestRun##name(&test); \ |
112 | return; \ |
113 | } \ |
114 | } \ |
115 | \ |
116 | const Error& error = Error::Handle(Thread::Current()->sticky_error()); \ |
117 | if (error.raw() == Object::branch_offset_error().raw()) { \ |
118 | bool use_far_branches = true; \ |
119 | compiler::ObjectPoolBuilder object_pool_builder; \ |
120 | compiler::Assembler assembler(&object_pool_builder, use_far_branches); \ |
121 | AssemblerTest test("" #name, &assembler); \ |
122 | AssemblerTestGenerate##name(test.assembler()); \ |
123 | test.Assemble(); \ |
124 | AssemblerTestRun##name(&test); \ |
125 | } else { \ |
126 | FATAL1("Unexpected error: %s\n", error.ToErrorCString()); \ |
127 | } \ |
128 | } \ |
129 | static void AssemblerTestRun##name(AssemblerTest* test) |
130 | |
131 | #define ASSEMBLER_TEST_RUN(name, test) \ |
132 | ASSEMBLER_TEST_RUN_WITH_EXPECTATION(name, test, "Pass") |
133 | |
134 | #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64) |
135 | #if defined(HOST_ARCH_ARM) || defined(HOST_ARCH_ARM64) |
136 | // Running on actual ARM hardware, execute code natively. |
137 | #define EXECUTE_TEST_CODE_INT32(name, entry) reinterpret_cast<name>(entry)() |
138 | #define EXECUTE_TEST_CODE_INT64(name, entry) reinterpret_cast<name>(entry)() |
139 | #define EXECUTE_TEST_CODE_INT64_LL(name, entry, long_arg0, long_arg1) \ |
140 | reinterpret_cast<name>(entry)(long_arg0, long_arg1) |
141 | #define EXECUTE_TEST_CODE_FLOAT(name, entry) reinterpret_cast<name>(entry)() |
142 | #define EXECUTE_TEST_CODE_DOUBLE(name, entry) reinterpret_cast<name>(entry)() |
143 | #define EXECUTE_TEST_CODE_INT32_F(name, entry, float_arg) \ |
144 | reinterpret_cast<name>(entry)(float_arg) |
145 | #define EXECUTE_TEST_CODE_INT32_D(name, entry, double_arg) \ |
146 | reinterpret_cast<name>(entry)(double_arg) |
147 | #define EXECUTE_TEST_CODE_INTPTR_INTPTR(name, entry, pointer_arg) \ |
148 | reinterpret_cast<name>(entry)(pointer_arg) |
149 | #define EXECUTE_TEST_CODE_INT32_INTPTR(name, entry, pointer_arg) \ |
150 | reinterpret_cast<name>(entry)(pointer_arg) |
151 | #else |
152 | // Not running on ARM hardware, call simulator to execute code. |
153 | #if defined(ARCH_IS_64_BIT) |
154 | #define EXECUTE_TEST_CODE_INT64(name, entry) \ |
155 | static_cast<int64_t>( \ |
156 | Simulator::Current()->Call(bit_cast<int64_t, uword>(entry), 0, 0, 0, 0)) |
157 | #define EXECUTE_TEST_CODE_DOUBLE(name, entry) \ |
158 | bit_cast<double, int64_t>(Simulator::Current()->Call( \ |
159 | bit_cast<int64_t, uword>(entry), 0, 0, 0, 0, true)) |
160 | #define EXECUTE_TEST_CODE_INTPTR_INTPTR(name, entry, pointer_arg) \ |
161 | static_cast<intptr_t>(Simulator::Current()->Call( \ |
162 | bit_cast<int64_t, uword>(entry), \ |
163 | bit_cast<int64_t, intptr_t>(pointer_arg), 0, 0, 0)) |
164 | #define EXECUTE_TEST_CODE_INT32_INTPTR(name, entry, pointer_arg) \ |
165 | static_cast<int32_t>(Simulator::Current()->Call( \ |
166 | bit_cast<int64_t, uword>(entry), \ |
167 | bit_cast<int64_t, intptr_t>(pointer_arg), 0, 0, 0)) |
168 | #else |
169 | #define EXECUTE_TEST_CODE_INT32(name, entry) \ |
170 | static_cast<int32_t>( \ |
171 | Simulator::Current()->Call(bit_cast<int32_t, uword>(entry), 0, 0, 0, 0)) |
172 | #define EXECUTE_TEST_CODE_DOUBLE(name, entry) \ |
173 | bit_cast<double, int64_t>(Simulator::Current()->Call( \ |
174 | bit_cast<int32_t, uword>(entry), 0, 0, 0, 0, true)) |
175 | #define EXECUTE_TEST_CODE_INTPTR_INTPTR(name, entry, pointer_arg) \ |
176 | static_cast<intptr_t>(Simulator::Current()->Call( \ |
177 | bit_cast<int32_t, uword>(entry), \ |
178 | bit_cast<int32_t, intptr_t>(pointer_arg), 0, 0, 0)) |
179 | #define EXECUTE_TEST_CODE_INT32_INTPTR(name, entry, pointer_arg) \ |
180 | static_cast<int32_t>(Simulator::Current()->Call( \ |
181 | bit_cast<int32_t, uword>(entry), \ |
182 | bit_cast<int32_t, intptr_t>(pointer_arg), 0, 0, 0)) |
183 | #endif // defined(ARCH_IS_64_BIT) |
184 | #define EXECUTE_TEST_CODE_INT64_LL(name, entry, long_arg0, long_arg1) \ |
185 | static_cast<int64_t>(Simulator::Current()->Call( \ |
186 | bit_cast<int32_t, uword>(entry), Utils::Low32Bits(long_arg0), \ |
187 | Utils::High32Bits(long_arg0), Utils::Low32Bits(long_arg1), \ |
188 | Utils::High32Bits(long_arg1))) |
189 | #define EXECUTE_TEST_CODE_FLOAT(name, entry) \ |
190 | bit_cast<float, int32_t>(Simulator::Current()->Call( \ |
191 | bit_cast<int32_t, uword>(entry), 0, 0, 0, 0, true)) |
192 | #define EXECUTE_TEST_CODE_INT32_F(name, entry, float_arg) \ |
193 | static_cast<int32_t>(Simulator::Current()->Call( \ |
194 | bit_cast<int32_t, uword>(entry), bit_cast<int32_t, float>(float_arg), 0, \ |
195 | 0, 0, false, true)) |
196 | #define EXECUTE_TEST_CODE_INT32_D(name, entry, double_arg) \ |
197 | static_cast<int32_t>(Simulator::Current()->Call( \ |
198 | bit_cast<int32_t, uword>(entry), \ |
199 | Utils::Low32Bits(bit_cast<int64_t, double>(double_arg)), \ |
200 | Utils::High32Bits(bit_cast<int64_t, double>(double_arg)), 0, 0, false, \ |
201 | true)) |
202 | #endif // defined(HOST_ARCH_ARM) |
203 | #endif // defined(TARGET_ARCH_{ARM, ARM64}) |
204 | |
205 | #define ZONE_STR(FMT, ...) \ |
206 | OS::SCreate(Thread::Current()->zone(), FMT, __VA_ARGS__) |
207 | |
208 | inline Dart_Handle NewString(const char* str) { |
209 | return Dart_NewStringFromCString(str); |
210 | } |
211 | |
212 | namespace dart { |
213 | |
214 | // Forward declarations. |
215 | namespace compiler { |
216 | class Assembler; |
217 | } |
218 | class CodeGenerator; |
219 | class VirtualMemory; |
220 | |
221 | namespace bin { |
222 | // Snapshot pieces if we link in a snapshot, otherwise initialized to NULL. |
223 | extern const uint8_t* vm_snapshot_data; |
224 | extern const uint8_t* vm_snapshot_instructions; |
225 | extern const uint8_t* core_isolate_snapshot_data; |
226 | extern const uint8_t* core_isolate_snapshot_instructions; |
227 | } // namespace bin |
228 | |
229 | extern const uint8_t* platform_strong_dill; |
230 | extern const intptr_t platform_strong_dill_size; |
231 | |
232 | class TesterState : public AllStatic { |
233 | public: |
234 | static const uint8_t* vm_snapshot_data; |
235 | static Dart_IsolateGroupCreateCallback create_callback; |
236 | static Dart_IsolateShutdownCallback shutdown_callback; |
237 | static Dart_IsolateGroupCleanupCallback group_cleanup_callback; |
238 | static const char** argv; |
239 | static int argc; |
240 | }; |
241 | |
242 | class KernelBufferList { |
243 | public: |
244 | explicit KernelBufferList(const uint8_t* kernel_buffer) |
245 | : kernel_buffer_(kernel_buffer), next_(NULL) {} |
246 | |
247 | KernelBufferList(const uint8_t* kernel_buffer, KernelBufferList* next) |
248 | : kernel_buffer_(kernel_buffer), next_(next) {} |
249 | |
250 | ~KernelBufferList() { |
251 | free(const_cast<uint8_t*>(kernel_buffer_)); |
252 | if (next_ != NULL) { |
253 | delete next_; |
254 | } |
255 | } |
256 | |
257 | void AddBufferToList(const uint8_t* kernel_buffer); |
258 | |
259 | private: |
260 | const uint8_t* kernel_buffer_; |
261 | KernelBufferList* next_; |
262 | }; |
263 | |
264 | class TestCaseBase { |
265 | public: |
266 | explicit TestCaseBase(const char* name, const char* expectation); |
267 | virtual ~TestCaseBase() {} |
268 | |
269 | const char* name() const { return name_; } |
270 | const char* expectation() const { return expectation_; } |
271 | |
272 | virtual void Run() = 0; |
273 | void RunTest(); |
274 | |
275 | static void RunAll(); |
276 | static void RunAllRaw(); |
277 | static void CleanupState(); |
278 | static void AddToKernelBuffers(const uint8_t* kernel_buffer); |
279 | |
280 | protected: |
281 | static KernelBufferList* current_kernel_buffers_; |
282 | bool raw_test_; |
283 | |
284 | private: |
285 | static TestCaseBase* first_; |
286 | static TestCaseBase* tail_; |
287 | |
288 | TestCaseBase* next_; |
289 | const char* name_; |
290 | const char* expectation_; |
291 | |
292 | DISALLOW_COPY_AND_ASSIGN(TestCaseBase); |
293 | }; |
294 | |
295 | #define USER_TEST_URI "test-lib" |
296 | #define RESOLVED_USER_TEST_URI "file:///test-lib" |
297 | #define CORELIB_TEST_URI "dart:test-lib" |
298 | |
299 | class TestCase : TestCaseBase { |
300 | public: |
301 | typedef void(RunEntry)(); |
302 | |
303 | TestCase(RunEntry* run, const char* name, const char* expectation) |
304 | : TestCaseBase(name, expectation), run_(run) {} |
305 | |
306 | static char* CompileTestScriptWithDFE(const char* url, |
307 | const char* source, |
308 | const uint8_t** kernel_buffer, |
309 | intptr_t* kernel_buffer_size, |
310 | bool incrementally = true, |
311 | bool allow_compile_errors = false, |
312 | const char* multiroot_filepaths = NULL, |
313 | const char* multiroot_scheme = NULL); |
314 | static char* CompileTestScriptWithDFE(const char* url, |
315 | int sourcefiles_count, |
316 | Dart_SourceFile sourcefiles[], |
317 | const uint8_t** kernel_buffer, |
318 | intptr_t* kernel_buffer_size, |
319 | bool incrementally = true, |
320 | bool allow_compile_errors = false, |
321 | const char* multiroot_filepaths = NULL, |
322 | const char* multiroot_scheme = NULL); |
323 | static Dart_Handle LoadTestScript( |
324 | const char* script, |
325 | Dart_NativeEntryResolver resolver, |
326 | const char* lib_uri = RESOLVED_USER_TEST_URI, |
327 | bool finalize = true, |
328 | bool allow_compile_errors = false); |
329 | static Dart_Handle LoadTestScriptWithErrors( |
330 | const char* script, |
331 | Dart_NativeEntryResolver resolver = NULL, |
332 | const char* lib_uri = RESOLVED_USER_TEST_URI, |
333 | bool finalize = true); |
334 | static Dart_Handle LoadTestLibrary(const char* lib_uri, |
335 | const char* script, |
336 | Dart_NativeEntryResolver resolver = NULL); |
337 | static Dart_Handle LoadTestScriptWithDFE( |
338 | int sourcefiles_count, |
339 | Dart_SourceFile sourcefiles[], |
340 | Dart_NativeEntryResolver resolver = NULL, |
341 | bool finalize = true, |
342 | bool incrementally = true, |
343 | bool allow_compile_errors = false, |
344 | const char* entry_script_uri = NULL, |
345 | const char* multiroot_filepaths = NULL, |
346 | const char* multiroot_scheme = NULL); |
347 | static Dart_Handle LoadCoreTestScript(const char* script, |
348 | Dart_NativeEntryResolver resolver); |
349 | |
350 | static Dart_Handle EvaluateExpression(const Library& lib, |
351 | const String& expr, |
352 | const Array& param_names, |
353 | const Array& param_values); |
354 | |
355 | static Dart_Handle lib(); |
356 | static const char* url(); |
357 | static Dart_Isolate CreateTestIsolateFromSnapshot(uint8_t* buffer, |
358 | const char* name = NULL) { |
359 | return CreateIsolate(buffer, 0, NULL, name); |
360 | } |
361 | static Dart_Isolate CreateTestIsolate(const char* name = nullptr, |
362 | void* isolate_group_data = nullptr, |
363 | void* isolate_data = nullptr); |
364 | static Dart_Isolate CreateTestIsolateInGroup(const char* name, |
365 | Dart_Isolate parent, |
366 | void* group_data = nullptr, |
367 | void* isolate_data = nullptr); |
368 | |
369 | static Dart_Handle library_handler(Dart_LibraryTag tag, |
370 | Dart_Handle library, |
371 | Dart_Handle url); |
372 | |
373 | virtual void Run(); |
374 | |
375 | // Sets |script| to be the source used at next reload. |
376 | static Dart_Handle SetReloadTestScript(const char* script); |
377 | |
378 | // Initiates the reload. |
379 | static Dart_Handle TriggerReload(const uint8_t* kernel_buffer, |
380 | intptr_t kernel_buffer_size); |
381 | |
382 | // Helper function which reloads the current isolate using |script|. |
383 | static Dart_Handle ReloadTestScript(const char* script); |
384 | |
385 | // Helper function which reloads the current isolate using |script|. |
386 | static Dart_Handle ReloadTestKernel(const uint8_t* kernel_buffer, |
387 | intptr_t kernel_buffer_size); |
388 | |
389 | static void AddTestLib(const char* url, const char* source); |
390 | static const char* GetTestLib(const char* url); |
391 | |
392 | // Return true if non-nullable experiment is enabled. |
393 | static bool IsNNBD(); |
394 | |
395 | static const char* NullableTag() { return IsNNBD() ? "?" : "" ; } |
396 | static const char* NullAssertTag() { return IsNNBD() ? "!" : "" ; } |
397 | static const char* LateTag() { return IsNNBD() ? "late" : "" ; } |
398 | |
399 | private: |
400 | // |data_buffer| can either be snapshot data, or kernel binary data. |
401 | // If |data_buffer| is snapshot data, then |len| should be zero as snapshot |
402 | // size is encoded within them. If |len| is non-zero, then |data_buffer| |
403 | // will be treated as a kernel binary (but CreateIsolate will not |
404 | // take ownership of the buffer) and |instr_buffer| will be ignored. |
405 | static Dart_Isolate CreateIsolate(const uint8_t* data_buffer, |
406 | intptr_t len, |
407 | const uint8_t* instr_buffer, |
408 | const char* name, |
409 | void* group_data = nullptr, |
410 | void* isolate_data = nullptr); |
411 | |
412 | static char* ValidateCompilationResult(Zone* zone, |
413 | Dart_KernelCompilationResult result, |
414 | const uint8_t** kernel_buffer, |
415 | intptr_t* kernel_buffer_size, |
416 | bool allow_compile_errors); |
417 | |
418 | RunEntry* const run_; |
419 | }; |
420 | |
421 | class RawTestCase : TestCaseBase { |
422 | public: |
423 | typedef void(RunEntry)(); |
424 | |
425 | RawTestCase(RunEntry* run, const char* name, const char* expectation) |
426 | : TestCaseBase(name, expectation), run_(run) { |
427 | raw_test_ = true; |
428 | } |
429 | virtual void Run(); |
430 | |
431 | private: |
432 | RunEntry* const run_; |
433 | }; |
434 | |
435 | class TestIsolateScope { |
436 | public: |
437 | TestIsolateScope() { |
438 | isolate_ = reinterpret_cast<Isolate*>(TestCase::CreateTestIsolate()); |
439 | Dart_EnterScope(); // Create a Dart API scope for unit tests. |
440 | } |
441 | ~TestIsolateScope() { |
442 | Dart_ExitScope(); // Exit the Dart API scope created for unit tests. |
443 | ASSERT(isolate_ == Isolate::Current()); |
444 | Dart_ShutdownIsolate(); |
445 | isolate_ = NULL; |
446 | } |
447 | Isolate* isolate() const { return isolate_; } |
448 | |
449 | private: |
450 | Isolate* isolate_; |
451 | |
452 | DISALLOW_COPY_AND_ASSIGN(TestIsolateScope); |
453 | }; |
454 | |
455 | // Ensures core libraries are initialized, thereby allowing vm/cc tests to |
456 | // e.g. run functions using microtasks. |
457 | void SetupCoreLibrariesForUnitTest(); |
458 | |
459 | template <typename T> |
460 | struct is_void { |
461 | static const bool value = false; |
462 | }; |
463 | |
464 | template <> |
465 | struct is_void<void> { |
466 | static const bool value = true; |
467 | }; |
468 | |
469 | template <typename T> |
470 | struct is_double { |
471 | static const bool value = false; |
472 | }; |
473 | |
474 | template <> |
475 | struct is_double<double> { |
476 | static const bool value = true; |
477 | }; |
478 | |
479 | class AssemblerTest { |
480 | public: |
481 | AssemblerTest(const char* name, compiler::Assembler* assembler) |
482 | : name_(name), |
483 | assembler_(assembler), |
484 | code_(Code::ZoneHandle()), |
485 | disassembly_(Thread::Current()->zone()->Alloc<char>(DISASSEMBLY_SIZE)) { |
486 | ASSERT(name != NULL); |
487 | ASSERT(assembler != NULL); |
488 | } |
489 | ~AssemblerTest() {} |
490 | |
491 | compiler::Assembler* assembler() const { return assembler_; } |
492 | |
493 | const Code& code() const { return code_; } |
494 | |
495 | uword payload_start() const { return code_.PayloadStart(); } |
496 | uword payload_size() const { return assembler_->CodeSize(); } |
497 | uword entry() const { return code_.EntryPoint(); } |
498 | |
499 | // Invoke/InvokeWithCodeAndThread is used to call assembler test functions |
500 | // using the ABI calling convention. |
501 | // ResultType is the return type of the assembler test function. |
502 | // ArgNType is the type of the Nth argument. |
503 | #if defined(USING_SIMULATOR) |
504 | |
505 | #if defined(ARCH_IS_64_BIT) |
506 | // TODO(fschneider): Make InvokeWithCodeAndThread<> more general and work on |
507 | // 32-bit. |
508 | // Since Simulator::Call always return a int64_t, bit_cast does not work |
509 | // on 32-bit platforms when returning an int32_t. Since template functions |
510 | // don't support partial specialization, we'd need to introduce a helper |
511 | // class to support 32-bit return types. |
512 | template <typename ResultType> |
513 | ResultType InvokeWithCodeAndThread() { |
514 | const bool fp_return = is_double<ResultType>::value; |
515 | const bool fp_args = false; |
516 | Thread* thread = Thread::Current(); |
517 | ASSERT(thread != NULL); |
518 | return bit_cast<ResultType, int64_t>(Simulator::Current()->Call( |
519 | bit_cast<intptr_t, uword>(entry()), reinterpret_cast<intptr_t>(&code_), |
520 | reinterpret_cast<intptr_t>(thread), 0, 0, fp_return, fp_args)); |
521 | } |
522 | template <typename ResultType, typename Arg1Type> |
523 | ResultType InvokeWithCodeAndThread(Arg1Type arg1) { |
524 | const bool fp_return = is_double<ResultType>::value; |
525 | const bool fp_args = is_double<Arg1Type>::value; |
526 | // TODO(fschneider): Support double arguments for simulator calls. |
527 | COMPILE_ASSERT(!fp_args); |
528 | Thread* thread = Thread::Current(); |
529 | ASSERT(thread != NULL); |
530 | return bit_cast<ResultType, int64_t>(Simulator::Current()->Call( |
531 | bit_cast<intptr_t, uword>(entry()), reinterpret_cast<intptr_t>(&code_), |
532 | reinterpret_cast<intptr_t>(thread), reinterpret_cast<intptr_t>(arg1), 0, |
533 | fp_return, fp_args)); |
534 | } |
535 | #endif // ARCH_IS_64_BIT |
536 | |
537 | template <typename ResultType, |
538 | typename Arg1Type, |
539 | typename Arg2Type, |
540 | typename Arg3Type> |
541 | ResultType Invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3) { |
542 | // TODO(fschneider): Support double arguments for simulator calls. |
543 | COMPILE_ASSERT(is_void<ResultType>::value); |
544 | COMPILE_ASSERT(!is_double<Arg1Type>::value); |
545 | COMPILE_ASSERT(!is_double<Arg2Type>::value); |
546 | COMPILE_ASSERT(!is_double<Arg3Type>::value); |
547 | const bool fp_args = false; |
548 | const bool fp_return = false; |
549 | Simulator::Current()->Call( |
550 | bit_cast<intptr_t, uword>(entry()), static_cast<intptr_t>(arg1), |
551 | static_cast<intptr_t>(arg2), reinterpret_cast<intptr_t>(arg3), 0, |
552 | fp_return, fp_args); |
553 | } |
554 | #else |
555 | template <typename ResultType> |
556 | ResultType InvokeWithCodeAndThread() { |
557 | Thread* thread = Thread::Current(); |
558 | ASSERT(thread != NULL); |
559 | typedef ResultType (*FunctionType)(const Code&, Thread*); |
560 | return reinterpret_cast<FunctionType>(entry())(code_, thread); |
561 | } |
562 | |
563 | template <typename ResultType, typename Arg1Type> |
564 | ResultType InvokeWithCodeAndThread(Arg1Type arg1) { |
565 | Thread* thread = Thread::Current(); |
566 | ASSERT(thread != NULL); |
567 | typedef ResultType (*FunctionType)(const Code&, Thread*, Arg1Type); |
568 | return reinterpret_cast<FunctionType>(entry())(code_, thread, arg1); |
569 | } |
570 | |
571 | template <typename ResultType, |
572 | typename Arg1Type, |
573 | typename Arg2Type, |
574 | typename Arg3Type> |
575 | ResultType Invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3) { |
576 | typedef ResultType (*FunctionType)(Arg1Type, Arg2Type, Arg3Type); |
577 | return reinterpret_cast<FunctionType>(entry())(arg1, arg2, arg3); |
578 | } |
579 | #endif // defined(USING_SIMULATOR) |
580 | |
581 | // Assemble test and set code_. |
582 | void Assemble(); |
583 | |
584 | // Disassembly of the code with large constants blanked out. |
585 | char* BlankedDisassembly() { return disassembly_; } |
586 | |
587 | private: |
588 | const char* name_; |
589 | compiler::Assembler* assembler_; |
590 | Code& code_; |
591 | static const intptr_t DISASSEMBLY_SIZE = 10240; |
592 | char* disassembly_; |
593 | |
594 | DISALLOW_COPY_AND_ASSIGN(AssemblerTest); |
595 | }; |
596 | |
597 | class CompilerTest : public AllStatic { |
598 | public: |
599 | // Test the Compiler::CompileFunction functionality by checking the return |
600 | // value to see if no parse errors were reported. |
601 | static bool TestCompileFunction(const Function& function); |
602 | }; |
603 | |
604 | #define EXPECT_VALID(handle) \ |
605 | do { \ |
606 | Dart_Handle tmp_handle = (handle); \ |
607 | if (!Api::IsValid(tmp_handle)) { \ |
608 | dart::Expect(__FILE__, __LINE__) \ |
609 | .Fail( \ |
610 | "expected '%s' to be a valid handle but '%s' has already been " \ |
611 | "freed\n", \ |
612 | #handle, #handle); \ |
613 | } \ |
614 | if (Dart_IsError(tmp_handle)) { \ |
615 | dart::Expect(__FILE__, __LINE__) \ |
616 | .Fail( \ |
617 | "expected '%s' to be a valid handle but found an error " \ |
618 | "handle:\n" \ |
619 | " '%s'\n", \ |
620 | #handle, Dart_GetError(tmp_handle)); \ |
621 | } \ |
622 | } while (0) |
623 | |
624 | #define EXPECT_ERROR(handle, substring) \ |
625 | do { \ |
626 | Dart_Handle tmp_handle = (handle); \ |
627 | if (Dart_IsError(tmp_handle)) { \ |
628 | dart::Expect(__FILE__, __LINE__) \ |
629 | .IsSubstring((substring), Dart_GetError(tmp_handle)); \ |
630 | } else { \ |
631 | dart::Expect(__FILE__, __LINE__) \ |
632 | .Fail( \ |
633 | "expected '%s' to be an error handle but found a valid " \ |
634 | "handle.\n", \ |
635 | #handle); \ |
636 | } \ |
637 | } while (0) |
638 | |
639 | #define EXPECT_TRUE(handle) \ |
640 | do { \ |
641 | Dart_Handle tmp_handle = (handle); \ |
642 | if (Dart_IsBoolean(tmp_handle)) { \ |
643 | bool value; \ |
644 | Dart_BooleanValue(tmp_handle, &value); \ |
645 | if (!value) { \ |
646 | dart::Expect(__FILE__, __LINE__) \ |
647 | .Fail("expected True, but was '%s'\n", #handle); \ |
648 | } \ |
649 | } else { \ |
650 | dart::Expect(__FILE__, __LINE__) \ |
651 | .Fail("expected True, but was '%s'\n", #handle); \ |
652 | } \ |
653 | } while (0) |
654 | |
655 | #define EXPECT_NULL(handle) \ |
656 | do { \ |
657 | Dart_Handle tmp_handle = (handle); \ |
658 | if (!Dart_IsNull(tmp_handle)) { \ |
659 | dart::Expect(__FILE__, __LINE__) \ |
660 | .Fail("expected '%s' to be a null handle.\n", #handle); \ |
661 | } \ |
662 | } while (0) |
663 | |
664 | #define EXPECT_NON_NULL(handle) \ |
665 | do { \ |
666 | Dart_Handle tmp_handle = (handle); \ |
667 | if (Dart_IsNull(tmp_handle)) { \ |
668 | dart::Expect(__FILE__, __LINE__) \ |
669 | .Fail("expected '%s' to be a non-null handle.\n", #handle); \ |
670 | } \ |
671 | } while (0) |
672 | |
673 | // Elide a substring which starts with some prefix and ends with a ". |
674 | // |
675 | // This is used to remove non-deterministic or fragile substrings from |
676 | // JSON output. |
677 | // |
678 | // For example: |
679 | // |
680 | // prefix = "classes" |
681 | // in = "\"id\":\"classes/46\"" |
682 | // |
683 | // Yields: |
684 | // |
685 | // out = "\"id\":\"\"" |
686 | // |
687 | void ElideJSONSubstring(const char* prefix, const char* in, char* out); |
688 | |
689 | template <typename T> |
690 | class SetFlagScope : public ValueObject { |
691 | public: |
692 | SetFlagScope(T* flag, T value) : flag_(flag), original_value_(*flag) { |
693 | *flag_ = value; |
694 | } |
695 | |
696 | ~SetFlagScope() { *flag_ = original_value_; } |
697 | |
698 | private: |
699 | T* flag_; |
700 | T original_value_; |
701 | }; |
702 | |
703 | } // namespace dart |
704 | |
705 | #endif // RUNTIME_VM_UNIT_TEST_H_ |
706 | |