b5bf34bce7
LocalHeap can be used on main thread, however allocation might cause a GC which works differently on the main thread than on a background thread. Support collection on main thread by directly performing the GC instead of requesting the GC as done on background threads. To allow for differentiation between main and background threads, LocalHeap/LocalIsolate now require an additional argument. Change-Id: I08094ea633e303e149913f21dff395da9e046534 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2463238 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Commit-Queue: Dominik Inführ <dinfuehr@chromium.org> Cr-Commit-Position: refs/heads/master@{#70590}
343 lines
12 KiB
C++
343 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(), ThreadKind::kMain),
|
|
unparked_scope_(local_isolate_.heap()) {
|
|
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_;
|
|
UnparkedScope unparked_scope_;
|
|
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
|