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:
parent
fd1b8bbf9e
commit
e861dbbcf1
1
BUILD.gn
1
BUILD.gn
@ -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",
|
||||
|
@ -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
19
src/detachable-vector.cc
Normal 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
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user