Skip to content

Commit

Permalink
Extend FixedVector to allow initialization from a failable generator
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=274491

Reviewed by Darin Adler.

Adds support to FixedVector (or any TrailingArray subtype) being
constructed using a failable generator. A failable generator is an
invocable type that takes an size_t index and returns a std::optional<T>.
If one of the indices fails, the whole construction fails.

This is going to be used to support variadic JS functions like:

  `undefined append((Node or DOMString or TrustedScript)... nodes);`

from ParentNode.idl, once interfaces in IDL unions use Ref rather than
RefPtr. The implementation function will end up being:

  `ExceptionOr<void> append(FixedVector<Ref<Node>, String, Ref<TrustedScript>>&&)`

To call that, the bindings used to create a FixedVector of the
appropriate size, and then insert the items in as they get
converted. That doesn't work if the items are non-POD types
due to TrailingArray's constructor calling:

  `VectorTypeOperations<T>::initializeIfNonPOD(begin(), end())`

* Source/WTF/wtf/EmbeddedFixedVector.h:
* Source/WTF/wtf/FixedVector.h:
(WTF::FixedVector::createWithSizeFromGenerator):
(WTF::FixedVector::FixedVector):
* Source/WTF/wtf/TrailingArray.h:
(WTF::TrailingArray::TrailingArray):
    - Pipe support to the failable constructor. On failure, the
      size is set to 0 and no storage is allocated.

* Tools/TestWebKitAPI/Tests/WTF/FixedVector.cpp:
(TestWebKitAPI::TEST(WTF_FixedVector, FailableGeneratorConstructor)):
    - Adds tests using the new constructor.

Canonical link: https://commits.webkit.org/279089@main
  • Loading branch information
weinig authored and Sam Weinig committed May 22, 2024
1 parent 52fa87c commit 6cfd378
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 6 deletions.
16 changes: 16 additions & 0 deletions Source/WTF/wtf/EmbeddedFixedVector.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ class EmbeddedFixedVector final : public TrailingArray<EmbeddedFixedVector<T>, T
return UniqueRef { *new (NotNull, EmbeddedFixedVectorMalloc::malloc(Base::allocationSize(size))) EmbeddedFixedVector(size, std::forward<Args>(args)...) };
}

template<std::invocable<size_t> Generator>
static std::unique_ptr<EmbeddedFixedVector> createWithSizeFromGenerator(unsigned size, Generator&& generator)
{

auto result = std::unique_ptr<EmbeddedFixedVector> { new (NotNull, EmbeddedFixedVectorMalloc::malloc(Base::allocationSize(size))) EmbeddedFixedVector(typename Base::Failable { }, size, std::forward<Generator>(generator)) };
if (result->size() != size)
return nullptr;
return result;
}

UniqueRef<EmbeddedFixedVector> clone() const
{
return create(Base::begin(), Base::end());
Expand Down Expand Up @@ -109,6 +119,12 @@ class EmbeddedFixedVector final : public TrailingArray<EmbeddedFixedVector<T>, T
: Base(size, std::forward<Args>(args)...)
{
}

template<std::invocable<size_t> FailableGenerator>
EmbeddedFixedVector(typename Base::Failable failable, unsigned size, FailableGenerator&& generator)
: Base(failable, size, std::forward<FailableGenerator>(generator))
{
}
};

} // namespace WTF
Expand Down
16 changes: 10 additions & 6 deletions Source/WTF/wtf/FixedVector.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,18 @@ class FixedVector {
return *this;
}

private:
FixedVector(std::unique_ptr<Storage>&& storage)
: m_storage { WTFMove(storage) }
{ }

public:
template<typename... Args>
static FixedVector createWithSizeAndConstructorArguments(size_t size, Args&&... args)
{
return FixedVector<T> { size ? Storage::createWithSizeAndConstructorArguments(size, std::forward<Args>(args)...).moveToUniquePtr() : std::unique_ptr<Storage> { nullptr } };
}

template<std::invocable<size_t> Generator>
static FixedVector createWithSizeFromGenerator(size_t size, Generator&& generator)
{
return FixedVector<T> { Storage::createWithSizeFromGenerator(size, std::forward<Generator>(generator)) };
}

size_t size() const { return m_storage ? m_storage->size() : 0; }
bool isEmpty() const { return m_storage ? m_storage->isEmpty() : true; }
size_t byteSize() const { return m_storage ? m_storage->byteSize() : 0; }
Expand Down Expand Up @@ -208,6 +208,10 @@ class FixedVector {
private:
friend class JSC::LLIntOffsetsExtractor;

FixedVector(std::unique_ptr<Storage>&& storage)
: m_storage { WTFMove(storage) }
{ }

std::unique_ptr<Storage> m_storage;
};
static_assert(sizeof(FixedVector<int>) == sizeof(int*));
Expand Down
28 changes: 28 additions & 0 deletions Source/WTF/wtf/TrailingArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#pragma once

#include <concepts>
#include <type_traits>
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
Expand Down Expand Up @@ -79,6 +80,33 @@ class TrailingArray {
VectorTypeOperations<T>::initializeWithArgs(begin(), end(), std::forward<Args>(args)...);
}

// This constructor, which is used via the `Failable` token, will attempt
// to initialize the array from the generator. The generator returns
// `std::optional` values, and if one is `nullopt`, that indicates a failure.
// The constructor sets `m_size` to the index of the most recently successful
// item to be added in order for the destructor to destroy the right number
// of elements.
//
// It is the responsibility of the caller to check that `size()` is equal
// to the `size` the caller passed in. If it is not, that is failure, and
// should be used as appropriate.
struct Failable { };
template<std::invocable<size_t> Generator>
explicit TrailingArray(Failable, unsigned size, Generator&& generator)
: m_size(size)
{
static_assert(std::is_final_v<Derived>);

for (size_t i = 0; i < m_size; ++i) {
if (auto value = generator(i))
begin()[i] = WTFMove(*value);
else {
m_size = i;
return;
}
}
}

~TrailingArray()
{
VectorTypeOperations<T>::destruct(begin(), end());
Expand Down
17 changes: 17 additions & 0 deletions Tools/TestWebKitAPI/Tests/WTF/FixedVector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,23 @@ TEST(WTF_FixedVector, MoveAssignVector)
EXPECT_EQ(index, vec2[index].value());
}

TEST(WTF_FixedVector, FailableGeneratorConstructor)
{
auto vec1 = FixedVector<MoveOnly>::createWithSizeFromGenerator(4, [](auto index) -> std::optional<MoveOnly> {
if (index == 0)
return MoveOnly(0);
return std::nullopt;
});
EXPECT_EQ(0U, vec1.size());

auto vec2 = FixedVector<MoveOnly>::createWithSizeFromGenerator(4, [](auto index) -> std::optional<MoveOnly> {
return MoveOnly(index);
});
EXPECT_EQ(4U, vec2.size());
for (unsigned index = 0; index < vec2.size(); ++index)
EXPECT_EQ(index, vec2[index].value());
}

TEST(WTF_FixedVector, Swap)
{
FixedVector<unsigned> vec1(3);
Expand Down

0 comments on commit 6cfd378

Please sign in to comment.