7a20b6b9d3
The Factory/OffThreadFactory allows us to cleanly separate object construction behaviour between main-thread and off-thread in a syntactically consistent way (so that methods templated on the factory type can be made to work on both). However, there are cases where we also have to access the Isolate, for handle creation or exception throwing. So far we have been pushing more and more "customization points" into the factories to allow these factory-templated methods to dispatch on this isolate behaviour via these factory methods. Unfortunately, this is an increasing layering violation between Factory and Isolate, particularly around exception handling. Now, we introduce an OffThreadIsolate, analogous to Isolate in the same way as OffThreadFactory is analogous to Factory. All methods which were templated on Factory are now templated on Isolate, and methods which used to take an Isolate, and which were recently changed to take a templated Factory, are changed/reverted to take a templated Isolate. OffThreadFactory gets an isolate() method to match Factory's. Notably, FactoryHandle is changed to "HandleFor", where the template argument can be either of the Isolate type or the Factory type (allowing us to dispatch on both depending on what is available). Bug: chromium:1011762 Change-Id: Id144176f7da534dd76f3d535ab2ade008b6845e3 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2030909 Commit-Queue: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#66101}
403 lines
16 KiB
C++
403 lines
16 KiB
C++
// Copyright 2014 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 "src/init/v8.h"
|
|
|
|
#include "src/ast/ast-value-factory.h"
|
|
#include "src/execution/isolate.h"
|
|
#include "src/handles/handles-inl.h"
|
|
#include "src/heap/factory.h"
|
|
#include "src/interpreter/constant-array-builder.h"
|
|
#include "src/numbers/hash-seed-inl.h"
|
|
#include "src/objects/objects-inl.h"
|
|
#include "test/unittests/test-utils.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace interpreter {
|
|
|
|
class ConstantArrayBuilderTest : public TestWithIsolateAndZone {
|
|
public:
|
|
ConstantArrayBuilderTest() = default;
|
|
~ConstantArrayBuilderTest() override = default;
|
|
|
|
static const size_t k8BitCapacity = ConstantArrayBuilder::k8BitCapacity;
|
|
static const size_t k16BitCapacity = ConstantArrayBuilder::k16BitCapacity;
|
|
};
|
|
|
|
STATIC_CONST_MEMBER_DEFINITION const size_t
|
|
ConstantArrayBuilderTest::k16BitCapacity;
|
|
STATIC_CONST_MEMBER_DEFINITION const size_t
|
|
ConstantArrayBuilderTest::k8BitCapacity;
|
|
|
|
TEST_F(ConstantArrayBuilderTest, AllocateAllEntries) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
for (size_t i = 0; i < k16BitCapacity; i++) {
|
|
builder.Insert(i + 0.5);
|
|
}
|
|
CHECK_EQ(builder.size(), k16BitCapacity);
|
|
ast_factory.Internalize(isolate());
|
|
for (size_t i = 0; i < k16BitCapacity; i++) {
|
|
CHECK_EQ(
|
|
Handle<HeapNumber>::cast(builder.At(i, isolate()).ToHandleChecked())
|
|
->value(),
|
|
i + 0.5);
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, ToFixedArray) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
static const int kNumberOfElements = 37;
|
|
for (int i = 0; i < kNumberOfElements; i++) {
|
|
builder.Insert(i + 0.5);
|
|
}
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
ASSERT_EQ(kNumberOfElements, constant_array->length());
|
|
for (int i = 0; i < kNumberOfElements; i++) {
|
|
Handle<Object> actual(constant_array->get(i), isolate());
|
|
Handle<Object> expected = builder.At(i, isolate()).ToHandleChecked();
|
|
ASSERT_EQ(expected->Number(), actual->Number()) << "Failure at index " << i;
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, ToLargeFixedArray) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
static const int kNumberOfElements = 37373;
|
|
for (int i = 0; i < kNumberOfElements; i++) {
|
|
builder.Insert(i + 0.5);
|
|
}
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
ASSERT_EQ(kNumberOfElements, constant_array->length());
|
|
for (int i = 0; i < kNumberOfElements; i++) {
|
|
Handle<Object> actual(constant_array->get(i), isolate());
|
|
Handle<Object> expected = builder.At(i, isolate()).ToHandleChecked();
|
|
ASSERT_EQ(expected->Number(), actual->Number()) << "Failure at index " << i;
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, ToLargeFixedArrayWithReservations) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
static const int kNumberOfElements = 37373;
|
|
for (int i = 0; i < kNumberOfElements; i++) {
|
|
builder.CommitReservedEntry(builder.CreateReservedEntry(), Smi::FromInt(i));
|
|
}
|
|
ast_factory.Internalize(isolate());
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
ASSERT_EQ(kNumberOfElements, constant_array->length());
|
|
for (int i = 0; i < kNumberOfElements; i++) {
|
|
Handle<Object> actual(constant_array->get(i), isolate());
|
|
Handle<Object> expected = builder.At(i, isolate()).ToHandleChecked();
|
|
ASSERT_EQ(expected->Number(), actual->Number()) << "Failure at index " << i;
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, AllocateEntriesWithIdx8Reservations) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
for (size_t reserved = 1; reserved < k8BitCapacity; reserved *= 3) {
|
|
ConstantArrayBuilder builder(zone());
|
|
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
for (size_t i = 0; i < reserved; i++) {
|
|
OperandSize operand_size = builder.CreateReservedEntry();
|
|
CHECK_EQ(operand_size, OperandSize::kByte);
|
|
}
|
|
for (size_t i = 0; i < 2 * k8BitCapacity; i++) {
|
|
builder.CommitReservedEntry(builder.CreateReservedEntry(),
|
|
Smi::FromInt(static_cast<int>(i)));
|
|
if (i + reserved < k8BitCapacity) {
|
|
CHECK_LE(builder.size(), k8BitCapacity);
|
|
CHECK_EQ(builder.size(), i + 1);
|
|
} else {
|
|
CHECK_GE(builder.size(), k8BitCapacity);
|
|
CHECK_EQ(builder.size(), i + reserved + 1);
|
|
}
|
|
}
|
|
CHECK_EQ(builder.size(), 2 * k8BitCapacity + reserved);
|
|
|
|
// Commit reserved entries with duplicates and check size does not change.
|
|
DCHECK_EQ(reserved + 2 * k8BitCapacity, builder.size());
|
|
size_t duplicates_in_idx8_space =
|
|
std::min(reserved, k8BitCapacity - reserved);
|
|
for (size_t i = 0; i < duplicates_in_idx8_space; i++) {
|
|
builder.CommitReservedEntry(OperandSize::kByte,
|
|
Smi::FromInt(static_cast<int>(i)));
|
|
DCHECK_EQ(reserved + 2 * k8BitCapacity, builder.size());
|
|
}
|
|
|
|
// Now make reservations, and commit them with unique entries.
|
|
for (size_t i = 0; i < duplicates_in_idx8_space; i++) {
|
|
OperandSize operand_size = builder.CreateReservedEntry();
|
|
CHECK_EQ(operand_size, OperandSize::kByte);
|
|
}
|
|
for (size_t i = 0; i < duplicates_in_idx8_space; i++) {
|
|
Smi value = Smi::FromInt(static_cast<int>(2 * k8BitCapacity + i));
|
|
size_t index = builder.CommitReservedEntry(OperandSize::kByte, value);
|
|
CHECK_EQ(index, k8BitCapacity - reserved + i);
|
|
}
|
|
|
|
// Clear any remaining uncommited reservations.
|
|
for (size_t i = 0; i < reserved - duplicates_in_idx8_space; i++) {
|
|
builder.DiscardReservedEntry(OperandSize::kByte);
|
|
}
|
|
|
|
ast_factory.Internalize(isolate());
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
CHECK_EQ(constant_array->length(),
|
|
static_cast<int>(2 * k8BitCapacity + reserved));
|
|
|
|
// Check all committed values match expected
|
|
for (size_t i = 0; i < k8BitCapacity - reserved; i++) {
|
|
Object value = constant_array->get(static_cast<int>(i));
|
|
Smi smi = Smi::FromInt(static_cast<int>(i));
|
|
CHECK(value.SameValue(smi));
|
|
}
|
|
for (size_t i = k8BitCapacity; i < 2 * k8BitCapacity + reserved; i++) {
|
|
Object value = constant_array->get(static_cast<int>(i));
|
|
Smi smi = Smi::FromInt(static_cast<int>(i - reserved));
|
|
CHECK(value.SameValue(smi));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, AllocateEntriesWithWideReservations) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
for (size_t reserved = 1; reserved < k8BitCapacity; reserved *= 3) {
|
|
ConstantArrayBuilder builder(zone());
|
|
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
builder.CommitReservedEntry(builder.CreateReservedEntry(),
|
|
Smi::FromInt(static_cast<int>(i)));
|
|
CHECK_EQ(builder.size(), i + 1);
|
|
}
|
|
for (size_t i = 0; i < reserved; i++) {
|
|
OperandSize operand_size = builder.CreateReservedEntry();
|
|
CHECK_EQ(operand_size, OperandSize::kShort);
|
|
CHECK_EQ(builder.size(), k8BitCapacity);
|
|
}
|
|
for (size_t i = 0; i < reserved; i++) {
|
|
builder.DiscardReservedEntry(OperandSize::kShort);
|
|
CHECK_EQ(builder.size(), k8BitCapacity);
|
|
}
|
|
for (size_t i = 0; i < reserved; i++) {
|
|
OperandSize operand_size = builder.CreateReservedEntry();
|
|
CHECK_EQ(operand_size, OperandSize::kShort);
|
|
builder.CommitReservedEntry(operand_size,
|
|
Smi::FromInt(static_cast<int>(i)));
|
|
CHECK_EQ(builder.size(), k8BitCapacity);
|
|
}
|
|
for (size_t i = k8BitCapacity; i < k8BitCapacity + reserved; i++) {
|
|
OperandSize operand_size = builder.CreateReservedEntry();
|
|
CHECK_EQ(operand_size, OperandSize::kShort);
|
|
builder.CommitReservedEntry(operand_size,
|
|
Smi::FromInt(static_cast<int>(i)));
|
|
CHECK_EQ(builder.size(), i + 1);
|
|
}
|
|
|
|
ast_factory.Internalize(isolate());
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
CHECK_EQ(constant_array->length(),
|
|
static_cast<int>(k8BitCapacity + reserved));
|
|
for (size_t i = 0; i < k8BitCapacity + reserved; i++) {
|
|
Object value = constant_array->get(static_cast<int>(i));
|
|
CHECK(value.SameValue(*isolate()->factory()->NewNumberFromSize(i)));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, GapFilledWhenLowReservationCommitted) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
OperandSize operand_size = builder.CreateReservedEntry();
|
|
CHECK_EQ(OperandSize::kByte, operand_size);
|
|
CHECK_EQ(builder.size(), 0u);
|
|
}
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
builder.CommitReservedEntry(builder.CreateReservedEntry(),
|
|
Smi::FromInt(static_cast<int>(i)));
|
|
CHECK_EQ(builder.size(), i + k8BitCapacity + 1);
|
|
}
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
builder.CommitReservedEntry(OperandSize::kByte,
|
|
Smi::FromInt(static_cast<int>(i)));
|
|
CHECK_EQ(builder.size(), 2 * k8BitCapacity);
|
|
}
|
|
ast_factory.Internalize(isolate());
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
CHECK_EQ(constant_array->length(), static_cast<int>(2 * k8BitCapacity));
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
Object original = constant_array->get(static_cast<int>(k8BitCapacity + i));
|
|
Object duplicate = constant_array->get(static_cast<int>(i));
|
|
CHECK(original.SameValue(duplicate));
|
|
Handle<Object> reference = isolate()->factory()->NewNumberFromSize(i);
|
|
CHECK(original.SameValue(*reference));
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, GapNotFilledWhenLowReservationDiscarded) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
OperandSize operand_size = builder.CreateReservedEntry();
|
|
CHECK_EQ(OperandSize::kByte, operand_size);
|
|
CHECK_EQ(builder.size(), 0u);
|
|
}
|
|
double values[k8BitCapacity];
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
values[i] = i + 0.5;
|
|
}
|
|
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
builder.Insert(values[i]);
|
|
CHECK_EQ(builder.size(), i + k8BitCapacity + 1);
|
|
}
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
builder.DiscardReservedEntry(OperandSize::kByte);
|
|
builder.Insert(values[i]);
|
|
CHECK_EQ(builder.size(), 2 * k8BitCapacity);
|
|
}
|
|
for (size_t i = 0; i < k8BitCapacity; i++) {
|
|
Handle<Object> reference = isolate()->factory()->NewNumber(i + 0.5);
|
|
Handle<Object> original =
|
|
builder.At(k8BitCapacity + i, isolate()).ToHandleChecked();
|
|
CHECK(original->SameValue(*reference));
|
|
MaybeHandle<Object> duplicate = builder.At(i, isolate());
|
|
CHECK(duplicate.is_null());
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, HolesWithUnusedReservations) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
static int kNumberOfHoles = 128;
|
|
static int k8BitCapacity = ConstantArrayBuilder::k8BitCapacity;
|
|
ConstantArrayBuilder builder(zone());
|
|
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
for (int i = 0; i < kNumberOfHoles; ++i) {
|
|
CHECK_EQ(builder.CreateReservedEntry(), OperandSize::kByte);
|
|
}
|
|
// Values are placed before the reserved entries in the same slice.
|
|
for (int i = 0; i < k8BitCapacity - kNumberOfHoles; ++i) {
|
|
CHECK_EQ(builder.Insert(i + 0.5), static_cast<size_t>(i));
|
|
}
|
|
// The next value is pushed into the next slice.
|
|
CHECK_EQ(builder.Insert(k8BitCapacity + 0.5), k8BitCapacity);
|
|
|
|
// Discard the reserved entries.
|
|
for (int i = 0; i < kNumberOfHoles; ++i) {
|
|
builder.DiscardReservedEntry(OperandSize::kByte);
|
|
}
|
|
|
|
ast_factory.Internalize(isolate());
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
CHECK_EQ(constant_array->length(), k8BitCapacity + 1);
|
|
for (int i = kNumberOfHoles; i < k8BitCapacity; i++) {
|
|
CHECK(constant_array->get(i).SameValue(
|
|
*isolate()->factory()->the_hole_value()));
|
|
}
|
|
CHECK(!constant_array->get(kNumberOfHoles - 1)
|
|
.SameValue(*isolate()->factory()->the_hole_value()));
|
|
CHECK(!constant_array->get(k8BitCapacity)
|
|
.SameValue(*isolate()->factory()->the_hole_value()));
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, ReservationsAtAllScales) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
|
|
HashSeed(isolate()));
|
|
for (int i = 0; i < 256; i++) {
|
|
CHECK_EQ(builder.CreateReservedEntry(), OperandSize::kByte);
|
|
}
|
|
for (int i = 256; i < 65536; ++i) {
|
|
CHECK_EQ(builder.CreateReservedEntry(), OperandSize::kShort);
|
|
}
|
|
for (int i = 65536; i < 131072; ++i) {
|
|
CHECK_EQ(builder.CreateReservedEntry(), OperandSize::kQuad);
|
|
}
|
|
CHECK_EQ(builder.CommitReservedEntry(OperandSize::kByte, Smi::FromInt(1)),
|
|
0u);
|
|
CHECK_EQ(builder.CommitReservedEntry(OperandSize::kShort, Smi::FromInt(2)),
|
|
256u);
|
|
CHECK_EQ(builder.CommitReservedEntry(OperandSize::kQuad, Smi::FromInt(3)),
|
|
65536u);
|
|
for (int i = 1; i < 256; i++) {
|
|
builder.DiscardReservedEntry(OperandSize::kByte);
|
|
}
|
|
for (int i = 257; i < 65536; ++i) {
|
|
builder.DiscardReservedEntry(OperandSize::kShort);
|
|
}
|
|
for (int i = 65537; i < 131072; ++i) {
|
|
builder.DiscardReservedEntry(OperandSize::kQuad);
|
|
}
|
|
|
|
ast_factory.Internalize(isolate());
|
|
Handle<FixedArray> constant_array = builder.ToFixedArray(isolate());
|
|
CHECK_EQ(constant_array->length(), 65537);
|
|
int count = 1;
|
|
for (int i = 0; i < constant_array->length(); ++i) {
|
|
Handle<Object> expected;
|
|
if (i == 0 || i == 256 || i == 65536) {
|
|
expected = isolate()->factory()->NewNumber(count++);
|
|
} else {
|
|
expected = isolate()->factory()->the_hole_value();
|
|
}
|
|
CHECK(constant_array->get(i).SameValue(*expected));
|
|
}
|
|
}
|
|
|
|
TEST_F(ConstantArrayBuilderTest, AllocateEntriesWithFixedReservations) {
|
|
CanonicalHandleScope canonical(isolate());
|
|
ConstantArrayBuilder builder(zone());
|
|
for (size_t i = 0; i < k16BitCapacity; i++) {
|
|
if ((i % 2) == 0) {
|
|
CHECK_EQ(i, builder.InsertDeferred());
|
|
} else {
|
|
builder.Insert(Smi::FromInt(static_cast<int>(i)));
|
|
}
|
|
}
|
|
CHECK_EQ(builder.size(), k16BitCapacity);
|
|
|
|
// Check values before reserved entries are inserted.
|
|
for (size_t i = 0; i < k16BitCapacity; i++) {
|
|
if ((i % 2) == 0) {
|
|
// Check reserved values are null.
|
|
MaybeHandle<Object> empty = builder.At(i, isolate());
|
|
CHECK(empty.is_null());
|
|
} else {
|
|
CHECK_EQ(Handle<Smi>::cast(builder.At(i, isolate()).ToHandleChecked())
|
|
->value(),
|
|
static_cast<int>(i));
|
|
}
|
|
}
|
|
|
|
// Insert reserved entries.
|
|
for (size_t i = 0; i < k16BitCapacity; i += 2) {
|
|
builder.SetDeferredAt(i,
|
|
handle(Smi::FromInt(static_cast<int>(i)), isolate()));
|
|
}
|
|
|
|
// Check values after reserved entries are inserted.
|
|
for (size_t i = 0; i < k16BitCapacity; i++) {
|
|
CHECK_EQ(
|
|
Handle<Smi>::cast(builder.At(i, isolate()).ToHandleChecked())->value(),
|
|
static_cast<int>(i));
|
|
}
|
|
}
|
|
|
|
} // namespace interpreter
|
|
} // namespace internal
|
|
} // namespace v8
|