ES6: Implement WeakSet
authormstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 22 Jul 2013 08:32:24 +0000 (08:32 +0000)
committermstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 22 Jul 2013 08:32:24 +0000 (08:32 +0000)
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 <arv@chromium.org>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15792 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

26 files changed:
src/bootstrapper.cc
src/collection.js
src/heap.cc
src/heap.h
src/incremental-marking.cc
src/macros.py
src/mark-compact.cc
src/mark-compact.h
src/messages.js
src/object-observe.js
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects-visiting-inl.h
src/objects-visiting.cc
src/objects-visiting.h
src/objects.cc
src/objects.h
src/runtime.cc
src/runtime.h
src/types.cc
test/cctest/cctest.gyp
test/cctest/cctest.status
test/cctest/test-weaksets.cc [new file with mode: 0644]
test/mjsunit/harmony/collections.js
tools/grokdump.py

index d8d065c..281f8b9 100644 (file)
@@ -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) {
index c5604ab..63ddbbb 100644 (file)
@@ -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();
index 310c910..dff217a 100644 (file)
@@ -1961,6 +1961,10 @@ class ScavengingVisitor : public StaticVisitorBase {
                     &ObjectEvacuationStrategy<POINTER_OBJECT>::
                     Visit);
 
+    table_.Register(kVisitJSWeakSet,
+                    &ObjectEvacuationStrategy<POINTER_OBJECT>::
+                    Visit);
+
     table_.Register(kVisitJSArrayBuffer,
                     &ObjectEvacuationStrategy<POINTER_OBJECT>::
                     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());
index 8be4323..6b02363 100644 (file)
@@ -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
     };
