dc45361e53
This is a reland of e95e1b6234
After landing https://crrev.com/c/2546682, this CL can be relanded
without changes.
Original change's description:
> [heap] Introduce LocalIsolate for main thread
>
> Add a LocalIsolate for the main thread to Isolate. This LocalIsolate is
> kept alive during the whole lifetime of the Isolate. The main thread
> LocalIsolate starts in the Running state in contrast to the background
> thread LocalIsolates (those start in Parked).
>
> Code paths in Turbofan that used to create a LocalIsolate on the main
> thread can now simply use the main thread LocalIsolate.
>
> LocalIsolate for the main thread will help in reducing differences
> between the main and background threads. The goal is that the main
> thread behaves more like a background thread.
>
> The main thread LocalIsolate should also make it simpler to share code
> between main thread and background threads by using LocalIsolate for
> both.
>
> Bug: v8:10315
> Change-Id: I7fd61d305a6fd7079e2319d75c291c1021e70018
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2509593
> Reviewed-by: Simon Zünd <szuend@chromium.org>
> Reviewed-by: Yang Guo <yangguo@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#71226}
Bug: v8:10315
Change-Id: I418b1217aeac4f3c44a0aa514dea9864f8a58656
TBR: szuend@chromium.org, yangguo@chromium.org, ulan@chromium.org, leszeks@chromium.org, neis@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2543399
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71274}
341 lines
12 KiB
C++
341 lines
12 KiB
C++
// Copyright 2020 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 <cmath>
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <memory>
|
|
|
|
#include "src/ast/ast-value-factory.h"
|
|
#include "src/ast/ast.h"
|
|
#include "src/ast/scopes.h"
|
|
#include "src/common/assert-scope.h"
|
|
#include "src/common/globals.h"
|
|
#include "src/handles/handles-inl.h"
|
|
#include "src/handles/handles.h"
|
|
#include "src/handles/maybe-handles.h"
|
|
#include "src/heap/local-factory-inl.h"
|
|
#include "src/objects/fixed-array.h"
|
|
#include "src/objects/script.h"
|
|
#include "src/objects/shared-function-info.h"
|
|
#include "src/objects/string.h"
|
|
#include "src/parsing/parse-info.h"
|
|
#include "src/parsing/parser.h"
|
|
#include "src/parsing/rewriter.h"
|
|
#include "src/parsing/scanner-character-streams.h"
|
|
#include "src/parsing/scanner.h"
|
|
#include "src/strings/unicode-inl.h"
|
|
#include "src/utils/utils.h"
|
|
#include "test/unittests/test-utils.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
class LocalIsolate;
|
|
|
|
namespace {
|
|
|
|
std::vector<uint16_t> DecodeUtf8(const std::string& string) {
|
|
if (string.empty()) return {};
|
|
|
|
auto utf8_data =
|
|
Vector<const uint8_t>::cast(VectorOf(string.data(), string.length()));
|
|
Utf8Decoder decoder(utf8_data);
|
|
|
|
std::vector<uint16_t> utf16(decoder.utf16_length());
|
|
decoder.Decode(&utf16[0], utf8_data);
|
|
|
|
return utf16;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
class LocalFactoryTest : public TestWithIsolateAndZone {
|
|
public:
|
|
LocalFactoryTest()
|
|
: TestWithIsolateAndZone(),
|
|
state_(isolate()),
|
|
parse_info_(
|
|
isolate(),
|
|
UnoptimizedCompileFlags::ForToplevelCompile(
|
|
isolate(), true, construct_language_mode(FLAG_use_strict),
|
|
REPLMode::kNo),
|
|
&state_),
|
|
local_isolate_(isolate()->main_thread_local_isolate()) {
|
|
FLAG_concurrent_allocation = true;
|
|
}
|
|
|
|
FunctionLiteral* ParseProgram(const char* source) {
|
|
auto utf16_source = DecodeUtf8(source);
|
|
|
|
// Normally this would be an external string or whatever, we don't have to
|
|
// worry about it for now.
|
|
source_string_ =
|
|
factory()->NewStringFromUtf8(CStrVector(source)).ToHandleChecked();
|
|
|
|
parse_info_.set_character_stream(
|
|
ScannerStream::ForTesting(utf16_source.data(), utf16_source.size()));
|
|
|
|
{
|
|
DisallowHeapAllocation no_allocation;
|
|
DisallowHandleAllocation no_handles;
|
|
DisallowHeapAccess no_heap_access;
|
|
|
|
Parser parser(parse_info());
|
|
parser.InitializeEmptyScopeChain(parse_info());
|
|
parser.ParseOnBackground(parse_info(), 0, 0, kFunctionLiteralIdTopLevel);
|
|
}
|
|
|
|
parse_info()->ast_value_factory()->Internalize(local_isolate());
|
|
DeclarationScope::AllocateScopeInfos(parse_info(), local_isolate());
|
|
|
|
script_ = parse_info_.CreateScript(local_isolate(),
|
|
local_factory()->empty_string(),
|
|
kNullMaybeHandle, ScriptOriginOptions());
|
|
|
|
// Create the SFI list on the script so that SFI SetScript works.
|
|
Handle<WeakFixedArray> infos = local_factory()->NewWeakFixedArray(
|
|
parse_info()->max_function_literal_id() + 1, AllocationType::kOld);
|
|
script_->set_shared_function_infos(*infos);
|
|
|
|
return parse_info()->literal();
|
|
}
|
|
|
|
ParseInfo* parse_info() { return &parse_info_; }
|
|
|
|
Handle<Script> script() { return script_; }
|
|
|
|
LocalIsolate* local_isolate() { return local_isolate_; }
|
|
LocalFactory* local_factory() { return local_isolate()->factory(); }
|
|
|
|
private:
|
|
SaveFlags save_flags_;
|
|
UnoptimizedCompileState state_;
|
|
ParseInfo parse_info_;
|
|
LocalIsolate* local_isolate_;
|
|
Handle<String> source_string_;
|
|
Handle<Script> script_;
|
|
};
|
|
|
|
TEST_F(LocalFactoryTest, OneByteInternalizedString_IsAddedToStringTable) {
|
|
Vector<const uint8_t> string_vector = StaticOneByteVector("foo");
|
|
|
|
Handle<String> string;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
Handle<String> local_string =
|
|
local_factory()->InternalizeString(string_vector);
|
|
|
|
string = local_isolate()->heap()->NewPersistentHandle(local_string);
|
|
}
|
|
|
|
EXPECT_TRUE(string->IsOneByteEqualTo(CStrVector("foo")));
|
|
EXPECT_TRUE(string->IsInternalizedString());
|
|
|
|
Handle<String> same_string = isolate()
|
|
->factory()
|
|
->NewStringFromOneByte(string_vector)
|
|
.ToHandleChecked();
|
|
EXPECT_NE(*string, *same_string);
|
|
EXPECT_FALSE(same_string->IsInternalizedString());
|
|
|
|
Handle<String> internalized_string =
|
|
isolate()->factory()->InternalizeString(same_string);
|
|
EXPECT_EQ(*string, *internalized_string);
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, OneByteInternalizedString_DuplicateIsDeduplicated) {
|
|
Vector<const uint8_t> string_vector = StaticOneByteVector("foo");
|
|
|
|
Handle<String> string_1;
|
|
Handle<String> string_2;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
Handle<String> local_string_1 =
|
|
local_factory()->InternalizeString(string_vector);
|
|
Handle<String> local_string_2 =
|
|
local_factory()->InternalizeString(string_vector);
|
|
|
|
string_1 = local_isolate()->heap()->NewPersistentHandle(local_string_1);
|
|
string_2 = local_isolate()->heap()->NewPersistentHandle(local_string_2);
|
|
}
|
|
|
|
EXPECT_TRUE(string_1->IsOneByteEqualTo(CStrVector("foo")));
|
|
EXPECT_TRUE(string_1->IsInternalizedString());
|
|
EXPECT_EQ(*string_1, *string_2);
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, AstRawString_IsInternalized) {
|
|
AstValueFactory ast_value_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
|
|
const AstRawString* raw_string = ast_value_factory.GetOneByteString("foo");
|
|
|
|
Handle<String> string;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
ast_value_factory.Internalize(local_isolate());
|
|
|
|
string = local_isolate()->heap()->NewPersistentHandle(raw_string->string());
|
|
}
|
|
|
|
EXPECT_TRUE(string->IsOneByteEqualTo(CStrVector("foo")));
|
|
EXPECT_TRUE(string->IsInternalizedString());
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, AstConsString_CreatesConsString) {
|
|
AstValueFactory ast_value_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
|
|
Handle<String> string;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
const AstRawString* foo_string = ast_value_factory.GetOneByteString("foo");
|
|
const AstRawString* bar_string =
|
|
ast_value_factory.GetOneByteString("bar-plus-padding-for-length");
|
|
AstConsString* foobar_string =
|
|
ast_value_factory.NewConsString(foo_string, bar_string);
|
|
|
|
ast_value_factory.Internalize(local_isolate());
|
|
|
|
string = local_isolate()->heap()->NewPersistentHandle(
|
|
foobar_string->GetString(local_isolate()));
|
|
}
|
|
|
|
EXPECT_TRUE(string->IsConsString());
|
|
EXPECT_TRUE(string->Equals(*isolate()->factory()->NewStringFromStaticChars(
|
|
"foobar-plus-padding-for-length")));
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, EmptyScript) {
|
|
FunctionLiteral* program = ParseProgram("");
|
|
|
|
Handle<SharedFunctionInfo> shared;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
shared = local_isolate()->heap()->NewPersistentHandle(
|
|
local_factory()->NewSharedFunctionInfoForLiteral(program, script(),
|
|
true));
|
|
}
|
|
Handle<SharedFunctionInfo> root_sfi = shared;
|
|
|
|
EXPECT_EQ(root_sfi->function_literal_id(), 0);
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, LazyFunction) {
|
|
FunctionLiteral* program = ParseProgram("function lazy() {}");
|
|
FunctionLiteral* lazy = program->scope()
|
|
->declarations()
|
|
->AtForTest(0)
|
|
->AsFunctionDeclaration()
|
|
->fun();
|
|
|
|
Handle<SharedFunctionInfo> shared;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
shared = local_isolate()->heap()->NewPersistentHandle(
|
|
local_factory()->NewSharedFunctionInfoForLiteral(lazy, script(), true));
|
|
}
|
|
Handle<SharedFunctionInfo> lazy_sfi = shared;
|
|
|
|
EXPECT_EQ(lazy_sfi->function_literal_id(), 1);
|
|
EXPECT_TRUE(lazy_sfi->Name().IsOneByteEqualTo(CStrVector("lazy")));
|
|
EXPECT_FALSE(lazy_sfi->is_compiled());
|
|
EXPECT_TRUE(lazy_sfi->HasUncompiledDataWithoutPreparseData());
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, EagerFunction) {
|
|
FunctionLiteral* program = ParseProgram("(function eager() {})");
|
|
// Rewritten to `.result = (function eager() {}); return .result`
|
|
FunctionLiteral* eager = program->body()
|
|
->at(0)
|
|
->AsExpressionStatement()
|
|
->expression()
|
|
->AsAssignment()
|
|
->value()
|
|
->AsFunctionLiteral();
|
|
|
|
Handle<SharedFunctionInfo> shared;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
shared = local_isolate()->heap()->NewPersistentHandle(
|
|
local_factory()->NewSharedFunctionInfoForLiteral(eager, script(),
|
|
true));
|
|
}
|
|
Handle<SharedFunctionInfo> eager_sfi = shared;
|
|
|
|
EXPECT_EQ(eager_sfi->function_literal_id(), 1);
|
|
EXPECT_TRUE(eager_sfi->Name().IsOneByteEqualTo(CStrVector("eager")));
|
|
EXPECT_FALSE(eager_sfi->HasUncompiledData());
|
|
// TODO(leszeks): Add compilation support and enable these checks.
|
|
// EXPECT_TRUE(eager_sfi->is_compiled());
|
|
// EXPECT_TRUE(eager_sfi->HasBytecodeArray());
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, ImplicitNameFunction) {
|
|
FunctionLiteral* program = ParseProgram("let implicit_name = function() {}");
|
|
FunctionLiteral* implicit_name = program->body()
|
|
->at(0)
|
|
->AsBlock()
|
|
->statements()
|
|
->at(0)
|
|
->AsExpressionStatement()
|
|
->expression()
|
|
->AsAssignment()
|
|
->value()
|
|
->AsFunctionLiteral();
|
|
|
|
Handle<SharedFunctionInfo> shared;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
shared = local_isolate()->heap()->NewPersistentHandle(
|
|
local_factory()->NewSharedFunctionInfoForLiteral(implicit_name,
|
|
script(), true));
|
|
}
|
|
Handle<SharedFunctionInfo> implicit_name_sfi = shared;
|
|
|
|
EXPECT_EQ(implicit_name_sfi->function_literal_id(), 1);
|
|
EXPECT_TRUE(
|
|
implicit_name_sfi->Name().IsOneByteEqualTo(CStrVector("implicit_name")));
|
|
}
|
|
|
|
TEST_F(LocalFactoryTest, GCDuringPublish) {
|
|
FunctionLiteral* program = ParseProgram("let implicit_name = function() {}");
|
|
FunctionLiteral* implicit_name = program->body()
|
|
->at(0)
|
|
->AsBlock()
|
|
->statements()
|
|
->at(0)
|
|
->AsExpressionStatement()
|
|
->expression()
|
|
->AsAssignment()
|
|
->value()
|
|
->AsFunctionLiteral();
|
|
|
|
Handle<SharedFunctionInfo> shared;
|
|
{
|
|
LocalHandleScope handle_scope(local_isolate());
|
|
|
|
shared = local_isolate()->heap()->NewPersistentHandle(
|
|
local_factory()->NewSharedFunctionInfoForLiteral(implicit_name,
|
|
script(), true));
|
|
}
|
|
Handle<SharedFunctionInfo> implicit_name_sfi = shared;
|
|
|
|
EXPECT_EQ(implicit_name_sfi->function_literal_id(), 1);
|
|
EXPECT_TRUE(
|
|
implicit_name_sfi->Name().IsOneByteEqualTo(CStrVector("implicit_name")));
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|