• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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