Commit 3308cb58 authored by adamk@chromium.org's avatar adamk@chromium.org

ES6: Add support for Map/Set forEach

This implements MapIterator and SetIterator which matches
the same constructs in the ES6 spec. However, these 2
iterators are not exposed to user code yet. They are only
used internally to implement Map.prototype.forEach and
Set.prototype.forEach.

Each iterator has a reference to the OrderedHashTable where
it directly accesses the hash table's entries.

The OrderedHashTable has a reference to the newest iterator
and each iterator has a reference to the next and previous
iterator, effectively creating a double linked list.

When the OrderedHashTable is mutated (or replaced) all the
iterators are updated.

When the iterator iterates passed the end of the data table
it closes itself. Closed iterators no longer have a
reference to the OrderedHashTable and they are removed from
the double linked list. In the case of Map/Set forEach, we
manually call Close on the iterator in case an exception was
thrown so that the iterator never reached the end.

At this point the OrderedHashTable keeps all the non finished
iterators alive but since the only thing we currently expose
is forEach there are no unfinished iterators outside a forEach
call. Once we expose the iterators to user code we will need
to make the references from the OrderedHashTable to the
iterators weak and have some mechanism to close an iterator
when it is garbage collected.

BUG=1793, 2323
LOG=Y
R=adamk@chromium.org
TBR=mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/238063009