index 17e79fd..df0f14a 100644 (file)
@@ -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) {}
index 5174d5f..d50231d 100644 (file)
@@ -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');
index 5c91dbf..95f673c 100644 (file)
@@ -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<JSWeakMap*>(object);
+    JSWeakCollection* weak_collection =
+        reinterpret_cast<JSWeakCollection*>(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<MarkCompactMarkingVisitor>::IteratePointers(
         map->GetHeap(),
         object,
-        JSWeakMap::BodyDescriptor::kStartOffset,
-        JSWeakMap::kTableOffset);
+        JSWeakCollection::BodyDescriptor::kStartOffset,
+        JSWeakCollection::kTableOffset);
     BodyVisitorBase<MarkCompactMarkingVisitor>::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<JSWeakMap*>(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<JSWeakCollection*>(weak_collection_obj);
+    ObjectHashTable* table = ObjectHashTable::cast(weak_collection->table());
     Object** anchor = reinterpret_cast<Object**>(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<JSWeakMap*>(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<JSWeakCollection*>(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));
 }
 
 
index a942600..4063bde 100644 (file)
@@ -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<Page*> evacuation_candidates_;
   List<Code*> invalidated_code_;
index 35f3255..761b311 100644 (file)
@@ -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"],
index 1c147d9..a5c12bf 100644 (file)
@@ -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));
index 8c0a092..cb5f2b7 100644 (file)
@@ -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());
index ee8ecf1..c12a12a 100644 (file)
@@ -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() {
index f00577e..91b1c2e 100644 (file)
@@ -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<void*>(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<void*>(map()));
index 29abbe2..9398d6d 100644 (file)
@@ -89,6 +89,8 @@ void StaticNewSpaceVisitor<StaticVisitor>::Initialize() {
 
   table_.Register(kVisitJSWeakMap, &JSObjectVisitor::Visit);
 
+  table_.Register(kVisitJSWeakSet, &JSObjectVisitor::Visit);
+
   table_.Register(kVisitJSRegExp, &JSObjectVisitor::Visit);
 
   table_.template RegisterSpecializations<DataObjectVisitor,
@@ -198,7 +200,9 @@ void StaticMarkingVisitor<StaticVisitor>::Initialize() {
 
   table_.Register(kVisitSeqTwoByteString, &DataObjectVisitor::Visit);
 
-  table_.Register(kVisitJSWeakMap, &StaticVisitor::VisitJSWeakMap);
+  table_.Register(kVisitJSWeakMap, &StaticVisitor::VisitWeakCollection);
+
+  table_.Register(kVisitJSWeakSet, &StaticVisitor::VisitWeakCollection);
 
   table_.Register(kVisitOddball,
                   &FixedBodyVisitor<StaticVisitor,
index 484bb88..cd46013 100644 (file)
@@ -110,6 +110,9 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
     case JS_WEAK_MAP_TYPE:
       return kVisitJSWeakMap;
 
+    case JS_WEAK_SET_TYPE:
+      return kVisitJSWeakSet;
+
     case JS_REGEXP_TYPE:
       return kVisitJSRegExp;
 
index de5ad82..32e457b 100644 (file)
@@ -94,6 +94,7 @@ class StaticVisitorBase : public AllStatic {
   V(SharedFunctionInfo)       \
   V(JSFunction)               \
   V(JSWeakMap)                \
+  V(JSWeakSet)                \
   V(JSArrayBuffer)            \
   V(JSTypedArray)             \
   V(JSDataView)               \
index c83a40e..1967b13 100644 (file)
@@ -1343,6 +1343,10 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
       accumulator->Add("<JS WeakMap>");
       break;
     }
+    case JS_WEAK_SET_TYPE: {
+      accumulator->Add("<JS WeakSet>");
+      break;
+    }
     case JS_REGEXP_TYPE: {
       accumulator->Add("<JS RegExp>");
       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:
index 672d81c..f197b23 100644 (file)
 //           - 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
index bad089b..44ecbe9 100644 (file)
@@ -1421,69 +1421,73 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGetSize) {
 }
 
 
-static JSWeakMap* WeakMapInitialize(Isolate* isolate,
-                                    Handle<JSWeakMap> weakmap) {
-  ASSERT(weakmap->map()->inobject_properties() == 0);
+static JSWeakCollection* WeakCollectionInitialize(Isolate* isolate,
+    Handle<JSWeakCollection> weak_collection) {
+  ASSERT(weak_collection->map()->inobject_properties() == 0);
   Handle<ObjectHashTable> 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<ObjectHashTable> table(ObjectHashTable::cast(weakmap->table()));
+  Handle<ObjectHashTable> table(
+      ObjectHashTable::cast(weak_collection->table()));
   Handle<Object> 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<ObjectHashTable> table(ObjectHashTable::cast(weakmap->table()));
+  Handle<ObjectHashTable> table(
+      ObjectHashTable::cast(weak_collection->table()));
   Handle<Object> 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<ObjectHashTable> table(ObjectHashTable::cast(weakmap->table()));
+  Handle<ObjectHashTable> table(ObjectHashTable::cast(
+      weak_collection->table()));
   Handle<Object> lookup(table->Lookup(*key), isolate);
   Handle<ObjectHashTable> 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<Object> value(args[2], isolate);
-  Handle<ObjectHashTable> table(ObjectHashTable::cast(weakmap->table()));
+  Handle<ObjectHashTable> table(
+      ObjectHashTable::cast(weak_collection->table()));
   Handle<ObjectHashTable> 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<JSWeakMap> weakmap =
       Handle<JSWeakMap>::cast(isolate->factory()->NewJSObjectFromMap(map));
-  return WeakMapInitialize(isolate, weakmap);
+  return WeakCollectionInitialize(isolate, weakmap);
 }
 
 
index 7f3f97e..266ec33 100644 (file)
@@ -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) \
index ff96b5a..8bf9129 100644 (file)
@@ -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:
index 04ca041..9df5c7b 100644 (file)
         'test-utils.cc',
         'test-version.cc',
         'test-weakmaps.cc',
+        'test-weaksets.cc',
         'test-weaktypedarrays.cc'
       ],
       'conditions': [
index b8d95b3..9e8f608 100644 (file)
@@ -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 (file)
index 0000000..707f903
--- /dev/null
@@ -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<Isolate*>((*context)->GetIsolate());
+}
+
+
+static Handle<JSWeakSet> AllocateJSWeakSet(Isolate* isolate) {
+  Factory* factory = isolate->factory();
+  Heap* heap = isolate->heap();
+  Handle<Map> map = factory->NewMap(JS_WEAK_SET_TYPE, JSWeakSet::kSize);
+  Handle<JSObject> weakset_obj = factory->NewJSObjectFromMap(map);
+  Handle<JSWeakSet> 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<JSWeakSet> weakset,
+                           Handle<JSObject> key,
+                           Handle<Object> value) {
+  Handle<ObjectHashTable> table = PutIntoObjectHashTable(
+      Handle<ObjectHashTable>(ObjectHashTable::cast(weakset->table())),
+      Handle<JSObject>(JSObject::cast(*key)),
+      value);
+  weakset->set_table(*table);
+}
+
+static int NumberOfWeakCalls = 0;
+static void WeakPointerCallback(v8::Isolate* isolate,
+                                v8::Persistent<v8::Value>* handle,
+                                void* id) {
+  ASSERT(id == reinterpret_cast<void*>(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<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
+  GlobalHandles* global_handles = isolate->global_handles();
+
+  // Keep global reference to the key.
+  Handle<Object> key;
+  {
+    HandleScope scope(isolate);
+    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+    Handle<JSObject> 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>(JSObject::cast(*key)),
+                   Handle<Smi>(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<void*>(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<JSWeakSet> 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> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+    for (int i = 0; i < 32; i++) {
+      Handle<JSObject> object = factory->NewJSObjectFromMap(map);
+      PutIntoWeakSet(weakset, object, Handle<Smi>(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<JSFunction> function =
+      factory->NewFunction(factory->function_string(), factory->null_value());
+  Handle<JSObject> key = factory->NewJSObject(function);
+  Handle<JSWeakSet> 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<JSObject> 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<JSFunction> 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<JSObject> 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<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
+  for (int i = 0; i < 32; i++) {
+    PutIntoWeakSet(weakset,
+                   keys[i],
+                   Handle<Smi>(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);
+}
index cf18745..67f91a8 100644 (file)
@@ -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
index ccdc4b3..9719376 100755 (executable)
@@ -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",