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 <arv@chromium.org>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15792 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
44f576ba65
commit
ce81b0d3a8
@ -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) {
|
||||
|
@ -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();
|
||||
|
10
src/heap.cc
10
src/heap.cc
@ -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());
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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) {}
|
||||
|
@ -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');
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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"],
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
@ -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() {
|
||||
|
@ -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()));
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -94,6 +94,7 @@ class StaticVisitorBase : public AllStatic {
|
||||
V(SharedFunctionInfo) \
|
||||
V(JSFunction) \
|
||||
V(JSWeakMap) \
|
||||
V(JSWeakSet) \
|
||||
V(JSArrayBuffer) \
|
||||
V(JSTypedArray) \
|
||||
V(JSDataView) \
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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:
|
||||
|
@ -104,6 +104,7 @@
|
||||
'test-utils.cc',
|
||||
'test-version.cc',
|
||||
'test-weakmaps.cc',
|
||||
'test-weaksets.cc',
|
||||
'test-weaktypedarrays.cc'
|
||||
],
|
||||
'conditions': [
|
||||
|
@ -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
|
||||
|
250
test/cctest/test-weaksets.cc
Normal file
250
test/cctest/test-weaksets.cc
Normal file
@ -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);
|
||||
}
|
@ -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 constructor property of the Set, Map and 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, 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));
|
||||
})();
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user