Make DetachableVector accessible from builtins

This CL updates DetachableVector to store the data at a known place
instead of in an std::vector<>, so that builtins can update it directly.

Bug: v8:8124
Change-Id: Iba5fb2e9d4e0ddc689d0f7eeaea40bc3218edf3a
Reviewed-on: https://chromium-review.googlesource.com/c/1297783
Commit-Queue: Taiju Tsuiki <tzik@chromium.org>
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57452}
This commit is contained in:
tzik 2018-11-12 14:03:41 +09:00 committed by Commit Bot
parent fd1b8bbf9e
commit e861dbbcf1
5 changed files with 151 additions and 39 deletions

View File

@ -1955,6 +1955,7 @@ v8_source_set("v8_base") {
"src/deoptimize-reason.h",
"src/deoptimizer.cc",
"src/deoptimizer.h",
"src/detachable-vector.cc",
"src/detachable-vector.h",
"src/disasm.h",
"src/disassembler.cc",

View File

@ -10556,6 +10556,7 @@ void HandleScopeImplementer::IterateThis(RootVisitor* v) {
DetachableVector<Context*>* context_lists[2] = {&saved_contexts_,
&entered_contexts_};
for (unsigned i = 0; i < arraysize(context_lists); i++) {
context_lists[i]->shrink_to_fit();
if (context_lists[i]->empty()) continue;
ObjectSlot start(reinterpret_cast<Address>(&context_lists[i]->front()));
v->VisitRootPointers(Root::kHandleScope, nullptr, start,

19
src/detachable-vector.cc Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2018 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/detachable-vector.h"
namespace v8 {
namespace internal {
const size_t DetachableVectorBase::kMinimumCapacity = 8;
const size_t DetachableVectorBase::kDataOffset =
offsetof(DetachableVectorBase, data_);
const size_t DetachableVectorBase::kCapacityOffset =
offsetof(DetachableVectorBase, capacity_);
const size_t DetachableVectorBase::kSizeOffset =
offsetof(DetachableVectorBase, size_);
} // namespace internal
} // namespace v8

View File

@ -5,65 +5,96 @@
#ifndef V8_DETACHABLE_VECTOR_H_
#define V8_DETACHABLE_VECTOR_H_
#include <vector>
#include <stddef.h>
#include <algorithm>
#include "src/base/logging.h"
#include "src/base/macros.h"
namespace v8 {
namespace internal {
// This class wraps a std::vector and provides a few of the common member
// functions for accessing the data. It acts as a lazy wrapper of the vector,
// not initiliazing the backing store until push_back() is first called. Two
// extra methods are also provided: free() and detach(), which allow for manual
// control of the backing store. This is currently required for use in the
// HandleScopeImplementer. Any other class should just use a std::vector
// directly.
template <typename T>
class DetachableVector {
class V8_EXPORT_PRIVATE DetachableVectorBase {
public:
DetachableVector() : vector_(nullptr) {}
// Clear our reference to the backing store. Does not delete it!
void detach() {
data_ = nullptr;
capacity_ = 0;
size_ = 0;
}
~DetachableVector() { delete vector_; }
void pop_back() { --size_; }
size_t capacity() const { return capacity_; }
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
static const size_t kMinimumCapacity;
static const size_t kDataOffset;
static const size_t kCapacityOffset;
static const size_t kSizeOffset;
protected:
void* data_ = nullptr;
size_t capacity_ = 0;
size_t size_ = 0;
};
// This class wraps an array and provides a few of the common member
// functions for accessing the data. Two extra methods are also provided: free()
// and detach(), which allow for manual control of the backing store. This is
// currently required for use in the HandleScopeImplementer. Any other class
// should just use a std::vector.
template <typename T>
class DetachableVector : public DetachableVectorBase {
public:
DetachableVector() = default;
~DetachableVector() { delete[] data(); }
void push_back(const T& value) {
ensureAttached();
vector_->push_back(value);
if (size_ == capacity_) {
size_t new_capacity = std::max(kMinimumCapacity, 2 * capacity_);
Resize(new_capacity);
}
data()[size_] = value;
++size_;
}
// Free the backing store and clear our reference to it.
void free() {
delete vector_;
vector_ = nullptr;
delete[] data();
data_ = nullptr;
capacity_ = 0;
size_ = 0;
}
// Clear our reference to the backing store. Does not delete it!
void detach() { vector_ = nullptr; }
T& at(typename std::vector<T>::size_type i) const { return vector_->at(i); }
T& back() const { return vector_->back(); }
T& front() const { return vector_->front(); }
void pop_back() { vector_->pop_back(); }
typename std::vector<T>::size_type size() const {
if (vector_) return vector_->size();
return 0;
T& at(size_t i) const {
DCHECK_LT(i, size_);
return data()[i];
}
T& back() const { return at(size_ - 1); }
T& front() const { return at(0); }
bool empty() const {
if (vector_) return vector_->empty();
return true;
void shrink_to_fit() {
size_t new_capacity = std::max(size_, kMinimumCapacity);
if (new_capacity < capacity_ / 2) {
Resize(new_capacity);
}
}
private:
std::vector<T>* vector_;
T* data() const { return static_cast<T*>(data_); }
// Attach a vector backing store if not present.
void ensureAttached() {
if (vector_ == nullptr) {
vector_ = new std::vector<T>();
}
void Resize(size_t new_capacity) {
DCHECK_LE(size_, new_capacity);
T* new_data_ = new T[new_capacity];
std::copy(data(), data() + size_, new_data_);
delete[] data();
data_ = new_data_;
capacity_ = new_capacity;
}
};

View File

@ -63,5 +63,65 @@ TEST(DetachableVector, DetachLeaksBackingStore) {
// The destructor of v2 will release the backing store.
}
TEST(DetachableVector, PushAndPopWithReallocation) {
DetachableVector<size_t> v;
const size_t kMinimumCapacity = DetachableVector<size_t>::kMinimumCapacity;
EXPECT_EQ(0u, v.capacity());
EXPECT_EQ(0u, v.size());
v.push_back(0);
EXPECT_EQ(kMinimumCapacity, v.capacity());
EXPECT_EQ(1u, v.size());
// Push values until the reallocation happens.
for (size_t i = 1; i <= kMinimumCapacity; ++i) {
v.push_back(i);
}
EXPECT_EQ(2 * kMinimumCapacity, v.capacity());
EXPECT_EQ(kMinimumCapacity + 1, v.size());
EXPECT_EQ(kMinimumCapacity, v.back());
v.pop_back();
v.push_back(100);
EXPECT_EQ(100u, v.back());
v.pop_back();
EXPECT_EQ(kMinimumCapacity - 1, v.back());
}
TEST(DetachableVector, ShrinkToFit) {
DetachableVector<size_t> v;
const size_t kMinimumCapacity = DetachableVector<size_t>::kMinimumCapacity;
// shrink_to_fit doesn't affect the empty capacity DetachableVector.
EXPECT_EQ(0u, v.capacity());
v.shrink_to_fit();
EXPECT_EQ(0u, v.capacity());
// Do not shrink the buffer if it's smaller than kMinimumCapacity.
v.push_back(0);
EXPECT_EQ(kMinimumCapacity, v.capacity());
v.shrink_to_fit();
EXPECT_EQ(kMinimumCapacity, v.capacity());
// Fill items to |v| until the buffer grows twice.
for (size_t i = 0; i < 2 * kMinimumCapacity; ++i) {
v.push_back(i);
}
EXPECT_EQ(2 * kMinimumCapacity + 1, v.size());
EXPECT_EQ(4 * kMinimumCapacity, v.capacity());
// Do not shrink the buffer if the number of unused slots is not large enough.
v.shrink_to_fit();
EXPECT_EQ(2 * kMinimumCapacity + 1, v.size());
EXPECT_EQ(4 * kMinimumCapacity, v.capacity());
v.pop_back();
v.pop_back();
v.shrink_to_fit();
EXPECT_EQ(2 * kMinimumCapacity - 1, v.size());
EXPECT_EQ(2 * kMinimumCapacity - 1, v.capacity());
}
} // namespace internal
} // namespace v8