Changeset 241784 in webkit


Ignore:
Timestamp:
Feb 19, 2019 4:07:28 PM (5 years ago)
Author:
Joseph Pecoraro
Message:

Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view
https://bugs.webkit.org/show_bug.cgi?id=172848
<rdar://problem/25709212>

Reviewed by Mark Lam.

JSTests:

  • typeProfiler/inheritance.js:

Rewrite the test slightly for clarity. The hoisting was confusing.

  • heapProfiler/class-names.js: Added.

(MyES5Class):
(MyES6Class):
(MyES6Subclass):
Test object types and improved class names.

  • heapProfiler/driver/driver.js:

(CheapHeapSnapshotNode):
(CheapHeapSnapshot):
(createCheapHeapSnapshot):
(HeapSnapshot):
(createHeapSnapshot):
Update snapshot parsing from version 1 to version 2.

Source/JavaScriptCore:

  • heap/HeapSnapshotBuilder.h:
  • heap/HeapSnapshotBuilder.cpp:

Update the snapshot version. Change the node's 0 | 1 internal value
to be a 32bit bit flag. This is nice in that it is both compatible
with the previous snapshot version and the same size. We can use more
flags in the future.

(JSC::HeapSnapshotBuilder::json):
In cases where the classInfo gives us "Object" check for a better
class name by checking (o).proto.constructor.name. We avoid this
check in cases where (o).hasOwnProperty("constructor") which is the
case for most Foo.prototype objects. Otherwise this would get the
name of the Foo superclass for the Foo.prototype object.

  • runtime/JSObject.cpp:

(JSC::JSObject::calculatedClassName):
Handle some possible edge cases that were not handled before, such as
a JSObject without a GlobalObject or an object which doesn't
have a default getPrototype. Try to make the code a little clearer.

Source/WebInspectorUI:

  • UserInterface/Workers/HeapSnapshot/HeapSnapshot.js:

(HeapSnapshot):
Support the new snapshot version. The only thing that changes are the
node flags, and its actually completely compatible with version 1.

(HeapSnapshot.updateCategoriesAndMetadata):
List the count of object type instances in each class category.

(HeapSnapshot.prototype.serializeNode):
Include whether or not the node is an object type.

  • UserInterface/Proxies/HeapSnapshotNodeProxy.js:

(WebInspector.HeapSnapshotNodeProxy):
(WebInspector.HeapSnapshotNodeProxy.deserialize):
Add a new Node isObjectType property based on the new data.

  • UserInterface/Views/HeapSnapshotClassDataGridNode.js:

(WebInspector.HeapSnapshotClassDataGridNode.prototype.createCellContent):

  • UserInterface/Views/HeapSnapshotClusterContentView.js:

(WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName):
If a class contains 50% or more object type instances then treat it as such
instead of defaulting to native.

  • UserInterface/Views/HeapSnapshotDataGridTree.js:

(WebInspector.HeapSnapshotInstancesDataGridTree.prototype.populateTopLevel):

  • UserInterface/Views/HeapSnapshotInstanceDataGridNode.js:

(WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCellContent):
We can be more specific than the default if the individual instance is
known to be an object type.

LayoutTests:

  • inspector/unit-tests/heap-snapshot-expected.txt:
  • inspector/unit-tests/heap-snapshot.html:

Update for the new node flag.

