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