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

#include <memory>

7
#include "include/v8.h"
8
#include "src/api/api-inl.h"
9
#include "src/ast/ast.h"
10
#include "src/ast/scopes.h"
11
#include "src/base/platform/semaphore.h"
12
#include "src/codegen/compiler.h"
13
#include "src/execution/isolate-inl.h"
14 15
#include "src/flags/flags.h"
#include "src/init/v8.h"
16
#include "src/objects/smi.h"
17
#include "src/parsing/parse-info.h"
18
#include "src/parsing/parser.h"
19
#include "src/parsing/preparse-data.h"
20
#include "src/zone/zone-list-inl.h"
21
#include "test/unittests/test-helpers.h"
22 23 24 25 26 27
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace v8 {
namespace internal {

28
class BackgroundCompileTaskTest : public TestWithNativeContext {
29
 public:
30 31
  BackgroundCompileTaskTest() : allocator_(isolate()->allocator()) {}
  ~BackgroundCompileTaskTest() override = default;
32

33
  AccountingAllocator* allocator() { return allocator_; }
34

35
  static void SetUpTestCase() {
36 37
    CHECK_NULL(save_flags_);
    save_flags_ = new SaveFlags();
38
    TestWithNativeContext::SetUpTestCase();
39 40 41
  }

  static void TearDownTestCase() {
42
    TestWithNativeContext::TearDownTestCase();
43 44 45
    CHECK_NOT_NULL(save_flags_);
    delete save_flags_;
    save_flags_ = nullptr;
46 47
  }

48
  BackgroundCompileTask* NewBackgroundCompileTask(
49 50
      Isolate* isolate, Handle<SharedFunctionInfo> shared,
      size_t stack_size = FLAG_stack_size) {
51
    UnoptimizedCompileState state(isolate);
52
    std::unique_ptr<ParseInfo> outer_parse_info =
53
        test::OuterParseInfoForShared(isolate, shared, &state);
54 55 56 57 58 59 60
    AstValueFactory* ast_value_factory =
        outer_parse_info->GetOrCreateAstValueFactory();
    AstNodeFactory ast_node_factory(ast_value_factory,
                                    outer_parse_info->zone());

    const AstRawString* function_name =
        ast_value_factory->GetOneByteString("f");
61 62 63
    DeclarationScope* script_scope =
        outer_parse_info->zone()->New<DeclarationScope>(
            outer_parse_info->zone(), ast_value_factory);
64
    DeclarationScope* function_scope =
65
        outer_parse_info->zone()->New<DeclarationScope>(
66 67 68
            outer_parse_info->zone(), script_scope, FUNCTION_SCOPE);
    function_scope->set_start_position(shared->StartPosition());
    function_scope->set_end_position(shared->EndPosition());
69 70
    std::vector<void*> buffer;
    ScopedPtrList<Statement> statements(&buffer);
71 72
    const FunctionLiteral* function_literal =
        ast_node_factory.NewFunctionLiteral(
73
            function_name, function_scope, statements, -1, -1, -1,
74
            FunctionLiteral::kNoDuplicateParameters,
75
            FunctionSyntaxKind::kAnonymousExpression,
76
            FunctionLiteral::kShouldEagerCompile, shared->StartPosition(), true,
77
            shared->function_literal_id(), nullptr);
78

79
    return new BackgroundCompileTask(
80
        outer_parse_info.get(), function_name, function_literal,
81
        isolate->counters()->worker_thread_runtime_call_stats(),
82
        isolate->counters()->compile_function_on_background(), FLAG_stack_size);
83 84
  }

85
 private:
86
  AccountingAllocator* allocator_;
87
  static SaveFlags* save_flags_;
88

89
  DISALLOW_COPY_AND_ASSIGN(BackgroundCompileTaskTest);
90 91
};

92
SaveFlags* BackgroundCompileTaskTest::save_flags_ = nullptr;
93

94
TEST_F(BackgroundCompileTaskTest, Construct) {
95 96 97
  Handle<SharedFunctionInfo> shared =
      test::CreateSharedFunctionInfo(isolate(), nullptr);
  ASSERT_FALSE(shared->is_compiled());
98 99
  std::unique_ptr<BackgroundCompileTask> task(
      NewBackgroundCompileTask(isolate(), shared));
jochen's avatar
jochen committed
100 101
}

102
TEST_F(BackgroundCompileTaskTest, SyntaxError) {
103
  test::ScriptResource* script = new test::ScriptResource("^^^", strlen("^^^"));
104 105
  Handle<SharedFunctionInfo> shared =
      test::CreateSharedFunctionInfo(isolate(), script);
106 107
  std::unique_ptr<BackgroundCompileTask> task(
      NewBackgroundCompileTask(isolate(), shared));
jochen's avatar
jochen committed
108

109 110 111
  task->Run();
  ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
      task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
112
  ASSERT_TRUE(isolate()->has_pending_exception());
113

114
  isolate()->clear_pending_exception();
115 116
}

117
TEST_F(BackgroundCompileTaskTest, CompileAndRun) {
118
  const char raw_script[] =
119 120 121 122 123 124 125 126
      "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();";
127 128
  test::ScriptResource* script =
      new test::ScriptResource(raw_script, strlen(raw_script));
129
  Handle<JSFunction> f = RunJS<JSFunction>(script);
130 131
  Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
  ASSERT_FALSE(shared->is_compiled());
132 133
  std::unique_ptr<BackgroundCompileTask> task(
      NewBackgroundCompileTask(isolate(), shared));
134

135 136 137
  task->Run();
  ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
      task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
138
  ASSERT_TRUE(shared->is_compiled());
139

140
  Smi value = Smi::cast(*RunJS("f(100);"));
141 142 143
  ASSERT_TRUE(value == Smi::FromInt(160));
}

144
TEST_F(BackgroundCompileTaskTest, CompileFailure) {
145
  std::string raw_script("() { var a = ");
146
  for (int i = 0; i < 10000; i++) {
147 148 149 150 151
    // TODO(leszeks): Figure out a more "unit-test-y" way of forcing an analysis
    // failure than a binop stack overflow.

    // Alternate + and - to avoid n-ary operation nodes.
    raw_script += "'x' + 'x' - ";
152 153
  }
  raw_script += " 'x'; }";
154 155
  test::ScriptResource* script =
      new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
156 157
  Handle<SharedFunctionInfo> shared =
      test::CreateSharedFunctionInfo(isolate(), script);
158 159
  std::unique_ptr<BackgroundCompileTask> task(
      NewBackgroundCompileTask(isolate(), shared, 100));
160

161 162 163
  task->Run();
  ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
      task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
164
  ASSERT_TRUE(isolate()->has_pending_exception());
165

166
  isolate()->clear_pending_exception();
167 168
}

169 170
class CompileTask : public Task {
 public:
171 172
  CompileTask(BackgroundCompileTask* task, base::Semaphore* semaphore)
      : task_(task), semaphore_(semaphore) {}
173
  ~CompileTask() override = default;
174 175

  void Run() override {
176
    task_->Run();
177 178 179 180
    semaphore_->Signal();
  }

 private:
181
  BackgroundCompileTask* task_;
182 183 184 185
  base::Semaphore* semaphore_;
  DISALLOW_COPY_AND_ASSIGN(CompileTask);
};

186
TEST_F(BackgroundCompileTaskTest, CompileOnBackgroundThread) {
187 188 189 190 191 192 193
  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;"
      "}";
194 195
  test::ScriptResource* script =
      new test::ScriptResource(raw_script, strlen(raw_script));
196 197
  Handle<SharedFunctionInfo> shared =
      test::CreateSharedFunctionInfo(isolate(), script);
198 199
  std::unique_ptr<BackgroundCompileTask> task(
      NewBackgroundCompileTask(isolate(), shared));
200 201

  base::Semaphore semaphore(0);
202
  auto background_task = std::make_unique<CompileTask>(task.get(), &semaphore);
203

204
  V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(background_task));
205
  semaphore.Wait();
206 207 208
  ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
      task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
  ASSERT_TRUE(shared->is_compiled());
209 210
}

211
TEST_F(BackgroundCompileTaskTest, EagerInnerFunctions) {
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  const char raw_script[] =
      "function g() {\n"
      "  f = function() {\n"
      "    // Simulate an eager IIFE with brackets.\n "
      "    var e = (function () { return 42; });\n"
      "    return e;\n"
      "  }\n"
      "  return f;\n"
      "}\n"
      "g();";
  test::ScriptResource* script =
      new test::ScriptResource(raw_script, strlen(raw_script));
  Handle<JSFunction> f = RunJS<JSFunction>(script);
  Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
  ASSERT_FALSE(shared->is_compiled());
227 228 229 230 231 232
  std::unique_ptr<BackgroundCompileTask> task(
      NewBackgroundCompileTask(isolate(), shared));

  task->Run();
  ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
      task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
233 234 235 236
  ASSERT_TRUE(shared->is_compiled());

  Handle<JSFunction> e = RunJS<JSFunction>("f();");

237
  ASSERT_TRUE(e->shared().is_compiled());
238 239
}

240
TEST_F(BackgroundCompileTaskTest, LazyInnerFunctions) {
241 242 243
  const char raw_script[] =
      "function g() {\n"
      "  f = function() {\n"
244
      "    function e() { return 42; };\n"
245 246 247 248 249 250 251
      "    return e;\n"
      "  }\n"
      "  return f;\n"
      "}\n"
      "g();";
  test::ScriptResource* script =
      new test::ScriptResource(raw_script, strlen(raw_script));
252
  Handle<JSFunction> f = RunJS<JSFunction>(script);
253 254
  Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
  ASSERT_FALSE(shared->is_compiled());
255 256 257 258 259 260
  std::unique_ptr<BackgroundCompileTask> task(
      NewBackgroundCompileTask(isolate(), shared));

  task->Run();
  ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
      task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
261
  ASSERT_TRUE(shared->is_compiled());
262

263
  Handle<JSFunction> e = RunJS<JSFunction>("f();");
264

265
  ASSERT_FALSE(e->shared().is_compiled());
266 267
}

268 269
}  // namespace internal
}  // namespace v8