3c9549a337
Fixing clang-tidy warning. Bug: v8:8015 Change-Id: I41b3d7c41ee9f8ff12f70feddcf38f2c9efc5049 Reviewed-on: https://chromium-review.googlesource.com/1224056 Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Commit-Queue: Florian Sattler <sattlerf@google.com> Cr-Commit-Position: refs/heads/master@{#55917}
279 lines
9.2 KiB
C++
279 lines
9.2 KiB
C++
// 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>
|
|
|
|
#include "include/v8.h"
|
|
#include "src/api-inl.h"
|
|
#include "src/ast/ast.h"
|
|
#include "src/ast/scopes.h"
|
|
#include "src/base/platform/semaphore.h"
|
|
#include "src/base/template-utils.h"
|
|
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
|
|
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
|
|
#include "src/compiler-dispatcher/unoptimized-compile-job.h"
|
|
#include "src/flags.h"
|
|
#include "src/isolate-inl.h"
|
|
#include "src/parsing/parse-info.h"
|
|
#include "src/v8.h"
|
|
#include "test/unittests/test-helpers.h"
|
|
#include "test/unittests/test-utils.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
class UnoptimizedCompileJobTest : public TestWithNativeContext {
|
|
public:
|
|
UnoptimizedCompileJobTest() : tracer_(isolate()) {}
|
|
~UnoptimizedCompileJobTest() override = default;
|
|
|
|
CompilerDispatcherTracer* tracer() { return &tracer_; }
|
|
|
|
static void SetUpTestCase() {
|
|
CHECK_NULL(save_flags_);
|
|
save_flags_ = new SaveFlags();
|
|
TestWithNativeContext ::SetUpTestCase();
|
|
}
|
|
|
|
static void TearDownTestCase() {
|
|
TestWithNativeContext ::TearDownTestCase();
|
|
CHECK_NOT_NULL(save_flags_);
|
|
delete save_flags_;
|
|
save_flags_ = nullptr;
|
|
}
|
|
|
|
static Variable* LookupVariableByName(UnoptimizedCompileJob* job,
|
|
const char* name) {
|
|
const AstRawString* name_raw_string =
|
|
job->parse_info_->ast_value_factory()->GetOneByteString(name);
|
|
return job->parse_info_->literal()->scope()->Lookup(name_raw_string);
|
|
}
|
|
|
|
private:
|
|
CompilerDispatcherTracer tracer_;
|
|
static SaveFlags* save_flags_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(UnoptimizedCompileJobTest);
|
|
};
|
|
|
|
SaveFlags* UnoptimizedCompileJobTest::save_flags_ = nullptr;
|
|
|
|
#define ASSERT_JOB_STATUS(STATUS, JOB) ASSERT_EQ(STATUS, JOB->status())
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, Construct) {
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), test::CreateSharedFunctionInfo(isolate(), nullptr),
|
|
FLAG_stack_size));
|
|
}
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, StateTransitions) {
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), test::CreateSharedFunctionInfo(isolate(), nullptr),
|
|
FLAG_stack_size));
|
|
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
job->PrepareOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kPrepared, job);
|
|
job->Compile(false);
|
|
ASSERT_FALSE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kCompiled, job);
|
|
job->FinalizeOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
|
|
job->ResetOnMainThread(isolate());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
}
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, SyntaxError) {
|
|
test::ScriptResource* script = new test::ScriptResource("^^^", strlen("^^^"));
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), test::CreateSharedFunctionInfo(isolate(), script),
|
|
FLAG_stack_size));
|
|
|
|
job->PrepareOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->Compile(false);
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->ReportErrorsOnMainThread(isolate());
|
|
ASSERT_TRUE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kFailed, job);
|
|
ASSERT_TRUE(isolate()->has_pending_exception());
|
|
|
|
isolate()->clear_pending_exception();
|
|
|
|
job->ResetOnMainThread(isolate());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
}
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, 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 = RunJS<JSFunction>(script);
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), handle(f->shared(), f->GetIsolate()),
|
|
FLAG_stack_size));
|
|
|
|
job->PrepareOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->Compile(false);
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->FinalizeOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
|
|
|
|
Smi* value = Smi::cast(*RunJS("f(100);"));
|
|
ASSERT_TRUE(value == Smi::FromInt(160));
|
|
|
|
job->ResetOnMainThread(isolate());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
}
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, CompileFailureToAnalyse) {
|
|
std::string raw_script("() { var a = ");
|
|
for (int i = 0; i < 500000; i++) {
|
|
// 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' - ";
|
|
}
|
|
raw_script += " 'x'; }";
|
|
test::ScriptResource* script =
|
|
new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), test::CreateSharedFunctionInfo(isolate(), script),
|
|
100));
|
|
|
|
job->PrepareOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->Compile(false);
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->ReportErrorsOnMainThread(isolate());
|
|
ASSERT_TRUE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kFailed, job);
|
|
ASSERT_TRUE(isolate()->has_pending_exception());
|
|
|
|
isolate()->clear_pending_exception();
|
|
job->ResetOnMainThread(isolate());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
}
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, CompileFailureToFinalize) {
|
|
std::string raw_script("() { var a = ");
|
|
for (int i = 0; i < 500; i++) {
|
|
// Alternate + and - to avoid n-ary operation nodes.
|
|
raw_script += "'x' + 'x' - ";
|
|
}
|
|
raw_script += " 'x'; }";
|
|
test::ScriptResource* script =
|
|
new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), test::CreateSharedFunctionInfo(isolate(), script),
|
|
50));
|
|
|
|
job->PrepareOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->Compile(false);
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->ReportErrorsOnMainThread(isolate());
|
|
ASSERT_TRUE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kFailed, job);
|
|
ASSERT_TRUE(isolate()->has_pending_exception());
|
|
|
|
isolate()->clear_pending_exception();
|
|
job->ResetOnMainThread(isolate());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
}
|
|
|
|
class CompileTask : public Task {
|
|
public:
|
|
CompileTask(UnoptimizedCompileJob* job, base::Semaphore* semaphore)
|
|
: job_(job), semaphore_(semaphore) {}
|
|
~CompileTask() override = default;
|
|
|
|
void Run() override {
|
|
job_->Compile(true);
|
|
ASSERT_FALSE(job_->IsFailed());
|
|
semaphore_->Signal();
|
|
}
|
|
|
|
private:
|
|
UnoptimizedCompileJob* job_;
|
|
base::Semaphore* semaphore_;
|
|
DISALLOW_COPY_AND_ASSIGN(CompileTask);
|
|
};
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, 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;"
|
|
"}";
|
|
test::ScriptResource* script =
|
|
new test::ScriptResource(raw_script, strlen(raw_script));
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), test::CreateSharedFunctionInfo(isolate(), script),
|
|
100));
|
|
|
|
job->PrepareOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
|
|
base::Semaphore semaphore(0);
|
|
auto background_task = base::make_unique<CompileTask>(job.get(), &semaphore);
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kPrepared, job);
|
|
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(background_task));
|
|
semaphore.Wait();
|
|
job->FinalizeOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
|
|
|
|
job->ResetOnMainThread(isolate());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
}
|
|
|
|
TEST_F(UnoptimizedCompileJobTest, LazyInnerFunctions) {
|
|
const char script[] =
|
|
"f = function() {\n"
|
|
" e = (function() { return 42; });\n"
|
|
" return e;\n"
|
|
"};\n"
|
|
"f;";
|
|
Handle<JSFunction> f = RunJS<JSFunction>(script);
|
|
|
|
std::unique_ptr<UnoptimizedCompileJob> job(new UnoptimizedCompileJob(
|
|
isolate(), tracer(), handle(f->shared(), f->GetIsolate()),
|
|
FLAG_stack_size));
|
|
|
|
job->PrepareOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->Compile(false);
|
|
ASSERT_FALSE(job->IsFailed());
|
|
job->FinalizeOnMainThread(isolate());
|
|
ASSERT_FALSE(job->IsFailed());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kDone, job);
|
|
|
|
Handle<JSFunction> e = RunJS<JSFunction>("f();");
|
|
|
|
ASSERT_FALSE(e->shared()->is_compiled());
|
|
|
|
job->ResetOnMainThread(isolate());
|
|
ASSERT_JOB_STATUS(CompilerDispatcherJob::Status::kInitial, job);
|
|
}
|
|
|
|
#undef ASSERT_JOB_STATUS
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|