1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <sstream>
6 
7 #include "include/v8-platform.h"
8 #include "src/api/api-inl.h"
9 #include "src/ast/ast-value-factory.h"
10 #include "src/ast/ast.h"
11 #include "src/ast/scopes.h"
12 #include "src/base/platform/semaphore.h"
13 #include "src/codegen/compiler.h"
14 #include "src/compiler-dispatcher/lazy-compile-dispatcher.h"
15 #include "src/flags/flags.h"
16 #include "src/handles/handles.h"
17 #include "src/init/v8.h"
18 #include "src/objects/objects-inl.h"
19 #include "src/parsing/parse-info.h"
20 #include "src/parsing/parsing.h"
21 #include "src/zone/zone-list-inl.h"
22 #include "test/unittests/test-helpers.h"
23 #include "test/unittests/test-utils.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 namespace v8 {
27 namespace internal {
28 
29 class LazyCompilerDispatcherTestFlags {
30  public:
31   LazyCompilerDispatcherTestFlags(const LazyCompilerDispatcherTestFlags&) =
32       delete;
33   LazyCompilerDispatcherTestFlags& operator=(
34       const LazyCompilerDispatcherTestFlags&) = delete;
SetFlagsForTest()35   static void SetFlagsForTest() {
36     CHECK_NULL(save_flags_);
37     save_flags_ = new SaveFlags();
38     FLAG_single_threaded = true;
39     FlagList::EnforceFlagImplications();
40     FLAG_lazy_compile_dispatcher = true;
41     FLAG_finalize_streaming_on_background = false;
42   }
43 
RestoreFlags()44   static void RestoreFlags() {
45     CHECK_NOT_NULL(save_flags_);
46     delete save_flags_;
47     save_flags_ = nullptr;
48   }
49 
50  private:
51   static SaveFlags* save_flags_;
52 };
53 
54 SaveFlags* LazyCompilerDispatcherTestFlags::save_flags_ = nullptr;
55 
56 class LazyCompilerDispatcherTest : public TestWithNativeContext {
57  public:
58   LazyCompilerDispatcherTest() = default;
59   ~LazyCompilerDispatcherTest() override = default;
60   LazyCompilerDispatcherTest(const LazyCompilerDispatcherTest&) = delete;
61   LazyCompilerDispatcherTest& operator=(const LazyCompilerDispatcherTest&) =
62       delete;
63 
SetUpTestCase()64   static void SetUpTestCase() {
65     LazyCompilerDispatcherTestFlags::SetFlagsForTest();
66     TestWithNativeContext::SetUpTestCase();
67   }
68 
TearDownTestCase()69   static void TearDownTestCase() {
70     TestWithNativeContext::TearDownTestCase();
71     LazyCompilerDispatcherTestFlags::RestoreFlags();
72   }
73 
74   static base::Optional<LazyCompileDispatcher::JobId>
EnqueueUnoptimizedCompileJob(LazyCompileDispatcher * dispatcher,Isolate * isolate,Handle<SharedFunctionInfo> shared)75   EnqueueUnoptimizedCompileJob(LazyCompileDispatcher* dispatcher,
76                                Isolate* isolate,
77                                Handle<SharedFunctionInfo> shared) {
78     UnoptimizedCompileState state(isolate);
79     std::unique_ptr<ParseInfo> outer_parse_info =
80         test::OuterParseInfoForShared(isolate, shared, &state);
81     AstValueFactory* ast_value_factory =
82         outer_parse_info->GetOrCreateAstValueFactory();
83     AstNodeFactory ast_node_factory(ast_value_factory,
84                                     outer_parse_info->zone());
85 
86     const AstRawString* function_name =
87         ast_value_factory->GetOneByteString("f");
88     DeclarationScope* script_scope =
89         outer_parse_info->zone()->New<DeclarationScope>(
90             outer_parse_info->zone(), ast_value_factory);
91     DeclarationScope* function_scope =
92         outer_parse_info->zone()->New<DeclarationScope>(
93             outer_parse_info->zone(), script_scope, FUNCTION_SCOPE);
94     function_scope->set_start_position(shared->StartPosition());
95     function_scope->set_end_position(shared->EndPosition());
96     std::vector<void*> pointer_buffer;
97     ScopedPtrList<Statement> statements(&pointer_buffer);
98     const FunctionLiteral* function_literal =
99         ast_node_factory.NewFunctionLiteral(
100             function_name, function_scope, statements, -1, -1, -1,
101             FunctionLiteral::kNoDuplicateParameters,
102             FunctionSyntaxKind::kAnonymousExpression,
103             FunctionLiteral::kShouldEagerCompile, shared->StartPosition(), true,
104             shared->function_literal_id(), nullptr);
105 
106     return dispatcher->Enqueue(outer_parse_info.get(), function_name,
107                                function_literal);
108   }
109 
110  protected:
SetUp()111   void SetUp() override {
112     // TODO(leszeks): Support background finalization in compiler dispatcher.
113     if (FLAG_finalize_streaming_on_background) {
114       GTEST_SKIP_(
115           "Parallel compile tasks don't yet support background finalization");
116     }
117   }
118 };
119 
120 namespace {
121 
122 class MockPlatform : public v8::Platform {
123  public:
MockPlatform()124   MockPlatform()
125       : time_(0.0),
126         time_step_(0.0),
127         idle_task_(nullptr),
128         sem_(0),
129         tracing_controller_(V8::GetCurrentPlatform()->GetTracingController()) {}
~MockPlatform()130   ~MockPlatform() override {
131     base::MutexGuard lock(&mutex_);
132     EXPECT_TRUE(foreground_tasks_.empty());
133     EXPECT_TRUE(worker_tasks_.empty());
134     EXPECT_TRUE(idle_task_ == nullptr);
135   }
136   MockPlatform(const MockPlatform&) = delete;
137   MockPlatform& operator=(const MockPlatform&) = delete;
138 
NumberOfWorkerThreads()139   int NumberOfWorkerThreads() override { return 1; }
140 
GetForegroundTaskRunner(v8::Isolate * isolate)141   std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
142       v8::Isolate* isolate) override {
143     return std::make_shared<MockForegroundTaskRunner>(this);
144   }
145 
CallOnWorkerThread(std::unique_ptr<Task> task)146   void CallOnWorkerThread(std::unique_ptr<Task> task) override {
147     base::MutexGuard lock(&mutex_);
148     worker_tasks_.push_back(std::move(task));
149   }
150 
CallDelayedOnWorkerThread(std::unique_ptr<Task> task,double delay_in_seconds)151   void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
152                                  double delay_in_seconds) override {
153     UNREACHABLE();
154   }
155 
IdleTasksEnabled(v8::Isolate * isolate)156   bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; }
157 
PostJob(TaskPriority priority,std::unique_ptr<JobTask> job_state)158   std::unique_ptr<JobHandle> PostJob(
159       TaskPriority priority, std::unique_ptr<JobTask> job_state) override {
160     UNREACHABLE();
161   }
162 
MonotonicallyIncreasingTime()163   double MonotonicallyIncreasingTime() override {
164     time_ += time_step_;
165     return time_;
166   }
167 
CurrentClockTimeMillis()168   double CurrentClockTimeMillis() override {
169     return time_ * base::Time::kMillisecondsPerSecond;
170   }
171 
GetTracingController()172   v8::TracingController* GetTracingController() override {
173     return tracing_controller_;
174   }
175 
RunIdleTask(double deadline_in_seconds,double time_step)176   void RunIdleTask(double deadline_in_seconds, double time_step) {
177     time_step_ = time_step;
178     IdleTask* task;
179     {
180       base::MutexGuard lock(&mutex_);
181       task = idle_task_;
182       ASSERT_TRUE(idle_task_ != nullptr);
183       idle_task_ = nullptr;
184     }
185     task->Run(deadline_in_seconds);
186     delete task;
187   }
188 
IdleTaskPending()189   bool IdleTaskPending() {
190     base::MutexGuard lock(&mutex_);
191     return idle_task_;
192   }
193 
WorkerTasksPending()194   bool WorkerTasksPending() {
195     base::MutexGuard lock(&mutex_);
196     return !worker_tasks_.empty();
197   }
198 
ForegroundTasksPending()199   bool ForegroundTasksPending() {
200     base::MutexGuard lock(&mutex_);
201     return !foreground_tasks_.empty();
202   }
203 
RunWorkerTasksAndBlock(Platform * platform)204   void RunWorkerTasksAndBlock(Platform* platform) {
205     std::vector<std::unique_ptr<Task>> tasks;
206     {
207       base::MutexGuard lock(&mutex_);
208       tasks.swap(worker_tasks_);
209     }
210     platform->CallOnWorkerThread(
211         std::make_unique<TaskWrapper>(this, std::move(tasks), true));
212     sem_.Wait();
213   }
214 
RunWorkerTasks(Platform * platform)215   void RunWorkerTasks(Platform* platform) {
216     std::vector<std::unique_ptr<Task>> tasks;
217     {
218       base::MutexGuard lock(&mutex_);
219       tasks.swap(worker_tasks_);
220     }
221     platform->CallOnWorkerThread(
222         std::make_unique<TaskWrapper>(this, std::move(tasks), false));
223   }
224 
RunForegroundTasks()225   void RunForegroundTasks() {
226     std::vector<std::unique_ptr<Task>> tasks;
227     {
228       base::MutexGuard lock(&mutex_);
229       tasks.swap(foreground_tasks_);
230     }
231     for (auto& task : tasks) {
232       task->Run();
233       // Reset |task| before running the next one.
234       task.reset();
235     }
236   }
237 
ClearWorkerTasks()238   void ClearWorkerTasks() {
239     std::vector<std::unique_ptr<Task>> tasks;
240     {
241       base::MutexGuard lock(&mutex_);
242       tasks.swap(worker_tasks_);
243     }
244   }
245 
ClearForegroundTasks()246   void ClearForegroundTasks() {
247     std::vector<std::unique_ptr<Task>> tasks;
248     {
249       base::MutexGuard lock(&mutex_);
250       tasks.swap(foreground_tasks_);
251     }
252   }
253 
ClearIdleTask()254   void ClearIdleTask() {
255     base::MutexGuard lock(&mutex_);
256     ASSERT_TRUE(idle_task_ != nullptr);
257     delete idle_task_;
258     idle_task_ = nullptr;
259   }
260 
261  private:
262   class TaskWrapper : public Task {
263    public:
TaskWrapper(MockPlatform * platform,std::vector<std::unique_ptr<Task>> tasks,bool signal)264     TaskWrapper(MockPlatform* platform,
265                 std::vector<std::unique_ptr<Task>> tasks, bool signal)
266         : platform_(platform), tasks_(std::move(tasks)), signal_(signal) {}
267     ~TaskWrapper() override = default;
268     TaskWrapper(const TaskWrapper&) = delete;
269     TaskWrapper& operator=(const TaskWrapper&) = delete;
270 
Run()271     void Run() override {
272       for (auto& task : tasks_) {
273         task->Run();
274         // Reset |task| before running the next one.
275         task.reset();
276       }
277       if (signal_) platform_->sem_.Signal();
278     }
279 
280    private:
281     MockPlatform* platform_;
282     std::vector<std::unique_ptr<Task>> tasks_;
283     bool signal_;
284   };
285 
286   class MockForegroundTaskRunner final : public TaskRunner {
287    public:
MockForegroundTaskRunner(MockPlatform * platform)288     explicit MockForegroundTaskRunner(MockPlatform* platform)
289         : platform_(platform) {}
290 
PostTask(std::unique_ptr<v8::Task> task)291     void PostTask(std::unique_ptr<v8::Task> task) override {
292       base::MutexGuard lock(&platform_->mutex_);
293       platform_->foreground_tasks_.push_back(std::move(task));
294     }
295 
PostNonNestableTask(std::unique_ptr<v8::Task> task)296     void PostNonNestableTask(std::unique_ptr<v8::Task> task) override {
297       // The mock platform does not nest tasks.
298       PostTask(std::move(task));
299     }
300 
PostDelayedTask(std::unique_ptr<Task> task,double delay_in_seconds)301     void PostDelayedTask(std::unique_ptr<Task> task,
302                          double delay_in_seconds) override {
303       UNREACHABLE();
304     }
305 
PostIdleTask(std::unique_ptr<IdleTask> task)306     void PostIdleTask(std::unique_ptr<IdleTask> task) override {
307       DCHECK(IdleTasksEnabled());
308       base::MutexGuard lock(&platform_->mutex_);
309       ASSERT_TRUE(platform_->idle_task_ == nullptr);
310       platform_->idle_task_ = task.release();
311     }
312 
IdleTasksEnabled()313     bool IdleTasksEnabled() override { return true; }
314 
NonNestableTasksEnabled() const315     bool NonNestableTasksEnabled() const override { return false; }
316 
317    private:
318     MockPlatform* platform_;
319   };
320 
321   double time_;
322   double time_step_;
323 
324   // Protects all *_tasks_.
325   base::Mutex mutex_;
326 
327   IdleTask* idle_task_;
328   std::vector<std::unique_ptr<Task>> worker_tasks_;
329   std::vector<std::unique_ptr<Task>> foreground_tasks_;
330 
331   base::Semaphore sem_;
332 
333   v8::TracingController* tracing_controller_;
334 };
335 
336 }  // namespace
337 
TEST_F(LazyCompilerDispatcherTest,Construct)338 TEST_F(LazyCompilerDispatcherTest, Construct) {
339   MockPlatform platform;
340   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
341   dispatcher.AbortAll();
342 }
343 
TEST_F(LazyCompilerDispatcherTest,IsEnqueued)344 TEST_F(LazyCompilerDispatcherTest, IsEnqueued) {
345   MockPlatform platform;
346   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
347 
348   Handle<SharedFunctionInfo> shared =
349       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
350   ASSERT_FALSE(shared->is_compiled());
351   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
352 
353   base::Optional<LazyCompileDispatcher::JobId> job_id =
354       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
355 
356   ASSERT_TRUE(job_id);
357   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
358   ASSERT_FALSE(dispatcher.IsEnqueued(shared));  // SFI not yet registered.
359 
360   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
361   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
362   ASSERT_TRUE(dispatcher.IsEnqueued(shared));
363 
364   dispatcher.AbortAll();
365   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
366   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
367 
368   ASSERT_FALSE(platform.IdleTaskPending());
369   ASSERT_TRUE(platform.WorkerTasksPending());
370   platform.ClearWorkerTasks();
371 }
372 
TEST_F(LazyCompilerDispatcherTest,FinishNow)373 TEST_F(LazyCompilerDispatcherTest, FinishNow) {
374   MockPlatform platform;
375   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
376 
377   Handle<SharedFunctionInfo> shared =
378       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
379   ASSERT_FALSE(shared->is_compiled());
380 
381   base::Optional<LazyCompileDispatcher::JobId> job_id =
382       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
383   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
384 
385   ASSERT_TRUE(dispatcher.FinishNow(shared));
386   // Finishing removes the SFI from the queue.
387   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
388   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
389   ASSERT_TRUE(shared->is_compiled());
390 
391   platform.ClearWorkerTasks();
392   ASSERT_FALSE(platform.IdleTaskPending());
393   dispatcher.AbortAll();
394 }
395 
TEST_F(LazyCompilerDispatcherTest,CompileAndFinalize)396 TEST_F(LazyCompilerDispatcherTest, CompileAndFinalize) {
397   MockPlatform platform;
398   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
399 
400   Handle<SharedFunctionInfo> shared =
401       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
402   ASSERT_FALSE(shared->is_compiled());
403   ASSERT_FALSE(platform.IdleTaskPending());
404 
405   base::Optional<LazyCompileDispatcher::JobId> job_id =
406       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
407   ASSERT_TRUE(platform.WorkerTasksPending());
408 
409   // Run compile steps.
410   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
411 
412   // Since we haven't yet registered the SFI for the job, it should still be
413   // enqueued and waiting.
414   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
415   ASSERT_FALSE(shared->is_compiled());
416   ASSERT_FALSE(platform.IdleTaskPending());
417 
418   // Register SFI, which should schedule another idle task to finalize the
419   // compilation.
420   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
421   ASSERT_TRUE(platform.IdleTaskPending());
422   platform.RunIdleTask(1000.0, 0.0);
423 
424   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
425   ASSERT_TRUE(shared->is_compiled());
426   ASSERT_FALSE(platform.WorkerTasksPending());
427   ASSERT_FALSE(platform.IdleTaskPending());
428   dispatcher.AbortAll();
429 }
430 
TEST_F(LazyCompilerDispatcherTest,IdleTaskNoIdleTime)431 TEST_F(LazyCompilerDispatcherTest, IdleTaskNoIdleTime) {
432   MockPlatform platform;
433   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
434 
435   Handle<SharedFunctionInfo> shared =
436       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
437   ASSERT_FALSE(shared->is_compiled());
438   ASSERT_FALSE(platform.IdleTaskPending());
439 
440   base::Optional<LazyCompileDispatcher::JobId> job_id =
441       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
442   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
443 
444   // Run compile steps.
445   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
446 
447   // Job should be ready to finalize.
448   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
449   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
450   ASSERT_TRUE(platform.IdleTaskPending());
451 
452   // Grant no idle time and have time advance beyond it in one step.
453   platform.RunIdleTask(0.0, 1.0);
454 
455   ASSERT_TRUE(dispatcher.IsEnqueued(shared));
456   ASSERT_FALSE(shared->is_compiled());
457   ASSERT_TRUE(platform.IdleTaskPending());
458 
459   // Job should be ready to finalize.
460   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
461   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
462 
463   // Now grant a lot of idle time and freeze time.
464   platform.RunIdleTask(1000.0, 0.0);
465 
466   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
467   ASSERT_TRUE(shared->is_compiled());
468   ASSERT_FALSE(platform.IdleTaskPending());
469   ASSERT_FALSE(platform.WorkerTasksPending());
470   dispatcher.AbortAll();
471 }
472 
TEST_F(LazyCompilerDispatcherTest,IdleTaskSmallIdleTime)473 TEST_F(LazyCompilerDispatcherTest, IdleTaskSmallIdleTime) {
474   MockPlatform platform;
475   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
476 
477   Handle<SharedFunctionInfo> shared_1 =
478       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
479   ASSERT_FALSE(shared_1->is_compiled());
480   Handle<SharedFunctionInfo> shared_2 =
481       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
482   ASSERT_FALSE(shared_2->is_compiled());
483 
484   base::Optional<LazyCompileDispatcher::JobId> job_id_1 =
485       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_1);
486   base::Optional<LazyCompileDispatcher::JobId> job_id_2 =
487       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
488 
489   dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
490   dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
491 
492   // Run compile steps.
493   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
494 
495   // Both jobs should be ready to finalize.
496   ASSERT_EQ(dispatcher.jobs_.size(), 2u);
497   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
498   ASSERT_TRUE((++dispatcher.jobs_.begin())->second->has_run);
499   ASSERT_TRUE(platform.IdleTaskPending());
500 
501   // Grant a small anount of idle time and have time advance beyond it in one
502   // step.
503   platform.RunIdleTask(2.0, 1.0);
504 
505   // Only one of the jobs should be finalized.
506   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
507   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
508   ASSERT_NE(dispatcher.IsEnqueued(shared_1), dispatcher.IsEnqueued(shared_2));
509   ASSERT_NE(shared_1->is_compiled(), shared_2->is_compiled());
510   ASSERT_TRUE(platform.IdleTaskPending());
511 
512   // Now grant a lot of idle time and freeze time.
513   platform.RunIdleTask(1000.0, 0.0);
514 
515   ASSERT_FALSE(dispatcher.IsEnqueued(shared_1) ||
516                dispatcher.IsEnqueued(shared_2));
517   ASSERT_TRUE(shared_1->is_compiled() && shared_2->is_compiled());
518   ASSERT_FALSE(platform.IdleTaskPending());
519   ASSERT_FALSE(platform.WorkerTasksPending());
520   dispatcher.AbortAll();
521 }
522 
TEST_F(LazyCompilerDispatcherTest,IdleTaskException)523 TEST_F(LazyCompilerDispatcherTest, IdleTaskException) {
524   MockPlatform platform;
525   LazyCompileDispatcher dispatcher(i_isolate(), &platform, 50);
526 
527   std::string raw_script("(x) { var a = ");
528   for (int i = 0; i < 1000; i++) {
529     // Alternate + and - to avoid n-ary operation nodes.
530     raw_script += "'x' + 'x' - ";
531   }
532   raw_script += " 'x'; };";
533   test::ScriptResource* script =
534       new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
535   Handle<SharedFunctionInfo> shared =
536       test::CreateSharedFunctionInfo(i_isolate(), script);
537   ASSERT_FALSE(shared->is_compiled());
538 
539   base::Optional<LazyCompileDispatcher::JobId> job_id =
540       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
541   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
542 
543   // Run compile steps and finalize.
544   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
545   platform.RunIdleTask(1000.0, 0.0);
546 
547   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
548   ASSERT_FALSE(shared->is_compiled());
549   ASSERT_FALSE(i_isolate()->has_pending_exception());
550   platform.ClearWorkerTasks();
551   dispatcher.AbortAll();
552 }
553 
TEST_F(LazyCompilerDispatcherTest,FinishNowWithWorkerTask)554 TEST_F(LazyCompilerDispatcherTest, FinishNowWithWorkerTask) {
555   MockPlatform platform;
556   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
557 
558   Handle<SharedFunctionInfo> shared =
559       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
560   ASSERT_FALSE(shared->is_compiled());
561 
562   base::Optional<LazyCompileDispatcher::JobId> job_id =
563       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
564   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
565 
566   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
567   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
568 
569   ASSERT_TRUE(dispatcher.IsEnqueued(shared));
570   ASSERT_FALSE(shared->is_compiled());
571   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
572   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
573   ASSERT_TRUE(platform.WorkerTasksPending());
574 
575   // This does not block, but races with the FinishNow() call below.
576   platform.RunWorkerTasks(V8::GetCurrentPlatform());
577 
578   ASSERT_TRUE(dispatcher.FinishNow(shared));
579   // Finishing removes the SFI from the queue.
580   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
581   ASSERT_TRUE(shared->is_compiled());
582   if (platform.IdleTaskPending()) platform.ClearIdleTask();
583   ASSERT_FALSE(platform.WorkerTasksPending());
584   dispatcher.AbortAll();
585 }
586 
TEST_F(LazyCompilerDispatcherTest,IdleTaskMultipleJobs)587 TEST_F(LazyCompilerDispatcherTest, IdleTaskMultipleJobs) {
588   MockPlatform platform;
589   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
590 
591   Handle<SharedFunctionInfo> shared_1 =
592       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
593   ASSERT_FALSE(shared_1->is_compiled());
594   Handle<SharedFunctionInfo> shared_2 =
595       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
596   ASSERT_FALSE(shared_2->is_compiled());
597 
598   base::Optional<LazyCompileDispatcher::JobId> job_id_1 =
599       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_1);
600   base::Optional<LazyCompileDispatcher::JobId> job_id_2 =
601       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
602 
603   dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
604   dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
605 
606   ASSERT_TRUE(dispatcher.IsEnqueued(shared_1));
607   ASSERT_TRUE(dispatcher.IsEnqueued(shared_2));
608 
609   // Run compile steps and finalize.
610   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
611   platform.RunIdleTask(1000.0, 0.0);
612 
613   ASSERT_FALSE(dispatcher.IsEnqueued(shared_1));
614   ASSERT_FALSE(dispatcher.IsEnqueued(shared_2));
615   ASSERT_TRUE(shared_1->is_compiled());
616   ASSERT_TRUE(shared_2->is_compiled());
617   ASSERT_FALSE(platform.IdleTaskPending());
618   ASSERT_FALSE(platform.WorkerTasksPending());
619   dispatcher.AbortAll();
620 }
621 
TEST_F(LazyCompilerDispatcherTest,FinishNowException)622 TEST_F(LazyCompilerDispatcherTest, FinishNowException) {
623   MockPlatform platform;
624   LazyCompileDispatcher dispatcher(i_isolate(), &platform, 50);
625 
626   std::string raw_script("(x) { var a = ");
627   for (int i = 0; i < 1000; i++) {
628     // Alternate + and - to avoid n-ary operation nodes.
629     raw_script += "'x' + 'x' - ";
630   }
631   raw_script += " 'x'; };";
632   test::ScriptResource* script =
633       new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
634   Handle<SharedFunctionInfo> shared =
635       test::CreateSharedFunctionInfo(i_isolate(), script);
636   ASSERT_FALSE(shared->is_compiled());
637 
638   base::Optional<LazyCompileDispatcher::JobId> job_id =
639       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
640   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
641 
642   ASSERT_FALSE(dispatcher.FinishNow(shared));
643 
644   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
645   ASSERT_FALSE(shared->is_compiled());
646   ASSERT_TRUE(i_isolate()->has_pending_exception());
647 
648   i_isolate()->clear_pending_exception();
649   ASSERT_FALSE(platform.IdleTaskPending());
650   platform.ClearWorkerTasks();
651   dispatcher.AbortAll();
652 }
653 
TEST_F(LazyCompilerDispatcherTest,AbortJobNotStarted)654 TEST_F(LazyCompilerDispatcherTest, AbortJobNotStarted) {
655   MockPlatform platform;
656   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
657 
658   Handle<SharedFunctionInfo> shared =
659       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
660   ASSERT_FALSE(shared->is_compiled());
661 
662   base::Optional<LazyCompileDispatcher::JobId> job_id =
663       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
664 
665   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
666   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
667 
668   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
669   ASSERT_FALSE(shared->is_compiled());
670   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
671   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
672   ASSERT_TRUE(platform.WorkerTasksPending());
673 
674   dispatcher.AbortJob(*job_id);
675 
676   // Aborting removes the job from the queue.
677   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
678   ASSERT_FALSE(shared->is_compiled());
679   ASSERT_FALSE(platform.IdleTaskPending());
680   platform.ClearWorkerTasks();
681   dispatcher.AbortAll();
682 }
683 
TEST_F(LazyCompilerDispatcherTest,AbortJobAlreadyStarted)684 TEST_F(LazyCompilerDispatcherTest, AbortJobAlreadyStarted) {
685   MockPlatform platform;
686   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
687 
688   Handle<SharedFunctionInfo> shared =
689       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
690   ASSERT_FALSE(shared->is_compiled());
691 
692   base::Optional<LazyCompileDispatcher::JobId> job_id =
693       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
694 
695   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
696   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
697 
698   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
699   ASSERT_FALSE(shared->is_compiled());
700   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
701   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
702   ASSERT_TRUE(platform.WorkerTasksPending());
703 
704   // Have dispatcher block on the background thread when running the job.
705   {
706     base::LockGuard<base::Mutex> lock(&dispatcher.mutex_);
707     dispatcher.block_for_testing_.SetValue(true);
708   }
709 
710   // Start background thread and wait until it is about to run the job.
711   platform.RunWorkerTasks(V8::GetCurrentPlatform());
712   while (dispatcher.block_for_testing_.Value()) {
713   }
714 
715   // Now abort while dispatcher is in the middle of running the job.
716   dispatcher.AbortJob(*job_id);
717 
718   // Unblock background thread, and wait for job to complete.
719   {
720     base::LockGuard<base::Mutex> lock(&dispatcher.mutex_);
721     dispatcher.main_thread_blocking_on_job_ =
722         dispatcher.jobs_.begin()->second.get();
723     dispatcher.semaphore_for_testing_.Signal();
724     while (dispatcher.main_thread_blocking_on_job_ != nullptr) {
725       dispatcher.main_thread_blocking_signal_.Wait(&dispatcher.mutex_);
726     }
727   }
728 
729   // Job should have finished running and then been aborted.
730   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
731   ASSERT_FALSE(shared->is_compiled());
732   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
733   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
734   ASSERT_TRUE(dispatcher.jobs_.begin()->second->aborted);
735   ASSERT_FALSE(platform.WorkerTasksPending());
736   ASSERT_TRUE(platform.IdleTaskPending());
737 
738   // Runt the pending idle task
739   platform.RunIdleTask(1000.0, 0.0);
740 
741   // Aborting removes the SFI from the queue.
742   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
743   ASSERT_FALSE(shared->is_compiled());
744   ASSERT_FALSE(platform.IdleTaskPending());
745   ASSERT_FALSE(platform.WorkerTasksPending());
746   dispatcher.AbortAll();
747 }
748 
TEST_F(LazyCompilerDispatcherTest,CompileLazyFinishesDispatcherJob)749 TEST_F(LazyCompilerDispatcherTest, CompileLazyFinishesDispatcherJob) {
750   // Use the real dispatcher so that CompileLazy checks the same one for
751   // enqueued functions.
752   LazyCompileDispatcher* dispatcher = i_isolate()->lazy_compile_dispatcher();
753 
754   const char raw_script[] = "function lazy() { return 42; }; lazy;";
755   test::ScriptResource* script =
756       new test::ScriptResource(raw_script, strlen(raw_script));
757   Handle<JSFunction> f = RunJS<JSFunction>(script);
758   Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
759   ASSERT_FALSE(shared->is_compiled());
760 
761   base::Optional<LazyCompileDispatcher::JobId> job_id =
762       EnqueueUnoptimizedCompileJob(dispatcher, i_isolate(), shared);
763   dispatcher->RegisterSharedFunctionInfo(*job_id, *shared);
764 
765   // Now force the function to run and ensure CompileLazy finished and dequeues
766   // it from the dispatcher.
767   RunJS("lazy();");
768   ASSERT_TRUE(shared->is_compiled());
769   ASSERT_FALSE(dispatcher->IsEnqueued(shared));
770 }
771 
TEST_F(LazyCompilerDispatcherTest,CompileLazy2FinishesDispatcherJob)772 TEST_F(LazyCompilerDispatcherTest, CompileLazy2FinishesDispatcherJob) {
773   // Use the real dispatcher so that CompileLazy checks the same one for
774   // enqueued functions.
775   LazyCompileDispatcher* dispatcher = i_isolate()->lazy_compile_dispatcher();
776 
777   const char raw_source_2[] = "function lazy2() { return 42; }; lazy2;";
778   test::ScriptResource* source_2 =
779       new test::ScriptResource(raw_source_2, strlen(raw_source_2));
780   Handle<JSFunction> lazy2 = RunJS<JSFunction>(source_2);
781   Handle<SharedFunctionInfo> shared_2(lazy2->shared(), i_isolate());
782   ASSERT_FALSE(shared_2->is_compiled());
783 
784   const char raw_source_1[] = "function lazy1() { return lazy2(); }; lazy1;";
785   test::ScriptResource* source_1 =
786       new test::ScriptResource(raw_source_1, strlen(raw_source_1));
787   Handle<JSFunction> lazy1 = RunJS<JSFunction>(source_1);
788   Handle<SharedFunctionInfo> shared_1(lazy1->shared(), i_isolate());
789   ASSERT_FALSE(shared_1->is_compiled());
790 
791   base::Optional<LazyCompileDispatcher::JobId> job_id_1 =
792       EnqueueUnoptimizedCompileJob(dispatcher, i_isolate(), shared_1);
793   dispatcher->RegisterSharedFunctionInfo(*job_id_1, *shared_1);
794 
795   base::Optional<LazyCompileDispatcher::JobId> job_id_2 =
796       EnqueueUnoptimizedCompileJob(dispatcher, i_isolate(), shared_2);
797   dispatcher->RegisterSharedFunctionInfo(*job_id_2, *shared_2);
798 
799   ASSERT_TRUE(dispatcher->IsEnqueued(shared_1));
800   ASSERT_TRUE(dispatcher->IsEnqueued(shared_2));
801 
802   RunJS("lazy1();");
803   ASSERT_TRUE(shared_1->is_compiled());
804   ASSERT_TRUE(shared_2->is_compiled());
805   ASSERT_FALSE(dispatcher->IsEnqueued(shared_1));
806   ASSERT_FALSE(dispatcher->IsEnqueued(shared_2));
807 }
808 
TEST_F(LazyCompilerDispatcherTest,CompileMultipleOnBackgroundThread)809 TEST_F(LazyCompilerDispatcherTest, CompileMultipleOnBackgroundThread) {
810   MockPlatform platform;
811   LazyCompileDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
812 
813   Handle<SharedFunctionInfo> shared_1 =
814       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
815   ASSERT_FALSE(shared_1->is_compiled());
816 
817   Handle<SharedFunctionInfo> shared_2 =
818       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
819   ASSERT_FALSE(shared_2->is_compiled());
820 
821   base::Optional<LazyCompileDispatcher::JobId> job_id_1 =
822       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_1);
823   dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
824 
825   base::Optional<LazyCompileDispatcher::JobId> job_id_2 =
826       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
827   dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
828 
829   ASSERT_EQ(dispatcher.jobs_.size(), 2u);
830   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
831   ASSERT_FALSE((++dispatcher.jobs_.begin())->second->has_run);
832 
833   ASSERT_TRUE(dispatcher.IsEnqueued(shared_1));
834   ASSERT_TRUE(dispatcher.IsEnqueued(shared_2));
835   ASSERT_FALSE(shared_1->is_compiled());
836   ASSERT_FALSE(shared_2->is_compiled());
837   ASSERT_FALSE(platform.IdleTaskPending());
838   ASSERT_TRUE(platform.WorkerTasksPending());
839 
840   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
841 
842   ASSERT_TRUE(platform.IdleTaskPending());
843   ASSERT_FALSE(platform.WorkerTasksPending());
844   ASSERT_EQ(dispatcher.jobs_.size(), 2u);
845   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
846   ASSERT_TRUE((++dispatcher.jobs_.begin())->second->has_run);
847 
848   // Now grant a lot of idle time and freeze time.
849   platform.RunIdleTask(1000.0, 0.0);
850 
851   ASSERT_FALSE(dispatcher.IsEnqueued(shared_1));
852   ASSERT_FALSE(dispatcher.IsEnqueued(shared_2));
853   ASSERT_TRUE(shared_1->is_compiled());
854   ASSERT_TRUE(shared_2->is_compiled());
855   ASSERT_FALSE(platform.IdleTaskPending());
856   dispatcher.AbortAll();
857 }
858 
859 }  // namespace internal
860 }  // namespace v8
861