From 399488071547762372020fc47446ef1a768d8e15 Mon Sep 17 00:00:00 2001 From: "yurys@chromium.org" Date: Thu, 10 Jul 2014 10:54:47 +0000 Subject: [PATCH] Support ES6 Map and Set in heap profiler Added special handling for Map and Set in the heap snapshot generator. Extracted common base type from JSMap/JSSet similar to JSWeakMap/JSWeakSet. After handling collection specific properties all collections are processed as regular JSObject to make sure all regular properties set on them are present in the heap snapshot. BUG=v8:3368 LOG=Y R=alph@chromium.org, rossberg@chromium.org Committed: https://code.google.com/p/v8/source/detail?r=22311 Review URL: https://codereview.chromium.org/373183002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22316 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/heap-snapshot-generator.cc | 20 +++++-- src/heap-snapshot-generator.h | 1 + src/objects-inl.h | 3 +- src/objects.h | 32 ++++++----- test/cctest/test-heap-profiler.cc | 93 ++++++++++++++++++++++++++++++- 5 files changed, 125 insertions(+), 24 deletions(-) diff --git a/src/heap-snapshot-generator.cc b/src/heap-snapshot-generator.cc index f39ac1db2a..6229386804 100644 --- a/src/heap-snapshot-generator.cc +++ b/src/heap-snapshot-generator.cc @@ -1097,11 +1097,16 @@ bool V8HeapExplorer::ExtractReferencesPass1(int entry, HeapObject* obj) { ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj)); } else if (obj->IsJSArrayBuffer()) { ExtractJSArrayBufferReferences(entry, JSArrayBuffer::cast(obj)); - } else if (obj->IsJSWeakSet()) { - ExtractJSWeakCollectionReferences(entry, JSWeakSet::cast(obj)); - } else if (obj->IsJSWeakMap()) { - ExtractJSWeakCollectionReferences(entry, JSWeakMap::cast(obj)); } else if (obj->IsJSObject()) { + if (obj->IsJSWeakSet()) { + ExtractJSWeakCollectionReferences(entry, JSWeakSet::cast(obj)); + } else if (obj->IsJSWeakMap()) { + ExtractJSWeakCollectionReferences(entry, JSWeakMap::cast(obj)); + } else if (obj->IsJSSet()) { + ExtractJSCollectionReferences(entry, JSSet::cast(obj)); + } else if (obj->IsJSMap()) { + ExtractJSCollectionReferences(entry, JSMap::cast(obj)); + } ExtractJSObjectReferences(entry, JSObject::cast(obj)); } else if (obj->IsString()) { ExtractStringReferences(entry, String::cast(obj)); @@ -1260,6 +1265,13 @@ void V8HeapExplorer::ExtractSymbolReferences(int entry, Symbol* symbol) { } +void V8HeapExplorer::ExtractJSCollectionReferences(int entry, + JSCollection* collection) { + SetInternalReference(collection, entry, "table", collection->table(), + JSCollection::kTableOffset); +} + + void V8HeapExplorer::ExtractJSWeakCollectionReferences( int entry, JSWeakCollection* collection) { MarkAsWeakContainer(collection->table()); diff --git a/src/heap-snapshot-generator.h b/src/heap-snapshot-generator.h index e18d70a220..329570f0e5 100644 --- a/src/heap-snapshot-generator.h +++ b/src/heap-snapshot-generator.h @@ -370,6 +370,7 @@ class V8HeapExplorer : public HeapEntriesAllocator { void ExtractJSObjectReferences(int entry, JSObject* js_obj); void ExtractStringReferences(int entry, String* obj); void ExtractSymbolReferences(int entry, Symbol* symbol); + void ExtractJSCollectionReferences(int entry, JSCollection* collection); void ExtractJSWeakCollectionReferences(int entry, JSWeakCollection* collection); void ExtractContextReferences(int entry, Context* context); diff --git a/src/objects-inl.h b/src/objects-inl.h index b841ee60f2..25a3b85cab 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -6032,8 +6032,7 @@ void JSProxy::InitializeBody(int object_size, Object* value) { } -ACCESSORS(JSSet, table, Object, kTableOffset) -ACCESSORS(JSMap, table, Object, kTableOffset) +ACCESSORS(JSCollection, table, Object, kTableOffset) #define ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(name, type, offset) \ diff --git a/src/objects.h b/src/objects.h index 007d4c10a0..8dd7cb1a85 100644 --- a/src/objects.h +++ b/src/objects.h @@ -44,8 +44,9 @@ // - JSArrayBufferView // - JSTypedArray // - JSDataView -// - JSSet -// - JSMap +// - JSCollection +// - JSSet +// - JSMap // - JSSetIterator // - JSMapIterator // - JSWeakCollection @@ -10078,41 +10079,42 @@ class JSFunctionProxy: public JSProxy { }; -// The JSSet describes EcmaScript Harmony sets -class JSSet: public JSObject { +class JSCollection : public JSObject { public: - // [set]: the backing hash set containing keys. + // [table]: the backing hash table DECL_ACCESSORS(table, Object) + static const int kTableOffset = JSObject::kHeaderSize; + static const int kSize = kTableOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSCollection); +}; + + +// The JSSet describes EcmaScript Harmony sets +class JSSet : public JSCollection { + public: DECLARE_CAST(JSSet) // Dispatched behavior. DECLARE_PRINTER(JSSet) DECLARE_VERIFIER(JSSet) - static const int kTableOffset = JSObject::kHeaderSize; - static const int kSize = kTableOffset + kPointerSize; - private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSSet); }; // The JSMap describes EcmaScript Harmony maps -class JSMap: public JSObject { +class JSMap : public JSCollection { public: - // [table]: the backing hash table mapping keys to values. - DECL_ACCESSORS(table, Object) - DECLARE_CAST(JSMap) // Dispatched behavior. DECLARE_PRINTER(JSMap) DECLARE_VERIFIER(JSMap) - static const int kTableOffset = JSObject::kHeaderSize; - static const int kSize = kTableOffset + kPointerSize; - private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSMap); }; diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc index db6b9da2a1..33528fbb03 100644 --- a/test/cctest/test-heap-profiler.cc +++ b/test/cctest/test-heap-profiler.cc @@ -502,9 +502,10 @@ TEST(HeapSnapshotWeakCollection) { v8::HandleScope scope(env->GetIsolate()); v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); - CompileRun("k = {}; v = {};\n" - "ws = new WeakSet(); ws.add(k); ws.add(v);\n" - "wm = new WeakMap(); wm.set(k, v);\n"); + CompileRun( + "k = {}; v = {}; s = 'str';\n" + "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n" + "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n"); const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(v8_str("WeakCollections")); CHECK(ValidateSnapshot(snapshot)); @@ -515,6 +516,9 @@ TEST(HeapSnapshotWeakCollection) { const v8::HeapGraphNode* v = GetProperty(global, v8::HeapGraphEdge::kProperty, "v"); CHECK_NE(NULL, v); + const v8::HeapGraphNode* s = + GetProperty(global, v8::HeapGraphEdge::kProperty, "s"); + CHECK_NE(NULL, s); const v8::HeapGraphNode* ws = GetProperty(global, v8::HeapGraphEdge::kProperty, "ws"); @@ -535,6 +539,10 @@ TEST(HeapSnapshotWeakCollection) { } } CHECK_EQ(1, weak_entries); + const v8::HeapGraphNode* ws_s = + GetProperty(ws, v8::HeapGraphEdge::kProperty, "str"); + CHECK_NE(NULL, ws_s); + CHECK_EQ(static_cast(s->GetId()), static_cast(ws_s->GetId())); const v8::HeapGraphNode* wm = GetProperty(global, v8::HeapGraphEdge::kProperty, "wm"); @@ -556,6 +564,85 @@ TEST(HeapSnapshotWeakCollection) { } } CHECK_EQ(2, weak_entries); + const v8::HeapGraphNode* wm_s = + GetProperty(wm, v8::HeapGraphEdge::kProperty, "str"); + CHECK_NE(NULL, wm_s); + CHECK_EQ(static_cast(s->GetId()), static_cast(wm_s->GetId())); +} + + +TEST(HeapSnapshotCollection) { + i::FLAG_harmony_collections = true; + + LocalContext env; + v8::HandleScope scope(env->GetIsolate()); + v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); + + CompileRun( + "k = {}; v = {}; s = 'str';\n" + "set = new Set(); set.add(k); set.add(v); set[s] = s;\n" + "map = new Map(); map.set(k, v); map[s] = s;\n"); + const v8::HeapSnapshot* snapshot = + heap_profiler->TakeHeapSnapshot(v8_str("Collections")); + CHECK(ValidateSnapshot(snapshot)); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + const v8::HeapGraphNode* k = + GetProperty(global, v8::HeapGraphEdge::kProperty, "k"); + CHECK_NE(NULL, k); + const v8::HeapGraphNode* v = + GetProperty(global, v8::HeapGraphEdge::kProperty, "v"); + CHECK_NE(NULL, v); + const v8::HeapGraphNode* s = + GetProperty(global, v8::HeapGraphEdge::kProperty, "s"); + CHECK_NE(NULL, s); + + const v8::HeapGraphNode* set = + GetProperty(global, v8::HeapGraphEdge::kProperty, "set"); + CHECK_NE(NULL, set); + CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType()); + CHECK_EQ(v8_str("Set"), set->GetName()); + + const v8::HeapGraphNode* set_table = + GetProperty(set, v8::HeapGraphEdge::kInternal, "table"); + CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType()); + CHECK_GT(set_table->GetChildrenCount(), 0); + int entries = 0; + for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = set_table->GetChild(i); + const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId(); + if (to_node_id == k->GetId() || to_node_id == v->GetId()) { + ++entries; + } + } + CHECK_EQ(2, entries); + const v8::HeapGraphNode* set_s = + GetProperty(set, v8::HeapGraphEdge::kProperty, "str"); + CHECK_NE(NULL, set_s); + CHECK_EQ(static_cast(s->GetId()), static_cast(set_s->GetId())); + + const v8::HeapGraphNode* map = + GetProperty(global, v8::HeapGraphEdge::kProperty, "map"); + CHECK_NE(NULL, map); + CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType()); + CHECK_EQ(v8_str("Map"), map->GetName()); + + const v8::HeapGraphNode* map_table = + GetProperty(map, v8::HeapGraphEdge::kInternal, "table"); + CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType()); + CHECK_GT(map_table->GetChildrenCount(), 0); + entries = 0; + for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = map_table->GetChild(i); + const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId(); + if (to_node_id == k->GetId() || to_node_id == v->GetId()) { + ++entries; + } + } + CHECK_EQ(2, entries); + const v8::HeapGraphNode* map_s = + GetProperty(map, v8::HeapGraphEdge::kProperty, "str"); + CHECK_NE(NULL, map_s); + CHECK_EQ(static_cast(s->GetId()), static_cast(map_s->GetId())); }