Commit d9707491 authored by Florian Sattler's avatar Florian Sattler Committed by Commit Bot

Reland "[preparser] Refactor VariableProxies to use ThreadedLists interface"

This is a reland of 78f8ff95

Original change's description:
> [preparser] Refactor VariableProxies to use ThreadedLists interface
>
> Bug: v8:7926
> Change-Id: Idfc520b67696c8a838a0ee297ea392d416dd899e
> Reviewed-on: https://chromium-review.googlesource.com/1206292
> Commit-Queue: Florian Sattler <sattlerf@google.com>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Reviewed-by: Marja Hölttä <marja@chromium.org>
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#55801}

Bug: v8:7926, chromium:883059
Change-Id: Icaa496be1b4df8306fe6d623e5825909d7b0c9c5
Reviewed-on: https://chromium-review.googlesource.com/1221529
Commit-Queue: Florian Sattler <sattlerf@google.com>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55833}
parent 87346deb
...@@ -1584,6 +1584,7 @@ v8_source_set("v8_base") { ...@@ -1584,6 +1584,7 @@ v8_source_set("v8_base") {
"src/ast/modules.h", "src/ast/modules.h",
"src/ast/prettyprinter.cc", "src/ast/prettyprinter.cc",
"src/ast/prettyprinter.h", "src/ast/prettyprinter.h",
"src/ast/scopes-inl.h",
"src/ast/scopes.cc", "src/ast/scopes.cc",
"src/ast/scopes.h", "src/ast/scopes.h",
"src/ast/variables.cc", "src/ast/variables.cc",
......
...@@ -1577,8 +1577,7 @@ class VariableProxy final : public Expression { ...@@ -1577,8 +1577,7 @@ class VariableProxy final : public Expression {
// Bind this proxy to the variable var. // Bind this proxy to the variable var.
void BindTo(Variable* var); void BindTo(Variable* var);
void set_next_unresolved(VariableProxy* next) { next_unresolved_ = next; } V8_INLINE VariableProxy* next_unresolved() { return next_unresolved_; }
VariableProxy* next_unresolved() { return next_unresolved_; }
// Provides an access type for the ThreadedList used by the PreParsers // Provides an access type for the ThreadedList used by the PreParsers
// expressions, lists, and formal parameters. // expressions, lists, and formal parameters.
...@@ -1621,10 +1620,14 @@ class VariableProxy final : public Expression { ...@@ -1621,10 +1620,14 @@ class VariableProxy final : public Expression {
const AstRawString* raw_name_; // if !is_resolved_ const AstRawString* raw_name_; // if !is_resolved_
Variable* var_; // if is_resolved_ Variable* var_; // if is_resolved_
}; };
V8_INLINE VariableProxy** next() { return &next_unresolved_; }
VariableProxy* next_unresolved_; VariableProxy* next_unresolved_;
VariableProxy** pre_parser_expr_next() { return &pre_parser_expr_next_; } VariableProxy** pre_parser_expr_next() { return &pre_parser_expr_next_; }
VariableProxy* pre_parser_expr_next_; VariableProxy* pre_parser_expr_next_;
friend ThreadedListTraits<VariableProxy>;
}; };
// Left-hand side can only be a property, a global or a (parameter or local) // Left-hand side can only be a property, a global or a (parameter or local)
......
// 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.
#ifndef V8_AST_SCOPES_INL_H_
#define V8_AST_SCOPES_INL_H_
#include "src/ast/scopes.h"
namespace v8 {
namespace internal {
template <typename T>
void Scope::ResolveScopesThenForEachVariable(DeclarationScope* max_outer_scope,
T variable_proxy_stackvisitor,
ParseInfo* info) {
// Module variables must be allocated before variable resolution
// to ensure that UpdateNeedsHoleCheck() can detect import variables.
if (info != nullptr && is_module_scope()) {
AsModuleScope()->AllocateModuleVariables();
}
// Lazy parsed declaration scopes are already partially analyzed. If there are
// unresolved references remaining, they just need to be resolved in outer
// scopes.
Scope* lookup =
is_declaration_scope() && AsDeclarationScope()->was_lazily_parsed()
? outer_scope()
: this;
for (VariableProxy *proxy = unresolved_list_.first(), *next = nullptr;
proxy != nullptr; proxy = next) {
next = proxy->next_unresolved();
DCHECK(!proxy->is_resolved());
Variable* var =
lookup->LookupRecursive(info, proxy, max_outer_scope->outer_scope());
if (var == nullptr) {
variable_proxy_stackvisitor(proxy);
} else if (var != Scope::kDummyPreParserVariable &&
var != Scope::kDummyPreParserLexicalVariable) {
if (info != nullptr) {
// In this case we need to leave scopes in a way that they can be
// allocated. If we resolved variables from lazy parsed scopes, we need
// to context allocate the var.
ResolveTo(info, proxy, var);
if (!var->is_dynamic() && lookup != this) var->ForceContextAllocation();
} else {
var->set_is_used();
if (proxy->is_assigned()) var->set_maybe_assigned();
}
}
}
// Clear unresolved_list_ as it's in an inconsistent state.
unresolved_list_.Clear();
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
scope->ResolveScopesThenForEachVariable(max_outer_scope,
variable_proxy_stackvisitor, info);
}
}
} // namespace internal
} // namespace v8
#endif // V8_AST_SCOPES_INL_H_
This diff is collapsed.
...@@ -217,8 +217,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -217,8 +217,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
DCHECK(!already_resolved_); DCHECK(!already_resolved_);
DCHECK_EQ(factory->zone(), zone()); DCHECK_EQ(factory->zone(), zone());
VariableProxy* proxy = factory->NewVariableProxy(name, kind, start_pos); VariableProxy* proxy = factory->NewVariableProxy(name, kind, start_pos);
proxy->set_next_unresolved(unresolved_); AddUnresolved(proxy);
unresolved_ = proxy;
return proxy; return proxy;
} }
...@@ -479,6 +478,9 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -479,6 +478,9 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
return false; return false;
} }
static void* const kDummyPreParserVariable;
static void* const kDummyPreParserLexicalVariable;
protected: protected:
explicit Scope(Zone* zone); explicit Scope(Zone* zone);
...@@ -524,7 +526,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -524,7 +526,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
ThreadedList<Variable> locals_; ThreadedList<Variable> locals_;
// Unresolved variables referred to from this scope. The proxies themselves // Unresolved variables referred to from this scope. The proxies themselves
// form a linked list of all unresolved proxies. // form a linked list of all unresolved proxies.
VariableProxy* unresolved_; ThreadedList<VariableProxy> unresolved_list_;
// Declarations. // Declarations.
ThreadedList<Declaration> decls_; ThreadedList<Declaration> decls_;
...@@ -596,9 +598,10 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -596,9 +598,10 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Finds free variables of this scope. This mutates the unresolved variables // Finds free variables of this scope. This mutates the unresolved variables
// list along the way, so full resolution cannot be done afterwards. // list along the way, so full resolution cannot be done afterwards.
// If a ParseInfo* is passed, non-free variables will be resolved. // If a ParseInfo* is passed, non-free variables will be resolved.
VariableProxy* FetchFreeVariables(DeclarationScope* max_outer_scope, template <typename T>
ParseInfo* info = nullptr, void ResolveScopesThenForEachVariable(DeclarationScope* max_outer_scope,
VariableProxy* stack = nullptr); T variable_proxy_stackvisitor,
ParseInfo* info = nullptr);
// Predicates. // Predicates.
bool MustAllocate(Variable* var); bool MustAllocate(Variable* var);
......
...@@ -126,7 +126,7 @@ class PreParserExpression { ...@@ -126,7 +126,7 @@ class PreParserExpression {
right.variables_); right.variables_);
} }
if (right.variables_ != nullptr) { if (right.variables_ != nullptr) {
left.variables_->Append(right.variables_); left.variables_->Append(std::move(*right.variables_));
} }
return PreParserExpression(TypeField::encode(kExpression), return PreParserExpression(TypeField::encode(kExpression),
left.variables_); left.variables_);
...@@ -457,7 +457,7 @@ inline void PreParserList<PreParserExpression>::Add( ...@@ -457,7 +457,7 @@ inline void PreParserList<PreParserExpression>::Add(
if (variables_ == nullptr) { if (variables_ == nullptr) {
variables_ = new (zone) VariableZoneThreadedListType(); variables_ = new (zone) VariableZoneThreadedListType();
} }
variables_->Append(expression.variables_); variables_->Append(std::move(*expression.variables_));
} }
++length_; ++length_;
} }
......
...@@ -1646,9 +1646,65 @@ class ThreadedListBase final : public BaseClass { ...@@ -1646,9 +1646,65 @@ class ThreadedListBase final : public BaseClass {
tail_ = TLTraits::next(v); tail_ = TLTraits::next(v);
} }
void Append(ThreadedListBase* list) { void AddFront(T* v) {
*tail_ = list->head_; DCHECK_NULL(*TLTraits::next(v));
tail_ = list->tail_; DCHECK_NOT_NULL(v);
T** const next = TLTraits::next(v);
*next = head_;
if (head_ == nullptr) tail_ = next;
head_ = v;
}
// Reinitializing the head to a new node, this costs O(n).
void ReinitializeHead(T* v) {
head_ = v;
T* current = v;
if (current != nullptr) { // Find tail
T* tmp;
while ((tmp = *TLTraits::next(current))) {
current = tmp;
}
tail_ = TLTraits::next(current);
} else {
tail_ = &head_;
}
SLOW_DCHECK(Verify());
}
void DropHead() {
DCHECK_NOT_NULL(head_);
SLOW_DCHECK(Verify());
T* old_head = head_;
head_ = *TLTraits::next(head_);
if (head_ == nullptr) tail_ = &head_;
*TLTraits::next(old_head) = nullptr;
}
void Append(ThreadedListBase&& list) {
SLOW_DCHECK(Verify());
SLOW_DCHECK(list.Verify());
*tail_ = list.head_;
tail_ = list.tail_;
list.Clear();
}
void Prepend(ThreadedListBase&& list) {
SLOW_DCHECK(Verify());
SLOW_DCHECK(list.Verify());
if (list.head_ == nullptr) return;
T* new_head = list.head_;
*list.tail_ = head_;
if (head_ == nullptr) {
tail_ = list.tail_;
}
head_ = new_head;
list.Clear();
} }
void Clear() { void Clear() {
...@@ -1656,13 +1712,67 @@ class ThreadedListBase final : public BaseClass { ...@@ -1656,13 +1712,67 @@ class ThreadedListBase final : public BaseClass {
tail_ = &head_; tail_ = &head_;
} }
ThreadedListBase& operator=(ThreadedListBase&& other) V8_NOEXCEPT {
head_ = other.head_;
tail_ = other.head_ ? other.tail_ : &head_;
#ifdef DEBUG
other.Clear();
#endif
return *this;
}
ThreadedListBase(ThreadedListBase&& other) V8_NOEXCEPT
: head_(other.head_),
tail_(other.head_ ? other.tail_ : &head_) {
#ifdef DEBUG
other.Clear();
#endif
}
bool Remove(T* v) {
SLOW_DCHECK(Verify());
T* current = first();
if (current == v) {
DropHead();
return true;
}
while (current != nullptr) {
T* next = *TLTraits::next(current);
if (next == v) {
*TLTraits::next(current) = *TLTraits::next(next);
*TLTraits::next(next) = nullptr;
if (TLTraits::next(next) == tail_) {
tail_ = TLTraits::next(current);
}
return true;
}
current = next;
}
return false;
}
class Iterator final { class Iterator final {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T*;
using reference = value_type;
using pointer = value_type*;
public: public:
Iterator& operator++() { Iterator& operator++() {
entry_ = TLTraits::next(*entry_); entry_ = TLTraits::next(*entry_);
return *this; return *this;
} }
bool operator!=(const Iterator& other) { return entry_ != other.entry_; } bool operator==(const Iterator& other) const {
return entry_ == other.entry_;
}
bool operator!=(const Iterator& other) const {
return entry_ != other.entry_;
}
T* operator*() { return *entry_; } T* operator*() { return *entry_; }
T* operator->() { return *entry_; } T* operator->() { return *entry_; }
Iterator& operator=(T* entry) { Iterator& operator=(T* entry) {
...@@ -1681,12 +1791,22 @@ class ThreadedListBase final : public BaseClass { ...@@ -1681,12 +1791,22 @@ class ThreadedListBase final : public BaseClass {
}; };
class ConstIterator final { class ConstIterator final {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T*;
using reference = const value_type;
using pointer = const value_type*;
public: public:
ConstIterator& operator++() { ConstIterator& operator++() {
entry_ = TLTraits::next(*entry_); entry_ = TLTraits::next(*entry_);
return *this; return *this;
} }
bool operator!=(const ConstIterator& other) { bool operator==(const ConstIterator& other) const {
return entry_ == other.entry_;
}
bool operator!=(const ConstIterator& other) const {
return entry_ != other.entry_; return entry_ != other.entry_;
} }
const T* operator*() const { return *entry_; } const T* operator*() const { return *entry_; }
...@@ -1705,23 +1825,34 @@ class ThreadedListBase final : public BaseClass { ...@@ -1705,23 +1825,34 @@ class ThreadedListBase final : public BaseClass {
ConstIterator begin() const { return ConstIterator(&head_); } ConstIterator begin() const { return ConstIterator(&head_); }
ConstIterator end() const { return ConstIterator(tail_); } ConstIterator end() const { return ConstIterator(tail_); }
// Rewinds the list's tail to the reset point, i.e., cutting of the rest of
// the list, including the reset_point.
void Rewind(Iterator reset_point) { void Rewind(Iterator reset_point) {
SLOW_DCHECK(Verify());
tail_ = reset_point.entry_; tail_ = reset_point.entry_;
*tail_ = nullptr; *tail_ = nullptr;
} }
void MoveTail(ThreadedListBase<T, BaseClass>* parent, Iterator location) { // Moves the tail of the from_list, starting at the from_location, to the end
if (parent->end() != location) { // of this list.
void MoveTail(ThreadedListBase* from_list, Iterator from_location) {
SLOW_DCHECK(Verify());
if (from_list->end() != from_location) {
DCHECK_NULL(*tail_); DCHECK_NULL(*tail_);
*tail_ = *location; *tail_ = *from_location;
tail_ = parent->tail_; tail_ = from_list->tail_;
parent->Rewind(location); from_list->Rewind(from_location);
SLOW_DCHECK(Verify());
SLOW_DCHECK(from_list->Verify());
} }
} }
bool is_empty() const { return head_ == nullptr; } bool is_empty() const { return head_ == nullptr; }
T* first() { return head_; } T* first() const { return head_; }
// Slow. For testing purposes. // Slow. For testing purposes.
int LengthForTest() { int LengthForTest() {
...@@ -1729,12 +1860,26 @@ class ThreadedListBase final : public BaseClass { ...@@ -1729,12 +1860,26 @@ class ThreadedListBase final : public BaseClass {
for (Iterator t = begin(); t != end(); ++t) ++result; for (Iterator t = begin(); t != end(); ++t) ++result;
return result; return result;
} }
T* AtForTest(int i) { T* AtForTest(int i) {
Iterator t = begin(); Iterator t = begin();
while (i-- > 0) ++t; while (i-- > 0) ++t;
return *t; return *t;
} }
bool Verify() {
T* last = this->first();
if (last == nullptr) {
CHECK_EQ(&head_, tail_);
} else {
while (*TLTraits::next(last) != nullptr) {
last = *TLTraits::next(last);
}
CHECK_EQ(TLTraits::next(last), tail_);
}
return true;
}
private: private:
T* head_; T* head_;
T** tail_; T** tail_;
......
// 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.
// Flags: --random-seed=-1595876594 --disable-in-process-stack-traces --no-lazy
var __v_47 = ({[__v_46]: __f_52}) => { var __v_46 = 'b'; return __f_52; };
...@@ -196,6 +196,7 @@ v8_source_set("unittests_sources") { ...@@ -196,6 +196,7 @@ v8_source_set("unittests_sources") {
"torque/earley-parser-unittest.cc", "torque/earley-parser-unittest.cc",
"unicode-unittest.cc", "unicode-unittest.cc",
"utils-unittest.cc", "utils-unittest.cc",
"utils/threaded-list.cc",
"value-serializer-unittest.cc", "value-serializer-unittest.cc",
"wasm/control-transfer-unittest.cc", "wasm/control-transfer-unittest.cc",
"wasm/decoder-unittest.cc", "wasm/decoder-unittest.cc",
......
// 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 <iterator>
#include "src/v8.h"
#include "src/utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
struct ThreadedListTestNode {
ThreadedListTestNode() : next_(nullptr), other_next_(nullptr) {}
ThreadedListTestNode** next() { return &next_; }
ThreadedListTestNode* next_;
struct OtherTraits {
static ThreadedListTestNode** next(ThreadedListTestNode* t) {
return t->other_next();
}
};
ThreadedListTestNode** other_next() { return &other_next_; }
ThreadedListTestNode* other_next_;
};
struct ThreadedListTest : public ::testing::Test {
static const size_t INIT_NODES = 5;
ThreadedListTest() {}
void SetUp() override {
for (size_t i = 0; i < INIT_NODES; i++) {
nodes[i] = ThreadedListTestNode();
}
for (size_t i = 0; i < INIT_NODES; i++) {
list.Add(&nodes[i]);
normal_next_list.Add(&nodes[i]);
}
// Verify if setup worked
CHECK(list.Verify());
CHECK_EQ(list.LengthForTest(), INIT_NODES);
CHECK(normal_next_list.Verify());
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
extra_test_node_0 = ThreadedListTestNode();
extra_test_node_1 = ThreadedListTestNode();
extra_test_node_2 = ThreadedListTestNode();
extra_test_list.Add(&extra_test_node_0);
extra_test_list.Add(&extra_test_node_1);
extra_test_list.Add(&extra_test_node_2);
CHECK_EQ(extra_test_list.LengthForTest(), 3);
CHECK(extra_test_list.Verify());
normal_extra_test_list.Add(&extra_test_node_0);
normal_extra_test_list.Add(&extra_test_node_1);
normal_extra_test_list.Add(&extra_test_node_2);
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
CHECK(normal_extra_test_list.Verify());
}
void TearDown() override {
// Check if the normal list threaded through next is still untouched.
CHECK(normal_next_list.Verify());
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
CHECK_EQ(normal_next_list.AtForTest(0), &nodes[0]);
CHECK_EQ(normal_next_list.AtForTest(4), &nodes[4]);
CHECK(normal_extra_test_list.Verify());
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
CHECK_EQ(normal_extra_test_list.AtForTest(0), &extra_test_node_0);
CHECK_EQ(normal_extra_test_list.AtForTest(2), &extra_test_node_2);
list.Clear();
extra_test_list.Clear();
}
ThreadedListTestNode nodes[INIT_NODES];
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> list;
ThreadedList<ThreadedListTestNode> normal_next_list;
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
extra_test_list;
ThreadedList<ThreadedListTestNode> normal_extra_test_list;
ThreadedListTestNode extra_test_node_0;
ThreadedListTestNode extra_test_node_1;
ThreadedListTestNode extra_test_node_2;
};
TEST_F(ThreadedListTest, Add) {
CHECK_EQ(list.LengthForTest(), 5);
ThreadedListTestNode new_node;
// Add to existing list
list.Add(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.AtForTest(5), &new_node);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
new_node = ThreadedListTestNode();
// Add to empty list
list.Add(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 1);
CHECK_EQ(list.AtForTest(0), &new_node);
}
TEST_F(ThreadedListTest, AddFront) {
CHECK_EQ(list.LengthForTest(), 5);
ThreadedListTestNode new_node;
// AddFront to existing list
list.AddFront(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.first(), &new_node);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
new_node = ThreadedListTestNode();
// AddFront to empty list
list.AddFront(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 1);
CHECK_EQ(list.first(), &new_node);
}
TEST_F(ThreadedListTest, ReinitializeHead) {
CHECK_EQ(list.LengthForTest(), 5);
CHECK_NE(extra_test_list.first(), list.first());
list.ReinitializeHead(&extra_test_node_0);
list.Verify();
CHECK_EQ(extra_test_list.first(), list.first());
CHECK_EQ(extra_test_list.end(), list.end());
CHECK_EQ(extra_test_list.LengthForTest(), 3);
}
TEST_F(ThreadedListTest, DropHead) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
CHECK_EQ(extra_test_list.first(), &extra_test_node_0);
extra_test_list.DropHead();
extra_test_list.Verify();
CHECK_EQ(extra_test_list.first(), &extra_test_node_1);
CHECK_EQ(extra_test_list.LengthForTest(), 2);
}
TEST_F(ThreadedListTest, Append) {
auto initial_extra_list_end = extra_test_list.end();
CHECK_EQ(list.LengthForTest(), 5);
list.Append(std::move(extra_test_list));
list.Verify();
extra_test_list.Verify();
CHECK(extra_test_list.is_empty());
CHECK_EQ(list.LengthForTest(), 8);
CHECK_EQ(list.AtForTest(4), &nodes[4]);
CHECK_EQ(list.AtForTest(5), &extra_test_node_0);
CHECK_EQ(list.end(), initial_extra_list_end);
}
TEST_F(ThreadedListTest, Prepend) {
CHECK_EQ(list.LengthForTest(), 5);
list.Prepend(std::move(extra_test_list));
list.Verify();
extra_test_list.Verify();
CHECK(extra_test_list.is_empty());
CHECK_EQ(list.LengthForTest(), 8);
CHECK_EQ(list.first(), &extra_test_node_0);
CHECK_EQ(list.AtForTest(2), &extra_test_node_2);
CHECK_EQ(list.AtForTest(3), &nodes[0]);
}
TEST_F(ThreadedListTest, Clear) {
CHECK_NE(list.LengthForTest(), 0);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
CHECK_NULL(list.first());
}
TEST_F(ThreadedListTest, MoveAssign) {
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list;
CHECK_EQ(extra_test_list.LengthForTest(), 3);
m_list = std::move(extra_test_list);
m_list.Verify();
CHECK_EQ(m_list.first(), &extra_test_node_0);
CHECK_EQ(m_list.LengthForTest(), 3);
// move assign from empty list
extra_test_list.Clear();
CHECK_EQ(extra_test_list.LengthForTest(), 0);
m_list = std::move(extra_test_list);
CHECK_EQ(m_list.LengthForTest(), 0);
m_list.Verify();
CHECK_NULL(m_list.first());
}
TEST_F(ThreadedListTest, MoveCtor) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list(
std::move(extra_test_list));
m_list.Verify();
CHECK_EQ(m_list.LengthForTest(), 3);
CHECK_EQ(m_list.first(), &extra_test_node_0);
// move construct from empty list
extra_test_list.Clear();
CHECK_EQ(extra_test_list.LengthForTest(), 0);
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list2(
std::move(extra_test_list));
CHECK_EQ(m_list2.LengthForTest(), 0);
m_list2.Verify();
CHECK_NULL(m_list2.first());
}
TEST_F(ThreadedListTest, Remove) {
CHECK_EQ(list.LengthForTest(), 5);
// Remove first
CHECK_EQ(list.first(), &nodes[0]);
list.Remove(&nodes[0]);
list.Verify();
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.LengthForTest(), 4);
// Remove middle
list.Remove(&nodes[2]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 3);
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.AtForTest(1), &nodes[3]);
// Remove last
list.Remove(&nodes[4]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 2);
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.AtForTest(1), &nodes[3]);
// Remove rest
list.Remove(&nodes[1]);
list.Remove(&nodes[3]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 0);
// Remove not found
list.Remove(&nodes[4]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 0);
}
TEST_F(ThreadedListTest, Rewind) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
for (auto iter = extra_test_list.begin(); iter != extra_test_list.end();
++iter) {
if (*iter == &extra_test_node_2) {
extra_test_list.Rewind(iter);
break;
}
}
CHECK_EQ(extra_test_list.LengthForTest(), 2);
auto iter = extra_test_list.begin();
CHECK_EQ(*iter, &extra_test_node_0);
std::advance(iter, 1);
CHECK_EQ(*iter, &extra_test_node_1);
extra_test_list.Rewind(extra_test_list.begin());
CHECK_EQ(extra_test_list.LengthForTest(), 0);
}
TEST_F(ThreadedListTest, IterComp) {
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> c_list =
std::move(extra_test_list);
bool found_first;
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
// This triggers the operator== on the iterator
if (iter == c_list.begin()) {
found_first = true;
}
}
CHECK(found_first);
}
TEST_F(ThreadedListTest, ConstIterComp) {
const ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
c_list = std::move(extra_test_list);
bool found_first;
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
// This triggers the operator== on the iterator
if (iter == c_list.begin()) {
found_first = true;
}
}
CHECK(found_first);
}
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment