1 // Copyright 2008 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #ifndef CCTEST_H_
29 #define CCTEST_H_
30
31 #include "src/v8.h"
32
33 #include "src/isolate-inl.h"
34
35 #ifndef TEST
36 #define TEST(Name) \
37 static void Test##Name(); \
38 CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true, true); \
39 static void Test##Name()
40 #endif
41
42 #ifndef UNINITIALIZED_TEST
43 #define UNINITIALIZED_TEST(Name) \
44 static void Test##Name(); \
45 CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true, false); \
46 static void Test##Name()
47 #endif
48
49 #ifndef DEPENDENT_TEST
50 #define DEPENDENT_TEST(Name, Dep) \
51 static void Test##Name(); \
52 CcTest register_test_##Name(Test##Name, __FILE__, #Name, #Dep, true, true); \
53 static void Test##Name()
54 #endif
55
56 #ifndef UNINITIALIZED_DEPENDENT_TEST
57 #define UNINITIALIZED_DEPENDENT_TEST(Name, Dep) \
58 static void Test##Name(); \
59 CcTest register_test_##Name(Test##Name, __FILE__, #Name, #Dep, true, false); \
60 static void Test##Name()
61 #endif
62
63 #ifndef DISABLED_TEST
64 #define DISABLED_TEST(Name) \
65 static void Test##Name(); \
66 CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, false, true); \
67 static void Test##Name()
68 #endif
69
70 #define EXTENSION_LIST(V) \
71 V(GC_EXTENSION, "v8/gc") \
72 V(PRINT_EXTENSION, "v8/print") \
73 V(PROFILER_EXTENSION, "v8/profiler") \
74 V(TRACE_EXTENSION, "v8/trace")
75
76 #define DEFINE_EXTENSION_ID(Name, Ident) Name##_ID,
77 enum CcTestExtensionIds {
78 EXTENSION_LIST(DEFINE_EXTENSION_ID)
79 kMaxExtensions
80 };
81 #undef DEFINE_EXTENSION_ID
82
83 typedef v8::internal::EnumSet<CcTestExtensionIds> CcTestExtensionFlags;
84 #define DEFINE_EXTENSION_FLAG(Name, Ident) \
85 static const CcTestExtensionFlags Name(1 << Name##_ID);
86 static const CcTestExtensionFlags NO_EXTENSIONS(0);
87 static const CcTestExtensionFlags ALL_EXTENSIONS((1 << kMaxExtensions) - 1);
EXTENSION_LIST(DEFINE_EXTENSION_FLAG)88 EXTENSION_LIST(DEFINE_EXTENSION_FLAG)
89 #undef DEFINE_EXTENSION_FLAG
90
91
92 // Use this to expose protected methods in i::Heap.
93 class TestHeap : public i::Heap {
94 public:
95 using i::Heap::AllocateByteArray;
96 using i::Heap::AllocateFixedArray;
97 using i::Heap::AllocateHeapNumber;
98 using i::Heap::AllocateJSObject;
99 using i::Heap::AllocateJSObjectFromMap;
100 using i::Heap::AllocateMap;
101 using i::Heap::CopyCode;
102 };
103
104
105 class CcTest {
106 public:
107 typedef void (TestFunction)();
108 CcTest(TestFunction* callback, const char* file, const char* name,
109 const char* dependency, bool enabled, bool initialize);
110 void Run();
last()111 static CcTest* last() { return last_; }
prev()112 CcTest* prev() { return prev_; }
file()113 const char* file() { return file_; }
name()114 const char* name() { return name_; }
dependency()115 const char* dependency() { return dependency_; }
enabled()116 bool enabled() { return enabled_; }
117
isolate()118 static v8::Isolate* isolate() {
119 CHECK(isolate_ != NULL);
120 isolate_used_ = true;
121 return isolate_;
122 }
123
InitIsolateOnce()124 static i::Isolate* InitIsolateOnce() {
125 if (!initialize_called_) InitializeVM();
126 return i_isolate();
127 }
128
i_isolate()129 static i::Isolate* i_isolate() {
130 return reinterpret_cast<i::Isolate*>(isolate());
131 }
132
heap()133 static i::Heap* heap() {
134 return i_isolate()->heap();
135 }
136
test_heap()137 static TestHeap* test_heap() {
138 return reinterpret_cast<TestHeap*>(i_isolate()->heap());
139 }
140
random_number_generator()141 static v8::base::RandomNumberGenerator* random_number_generator() {
142 return InitIsolateOnce()->random_number_generator();
143 }
144
global()145 static v8::Local<v8::Object> global() {
146 return isolate()->GetCurrentContext()->Global();
147 }
148
149 // TODO(dcarney): Remove.
150 // This must be called first in a test.
InitializeVM()151 static void InitializeVM() {
152 CHECK(!isolate_used_);
153 CHECK(!initialize_called_);
154 initialize_called_ = true;
155 v8::HandleScope handle_scope(CcTest::isolate());
156 v8::Context::New(CcTest::isolate())->Enter();
157 }
158
159 // Only for UNINITIALIZED_TESTs
160 static void DisableAutomaticDispose();
161
162 // Helper function to configure a context.
163 // Must be in a HandleScope.
164 static v8::Local<v8::Context> NewContext(
165 CcTestExtensionFlags extensions,
166 v8::Isolate* isolate = CcTest::isolate());
167
TearDown()168 static void TearDown() {
169 if (isolate_ != NULL) isolate_->Dispose();
170 }
171
172 private:
173 friend int main(int argc, char** argv);
174 TestFunction* callback_;
175 const char* file_;
176 const char* name_;
177 const char* dependency_;
178 bool enabled_;
179 bool initialize_;
180 CcTest* prev_;
181 static CcTest* last_;
182 static v8::Isolate* isolate_;
183 static bool initialize_called_;
184 static bool isolate_used_;
185 };
186
187 // Switches between all the Api tests using the threading support.
188 // In order to get a surprising but repeatable pattern of thread
189 // switching it has extra semaphores to control the order in which
190 // the tests alternate, not relying solely on the big V8 lock.
191 //
192 // A test is augmented with calls to ApiTestFuzzer::Fuzz() in its
193 // callbacks. This will have no effect when we are not running the
194 // thread fuzzing test. In the thread fuzzing test it will
195 // pseudorandomly select a successor thread and switch execution
196 // to that thread, suspending the current test.
197 class ApiTestFuzzer: public v8::base::Thread {
198 public:
199 void CallTest();
200
201 // The ApiTestFuzzer is also a Thread, so it has a Run method.
202 virtual void Run();
203
204 enum PartOfTest { FIRST_PART,
205 SECOND_PART,
206 THIRD_PART,
207 FOURTH_PART,
208 LAST_PART = FOURTH_PART };
209
210 static void SetUp(PartOfTest part);
211 static void RunAllTests();
212 static void TearDown();
213 // This method switches threads if we are running the Threading test.
214 // Otherwise it does nothing.
215 static void Fuzz();
216
217 private:
ApiTestFuzzer(int num)218 explicit ApiTestFuzzer(int num)
219 : Thread(Options("ApiTestFuzzer")),
220 test_number_(num),
221 gate_(0),
222 active_(true) {}
~ApiTestFuzzer()223 ~ApiTestFuzzer() {}
224
225 static bool fuzzing_;
226 static int tests_being_run_;
227 static int current_;
228 static int active_tests_;
229 static bool NextThread();
230 int test_number_;
231 v8::base::Semaphore gate_;
232 bool active_;
233 void ContextSwitch();
234 static int GetNextTestNumber();
235 static v8::base::Semaphore all_tests_done_;
236 };
237
238
239 #define THREADED_TEST(Name) \
240 static void Test##Name(); \
241 RegisterThreadedTest register_##Name(Test##Name, #Name); \
242 /* */ TEST(Name)
243
244
245 class RegisterThreadedTest {
246 public:
RegisterThreadedTest(CcTest::TestFunction * callback,const char * name)247 explicit RegisterThreadedTest(CcTest::TestFunction* callback,
248 const char* name)
249 : fuzzer_(NULL), callback_(callback), name_(name) {
250 prev_ = first_;
251 first_ = this;
252 count_++;
253 }
count()254 static int count() { return count_; }
nth(int i)255 static RegisterThreadedTest* nth(int i) {
256 CHECK(i < count());
257 RegisterThreadedTest* current = first_;
258 while (i > 0) {
259 i--;
260 current = current->prev_;
261 }
262 return current;
263 }
callback()264 CcTest::TestFunction* callback() { return callback_; }
265 ApiTestFuzzer* fuzzer_;
name()266 const char* name() { return name_; }
267
268 private:
269 static RegisterThreadedTest* first_;
270 static int count_;
271 CcTest::TestFunction* callback_;
272 RegisterThreadedTest* prev_;
273 const char* name_;
274 };
275
276 // A LocalContext holds a reference to a v8::Context.
277 class LocalContext {
278 public:
279 LocalContext(v8::Isolate* isolate,
280 v8::ExtensionConfiguration* extensions = 0,
281 v8::Handle<v8::ObjectTemplate> global_template =
282 v8::Handle<v8::ObjectTemplate>(),
283 v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) {
284 Initialize(isolate, extensions, global_template, global_object);
285 }
286
287 LocalContext(v8::ExtensionConfiguration* extensions = 0,
288 v8::Handle<v8::ObjectTemplate> global_template =
289 v8::Handle<v8::ObjectTemplate>(),
290 v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) {
291 Initialize(CcTest::isolate(), extensions, global_template, global_object);
292 }
293
~LocalContext()294 virtual ~LocalContext() {
295 v8::HandleScope scope(isolate_);
296 v8::Local<v8::Context>::New(isolate_, context_)->Exit();
297 context_.Reset();
298 }
299
300 v8::Context* operator->() {
301 return *reinterpret_cast<v8::Context**>(&context_);
302 }
303 v8::Context* operator*() { return operator->(); }
IsReady()304 bool IsReady() { return !context_.IsEmpty(); }
305
local()306 v8::Local<v8::Context> local() {
307 return v8::Local<v8::Context>::New(isolate_, context_);
308 }
309
310 private:
Initialize(v8::Isolate * isolate,v8::ExtensionConfiguration * extensions,v8::Handle<v8::ObjectTemplate> global_template,v8::Handle<v8::Value> global_object)311 void Initialize(v8::Isolate* isolate,
312 v8::ExtensionConfiguration* extensions,
313 v8::Handle<v8::ObjectTemplate> global_template,
314 v8::Handle<v8::Value> global_object) {
315 v8::HandleScope scope(isolate);
316 v8::Local<v8::Context> context = v8::Context::New(isolate,
317 extensions,
318 global_template,
319 global_object);
320 context_.Reset(isolate, context);
321 context->Enter();
322 // We can't do this later perhaps because of a fatal error.
323 isolate_ = isolate;
324 }
325
326 v8::Persistent<v8::Context> context_;
327 v8::Isolate* isolate_;
328 };
329
330
AsciiToTwoByteString(const char * source)331 static inline uint16_t* AsciiToTwoByteString(const char* source) {
332 int array_length = i::StrLength(source) + 1;
333 uint16_t* converted = i::NewArray<uint16_t>(array_length);
334 for (int i = 0; i < array_length; i++) converted[i] = source[i];
335 return converted;
336 }
337
338
v8_num(double x)339 static inline v8::Local<v8::Value> v8_num(double x) {
340 return v8::Number::New(v8::Isolate::GetCurrent(), x);
341 }
342
343
v8_str(const char * x)344 static inline v8::Local<v8::String> v8_str(const char* x) {
345 return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x);
346 }
347
348
v8_compile(const char * x)349 static inline v8::Local<v8::Script> v8_compile(const char* x) {
350 return v8::Script::Compile(v8_str(x));
351 }
352
353
v8_compile(v8::Local<v8::String> x)354 static inline v8::Local<v8::Script> v8_compile(v8::Local<v8::String> x) {
355 return v8::Script::Compile(x);
356 }
357
358
CompileWithOrigin(v8::Local<v8::String> source,v8::Local<v8::String> origin_url)359 static inline v8::Local<v8::Script> CompileWithOrigin(
360 v8::Local<v8::String> source, v8::Local<v8::String> origin_url) {
361 v8::ScriptOrigin origin(origin_url);
362 v8::ScriptCompiler::Source script_source(source, origin);
363 return v8::ScriptCompiler::Compile(
364 v8::Isolate::GetCurrent(), &script_source);
365 }
366
367
CompileWithOrigin(v8::Local<v8::String> source,const char * origin_url)368 static inline v8::Local<v8::Script> CompileWithOrigin(
369 v8::Local<v8::String> source, const char* origin_url) {
370 return CompileWithOrigin(source, v8_str(origin_url));
371 }
372
373
CompileWithOrigin(const char * source,const char * origin_url)374 static inline v8::Local<v8::Script> CompileWithOrigin(const char* source,
375 const char* origin_url) {
376 return CompileWithOrigin(v8_str(source), v8_str(origin_url));
377 }
378
379
380 // Helper functions that compile and run the source.
CompileRun(const char * source)381 static inline v8::Local<v8::Value> CompileRun(const char* source) {
382 return v8::Script::Compile(v8_str(source))->Run();
383 }
384
385
CompileRun(v8::Local<v8::String> source)386 static inline v8::Local<v8::Value> CompileRun(v8::Local<v8::String> source) {
387 return v8::Script::Compile(source)->Run();
388 }
389
390
ParserCacheCompileRun(const char * source)391 static inline v8::Local<v8::Value> ParserCacheCompileRun(const char* source) {
392 // Compile once just to get the preparse data, then compile the second time
393 // using the data.
394 v8::Isolate* isolate = v8::Isolate::GetCurrent();
395 v8::ScriptCompiler::Source script_source(v8_str(source));
396 v8::ScriptCompiler::Compile(isolate, &script_source,
397 v8::ScriptCompiler::kProduceParserCache);
398
399 // Check whether we received cached data, and if so use it.
400 v8::ScriptCompiler::CompileOptions options =
401 script_source.GetCachedData() ? v8::ScriptCompiler::kConsumeParserCache
402 : v8::ScriptCompiler::kNoCompileOptions;
403
404 return v8::ScriptCompiler::Compile(isolate, &script_source, options)->Run();
405 }
406
407
408 // Helper functions that compile and run the source with given origin.
CompileRunWithOrigin(const char * source,const char * origin_url,int line_number,int column_number)409 static inline v8::Local<v8::Value> CompileRunWithOrigin(const char* source,
410 const char* origin_url,
411 int line_number,
412 int column_number) {
413 v8::Isolate* isolate = v8::Isolate::GetCurrent();
414 v8::ScriptOrigin origin(v8_str(origin_url),
415 v8::Integer::New(isolate, line_number),
416 v8::Integer::New(isolate, column_number));
417 v8::ScriptCompiler::Source script_source(v8_str(source), origin);
418 return v8::ScriptCompiler::Compile(isolate, &script_source)->Run();
419 }
420
421
CompileRunWithOrigin(v8::Local<v8::String> source,const char * origin_url)422 static inline v8::Local<v8::Value> CompileRunWithOrigin(
423 v8::Local<v8::String> source, const char* origin_url) {
424 v8::ScriptCompiler::Source script_source(
425 source, v8::ScriptOrigin(v8_str(origin_url)));
426 return v8::ScriptCompiler::Compile(v8::Isolate::GetCurrent(), &script_source)
427 ->Run();
428 }
429
430
CompileRunWithOrigin(const char * source,const char * origin_url)431 static inline v8::Local<v8::Value> CompileRunWithOrigin(
432 const char* source, const char* origin_url) {
433 return CompileRunWithOrigin(v8_str(source), origin_url);
434 }
435
436
437
ExpectString(const char * code,const char * expected)438 static inline void ExpectString(const char* code, const char* expected) {
439 v8::Local<v8::Value> result = CompileRun(code);
440 CHECK(result->IsString());
441 v8::String::Utf8Value utf8(result);
442 CHECK_EQ(expected, *utf8);
443 }
444
445
ExpectInt32(const char * code,int expected)446 static inline void ExpectInt32(const char* code, int expected) {
447 v8::Local<v8::Value> result = CompileRun(code);
448 CHECK(result->IsInt32());
449 CHECK_EQ(expected, result->Int32Value());
450 }
451
452
ExpectBoolean(const char * code,bool expected)453 static inline void ExpectBoolean(const char* code, bool expected) {
454 v8::Local<v8::Value> result = CompileRun(code);
455 CHECK(result->IsBoolean());
456 CHECK_EQ(expected, result->BooleanValue());
457 }
458
459
ExpectTrue(const char * code)460 static inline void ExpectTrue(const char* code) {
461 ExpectBoolean(code, true);
462 }
463
464
ExpectFalse(const char * code)465 static inline void ExpectFalse(const char* code) {
466 ExpectBoolean(code, false);
467 }
468
469
ExpectObject(const char * code,v8::Local<v8::Value> expected)470 static inline void ExpectObject(const char* code,
471 v8::Local<v8::Value> expected) {
472 v8::Local<v8::Value> result = CompileRun(code);
473 CHECK(result->SameValue(expected));
474 }
475
476
ExpectUndefined(const char * code)477 static inline void ExpectUndefined(const char* code) {
478 v8::Local<v8::Value> result = CompileRun(code);
479 CHECK(result->IsUndefined());
480 }
481
482
483 // Helper function that simulates a full new-space in the heap.
SimulateFullSpace(v8::internal::NewSpace * space)484 static inline void SimulateFullSpace(v8::internal::NewSpace* space) {
485 int new_linear_size = static_cast<int>(
486 *space->allocation_limit_address() - *space->allocation_top_address());
487 if (new_linear_size == 0) return;
488 v8::internal::AllocationResult allocation =
489 space->AllocateRaw(new_linear_size);
490 v8::internal::FreeListNode* node =
491 v8::internal::FreeListNode::cast(allocation.ToObjectChecked());
492 node->set_size(space->heap(), new_linear_size);
493 }
494
495
496 // Helper function that simulates a full old-space in the heap.
SimulateFullSpace(v8::internal::PagedSpace * space)497 static inline void SimulateFullSpace(v8::internal::PagedSpace* space) {
498 space->EmptyAllocationInfo();
499 space->ResetFreeList();
500 space->ClearStats();
501 }
502
503
504 // Helper function that simulates many incremental marking steps until
505 // marking is completed.
SimulateIncrementalMarking(i::Heap * heap)506 static inline void SimulateIncrementalMarking(i::Heap* heap) {
507 i::MarkCompactCollector* collector = heap->mark_compact_collector();
508 i::IncrementalMarking* marking = heap->incremental_marking();
509 if (collector->sweeping_in_progress()) {
510 collector->EnsureSweepingCompleted();
511 }
512 CHECK(marking->IsMarking() || marking->IsStopped());
513 if (marking->IsStopped()) {
514 marking->Start();
515 }
516 CHECK(marking->IsMarking());
517 while (!marking->IsComplete()) {
518 marking->Step(i::MB, i::IncrementalMarking::NO_GC_VIA_STACK_GUARD);
519 }
520 CHECK(marking->IsComplete());
521 }
522
523
524 // Helper class for new allocations tracking and checking.
525 // To use checking of JS allocations tracking in a test,
526 // just create an instance of this class.
527 class HeapObjectsTracker {
528 public:
HeapObjectsTracker()529 HeapObjectsTracker() {
530 heap_profiler_ = i::Isolate::Current()->heap_profiler();
531 CHECK_NE(NULL, heap_profiler_);
532 heap_profiler_->StartHeapObjectsTracking(true);
533 }
534
~HeapObjectsTracker()535 ~HeapObjectsTracker() {
536 i::Isolate::Current()->heap()->CollectAllAvailableGarbage();
537 CHECK_EQ(0, heap_profiler_->heap_object_map()->FindUntrackedObjects());
538 heap_profiler_->StopHeapObjectsTracking();
539 }
540
541 private:
542 i::HeapProfiler* heap_profiler_;
543 };
544
545
546 class InitializedHandleScope {
547 public:
InitializedHandleScope()548 InitializedHandleScope()
549 : main_isolate_(CcTest::InitIsolateOnce()),
550 handle_scope_(main_isolate_) {}
551
552 // Prefixing the below with main_ reduces a lot of naming clashes.
main_isolate()553 i::Isolate* main_isolate() { return main_isolate_; }
554
555 private:
556 i::Isolate* main_isolate_;
557 i::HandleScope handle_scope_;
558 };
559
560
561 class HandleAndZoneScope : public InitializedHandleScope {
562 public:
HandleAndZoneScope()563 HandleAndZoneScope() : main_zone_(main_isolate()) {}
564
565 // Prefixing the below with main_ reduces a lot of naming clashes.
main_zone()566 i::Zone* main_zone() { return &main_zone_; }
567
568 private:
569 i::Zone main_zone_;
570 };
571
572 #endif // ifndef CCTEST_H_
573