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 #ifndef TEST
34 #define TEST(Name) \
35 static void Test##Name(); \
36 CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true, true); \
37 static void Test##Name()
38 #endif
39
40 #ifndef UNINITIALIZED_TEST
41 #define UNINITIALIZED_TEST(Name) \
42 static void Test##Name(); \
43 CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true, false); \
44 static void Test##Name()
45 #endif
46
47 #ifndef DEPENDENT_TEST
48 #define DEPENDENT_TEST(Name, Dep) \
49 static void Test##Name(); \
50 CcTest register_test_##Name(Test##Name, __FILE__, #Name, #Dep, true, true); \
51 static void Test##Name()
52 #endif
53
54 #ifndef DISABLED_TEST
55 #define DISABLED_TEST(Name) \
56 static void Test##Name(); \
57 CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, false, true); \
58 static void Test##Name()
59 #endif
60
61 #define EXTENSION_LIST(V) \
62 V(GC_EXTENSION, "v8/gc") \
63 V(PRINT_EXTENSION, "v8/print") \
64 V(PROFILER_EXTENSION, "v8/profiler") \
65 V(TRACE_EXTENSION, "v8/trace")
66
67 #define DEFINE_EXTENSION_ID(Name, Ident) Name##_ID,
68 enum CcTestExtensionIds {
69 EXTENSION_LIST(DEFINE_EXTENSION_ID)
70 kMaxExtensions
71 };
72 #undef DEFINE_EXTENSION_ID
73
74 typedef v8::internal::EnumSet<CcTestExtensionIds> CcTestExtensionFlags;
75 #define DEFINE_EXTENSION_FLAG(Name, Ident) \
76 static const CcTestExtensionFlags Name(1 << Name##_ID);
77 static const CcTestExtensionFlags NO_EXTENSIONS(0);
78 static const CcTestExtensionFlags ALL_EXTENSIONS((1 << kMaxExtensions) - 1);
EXTENSION_LIST(DEFINE_EXTENSION_FLAG)79 EXTENSION_LIST(DEFINE_EXTENSION_FLAG)
80 #undef DEFINE_EXTENSION_FLAG
81
82
83 // Use this to expose protected methods in i::Heap.
84 class TestHeap : public i::Heap {
85 public:
86 using i::Heap::AllocateArgumentsObject;
87 using i::Heap::AllocateByteArray;
88 using i::Heap::AllocateFixedArray;
89 using i::Heap::AllocateHeapNumber;
90 using i::Heap::AllocateJSObject;
91 using i::Heap::AllocateJSObjectFromMap;
92 using i::Heap::AllocateMap;
93 using i::Heap::CopyCode;
94 };
95
96
97 class CcTest {
98 public:
99 typedef void (TestFunction)();
100 CcTest(TestFunction* callback, const char* file, const char* name,
101 const char* dependency, bool enabled, bool initialize);
102 void Run();
last()103 static CcTest* last() { return last_; }
prev()104 CcTest* prev() { return prev_; }
file()105 const char* file() { return file_; }
name()106 const char* name() { return name_; }
dependency()107 const char* dependency() { return dependency_; }
enabled()108 bool enabled() { return enabled_; }
109
isolate()110 static v8::Isolate* isolate() {
111 CHECK(isolate_ != NULL);
112 isolate_used_ = true;
113 return isolate_;
114 }
115
i_isolate()116 static i::Isolate* i_isolate() {
117 return reinterpret_cast<i::Isolate*>(isolate());
118 }
119
heap()120 static i::Heap* heap() {
121 return i_isolate()->heap();
122 }
123
test_heap()124 static TestHeap* test_heap() {
125 return reinterpret_cast<TestHeap*>(i_isolate()->heap());
126 }
127
global()128 static v8::Local<v8::Object> global() {
129 return isolate()->GetCurrentContext()->Global();
130 }
131
132 // TODO(dcarney): Remove.
133 // This must be called first in a test.
InitializeVM()134 static void InitializeVM() {
135 CHECK(!isolate_used_);
136 CHECK(!initialize_called_);
137 initialize_called_ = true;
138 v8::HandleScope handle_scope(CcTest::isolate());
139 v8::Context::New(CcTest::isolate())->Enter();
140 }
141
142 // Only for UNINITIALIZED_TESTs
143 static void DisableAutomaticDispose();
144
145 // Helper function to configure a context.
146 // Must be in a HandleScope.
147 static v8::Local<v8::Context> NewContext(
148 CcTestExtensionFlags extensions,
149 v8::Isolate* isolate = CcTest::isolate());
150
TearDown()151 static void TearDown() {
152 if (isolate_ != NULL) isolate_->Dispose();
153 }
154
155 private:
156 friend int main(int argc, char** argv);
157 TestFunction* callback_;
158 const char* file_;
159 const char* name_;
160 const char* dependency_;
161 bool enabled_;
162 bool initialize_;
163 CcTest* prev_;
164 static CcTest* last_;
165 static v8::Isolate* isolate_;
166 static bool initialize_called_;
167 static bool isolate_used_;
168 };
169
170 // Switches between all the Api tests using the threading support.
171 // In order to get a surprising but repeatable pattern of thread
172 // switching it has extra semaphores to control the order in which
173 // the tests alternate, not relying solely on the big V8 lock.
174 //
175 // A test is augmented with calls to ApiTestFuzzer::Fuzz() in its
176 // callbacks. This will have no effect when we are not running the
177 // thread fuzzing test. In the thread fuzzing test it will
178 // pseudorandomly select a successor thread and switch execution
179 // to that thread, suspending the current test.
180 class ApiTestFuzzer: public v8::internal::Thread {
181 public:
182 void CallTest();
183
184 // The ApiTestFuzzer is also a Thread, so it has a Run method.
185 virtual void Run();
186
187 enum PartOfTest { FIRST_PART,
188 SECOND_PART,
189 THIRD_PART,
190 FOURTH_PART,
191 LAST_PART = FOURTH_PART };
192
193 static void SetUp(PartOfTest part);
194 static void RunAllTests();
195 static void TearDown();
196 // This method switches threads if we are running the Threading test.
197 // Otherwise it does nothing.
198 static void Fuzz();
199
200 private:
ApiTestFuzzer(int num)201 explicit ApiTestFuzzer(int num)
202 : Thread("ApiTestFuzzer"),
203 test_number_(num),
204 gate_(0),
205 active_(true) {
206 }
~ApiTestFuzzer()207 ~ApiTestFuzzer() {}
208
209 static bool fuzzing_;
210 static int tests_being_run_;
211 static int current_;
212 static int active_tests_;
213 static bool NextThread();
214 int test_number_;
215 v8::internal::Semaphore gate_;
216 bool active_;
217 void ContextSwitch();
218 static int GetNextTestNumber();
219 static v8::internal::Semaphore all_tests_done_;
220 };
221
222
223 #define THREADED_TEST(Name) \
224 static void Test##Name(); \
225 RegisterThreadedTest register_##Name(Test##Name, #Name); \
226 /* */ TEST(Name)
227
228
229 class RegisterThreadedTest {
230 public:
RegisterThreadedTest(CcTest::TestFunction * callback,const char * name)231 explicit RegisterThreadedTest(CcTest::TestFunction* callback,
232 const char* name)
233 : fuzzer_(NULL), callback_(callback), name_(name) {
234 prev_ = first_;
235 first_ = this;
236 count_++;
237 }
count()238 static int count() { return count_; }
nth(int i)239 static RegisterThreadedTest* nth(int i) {
240 CHECK(i < count());
241 RegisterThreadedTest* current = first_;
242 while (i > 0) {
243 i--;
244 current = current->prev_;
245 }
246 return current;
247 }
callback()248 CcTest::TestFunction* callback() { return callback_; }
249 ApiTestFuzzer* fuzzer_;
name()250 const char* name() { return name_; }
251
252 private:
253 static RegisterThreadedTest* first_;
254 static int count_;
255 CcTest::TestFunction* callback_;
256 RegisterThreadedTest* prev_;
257 const char* name_;
258 };
259
260 // A LocalContext holds a reference to a v8::Context.
261 class LocalContext {
262 public:
263 LocalContext(v8::Isolate* isolate,
264 v8::ExtensionConfiguration* extensions = 0,
265 v8::Handle<v8::ObjectTemplate> global_template =
266 v8::Handle<v8::ObjectTemplate>(),
267 v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) {
268 Initialize(isolate, extensions, global_template, global_object);
269 }
270
271 LocalContext(v8::ExtensionConfiguration* extensions = 0,
272 v8::Handle<v8::ObjectTemplate> global_template =
273 v8::Handle<v8::ObjectTemplate>(),
274 v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) {
275 Initialize(CcTest::isolate(), extensions, global_template, global_object);
276 }
277
~LocalContext()278 virtual ~LocalContext() {
279 v8::HandleScope scope(isolate_);
280 v8::Local<v8::Context>::New(isolate_, context_)->Exit();
281 context_.Reset();
282 }
283
284 v8::Context* operator->() {
285 return *reinterpret_cast<v8::Context**>(&context_);
286 }
287 v8::Context* operator*() { return operator->(); }
IsReady()288 bool IsReady() { return !context_.IsEmpty(); }
289
local()290 v8::Local<v8::Context> local() {
291 return v8::Local<v8::Context>::New(isolate_, context_);
292 }
293
294 private:
Initialize(v8::Isolate * isolate,v8::ExtensionConfiguration * extensions,v8::Handle<v8::ObjectTemplate> global_template,v8::Handle<v8::Value> global_object)295 void Initialize(v8::Isolate* isolate,
296 v8::ExtensionConfiguration* extensions,
297 v8::Handle<v8::ObjectTemplate> global_template,
298 v8::Handle<v8::Value> global_object) {
299 v8::HandleScope scope(isolate);
300 v8::Local<v8::Context> context = v8::Context::New(isolate,
301 extensions,
302 global_template,
303 global_object);
304 context_.Reset(isolate, context);
305 context->Enter();
306 // We can't do this later perhaps because of a fatal error.
307 isolate_ = isolate;
308 }
309
310 v8::Persistent<v8::Context> context_;
311 v8::Isolate* isolate_;
312 };
313
314
AsciiToTwoByteString(const char * source)315 static inline uint16_t* AsciiToTwoByteString(const char* source) {
316 int array_length = i::StrLength(source) + 1;
317 uint16_t* converted = i::NewArray<uint16_t>(array_length);
318 for (int i = 0; i < array_length; i++) converted[i] = source[i];
319 return converted;
320 }
321
322
v8_num(double x)323 static inline v8::Local<v8::Value> v8_num(double x) {
324 return v8::Number::New(v8::Isolate::GetCurrent(), x);
325 }
326
327
v8_str(const char * x)328 static inline v8::Local<v8::String> v8_str(const char* x) {
329 return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x);
330 }
331
332
v8_compile(const char * x)333 static inline v8::Local<v8::Script> v8_compile(const char* x) {
334 return v8::Script::Compile(v8_str(x));
335 }
336
337
v8_compile(v8::Local<v8::String> x)338 static inline v8::Local<v8::Script> v8_compile(v8::Local<v8::String> x) {
339 return v8::Script::Compile(x);
340 }
341
342
CompileWithOrigin(v8::Local<v8::String> source,v8::Local<v8::String> origin_url)343 static inline v8::Local<v8::Script> CompileWithOrigin(
344 v8::Local<v8::String> source, v8::Local<v8::String> origin_url) {
345 v8::ScriptOrigin origin(origin_url);
346 v8::ScriptCompiler::Source script_source(source, origin);
347 return v8::ScriptCompiler::Compile(
348 v8::Isolate::GetCurrent(), &script_source);
349 }
350
351
CompileWithOrigin(v8::Local<v8::String> source,const char * origin_url)352 static inline v8::Local<v8::Script> CompileWithOrigin(
353 v8::Local<v8::String> source, const char* origin_url) {
354 return CompileWithOrigin(source, v8_str(origin_url));
355 }
356
357
CompileWithOrigin(const char * source,const char * origin_url)358 static inline v8::Local<v8::Script> CompileWithOrigin(const char* source,
359 const char* origin_url) {
360 return CompileWithOrigin(v8_str(source), v8_str(origin_url));
361 }
362
363
364 // Helper functions that compile and run the source.
CompileRun(const char * source)365 static inline v8::Local<v8::Value> CompileRun(const char* source) {
366 return v8::Script::Compile(v8_str(source))->Run();
367 }
368
369
CompileRun(v8::Local<v8::String> source)370 static inline v8::Local<v8::Value> CompileRun(v8::Local<v8::String> source) {
371 return v8::Script::Compile(source)->Run();
372 }
373
374
PreCompileCompileRun(const char * source)375 static inline v8::Local<v8::Value> PreCompileCompileRun(const char* source) {
376 // Compile once just to get the preparse data, then compile the second time
377 // using the data.
378 v8::Isolate* isolate = v8::Isolate::GetCurrent();
379 v8::ScriptCompiler::Source script_source(v8_str(source));
380 v8::ScriptCompiler::Compile(isolate, &script_source,
381 v8::ScriptCompiler::kProduceDataToCache);
382 return v8::ScriptCompiler::Compile(isolate, &script_source)->Run();
383 }
384
385
386 // 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)387 static inline v8::Local<v8::Value> CompileRunWithOrigin(const char* source,
388 const char* origin_url,
389 int line_number,
390 int column_number) {
391 v8::Isolate* isolate = v8::Isolate::GetCurrent();
392 v8::ScriptOrigin origin(v8_str(origin_url),
393 v8::Integer::New(isolate, line_number),
394 v8::Integer::New(isolate, column_number));
395 v8::ScriptCompiler::Source script_source(v8_str(source), origin);
396 return v8::ScriptCompiler::Compile(isolate, &script_source)->Run();
397 }
398
399
CompileRunWithOrigin(v8::Local<v8::String> source,const char * origin_url)400 static inline v8::Local<v8::Value> CompileRunWithOrigin(
401 v8::Local<v8::String> source, const char* origin_url) {
402 v8::ScriptCompiler::Source script_source(
403 source, v8::ScriptOrigin(v8_str(origin_url)));
404 return v8::ScriptCompiler::Compile(v8::Isolate::GetCurrent(), &script_source)
405 ->Run();
406 }
407
408
CompileRunWithOrigin(const char * source,const char * origin_url)409 static inline v8::Local<v8::Value> CompileRunWithOrigin(
410 const char* source, const char* origin_url) {
411 return CompileRunWithOrigin(v8_str(source), origin_url);
412 }
413
414
415
ExpectString(const char * code,const char * expected)416 static inline void ExpectString(const char* code, const char* expected) {
417 v8::Local<v8::Value> result = CompileRun(code);
418 CHECK(result->IsString());
419 v8::String::Utf8Value utf8(result);
420 CHECK_EQ(expected, *utf8);
421 }
422
423
ExpectInt32(const char * code,int expected)424 static inline void ExpectInt32(const char* code, int expected) {
425 v8::Local<v8::Value> result = CompileRun(code);
426 CHECK(result->IsInt32());
427 CHECK_EQ(expected, result->Int32Value());
428 }
429
430
ExpectBoolean(const char * code,bool expected)431 static inline void ExpectBoolean(const char* code, bool expected) {
432 v8::Local<v8::Value> result = CompileRun(code);
433 CHECK(result->IsBoolean());
434 CHECK_EQ(expected, result->BooleanValue());
435 }
436
437
ExpectTrue(const char * code)438 static inline void ExpectTrue(const char* code) {
439 ExpectBoolean(code, true);
440 }
441
442
ExpectFalse(const char * code)443 static inline void ExpectFalse(const char* code) {
444 ExpectBoolean(code, false);
445 }
446
447
ExpectObject(const char * code,v8::Local<v8::Value> expected)448 static inline void ExpectObject(const char* code,
449 v8::Local<v8::Value> expected) {
450 v8::Local<v8::Value> result = CompileRun(code);
451 CHECK(result->SameValue(expected));
452 }
453
454
ExpectUndefined(const char * code)455 static inline void ExpectUndefined(const char* code) {
456 v8::Local<v8::Value> result = CompileRun(code);
457 CHECK(result->IsUndefined());
458 }
459
460
461 // Helper function that simulates a full new-space in the heap.
SimulateFullSpace(v8::internal::NewSpace * space)462 static inline void SimulateFullSpace(v8::internal::NewSpace* space) {
463 int new_linear_size = static_cast<int>(
464 *space->allocation_limit_address() - *space->allocation_top_address());
465 if (new_linear_size == 0) return;
466 v8::internal::AllocationResult allocation =
467 space->AllocateRaw(new_linear_size);
468 v8::internal::FreeListNode* node =
469 v8::internal::FreeListNode::cast(allocation.ToObjectChecked());
470 node->set_size(space->heap(), new_linear_size);
471 }
472
473
474 // Helper function that simulates a full old-space in the heap.
SimulateFullSpace(v8::internal::PagedSpace * space)475 static inline void SimulateFullSpace(v8::internal::PagedSpace* space) {
476 space->EmptyAllocationInfo();
477 space->ResetFreeList();
478 space->ClearStats();
479 }
480
481
482 // Helper class for new allocations tracking and checking.
483 // To use checking of JS allocations tracking in a test,
484 // just create an instance of this class.
485 class HeapObjectsTracker {
486 public:
HeapObjectsTracker()487 HeapObjectsTracker() {
488 heap_profiler_ = i::Isolate::Current()->heap_profiler();
489 CHECK_NE(NULL, heap_profiler_);
490 heap_profiler_->StartHeapObjectsTracking(true);
491 }
492
~HeapObjectsTracker()493 ~HeapObjectsTracker() {
494 i::Isolate::Current()->heap()->CollectAllAvailableGarbage();
495 CHECK_EQ(0, heap_profiler_->heap_object_map()->FindUntrackedObjects());
496 heap_profiler_->StopHeapObjectsTracking();
497 }
498
499 private:
500 i::HeapProfiler* heap_profiler_;
501 };
502
503
504 #endif // ifndef CCTEST_H_
505