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); 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(); 103 static CcTest* last() { return last_; } 104 CcTest* prev() { return prev_; } 105 const char* file() { return file_; } 106 const char* name() { return name_; } 107 const char* dependency() { return dependency_; } 108 bool enabled() { return enabled_; } 109 110 static v8::Isolate* isolate() { 111 CHECK(isolate_ != NULL); 112 isolate_used_ = true; 113 return isolate_; 114 } 115 116 static i::Isolate* i_isolate() { 117 return reinterpret_cast<i::Isolate*>(isolate()); 118 } 119 120 static i::Heap* heap() { 121 return i_isolate()->heap(); 122 } 123 124 static TestHeap* test_heap() { 125 return reinterpret_cast<TestHeap*>(i_isolate()->heap()); 126 } 127 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. 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 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: 201 explicit ApiTestFuzzer(int num) 202 : Thread("ApiTestFuzzer"), 203 test_number_(num), 204 gate_(0), 205 active_(true) { 206 } 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: 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 } 238 static int count() { return count_; } 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 } 248 CcTest::TestFunction* callback() { return callback_; } 249 ApiTestFuzzer* fuzzer_; 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 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->(); } 288 bool IsReady() { return !context_.IsEmpty(); } 289 290 v8::Local<v8::Context> local() { 291 return v8::Local<v8::Context>::New(isolate_, context_); 292 } 293 294 private: 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 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 323 static inline v8::Local<v8::Value> v8_num(double x) { 324 return v8::Number::New(v8::Isolate::GetCurrent(), x); 325 } 326 327 328 static inline v8::Local<v8::String> v8_str(const char* x) { 329 return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x); 330 } 331 332 333 static inline v8::Local<v8::Script> v8_compile(const char* x) { 334 return v8::Script::Compile(v8_str(x)); 335 } 336 337 338 static inline v8::Local<v8::Script> v8_compile(v8::Local<v8::String> x) { 339 return v8::Script::Compile(x); 340 } 341 342 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 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 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. 365 static inline v8::Local<v8::Value> CompileRun(const char* source) { 366 return v8::Script::Compile(v8_str(source))->Run(); 367 } 368 369 370 static inline v8::Local<v8::Value> CompileRun(v8::Local<v8::String> source) { 371 return v8::Script::Compile(source)->Run(); 372 } 373 374 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. 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 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 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 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 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 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 438 static inline void ExpectTrue(const char* code) { 439 ExpectBoolean(code, true); 440 } 441 442 443 static inline void ExpectFalse(const char* code) { 444 ExpectBoolean(code, false); 445 } 446 447 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 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. 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. 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: 487 HeapObjectsTracker() { 488 heap_profiler_ = i::Isolate::Current()->heap_profiler(); 489 CHECK_NE(NULL, heap_profiler_); 490 heap_profiler_->StartHeapObjectsTracking(true); 491 } 492 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