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 <memory>
32 
33 #include "include/libplatform/libplatform.h"
34 #include "include/v8-platform.h"
35 #include "src/base/enum-set.h"
36 #include "src/codegen/register-configuration.h"
37 #include "src/debug/debug-interface.h"
38 #include "src/execution/isolate.h"
39 #include "src/execution/simulator.h"
40 #include "src/flags/flags.h"
41 #include "src/heap/factory.h"
42 #include "src/init/v8.h"
43 #include "src/objects/js-function.h"
44 #include "src/objects/objects.h"
45 #include "src/zone/accounting-allocator.h"
46 
47 namespace v8 {
48 namespace base {
49 
50 class RandomNumberGenerator;
51 
52 }  // namespace base
53 
54 namespace internal {
55 
56 const auto GetRegConfig = RegisterConfiguration::Default;
57 
58 class HandleScope;
59 class Zone;
60 
61 namespace compiler {
62 
63 class JSHeapBroker;
64 
65 }  // namespace compiler
66 
67 }  // namespace internal
68 
69 }  // namespace v8
70 
71 #ifndef TEST
72 #define TEST(Name)                                                      \
73   static void Test##Name();                                             \
74   CcTest register_test_##Name(Test##Name, __FILE__, #Name, true, true); \
75   static void Test##Name()
76 #endif
77 
78 #ifndef UNINITIALIZED_TEST
79 #define UNINITIALIZED_TEST(Name)                                         \
80   static void Test##Name();                                              \
81   CcTest register_test_##Name(Test##Name, __FILE__, #Name, true, false); \
82   static void Test##Name()
83 #endif
84 
85 #ifndef DISABLED_TEST
86 #define DISABLED_TEST(Name)                                              \
87   static void Test##Name();                                              \
88   CcTest register_test_##Name(Test##Name, __FILE__, #Name, false, true); \
89   static void Test##Name()
90 #endif
91 
92 // Similar to TEST, but used when test definitions appear as members of a
93 // (probably parameterized) class. This allows re-using the given tests multiple
94 // times. For this to work, the following conditions must hold:
95 //   1. The class has a template parameter named kTestFileName of type  char
96 //      const*, which is instantiated with __FILE__ at the *use site*, in order
97 //      to correctly associate the tests with the test suite using them.
98 //   2. To actually execute the tests, create an instance of the class
99 //      containing the MEMBER_TESTs.
100 #define MEMBER_TEST(Name)                                   \
101   CcTest register_test_##Name =                             \
102       CcTest(Test##Name, kTestFileName, #Name, true, true); \
103   static void Test##Name()
104 
105 #define EXTENSION_LIST(V)                                                      \
106   V(GC_EXTENSION,       "v8/gc")                                               \
107   V(PRINT_EXTENSION,    "v8/print")                                            \
108   V(PROFILER_EXTENSION, "v8/profiler")                                         \
109   V(TRACE_EXTENSION,    "v8/trace")
110 
111 #define DEFINE_EXTENSION_ID(Name, Ident) Name##_ID,
112 enum CcTestExtensionId { EXTENSION_LIST(DEFINE_EXTENSION_ID) kMaxExtensions };
113 #undef DEFINE_EXTENSION_ID
114 
115 using CcTestExtensionFlags = v8::base::EnumSet<CcTestExtensionId>;
116 
117 #define DEFINE_EXTENSION_NAME(Name, Ident) Ident,
118 static constexpr const char* kExtensionName[kMaxExtensions] = {
119     EXTENSION_LIST(DEFINE_EXTENSION_NAME)};
120 #undef DEFINE_EXTENSION_NAME
121 
122 class CcTest {
123  public:
124   using TestFunction = void();
125   CcTest(TestFunction* callback, const char* file, const char* name,
126          bool enabled, bool initialize);
~CcTest()127   ~CcTest() { i::DeleteArray(file_); }
128   void Run();
last()129   static CcTest* last() { return last_; }
prev()130   CcTest* prev() { return prev_; }
file()131   const char* file() { return file_; }
name()132   const char* name() { return name_; }
enabled()133   bool enabled() { return enabled_; }
134 
isolate()135   static v8::Isolate* isolate() {
136     CHECK_NOT_NULL(isolate_);
137     v8::base::Relaxed_Store(&isolate_used_, 1);
138     return isolate_;
139   }
140 
InitIsolateOnce()141   static i::Isolate* InitIsolateOnce() {
142     if (!initialize_called_) InitializeVM();
143     return i_isolate();
144   }
145 
i_isolate()146   static i::Isolate* i_isolate() {
147     return reinterpret_cast<i::Isolate*>(isolate());
148   }
149 
150   static i::Heap* heap();
151   static i::ReadOnlyHeap* read_only_heap();
152 
153   static void AddGlobalFunction(v8::Local<v8::Context> env, const char* name,
154                                 v8::FunctionCallback callback);
155   static void CollectGarbage(i::AllocationSpace space,
156                              i::Isolate* isolate = nullptr);
157   static void CollectAllGarbage(i::Isolate* isolate = nullptr);
158   static void CollectAllAvailableGarbage(i::Isolate* isolate = nullptr);
159   static void PreciseCollectAllGarbage(i::Isolate* isolate = nullptr);
160 
161   static i::Handle<i::String> MakeString(const char* str);
162   static i::Handle<i::String> MakeName(const char* str, int suffix);
163 
164   static v8::base::RandomNumberGenerator* random_number_generator();
165 
166   static v8::Local<v8::Object> global();
167 
array_buffer_allocator()168   static v8::ArrayBuffer::Allocator* array_buffer_allocator() {
169     return allocator_;
170   }
171 
set_array_buffer_allocator(v8::ArrayBuffer::Allocator * allocator)172   static void set_array_buffer_allocator(
173       v8::ArrayBuffer::Allocator* allocator) {
174     allocator_ = allocator;
175   }
176 
177   // TODO(dcarney): Remove.
178   // This must be called first in a test.
179   static void InitializeVM();
180 
181   // Only for UNINITIALIZED_TESTs
182   static void DisableAutomaticDispose();
183 
184   // Helper function to configure a context.
185   // Must be in a HandleScope.
186   static v8::Local<v8::Context> NewContext(
187       v8::Isolate* isolate = CcTest::isolate()) {
188     return NewContext({}, isolate);
189   }
190   static v8::Local<v8::Context> NewContext(
191       CcTestExtensionFlags extension_flags,
192       v8::Isolate* isolate = CcTest::isolate());
193   static v8::Local<v8::Context> NewContext(
194       std::initializer_list<CcTestExtensionId> extensions,
195       v8::Isolate* isolate = CcTest::isolate()) {
196     return NewContext(CcTestExtensionFlags{extensions}, isolate);
197   }
198 
199   static void TearDown();
200 
201  private:
202   friend int main(int argc, char** argv);
203   TestFunction* callback_;
204   const char* file_;
205   const char* name_;
206   bool enabled_;
207   bool initialize_;
208   CcTest* prev_;
209   static CcTest* last_;
210   static v8::ArrayBuffer::Allocator* allocator_;
211   static v8::Isolate* isolate_;
212   static bool initialize_called_;
213   static v8::base::Atomic32 isolate_used_;
214 };
215 
216 // Switches between all the Api tests using the threading support.
217 // In order to get a surprising but repeatable pattern of thread
218 // switching it has extra semaphores to control the order in which
219 // the tests alternate, not relying solely on the big V8 lock.
220 //
221 // A test is augmented with calls to ApiTestFuzzer::Fuzz() in its
222 // callbacks.  This will have no effect when we are not running the
223 // thread fuzzing test.  In the thread fuzzing test it will
224 // pseudorandomly select a successor thread and switch execution
225 // to that thread, suspending the current test.
226 class ApiTestFuzzer: public v8::base::Thread {
227  public:
228   void CallTest();
229 
230   // The ApiTestFuzzer is also a Thread, so it has a Run method.
231   void Run() override;
232 
233   enum PartOfTest {
234     FIRST_PART,
235     SECOND_PART,
236     THIRD_PART,
237     FOURTH_PART,
238     FIFTH_PART,
239     SIXTH_PART,
240     SEVENTH_PART,
241     EIGHTH_PART,
242     LAST_PART = EIGHTH_PART
243   };
244 
245   static void SetUp(PartOfTest part);
246   static void RunAllTests();
247   static void TearDown();
248   // This method switches threads if we are running the Threading test.
249   // Otherwise it does nothing.
250   static void Fuzz();
251 
252  private:
ApiTestFuzzer(int num)253   explicit ApiTestFuzzer(int num)
254       : Thread(Options("ApiTestFuzzer")),
255         test_number_(num),
256         gate_(0),
257         active_(true) {}
258   ~ApiTestFuzzer() override = default;
259 
260   static bool fuzzing_;
261   static int tests_being_run_;
262   static int current_;
263   static int active_tests_;
264   static bool NextThread();
265   int test_number_;
266   v8::base::Semaphore gate_;
267   bool active_;
268   void ContextSwitch();
269   static int GetNextTestNumber();
270   static v8::base::Semaphore all_tests_done_;
271 };
272 
273 
274 #define THREADED_TEST(Name)                                          \
275   static void Test##Name();                                          \
276   RegisterThreadedTest register_##Name(Test##Name, #Name);           \
277   /* */ TEST(Name)
278 
279 class RegisterThreadedTest {
280  public:
RegisterThreadedTest(CcTest::TestFunction * callback,const char * name)281   explicit RegisterThreadedTest(CcTest::TestFunction* callback,
282                                 const char* name)
283       : fuzzer_(nullptr), callback_(callback), name_(name) {
284     prev_ = first_;
285     first_ = this;
286     count_++;
287   }
count()288   static int count() { return count_; }
nth(int i)289   static RegisterThreadedTest* nth(int i) {
290     CHECK(i < count());
291     RegisterThreadedTest* current = first_;
292     while (i > 0) {
293       i--;
294       current = current->prev_;
295     }
296     return current;
297   }
callback()298   CcTest::TestFunction* callback() { return callback_; }
299   ApiTestFuzzer* fuzzer_;
name()300   const char* name() { return name_; }
301 
302  private:
303   static RegisterThreadedTest* first_;
304   static int count_;
305   CcTest::TestFunction* callback_;
306   RegisterThreadedTest* prev_;
307   const char* name_;
308 };
309 
310 // A LocalContext holds a reference to a v8::Context.
311 class LocalContext {
312  public:
313   LocalContext(v8::Isolate* isolate,
314                v8::ExtensionConfiguration* extensions = nullptr,
315                v8::Local<v8::ObjectTemplate> global_template =
316                    v8::Local<v8::ObjectTemplate>(),
317                v8::Local<v8::Value> global_object = v8::Local<v8::Value>()) {
318     Initialize(isolate, extensions, global_template, global_object);
319   }
320 
321   LocalContext(v8::ExtensionConfiguration* extensions = nullptr,
322                v8::Local<v8::ObjectTemplate> global_template =
323                    v8::Local<v8::ObjectTemplate>(),
324                v8::Local<v8::Value> global_object = v8::Local<v8::Value>()) {
325     Initialize(CcTest::isolate(), extensions, global_template, global_object);
326   }
327 
328   virtual ~LocalContext();
329 
330   v8::Context* operator->() {
331     return *reinterpret_cast<v8::Context**>(&context_);
332   }
333   v8::Context* operator*() { return operator->(); }
IsReady()334   bool IsReady() { return !context_.IsEmpty(); }
335 
local()336   v8::Local<v8::Context> local() const {
337     return v8::Local<v8::Context>::New(isolate_, context_);
338   }
339 
340  private:
341   void Initialize(v8::Isolate* isolate, v8::ExtensionConfiguration* extensions,
342                   v8::Local<v8::ObjectTemplate> global_template,
343                   v8::Local<v8::Value> global_object);
344 
345   v8::Persistent<v8::Context> context_;
346   v8::Isolate* isolate_;
347 };
348 
349 
AsciiToTwoByteString(const char * source)350 static inline uint16_t* AsciiToTwoByteString(const char* source) {
351   size_t array_length = strlen(source) + 1;
352   uint16_t* converted = i::NewArray<uint16_t>(array_length);
353   for (size_t i = 0; i < array_length; i++) converted[i] = source[i];
354   return converted;
355 }
356 
357 template <typename T>
GetGlobal(const char * name)358 static inline i::Handle<T> GetGlobal(const char* name) {
359   i::Isolate* isolate = CcTest::i_isolate();
360   i::Handle<i::String> str_name =
361       isolate->factory()->InternalizeUtf8String(name);
362 
363   i::Handle<i::Object> value =
364       i::Object::GetProperty(isolate, isolate->global_object(), str_name)
365           .ToHandleChecked();
366   return i::Handle<T>::cast(value);
367 }
368 
v8_bool(bool val)369 static inline v8::Local<v8::Boolean> v8_bool(bool val) {
370   return v8::Boolean::New(v8::Isolate::GetCurrent(), val);
371 }
372 
v8_num(double x)373 static inline v8::Local<v8::Value> v8_num(double x) {
374   return v8::Number::New(v8::Isolate::GetCurrent(), x);
375 }
376 
v8_int(int32_t x)377 static inline v8::Local<v8::Integer> v8_int(int32_t x) {
378   return v8::Integer::New(v8::Isolate::GetCurrent(), x);
379 }
380 
v8_bigint(int64_t x)381 static inline v8::Local<v8::BigInt> v8_bigint(int64_t x) {
382   return v8::BigInt::New(v8::Isolate::GetCurrent(), x);
383 }
384 
v8_str(const char * x)385 static inline v8::Local<v8::String> v8_str(const char* x) {
386   return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x).ToLocalChecked();
387 }
388 
389 
v8_str(v8::Isolate * isolate,const char * x)390 static inline v8::Local<v8::String> v8_str(v8::Isolate* isolate,
391                                            const char* x) {
392   return v8::String::NewFromUtf8(isolate, x).ToLocalChecked();
393 }
394 
395 
v8_symbol(const char * name)396 static inline v8::Local<v8::Symbol> v8_symbol(const char* name) {
397   return v8::Symbol::New(v8::Isolate::GetCurrent(), v8_str(name));
398 }
399 
400 
v8_compile(v8::Local<v8::String> x)401 static inline v8::Local<v8::Script> v8_compile(v8::Local<v8::String> x) {
402   v8::Local<v8::Script> result;
403   CHECK(v8::Script::Compile(v8::Isolate::GetCurrent()->GetCurrentContext(), x)
404             .ToLocal(&result));
405   return result;
406 }
407 
v8_compile(const char * x)408 static inline v8::Local<v8::Script> v8_compile(const char* x) {
409   return v8_compile(v8_str(x));
410 }
411 
v8_try_compile(v8::Local<v8::String> x)412 static inline v8::MaybeLocal<v8::Script> v8_try_compile(
413     v8::Local<v8::String> x) {
414   return v8::Script::Compile(v8::Isolate::GetCurrent()->GetCurrentContext(), x);
415 }
416 
v8_try_compile(const char * x)417 static inline v8::MaybeLocal<v8::Script> v8_try_compile(const char* x) {
418   return v8_try_compile(v8_str(x));
419 }
420 
v8_run_int32value(v8::Local<v8::Script> script)421 static inline int32_t v8_run_int32value(v8::Local<v8::Script> script) {
422   v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext();
423   return script->Run(context).ToLocalChecked()->Int32Value(context).FromJust();
424 }
425 
CompileWithOrigin(v8::Local<v8::String> source,v8::Local<v8::String> origin_url,bool is_shared_cross_origin)426 static inline v8::Local<v8::Script> CompileWithOrigin(
427     v8::Local<v8::String> source, v8::Local<v8::String> origin_url,
428     bool is_shared_cross_origin) {
429   v8::Isolate* isolate = v8::Isolate::GetCurrent();
430   v8::ScriptOrigin origin(isolate, origin_url, 0, 0, is_shared_cross_origin);
431   v8::ScriptCompiler::Source script_source(source, origin);
432   return v8::ScriptCompiler::Compile(isolate->GetCurrentContext(),
433                                      &script_source)
434       .ToLocalChecked();
435 }
436 
CompileWithOrigin(v8::Local<v8::String> source,const char * origin_url,bool is_shared_cross_origin)437 static inline v8::Local<v8::Script> CompileWithOrigin(
438     v8::Local<v8::String> source, const char* origin_url,
439     bool is_shared_cross_origin) {
440   return CompileWithOrigin(source, v8_str(origin_url), is_shared_cross_origin);
441 }
442 
CompileWithOrigin(const char * source,const char * origin_url,bool is_shared_cross_origin)443 static inline v8::Local<v8::Script> CompileWithOrigin(
444     const char* source, const char* origin_url, bool is_shared_cross_origin) {
445   return CompileWithOrigin(v8_str(source), v8_str(origin_url),
446                            is_shared_cross_origin);
447 }
448 
449 // Helper functions that compile and run the source.
CompileRun(v8::Local<v8::Context> context,const char * source)450 static inline v8::MaybeLocal<v8::Value> CompileRun(
451     v8::Local<v8::Context> context, const char* source) {
452   return v8::Script::Compile(context, v8_str(source))
453       .ToLocalChecked()
454       ->Run(context);
455 }
456 
457 
CompileRunChecked(v8::Isolate * isolate,const char * source)458 static inline v8::Local<v8::Value> CompileRunChecked(v8::Isolate* isolate,
459                                                      const char* source) {
460   v8::Local<v8::String> source_string =
461       v8::String::NewFromUtf8(isolate, source).ToLocalChecked();
462   v8::Local<v8::Context> context = isolate->GetCurrentContext();
463   v8::Local<v8::Script> script =
464       v8::Script::Compile(context, source_string).ToLocalChecked();
465   return script->Run(context).ToLocalChecked();
466 }
467 
468 
CompileRun(v8::Local<v8::String> source)469 static inline v8::Local<v8::Value> CompileRun(v8::Local<v8::String> source) {
470   v8::Local<v8::Value> result;
471   if (v8_compile(source)
472           ->Run(v8::Isolate::GetCurrent()->GetCurrentContext())
473           .ToLocal(&result)) {
474     return result;
475   }
476   return v8::Local<v8::Value>();
477 }
478 
479 
480 // Helper functions that compile and run the source.
CompileRun(const char * source)481 static inline v8::Local<v8::Value> CompileRun(const char* source) {
482   return CompileRun(v8_str(source));
483 }
484 
485 
CompileRun(v8::Local<v8::Context> context,v8::ScriptCompiler::Source * script_source,v8::ScriptCompiler::CompileOptions options)486 static inline v8::Local<v8::Value> CompileRun(
487     v8::Local<v8::Context> context, v8::ScriptCompiler::Source* script_source,
488     v8::ScriptCompiler::CompileOptions options) {
489   v8::Local<v8::Value> result;
490   if (v8::ScriptCompiler::Compile(context, script_source, options)
491           .ToLocalChecked()
492           ->Run(context)
493           .ToLocal(&result)) {
494     return result;
495   }
496   return v8::Local<v8::Value>();
497 }
498 
499 
500 // 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)501 static inline v8::Local<v8::Value> CompileRunWithOrigin(const char* source,
502                                                         const char* origin_url,
503                                                         int line_number,
504                                                         int column_number) {
505   v8::Isolate* isolate = v8::Isolate::GetCurrent();
506   v8::Local<v8::Context> context = isolate->GetCurrentContext();
507   v8::ScriptOrigin origin(isolate, v8_str(origin_url), line_number,
508                           column_number);
509   v8::ScriptCompiler::Source script_source(v8_str(source), origin);
510   return CompileRun(context, &script_source,
511                     v8::ScriptCompiler::CompileOptions());
512 }
513 
514 
CompileRunWithOrigin(v8::Local<v8::String> source,const char * origin_url)515 static inline v8::Local<v8::Value> CompileRunWithOrigin(
516     v8::Local<v8::String> source, const char* origin_url) {
517   v8::Isolate* isolate = v8::Isolate::GetCurrent();
518   v8::Local<v8::Context> context = isolate->GetCurrentContext();
519   v8::ScriptCompiler::Source script_source(
520       source, v8::ScriptOrigin(isolate, v8_str(origin_url)));
521   return CompileRun(context, &script_source,
522                     v8::ScriptCompiler::CompileOptions());
523 }
524 
525 
CompileRunWithOrigin(const char * source,const char * origin_url)526 static inline v8::Local<v8::Value> CompileRunWithOrigin(
527     const char* source, const char* origin_url) {
528   return CompileRunWithOrigin(v8_str(source), origin_url);
529 }
530 
531 // Takes a JSFunction and runs it through the test version of the optimizing
532 // pipeline, allocating the temporary compilation artifacts in a given Zone.
533 // For possible {flags} values, look at OptimizedCompilationInfo::Flag.  If
534 // {out_broker} is not nullptr, returns the JSHeapBroker via that (transferring
535 // ownership to the caller).
536 i::Handle<i::JSFunction> Optimize(
537     i::Handle<i::JSFunction> function, i::Zone* zone, i::Isolate* isolate,
538     uint32_t flags,
539     std::unique_ptr<i::compiler::JSHeapBroker>* out_broker = nullptr);
540 
ExpectString(const char * code,const char * expected)541 static inline void ExpectString(const char* code, const char* expected) {
542   v8::Local<v8::Value> result = CompileRun(code);
543   CHECK(result->IsString());
544   v8::String::Utf8Value utf8(v8::Isolate::GetCurrent(), result);
545   CHECK_EQ(0, strcmp(expected, *utf8));
546 }
547 
548 
ExpectInt32(const char * code,int expected)549 static inline void ExpectInt32(const char* code, int expected) {
550   v8::Local<v8::Value> result = CompileRun(code);
551   CHECK(result->IsInt32());
552   CHECK_EQ(expected,
553            result->Int32Value(v8::Isolate::GetCurrent()->GetCurrentContext())
554                .FromJust());
555 }
556 
557 
ExpectBoolean(const char * code,bool expected)558 static inline void ExpectBoolean(const char* code, bool expected) {
559   v8::Local<v8::Value> result = CompileRun(code);
560   CHECK(result->IsBoolean());
561   CHECK_EQ(expected, result->BooleanValue(v8::Isolate::GetCurrent()));
562 }
563 
564 
ExpectTrue(const char * code)565 static inline void ExpectTrue(const char* code) {
566   ExpectBoolean(code, true);
567 }
568 
569 
ExpectFalse(const char * code)570 static inline void ExpectFalse(const char* code) {
571   ExpectBoolean(code, false);
572 }
573 
574 
ExpectObject(const char * code,v8::Local<v8::Value> expected)575 static inline void ExpectObject(const char* code,
576                                 v8::Local<v8::Value> expected) {
577   v8::Local<v8::Value> result = CompileRun(code);
578   CHECK(result->SameValue(expected));
579 }
580 
581 
ExpectUndefined(const char * code)582 static inline void ExpectUndefined(const char* code) {
583   v8::Local<v8::Value> result = CompileRun(code);
584   CHECK(result->IsUndefined());
585 }
586 
587 
ExpectNull(const char * code)588 static inline void ExpectNull(const char* code) {
589   v8::Local<v8::Value> result = CompileRun(code);
590   CHECK(result->IsNull());
591 }
592 
593 
CheckDoubleEquals(double expected,double actual)594 static inline void CheckDoubleEquals(double expected, double actual) {
595   const double kEpsilon = 1e-10;
596   CHECK_LE(expected, actual + kEpsilon);
597   CHECK_GE(expected, actual - kEpsilon);
598 }
599 
600 static v8::debug::DebugDelegate dummy_delegate;
601 
EnableDebugger(v8::Isolate * isolate)602 static inline void EnableDebugger(v8::Isolate* isolate) {
603   v8::debug::SetDebugDelegate(isolate, &dummy_delegate);
604 }
605 
606 
DisableDebugger(v8::Isolate * isolate)607 static inline void DisableDebugger(v8::Isolate* isolate) {
608   v8::debug::SetDebugDelegate(isolate, nullptr);
609 }
610 
611 
EmptyMessageQueues(v8::Isolate * isolate)612 static inline void EmptyMessageQueues(v8::Isolate* isolate) {
613   while (v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(),
614                                        isolate)) {
615   }
616 }
617 
618 class InitializedHandleScopeImpl;
619 
620 class V8_NODISCARD InitializedHandleScope {
621  public:
622   explicit InitializedHandleScope(i::Isolate* isolate = nullptr);
623   ~InitializedHandleScope();
624 
625   // Prefixing the below with main_ reduces a lot of naming clashes.
main_isolate()626   i::Isolate* main_isolate() { return main_isolate_; }
627 
628  private:
629   i::Isolate* main_isolate_;
630   std::unique_ptr<InitializedHandleScopeImpl> initialized_handle_scope_impl_;
631 };
632 
633 class V8_NODISCARD HandleAndZoneScope : public InitializedHandleScope {
634  public:
635   explicit HandleAndZoneScope(bool support_zone_compression = false);
636   ~HandleAndZoneScope();
637 
638   // Prefixing the below with main_ reduces a lot of naming clashes.
main_zone()639   i::Zone* main_zone() { return main_zone_.get(); }
640 
641  private:
642   v8::internal::AccountingAllocator allocator_;
643   std::unique_ptr<i::Zone> main_zone_;
644 };
645 
646 class StaticOneByteResource : public v8::String::ExternalOneByteStringResource {
647  public:
StaticOneByteResource(const char * data)648   explicit StaticOneByteResource(const char* data) : data_(data) {}
649 
650   ~StaticOneByteResource() override = default;
651 
data()652   const char* data() const override { return data_; }
653 
length()654   size_t length() const override { return strlen(data_); }
655 
656  private:
657   const char* data_;
658 };
659 
660 class V8_NODISCARD ManualGCScope {
661  public:
ManualGCScope()662   ManualGCScope()
663       : flag_concurrent_marking_(i::FLAG_concurrent_marking),
664         flag_concurrent_sweeping_(i::FLAG_concurrent_sweeping),
665         flag_stress_concurrent_allocation_(
666             i::FLAG_stress_concurrent_allocation),
667         flag_stress_incremental_marking_(i::FLAG_stress_incremental_marking),
668         flag_parallel_marking_(i::FLAG_parallel_marking),
669         flag_detect_ineffective_gcs_near_heap_limit_(
670             i::FLAG_detect_ineffective_gcs_near_heap_limit) {
671     i::FLAG_concurrent_marking = false;
672     i::FLAG_concurrent_sweeping = false;
673     i::FLAG_stress_incremental_marking = false;
674     i::FLAG_stress_concurrent_allocation = false;
675     // Parallel marking has a dependency on concurrent marking.
676     i::FLAG_parallel_marking = false;
677     i::FLAG_detect_ineffective_gcs_near_heap_limit = false;
678   }
~ManualGCScope()679   ~ManualGCScope() {
680     i::FLAG_concurrent_marking = flag_concurrent_marking_;
681     i::FLAG_concurrent_sweeping = flag_concurrent_sweeping_;
682     i::FLAG_stress_concurrent_allocation = flag_stress_concurrent_allocation_;
683     i::FLAG_stress_incremental_marking = flag_stress_incremental_marking_;
684     i::FLAG_parallel_marking = flag_parallel_marking_;
685     i::FLAG_detect_ineffective_gcs_near_heap_limit =
686         flag_detect_ineffective_gcs_near_heap_limit_;
687   }
688 
689  private:
690   bool flag_concurrent_marking_;
691   bool flag_concurrent_sweeping_;
692   bool flag_stress_concurrent_allocation_;
693   bool flag_stress_incremental_marking_;
694   bool flag_parallel_marking_;
695   bool flag_detect_ineffective_gcs_near_heap_limit_;
696 };
697 
698 // This is an abstract base class that can be overridden to implement a test
699 // platform. It delegates all operations to a given platform at the time
700 // of construction.
701 class TestPlatform : public v8::Platform {
702  public:
703   TestPlatform(const TestPlatform&) = delete;
704   TestPlatform& operator=(const TestPlatform&) = delete;
705 
706   // v8::Platform implementation.
GetPageAllocator()707   v8::PageAllocator* GetPageAllocator() override {
708     return old_platform_->GetPageAllocator();
709   }
710 
OnCriticalMemoryPressure()711   void OnCriticalMemoryPressure() override {
712     old_platform_->OnCriticalMemoryPressure();
713   }
714 
OnCriticalMemoryPressure(size_t length)715   bool OnCriticalMemoryPressure(size_t length) override {
716     return old_platform_->OnCriticalMemoryPressure(length);
717   }
718 
NumberOfWorkerThreads()719   int NumberOfWorkerThreads() override {
720     return old_platform_->NumberOfWorkerThreads();
721   }
722 
GetForegroundTaskRunner(v8::Isolate * isolate)723   std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
724       v8::Isolate* isolate) override {
725     return old_platform_->GetForegroundTaskRunner(isolate);
726   }
727 
CallOnWorkerThread(std::unique_ptr<v8::Task> task)728   void CallOnWorkerThread(std::unique_ptr<v8::Task> task) override {
729     old_platform_->CallOnWorkerThread(std::move(task));
730   }
731 
CallDelayedOnWorkerThread(std::unique_ptr<v8::Task> task,double delay_in_seconds)732   void CallDelayedOnWorkerThread(std::unique_ptr<v8::Task> task,
733                                  double delay_in_seconds) override {
734     old_platform_->CallDelayedOnWorkerThread(std::move(task), delay_in_seconds);
735   }
736 
PostJob(v8::TaskPriority priority,std::unique_ptr<v8::JobTask> job_task)737   std::unique_ptr<v8::JobHandle> PostJob(
738       v8::TaskPriority priority,
739       std::unique_ptr<v8::JobTask> job_task) override {
740     return old_platform_->PostJob(priority, std::move(job_task));
741   }
742 
MonotonicallyIncreasingTime()743   double MonotonicallyIncreasingTime() override {
744     return old_platform_->MonotonicallyIncreasingTime();
745   }
746 
CurrentClockTimeMillis()747   double CurrentClockTimeMillis() override {
748     return old_platform_->CurrentClockTimeMillis();
749   }
750 
IdleTasksEnabled(v8::Isolate * isolate)751   bool IdleTasksEnabled(v8::Isolate* isolate) override {
752     return old_platform_->IdleTasksEnabled(isolate);
753   }
754 
GetTracingController()755   v8::TracingController* GetTracingController() override {
756     return old_platform_->GetTracingController();
757   }
758 
759  protected:
TestPlatform()760   TestPlatform() : old_platform_(i::V8::GetCurrentPlatform()) {}
~TestPlatform()761   ~TestPlatform() override { i::V8::SetPlatformForTesting(old_platform_); }
762 
old_platform()763   v8::Platform* old_platform() const { return old_platform_; }
764 
765  private:
766   v8::Platform* old_platform_;
767 };
768 
769 #if defined(USE_SIMULATOR)
770 class SimulatorHelper {
771  public:
Init(v8::Isolate * isolate)772   inline bool Init(v8::Isolate* isolate) {
773     simulator_ = reinterpret_cast<v8::internal::Isolate*>(isolate)
774                      ->thread_local_top()
775                      ->simulator_;
776     // Check if there is active simulator.
777     return simulator_ != nullptr;
778   }
779 
FillRegisters(v8::RegisterState * state)780   inline void FillRegisters(v8::RegisterState* state) {
781 #if V8_TARGET_ARCH_ARM
782     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
783     state->sp = reinterpret_cast<void*>(
784         simulator_->get_register(v8::internal::Simulator::sp));
785     state->fp = reinterpret_cast<void*>(
786         simulator_->get_register(v8::internal::Simulator::r11));
787     state->lr = reinterpret_cast<void*>(
788         simulator_->get_register(v8::internal::Simulator::lr));
789 #elif V8_TARGET_ARCH_ARM64
790     if (simulator_->sp() == 0 || simulator_->fp() == 0) {
791       // It's possible that the simulator is interrupted while it is updating
792       // the sp or fp register. ARM64 simulator does this in two steps:
793       // first setting it to zero and then setting it to a new value.
794       // Bailout if sp/fp doesn't contain the new value.
795       return;
796     }
797     state->pc = reinterpret_cast<void*>(simulator_->pc());
798     state->sp = reinterpret_cast<void*>(simulator_->sp());
799     state->fp = reinterpret_cast<void*>(simulator_->fp());
800     state->lr = reinterpret_cast<void*>(simulator_->lr());
801 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
802     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
803     state->sp = reinterpret_cast<void*>(
804         simulator_->get_register(v8::internal::Simulator::sp));
805     state->fp = reinterpret_cast<void*>(
806         simulator_->get_register(v8::internal::Simulator::fp));
807 #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
808     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
809     state->sp = reinterpret_cast<void*>(
810         simulator_->get_register(v8::internal::Simulator::sp));
811     state->fp = reinterpret_cast<void*>(
812         simulator_->get_register(v8::internal::Simulator::fp));
813     state->lr = reinterpret_cast<void*>(simulator_->get_lr());
814 #elif V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
815     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
816     state->sp = reinterpret_cast<void*>(
817         simulator_->get_register(v8::internal::Simulator::sp));
818     state->fp = reinterpret_cast<void*>(
819         simulator_->get_register(v8::internal::Simulator::fp));
820     state->lr = reinterpret_cast<void*>(
821         simulator_->get_register(v8::internal::Simulator::ra));
822 #endif
823   }
824 
825  private:
826   v8::internal::Simulator* simulator_;
827 };
828 #endif  // USE_SIMULATOR
829 
830 // The following should correspond to Chromium's kV8DOMWrapperTypeIndex and
831 // kV8DOMWrapperObjectIndex.
832 static const int kV8WrapperTypeIndex = 0;
833 static const int kV8WrapperObjectIndex = 1;
834 
835 enum class ApiCheckerResult : uint8_t {
836   kNotCalled = 0,
837   kSlowCalled = 1 << 0,
838   kFastCalled = 1 << 1,
839 };
840 using ApiCheckerResultFlags = v8::base::Flags<ApiCheckerResult>;
841 DEFINE_OPERATORS_FOR_FLAGS(ApiCheckerResultFlags)
842 
843 bool IsValidUnwrapObject(v8::Object* object);
844 
845 template <typename T>
GetInternalField(v8::Object * wrapper)846 T* GetInternalField(v8::Object* wrapper) {
847   assert(kV8WrapperObjectIndex < wrapper->InternalFieldCount());
848   return reinterpret_cast<T*>(
849       wrapper->GetAlignedPointerFromInternalField(kV8WrapperObjectIndex));
850 }
851 
852 #endif  // ifndef CCTEST_H_
853