2018-10-09 09:55:47 +00:00
|
|
|
// Copyright 2018 the V8 project authors. All rights reserved.
|
2016-07-22 12:19:50 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
2016-08-05 13:18:42 +00:00
|
|
|
#include "include/v8.h"
|
2019-05-17 12:13:44 +00:00
|
|
|
#include "src/api/api-inl.h"
|
2016-09-05 11:54:00 +00:00
|
|
|
#include "src/ast/ast.h"
|
2016-08-05 13:18:42 +00:00
|
|
|
#include "src/ast/scopes.h"
|
2016-09-06 13:34:31 +00:00
|
|
|
#include "src/base/platform/semaphore.h"
|
2018-03-26 16:44:23 +00:00
|
|
|
#include "src/base/template-utils.h"
|
2019-05-21 09:30:15 +00:00
|
|
|
#include "src/codegen/compiler.h"
|
2019-05-22 07:55:37 +00:00
|
|
|
#include "src/execution/isolate-inl.h"
|
2019-05-24 13:51:59 +00:00
|
|
|
#include "src/flags/flags.h"
|
|
|
|
#include "src/init/v8.h"
|
2018-11-03 00:13:22 +00:00
|
|
|
#include "src/objects/smi.h"
|
2016-08-22 11:33:30 +00:00
|
|
|
#include "src/parsing/parse-info.h"
|
2018-10-09 09:55:47 +00:00
|
|
|
#include "src/parsing/parser.h"
|
2019-01-07 14:09:18 +00:00
|
|
|
#include "src/parsing/preparse-data.h"
|
2019-02-14 21:10:30 +00:00
|
|
|
#include "src/zone/zone-list-inl.h"
|
2017-04-18 14:01:12 +00:00
|
|
|
#include "test/unittests/test-helpers.h"
|
2016-07-22 12:19:50 +00:00
|
|
|
#include "test/unittests/test-utils.h"
|
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
class BackgroundCompileTaskTest : public TestWithNativeContext {
|
2016-12-12 15:35:41 +00:00
|
|
|
public:
|
2018-10-09 09:55:47 +00:00
|
|
|
BackgroundCompileTaskTest() : allocator_(isolate()->allocator()) {}
|
|
|
|
~BackgroundCompileTaskTest() override = default;
|
2016-12-12 15:35:41 +00:00
|
|
|
|
2018-09-20 13:18:16 +00:00
|
|
|
AccountingAllocator* allocator() { return allocator_; }
|
2016-12-12 15:35:41 +00:00
|
|
|
|
2016-09-06 13:34:31 +00:00
|
|
|
static void SetUpTestCase() {
|
2017-03-14 13:33:13 +00:00
|
|
|
CHECK_NULL(save_flags_);
|
|
|
|
save_flags_ = new SaveFlags();
|
2019-03-08 12:19:31 +00:00
|
|
|
TestWithNativeContext::SetUpTestCase();
|
2016-09-06 13:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void TearDownTestCase() {
|
2019-03-08 12:19:31 +00:00
|
|
|
TestWithNativeContext::TearDownTestCase();
|
2017-03-14 13:33:13 +00:00
|
|
|
CHECK_NOT_NULL(save_flags_);
|
|
|
|
delete save_flags_;
|
|
|
|
save_flags_ = nullptr;
|
2016-09-06 13:34:31 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
BackgroundCompileTask* NewBackgroundCompileTask(
|
2018-09-20 13:18:16 +00:00
|
|
|
Isolate* isolate, Handle<SharedFunctionInfo> shared,
|
|
|
|
size_t stack_size = FLAG_stack_size) {
|
|
|
|
std::unique_ptr<ParseInfo> outer_parse_info =
|
|
|
|
test::OuterParseInfoForShared(isolate, shared);
|
|
|
|
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");
|
|
|
|
DeclarationScope* script_scope = new (outer_parse_info->zone())
|
|
|
|
DeclarationScope(outer_parse_info->zone(), ast_value_factory);
|
|
|
|
DeclarationScope* function_scope =
|
|
|
|
new (outer_parse_info->zone()) DeclarationScope(
|
|
|
|
outer_parse_info->zone(), script_scope, FUNCTION_SCOPE);
|
|
|
|
function_scope->set_start_position(shared->StartPosition());
|
|
|
|
function_scope->set_end_position(shared->EndPosition());
|
2018-11-05 15:20:49 +00:00
|
|
|
std::vector<void*> buffer;
|
|
|
|
ScopedPtrList<Statement> statements(&buffer);
|
2018-09-20 13:18:16 +00:00
|
|
|
const FunctionLiteral* function_literal =
|
|
|
|
ast_node_factory.NewFunctionLiteral(
|
2018-11-05 15:20:49 +00:00
|
|
|
function_name, function_scope, statements, -1, -1, -1,
|
2018-09-20 13:18:16 +00:00
|
|
|
FunctionLiteral::kNoDuplicateParameters,
|
|
|
|
FunctionLiteral::kAnonymousExpression,
|
|
|
|
FunctionLiteral::kShouldEagerCompile, shared->StartPosition(), true,
|
2019-06-21 15:43:35 +00:00
|
|
|
shared->function_literal_id(), nullptr);
|
2018-09-20 13:18:16 +00:00
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
return new BackgroundCompileTask(
|
|
|
|
allocator(), outer_parse_info.get(), function_name, function_literal,
|
2018-09-20 13:18:16 +00:00
|
|
|
isolate->counters()->worker_thread_runtime_call_stats(),
|
2018-09-23 21:05:29 +00:00
|
|
|
isolate->counters()->compile_function_on_background(), FLAG_stack_size);
|
2018-09-20 13:18:16 +00:00
|
|
|
}
|
|
|
|
|
2016-09-06 13:34:31 +00:00
|
|
|
private:
|
2018-09-20 13:18:16 +00:00
|
|
|
AccountingAllocator* allocator_;
|
2017-03-14 13:33:13 +00:00
|
|
|
static SaveFlags* save_flags_;
|
2017-01-16 10:49:16 +00:00
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
DISALLOW_COPY_AND_ASSIGN(BackgroundCompileTaskTest);
|
2016-09-06 13:34:31 +00:00
|
|
|
};
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
SaveFlags* BackgroundCompileTaskTest::save_flags_ = nullptr;
|
2016-09-06 13:34:31 +00:00
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
TEST_F(BackgroundCompileTaskTest, Construct) {
|
2018-09-20 13:18:16 +00:00
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
test::CreateSharedFunctionInfo(isolate(), nullptr);
|
|
|
|
ASSERT_FALSE(shared->is_compiled());
|
2018-10-09 09:55:47 +00:00
|
|
|
std::unique_ptr<BackgroundCompileTask> task(
|
|
|
|
NewBackgroundCompileTask(isolate(), shared));
|
2016-08-01 11:27:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
TEST_F(BackgroundCompileTaskTest, SyntaxError) {
|
2018-07-04 09:31:54 +00:00
|
|
|
test::ScriptResource* script = new test::ScriptResource("^^^", strlen("^^^"));
|
2018-09-20 13:18:16 +00:00
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
test::CreateSharedFunctionInfo(isolate(), script);
|
2018-10-09 09:55:47 +00:00
|
|
|
std::unique_ptr<BackgroundCompileTask> task(
|
|
|
|
NewBackgroundCompileTask(isolate(), shared));
|
2016-08-01 11:27:58 +00:00
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
task->Run();
|
|
|
|
ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
|
|
|
|
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
|
2017-11-13 12:04:57 +00:00
|
|
|
ASSERT_TRUE(isolate()->has_pending_exception());
|
2016-08-23 12:10:39 +00:00
|
|
|
|
2017-11-13 12:04:57 +00:00
|
|
|
isolate()->clear_pending_exception();
|
2016-08-05 13:18:42 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
TEST_F(BackgroundCompileTaskTest, CompileAndRun) {
|
2018-09-20 13:18:16 +00:00
|
|
|
const char raw_script[] =
|
2016-08-25 10:24:53 +00:00
|
|
|
"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();";
|
2018-09-20 13:18:16 +00:00
|
|
|
test::ScriptResource* script =
|
|
|
|
new test::ScriptResource(raw_script, strlen(raw_script));
|
2017-11-13 12:04:57 +00:00
|
|
|
Handle<JSFunction> f = RunJS<JSFunction>(script);
|
2018-09-20 13:18:16 +00:00
|
|
|
Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
|
|
|
|
ASSERT_FALSE(shared->is_compiled());
|
2018-10-09 09:55:47 +00:00
|
|
|
std::unique_ptr<BackgroundCompileTask> task(
|
|
|
|
NewBackgroundCompileTask(isolate(), shared));
|
2016-08-25 10:24:53 +00:00
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
task->Run();
|
|
|
|
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
|
|
|
|
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
|
2018-09-20 13:18:16 +00:00
|
|
|
ASSERT_TRUE(shared->is_compiled());
|
2016-08-25 10:24:53 +00:00
|
|
|
|
2018-11-03 00:13:22 +00:00
|
|
|
Smi value = Smi::cast(*RunJS("f(100);"));
|
2016-08-25 10:24:53 +00:00
|
|
|
ASSERT_TRUE(value == Smi::FromInt(160));
|
|
|
|
}
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
TEST_F(BackgroundCompileTaskTest, CompileFailure) {
|
2016-08-25 10:24:53 +00:00
|
|
|
std::string raw_script("() { var a = ");
|
2018-09-20 13:18:16 +00:00
|
|
|
for (int i = 0; i < 10000; i++) {
|
2017-10-25 10:47:24 +00:00
|
|
|
// 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' - ";
|
2016-08-25 10:24:53 +00:00
|
|
|
}
|
|
|
|
raw_script += " 'x'; }";
|
2018-07-04 09:31:54 +00:00
|
|
|
test::ScriptResource* script =
|
|
|
|
new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
|
2018-09-20 13:18:16 +00:00
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
test::CreateSharedFunctionInfo(isolate(), script);
|
2018-10-09 09:55:47 +00:00
|
|
|
std::unique_ptr<BackgroundCompileTask> task(
|
|
|
|
NewBackgroundCompileTask(isolate(), shared, 100));
|
2016-08-25 10:24:53 +00:00
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
task->Run();
|
|
|
|
ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
|
|
|
|
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
|
2017-11-13 12:04:57 +00:00
|
|
|
ASSERT_TRUE(isolate()->has_pending_exception());
|
2016-08-25 10:24:53 +00:00
|
|
|
|
2017-11-13 12:04:57 +00:00
|
|
|
isolate()->clear_pending_exception();
|
2016-08-25 10:24:53 +00:00
|
|
|
}
|
|
|
|
|
2016-09-06 13:34:31 +00:00
|
|
|
class CompileTask : public Task {
|
|
|
|
public:
|
2018-10-09 09:55:47 +00:00
|
|
|
CompileTask(BackgroundCompileTask* task, base::Semaphore* semaphore)
|
|
|
|
: task_(task), semaphore_(semaphore) {}
|
2018-09-13 09:48:15 +00:00
|
|
|
~CompileTask() override = default;
|
2016-09-06 13:34:31 +00:00
|
|
|
|
|
|
|
void Run() override {
|
2018-10-09 09:55:47 +00:00
|
|
|
task_->Run();
|
2016-09-06 13:34:31 +00:00
|
|
|
semaphore_->Signal();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-10-09 09:55:47 +00:00
|
|
|
BackgroundCompileTask* task_;
|
2016-09-06 13:34:31 +00:00
|
|
|
base::Semaphore* semaphore_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CompileTask);
|
|
|
|
};
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
TEST_F(BackgroundCompileTaskTest, CompileOnBackgroundThread) {
|
2016-09-06 13:34:31 +00:00
|
|
|
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;"
|
|
|
|
"}";
|
2018-07-04 09:31:54 +00:00
|
|
|
test::ScriptResource* script =
|
|
|
|
new test::ScriptResource(raw_script, strlen(raw_script));
|
2018-09-20 13:18:16 +00:00
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
test::CreateSharedFunctionInfo(isolate(), script);
|
2018-10-09 09:55:47 +00:00
|
|
|
std::unique_ptr<BackgroundCompileTask> task(
|
|
|
|
NewBackgroundCompileTask(isolate(), shared));
|
2016-09-06 13:34:31 +00:00
|
|
|
|
|
|
|
base::Semaphore semaphore(0);
|
2018-10-09 09:55:47 +00:00
|
|
|
auto background_task = base::make_unique<CompileTask>(task.get(), &semaphore);
|
2018-09-20 13:18:16 +00:00
|
|
|
|
2018-03-26 16:44:23 +00:00
|
|
|
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(background_task));
|
2016-09-06 13:34:31 +00:00
|
|
|
semaphore.Wait();
|
2018-10-09 09:55:47 +00:00
|
|
|
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
|
|
|
|
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
|
|
|
|
ASSERT_TRUE(shared->is_compiled());
|
2016-09-06 13:34:31 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
TEST_F(BackgroundCompileTaskTest, EagerInnerFunctions) {
|
2018-09-23 21:05:29 +00:00
|
|
|
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());
|
2018-10-09 09:55:47 +00:00
|
|
|
std::unique_ptr<BackgroundCompileTask> task(
|
|
|
|
NewBackgroundCompileTask(isolate(), shared));
|
|
|
|
|
|
|
|
task->Run();
|
|
|
|
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
|
|
|
|
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
|
2018-09-23 21:05:29 +00:00
|
|
|
ASSERT_TRUE(shared->is_compiled());
|
|
|
|
|
|
|
|
Handle<JSFunction> e = RunJS<JSFunction>("f();");
|
|
|
|
|
|
|
|
ASSERT_TRUE(e->shared().is_compiled());
|
|
|
|
}
|
|
|
|
|
2018-10-09 09:55:47 +00:00
|
|
|
TEST_F(BackgroundCompileTaskTest, LazyInnerFunctions) {
|
2018-09-20 13:18:16 +00:00
|
|
|
const char raw_script[] =
|
|
|
|
"function g() {\n"
|
|
|
|
" f = function() {\n"
|
2018-09-23 21:05:29 +00:00
|
|
|
" function e() { return 42; };\n"
|
2018-09-20 13:18:16 +00:00
|
|
|
" return e;\n"
|
|
|
|
" }\n"
|
|
|
|
" return f;\n"
|
|
|
|
"}\n"
|
|
|
|
"g();";
|
|
|
|
test::ScriptResource* script =
|
|
|
|
new test::ScriptResource(raw_script, strlen(raw_script));
|
2017-11-13 12:04:57 +00:00
|
|
|
Handle<JSFunction> f = RunJS<JSFunction>(script);
|
2018-09-20 13:18:16 +00:00
|
|
|
Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
|
|
|
|
ASSERT_FALSE(shared->is_compiled());
|
2018-10-09 09:55:47 +00:00
|
|
|
std::unique_ptr<BackgroundCompileTask> task(
|
|
|
|
NewBackgroundCompileTask(isolate(), shared));
|
|
|
|
|
|
|
|
task->Run();
|
|
|
|
ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
|
|
|
|
task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
|
2018-09-20 13:18:16 +00:00
|
|
|
ASSERT_TRUE(shared->is_compiled());
|
2016-12-16 12:38:17 +00:00
|
|
|
|
2017-11-13 12:04:57 +00:00
|
|
|
Handle<JSFunction> e = RunJS<JSFunction>("f();");
|
2016-12-16 12:38:17 +00:00
|
|
|
|
2017-09-11 14:23:35 +00:00
|
|
|
ASSERT_FALSE(e->shared().is_compiled());
|
2016-12-16 12:38:17 +00:00
|
|
|
}
|
|
|
|
|
2016-07-22 12:19:50 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|