Commit 1287688c authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Inline Map and Set iterators into optimized code.

This CL inlines the following builtins into TurboFan

  - %MapIteratorPrototype%.next
  - %SetIteratorPrototype%.next

following the design that we are using for Array iteration already
(different instance types for the different kinds of iterators). Details
can be found in the relevant design document at:

  https://docs.google.com/document/d/13z1fvRVpe_oEroplXEEX0a3WK94fhXorHjcOMsDmR-8

The key to great performance here is to ensure that the inlined code
allows escape analysis and scalar replacement of aggregates to remove
the allocations for the iterator itself as well as the iterator results
and potential key/value arrays in the simple case of a for-of loop (and
by extension also in other constructs that reduce to for-of loops
internally), i.e.:

  const s = new Set;
  // ... do something with s
  for (const x of s) {
    // ...
  }

Here the for-of loop shouldn't perform any allocations of helper
objects.

Drive-by-fix: Replace the ExistsJSMapWithness in JSBuiltinReducer with a more
general HasInstanceTypeWitness, similar to what's in JSCallReducer. Also
migrate the {Map,Set}.prototype.size getter inlining to the
JSBuiltinReducer, so that everything is in a single place.

R=jgruber@chromium.org

Bug: v8:6344, v8:6571, chromium:740122
Change-Id: I09cb506fe26ed3e10d7dcb2f95ec4415e639582d
Reviewed-on: https://chromium-review.googlesource.com/570159Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46655}
parent b77f869b
......@@ -3030,7 +3030,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleInstallFunction(prototype, "keys", Builtins::kMapPrototypeKeys, 0,
true);
SimpleInstallGetter(prototype, factory->InternalizeUtf8String("size"),
Builtins::kMapPrototypeGetSize, true);
Builtins::kMapPrototypeGetSize, true,
BuiltinFunctionId::kMapSize);
SimpleInstallFunction(prototype, "values", Builtins::kMapPrototypeValues, 0,
true);
InstallSpeciesGetter(js_map_fun);
......@@ -3067,7 +3068,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleInstallFunction(prototype, "forEach", Builtins::kSetPrototypeForEach,
1, false);
SimpleInstallGetter(prototype, factory->InternalizeUtf8String("size"),
Builtins::kSetPrototypeGetSize, true);
Builtins::kSetPrototypeGetSize, true,
BuiltinFunctionId::kSetSize);
Handle<JSFunction> values = SimpleInstallFunction(
prototype, "values", Builtins::kSetPrototypeValues, 0, true);
JSObject::AddProperty(prototype, factory->keys_string(), values, DONT_ENUM);
......
......@@ -640,6 +640,46 @@ void CollectionsBuiltinsAssembler::FindOrderedHashMapEntry(
}
}
TF_BUILTIN(OrderedHashTableHealIndex, CollectionsBuiltinsAssembler) {
Node* table = Parameter(Descriptor::kTable);
Node* index = Parameter(Descriptor::kIndex);
CSA_ASSERT(this, TaggedIsNotSmi(table));
CSA_ASSERT(this, TaggedIsSmi(index));
Label return_index(this), return_zero(this);
// Check if we need to update the {index}.
GotoIfNot(SmiLessThan(SmiConstant(Smi::kZero), index), &return_zero);
// Check if the {table} was cleared.
Node* number_of_deleted_elements = LoadAndUntagObjectField(
table, OrderedHashTableBase::kNumberOfDeletedElementsOffset);
GotoIf(WordEqual(number_of_deleted_elements,
IntPtrConstant(OrderedHashTableBase::kClearedTableSentinel)),
&return_zero);
VARIABLE(var_i, MachineType::PointerRepresentation(), IntPtrConstant(0));
VARIABLE(var_index, MachineRepresentation::kTagged, index);
Label loop(this, {&var_i, &var_index});
Goto(&loop);
BIND(&loop);
{
Node* i = var_i.value();
GotoIfNot(IntPtrLessThan(i, number_of_deleted_elements), &return_index);
Node* removed_index = LoadFixedArrayElement(
table, i, OrderedHashTableBase::kRemovedHolesIndex * kPointerSize);
GotoIf(SmiGreaterThanOrEqual(removed_index, index), &return_index);
Decrement(var_index, 1, SMI_PARAMETERS);
Increment(var_i);
Goto(&loop);
}
BIND(&return_index);
Return(var_index.value());
BIND(&return_zero);
Return(SmiConstant(Smi::kZero));
}
template <typename TableType>
std::tuple<Node*, Node*> CollectionsBuiltinsAssembler::Transition(
Node* const table, Node* const index,
......@@ -664,41 +704,10 @@ std::tuple<Node*, Node*> CollectionsBuiltinsAssembler::Transition(
GotoIf(TaggedIsSmi(next_table), &done_loop);
var_table.Bind(next_table);
// Check if we need to update the {index}.
GotoIfNot(IntPtrLessThan(IntPtrConstant(0), index), &loop);
// Check if the {table} was cleared.
Node* nod = LoadAndUntagObjectField(
table, TableType::kNumberOfDeletedElementsOffset);
Label if_table_cleared(this), if_table_not_cleared(this);
Branch(WordEqual(nod, IntPtrConstant(TableType::kClearedTableSentinel)),
&if_table_cleared, &if_table_not_cleared);
BIND(&if_table_cleared);
{
var_index.Bind(IntPtrConstant(0));
Goto(&loop);
}
BIND(&if_table_not_cleared);
{
VARIABLE(var_i, MachineType::PointerRepresentation(),
IntPtrConstant(0));
Label iloop(this, {&var_i, &var_index});
Goto(&iloop);
BIND(&iloop);
{
Node* i = var_i.value();
GotoIfNot(IntPtrLessThan(i, nod), &loop);
Node* removed_index = SmiUntag(LoadFixedArrayElement(
table, i, TableType::kRemovedHolesIndex * kPointerSize));
GotoIf(IntPtrGreaterThanOrEqual(removed_index, index), &loop);
Decrement(var_index);
Increment(var_i);
Goto(&iloop);
}
}
var_index.Bind(
SmiUntag(CallBuiltin(Builtins::kOrderedHashTableHealIndex,
NoContextConstant(), table, SmiTag(index))));
Goto(&loop);
}
BIND(&done_loop);
......
......@@ -124,6 +124,9 @@ namespace internal {
TFC(StringLessThanOrEqual, Compare, 1) \
TFC(StringConcat, StringConcat, 1) \
\
/* OrderedHashTable helpers */ \
TFS(OrderedHashTableHealIndex, kTable, kIndex) \
\
/* Interpreter */ \
ASM(InterpreterEntryTrampoline) \
ASM(InterpreterPushArgsThenCall) \
......
......@@ -103,6 +103,28 @@ FieldAccess AccessBuilder::ForJSCollectionTable() {
return access;
}
// static
FieldAccess AccessBuilder::ForJSCollectionIteratorTable() {
FieldAccess access = {
kTaggedBase, JSCollectionIterator::kTableOffset,
MaybeHandle<Name>(), MaybeHandle<Map>(),
Type::OtherInternal(), MachineType::TaggedPointer(),
kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSCollectionIteratorIndex() {
FieldAccess access = {kTaggedBase,
JSCollectionIterator::kIndexOffset,
MaybeHandle<Name>(),
MaybeHandle<Map>(),
TypeCache::Get().kFixedArrayLengthType,
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSFunctionPrototypeOrInitialMap() {
FieldAccess access = {
......@@ -994,29 +1016,60 @@ FieldAccess AccessBuilder::ForHashTableBaseCapacity() {
}
// static
FieldAccess AccessBuilder::ForOrderedHashMapNumberOfElements() {
FieldAccess AccessBuilder::ForOrderedHashTableBaseNextTable() {
// TODO(turbofan): This will be redundant with the HashTableBase
// methods above once the hash table unification is done.
FieldAccess const access = {
kTaggedBase, OrderedHashMap::kNumberOfElementsOffset,
MaybeHandle<Name>(), MaybeHandle<Map>(),
Type::UnsignedSmall(), MachineType::TaggedSigned(),
kNoWriteBarrier};
kTaggedBase, OrderedHashTableBase::kNextTableOffset,
MaybeHandle<Name>(), MaybeHandle<Map>(),
Type::Any(), MachineType::AnyTagged(),
kFullWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets() {
// TODO(turbofan): This will be redundant with the HashTableBase
// methods above once the hash table unification is done.
FieldAccess const access = {kTaggedBase,
OrderedHashTableBase::kNumberOfBucketsOffset,
MaybeHandle<Name>(),
MaybeHandle<Map>(),
TypeCache::Get().kFixedArrayLengthType,
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForOrderedHashSetNumberOfElements() {
FieldAccess AccessBuilder::ForOrderedHashTableBaseNumberOfDeletedElements() {
// TODO(turbofan): This will be redundant with the HashTableBase
// methods above once the hash table unification is done.
FieldAccess const access = {
kTaggedBase, OrderedHashSet::kNumberOfElementsOffset,
MaybeHandle<Name>(), MaybeHandle<Map>(),
Type::UnsignedSmall(), MachineType::TaggedSigned(),
kTaggedBase,
OrderedHashTableBase::kNumberOfDeletedElementsOffset,
MaybeHandle<Name>(),
MaybeHandle<Map>(),
TypeCache::Get().kFixedArrayLengthType,
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForOrderedHashTableBaseNumberOfElements() {
// TODO(turbofan): This will be redundant with the HashTableBase
// methods above once the hash table unification is done.
FieldAccess const access = {kTaggedBase,
OrderedHashTableBase::kNumberOfElementsOffset,
MaybeHandle<Name>(),
MaybeHandle<Map>(),
TypeCache::Get().kFixedArrayLengthType,
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForDictionaryMaxNumberKey() {
FieldAccess access = {
......
......@@ -52,6 +52,12 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to JSCollecton::table() field.
static FieldAccess ForJSCollectionTable();
// Provides access to JSCollectionIterator::table() field.
static FieldAccess ForJSCollectionIteratorTable();
// Provides access to JSCollectionIterator::index() field.
static FieldAccess ForJSCollectionIteratorIndex();
// Provides access to JSFunction::prototype_or_initial_map() field.
static FieldAccess ForJSFunctionPrototypeOrInitialMap();
......@@ -283,11 +289,11 @@ class V8_EXPORT_PRIVATE AccessBuilder final
static FieldAccess ForHashTableBaseNumberOfDeletedElement();
static FieldAccess ForHashTableBaseCapacity();
// Provides access to OrderedHashMap fields.
static FieldAccess ForOrderedHashMapNumberOfElements();
// Provides access to OrderedHashSet fields.
static FieldAccess ForOrderedHashSetNumberOfElements();
// Provides access to OrderedHashTableBase fields.
static FieldAccess ForOrderedHashTableBaseNextTable();
static FieldAccess ForOrderedHashTableBaseNumberOfBuckets();
static FieldAccess ForOrderedHashTableBaseNumberOfElements();
static FieldAccess ForOrderedHashTableBaseNumberOfDeletedElements();
// Provides access to Dictionary fields.
static FieldAccess ForDictionaryMaxNumberKey();
......
This diff is collapsed.
......@@ -61,6 +61,15 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceArrayPop(Node* node);
Reduction ReduceArrayPush(Node* node);
Reduction ReduceArrayShift(Node* node);
Reduction ReduceCollectionIterator(Node* node,
InstanceType collection_instance_type,
int collection_iterator_map_index);
Reduction ReduceCollectionSize(Node* node,
InstanceType collection_instance_type);
Reduction ReduceCollectionIteratorNext(
Node* node, int entry_size,
InstanceType collection_iterator_instance_type_first,
InstanceType collection_iterator_instance_type_last);
Reduction ReduceDateNow(Node* node);
Reduction ReduceDateGetTime(Node* node);
Reduction ReduceFunctionBind(Node* node);
......
......@@ -335,50 +335,6 @@ Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) {
return Changed(node);
}
namespace {
bool HasInstanceTypeWitness(Node* receiver, Node* effect,
InstanceType instance_type) {
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
switch (result) {
case NodeProperties::kNoReceiverMaps:
return false;
case NodeProperties::kReliableReceiverMaps:
case NodeProperties::kUnreliableReceiverMaps:
DCHECK_NE(0, receiver_maps.size());
for (size_t i = 0; i < receiver_maps.size(); ++i) {
if (receiver_maps[i]->instance_type() != instance_type) return false;
}
return true;
}
UNREACHABLE();
return false;
}
} // namespace
// ES #sec-get-map.prototype.size
Reduction JSCallReducer::ReduceMapPrototypeGetSize(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (HasInstanceTypeWitness(receiver, effect, JS_MAP_TYPE)) {
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
receiver, effect, control);
Node* value = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashMapNumberOfElements()),
table, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
}
Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) {
Node* effect = NodeProperties::GetEffectInput(node);
......@@ -524,26 +480,6 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
return ReduceObjectGetPrototype(node, target);
}
// ES #sec-get-set.prototype.size
Reduction JSCallReducer::ReduceSetPrototypeGetSize(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (HasInstanceTypeWitness(receiver, effect, JS_SET_TYPE)) {
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
receiver, effect, control);
Node* value = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashSetNumberOfElements()),
table, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
}
bool CanInlineArrayIteratingBuiltin(Handle<Map> receiver_map) {
Isolate* const isolate = receiver_map->GetIsolate();
if (!receiver_map->prototype()->IsJSArray()) return false;
......@@ -1220,8 +1156,6 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceFunctionPrototypeCall(node);
case Builtins::kFunctionPrototypeHasInstance:
return ReduceFunctionPrototypeHasInstance(node);
case Builtins::kMapPrototypeGetSize:
return ReduceMapPrototypeGetSize(node);
case Builtins::kNumberConstructor:
return ReduceNumberConstructor(node);
case Builtins::kObjectGetPrototypeOf:
......@@ -1236,8 +1170,6 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceReflectConstruct(node);
case Builtins::kReflectGetPrototypeOf:
return ReduceReflectGetPrototypeOf(node);
case Builtins::kSetPrototypeGetSize:
return ReduceSetPrototypeGetSize(node);
case Builtins::kArrayForEach:
return ReduceArrayForEach(function, node);
case Builtins::kArrayMap:
......
......@@ -59,7 +59,6 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceFunctionPrototypeApply(Node* node);
Reduction ReduceFunctionPrototypeCall(Node* node);
Reduction ReduceFunctionPrototypeHasInstance(Node* node);
Reduction ReduceMapPrototypeGetSize(Node* node);
Reduction ReduceObjectGetPrototype(Node* node, Node* object);
Reduction ReduceObjectGetPrototypeOf(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node);
......@@ -67,7 +66,6 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceReflectApply(Node* node);
Reduction ReduceReflectConstruct(Node* node);
Reduction ReduceReflectGetPrototypeOf(Node* node);
Reduction ReduceSetPrototypeGetSize(Node* node);
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayMap(Handle<JSFunction> function, Node* node);
Reduction ReduceCallOrConstructWithArrayLikeOrSpread(
......
......@@ -1577,7 +1577,6 @@ Type* Typer::Visitor::JSCallTyper(Type* fun, Typer* t) {
// Set functions.
case kSetAdd:
case kSetEntries:
case kSetKeys:
case kSetValues:
return Type::OtherObject();
case kSetClear:
......
......@@ -4860,25 +4860,8 @@ ACCESSORS(JSProxy, hash, Object, kHashOffset)
bool JSProxy::IsRevoked() const { return !handler()->IsJSReceiver(); }
ACCESSORS(JSCollection, table, Object, kTableOffset)
#define ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(name, type, offset) \
template<class Derived, class TableType> \
type* OrderedHashTableIterator<Derived, TableType>::name() const { \
return type::cast(READ_FIELD(this, offset)); \
} \
template<class Derived, class TableType> \
void OrderedHashTableIterator<Derived, TableType>::set_##name( \
type* value, WriteBarrierMode mode) { \
WRITE_FIELD(this, offset, value); \
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode); \
}
ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(table, Object, kTableOffset)
ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(index, Object, kIndexOffset)
#undef ORDERED_HASH_TABLE_ITERATOR_ACCESSORS
ACCESSORS(JSCollectionIterator, table, Object, kTableOffset)
ACCESSORS(JSCollectionIterator, index, Object, kIndexOffset)
ACCESSORS(JSWeakCollection, table, Object, kTableOffset)
ACCESSORS(JSWeakCollection, next, Object, kNextOffset)
......
......@@ -946,10 +946,7 @@ void JSMap::JSMapPrint(std::ostream& os) { // NOLINT
JSObjectPrintBody(os, this);
}
template <class Derived, class TableType>
void
OrderedHashTableIterator<Derived, TableType>::OrderedHashTableIteratorPrint(
void JSCollectionIterator::JSCollectionIteratorPrint(
std::ostream& os) { // NOLINT
os << "\n - table = " << Brief(table());
os << "\n - index = " << Brief(index());
......@@ -957,25 +954,15 @@ OrderedHashTableIterator<Derived, TableType>::OrderedHashTableIteratorPrint(
}
template void OrderedHashTableIterator<
JSSetIterator,
OrderedHashSet>::OrderedHashTableIteratorPrint(std::ostream& os); // NOLINT
template void OrderedHashTableIterator<
JSMapIterator,
OrderedHashMap>::OrderedHashTableIteratorPrint(std::ostream& os); // NOLINT
void JSSetIterator::JSSetIteratorPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSSetIterator");
OrderedHashTableIteratorPrint(os);
JSCollectionIteratorPrint(os);
}
void JSMapIterator::JSMapIteratorPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSMapIterator");
OrderedHashTableIteratorPrint(os);
JSCollectionIteratorPrint(os);
}
......
......@@ -859,6 +859,12 @@ enum InstanceType : uint8_t {
FIRST_ARRAY_ITERATOR_TYPE = FIRST_ARRAY_KEY_ITERATOR_TYPE,
LAST_ARRAY_ITERATOR_TYPE = LAST_ARRAY_VALUE_ITERATOR_TYPE,
FIRST_SET_ITERATOR_TYPE = JS_SET_KEY_VALUE_ITERATOR_TYPE,
LAST_SET_ITERATOR_TYPE = JS_SET_VALUE_ITERATOR_TYPE,
FIRST_MAP_ITERATOR_TYPE = JS_MAP_KEY_ITERATOR_TYPE,
LAST_MAP_ITERATOR_TYPE = JS_MAP_VALUE_ITERATOR_TYPE,
};
STATIC_ASSERT(JS_OBJECT_TYPE == Internals::kJSObjectType);
......@@ -4708,7 +4714,6 @@ class ContextExtension : public Struct {
V(Set.prototype, entries, SetEntries) \
V(Set.prototype, forEach, SetForEach) \
V(Set.prototype, has, SetHas) \
V(Set.prototype, keys, SetKeys) \
V(Set.prototype, values, SetValues) \
V(WeakMap.prototype, delete, WeakMapDelete) \
V(WeakMap.prototype, has, WeakMapHas) \
......@@ -4744,6 +4749,8 @@ enum BuiltinFunctionId {
kArrayKeys,
kArrayValues,
kArrayIteratorNext,
kMapSize,
kSetSize,
kMapIteratorNext,
kSetIteratorNext,
kDataViewBuffer,
......
......@@ -340,6 +340,40 @@ class ObjectHashSet : public HashTable<ObjectHashSet, ObjectHashSetShape> {
DECL_CAST(ObjectHashSet)
};
// Non-templatized base class for {OrderedHashTable}s.
// TODO(hash): Unify this with the HashTableBase above.
class OrderedHashTableBase : public FixedArray {
public:
static const int kNotFound = -1;
static const int kMinCapacity = 4;
static const int kNumberOfElementsIndex = 0;
// The next table is stored at the same index as the nof elements.
static const int kNextTableIndex = kNumberOfElementsIndex;
static const int kNumberOfDeletedElementsIndex = kNumberOfElementsIndex + 1;
static const int kNumberOfBucketsIndex = kNumberOfDeletedElementsIndex + 1;
static const int kHashTableStartIndex = kNumberOfBucketsIndex + 1;
static const int kRemovedHolesIndex = kHashTableStartIndex;
static constexpr const int kNumberOfElementsOffset =
FixedArray::OffsetOfElementAt(kNumberOfElementsIndex);
static constexpr const int kNextTableOffset =
FixedArray::OffsetOfElementAt(kNextTableIndex);
static constexpr const int kNumberOfDeletedElementsOffset =
FixedArray::OffsetOfElementAt(kNumberOfDeletedElementsIndex);
static constexpr const int kNumberOfBucketsOffset =
FixedArray::OffsetOfElementAt(kNumberOfBucketsIndex);
static constexpr const int kHashTableStartOffset =
FixedArray::OffsetOfElementAt(kHashTableStartIndex);
static const int kLoadFactor = 2;
// NumberOfDeletedElements is set to kClearedTableSentinel when
// the table is cleared, which allows iterator transitions to
// optimize that case.
static const int kClearedTableSentinel = -1;
};
// OrderedHashTable is a HashTable with Object keys that preserves
// insertion order. There are Map and Set interfaces (OrderedHashMap
// and OrderedHashTable, below). It is meant to be used by JSMap/JSSet.
......@@ -377,7 +411,7 @@ class ObjectHashSet : public HashTable<ObjectHashSet, ObjectHashSetShape> {
// [3 + NumberOfRemovedHoles()..length]: Not used
//
template <class Derived, int entrysize>
class OrderedHashTable : public FixedArray {
class OrderedHashTable : public OrderedHashTableBase {
public:
// Returns an OrderedHashTable with a capacity of at least |capacity|.
static Handle<Derived> Allocate(Isolate* isolate, int capacity,
......@@ -474,38 +508,9 @@ class OrderedHashTable : public FixedArray {
return Smi::ToInt(get(kRemovedHolesIndex + index));
}
static const int kNotFound = -1;
static const int kMinCapacity = 4;
static const int kNumberOfElementsIndex = 0;
// The next table is stored at the same index as the nof elements.
static const int kNextTableIndex = kNumberOfElementsIndex;
static const int kNumberOfDeletedElementsIndex = kNumberOfElementsIndex + 1;
static const int kNumberOfBucketsIndex = kNumberOfDeletedElementsIndex + 1;
static const int kHashTableStartIndex = kNumberOfBucketsIndex + 1;
static const int kRemovedHolesIndex = kHashTableStartIndex;
static constexpr const int kNumberOfElementsOffset =
FixedArray::OffsetOfElementAt(kNumberOfElementsIndex);
static constexpr const int kNextTableOffset =
FixedArray::OffsetOfElementAt(kNextTableIndex);
static constexpr const int kNumberOfDeletedElementsOffset =
FixedArray::OffsetOfElementAt(kNumberOfDeletedElementsIndex);
static constexpr const int kNumberOfBucketsOffset =
FixedArray::OffsetOfElementAt(kNumberOfBucketsIndex);
static constexpr const int kHashTableStartOffset =
FixedArray::OffsetOfElementAt(kHashTableStartIndex);
static const int kEntrySize = entrysize + 1;
static const int kChainOffset = entrysize;
static const int kLoadFactor = 2;
// NumberOfDeletedElements is set to kClearedTableSentinel when
// the table is cleared, which allows iterator transitions to
// optimize that case.
static const int kClearedTableSentinel = -1;
static const int kMaxCapacity =
(FixedArray::kMaxLength - kHashTableStartIndex) /
(1 + (kEntrySize * kLoadFactor));
......@@ -831,6 +836,25 @@ class SmallOrderedHashMap : public SmallOrderedHashTable<SmallOrderedHashMap> {
Handle<Object> value);
};
class JSCollectionIterator : public JSObject {
public:
// [table]: the backing hash table mapping keys to values.
DECL_ACCESSORS(table, Object)
// [index]: The index into the data table.
DECL_ACCESSORS(index, Object)
// Dispatched behavior.
DECL_PRINTER(JSCollectionIterator)
static const int kTableOffset = JSObject::kHeaderSize;
static const int kIndexOffset = kTableOffset + kPointerSize;
static const int kSize = kIndexOffset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSCollectionIterator);
};
// OrderedHashTableIterator is an iterator that iterates over the keys and
// values of an OrderedHashTable.
//
......@@ -844,22 +868,8 @@ class SmallOrderedHashMap : public SmallOrderedHashTable<SmallOrderedHashMap> {
// When the [Next] result from the iterator is requested, the iterator checks if
// there is a newer table that it needs to transition to.
template <class Derived, class TableType>
class OrderedHashTableIterator : public JSObject {
class OrderedHashTableIterator : public JSCollectionIterator {
public:
// [table]: the backing hash table mapping keys to values.
DECL_ACCESSORS(table, Object)
// [index]: The index into the data table.
DECL_ACCESSORS(index, Object)
#ifdef OBJECT_PRINT
void OrderedHashTableIteratorPrint(std::ostream& os); // NOLINT
#endif
static const int kTableOffset = JSObject::kHeaderSize;
static const int kIndexOffset = kTableOffset + kPointerSize;
static const int kSize = kIndexOffset + kPointerSize;
// Whether the iterator has more elements. This needs to be called before
// calling |CurrentKey| and/or |CurrentValue|.
bool HasMore();
......
......@@ -4,8 +4,14 @@
// Flags: --allow-natives-syntax
(function TestSetIterator() {
function test(f) {
f();
f();
%OptimizeFunctionOnNextCall(f);
f();
}
test(function TestSetIterator() {
var s = new Set;
var iter = s.values();
assertEquals('Set Iterator', %_ClassOf(iter));
......@@ -28,10 +34,10 @@
assertTrue(desc.configurable);
assertFalse(desc.writable);
assertEquals("Set Iterator", desc.value);
})();
});
(function TestSetIteratorValues() {
test(function TestSetIteratorValues() {
var s = new Set;
s.add(1);
s.add(2);
......@@ -43,15 +49,15 @@
assertEquals({value: 3, done: false}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
})();
});
(function TestSetIteratorKeys() {
test(function TestSetIteratorKeys() {
assertEquals(Set.prototype.keys, Set.prototype.values);
})();
});
(function TestSetIteratorEntries() {
test(function TestSetIteratorEntries() {
var s = new Set;
s.add(1);
s.add(2);
......@@ -63,10 +69,10 @@
assertEquals({value: [3, 3], done: false}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
})();
});
(function TestSetIteratorMutations() {
test(function TestSetIteratorMutations() {
var s = new Set;
s.add(1);
var iter = s.values();
......@@ -82,10 +88,10 @@
assertEquals({value: undefined, done: true}, iter.next());
s.add(4);
assertEquals({value: undefined, done: true}, iter.next());
})();
});
(function TestSetIteratorMutations2() {
test(function TestSetIteratorMutations2() {
var s = new Set;
s.add(1);
s.add(2);
......@@ -96,10 +102,10 @@
s.add(2);
assertEquals({value: 2, done: false}, i.next());
assertEquals({value: undefined, done: true}, i.next());
})();
});
(function TestSetIteratorMutations3() {
test(function TestSetIteratorMutations3() {
var s = new Set;
s.add(1);
s.add(2);
......@@ -119,28 +125,28 @@
assertEquals({value: undefined, done: true}, i.next());
s.add(4);
assertEquals({value: undefined, done: true}, i.next());
})();
});
(function TestSetInvalidReceiver() {
test(function TestSetInvalidReceiver() {
assertThrows(function() {
Set.prototype.values.call({});
}, TypeError);
assertThrows(function() {
Set.prototype.entries.call({});
}, TypeError);
})();
});
(function TestSetIteratorInvalidReceiver() {
test(function TestSetIteratorInvalidReceiver() {
var iter = new Set().values();
assertThrows(function() {
iter.next.call({});
});
})();
});
(function TestSetIteratorSymbol() {
test(function TestSetIteratorSymbol() {
assertEquals(Set.prototype[Symbol.iterator], Set.prototype.values);
assertTrue(Set.prototype.hasOwnProperty(Symbol.iterator));
assertFalse(Set.prototype.propertyIsEnumerable(Symbol.iterator));
......@@ -148,10 +154,10 @@
var iter = new Set().values();
assertEquals(iter, iter[Symbol.iterator]());
assertEquals(iter[Symbol.iterator].name, '[Symbol.iterator]');
})();
});
(function TestMapIterator() {
test(function TestMapIterator() {
var m = new Map;
var iter = m.values();
assertEquals('Map Iterator', %_ClassOf(iter));
......@@ -175,10 +181,10 @@
assertTrue(desc.configurable);
assertFalse(desc.writable);
assertEquals("Map Iterator", desc.value);
})();
});
(function TestMapIteratorValues() {
test(function TestMapIteratorValues() {
var m = new Map;
m.set(1, 11);
m.set(2, 22);
......@@ -190,10 +196,10 @@
assertEquals({value: 33, done: false}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
})();
});
(function TestMapIteratorKeys() {
test(function TestMapIteratorKeys() {
var m = new Map;
m.set(1, 11);
m.set(2, 22);
......@@ -205,10 +211,10 @@
assertEquals({value: 3, done: false}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
})();
});
(function TestMapIteratorEntries() {
test(function TestMapIteratorEntries() {
var m = new Map;
m.set(1, 11);
m.set(2, 22);
......@@ -220,10 +226,10 @@
assertEquals({value: [3, 33], done: false}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
assertEquals({value: undefined, done: true}, iter.next());
})();
});
(function TestMapInvalidReceiver() {
test(function TestMapInvalidReceiver() {
assertThrows(function() {
Map.prototype.values.call({});
}, TypeError);
......@@ -233,18 +239,18 @@
assertThrows(function() {
Map.prototype.entries.call({});
}, TypeError);
})();
});
(function TestMapIteratorInvalidReceiver() {
test(function TestMapIteratorInvalidReceiver() {
var iter = new Map().values();
assertThrows(function() {
iter.next.call({});
}, TypeError);
})();
});
(function TestMapIteratorSymbol() {
test(function TestMapIteratorSymbol() {
assertEquals(Map.prototype[Symbol.iterator], Map.prototype.entries);
assertTrue(Map.prototype.hasOwnProperty(Symbol.iterator));
assertFalse(Map.prototype.propertyIsEnumerable(Symbol.iterator));
......@@ -252,4 +258,4 @@
var iter = new Map().values();
assertEquals(iter, iter[Symbol.iterator]());
assertEquals(iter[Symbol.iterator].name, '[Symbol.iterator]');
})();
});
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