compiler-dispatcher-job-unittest.cc 11.7 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>

7 8
#include "include/v8.h"
#include "src/api.h"
9
#include "src/ast/ast.h"
10
#include "src/ast/scopes.h"
11
#include "src/base/platform/semaphore.h"
12
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
13
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
14
#include "src/flags.h"
15
#include "src/isolate-inl.h"
16
#include "src/parsing/parse-info.h"
17
#include "src/v8.h"
18
#include "test/unittests/compiler-dispatcher/compiler-dispatcher-helper.h"
19 20 21 22 23 24
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace v8 {
namespace internal {

25 26 27 28 29 30 31 32 33
class CompilerDispatcherJobTest : public TestWithContext {
 public:
  CompilerDispatcherJobTest() : tracer_(i_isolate()) {}
  ~CompilerDispatcherJobTest() override {}

  CompilerDispatcherTracer* tracer() { return &tracer_; }

 private:
  CompilerDispatcherTracer tracer_;
34

35 36 37 38
  DISALLOW_COPY_AND_ASSIGN(CompilerDispatcherJobTest);
};

class IgnitionCompilerDispatcherJobTest : public CompilerDispatcherJobTest {
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
 public:
  IgnitionCompilerDispatcherJobTest() {}
  ~IgnitionCompilerDispatcherJobTest() override {}

  static void SetUpTestCase() {
    old_flag_ = i::FLAG_ignition;
    i::FLAG_ignition = true;
    TestWithContext::SetUpTestCase();
  }

  static void TearDownTestCase() {
    TestWithContext::TearDownTestCase();
    i::FLAG_ignition = old_flag_;
  }

 private:
  static bool old_flag_;
  DISALLOW_COPY_AND_ASSIGN(IgnitionCompilerDispatcherJobTest);
};

bool IgnitionCompilerDispatcherJobTest::old_flag_;

61 62
namespace {

63
const char test_script[] = "(x) { x*x; }";
64

65 66 67 68
class ScriptResource : public v8::String::ExternalOneByteStringResource {
 public:
  ScriptResource(const char* data, size_t length)
      : data_(data), length_(length) {}
69
  ~ScriptResource() override = default;
70

71 72
  const char* data() const override { return data_; }
  size_t length() const override { return length_; }
73 74 75 76 77 78 79 80

 private:
  const char* data_;
  size_t length_;

  DISALLOW_COPY_AND_ASSIGN(ScriptResource);
};

81
Handle<SharedFunctionInfo> CreateSharedFunctionInfo(
82 83 84 85 86 87 88 89
    Isolate* isolate, ExternalOneByteString::Resource* maybe_resource) {
  HandleScope scope(isolate);
  Handle<String> source;
  if (maybe_resource) {
    source = isolate->factory()
                 ->NewExternalStringFromOneByte(maybe_resource)
                 .ToHandleChecked();
  } else {
90
    source = isolate->factory()->NewStringFromAsciiChecked(test_script);
91 92
  }
  Handle<Script> script = isolate->factory()->NewScript(source);
93 94
  Handle<FixedArray> infos = isolate->factory()->NewFixedArray(3);
  script->set_shared_function_infos(*infos);
95
  Handle<SharedFunctionInfo> shared = isolate->factory()->NewSharedFunctionInfo(
96 97
      isolate->factory()->NewStringFromAsciiChecked("f"),
      isolate->builtins()->CompileLazy(), false);
98
  shared->set_end_position(source->length());
99
  shared->set_outer_scope_info(ScopeInfo::Empty(isolate));
100
  shared->set_function_literal_id(1);
101
  SharedFunctionInfo::SetScript(shared, script);
102
  return scope.CloseAndEscape(shared);
103 104 105 106
}

}  // namespace

107
TEST_F(CompilerDispatcherJobTest, Construct) {
108
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
109
      i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), nullptr),
110
      FLAG_stack_size));
111 112 113 114 115
}

TEST_F(CompilerDispatcherJobTest, CanParseOnBackgroundThread) {
  {
    std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
116
        i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), nullptr),
117
        FLAG_stack_size));
118 119 120
    ASSERT_FALSE(job->can_parse_on_background_thread());
  }
  {
121
    ScriptResource script(test_script, strlen(test_script));
122
    std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
123
        i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
124
        FLAG_stack_size));
125 126
    ASSERT_TRUE(job->can_parse_on_background_thread());
  }
127 128
}

129 130
TEST_F(CompilerDispatcherJobTest, StateTransitions) {
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
131
      i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), nullptr),
132
      FLAG_stack_size));
133 134 135 136

  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
  job->PrepareToParseOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToParse);
137 138
  job->Parse();
  ASSERT_TRUE(job->status() == CompileJobStatus::kParsed);
139
  ASSERT_TRUE(job->FinalizeParsingOnMainThread());
140 141
  ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToAnalyse);
  ASSERT_TRUE(job->PrepareToCompileOnMainThread());
jochen's avatar
jochen committed
142
  ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToCompile);
143 144 145 146
  job->Compile();
  ASSERT_TRUE(job->status() == CompileJobStatus::kCompiled);
  ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
  ASSERT_TRUE(job->status() == CompileJobStatus::kDone);
jochen's avatar
jochen committed
147 148 149 150 151 152 153
  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
}

TEST_F(CompilerDispatcherJobTest, SyntaxError) {
  ScriptResource script("^^^", strlen("^^^"));
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
154
      i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
155
      FLAG_stack_size));
jochen's avatar
jochen committed
156 157 158

  job->PrepareToParseOnMainThread();
  job->Parse();
159
  ASSERT_FALSE(job->FinalizeParsingOnMainThread());
jochen's avatar
jochen committed
160 161
  ASSERT_TRUE(job->status() == CompileJobStatus::kFailed);
  ASSERT_TRUE(i_isolate()->has_pending_exception());
162

163
  i_isolate()->clear_pending_exception();
164 165 166

  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
167 168 169 170
}

TEST_F(CompilerDispatcherJobTest, ScopeChain) {
  const char script[] =
171
      "function g() { var y = 1; function f(x) { return x * y }; return f; } "
172
      "g();";
173
  Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
174

175
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
176
      i_isolate(), tracer(), handle(f->shared()), FLAG_stack_size));
177 178 179

  job->PrepareToParseOnMainThread();
  job->Parse();
180
  ASSERT_TRUE(job->FinalizeParsingOnMainThread());
181 182
  ASSERT_TRUE(job->PrepareToCompileOnMainThread());
  ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToCompile);
183 184 185 186 187

  const AstRawString* var_x =
      job->parse_info_->ast_value_factory()->GetOneByteString("x");
  Variable* var = job->parse_info_->literal()->scope()->Lookup(var_x);
  ASSERT_TRUE(var);
188
  ASSERT_TRUE(var->IsParameter());
189

190 191 192
  const AstRawString* var_y =
      job->parse_info_->ast_value_factory()->GetOneByteString("y");
  var = job->parse_info_->literal()->scope()->Lookup(var_y);
193 194 195 196 197
  ASSERT_TRUE(var);
  ASSERT_TRUE(var->IsContextSlot());

  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
198 199
}

200 201 202 203 204 205 206 207 208 209 210
TEST_F(CompilerDispatcherJobTest, CompileAndRun) {
  const char script[] =
      "function g() {\n"
      "  f = function(a) {\n"
      "        for (var i = 0; i < 3; i++) { a += 20; }\n"
      "        return a;\n"
      "      }\n"
      "  return f;\n"
      "}\n"
      "g();";
  Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
211
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
212
      i_isolate(), tracer(), handle(f->shared()), FLAG_stack_size));
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236

  job->PrepareToParseOnMainThread();
  job->Parse();
  job->FinalizeParsingOnMainThread();
  job->PrepareToCompileOnMainThread();
  job->Compile();
  ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
  ASSERT_TRUE(job->status() == CompileJobStatus::kDone);

  Smi* value = Smi::cast(*RunJS(isolate(), "f(100);"));
  ASSERT_TRUE(value == Smi::FromInt(160));

  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
}