Location:
trunk
Files:
1 added
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r241756 r241784  
     12019-02-19  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view
     4        https://bugs.webkit.org/show_bug.cgi?id=172848
     5        <rdar://problem/25709212>
     6
     7        Reviewed by Mark Lam.
     8
     9        * typeProfiler/inheritance.js:
     10        Rewrite the test slightly for clarity. The hoisting was confusing.
     11
     12        * heapProfiler/class-names.js: Added.
     13        (MyES5Class):
     14        (MyES6Class):
     15        (MyES6Subclass):
     16        Test object types and improved class names.
     17
     18        * heapProfiler/driver/driver.js:
     19        (CheapHeapSnapshotNode):
     20        (CheapHeapSnapshot):
     21        (createCheapHeapSnapshot):
     22        (HeapSnapshot):
     23        (createHeapSnapshot):
     24        Update snapshot parsing from version 1 to version 2.
     25
    1262019-02-18  Dominik Infuehr  <dinfuehr@igalia.com>
    227
  • trunk/JSTests/heapProfiler/basic-nodes.js

    r197489 r241784  
     1var SimpleObject = $vm.SimpleObject;
     2
    13load("./driver/driver.js");
    24
  • trunk/JSTests/heapProfiler/driver/driver.js

    r217843 r241784  
    1010// Lazily creates node and edge objects off of indexes into these lists.
    1111
    12 // [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>, <4:firstEdgeIndex>];
     12// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:flags>, <4:firstEdgeIndex>];
    1313const nodeFieldCount = 5;
    1414const nodeIdOffset = 0;
    1515const nodeSizeOffset = 1;
    1616const nodeClassNameOffset = 2;
    17 const nodeInternalOffset = 3;
     17const nodeFlagsOffset = 3;
    1818const nodeFirstEdgeOffset = 4;
    1919const nodeNoEdgeValue = 0xffffffff; // UINT_MAX
     20
     21// Node Flags.
     22const internalFlagMask = (1 << 0);
     23const objectTypeMask = (1 << 1);
    2024
    2125// [<0:fromId>, <1:toId>, <2:typeTableIndex>, <3:edgeDataIndexOrEdgeNameIndex>]
     
    3640        this.size = nodes[nodeIndex + nodeSizeOffset];
    3741        this.className = snapshot.classNameFromTableIndex(nodes[nodeIndex + nodeClassNameOffset]);
    38         this.internal = nodes[nodeIndex + nodeInternalOffset] ? true : false;
     42
     43        let flags = nodes[nodeIndex + nodeFlagsOffset];
     44        this.internal = flags & internalFlagMask ? true : false;
     45        this.isObjectType = flags & objectTypeMask ? true : false;
    3946
    4047        this.outgoingEdges = [];
     
    9299            this._nodes[n++] = nodes[i++]; // size
    93100            this._nodes[n++] = nodes[i++]; // classNameTableIndex
    94             this._nodes[n++] = nodes[i++]; // internal
     101            this._nodes[n++] = nodes[i++]; // flags
    95102            this._nodes[n++] = nodeNoEdgeValue;
    96103        }
     
    155162
    156163    let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
    157     assert(version === 1, "Heap Snapshot payload should be version 1");
     164    assert(version === 2, "Heap Snapshot payload should be version 2");
    158165    assert(nodes.length, "Heap Snapshot should have nodes");
    159166    assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames");
     
    211218            let size = nodes[i++];
    212219            let classNameIndex = nodes[i++];
    213             let internal = nodes[i++];
     220            let flags = nodes[i++];
     221            let internal = flags & internalFlagMask ? true : false;
    214222
    215223            let node = new HeapSnapshotNode(id, nodeClassNames[classNameIndex], size, internal);
     
    257265
    258266    let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
    259     assert(version === 1, "Heap Snapshot payload should be version 1");
     267    assert(version === 2, "Heap Snapshot payload should be version 2");
    260268    assert(nodes.length, "Heap Snapshot should have nodes");
    261269    assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames");
  • trunk/JSTests/heapProfiler/variable-edge-types.js

    r197712 r241784  
     1var SimpleObject = $vm.SimpleObject;
     2
    13load("./driver/driver.js");
    24
  • trunk/JSTests/typeProfiler/inheritance.js

    r225129 r241784  
    55function wrapper()
    66{
     7
     8function A() { };
     9function B() { };
     10function C() { };
    711
    812var theA = new A;
     
    1721var secondB = Object.create(theB);
    1822
    19 function A() { };
    20 function B() { }; B.prototype.__proto__ = A.prototype;
    21 function C() { }; C.prototype.__proto__ = A.prototype;
     23B.prototype.__proto__ = A.prototype;
     24C.prototype.__proto__ = A.prototype;
    2225
    2326}
  • trunk/LayoutTests/ChangeLog

    r241780 r241784  
     12019-02-19  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view
     4        https://bugs.webkit.org/show_bug.cgi?id=172848
     5        <rdar://problem/25709212>
     6
     7        Reviewed by Mark Lam.
     8
     9        * inspector/unit-tests/heap-snapshot-expected.txt:
     10        * inspector/unit-tests/heap-snapshot.html:
     11        Update for the new node flag.
     12
    1132019-02-19  Ryosuke Niwa  <rniwa@webkit.org>
    214
  • trunk/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt

    r217843 r241784  
    2424PASS: Node size should match.
    2525PASS: Node internal state should match.
     26PASS: Node isObjectType state should match.
    2627PASS: Node gcRoot state should match.
    2728PASS: Node retainedSize should at least be the size.
  • trunk/LayoutTests/inspector/unit-tests/heap-snapshot.html

    r220119 r241784  
    1010    WI.TestHeapSnapshotNode = class TestHeapSnapshotNode
    1111    {
    12         constructor(identifier, className, size, internal)
     12        constructor(identifier, className, size, flags)
    1313        {
    1414            this.id = identifier;
    1515            this.className = className;
    1616            this.size = size;
    17             this.internal = internal;
     17            this.internal = flags & (1 << 0) ? true : false;
     18            this.isObjectType = flags & (1 << 1) ? true : false;
    1819            this.gcRoot = false;
    1920            this.outgoingEdges = [];
     
    6061                let size = nodes[i++];
    6162                let classNameIndex = nodes[i++];
    62                 let internal = nodes[i++];
    63 
    64                 let node = new WI.TestHeapSnapshotNode(id, nodeClassNames[classNameIndex], size, !!internal);
     63                let flags = nodes[i++];
     64
     65                let node = new WI.TestHeapSnapshotNode(id, nodeClassNames[classNameIndex], size, flags);
    6566                nodeMap.set(id, node);
    6667                processedNodes.push(node);
     
    128129            && node1.className === node2.className
    129130            && node1.internal === node2.internal
     131            && node1.isObjectType === node2.isObjectType
    130132            && node1.gcRoot === node2.gcRoot;
    131133    }
     
    190192                InspectorTest.expectThat(heapSnapshotNode.size === testSnapshotNodeForWindowObject.size, "Node size should match.");
    191193                InspectorTest.expectThat(heapSnapshotNode.internal === testSnapshotNodeForWindowObject.internal, "Node internal state should match.");
     194                InspectorTest.expectThat(heapSnapshotNode.isObjectType === testSnapshotNodeForWindowObject.isObjectType, "Node isObjectType state should match.");
    192195                InspectorTest.expectThat(heapSnapshotNode.gcRoot === testSnapshotNodeForWindowObject.gcRoot, "Node gcRoot state should match.");
    193196                InspectorTest.expectThat(heapSnapshotNode.retainedSize >= heapSnapshotNode.size, "Node retainedSize should at least be the size.");
  • trunk/Source/JavaScriptCore/ChangeLog

    r241783 r241784  
     12019-02-19  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view
     4        https://bugs.webkit.org/show_bug.cgi?id=172848
     5        <rdar://problem/25709212>
     6
     7        Reviewed by Mark Lam.
     8
     9        * heap/HeapSnapshotBuilder.h:
     10        * heap/HeapSnapshotBuilder.cpp:
     11        Update the snapshot version. Change the node's 0 | 1 internal value
     12        to be a 32bit bit flag. This is nice in that it is both compatible
     13        with the previous snapshot version and the same size. We can use more
     14        flags in the future.
     15
     16        (JSC::HeapSnapshotBuilder::json):
     17        In cases where the classInfo gives us "Object" check for a better
     18        class name by checking (o).__proto__.constructor.name. We avoid this
     19        check in cases where (o).hasOwnProperty("constructor") which is the
     20        case for most Foo.prototype objects. Otherwise this would get the
     21        name of the Foo superclass for the Foo.prototype object.
     22
     23        * runtime/JSObject.cpp:
     24        (JSC::JSObject::calculatedClassName):
     25        Handle some possible edge cases that were not handled before, such as
     26        a JSObject without a GlobalObject or an object which doesn't
     27        have a default getPrototype. Try to make the code a little clearer.
     28
    1292019-02-19  Robin Morisset  <rmorisset@apple.com>
    230
  • trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp

    r241751 r241784  
    11/*
    2  * Copyright (C) 2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2121 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    2222 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
     23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2424 */
    2525
     
    8080{
    8181    ASSERT(m_profiler.activeSnapshotBuilder() == this);
    82    
     82
    8383    ASSERT(Heap::isMarked(cell));
    8484
     
    178178//
    179179//   {
    180 //      "version": 1.0,
     180//      "version": 2,
    181181//      "type": "Inspector",
    182 //      // [<address>, <labelIndex>, <wrappedEddress>] only present in GCDebuggingSnapshot-type snapshots
     182//      // [<address>, <labelIndex>, <wrappedAddress>] only present in GCDebuggingSnapshot-type snapshots
    183183//      "nodes": [
    184 //          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>
    185 //          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>
     184//          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>
     185//          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>
    186186//          ...
    187187//      ],
     
    205205//
    206206//   {
    207 //      "version": 1.0,
     207//      "version": 2,
    208208//      "type": "GCDebugging",
    209209//      "nodes": [
    210 //          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>, <labelIndex>, <cellEddress>, <wrappedEddress>,
    211 //          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>, <labelIndex>, <cellEddress>, <wrappedEddress>,
     210//          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>, <labelIndex>, <cellEddress>, <wrappedAddress>,
     211//          <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>, <labelIndex>, <cellEddress>, <wrappedAddress>,
    212212//          ...
    213213//      ],
     
    241241//       - index into the "nodeClassNames" list.
    242242//
    243 //     <internal>
    244 //       - 0 = false, 1 = true.
     243//     <flags>
     244//       - 0b0000 - no flags
     245//       - 0b0001 - internal instance
     246//       - 0b0010 - Object subclassification
    245247//
    246248//     <edgeTypeIndex>
     
    254256//      <rootReasonIndex>
    255257//       - index into the "labels" list.
     258
     259enum class NodeFlags {
     260    Internal      = 1 << 0,
     261    ObjectSubtype = 1 << 1,
     262};
    256263
    257264static uint8_t edgeTypeToNumber(EdgeType type)
     
    359366
    360367    // Build a list of used class names.
    361     HashMap<const char*, unsigned> classNameIndexes;
    362     classNameIndexes.set("<root>", 0);
     368    HashMap<String, unsigned> classNameIndexes;
     369    classNameIndexes.set("<root>"_s, 0);
    363370    unsigned nextClassNameIndex = 1;
    364371
     
    379386            return;
    380387
     388        unsigned flags = 0;
     389
    381390        allowedNodeIdentifiers.set(node.cell, node.identifier);
    382391
    383         auto result = classNameIndexes.add(node.cell->classInfo(vm)->className, nextClassNameIndex);
     392        String className = node.cell->classInfo(vm)->className;
     393        if (node.cell->isObject() && className == JSObject::info()->className) {
     394            flags |= NodeFlags::ObjectSubtype;
     395
     396            // Skip calculating a class name if this object has a `constructor` own property.
     397            // These cases are typically F.prototype objects and we want to treat these as
     398            // "Object" in snapshots and not get the name of the prototype's parent.
     399            JSObject* object = asObject(node.cell);
     400            if (JSGlobalObject* globalObject = object->globalObject(vm)) {
     401                ExecState* exec = globalObject->globalExec();
     402                PropertySlot slot(object, PropertySlot::InternalMethodType::VMInquiry);
     403                if (!object->getOwnPropertySlot(object, exec, vm.propertyNames->constructor, slot))
     404                    className = JSObject::calculatedClassName(object);
     405            }
     406        }
     407
     408        auto result = classNameIndexes.add(className, nextClassNameIndex);
    384409        if (result.isNewEntry)
    385410            nextClassNameIndex++;
    386411        unsigned classNameIndex = result.iterator->value;
    387412
    388         bool isInternal = false;
    389413        void* wrappedAddress = 0;
    390414        unsigned labelIndex = 0;
    391415        if (!node.cell->isString()) {
    392416            Structure* structure = node.cell->structure(vm);
    393             isInternal = !structure || !structure->globalObject();
     417            if (!structure || !structure->globalObject())
     418                flags |= NodeFlags::Internal;
    394419
    395420            if (m_snapshotType == SnapshotType::GCDebuggingSnapshot) {
     
    405430                    }
    406431                }
    407                
     432
    408433                String description = descriptionForCell(node.cell);
    409434                if (description.length()) {
     
    419444                    labelIndex = result.iterator->value;
    420445                }
    421                
     446
    422447                wrappedAddress = m_wrappedObjectPointers.get(node.cell);
    423448            }
    424449        }
    425450
    426         // <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>, [<labelIndex>, <cellEddress>, <wrappedEddress>]
     451        // <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>, [<labelIndex>, <cellEddress>, <wrappedAddress>]
    427452        json.append(',');
    428453        json.appendNumber(node.identifier);
     
    432457        json.appendNumber(classNameIndex);
    433458        json.append(',');
    434         json.append(isInternal ? '1' : '0');
     459        json.appendNumber(flags);
    435460        if (m_snapshotType == SnapshotType::GCDebuggingSnapshot) {
    436461            json.append(',');
     
    480505
    481506    // version
    482     json.appendLiteral("\"version\":1");
     507    json.appendLiteral("\"version\":2");
    483508
    484509    // type
     
    507532    json.appendLiteral("\"nodeClassNames\":");
    508533    json.append('[');
    509     Vector<const char *> orderedClassNames(classNameIndexes.size());
     534    Vector<String> orderedClassNames(classNameIndexes.size());
    510535    for (auto& entry : classNameIndexes)
    511536        orderedClassNames[entry.value] = entry.key;
     
    604629        json.appendLiteral("\"roots\":");
    605630        json.append('[');
    606        
     631
    607632        HeapSnapshot* snapshot = m_profiler.mostRecentSnapshot();
    608633
     
    620645            firstNode = false;
    621646            json.appendNumber(snapshotNode.value().identifier);
    622            
     647
    623648            // Maybe we should just always encode the root names.
    624649            const char* rootName = rootTypeToString(it.value.markReason);
  • trunk/Source/JavaScriptCore/runtime/JSObject.cpp

    r240951 r241784  
    525525String JSObject::calculatedClassName(JSObject* object)
    526526{
    527     String prototypeFunctionName;
    528     auto globalObject = object->globalObject();
     527    String constructorFunctionName;
     528    auto* structure = object->structure();
     529    auto* globalObject = structure->globalObject();
    529530    VM& vm = globalObject->vm();
    530531    auto scope = DECLARE_CATCH_SCOPE(vm);
    531 
    532     ExecState* exec = globalObject->globalExec();
    533     PropertySlot slot(object->getPrototypeDirect(vm), PropertySlot::InternalMethodType::VMInquiry);
    534     PropertyName constructor(vm.propertyNames->constructor);
    535     if (object->getPropertySlot(exec, constructor, slot)) {
     532    auto* exec = globalObject->globalExec();
     533
     534    // Check for a display name of obj.constructor.
     535    // This is useful to get `Foo` for the `(class Foo).prototype` object.
     536    PropertySlot slot(object, PropertySlot::InternalMethodType::VMInquiry);
     537    if (object->getOwnPropertySlot(object, exec, vm.propertyNames->constructor, slot)) {
    536538        EXCEPTION_ASSERT(!scope.exception());
    537539        if (slot.isValue()) {
    538             JSValue constructorValue = slot.getValue(exec, constructor);
    539             if (constructorValue.isCell()) {
    540                 if (JSCell* constructorCell = constructorValue.asCell()) {
    541                     if (JSObject* ctorObject = constructorCell->getObject()) {
    542                         if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject))
    543                             prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
    544                         else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject))
    545                             prototypeFunctionName = constructorFunction->calculatedDisplayName(vm);
     540            if (JSObject* ctorObject = jsDynamicCast<JSObject*>(vm, slot.getValue(exec, vm.propertyNames->constructor))) {
     541                if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject))
     542                    constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
     543                else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject))
     544                    constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
     545            }
     546        }
     547    }
     548
     549    EXCEPTION_ASSERT(!scope.exception() || constructorFunctionName.isNull());
     550    if (UNLIKELY(scope.exception()))
     551        scope.clearException();
     552
     553    // Get the display name of obj.__proto__.constructor.
     554    // This is useful to get `Foo` for a `new Foo` object.
     555    if (constructorFunctionName.isNull()) {
     556        MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
     557        if (LIKELY(structure->classInfo()->methodTable.getPrototype == defaultGetPrototype)) {
     558            JSValue protoValue = object->getPrototypeDirect(vm);
     559            if (protoValue.isObject()) {
     560                JSObject* protoObject = asObject(protoValue);
     561                PropertySlot slot(protoValue, PropertySlot::InternalMethodType::VMInquiry);
     562                if (protoObject->getPropertySlot(exec, vm.propertyNames->constructor, slot)) {
     563                    EXCEPTION_ASSERT(!scope.exception());
     564                    if (slot.isValue()) {
     565                        if (JSObject* ctorObject = jsDynamicCast<JSObject*>(vm, slot.getValue(exec, vm.propertyNames->constructor))) {
     566                            if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject))
     567                                constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
     568                            else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject))
     569                                constructorFunctionName = constructorFunction->calculatedDisplayName(vm);
     570                        }
    546571                    }
    547572                }
     
    549574        }
    550575    }
    551     EXCEPTION_ASSERT(!scope.exception() || prototypeFunctionName.isNull());
     576
     577    EXCEPTION_ASSERT(!scope.exception() || constructorFunctionName.isNull());
    552578    if (UNLIKELY(scope.exception()))
    553579        scope.clearException();
    554580
    555     if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") {
     581    if (constructorFunctionName.isNull() || constructorFunctionName == "Object") {
    556582        String tableClassName = object->methodTable(vm)->className(object, vm);
    557583        if (!tableClassName.isNull() && tableClassName != "Object")
     
    562588            return classInfoName;
    563589
    564         if (prototypeFunctionName.isNull())
     590        if (constructorFunctionName.isNull())
    565591            return "Object"_s;
    566592    }
    567593
    568     return prototypeFunctionName;
     594    return constructorFunctionName;
    569595}
    570596
  • trunk/Source/WebInspectorUI/ChangeLog

    r241757 r241784  
     12019-02-19  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view
     4        https://bugs.webkit.org/show_bug.cgi?id=172848
     5        <rdar://problem/25709212>
     6
     7        Reviewed by Mark Lam.
     8
     9        * UserInterface/Workers/HeapSnapshot/HeapSnapshot.js:
     10        (HeapSnapshot):
     11        Support the new snapshot version. The only thing that changes are the
     12        node flags, and its actually completely compatible with version 1.
     13
     14        (HeapSnapshot.updateCategoriesAndMetadata):
     15        List the count of object type instances in each class category.
     16
     17        (HeapSnapshot.prototype.serializeNode):
     18        Include whether or not the node is an object type.
     19
     20        * UserInterface/Proxies/HeapSnapshotNodeProxy.js:
     21        (WebInspector.HeapSnapshotNodeProxy):
     22        (WebInspector.HeapSnapshotNodeProxy.deserialize):
     23        Add a new Node isObjectType property based on the new data.
     24
     25        * UserInterface/Views/HeapSnapshotClassDataGridNode.js:
     26        (WebInspector.HeapSnapshotClassDataGridNode.prototype.createCellContent):
     27        * UserInterface/Views/HeapSnapshotClusterContentView.js:
     28        (WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName):
     29        If a class contains 50% or more object type instances then treat it as such
     30        instead of defaulting to native.
     31
     32        * UserInterface/Views/HeapSnapshotDataGridTree.js:
     33        (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.populateTopLevel):
     34        * UserInterface/Views/HeapSnapshotInstanceDataGridNode.js:
     35        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCellContent):
     36        We can be more specific than the default if the individual instance is
     37        known to be an object type.
     38
    1392019-02-19  Joseph Pecoraro  <pecoraro@apple.com>
    240
  • trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js

    r220119 r241784  
    2626WI.HeapSnapshotNodeProxy = class HeapSnapshotNodeProxy
    2727{
    28     constructor(snapshotObjectId, identifier, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren)
     28    constructor(snapshotObjectId, {id, className, size, retainedSize, internal, isObjectType, gcRoot, dead, dominatorNodeIdentifier, hasChildren})
    2929    {
    3030        this._proxyObjectId = snapshotObjectId;
    3131
    32         this.id = identifier;
     32        this.id = id;
    3333        this.className = className;
    3434        this.size = size;
    3535        this.retainedSize = retainedSize;
    3636        this.internal = internal;
     37        this.isObjectType = isObjectType;
    3738        this.gcRoot = gcRoot;
    3839        this.dead = dead;
     
    4546    static deserialize(objectId, serializedNode)
    4647    {
    47         let {id, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren} = serializedNode;
    48         return new WI.HeapSnapshotNodeProxy(objectId, id, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren);
     48        return new WI.HeapSnapshotNodeProxy(objectId, serializedNode);
    4949    }
    5050
  • trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js

    r220119 r241784  
    6161
    6262        if (columnIdentifier === "className") {
    63             let {className} = this._data;
     63            const internal = false;
     64            let {className, isObjectSubcategory} = this._data;
    6465            let fragment = document.createDocumentFragment();
    6566            let iconElement = fragment.appendChild(document.createElement("img"));
    66             iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className));
     67            iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal, isObjectSubcategory));
    6768            let nameElement = fragment.appendChild(document.createElement("span"));
    6869            nameElement.classList.add("class-name");
  • trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js

    r220119 r241784  
    5959    // Static
    6060
    61     static iconStyleClassNameForClassName(className, internal)
     61    static iconStyleClassNameForClassName(className, internal, isObjectType)
    6262    {
    6363        if (internal)
    6464            return "native";
     65        if (isObjectType)
     66            return "object";
    6567
    6668        switch (className) {
  • trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js

    r241216 r241784  
    219219        let skipInternalOnlyObjects = !WI.settings.debugShowInternalObjectsInHeapSnapshot.value;
    220220
    221         for (let [className, {size, retainedSize, count, internalCount, deadCount}] of this.heapSnapshot.categories) {
     221        for (let [className, {size, retainedSize, count, internalCount, deadCount, objectCount}] of this.heapSnapshot.categories) {
     222            console.assert(count > 0);
     223
    222224            // Possibly skip internal only classes.
    223225            if (skipInternalOnlyObjects && count === internalCount)
     
    229231                continue;
    230232
    231             this.appendChild(new WI.HeapSnapshotClassDataGridNode({className, size, retainedSize, count: liveCount}, this));
     233            // If over half of the objects with this class name are Object sub-types, treat this as an Object category.
     234            // This can happen if the page has a JavaScript Class with the same name as a native class.
     235            let isObjectSubcategory = (objectCount / count) > 0.5;
     236
     237            this.appendChild(new WI.HeapSnapshotClassDataGridNode({className, size, retainedSize, isObjectSubcategory, count: liveCount}, this));
    232238        }
    233239
  • trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js

    r220119 r241784  
    147147
    148148        if (columnIdentifier === "className") {
    149             let {className, id, internal} = this._node;
     149            let {className, id, internal, isObjectType} = this._node;
    150150            let containerElement = document.createElement("span");
    151151            containerElement.addEventListener("contextmenu", this._contextMenuHandler.bind(this));
    152152
    153153            let iconElement = containerElement.appendChild(document.createElement("img"));
    154             iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal));
     154            iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal, isObjectType));
    155155
    156156            if (this._edge) {
     
    411411
    412412            let iconElement = containerElement.appendChild(document.createElement("img"));
    413             iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(node.className, node.internal));
     413            iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(node.className, node.internal, node.isObjectType));
    414414
    415415            let classNameElement = containerElement.appendChild(document.createElement("span"));
  • trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js

    r241219 r241784  
    3131
    3232// nodes
    33 // [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>]
     33// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:flags>]
    3434const nodeFieldCount = 4;
    3535const nodeIdOffset = 0;
    3636const nodeSizeOffset = 1;
    3737const nodeClassNameOffset = 2;
    38 const nodeInternalOffset = 3;
     38const nodeFlagsOffset = 3;
    3939const gcDebuggingNodeFieldCount = 7;
     40
     41// node flags
     42const internalFlagsMask = (1 << 0);
     43const objectTypeMask = (1 << 1);
    4044
    4145// edges
     
    5256const rootNodeIdentifier = 0;
    5357
     58// Version Differences:
     59//   - In Version 1, node[3] now named <flags> was the value 0 or 1 indicating not-internal or internal.
     60//   - In Version 2, this became a bitmask so multiple flags could be included without modifying the size.
     61//
    5462// Terminology:
    5563//   - `nodeIndex` is an index into the `nodes` list.
     
    8694
    8795        let {version, type, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = json;
    88         console.assert(version === 1, "Expect JavaScriptCore Heap Snapshot version 1");
     96        console.assert(version === 1 || version === 2, "Expect JavaScriptCore Heap Snapshot version 1 or 2");
    8997        console.assert(!type || (type === "Inspector" || type === "GCDebugging"), "Expect an Inspector / GCDebugging Heap Snapshot");
    9098
     
    166174            let size = nodes[nodeIndex + nodeSizeOffset];
    167175            let retainedSize = nodeOrdinalToRetainedSizes[nodeOrdinal];
    168             let internal = nodes[nodeIndex + nodeInternalOffset] ? true : false;
     176            let flags = nodes[nodeIndex + nodeFlagsOffset];
    169177            let dead = nodeOrdinalIsDead[nodeOrdinal] ? true : false;
    170178
    171179            let category = categories[className];
    172180            if (!category)
    173                 category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0};
     181                category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0, objectCount: 0};
    174182
    175183            category.size += size;
    176184            category.retainedSize += retainedSize;
    177185            category.count += 1;
    178             if (internal)
     186            if (flags & internalFlagsMask)
    179187                category.internalCount += 1;
     188            if (flags & objectTypeMask)
     189                category.objectCount += 1;
    180190            if (dead)
    181191                category.deadCount += 1;
     
    423433        let edgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal];
    424434        let hasChildren = this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier;
     435        let nodeFlags = this._nodes[nodeIndex + nodeFlagsOffset];
    425436
    426437        let dominatorNodeOrdinal = this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal];
     
    433444            size: this._nodes[nodeIndex + nodeSizeOffset],
    434445            retainedSize: this._nodeOrdinalToRetainedSizes[nodeOrdinal],
    435             internal: this._nodes[nodeIndex + nodeInternalOffset] ? true : false,
     446            internal: nodeFlags & internalFlagsMask ? true : false,
     447            isObjectType: nodeFlags & objectTypeMask ? true : false,
    436448            gcRoot: this._nodeOrdinalIsGCRoot[nodeOrdinal] ? true : false,
    437449            dead: this._nodeOrdinalIsDead[nodeOrdinal] ? true : false,
     
    760772                let fromNodeOrdinal = this._incomingNodes[incomingEdgeIndex];
    761773                let fromNodeIndex = fromNodeOrdinal * this._nodeFieldCount;
    762                 let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeInternalOffset];
     774                let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeFlagsOffset] & internalFlagsMask;
    763775                if (fromNodeIsInternal)
    764776                    continue;
  • trunk/Tools/GCHeapInspector/heap-analysis/HeapSnapshot.js

    r235271 r241784  
    3131
    3232// nodes
    33 // [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>, <4:labelIndex>, <5:address>, <6:wrapped address>]
    34 let nodeFieldCount = 7;
     33// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:flags>, <4:labelIndex>, <5:address>, <6:wrapped address>]
     34const nodeFieldCount = 7;
    3535const nodeIdOffset = 0;
    3636const nodeSizeOffset = 1;
    3737const nodeClassNameOffset = 2;
    38 const nodeInternalOffset = 3;
     38const nodeFlagsOffset = 3;
    3939const nodeLabelOffset = 4;
    4040const nodeAddressOffset = 5;
    4141const nodeWrappedAddressOffset = 6;
     42
     43// node flags
     44const internalFlagsMask = (1 << 0);
     45const objectTypeMask = (1 << 1);
    4246
    4347// edges
     
    9498
    9599        let {version, type, nodes, nodeClassNames, edges, edgeTypes, edgeNames, roots, labels} = json;
    96         console.assert(version === 1, "Expect JavaScriptCore Heap Snapshot version 1");
     100        console.assert(version === 1 || version === 2, "Expect JavaScriptCore Heap Snapshot version 1 or 2");
    97101        console.assert(type === "GCDebugging", "Expect a GCDebugging-type snapshot");
    98102
     
    200204            let size = nodes[nodeIndex + nodeSizeOffset];
    201205            let retainedSize = nodeOrdinalToRetainedSizes[nodeOrdinal];
    202             let internal = nodes[nodeIndex + nodeInternalOffset] ? true : false;
     206            let flags = nodes[nodeIndex + nodeFlagsOffset];
    203207            let dead = nodeOrdinalIsDead[nodeOrdinal] ? true : false;
    204208
    205209            let category = categories[className];
    206210            if (!category)
    207                 category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0};
     211                category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0, objectCount: 0};
    208212
    209213            category.size += size;
    210214            category.retainedSize += retainedSize;
    211215            category.count += 1;
    212             if (internal)
     216            if (flags & internalFlagsMask)
    213217                category.internalCount += 1;
     218            if (flags & objectTypeMask)
     219                category.objectCount += 1;
    214220            if (dead)
    215221                category.deadCount += 1;
     
    479485        let edgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal];
    480486        let hasChildren = this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier;
     487        let nodeFlags = this._nodes[nodeIndex + nodeFlagsOffset];
    481488
    482489        let dominatorNodeOrdinal = this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal];
     
    489496            size: this._nodes[nodeIndex + nodeSizeOffset],
    490497            retainedSize: this._nodeOrdinalToRetainedSizes[nodeOrdinal],
    491             internal: this._nodes[nodeIndex + nodeInternalOffset] ? true : false,
     498            internal: nodeFlags & internalFlagsMask ? true : false,
     499            isObjectType: nodeFlags & objectTypeMask ? true : false,
    492500            gcRoot: this._nodeOrdinalIsGCRoot[nodeOrdinal] ? true : false,
    493501            markedRoot : this._rootIdentifierToReasons.has(nodeIdentifier),
     
    830838                let fromNodeOrdinal = this._incomingNodes[incomingEdgeIndex];
    831839                let fromNodeIndex = fromNodeOrdinal * nodeFieldCount;
    832                 // let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeInternalOffset];
    833                 // if (fromNodeIsInternal)
    834                 //     continue;
    835840
    836841                let edgeIndex = this._incomingEdges[incomingEdgeIndex];
Note: See TracChangeset for help on using the changeset viewer.