v8/test/unittests/heap/local-factory-unittest.cc
Leszek Swirski f1589bbe11 [offthread] Change OffThreadIsolate to LocalIsolate
This patch introduces a new LocalIsolate and LocalFactory, which use
LocalHeap and replace OffThreadIsolate and OffThreadFactory. This allows
us to remove those classes, as well as the related OffThreadSpace,
OffThreadLargeObjectSpace, OffThreadHeap, and OffThreadTransferHandle.
OffThreadLogger becomes LocalLogger.

LocalHeap behaves more like Heap than OffThreadHeap did, so this allows
us to additionally remove the concept of "Finish" and "Publish" that the
OffThreadIsolate had, and allows us to internalize strings directly with
the newly-concurrent string table (where the implementation can now move
to FactoryBase).

This patch also removes the off-thread support from the deserializer
entirely, as well as removing the LocalIsolateWrapper which allowed
run-time distinction between Isolate and OffThreadIsolate. LocalHeap
doesn't support the reservation model used by the deserializer, and we
will likely move the deserializer to use LocalIsolate unconditionally
once we figure out the details of how to do this.

Bug: chromium:1011762

Change-Id: I1a1a0a72952b19a8a4c167c11a863c153a1252fc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2315990
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69397}
2020-08-14 10:57:27 +00:00

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()) {
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