TEST_F(CompilerDispatcherJobTest, CompileFailureToPrepare) {
  std::string raw_script("() { var a = ");
  for (int i = 0; i < 100000; i++) {
    raw_script += "'x' + ";
  }
  raw_script += " 'x'; }";
  ScriptResource script(raw_script.c_str(), strlen(raw_script.c_str()));
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
237 238
      i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
      100));
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

  job->PrepareToParseOnMainThread();
  job->Parse();
  job->FinalizeParsingOnMainThread();
  ASSERT_FALSE(job->PrepareToCompileOnMainThread());
  ASSERT_TRUE(job->status() == CompileJobStatus::kFailed);
  ASSERT_TRUE(i_isolate()->has_pending_exception());

  i_isolate()->clear_pending_exception();
  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
}

TEST_F(CompilerDispatcherJobTest, CompileFailureToFinalize) {
  std::string raw_script("() { var a = ");
254
  for (int i = 0; i < 1000; i++) {
255 256 257 258 259
    raw_script += "'x' + ";
  }
  raw_script += " 'x'; }";
  ScriptResource script(raw_script.c_str(), strlen(raw_script.c_str()));
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
260 261
      i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
      50));
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276

  job->PrepareToParseOnMainThread();
  job->Parse();
  job->FinalizeParsingOnMainThread();
  job->PrepareToCompileOnMainThread();
  job->Compile();
  ASSERT_FALSE(job->FinalizeCompilingOnMainThread());
  ASSERT_TRUE(job->status() == CompileJobStatus::kFailed);
  ASSERT_TRUE(i_isolate()->has_pending_exception());

  i_isolate()->clear_pending_exception();
  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
class CompileTask : public Task {
 public:
  CompileTask(CompilerDispatcherJob* job, base::Semaphore* semaphore)
      : job_(job), semaphore_(semaphore) {}
  ~CompileTask() override {}

  void Run() override {
    job_->Compile();
    semaphore_->Signal();
  }

 private:
  CompilerDispatcherJob* job_;
  base::Semaphore* semaphore_;
  DISALLOW_COPY_AND_ASSIGN(CompileTask);
};

TEST_F(IgnitionCompilerDispatcherJobTest, CompileOnBackgroundThread) {
  const char* raw_script =
      "(a, b) {\n"
      "  var c = a + b;\n"
      "  function bar() { return b }\n"
      "  var d = { foo: 100, bar : bar() }\n"
      "  return bar;"
      "}";
  ScriptResource script(raw_script, strlen(raw_script));
  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
304 305
      i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
      100));
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324

  job->PrepareToParseOnMainThread();
  job->Parse();
  job->FinalizeParsingOnMainThread();
  job->PrepareToCompileOnMainThread();
  ASSERT_TRUE(job->can_compile_on_background_thread());

  base::Semaphore semaphore(0);
  CompileTask* background_task = new CompileTask(job.get(), &semaphore);
  V8::GetCurrentPlatform()->CallOnBackgroundThread(background_task,
                                                   Platform::kShortRunningTask);
  semaphore.Wait();
  ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
  ASSERT_TRUE(job->status() == CompileJobStatus::kDone);

  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
}

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
TEST_F(CompilerDispatcherJobTest, LazyInnerFunctions) {
  const char script[] =
      "function g() {\n"
      "  f = function() {\n"
      "    e = (function() { return 42; });\n"
      "    return e;\n"
      "  };\n"
      "  return f;\n"
      "}\n"
      "g();";
  Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));

  std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
      i_isolate(), tracer(), handle(f->shared()), FLAG_stack_size));

  job->PrepareToParseOnMainThread();
  job->Parse();
  ASSERT_TRUE(job->FinalizeParsingOnMainThread());
  ASSERT_TRUE(job->PrepareToCompileOnMainThread());
  job->Compile();
  ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
  ASSERT_TRUE(job->status() == CompileJobStatus::kDone);

  Handle<JSFunction> e = Handle<JSFunction>::cast(RunJS(isolate(), "f();"));

  ASSERT_FALSE(e->shared()->HasBaselineCode());

  job->ResetOnMainThread();
  ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
}

356 357
}  // namespace internal
}  // namespace v8