Patch from Erik Arvidsson <arv@chromium.org>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20857 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1213ecb5
...@@ -2278,7 +2278,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { ...@@ -2278,7 +2278,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required; Label gc_required;
Label allocated; Label allocated;
Handle<Map> map(isolate()->native_context()->generator_result_map()); Handle<Map> map(isolate()->native_context()->iterator_result_map());
__ Allocate(map->instance_size(), r0, r2, r3, &gc_required, TAG_OBJECT); __ Allocate(map->instance_size(), r0, r2, r3, &gc_required, TAG_OBJECT);
__ jmp(&allocated); __ jmp(&allocated);
......
...@@ -4706,7 +4706,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { ...@@ -4706,7 +4706,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required; Label gc_required;
Label allocated; Label allocated;
Handle<Map> map(isolate()->native_context()->generator_result_map()); Handle<Map> map(isolate()->native_context()->iterator_result_map());
// Allocate and populate an object with this form: { value: VAL, done: DONE } // Allocate and populate an object with this form: { value: VAL, done: DONE }
......
...@@ -31,11 +31,6 @@ ...@@ -31,11 +31,6 @@
// in runtime.js: // in runtime.js:
// var $Array = global.Array; // var $Array = global.Array;
var ARRAY_ITERATOR_KIND_KEYS = 1;
var ARRAY_ITERATOR_KIND_VALUES = 2;
var ARRAY_ITERATOR_KIND_ENTRIES = 3;
// The spec draft also has "sparse" but it is never used.
var arrayIteratorObjectSymbol = GLOBAL_PRIVATE("ArrayIterator#object"); var arrayIteratorObjectSymbol = GLOBAL_PRIVATE("ArrayIterator#object");
var arrayIteratorNextIndexSymbol = GLOBAL_PRIVATE("ArrayIterator#next"); var arrayIteratorNextIndexSymbol = GLOBAL_PRIVATE("ArrayIterator#next");
var arrayIterationKindSymbol = GLOBAL_PRIVATE("ArrayIterator#kind"); var arrayIterationKindSymbol = GLOBAL_PRIVATE("ArrayIterator#kind");
...@@ -79,25 +74,25 @@ function ArrayIteratorNext() { ...@@ -79,25 +74,25 @@ function ArrayIteratorNext() {
SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1); SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1);
if (itemKind == ARRAY_ITERATOR_KIND_VALUES) if (itemKind == ITERATOR_KIND_VALUES)
return CreateIteratorResultObject(array[index], false); return CreateIteratorResultObject(array[index], false);
if (itemKind == ARRAY_ITERATOR_KIND_ENTRIES) if (itemKind == ITERATOR_KIND_ENTRIES)
return CreateIteratorResultObject([index, array[index]], false); return CreateIteratorResultObject([index, array[index]], false);
return CreateIteratorResultObject(index, false); return CreateIteratorResultObject(index, false);
} }
function ArrayEntries() { function ArrayEntries() {
return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_ENTRIES); return CreateArrayIterator(this, ITERATOR_KIND_ENTRIES);
} }
function ArrayValues() { function ArrayValues() {
return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_VALUES); return CreateArrayIterator(this, ITERATOR_KIND_VALUES);
} }
function ArrayKeys() { function ArrayKeys() {
return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_KEYS); return CreateArrayIterator(this, ITERATOR_KIND_KEYS);
} }
function SetUpArrayIterator() { function SetUpArrayIterator() {
......
...@@ -1297,6 +1297,16 @@ void Genesis::InitializeExperimentalGlobal() { ...@@ -1297,6 +1297,16 @@ void Genesis::InitializeExperimentalGlobal() {
isolate()->initial_object_prototype(), isolate()->initial_object_prototype(),
Builtins::kIllegal, true, true); Builtins::kIllegal, true, true);
} }
{ // -- S e t I t e r a t o r
Handle<Map> map = isolate()->factory()->NewMap(
JS_SET_ITERATOR_TYPE, JSSetIterator::kSize);
native_context()->set_set_iterator_map(*map);
}
{ // -- M a p I t e r a t o r
Handle<Map> map = isolate()->factory()->NewMap(
JS_MAP_ITERATOR_TYPE, JSMapIterator::kSize);
native_context()->set_map_iterator_map(*map);
}
} }
if (FLAG_harmony_weak_collections) { if (FLAG_harmony_weak_collections) {
...@@ -1351,37 +1361,38 @@ void Genesis::InitializeExperimentalGlobal() { ...@@ -1351,37 +1361,38 @@ void Genesis::InitializeExperimentalGlobal() {
*generator_object_prototype); *generator_object_prototype);
native_context()->set_generator_object_prototype_map( native_context()->set_generator_object_prototype_map(
*generator_object_prototype_map); *generator_object_prototype_map);
}
if (FLAG_harmony_collections || FLAG_harmony_generators) {
// Collection forEach uses an iterator result object.
// Generators return iteraror result objects.
// Create a map for generator result objects.
ASSERT(object_function->initial_map()->inobject_properties() == 0);
STATIC_ASSERT(JSGeneratorObject::kResultPropertyCount == 2); STATIC_ASSERT(JSGeneratorObject::kResultPropertyCount == 2);
Handle<Map> generator_result_map = Map::Create( Handle<JSFunction> object_function(native_context()->object_function());
ASSERT(object_function->initial_map()->inobject_properties() == 0);
Handle<Map> iterator_result_map = Map::Create(
object_function, JSGeneratorObject::kResultPropertyCount); object_function, JSGeneratorObject::kResultPropertyCount);
ASSERT(generator_result_map->inobject_properties() == ASSERT(iterator_result_map->inobject_properties() ==
JSGeneratorObject::kResultPropertyCount); JSGeneratorObject::kResultPropertyCount);
Map::EnsureDescriptorSlack( Map::EnsureDescriptorSlack(
generator_result_map, JSGeneratorObject::kResultPropertyCount); iterator_result_map, JSGeneratorObject::kResultPropertyCount);
Handle<String> value_string = factory()->InternalizeOneByteString( FieldDescriptor value_descr(isolate()->factory()->value_string(),
STATIC_ASCII_VECTOR("value"));
FieldDescriptor value_descr(value_string,
JSGeneratorObject::kResultValuePropertyIndex, JSGeneratorObject::kResultValuePropertyIndex,
NONE, NONE,
Representation::Tagged()); Representation::Tagged());
generator_result_map->AppendDescriptor(&value_descr); iterator_result_map->AppendDescriptor(&value_descr);
Handle<String> done_string = factory()->InternalizeOneByteString( FieldDescriptor done_descr(isolate()->factory()->done_string(),
STATIC_ASCII_VECTOR("done"));
FieldDescriptor done_descr(done_string,
JSGeneratorObject::kResultDonePropertyIndex, JSGeneratorObject::kResultDonePropertyIndex,
NONE, NONE,
Representation::Tagged()); Representation::Tagged());
generator_result_map->AppendDescriptor(&done_descr); iterator_result_map->AppendDescriptor(&done_descr);
generator_result_map->set_unused_property_fields(0); iterator_result_map->set_unused_property_fields(0);
ASSERT_EQ(JSGeneratorObject::kResultSize, ASSERT_EQ(JSGeneratorObject::kResultSize,
generator_result_map->instance_size()); iterator_result_map->instance_size());
native_context()->set_generator_result_map(*generator_result_map); native_context()->set_iterator_result_map(*iterator_result_map);
} }
} }
......
...@@ -113,8 +113,29 @@ function SetClear() { ...@@ -113,8 +113,29 @@ function SetClear() {
throw MakeTypeError('incompatible_method_receiver', throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.clear', this]); ['Set.prototype.clear', this]);
} }
// Replace the internal table with a new empty table. %SetClear(this);
%SetInitialize(this); }
function SetForEach(f, receiver) {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.forEach', this]);
}
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [f]);
}
var iterator = %SetCreateIterator(this, ITERATOR_KIND_VALUES);
var entry;
try {
while (!(entry = %SetIteratorNext(iterator)).done) {
%_CallFunction(receiver, entry.value, entry.value, this, f);
}
} finally {
%SetIteratorClose(iterator);
}
} }
...@@ -127,13 +148,16 @@ function SetUpSet() { ...@@ -127,13 +148,16 @@ function SetUpSet() {
%FunctionSetPrototype($Set, new $Object()); %FunctionSetPrototype($Set, new $Object());
%SetProperty($Set.prototype, "constructor", $Set, DONT_ENUM); %SetProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
%FunctionSetLength(SetForEach, 1);
// Set up the non-enumerable functions on the Set prototype object. // Set up the non-enumerable functions on the Set prototype object.
InstallGetter($Set.prototype, "size", SetGetSize); InstallGetter($Set.prototype, "size", SetGetSize);
InstallFunctions($Set.prototype, DONT_ENUM, $Array( InstallFunctions($Set.prototype, DONT_ENUM, $Array(
"add", SetAdd, "add", SetAdd,
"has", SetHas, "has", SetHas,
"delete", SetDelete, "delete", SetDelete,
"clear", SetClear "clear", SetClear,
"forEach", SetForEach
)); ));
} }
...@@ -202,8 +226,29 @@ function MapClear() { ...@@ -202,8 +226,29 @@ function MapClear() {
throw MakeTypeError('incompatible_method_receiver', throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.clear', this]); ['Map.prototype.clear', this]);
} }
// Replace the internal table with a new empty table. %MapClear(this);
%MapInitialize(this); }
function MapForEach(f, receiver) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.forEach', this]);
}
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [f]);
}
var iterator = %MapCreateIterator(this, ITERATOR_KIND_ENTRIES);
var entry;
try {
while (!(entry = %MapIteratorNext(iterator)).done) {
%_CallFunction(receiver, entry.value[1], entry.value[0], this, f);
}
} finally {
%MapIteratorClose(iterator);
}
} }
...@@ -216,6 +261,8 @@ function SetUpMap() { ...@@ -216,6 +261,8 @@ function SetUpMap() {
%FunctionSetPrototype($Map, new $Object()); %FunctionSetPrototype($Map, new $Object());
%SetProperty($Map.prototype, "constructor", $Map, DONT_ENUM); %SetProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
%FunctionSetLength(MapForEach, 1);
// Set up the non-enumerable functions on the Map prototype object. // Set up the non-enumerable functions on the Map prototype object.
InstallGetter($Map.prototype, "size", MapGetSize); InstallGetter($Map.prototype, "size", MapGetSize);
InstallFunctions($Map.prototype, DONT_ENUM, $Array( InstallFunctions($Map.prototype, DONT_ENUM, $Array(
...@@ -223,7 +270,8 @@ function SetUpMap() { ...@@ -223,7 +270,8 @@ function SetUpMap() {
"set", MapSet, "set", MapSet,
"has", MapHas, "has", MapHas,
"delete", MapDelete, "delete", MapDelete,
"clear", MapClear "clear", MapClear,
"forEach", MapForEach
)); ));
} }
......
...@@ -191,7 +191,9 @@ enum BindingFlags { ...@@ -191,7 +191,9 @@ enum BindingFlags {
V(STRICT_GENERATOR_FUNCTION_MAP_INDEX, Map, strict_generator_function_map) \ V(STRICT_GENERATOR_FUNCTION_MAP_INDEX, Map, strict_generator_function_map) \
V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, \ V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, \
generator_object_prototype_map) \ generator_object_prototype_map) \
V(GENERATOR_RESULT_MAP_INDEX, Map, generator_result_map) V(ITERATOR_RESULT_MAP_INDEX, Map, iterator_result_map) \
V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \
V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map)
// JSFunctions are pairs (context, function code), sometimes also called // JSFunctions are pairs (context, function code), sometimes also called
// closures. A Context object is used to represent function contexts and // closures. A Context object is used to represent function contexts and
...@@ -347,7 +349,9 @@ class Context: public FixedArray { ...@@ -347,7 +349,9 @@ class Context: public FixedArray {
SLOPPY_GENERATOR_FUNCTION_MAP_INDEX, SLOPPY_GENERATOR_FUNCTION_MAP_INDEX,
STRICT_GENERATOR_FUNCTION_MAP_INDEX, STRICT_GENERATOR_FUNCTION_MAP_INDEX,
GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX,
GENERATOR_RESULT_MAP_INDEX, ITERATOR_RESULT_MAP_INDEX,
MAP_ITERATOR_MAP_INDEX,
SET_ITERATOR_MAP_INDEX,
// Properties from here are treated as weak references by the full GC. // Properties from here are treated as weak references by the full GC.
// Scavenge treats them as strong references. // Scavenge treats them as strong references.
......
...@@ -1326,6 +1326,18 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name, ...@@ -1326,6 +1326,18 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name,
} }
Handle<JSObject> Factory::NewIteratorResultObject(Handle<Object> value,
bool done) {
Handle<Map> map(isolate()->native_context()->iterator_result_map());
Handle<JSObject> result = NewJSObjectFromMap(map, NOT_TENURED, false);
result->InObjectPropertyAtPut(
JSGeneratorObject::kResultValuePropertyIndex, *value);
result->InObjectPropertyAtPut(
JSGeneratorObject::kResultDonePropertyIndex, *ToBoolean(done));
return result;
}
Handle<ScopeInfo> Factory::NewScopeInfo(int length) { Handle<ScopeInfo> Factory::NewScopeInfo(int length) {
Handle<FixedArray> array = NewFixedArray(length, TENURED); Handle<FixedArray> array = NewFixedArray(length, TENURED);
array->set_map_no_write_barrier(*scope_info_map()); array->set_map_no_write_barrier(*scope_info_map());
...@@ -2345,4 +2357,5 @@ Handle<Object> Factory::ToBoolean(bool value) { ...@@ -2345,4 +2357,5 @@ Handle<Object> Factory::ToBoolean(bool value) {
return value ? true_value() : false_value(); return value ? true_value() : false_value();
} }
} } // namespace v8::internal } } // namespace v8::internal
...@@ -522,6 +522,8 @@ class Factory V8_FINAL { ...@@ -522,6 +522,8 @@ class Factory V8_FINAL {
Handle<Object> NewEvalError(const char* message, Handle<Object> NewEvalError(const char* message,
Vector< Handle<Object> > args); Vector< Handle<Object> > args);
Handle<JSObject> NewIteratorResultObject(Handle<Object> value, bool done);
Handle<String> NumberToString(Handle<Object> number, Handle<String> NumberToString(Handle<Object> number,
bool check_number_string_cache = true); bool check_number_string_cache = true);
......
...@@ -2227,7 +2227,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { ...@@ -2227,7 +2227,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required; Label gc_required;
Label allocated; Label allocated;
Handle<Map> map(isolate()->native_context()->generator_result_map()); Handle<Map> map(isolate()->native_context()->iterator_result_map());
__ Allocate(map->instance_size(), eax, ecx, edx, &gc_required, TAG_OBJECT); __ Allocate(map->instance_size(), eax, ecx, edx, &gc_required, TAG_OBJECT);
__ jmp(&allocated); __ jmp(&allocated);
......
...@@ -276,3 +276,8 @@ const PROPERTY_ATTRIBUTES_NONE = 0; ...@@ -276,3 +276,8 @@ const PROPERTY_ATTRIBUTES_NONE = 0;
const PROPERTY_ATTRIBUTES_STRING = 8; const PROPERTY_ATTRIBUTES_STRING = 8;
const PROPERTY_ATTRIBUTES_SYMBOLIC = 16; const PROPERTY_ATTRIBUTES_SYMBOLIC = 16;
const PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL = 32; const PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL = 32;
# Use for keys, values and entries iterators.
const ITERATOR_KIND_KEYS = 1;
const ITERATOR_KIND_VALUES = 2;
const ITERATOR_KIND_ENTRIES = 3;
...@@ -2285,7 +2285,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { ...@@ -2285,7 +2285,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required; Label gc_required;
Label allocated; Label allocated;
Handle<Map> map(isolate()->native_context()->generator_result_map()); Handle<Map> map(isolate()->native_context()->iterator_result_map());
__ Allocate(map->instance_size(), v0, a2, a3, &gc_required, TAG_OBJECT); __ Allocate(map->instance_size(), v0, a2, a3, &gc_required, TAG_OBJECT);
__ jmp(&allocated); __ jmp(&allocated);
......
...@@ -170,6 +170,12 @@ void HeapObject::HeapObjectVerify() { ...@@ -170,6 +170,12 @@ void HeapObject::HeapObjectVerify() {
case JS_MAP_TYPE: case JS_MAP_TYPE:
JSMap::cast(this)->JSMapVerify(); JSMap::cast(this)->JSMapVerify();
break; break;
case JS_SET_ITERATOR_TYPE:
JSSetIterator::cast(this)->JSSetIteratorVerify();
break;
case JS_MAP_ITERATOR_TYPE:
JSMapIterator::cast(this)->JSMapIteratorVerify();
break;
case JS_WEAK_MAP_TYPE: case JS_WEAK_MAP_TYPE:
JSWeakMap::cast(this)->JSWeakMapVerify(); JSWeakMap::cast(this)->JSWeakMapVerify();
break; break;
...@@ -708,6 +714,7 @@ void JSSet::JSSetVerify() { ...@@ -708,6 +714,7 @@ void JSSet::JSSetVerify() {
JSObjectVerify(); JSObjectVerify();
VerifyHeapPointer(table()); VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashTable() || table()->IsUndefined()); CHECK(table()->IsOrderedHashTable() || table()->IsUndefined());
// TODO(arv): Verify OrderedHashTable too.
} }
...@@ -716,6 +723,39 @@ void JSMap::JSMapVerify() { ...@@ -716,6 +723,39 @@ void JSMap::JSMapVerify() {
JSObjectVerify(); JSObjectVerify();
VerifyHeapPointer(table()); VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashTable() || table()->IsUndefined()); CHECK(table()->IsOrderedHashTable() || table()->IsUndefined());
// TODO(arv): Verify OrderedHashTable too.
}
void JSSetIterator::JSSetIteratorVerify() {
CHECK(IsJSSetIterator());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashTable() || table()->IsUndefined());
CHECK(index()->IsSmi());
CHECK(count()->IsSmi());
CHECK(kind()->IsSmi());
VerifyHeapPointer(next_iterator());
CHECK(next_iterator()->IsJSSetIterator() || next_iterator()->IsUndefined());
VerifyHeapPointer(table());
CHECK(previous_iterator()->IsJSSetIterator()
|| previous_iterator()->IsUndefined());
}
void JSMapIterator::JSMapIteratorVerify() {
CHECK(IsJSMapIterator());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashTable() || table()->IsUndefined());
CHECK(index()->IsSmi());
CHECK(count()->IsSmi());
CHECK(kind()->IsSmi());
VerifyHeapPointer(next_iterator());
CHECK(next_iterator()->IsJSMapIterator() || next_iterator()->IsUndefined());
VerifyHeapPointer(table());
CHECK(previous_iterator()->IsJSMapIterator()
|| previous_iterator()->IsUndefined());
} }
......
...@@ -696,6 +696,8 @@ bool Object::IsJSProxy() { ...@@ -696,6 +696,8 @@ bool Object::IsJSProxy() {
TYPE_CHECKER(JSFunctionProxy, JS_FUNCTION_PROXY_TYPE) TYPE_CHECKER(JSFunctionProxy, JS_FUNCTION_PROXY_TYPE)
TYPE_CHECKER(JSSet, JS_SET_TYPE) TYPE_CHECKER(JSSet, JS_SET_TYPE)
TYPE_CHECKER(JSMap, JS_MAP_TYPE) TYPE_CHECKER(JSMap, JS_MAP_TYPE)
TYPE_CHECKER(JSSetIterator, JS_SET_ITERATOR_TYPE)
TYPE_CHECKER(JSMapIterator, JS_MAP_ITERATOR_TYPE)
TYPE_CHECKER(JSWeakMap, JS_WEAK_MAP_TYPE) TYPE_CHECKER(JSWeakMap, JS_WEAK_MAP_TYPE)
TYPE_CHECKER(JSWeakSet, JS_WEAK_SET_TYPE) TYPE_CHECKER(JSWeakSet, JS_WEAK_SET_TYPE)
TYPE_CHECKER(JSContextExtensionObject, JS_CONTEXT_EXTENSION_OBJECT_TYPE) TYPE_CHECKER(JSContextExtensionObject, JS_CONTEXT_EXTENSION_OBJECT_TYPE)
...@@ -1916,6 +1918,10 @@ int JSObject::GetHeaderSize() { ...@@ -1916,6 +1918,10 @@ int JSObject::GetHeaderSize() {
return JSSet::kSize; return JSSet::kSize;
case JS_MAP_TYPE: case JS_MAP_TYPE:
return JSMap::kSize; return JSMap::kSize;
case JS_SET_ITERATOR_TYPE:
return JSSetIterator::kSize;
case JS_MAP_ITERATOR_TYPE:
return JSMapIterator::kSize;
case JS_WEAK_MAP_TYPE: case JS_WEAK_MAP_TYPE:
return JSWeakMap::kSize; return JSWeakMap::kSize;
case JS_WEAK_SET_TYPE: case JS_WEAK_SET_TYPE:
...@@ -2976,6 +2982,8 @@ CAST_ACCESSOR(JSProxy) ...@@ -2976,6 +2982,8 @@ CAST_ACCESSOR(JSProxy)
CAST_ACCESSOR(JSFunctionProxy) CAST_ACCESSOR(JSFunctionProxy)
CAST_ACCESSOR(JSSet) CAST_ACCESSOR(JSSet)
CAST_ACCESSOR(JSMap) CAST_ACCESSOR(JSMap)
CAST_ACCESSOR(JSSetIterator)
CAST_ACCESSOR(JSMapIterator)
CAST_ACCESSOR(JSWeakMap) CAST_ACCESSOR(JSWeakMap)
CAST_ACCESSOR(JSWeakSet) CAST_ACCESSOR(JSWeakSet)
CAST_ACCESSOR(Foreign) CAST_ACCESSOR(Foreign)
...@@ -5807,6 +5815,32 @@ void JSProxy::InitializeBody(int object_size, Object* value) { ...@@ -5807,6 +5815,32 @@ void JSProxy::InitializeBody(int object_size, Object* value) {
ACCESSORS(JSSet, table, Object, kTableOffset) ACCESSORS(JSSet, table, Object, kTableOffset)
ACCESSORS(JSMap, table, Object, kTableOffset) ACCESSORS(JSMap, table, Object, kTableOffset)
#define ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(name, type, offset) \
template<class Derived, class TableType> \
type* OrderedHashTableIterator<Derived, TableType>::name() { \
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, Smi, kIndexOffset)
ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(count, Smi, kCountOffset)
ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(kind, Smi, kKindOffset)
ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(next_iterator, Object,
kNextIteratorOffset)
ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(previous_iterator, Object,
kPreviousIteratorOffset)
#undef ORDERED_HASH_TABLE_ITERATOR_ACCESSORS
ACCESSORS(JSWeakCollection, table, Object, kTableOffset) ACCESSORS(JSWeakCollection, table, Object, kTableOffset)
ACCESSORS(JSWeakCollection, next, Object, kNextOffset) ACCESSORS(JSWeakCollection, next, Object, kNextOffset)
...@@ -6246,6 +6280,20 @@ SeededNumberDictionary* JSObject::element_dictionary() { ...@@ -6246,6 +6280,20 @@ SeededNumberDictionary* JSObject::element_dictionary() {
} }
Handle<JSSetIterator> JSSetIterator::Create(
Handle<OrderedHashSet> table,
int kind) {
return CreateInternal(table->GetIsolate()->set_iterator_map(), table, kind);
}
Handle<JSMapIterator> JSMapIterator::Create(
Handle<OrderedHashMap> table,
int kind) {
return CreateInternal(table->GetIsolate()->map_iterator_map(), table, kind);
}
bool Name::IsHashFieldComputed(uint32_t field) { bool Name::IsHashFieldComputed(uint32_t field) {
return (field & kHashNotComputedMask) == 0; return (field & kHashNotComputedMask) == 0;
} }
......
...@@ -174,6 +174,12 @@ void HeapObject::HeapObjectPrint(FILE* out) { ...@@ -174,6 +174,12 @@ void HeapObject::HeapObjectPrint(FILE* out) {
case JS_MAP_TYPE: case JS_MAP_TYPE:
JSMap::cast(this)->JSMapPrint(out); JSMap::cast(this)->JSMapPrint(out);
break; break;
case JS_SET_ITERATOR_TYPE:
JSSetIterator::cast(this)->JSSetIteratorPrint(out);
break;
case JS_MAP_ITERATOR_TYPE:
JSMapIterator::cast(this)->JSMapIteratorPrint(out);
break;
case JS_WEAK_MAP_TYPE: case JS_WEAK_MAP_TYPE:
JSWeakMap::cast(this)->JSWeakMapPrint(out); JSWeakMap::cast(this)->JSWeakMapPrint(out);
break; break;
...@@ -722,7 +728,7 @@ void JSProxy::JSProxyPrint(FILE* out) { ...@@ -722,7 +728,7 @@ void JSProxy::JSProxyPrint(FILE* out) {
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map())); PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
PrintF(out, " - handler = "); PrintF(out, " - handler = ");
handler()->Print(out); handler()->Print(out);
PrintF(out, " - hash = "); PrintF(out, "\n - hash = ");
hash()->Print(out); hash()->Print(out);
PrintF(out, "\n"); PrintF(out, "\n");
} }
...@@ -733,9 +739,9 @@ void JSFunctionProxy::JSFunctionProxyPrint(FILE* out) { ...@@ -733,9 +739,9 @@ void JSFunctionProxy::JSFunctionProxyPrint(FILE* out) {
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map())); PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
PrintF(out, " - handler = "); PrintF(out, " - handler = ");
handler()->Print(out); handler()->Print(out);
PrintF(out, " - call_trap = "); PrintF(out, "\n - call_trap = ");
call_trap()->Print(out); call_trap()->Print(out);
PrintF(out, " - construct_trap = "); PrintF(out, "\n - construct_trap = ");
construct_trap()->Print(out); construct_trap()->Print(out);
PrintF(out, "\n"); PrintF(out, "\n");
} }
...@@ -759,6 +765,48 @@ void JSMap::JSMapPrint(FILE* out) { ...@@ -759,6 +765,48 @@ void JSMap::JSMapPrint(FILE* out) {
} }
template<class Derived, class TableType>
void OrderedHashTableIterator<Derived, TableType>::
OrderedHashTableIteratorPrint(FILE* out) {
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
PrintF(out, " - table = ");
table()->ShortPrint(out);
PrintF(out, "\n - index = ");
index()->ShortPrint(out);
PrintF(out, "\n - count = ");
count()->ShortPrint(out);
PrintF(out, "\n - kind = ");
kind()->ShortPrint(out);
PrintF(out, "\n - next_iterator = ");
next_iterator()->ShortPrint(out);
PrintF(out, "\n - previous_iterator = ");
previous_iterator()->ShortPrint(out);
PrintF(out, "\n");
}
template void
OrderedHashTableIterator<JSSetIterator,
OrderedHashSet>::OrderedHashTableIteratorPrint(FILE* out);
template void
OrderedHashTableIterator<JSMapIterator,
OrderedHashMap>::OrderedHashTableIteratorPrint(FILE* out);
void JSSetIterator::JSSetIteratorPrint(FILE* out) {
HeapObject::PrintHeader(out, "JSSetIterator");
OrderedHashTableIteratorPrint(out);
}
void JSMapIterator::JSMapIteratorPrint(FILE* out) {
HeapObject::PrintHeader(out, "JSMapIterator");
OrderedHashTableIteratorPrint(out);
}
void JSWeakMap::JSWeakMapPrint(FILE* out) { void JSWeakMap::JSWeakMapPrint(FILE* out) {
HeapObject::PrintHeader(out, "JSWeakMap"); HeapObject::PrintHeader(out, "JSWeakMap");
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map())); PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
......
...@@ -163,6 +163,8 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( ...@@ -163,6 +163,8 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_GLOBAL_OBJECT_TYPE: case JS_GLOBAL_OBJECT_TYPE:
case JS_BUILTINS_OBJECT_TYPE: case JS_BUILTINS_OBJECT_TYPE:
case JS_MESSAGE_OBJECT_TYPE: case JS_MESSAGE_OBJECT_TYPE:
case JS_SET_ITERATOR_TYPE:
case JS_MAP_ITERATOR_TYPE:
return GetVisitorIdForSize(kVisitJSObject, return GetVisitorIdForSize(kVisitJSObject,
kVisitJSObjectGeneric, kVisitJSObjectGeneric,
instance_size); instance_size);
......
This diff is collapsed.
This diff is collapsed.
...@@ -1554,6 +1554,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDelete) { ...@@ -1554,6 +1554,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDelete) {
} }
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetClear) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
table = OrderedHashSet::Clear(table);
holder->set_table(*table);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetGetSize) { RUNTIME_FUNCTION(MaybeObject*, Runtime_SetGetSize) {
HandleScope scope(isolate); HandleScope scope(isolate);
ASSERT(args.length() == 1); ASSERT(args.length() == 1);
...@@ -1563,6 +1574,37 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetGetSize) { ...@@ -1563,6 +1574,37 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetGetSize) {
} }
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCreateIterator) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
CONVERT_SMI_ARG_CHECKED(kind, 1)
ASSERT(kind == JSSetIterator::kKindValues
|| kind == JSSetIterator::kKindEntries);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
Handle<JSSetIterator> iterator = JSSetIterator::Create(table, kind);
return *iterator;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetIteratorNext) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
Handle<JSObject> result = JSSetIterator::Next(holder);
return *result;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetIteratorClose) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
holder->Close();
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapInitialize) { RUNTIME_FUNCTION(MaybeObject*, Runtime_MapInitialize) {
HandleScope scope(isolate); HandleScope scope(isolate);
ASSERT(args.length() == 1); ASSERT(args.length() == 1);
...@@ -1609,6 +1651,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MapDelete) { ...@@ -1609,6 +1651,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MapDelete) {
} }
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapClear) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
table = OrderedHashMap::Clear(table);
holder->set_table(*table);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapSet) { RUNTIME_FUNCTION(MaybeObject*, Runtime_MapSet) {
HandleScope scope(isolate); HandleScope scope(isolate);
ASSERT(args.length() == 3); ASSERT(args.length() == 3);
...@@ -1631,6 +1684,38 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGetSize) { ...@@ -1631,6 +1684,38 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGetSize) {
} }
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapCreateIterator) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_SMI_ARG_CHECKED(kind, 1)
ASSERT(kind == JSMapIterator::kKindKeys
|| kind == JSMapIterator::kKindValues
|| kind == JSMapIterator::kKindEntries);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
Handle<JSMapIterator> iterator = JSMapIterator::Create(table, kind);
return *iterator;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapIteratorNext) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
Handle<JSObject> result = JSMapIterator::Next(holder);
return *result;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapIteratorClose) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
holder->Close();
return isolate->heap()->undefined_value();
}
static JSWeakCollection* WeakCollectionInitialize(Isolate* isolate, static JSWeakCollection* WeakCollectionInitialize(Isolate* isolate,
Handle<JSWeakCollection> weak_collection) { Handle<JSWeakCollection> weak_collection) {
ASSERT(weak_collection->map()->inobject_properties() == 0); ASSERT(weak_collection->map()->inobject_properties() == 0);
......
...@@ -297,15 +297,25 @@ namespace internal { ...@@ -297,15 +297,25 @@ namespace internal {
F(SetAdd, 2, 1) \ F(SetAdd, 2, 1) \
F(SetHas, 2, 1) \ F(SetHas, 2, 1) \
F(SetDelete, 2, 1) \ F(SetDelete, 2, 1) \
F(SetClear, 1, 1) \
F(SetGetSize, 1, 1) \ F(SetGetSize, 1, 1) \
F(SetCreateIterator, 2, 1) \
\
F(SetIteratorNext, 1, 1) \
F(SetIteratorClose, 1, 1) \
\ \
/* Harmony maps */ \ /* Harmony maps */ \
F(MapInitialize, 1, 1) \ F(MapInitialize, 1, 1) \
F(MapGet, 2, 1) \ F(MapGet, 2, 1) \
F(MapHas, 2, 1) \ F(MapHas, 2, 1) \
F(MapDelete, 2, 1) \ F(MapDelete, 2, 1) \
F(MapClear, 1, 1) \
F(MapSet, 3, 1) \ F(MapSet, 3, 1) \
F(MapGetSize, 1, 1) \ F(MapGetSize, 1, 1) \
F(MapCreateIterator, 2, 1) \
\
F(MapIteratorNext, 1, 1) \
F(MapIteratorClose, 1, 1) \
\ \
/* Harmony weak maps and sets */ \ /* Harmony weak maps and sets */ \
F(WeakCollectionInitialize, 1, 1) \ F(WeakCollectionInitialize, 1, 1) \
......
...@@ -2264,7 +2264,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { ...@@ -2264,7 +2264,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required; Label gc_required;
Label allocated; Label allocated;
Handle<Map> map(isolate()->native_context()->generator_result_map()); Handle<Map> map(isolate()->native_context()->iterator_result_map());
__ Allocate(map->instance_size(), rax, rcx, rdx, &gc_required, TAG_OBJECT); __ Allocate(map->instance_size(), rax, rcx, rdx, &gc_required, TAG_OBJECT);
__ jmp(&allocated); __ jmp(&allocated);
......
...@@ -36,7 +36,23 @@ namespace { ...@@ -36,7 +36,23 @@ namespace {
using namespace v8::internal; using namespace v8::internal;
void CheckIterResultObject(Isolate* isolate,
Handle<JSObject> result,
Handle<Object> value,
bool done) {
CHECK(Object::GetProperty(isolate, result, "value").ToHandleChecked()
->SameValue(*value));
CHECK(Object::GetProperty(isolate, result, "done").ToHandleChecked()
->IsBoolean());
CHECK_EQ(Object::GetProperty(isolate, result, "done").ToHandleChecked()
->BooleanValue(), done);
}
TEST(Set) { TEST(Set) {
i::FLAG_harmony_collections = true;
LocalContext context; LocalContext context;
Isolate* isolate = CcTest::i_isolate(); Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
...@@ -46,6 +62,11 @@ TEST(Set) { ...@@ -46,6 +62,11 @@ TEST(Set) {
CHECK_EQ(0, ordered_set->NumberOfElements()); CHECK_EQ(0, ordered_set->NumberOfElements());
CHECK_EQ(0, ordered_set->NumberOfDeletedElements()); CHECK_EQ(0, ordered_set->NumberOfDeletedElements());
Handle<JSSetIterator> value_iterator =
JSSetIterator::Create(ordered_set, JSSetIterator::kKindValues);
Handle<JSSetIterator> value_iterator_2 =
JSSetIterator::Create(ordered_set, JSSetIterator::kKindValues);
Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
Handle<JSObject> obj = factory->NewJSObjectFromMap(map); Handle<JSObject> obj = factory->NewJSObjectFromMap(map);
CHECK(!ordered_set->Contains(*obj)); CHECK(!ordered_set->Contains(*obj));
...@@ -68,6 +89,18 @@ TEST(Set) { ...@@ -68,6 +89,18 @@ TEST(Set) {
CHECK(ordered_set->Contains(*obj2)); CHECK(ordered_set->Contains(*obj2));
CHECK(ordered_set->Contains(*obj3)); CHECK(ordered_set->Contains(*obj3));
// Test iteration
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator), obj1, false);
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator), obj2, false);
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator), obj3, false);
CheckIterResultObject(isolate,
JSSetIterator::Next(value_iterator),
factory->undefined_value(),
true);
// Test growth // Test growth
ordered_set = OrderedHashSet::Add(ordered_set, obj); ordered_set = OrderedHashSet::Add(ordered_set, obj);
Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map); Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map);
...@@ -81,6 +114,22 @@ TEST(Set) { ...@@ -81,6 +114,22 @@ TEST(Set) {
CHECK_EQ(0, ordered_set->NumberOfDeletedElements()); CHECK_EQ(0, ordered_set->NumberOfDeletedElements());
CHECK_EQ(4, ordered_set->NumberOfBuckets()); CHECK_EQ(4, ordered_set->NumberOfBuckets());
// Test iteration after growth
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator_2), obj1, false);
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator_2), obj2, false);
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator_2), obj3, false);
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator_2), obj, false);
CheckIterResultObject(
isolate, JSSetIterator::Next(value_iterator_2), obj4, false);
CheckIterResultObject(isolate,
JSSetIterator::Next(value_iterator_2),
factory->undefined_value(),
true);
// Test shrinking // Test shrinking
ordered_set = OrderedHashSet::Remove(ordered_set, obj); ordered_set = OrderedHashSet::Remove(ordered_set, obj);
ordered_set = OrderedHashSet::Remove(ordered_set, obj1); ordered_set = OrderedHashSet::Remove(ordered_set, obj1);
...@@ -92,6 +141,8 @@ TEST(Set) { ...@@ -92,6 +141,8 @@ TEST(Set) {
TEST(Map) { TEST(Map) {
i::FLAG_harmony_collections = true;
LocalContext context; LocalContext context;
Isolate* isolate = CcTest::i_isolate(); Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
...@@ -101,6 +152,11 @@ TEST(Map) { ...@@ -101,6 +152,11 @@ TEST(Map) {
CHECK_EQ(0, ordered_map->NumberOfElements()); CHECK_EQ(0, ordered_map->NumberOfElements());
CHECK_EQ(0, ordered_map->NumberOfDeletedElements()); CHECK_EQ(0, ordered_map->NumberOfDeletedElements());
Handle<JSMapIterator> value_iterator =
JSMapIterator::Create(ordered_map, JSMapIterator::kKindValues);
Handle<JSMapIterator> key_iterator =
JSMapIterator::Create(ordered_map, JSMapIterator::kKindKeys);
Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
Handle<JSObject> obj = factory->NewJSObjectFromMap(map); Handle<JSObject> obj = factory->NewJSObjectFromMap(map);
Handle<JSObject> val = factory->NewJSObjectFromMap(map); Handle<JSObject> val = factory->NewJSObjectFromMap(map);
...@@ -128,6 +184,18 @@ TEST(Map) { ...@@ -128,6 +184,18 @@ TEST(Map) {
CHECK(ordered_map->Lookup(*obj2)->SameValue(*val2)); CHECK(ordered_map->Lookup(*obj2)->SameValue(*val2));
CHECK(ordered_map->Lookup(*obj3)->SameValue(*val3)); CHECK(ordered_map->Lookup(*obj3)->SameValue(*val3));
// Test iteration
CheckIterResultObject(
isolate, JSMapIterator::Next(value_iterator), val1, false);
CheckIterResultObject(
isolate, JSMapIterator::Next(value_iterator), val2, false);
CheckIterResultObject(
isolate, JSMapIterator::Next(value_iterator), val3, false);
CheckIterResultObject(isolate,
JSMapIterator::Next(value_iterator),
factory->undefined_value(),
true);
// Test growth // Test growth
ordered_map = OrderedHashMap::Put(ordered_map, obj, val); ordered_map = OrderedHashMap::Put(ordered_map, obj, val);
Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map); Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map);
...@@ -141,6 +209,22 @@ TEST(Map) { ...@@ -141,6 +209,22 @@ TEST(Map) {
CHECK_EQ(5, ordered_map->NumberOfElements()); CHECK_EQ(5, ordered_map->NumberOfElements());
CHECK_EQ(4, ordered_map->NumberOfBuckets()); CHECK_EQ(4, ordered_map->NumberOfBuckets());
// Test iteration after growth
CheckIterResultObject(
isolate, JSMapIterator::Next(key_iterator), obj1, false);
CheckIterResultObject(
isolate, JSMapIterator::Next(key_iterator), obj2, false);
CheckIterResultObject(
isolate, JSMapIterator::Next(key_iterator), obj3, false);
CheckIterResultObject(
isolate, JSMapIterator::Next(key_iterator), obj, false);
CheckIterResultObject(
isolate, JSMapIterator::Next(key_iterator), obj4, false);
CheckIterResultObject(isolate,
JSMapIterator::Next(key_iterator),
factory->undefined_value(),
true);
// Test shrinking // Test shrinking
ordered_map = OrderedHashMap::Put( ordered_map = OrderedHashMap::Put(
ordered_map, obj, factory->the_hole_value()); ordered_map, obj, factory->the_hole_value());
......
...@@ -507,3 +507,347 @@ for (var i = 9; i >= 0; i--) { ...@@ -507,3 +507,347 @@ for (var i = 9; i >= 0; i--) {
assertEquals('minus', m.get(0)); assertEquals('minus', m.get(0));
assertEquals('minus', m.get(-0)); assertEquals('minus', m.get(-0));
})(); })();
(function TestSetForEachInvalidTypes() {
assertThrows(function() {
Set.prototype.set.forEach.call({});
}, TypeError);
var set = new Set();
assertThrows(function() {
set.forEach({});
}, TypeError);
})();
(function TestSetForEach() {
var set = new Set();
set.add('a');
set.add('b');
set.add('c');
var buffer = '';
var receiver = {};
set.forEach(function(v, k, s) {
assertSame(v, k);
assertSame(set, s);
assertSame(this, receiver);
buffer += v;
if (v === 'a') {
set.delete('b');
set.add('d');
set.add('e');
set.add('f');
} else if (v === 'c') {
set.add('b');
set.delete('e');
}
}, receiver);
assertEquals('acdfb', buffer);
})();
(function TestSetForEachAddAtEnd() {
var set = new Set();
set.add('a');
set.add('b');
var buffer = '';
set.forEach(function(v) {
buffer += v;
if (v === 'b') {
set.add('c');
}
});
assertEquals('abc', buffer);
})();
(function TestSetForEachDeleteNext() {
var set = new Set();
set.add('a');
set.add('b');
set.add('c');
var buffer = '';
set.forEach(function(v) {
buffer += v;
if (v === 'b') {
set.delete('c');
}
});
assertEquals('ab', buffer);
})();
(function TestSetForEachDeleteVisitedAndAddAgain() {
var set = new Set();
set.add('a');
set.add('b');
set.add('c');
var buffer = '';
set.forEach(function(v) {
buffer += v;
if (v === 'b') {
set.delete('a');
} else if (v === 'c') {
set.add('a');
}
});
assertEquals('abca', buffer);
})();
(function TestSetForEachClear() {
var set = new Set();
set.add('a');
set.add('b');
set.add('c');
var buffer = '';
set.forEach(function(v) {
buffer += v;
if (v === 'a') {
set.clear();
set.add('d');
set.add('e');
}
});
assertEquals('ade', buffer);
})();
(function TestSetForEachNested() {
var set = new Set();
set.add('a');
set.add('b');
set.add('c');
var buffer = '';
set.forEach(function(v) {
buffer += v;
set.forEach(function(v) {
buffer += v;
if (v === 'a') {
set.delete('b');
}
});
});
assertEquals('aaccac', buffer);
})();
(function TestSetForEachEarlyExit() {
var set = new Set();
set.add('a');
set.add('b');
set.add('c');
var buffer = '';
var ex = {};
try {
set.forEach(function(v) {
buffer += v;
throw ex;
});
} catch (e) {
assertEquals(ex, e);
}
assertEquals('a', buffer);
})();
(function TestSetForEachGC() {
var set = new Set();
for (var i = 0; i < 100; i++) {
set.add(i);
}
var accumulated = 0;
set.forEach(function(v) {
accumulated += v;
if (v % 10 === 0) {
gc();
}
});
assertEquals(4950, accumulated);
})();
(function TestMapForEachInvalidTypes() {
assertThrows(function() {
Map.prototype.map.forEach.call({});
}, TypeError);
var map = new Map();
assertThrows(function() {
map.forEach({});
}, TypeError);
})();
(function TestMapForEach() {
var map = new Map();
map.set(0, 'a');
map.set(1, 'b');
map.set(2, 'c');
var buffer = [];
var receiver = {};
map.forEach(function(v, k, m) {
assertEquals(map, m);
assertEquals(this, receiver);
buffer.push(k, v);
if (k === 0) {
map.delete(1);
map.set(3, 'd');
map.set(4, 'e');
map.set(5, 'f');
} else if (k === 2) {
map.set(1, 'B');
map.delete(4);
}
}, receiver);
assertArrayEquals([0, 'a', 2, 'c', 3, 'd', 5, 'f', 1, 'B'], buffer);
})();
(function TestMapForEachAddAtEnd() {
var map = new Map();
map.set(0, 'a');
map.set(1, 'b');
var buffer = [];
map.forEach(function(v, k) {
buffer.push(k, v);
if (k === 1) {
map.set(2, 'c');
}
});
assertArrayEquals([0, 'a', 1, 'b', 2, 'c'], buffer);
})();
(function TestMapForEachDeleteNext() {
var map = new Map();
map.set(0, 'a');
map.set(1, 'b');
map.set(2, 'c');
var buffer = [];
map.forEach(function(v, k) {
buffer.push(k, v);
if (k === 1) {
map.delete(2);
}
});
assertArrayEquals([0, 'a', 1, 'b'], buffer);
})();
(function TestSetForEachDeleteVisitedAndAddAgain() {
var map = new Map();
map.set(0, 'a');
map.set(1, 'b');
map.set(2, 'c');
var buffer = [];
map.forEach(function(v, k) {
buffer.push(k, v);
if (k === 1) {
map.delete(0);
} else if (k === 2) {
map.set(0, 'a');
}
});
assertArrayEquals([0, 'a', 1, 'b', 2, 'c', 0, 'a'], buffer);
})();
(function TestMapForEachClear() {
var map = new Map();
map.set(0, 'a');
map.set(1, 'b');
map.set(2, 'c');
var buffer = [];
map.forEach(function(v, k) {
buffer.push(k, v);
if (k === 0) {
map.clear();
map.set(3, 'd');
map.set(4, 'e');
}
});
assertArrayEquals([0, 'a', 3, 'd', 4, 'e'], buffer);
})();
(function TestMapForEachNested() {
var map = new Map();
map.set(0, 'a');
map.set(1, 'b');
map.set(2, 'c');
var buffer = [];
map.forEach(function(v, k) {
buffer.push(k, v);
map.forEach(function(v, k) {
buffer.push(k, v);
if (k === 0) {
map.delete(1);
}
});
});
assertArrayEquals([0, 'a', 0, 'a', 2, 'c', 2, 'c', 0, 'a', 2, 'c'], buffer);
})();
(function TestMapForEachEarlyExit() {
var map = new Map();
map.set(0, 'a');
map.set(1, 'b');
map.set(2, 'c');
var buffer = [];
var ex = {};
try {
map.forEach(function(v, k) {
buffer.push(k, v);
throw ex;
});
} catch (e) {
assertEquals(ex, e);
}
assertArrayEquals([0, 'a'], buffer);
})();
(function TestMapForEachGC() {
var map = new Map();
for (var i = 0; i < 100; i++) {
map.set(i, i);
}
var accumulated = 0;
map.forEach(function(v) {
accumulated += v;
if (v % 10 === 0) {
gc();
}
});
assertEquals(4950, accumulated);
})();
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