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) {
Label gc_required;
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);
__ jmp(&allocated);
......
......@@ -4706,7 +4706,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required;
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 }
......
......@@ -31,11 +31,6 @@
// in runtime.js:
// 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 arrayIteratorNextIndexSymbol = GLOBAL_PRIVATE("ArrayIterator#next");
var arrayIterationKindSymbol = GLOBAL_PRIVATE("ArrayIterator#kind");
......@@ -79,25 +74,25 @@ function ArrayIteratorNext() {
SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1);
if (itemKind == ARRAY_ITERATOR_KIND_VALUES)
if (itemKind == ITERATOR_KIND_VALUES)
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, false);
}
function ArrayEntries() {
return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_ENTRIES);
return CreateArrayIterator(this, ITERATOR_KIND_ENTRIES);
}
function ArrayValues() {
return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_VALUES);
return CreateArrayIterator(this, ITERATOR_KIND_VALUES);
}
function ArrayKeys() {
return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_KEYS);
return CreateArrayIterator(this, ITERATOR_KIND_KEYS);
}
function SetUpArrayIterator() {
......
......@@ -1297,6 +1297,16 @@ void Genesis::InitializeExperimentalGlobal() {
isolate()->initial_object_prototype(),
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) {
......@@ -1351,37 +1361,38 @@ void Genesis::InitializeExperimentalGlobal() {
*generator_object_prototype);
native_context()->set_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);
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);
ASSERT(generator_result_map->inobject_properties() ==
ASSERT(iterator_result_map->inobject_properties() ==
JSGeneratorObject::kResultPropertyCount);
Map::EnsureDescriptorSlack(
generator_result_map, JSGeneratorObject::kResultPropertyCount);
iterator_result_map, JSGeneratorObject::kResultPropertyCount);
Handle<String> value_string = factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("value"));
FieldDescriptor value_descr(value_string,
FieldDescriptor value_descr(isolate()->factory()->value_string(),
JSGeneratorObject::kResultValuePropertyIndex,
NONE,
Representation::Tagged());
generator_result_map->AppendDescriptor(&value_descr);
iterator_result_map->AppendDescriptor(&value_descr);
Handle<String> done_string = factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("done"));
FieldDescriptor done_descr(done_string,
FieldDescriptor done_descr(isolate()->factory()->done_string(),
JSGeneratorObject::kResultDonePropertyIndex,
NONE,
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,
generator_result_map->instance_size());
native_context()->set_generator_result_map(*generator_result_map);
iterator_result_map->instance_size());
native_context()->set_iterator_result_map(*iterator_result_map);
}
}
......
......@@ -113,8 +113,29 @@ function SetClear() {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.clear', this]);
}
// Replace the internal table with a new empty table.
%SetInitialize(this);
%SetClear(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() {
%FunctionSetPrototype($Set, new $Object());
%SetProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
%FunctionSetLength(SetForEach, 1);
// Set up the non-enumerable functions on the Set prototype object.
InstallGetter($Set.prototype, "size", SetGetSize);
InstallFunctions($Set.prototype, DONT_ENUM, $Array(
"add", SetAdd,
"has", SetHas,
"delete", SetDelete,
"clear", SetClear
"clear", SetClear,
"forEach", SetForEach
));
}
......@@ -202,8 +226,29 @@ function MapClear() {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.clear', this]);
}
// Replace the internal table with a new empty table.
%MapInitialize(this);
%MapClear(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() {
%FunctionSetPrototype($Map, new $Object());
%SetProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
%FunctionSetLength(MapForEach, 1);
// Set up the non-enumerable functions on the Map prototype object.
InstallGetter($Map.prototype, "size", MapGetSize);
InstallFunctions($Map.prototype, DONT_ENUM, $Array(
......@@ -223,7 +270,8 @@ function SetUpMap() {
"set", MapSet,
"has", MapHas,
"delete", MapDelete,
"clear", MapClear
"clear", MapClear,
"forEach", MapForEach
));
}
......
......@@ -191,7 +191,9 @@ enum BindingFlags {
V(STRICT_GENERATOR_FUNCTION_MAP_INDEX, Map, strict_generator_function_map) \
V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, 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
// closures. A Context object is used to represent function contexts and
......@@ -347,7 +349,9 @@ class Context: public FixedArray {
SLOPPY_GENERATOR_FUNCTION_MAP_INDEX,
STRICT_GENERATOR_FUNCTION_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.
// Scavenge treats them as strong references.
......
......@@ -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<FixedArray> array = NewFixedArray(length, TENURED);
array->set_map_no_write_barrier(*scope_info_map());
......@@ -2345,4 +2357,5 @@ Handle<Object> Factory::ToBoolean(bool value) {
return value ? true_value() : false_value();
}
} } // namespace v8::internal
......@@ -522,6 +522,8 @@ class Factory V8_FINAL {
Handle<Object> NewEvalError(const char* message,
Vector< Handle<Object> > args);
Handle<JSObject> NewIteratorResultObject(Handle<Object> value, bool done);
Handle<String> NumberToString(Handle<Object> number,
bool check_number_string_cache = true);
......
......@@ -2227,7 +2227,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required;
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);
__ jmp(&allocated);
......
......@@ -276,3 +276,8 @@ const PROPERTY_ATTRIBUTES_NONE = 0;
const PROPERTY_ATTRIBUTES_STRING = 8;
const PROPERTY_ATTRIBUTES_SYMBOLIC = 16;
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) {
Label gc_required;
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);
__ jmp(&allocated);
......
......@@ -170,6 +170,12 @@ void HeapObject::HeapObjectVerify() {
case JS_MAP_TYPE:
JSMap::cast(this)->JSMapVerify();
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:
JSWeakMap::cast(this)->JSWeakMapVerify();
break;
......@@ -708,6 +714,7 @@ void JSSet::JSSetVerify() {
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashTable() || table()->IsUndefined());
// TODO(arv): Verify OrderedHashTable too.
}
......@@ -716,6 +723,39 @@ void JSMap::JSMapVerify() {
JSObjectVerify();
VerifyHeapPointer(table());
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() {
TYPE_CHECKER(JSFunctionProxy, JS_FUNCTION_PROXY_TYPE)
TYPE_CHECKER(JSSet, JS_SET_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(JSWeakSet, JS_WEAK_SET_TYPE)
TYPE_CHECKER(JSContextExtensionObject, JS_CONTEXT_EXTENSION_OBJECT_TYPE)
......@@ -1916,6 +1918,10 @@ int JSObject::GetHeaderSize() {
return JSSet::kSize;
case JS_MAP_TYPE:
return JSMap::kSize;
case JS_SET_ITERATOR_TYPE:
return JSSetIterator::kSize;
case JS_MAP_ITERATOR_TYPE:
return JSMapIterator::kSize;
case JS_WEAK_MAP_TYPE:
return JSWeakMap::kSize;
case JS_WEAK_SET_TYPE:
......@@ -2976,6 +2982,8 @@ CAST_ACCESSOR(JSProxy)
CAST_ACCESSOR(JSFunctionProxy)
CAST_ACCESSOR(JSSet)
CAST_ACCESSOR(JSMap)
CAST_ACCESSOR(JSSetIterator)
CAST_ACCESSOR(JSMapIterator)
CAST_ACCESSOR(JSWeakMap)
CAST_ACCESSOR(JSWeakSet)
CAST_ACCESSOR(Foreign)
......@@ -5807,6 +5815,32 @@ void JSProxy::InitializeBody(int object_size, Object* value) {
ACCESSORS(JSSet, 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, next, Object, kNextOffset)
......@@ -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) {
return (field & kHashNotComputedMask) == 0;
}
......
......@@ -174,6 +174,12 @@ void HeapObject::HeapObjectPrint(FILE* out) {
case JS_MAP_TYPE:
JSMap::cast(this)->JSMapPrint(out);
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:
JSWeakMap::cast(this)->JSWeakMapPrint(out);
break;
......@@ -722,7 +728,7 @@ void JSProxy::JSProxyPrint(FILE* out) {
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
PrintF(out, " - handler = ");
handler()->Print(out);
PrintF(out, " - hash = ");
PrintF(out, "\n - hash = ");
hash()->Print(out);
PrintF(out, "\n");
}
......@@ -733,9 +739,9 @@ void JSFunctionProxy::JSFunctionProxyPrint(FILE* out) {
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
PrintF(out, " - handler = ");
handler()->Print(out);
PrintF(out, " - call_trap = ");
PrintF(out, "\n - call_trap = ");
call_trap()->Print(out);
PrintF(out, " - construct_trap = ");
PrintF(out, "\n - construct_trap = ");
construct_trap()->Print(out);
PrintF(out, "\n");
}
......@@ -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) {
HeapObject::PrintHeader(out, "JSWeakMap");
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
......
......@@ -163,6 +163,8 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_GLOBAL_OBJECT_TYPE:
case JS_BUILTINS_OBJECT_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
case JS_SET_ITERATOR_TYPE:
case JS_MAP_ITERATOR_TYPE:
return GetVisitorIdForSize(kVisitJSObject,
kVisitJSObjectGeneric,
instance_size);
......
This diff is collapsed.
This diff is collapsed.
......@@ -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) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
......@@ -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) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
......@@ -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) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
......@@ -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,
Handle<JSWeakCollection> weak_collection) {
ASSERT(weak_collection->map()->inobject_properties() == 0);
......
......@@ -297,15 +297,25 @@ namespace internal {
F(SetAdd, 2, 1) \
F(SetHas, 2, 1) \
F(SetDelete, 2, 1) \
F(SetClear, 1, 1) \
F(SetGetSize, 1, 1) \
F(SetCreateIterator, 2, 1) \
\
F(SetIteratorNext, 1, 1) \
F(SetIteratorClose, 1, 1) \
\
/* Harmony maps */ \
F(MapInitialize, 1, 1) \
F(MapGet, 2, 1) \
F(MapHas, 2, 1) \
F(MapDelete, 2, 1) \
F(MapClear, 1, 1) \
F(MapSet, 3, 1) \
F(MapGetSize, 1, 1) \
F(MapCreateIterator, 2, 1) \
\
F(MapIteratorNext, 1, 1) \
F(MapIteratorClose, 1, 1) \
\
/* Harmony weak maps and sets */ \
F(WeakCollectionInitialize, 1, 1) \
......
......@@ -2264,7 +2264,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
Label gc_required;
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);
__ jmp(&allocated);
......
......@@ -36,7 +36,23 @@ namespace {
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) {
i::FLAG_harmony_collections = true;
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
......@@ -46,6 +62,11 @@ TEST(Set) {
CHECK_EQ(0, ordered_set->NumberOfElements());
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<JSObject> obj = factory->NewJSObjectFromMap(map);
CHECK(!ordered_set->Contains(*obj));
......@@ -68,6 +89,18 @@ TEST(Set) {
CHECK(ordered_set->Contains(*obj2));
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
ordered_set = OrderedHashSet::Add(ordered_set, obj);
Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map);
......@@ -81,6 +114,22 @@ TEST(Set) {
CHECK_EQ(0, ordered_set->NumberOfDeletedElements());
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
ordered_set = OrderedHashSet::Remove(ordered_set, obj);
ordered_set = OrderedHashSet::Remove(ordered_set, obj1);
......@@ -92,6 +141,8 @@ TEST(Set) {
TEST(Map) {
i::FLAG_harmony_collections = true;
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
......@@ -101,6 +152,11 @@ TEST(Map) {
CHECK_EQ(0, ordered_map->NumberOfElements());
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<JSObject> obj = factory->NewJSObjectFromMap(map);
Handle<JSObject> val = factory->NewJSObjectFromMap(map);
......@@ -128,6 +184,18 @@ TEST(Map) {
CHECK(ordered_map->Lookup(*obj2)->SameValue(*val2));
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
ordered_map = OrderedHashMap::Put(ordered_map, obj, val);
Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map);
......@@ -141,6 +209,22 @@ TEST(Map) {
CHECK_EQ(5, ordered_map->NumberOfElements());
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
ordered_map = OrderedHashMap::Put(
ordered_map, obj, factory->the_hole_value());
......
......@@ -507,3 +507,347 @@ for (var i = 9; i >= 0; i--) {
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