From ce81b0d3a8c1e456779e45708e2370b9aade2fed Mon Sep 17 00:00:00 2001 From: "mstarzinger@chromium.org" Date: Mon, 22 Jul 2013 08:32:24 +0000 Subject: [PATCH] ES6: Implement WeakSet WeakSets work similar to ordinary Sets but the value (which must be an object) is held weakly. This is available under --harmony-collections BUG=v8:2785 R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/19678023 Patch from Erik Arvidsson . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15792 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/bootstrapper.cc | 5 + src/collection.js | 92 ++++++++++++- src/heap.cc | 10 +- src/heap.h | 4 +- src/incremental-marking.cc | 7 +- src/macros.py | 1 + src/mark-compact.cc | 71 +++++----- src/mark-compact.h | 20 +-- src/messages.js | 1 + src/object-observe.js | 4 +- src/objects-debug.cc | 11 ++ src/objects-inl.h | 13 +- src/objects-printer.cc | 13 ++ src/objects-visiting-inl.h | 6 +- src/objects-visiting.cc | 3 + src/objects-visiting.h | 1 + src/objects.cc | 5 + src/objects.h | 47 +++++-- src/runtime.cc | 52 ++++---- src/runtime.h | 12 +- src/types.cc | 1 + test/cctest/cctest.gyp | 1 + test/cctest/cctest.status | 1 + test/cctest/test-weaksets.cc | 250 ++++++++++++++++++++++++++++++++++++ test/mjsunit/harmony/collections.js | 65 +++++++++- tools/grokdump.py | 5 +- 26 files changed, 595 insertions(+), 106 deletions(-) create mode 100644 test/cctest/test-weaksets.cc diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index d8d065c..281f8b9 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1324,6 +1324,11 @@ void Genesis::InitializeExperimentalGlobal() { isolate()->initial_object_prototype(), Builtins::kIllegal, true, true); } + { // -- W e a k S e t + InstallFunction(global, "WeakSet", JS_WEAK_SET_TYPE, JSWeakSet::kSize, + isolate()->initial_object_prototype(), + Builtins::kIllegal, true, true); + } } if (FLAG_harmony_array_buffer) { diff --git a/src/collection.js b/src/collection.js index c5604ab..63ddbbb 100644 --- a/src/collection.js +++ b/src/collection.js @@ -34,6 +34,7 @@ var $Set = global.Set; var $Map = global.Map; var $WeakMap = global.WeakMap; +var $WeakSet = global.WeakSet; // Global sentinel to be used instead of undefined keys, which are not // supported internally but required for Harmony sets and maps. @@ -240,7 +241,7 @@ SetUpMap(); function WeakMapConstructor() { if (%_IsConstructCall()) { - %WeakMapInitialize(this); + %WeakCollectionInitialize(this); } else { return new $WeakMap(); } @@ -255,7 +256,7 @@ function WeakMapGet(key) { if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) { throw %MakeTypeError('invalid_weakmap_key', [this, key]); } - return %WeakMapGet(this, key); + return %WeakCollectionGet(this, key); } @@ -267,7 +268,7 @@ function WeakMapSet(key, value) { if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) { throw %MakeTypeError('invalid_weakmap_key', [this, key]); } - return %WeakMapSet(this, key, value); + return %WeakCollectionSet(this, key, value); } @@ -279,7 +280,7 @@ function WeakMapHas(key) { if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) { throw %MakeTypeError('invalid_weakmap_key', [this, key]); } - return %WeakMapHas(this, key); + return %WeakCollectionHas(this, key); } @@ -291,7 +292,7 @@ function WeakMapDelete(key) { if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) { throw %MakeTypeError('invalid_weakmap_key', [this, key]); } - return %WeakMapDelete(this, key); + return %WeakCollectionDelete(this, key); } @@ -301,7 +302,7 @@ function WeakMapClear() { ['WeakMap.prototype.clear', this]); } // Replace the internal table with a new empty table. - %WeakMapInitialize(this); + %WeakCollectionInitialize(this); } @@ -325,3 +326,82 @@ function SetUpWeakMap() { } SetUpWeakMap(); + + +// ------------------------------------------------------------------- +// Harmony WeakSet + +function WeakSetConstructor() { + if (%_IsConstructCall()) { + %WeakCollectionInitialize(this); + } else { + return new $WeakSet(); + } +} + + +function WeakSetAdd(value) { + if (!IS_WEAKSET(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakSet.prototype.add', this]); + } + if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) { + throw %MakeTypeError('invalid_weakset_value', [this, value]); + } + return %WeakCollectionSet(this, value, true); +} + + +function WeakSetHas(value) { + if (!IS_WEAKSET(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakSet.prototype.has', this]); + } + if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) { + throw %MakeTypeError('invalid_weakset_value', [this, value]); + } + return %WeakCollectionHas(this, value); +} + + +function WeakSetDelete(value) { + if (!IS_WEAKSET(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakSet.prototype.delete', this]); + } + if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) { + throw %MakeTypeError('invalid_weakset_value', [this, value]); + } + return %WeakCollectionDelete(this, value); +} + + +function WeakSetClear() { + if (!IS_WEAKSET(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakSet.prototype.clear', this]); + } + // Replace the internal table with a new empty table. + %WeakCollectionInitialize(this); +} + + +// ------------------------------------------------------------------- + +function SetUpWeakSet() { + %CheckIsBootstrapping(); + + %SetCode($WeakSet, WeakSetConstructor); + %FunctionSetPrototype($WeakSet, new $Object()); + %SetProperty($WeakSet.prototype, "constructor", $WeakSet, DONT_ENUM); + + // Set up the non-enumerable functions on the WeakSet prototype object. + InstallFunctions($WeakSet.prototype, DONT_ENUM, $Array( + "add", WeakSetAdd, + "has", WeakSetHas, + "delete", WeakSetDelete, + "clear", WeakSetClear + )); +} + +SetUpWeakSet(); diff --git a/src/heap.cc b/src/heap.cc index 310c910..dff217a 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1961,6 +1961,10 @@ class ScavengingVisitor : public StaticVisitorBase { &ObjectEvacuationStrategy:: Visit); + table_.Register(kVisitJSWeakSet, + &ObjectEvacuationStrategy:: + Visit); + table_.Register(kVisitJSArrayBuffer, &ObjectEvacuationStrategy:: Visit); @@ -7736,8 +7740,10 @@ GCTracer::~GCTracer() { PrintF("intracompaction_ptrs=%.1f ", scopes_[Scope::MC_UPDATE_POINTERS_BETWEEN_EVACUATED]); PrintF("misc_compaction=%.1f ", scopes_[Scope::MC_UPDATE_MISC_POINTERS]); - PrintF("weakmap_process=%.1f ", scopes_[Scope::MC_WEAKMAP_PROCESS]); - PrintF("weakmap_clear=%.1f ", scopes_[Scope::MC_WEAKMAP_CLEAR]); + PrintF("weakcollection_process=%.1f ", + scopes_[Scope::MC_WEAKCOLLECTION_PROCESS]); + PrintF("weakcollection_clear=%.1f ", + scopes_[Scope::MC_WEAKCOLLECTION_CLEAR]); PrintF("total_size_before=%" V8_PTR_PREFIX "d ", start_object_size_); PrintF("total_size_after=%" V8_PTR_PREFIX "d ", heap_->SizeOfObjects()); diff --git a/src/heap.h b/src/heap.h index 8be4323..6b02363 100644 --- a/src/heap.h +++ b/src/heap.h @@ -2758,8 +2758,8 @@ class GCTracer BASE_EMBEDDED { MC_UPDATE_POINTERS_TO_EVACUATED, MC_UPDATE_POINTERS_BETWEEN_EVACUATED, MC_UPDATE_MISC_POINTERS, - MC_WEAKMAP_PROCESS, - MC_WEAKMAP_CLEAR, + MC_WEAKCOLLECTION_PROCESS, + MC_WEAKCOLLECTION_CLEAR, MC_FLUSH_CODE, kNumberOfScopes }; diff --git a/src/incremental-marking.cc b/src/incremental-marking.cc index 17e79fd..df0f14a 100644 --- a/src/incremental-marking.cc +++ b/src/incremental-marking.cc @@ -273,11 +273,12 @@ class IncrementalMarkingMarkingVisitor VisitNativeContext(map, context); } - static void VisitJSWeakMap(Map* map, HeapObject* object) { + static void VisitWeakCollection(Map* map, HeapObject* object) { Heap* heap = map->GetHeap(); VisitPointers(heap, - HeapObject::RawField(object, JSWeakMap::kPropertiesOffset), - HeapObject::RawField(object, JSWeakMap::kSize)); + HeapObject::RawField(object, + JSWeakCollection::kPropertiesOffset), + HeapObject::RawField(object, JSWeakCollection::kSize)); } static void BeforeVisitingSharedFunctionInfo(HeapObject* object) {} diff --git a/src/macros.py b/src/macros.py index 5174d5f..d50231d 100644 --- a/src/macros.py +++ b/src/macros.py @@ -107,6 +107,7 @@ macro IS_REGEXP(arg) = (%_IsRegExp(arg)); macro IS_SET(arg) = (%_ClassOf(arg) === 'Set'); macro IS_MAP(arg) = (%_ClassOf(arg) === 'Map'); macro IS_WEAKMAP(arg) = (%_ClassOf(arg) === 'WeakMap'); +macro IS_WEAKSET(arg) = (%_ClassOf(arg) === 'WeakSet'); macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date'); macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number'); macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String'); diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 5c91dbf..95f673c 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -73,7 +73,7 @@ MarkCompactCollector::MarkCompactCollector() : // NOLINT migration_slots_buffer_(NULL), heap_(NULL), code_flusher_(NULL), - encountered_weak_maps_(NULL) { } + encountered_weak_collections_(NULL) { } #ifdef VERIFY_HEAP @@ -396,14 +396,14 @@ void MarkCompactCollector::CollectGarbage() { // Make sure that Prepare() has been called. The individual steps below will // update the state as they proceed. ASSERT(state_ == PREPARE_GC); - ASSERT(encountered_weak_maps_ == Smi::FromInt(0)); + ASSERT(encountered_weak_collections_ == Smi::FromInt(0)); MarkLiveObjects(); ASSERT(heap_->incremental_marking()->IsStopped()); if (FLAG_collect_maps) ClearNonLiveReferences(); - ClearWeakMaps(); + ClearWeakCollections(); #ifdef VERIFY_HEAP if (FLAG_verify_heap) { @@ -1449,35 +1449,36 @@ class MarkCompactMarkingVisitor shared->BeforeVisitingPointers(); } - static void VisitJSWeakMap(Map* map, HeapObject* object) { + static void VisitWeakCollection(Map* map, HeapObject* object) { MarkCompactCollector* collector = map->GetHeap()->mark_compact_collector(); - JSWeakMap* weak_map = reinterpret_cast(object); + JSWeakCollection* weak_collection = + reinterpret_cast(object); // Enqueue weak map in linked list of encountered weak maps. - if (weak_map->next() == Smi::FromInt(0)) { - weak_map->set_next(collector->encountered_weak_maps()); - collector->set_encountered_weak_maps(weak_map); + if (weak_collection->next() == Smi::FromInt(0)) { + weak_collection->set_next(collector->encountered_weak_collections()); + collector->set_encountered_weak_collections(weak_collection); } // Skip visiting the backing hash table containing the mappings. - int object_size = JSWeakMap::BodyDescriptor::SizeOf(map, object); + int object_size = JSWeakCollection::BodyDescriptor::SizeOf(map, object); BodyVisitorBase::IteratePointers( map->GetHeap(), object, - JSWeakMap::BodyDescriptor::kStartOffset, - JSWeakMap::kTableOffset); + JSWeakCollection::BodyDescriptor::kStartOffset, + JSWeakCollection::kTableOffset); BodyVisitorBase::IteratePointers( map->GetHeap(), object, - JSWeakMap::kTableOffset + kPointerSize, + JSWeakCollection::kTableOffset + kPointerSize, object_size); // Mark the backing hash table without pushing it on the marking stack. - Object* table_object = weak_map->table(); + Object* table_object = weak_collection->table(); if (!table_object->IsHashTable()) return; ObjectHashTable* table = ObjectHashTable::cast(table_object); Object** table_slot = - HeapObject::RawField(weak_map, JSWeakMap::kTableOffset); + HeapObject::RawField(weak_collection, JSWeakCollection::kTableOffset); MarkBit table_mark = Marking::MarkBitFrom(table); collector->RecordSlot(table_slot, table_slot, table); if (!table_mark.Get()) collector->SetMark(table, table_mark); @@ -2245,7 +2246,7 @@ void MarkCompactCollector::ProcessEphemeralMarking(ObjectVisitor* visitor) { isolate()->global_handles()->IterateObjectGroups( visitor, &IsUnmarkedHeapObjectWithHeap); MarkImplicitRefGroups(); - ProcessWeakMaps(); + ProcessWeakCollections(); work_to_do = !marking_deque_.IsEmpty(); ProcessMarkingDeque(); } @@ -2654,13 +2655,15 @@ void MarkCompactCollector::ClearNonLiveDependentCode(DependentCode* entries) { } -void MarkCompactCollector::ProcessWeakMaps() { - GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_WEAKMAP_PROCESS); - Object* weak_map_obj = encountered_weak_maps(); - while (weak_map_obj != Smi::FromInt(0)) { - ASSERT(MarkCompactCollector::IsMarked(HeapObject::cast(weak_map_obj))); - JSWeakMap* weak_map = reinterpret_cast(weak_map_obj); - ObjectHashTable* table = ObjectHashTable::cast(weak_map->table()); +void MarkCompactCollector::ProcessWeakCollections() { + GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_WEAKCOLLECTION_PROCESS); + Object* weak_collection_obj = encountered_weak_collections(); + while (weak_collection_obj != Smi::FromInt(0)) { + ASSERT(MarkCompactCollector::IsMarked( + HeapObject::cast(weak_collection_obj))); + JSWeakCollection* weak_collection = + reinterpret_cast(weak_collection_obj); + ObjectHashTable* table = ObjectHashTable::cast(weak_collection->table()); Object** anchor = reinterpret_cast(table->address()); for (int i = 0; i < table->Capacity(); i++) { if (MarkCompactCollector::IsMarked(HeapObject::cast(table->KeyAt(i)))) { @@ -2675,27 +2678,29 @@ void MarkCompactCollector::ProcessWeakMaps() { this, anchor, value_slot); } } - weak_map_obj = weak_map->next(); + weak_collection_obj = weak_collection->next(); } } -void MarkCompactCollector::ClearWeakMaps() { - GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_WEAKMAP_CLEAR); - Object* weak_map_obj = encountered_weak_maps(); - while (weak_map_obj != Smi::FromInt(0)) { - ASSERT(MarkCompactCollector::IsMarked(HeapObject::cast(weak_map_obj))); - JSWeakMap* weak_map = reinterpret_cast(weak_map_obj); - ObjectHashTable* table = ObjectHashTable::cast(weak_map->table()); +void MarkCompactCollector::ClearWeakCollections() { + GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_WEAKCOLLECTION_CLEAR); + Object* weak_collection_obj = encountered_weak_collections(); + while (weak_collection_obj != Smi::FromInt(0)) { + ASSERT(MarkCompactCollector::IsMarked( + HeapObject::cast(weak_collection_obj))); + JSWeakCollection* weak_collection = + reinterpret_cast(weak_collection_obj); + ObjectHashTable* table = ObjectHashTable::cast(weak_collection->table()); for (int i = 0; i < table->Capacity(); i++) { if (!MarkCompactCollector::IsMarked(HeapObject::cast(table->KeyAt(i)))) { table->RemoveEntry(i); } } - weak_map_obj = weak_map->next(); - weak_map->set_next(Smi::FromInt(0)); + weak_collection_obj = weak_collection->next(); + weak_collection->set_next(Smi::FromInt(0)); } - set_encountered_weak_maps(Smi::FromInt(0)); + set_encountered_weak_collections(Smi::FromInt(0)); } diff --git a/src/mark-compact.h b/src/mark-compact.h index a942600..4063bde 100644 --- a/src/mark-compact.h +++ b/src/mark-compact.h @@ -695,9 +695,11 @@ class MarkCompactCollector { bool TryPromoteObject(HeapObject* object, int object_size); - inline Object* encountered_weak_maps() { return encountered_weak_maps_; } - inline void set_encountered_weak_maps(Object* weak_map) { - encountered_weak_maps_ = weak_map; + inline Object* encountered_weak_collections() { + return encountered_weak_collections_; + } + inline void set_encountered_weak_collections(Object* weak_collection) { + encountered_weak_collections_ = weak_collection; } void InvalidateCode(Code* code); @@ -893,15 +895,15 @@ class MarkCompactCollector { // ClearNonLiveTransitions pass or by calling this function. void ReattachInitialMaps(); - // Mark all values associated with reachable keys in weak maps encountered - // so far. This might push new object or even new weak maps onto the - // marking stack. - void ProcessWeakMaps(); + // Mark all values associated with reachable keys in weak collections + // encountered so far. This might push new object or even new weak maps onto + // the marking stack. + void ProcessWeakCollections(); // After all reachable objects have been marked those weak map entries // with an unreachable key are removed from all encountered weak maps. // The linked list of all encountered weak maps is destroyed. - void ClearWeakMaps(); + void ClearWeakCollections(); // ----------------------------------------------------------------------- // Phase 2: Sweeping to clear mark bits and free non-live objects for @@ -943,7 +945,7 @@ class MarkCompactCollector { Heap* heap_; MarkingDeque marking_deque_; CodeFlusher* code_flusher_; - Object* encountered_weak_maps_; + Object* encountered_weak_collections_; List evacuation_candidates_; List invalidated_code_; diff --git a/src/messages.js b/src/messages.js index 35f3255..761b311 100644 --- a/src/messages.js +++ b/src/messages.js @@ -94,6 +94,7 @@ var kMessages = { proxy_non_object_prop_names: ["Trap '", "%1", "' returned non-object ", "%0"], proxy_repeated_prop_name: ["Trap '", "%1", "' returned repeated property name '", "%2", "'"], invalid_weakmap_key: ["Invalid value used as weak map key"], + invalid_weakset_value: ["Invalid value used in weak set"], not_date_object: ["this is not a Date object."], observe_non_object: ["Object.", "%0", " cannot ", "%0", " non-object"], observe_non_function: ["Object.", "%0", " cannot deliver to non-function"], diff --git a/src/object-observe.js b/src/object-observe.js index 1c147d9..a5c12bf 100644 --- a/src/object-observe.js +++ b/src/object-observe.js @@ -44,12 +44,12 @@ ObservationWeakMap.prototype = { get: function(key) { key = %UnwrapGlobalProxy(key); if (!IS_SPEC_OBJECT(key)) return void 0; - return %WeakMapGet(this.map_, key); + return %WeakCollectionGet(this.map_, key); }, set: function(key, value) { key = %UnwrapGlobalProxy(key); if (!IS_SPEC_OBJECT(key)) return void 0; - %WeakMapSet(this.map_, key, value); + %WeakCollectionSet(this.map_, key, value); }, has: function(key) { return !IS_UNDEFINED(this.get(key)); diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 8c0a092..cb5f2b7 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -181,6 +181,9 @@ void HeapObject::HeapObjectVerify() { case JS_WEAK_MAP_TYPE: JSWeakMap::cast(this)->JSWeakMapVerify(); break; + case JS_WEAK_SET_TYPE: + JSWeakSet::cast(this)->JSWeakSetVerify(); + break; case JS_REGEXP_TYPE: JSRegExp::cast(this)->JSRegExpVerify(); break; @@ -699,6 +702,14 @@ void JSWeakMap::JSWeakMapVerify() { } +void JSWeakSet::JSWeakSetVerify() { + CHECK(IsJSWeakSet()); + JSObjectVerify(); + VerifyHeapPointer(table()); + CHECK(table()->IsHashTable() || table()->IsUndefined()); +} + + void JSRegExp::JSRegExpVerify() { JSObjectVerify(); CHECK(data()->IsUndefined() || data()->IsFixedArray()); diff --git a/src/objects-inl.h b/src/objects-inl.h index ee8ecf1..c12a12a 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -567,12 +567,18 @@ TYPE_CHECKER(JSFunctionProxy, JS_FUNCTION_PROXY_TYPE) TYPE_CHECKER(JSSet, JS_SET_TYPE) TYPE_CHECKER(JSMap, JS_MAP_TYPE) TYPE_CHECKER(JSWeakMap, JS_WEAK_MAP_TYPE) +TYPE_CHECKER(JSWeakSet, JS_WEAK_SET_TYPE) TYPE_CHECKER(JSContextExtensionObject, JS_CONTEXT_EXTENSION_OBJECT_TYPE) TYPE_CHECKER(Map, MAP_TYPE) TYPE_CHECKER(FixedArray, FIXED_ARRAY_TYPE) TYPE_CHECKER(FixedDoubleArray, FIXED_DOUBLE_ARRAY_TYPE) +bool Object::IsJSWeakCollection() { + return IsJSWeakMap() || IsJSWeakSet(); +} + + bool Object::IsDescriptorArray() { return IsFixedArray(); } @@ -1688,6 +1694,8 @@ int JSObject::GetHeaderSize() { return JSMap::kSize; case JS_WEAK_MAP_TYPE: return JSWeakMap::kSize; + case JS_WEAK_SET_TYPE: + return JSWeakSet::kSize; case JS_REGEXP_TYPE: return JSRegExp::kSize; case JS_CONTEXT_EXTENSION_OBJECT_TYPE: @@ -2569,6 +2577,7 @@ CAST_ACCESSOR(JSFunctionProxy) CAST_ACCESSOR(JSSet) CAST_ACCESSOR(JSMap) CAST_ACCESSOR(JSWeakMap) +CAST_ACCESSOR(JSWeakSet) CAST_ACCESSOR(Foreign) CAST_ACCESSOR(ByteArray) CAST_ACCESSOR(FreeSpace) @@ -5092,8 +5101,8 @@ void JSProxy::InitializeBody(int object_size, Object* value) { ACCESSORS(JSSet, table, Object, kTableOffset) ACCESSORS(JSMap, table, Object, kTableOffset) -ACCESSORS(JSWeakMap, table, Object, kTableOffset) -ACCESSORS(JSWeakMap, next, Object, kNextOffset) +ACCESSORS(JSWeakCollection, table, Object, kTableOffset) +ACCESSORS(JSWeakCollection, next, Object, kNextOffset) Address Foreign::foreign_address() { diff --git a/src/objects-printer.cc b/src/objects-printer.cc index f00577e..91b1c2e 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -183,6 +183,9 @@ void HeapObject::HeapObjectPrint(FILE* out) { case JS_WEAK_MAP_TYPE: JSWeakMap::cast(this)->JSWeakMapPrint(out); break; + case JS_WEAK_SET_TYPE: + JSWeakSet::cast(this)->JSWeakSetPrint(out); + break; case FOREIGN_TYPE: Foreign::cast(this)->ForeignPrint(out); break; @@ -559,6 +562,7 @@ static const char* TypeToString(InstanceType type) { case JS_ARRAY_TYPE: return "JS_ARRAY"; case JS_PROXY_TYPE: return "JS_PROXY"; case JS_WEAK_MAP_TYPE: return "JS_WEAK_MAP"; + case JS_WEAK_SET_TYPE: return "JS_WEAK_SET"; case JS_REGEXP_TYPE: return "JS_REGEXP"; case JS_VALUE_TYPE: return "JS_VALUE"; case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT"; @@ -824,6 +828,15 @@ void JSWeakMap::JSWeakMapPrint(FILE* out) { } +void JSWeakSet::JSWeakSetPrint(FILE* out) { + HeapObject::PrintHeader(out, "JSWeakSet"); + PrintF(out, " - map = 0x%p\n", reinterpret_cast(map())); + PrintF(out, " - table = "); + table()->ShortPrint(out); + PrintF(out, "\n"); +} + + void JSArrayBuffer::JSArrayBufferPrint(FILE* out) { HeapObject::PrintHeader(out, "JSArrayBuffer"); PrintF(out, " - map = 0x%p\n", reinterpret_cast(map())); diff --git a/src/objects-visiting-inl.h b/src/objects-visiting-inl.h index 29abbe2..9398d6d 100644 --- a/src/objects-visiting-inl.h +++ b/src/objects-visiting-inl.h @@ -89,6 +89,8 @@ void StaticNewSpaceVisitor::Initialize() { table_.Register(kVisitJSWeakMap, &JSObjectVisitor::Visit); + table_.Register(kVisitJSWeakSet, &JSObjectVisitor::Visit); + table_.Register(kVisitJSRegExp, &JSObjectVisitor::Visit); table_.template RegisterSpecializations::Initialize() { table_.Register(kVisitSeqTwoByteString, &DataObjectVisitor::Visit); - table_.Register(kVisitJSWeakMap, &StaticVisitor::VisitJSWeakMap); + table_.Register(kVisitJSWeakMap, &StaticVisitor::VisitWeakCollection); + + table_.Register(kVisitJSWeakSet, &StaticVisitor::VisitWeakCollection); table_.Register(kVisitOddball, &FixedBodyVisitorAdd(""); break; } + case JS_WEAK_SET_TYPE: { + accumulator->Add(""); + break; + } case JS_REGEXP_TYPE: { accumulator->Add(""); break; @@ -1653,6 +1657,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, case JS_SET_TYPE: case JS_MAP_TYPE: case JS_WEAK_MAP_TYPE: + case JS_WEAK_SET_TYPE: case JS_REGEXP_TYPE: case JS_GLOBAL_PROXY_TYPE: case JS_GLOBAL_OBJECT_TYPE: diff --git a/src/objects.h b/src/objects.h index 672d81c..f197b23 100644 --- a/src/objects.h +++ b/src/objects.h @@ -60,11 +60,13 @@ // - JSArray // - JSArrayBuffer // - JSArrayBufferView -// - JSTypedArray -// - JSDataView +// - JSTypedArray +// - JSDataView // - JSSet // - JSMap -// - JSWeakMap +// - JSWeakCollection +// - JSWeakMap +// - JSWeakSet // - JSRegExp // - JSFunction // - JSGeneratorObject @@ -415,6 +417,7 @@ const int kStubMinorKeyBits = kBitsPerInt - kSmiTagSize - kStubMajorKeyBits; V(JS_DATA_VIEW_TYPE) \ V(JS_PROXY_TYPE) \ V(JS_WEAK_MAP_TYPE) \ + V(JS_WEAK_SET_TYPE) \ V(JS_REGEXP_TYPE) \ \ V(JS_FUNCTION_TYPE) \ @@ -771,6 +774,7 @@ enum InstanceType { JS_SET_TYPE, JS_MAP_TYPE, JS_WEAK_MAP_TYPE, + JS_WEAK_SET_TYPE, JS_REGEXP_TYPE, @@ -1020,7 +1024,9 @@ class MaybeObject BASE_EMBEDDED { V(JSFunctionProxy) \ V(JSSet) \ V(JSMap) \ + V(JSWeakCollection) \ V(JSWeakMap) \ + V(JSWeakSet) \ V(JSRegExp) \ V(HashTable) \ V(Dictionary) \ @@ -8890,8 +8896,8 @@ class JSMap: public JSObject { }; -// The JSWeakMap describes EcmaScript Harmony weak maps -class JSWeakMap: public JSObject { +// Base class for both JSWeakMap and JSWeakSet +class JSWeakCollection: public JSObject { public: // [table]: the backing hash table mapping keys to values. DECL_ACCESSORS(table, Object) @@ -8899,6 +8905,18 @@ class JSWeakMap: public JSObject { // [next]: linked list of encountered weak maps during GC. DECL_ACCESSORS(next, Object) + static const int kTableOffset = JSObject::kHeaderSize; + static const int kNextOffset = kTableOffset + kPointerSize; + static const int kSize = kNextOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakCollection); +}; + + +// The JSWeakMap describes EcmaScript Harmony weak maps +class JSWeakMap: public JSWeakCollection { + public: // Casting. static inline JSWeakMap* cast(Object* obj); @@ -8906,15 +8924,26 @@ class JSWeakMap: public JSObject { DECLARE_PRINTER(JSWeakMap) DECLARE_VERIFIER(JSWeakMap) - static const int kTableOffset = JSObject::kHeaderSize; - static const int kNextOffset = kTableOffset + kPointerSize; - static const int kSize = kNextOffset + kPointerSize; - private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakMap); }; +// The JSWeakSet describes EcmaScript Harmony weak sets +class JSWeakSet: public JSWeakCollection { + public: + // Casting. + static inline JSWeakSet* cast(Object* obj); + + // Dispatched behavior. + DECLARE_PRINTER(JSWeakSet) + DECLARE_VERIFIER(JSWeakSet) + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakSet); +}; + + class JSArrayBuffer: public JSObject { public: // [backing_store]: backing memory for this array diff --git a/src/runtime.cc b/src/runtime.cc index bad089b..44ecbe9 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1421,69 +1421,73 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGetSize) { } -static JSWeakMap* WeakMapInitialize(Isolate* isolate, - Handle weakmap) { - ASSERT(weakmap->map()->inobject_properties() == 0); +static JSWeakCollection* WeakCollectionInitialize(Isolate* isolate, + Handle weak_collection) { + ASSERT(weak_collection->map()->inobject_properties() == 0); Handle table = isolate->factory()->NewObjectHashTable(0); - weakmap->set_table(*table); - weakmap->set_next(Smi::FromInt(0)); - return *weakmap; + weak_collection->set_table(*table); + weak_collection->set_next(Smi::FromInt(0)); + return *weak_collection; } -RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapInitialize) { +RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionInitialize) { HandleScope scope(isolate); ASSERT(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); - return WeakMapInitialize(isolate, weakmap); + CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); + return WeakCollectionInitialize(isolate, weak_collection); } -RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapGet) { +RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionGet) { HandleScope scope(isolate); ASSERT(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); + CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - Handle table(ObjectHashTable::cast(weakmap->table())); + Handle table( + ObjectHashTable::cast(weak_collection->table())); Handle lookup(table->Lookup(*key), isolate); return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup; } -RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapHas) { +RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionHas) { HandleScope scope(isolate); ASSERT(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); + CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - Handle table(ObjectHashTable::cast(weakmap->table())); + Handle table( + ObjectHashTable::cast(weak_collection->table())); Handle lookup(table->Lookup(*key), isolate); return isolate->heap()->ToBoolean(!lookup->IsTheHole()); } -RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapDelete) { +RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionDelete) { HandleScope scope(isolate); ASSERT(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); + CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - Handle table(ObjectHashTable::cast(weakmap->table())); + Handle table(ObjectHashTable::cast( + weak_collection->table())); Handle lookup(table->Lookup(*key), isolate); Handle new_table = PutIntoObjectHashTable(table, key, isolate->factory()->the_hole_value()); - weakmap->set_table(*new_table); + weak_collection->set_table(*new_table); return isolate->heap()->ToBoolean(!lookup->IsTheHole()); } -RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapSet) { +RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionSet) { HandleScope scope(isolate); ASSERT(args.length() == 3); - CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); + CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); Handle value(args[2], isolate); - Handle table(ObjectHashTable::cast(weakmap->table())); + Handle table( + ObjectHashTable::cast(weak_collection->table())); Handle new_table = PutIntoObjectHashTable(table, key, value); - weakmap->set_table(*new_table); + weak_collection->set_table(*new_table); return isolate->heap()->undefined_value(); } @@ -13787,7 +13791,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ObservationWeakMapCreate) { isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize); Handle weakmap = Handle::cast(isolate->factory()->NewJSObjectFromMap(map)); - return WeakMapInitialize(isolate, weakmap); + return WeakCollectionInitialize(isolate, weakmap); } diff --git a/src/runtime.h b/src/runtime.h index 7f3f97e..266ec33 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -343,12 +343,12 @@ namespace internal { F(MapSet, 3, 1) \ F(MapGetSize, 1, 1) \ \ - /* Harmony weakmaps */ \ - F(WeakMapInitialize, 1, 1) \ - F(WeakMapGet, 2, 1) \ - F(WeakMapHas, 2, 1) \ - F(WeakMapDelete, 2, 1) \ - F(WeakMapSet, 3, 1) \ + /* Harmony weak maps and sets */ \ + F(WeakCollectionInitialize, 1, 1) \ + F(WeakCollectionGet, 2, 1) \ + F(WeakCollectionHas, 2, 1) \ + F(WeakCollectionDelete, 2, 1) \ + F(WeakCollectionSet, 3, 1) \ \ /* Harmony observe */ \ F(IsObserved, 1, 1) \ diff --git a/src/types.cc b/src/types.cc index ff96b5a..8bf9129 100644 --- a/src/types.cc +++ b/src/types.cc @@ -180,6 +180,7 @@ int Type::LubBitset() { case JS_SET_TYPE: case JS_MAP_TYPE: case JS_WEAK_MAP_TYPE: + case JS_WEAK_SET_TYPE: if (map->is_undetectable()) return kUndetectable; return kOtherObject; case JS_ARRAY_TYPE: diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 04ca041..9df5c7b 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -104,6 +104,7 @@ 'test-utils.cc', 'test-version.cc', 'test-weakmaps.cc', + 'test-weaksets.cc', 'test-weaktypedarrays.cc' ], 'conditions': [ diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status index b8d95b3..9e8f608 100644 --- a/test/cctest/cctest.status +++ b/test/cctest/cctest.status @@ -47,6 +47,7 @@ test-log/EquivalenceOfLoggingAndTraversal: PASS || FAIL # We do not yet shrink weak maps after they have been emptied by the GC test-weakmaps/Shrinking: FAIL +test-weaksets/WeakSet_Shrinking: FAIL # Deferred stack trace formatting is temporarily disabled. test-heap/ReleaseStackTraceData: PASS || FAIL diff --git a/test/cctest/test-weaksets.cc b/test/cctest/test-weaksets.cc new file mode 100644 index 0000000..707f903 --- /dev/null +++ b/test/cctest/test-weaksets.cc @@ -0,0 +1,250 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "global-handles.h" +#include "snapshot.h" +#include "cctest.h" + +using namespace v8::internal; + + +static Isolate* GetIsolateFrom(LocalContext* context) { + return reinterpret_cast((*context)->GetIsolate()); +} + + +static Handle AllocateJSWeakSet(Isolate* isolate) { + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + Handle map = factory->NewMap(JS_WEAK_SET_TYPE, JSWeakSet::kSize); + Handle weakset_obj = factory->NewJSObjectFromMap(map); + Handle weakset(JSWeakSet::cast(*weakset_obj)); + // Do not use handles for the hash table, it would make entries strong. + Object* table_obj = ObjectHashTable::Allocate(heap, 1)->ToObjectChecked(); + ObjectHashTable* table = ObjectHashTable::cast(table_obj); + weakset->set_table(table); + weakset->set_next(Smi::FromInt(0)); + return weakset; +} + +static void PutIntoWeakSet(Handle weakset, + Handle key, + Handle value) { + Handle table = PutIntoObjectHashTable( + Handle(ObjectHashTable::cast(weakset->table())), + Handle(JSObject::cast(*key)), + value); + weakset->set_table(*table); +} + +static int NumberOfWeakCalls = 0; +static void WeakPointerCallback(v8::Isolate* isolate, + v8::Persistent* handle, + void* id) { + ASSERT(id == reinterpret_cast(1234)); + NumberOfWeakCalls++; + handle->Dispose(isolate); +} + + +TEST(WeakSet_Weakness) { + FLAG_incremental_marking = false; + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + HandleScope scope(isolate); + Handle weakset = AllocateJSWeakSet(isolate); + GlobalHandles* global_handles = isolate->global_handles(); + + // Keep global reference to the key. + Handle key; + { + HandleScope scope(isolate); + Handle map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); + Handle object = factory->NewJSObjectFromMap(map); + key = global_handles->Create(*object); + } + CHECK(!global_handles->IsWeak(key.location())); + + // Put entry into weak set. + { + HandleScope scope(isolate); + PutIntoWeakSet(weakset, + Handle(JSObject::cast(*key)), + Handle(Smi::FromInt(23), isolate)); + } + CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements()); + + // Force a full GC. + heap->CollectAllGarbage(false); + CHECK_EQ(0, NumberOfWeakCalls); + CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements()); + CHECK_EQ( + 0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements()); + + // Make the global reference to the key weak. + { + HandleScope scope(isolate); + global_handles->MakeWeak(key.location(), + reinterpret_cast(1234), + &WeakPointerCallback); + } + CHECK(global_handles->IsWeak(key.location())); + + // Force a full GC. + // Perform two consecutive GCs because the first one will only clear + // weak references whereas the second one will also clear weak sets. + heap->CollectAllGarbage(false); + CHECK_EQ(1, NumberOfWeakCalls); + CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements()); + CHECK_EQ( + 0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements()); + heap->CollectAllGarbage(false); + CHECK_EQ(1, NumberOfWeakCalls); + CHECK_EQ(0, ObjectHashTable::cast(weakset->table())->NumberOfElements()); + CHECK_EQ( + 1, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements()); +} + + +TEST(WeakSet_Shrinking) { + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + HandleScope scope(isolate); + Handle weakset = AllocateJSWeakSet(isolate); + + // Check initial capacity. + CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->Capacity()); + + // Fill up weak set to trigger capacity change. + { + HandleScope scope(isolate); + Handle map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); + for (int i = 0; i < 32; i++) { + Handle object = factory->NewJSObjectFromMap(map); + PutIntoWeakSet(weakset, object, Handle(Smi::FromInt(i), isolate)); + } + } + + // Check increased capacity. + CHECK_EQ(128, ObjectHashTable::cast(weakset->table())->Capacity()); + + // Force a full GC. + CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->NumberOfElements()); + CHECK_EQ( + 0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements()); + heap->CollectAllGarbage(false); + CHECK_EQ(0, ObjectHashTable::cast(weakset->table())->NumberOfElements()); + CHECK_EQ( + 32, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements()); + + // Check shrunk capacity. + CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->Capacity()); +} + + +// Test that weak set values on an evacuation candidate which are not reachable +// by other paths are correctly recorded in the slots buffer. +TEST(WeakSet_Regress2060a) { + FLAG_always_compact = true; + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + HandleScope scope(isolate); + Handle function = + factory->NewFunction(factory->function_string(), factory->null_value()); + Handle key = factory->NewJSObject(function); + Handle weakset = AllocateJSWeakSet(isolate); + + // Start second old-space page so that values land on evacuation candidate. + Page* first_page = heap->old_pointer_space()->anchor()->next_page(); + factory->NewFixedArray(900 * KB / kPointerSize, TENURED); + + // Fill up weak set with values on an evacuation candidate. + { + HandleScope scope(isolate); + for (int i = 0; i < 32; i++) { + Handle object = factory->NewJSObject(function, TENURED); + CHECK(!heap->InNewSpace(object->address())); + CHECK(!first_page->Contains(object->address())); + PutIntoWeakSet(weakset, key, object); + } + } + + // Force compacting garbage collection. + CHECK(FLAG_always_compact); + heap->CollectAllGarbage(Heap::kNoGCFlags); +} + + +// Test that weak set keys on an evacuation candidate which are reachable by +// other strong paths are correctly recorded in the slots buffer. +TEST(WeakSet_Regress2060b) { + FLAG_always_compact = true; +#ifdef VERIFY_HEAP + FLAG_verify_heap = true; +#endif + + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + Factory* factory = isolate->factory(); + Heap* heap = isolate->heap(); + HandleScope scope(isolate); + Handle function = + factory->NewFunction(factory->function_string(), factory->null_value()); + + // Start second old-space page so that keys land on evacuation candidate. + Page* first_page = heap->old_pointer_space()->anchor()->next_page(); + factory->NewFixedArray(900 * KB / kPointerSize, TENURED); + + // Fill up weak set with keys on an evacuation candidate. + Handle keys[32]; + for (int i = 0; i < 32; i++) { + keys[i] = factory->NewJSObject(function, TENURED); + CHECK(!heap->InNewSpace(keys[i]->address())); + CHECK(!first_page->Contains(keys[i]->address())); + } + Handle weakset = AllocateJSWeakSet(isolate); + for (int i = 0; i < 32; i++) { + PutIntoWeakSet(weakset, + keys[i], + Handle(Smi::FromInt(i), isolate)); + } + + // Force compacting garbage collection. The subsequent collections are used + // to verify that key references were actually updated. + CHECK(FLAG_always_compact); + heap->CollectAllGarbage(Heap::kNoGCFlags); + heap->CollectAllGarbage(Heap::kNoGCFlags); + heap->CollectAllGarbage(Heap::kNoGCFlags); +} diff --git a/test/mjsunit/harmony/collections.js b/test/mjsunit/harmony/collections.js index cf18745..67f91a8 100644 --- a/test/mjsunit/harmony/collections.js +++ b/test/mjsunit/harmony/collections.js @@ -35,6 +35,7 @@ function TestValidSetCalls(m) { assertDoesNotThrow(function () { m.delete(new Object) }); } TestValidSetCalls(new Set); +TestValidSetCalls(new WeakSet); // Test valid getter and setter calls on Maps and WeakMaps @@ -85,6 +86,7 @@ function TestSetBehavior(set) { } } TestSetBehavior(new Set); +TestSet(new WeakSet, new Object); // Test expected mapping behavior for Maps and WeakMaps @@ -185,6 +187,7 @@ function TestEnumerable(func) { TestEnumerable(Set); TestEnumerable(Map); TestEnumerable(WeakMap); +TestEnumerable(WeakSet); // Test arbitrary properties on Maps and WeakMaps @@ -207,6 +210,7 @@ TestArbitrary(new WeakMap); assertTrue(Set() instanceof Set); assertTrue(Map() instanceof Map); assertTrue(WeakMap() instanceof WeakMap); +assertTrue(WeakSet() instanceof WeakSet); // Test whether NaN values as keys are treated correctly. @@ -234,6 +238,7 @@ assertTrue(s instanceof Set); assertTrue(Set.prototype.add instanceof Function) assertTrue(Set.prototype.has instanceof Function) assertTrue(Set.prototype.delete instanceof Function) +assertTrue(Set.prototype.clear instanceof Function) // Test some common JavaScript idioms for Maps @@ -243,6 +248,7 @@ assertTrue(Map.prototype.set instanceof Function) assertTrue(Map.prototype.get instanceof Function) assertTrue(Map.prototype.has instanceof Function) assertTrue(Map.prototype.delete instanceof Function) +assertTrue(Map.prototype.clear instanceof Function) // Test some common JavaScript idioms for WeakMaps @@ -252,18 +258,37 @@ assertTrue(WeakMap.prototype.set instanceof Function) assertTrue(WeakMap.prototype.get instanceof Function) assertTrue(WeakMap.prototype.has instanceof Function) assertTrue(WeakMap.prototype.delete instanceof Function) +assertTrue(WeakMap.prototype.clear instanceof Function) -// Test class of the Set, Map and WeakMap instance and prototype. +// Test some common JavaScript idioms for WeakSets +var s = new WeakSet; +assertTrue(s instanceof WeakSet); +assertTrue(WeakSet.prototype.add instanceof Function) +assertTrue(WeakSet.prototype.has instanceof Function) +assertTrue(WeakSet.prototype.delete instanceof Function) +assertTrue(WeakSet.prototype.clear instanceof Function) + + +// Test class of instance and prototype. assertEquals("Set", %_ClassOf(new Set)) assertEquals("Object", %_ClassOf(Set.prototype)) assertEquals("Map", %_ClassOf(new Map)) assertEquals("Object", %_ClassOf(Map.prototype)) assertEquals("WeakMap", %_ClassOf(new WeakMap)) assertEquals("Object", %_ClassOf(WeakMap.prototype)) +assertEquals("WeakSet", %_ClassOf(new WeakSet)) +assertEquals("Object", %_ClassOf(WeakMap.prototype)) + + +// Test name of constructor. +assertEquals("Set", Set.name); +assertEquals("Map", Map.name); +assertEquals("WeakMap", WeakMap.name); +assertEquals("WeakSet", WeakSet.name); -// Test constructor property of the Set, Map and WeakMap prototype. +// Test constructor property of the Set, Map, WeakMap and WeakSet prototype. function TestConstructor(C) { assertFalse(C === Object.prototype.constructor); assertSame(C, C.prototype.constructor); @@ -273,6 +298,21 @@ function TestConstructor(C) { TestConstructor(Set); TestConstructor(Map); TestConstructor(WeakMap); +TestConstructor(WeakSet); + + +function TestDescriptor(global, C) { + assertEquals({ + value: C, + writable: true, + enumerable: false, + configurable: true + }, Object.getOwnPropertyDescriptor(global, C.name)); +} +TestDescriptor(this, Set); +TestDescriptor(this, Map); +TestDescriptor(this, WeakMap); +TestDescriptor(this, WeakSet); // Regression test for WeakMap prototype. @@ -304,15 +344,19 @@ var alwaysBogus = [ undefined, null, true, "x", 23, {} ]; var bogusReceiversTestSet = [ { proto: Set.prototype, funcs: [ 'add', 'has', 'delete' ], - receivers: alwaysBogus.concat([ new Map, new WeakMap ]), + receivers: alwaysBogus.concat([ new Map, new WeakMap, new WeakSet ]), }, { proto: Map.prototype, funcs: [ 'get', 'set', 'has', 'delete' ], - receivers: alwaysBogus.concat([ new Set, new WeakMap ]), + receivers: alwaysBogus.concat([ new Set, new WeakMap, new WeakSet ]), }, { proto: WeakMap.prototype, funcs: [ 'get', 'set', 'has', 'delete' ], - receivers: alwaysBogus.concat([ new Set, new Map ]), + receivers: alwaysBogus.concat([ new Set, new Map, new WeakSet ]), + }, + { proto: WeakSet.prototype, + funcs: [ 'add', 'has', 'delete' ], + receivers: alwaysBogus.concat([ new Set, new Map, new WeakMap ]), }, ]; function TestBogusReceivers(testSet) { @@ -413,3 +457,14 @@ for (var i = 9; i >= 0; i--) { assertFalse(w.has(k)); assertEquals(undefined, w.get(k)); })(); + + +// Test WeakSet clear +(function() { + var k = new Object(); + var w = new WeakSet(); + w.add(k); + assertTrue(w.has(k)); + w.clear(); + assertFalse(w.has(k)); +})(); \ No newline at end of file diff --git a/tools/grokdump.py b/tools/grokdump.py index ccdc4b3..9719376 100755 --- a/tools/grokdump.py +++ b/tools/grokdump.py @@ -918,8 +918,9 @@ INSTANCE_TYPES = { 180: "JS_ARRAY_TYPE", 171: "JS_PROXY_TYPE", 183: "JS_WEAK_MAP_TYPE", - 184: "JS_REGEXP_TYPE", - 185: "JS_FUNCTION_TYPE", + 184: "JS_WEAK_SET_TYPE", + 185: "JS_REGEXP_TYPE", + 186: "JS_FUNCTION_TYPE", 170: "JS_FUNCTION_PROXY_TYPE", 165: "DEBUG_INFO_TYPE", 166: "BREAK_POINT_INFO_TYPE", -- 2.7.4