Web Inspector: Modify the type profiler runtime protocol to transfer some computation...
authorsaambarati1@gmail.com <saambarati1@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Sep 2014 02:37:57 +0000 (02:37 +0000)
committersaambarati1@gmail.com <saambarati1@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Sep 2014 02:37:57 +0000 (02:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=136500

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

This patch changes the type profiler protocol to the Web Inspector
by moving the work of calculating computed properties that effect the UI
into the Web Inspector. This makes the Web Inspector have control over the
strings it displays as UI elements representing type information to the user
instead of JavaScriptCore deciding on a convention for these strings.
JavaScriptCore now sends enough information to the Web Inspector so that
it can compute the properties JavaScriptCore used to compute.

* inspector/agents/InspectorRuntimeAgent.cpp:
(Inspector::InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets):
* inspector/protocol/Runtime.json:
* runtime/TypeProfiler.cpp:
(JSC::TypeProfiler::getTypesForVariableAtOffsetForInspector): Deleted.
* runtime/TypeProfiler.h:
* runtime/TypeSet.cpp:
(JSC::TypeSet::inspectorTypeSet):
(JSC::StructureShape::leastCommonAncestor):
(JSC::StructureShape::inspectorRepresentation):
* runtime/TypeSet.h:

Source/WebInspectorUI:

This patch maintains the same UI in the Web Inspector for displaying
type information, but now accounts for the new protocol for type profiling.

The Inspector now has a TypeSet class that wraps the TypeDescription
protocol object and creates a simple interface for asking protocol objects
about their type.

* UserInterface/Controllers/TypeTokenAnnotator.js:
(WebInspector.TypeTokenAnnotator.prototype._insertTypeTokensForEachNode):
* UserInterface/Main.html:
* UserInterface/Models/TypeSet.js: Added.
(WebInspector.TypeSet):
(WebInspector.TypeSet.fromPayload):
(WebInspector.TypeSet.prototype.isContainedIn):
* UserInterface/Views/SourceCodeTextEditor.js:
(WebInspector.SourceCodeTextEditor.prototype._tokenTrackingControllerHighlightedJavaScriptTypeInformation.handler):
(WebInspector.SourceCodeTextEditor.prototype._tokenTrackingControllerHighlightedJavaScriptTypeInformation):
* UserInterface/Views/TypePropertiesSection.js:
(WebInspector.TypePropertiesSection.prototype.onpopulate):
(WebInspector.TypePropertyTreeElement.prototype.onpopulate):
* UserInterface/Views/TypeTokenView.js:
(WebInspector.TypeTokenView.prototype.update):
(WebInspector.TypeTokenView.prototype._shouldShowPopover):
(WebInspector.TypeTokenView.prototype._displayTypeName):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@173505 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
Source/JavaScriptCore/inspector/protocol/Runtime.json
Source/JavaScriptCore/runtime/TypeProfiler.cpp
Source/JavaScriptCore/runtime/TypeProfiler.h
Source/JavaScriptCore/runtime/TypeSet.cpp
Source/JavaScriptCore/runtime/TypeSet.h
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Controllers/TypeTokenAnnotator.js
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/TypeSet.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/SourceCodeTextEditor.js
Source/WebInspectorUI/UserInterface/Views/TypePropertiesSection.js
Source/WebInspectorUI/UserInterface/Views/TypeTokenView.js

index 3f937d2..405cce5 100644 (file)
@@ -1,3 +1,30 @@
+2014-09-10  Saam Barati  <saambarati1@gmail.com>
+
+        Web Inspector: Modify the type profiler runtime protocol to transfer some computation into the WebInspector
+        https://bugs.webkit.org/show_bug.cgi?id=136500
+
+        Reviewed by Joseph Pecoraro.
+
+        This patch changes the type profiler protocol to the Web Inspector
+        by moving the work of calculating computed properties that effect the UI 
+        into the Web Inspector. This makes the Web Inspector have control over the 
+        strings it displays as UI elements representing type information to the user 
+        instead of JavaScriptCore deciding on a convention for these strings.
+        JavaScriptCore now sends enough information to the Web Inspector so that 
+        it can compute the properties JavaScriptCore used to compute.
+
+        * inspector/agents/InspectorRuntimeAgent.cpp:
+        (Inspector::InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets):
+        * inspector/protocol/Runtime.json:
+        * runtime/TypeProfiler.cpp:
+        (JSC::TypeProfiler::getTypesForVariableAtOffsetForInspector): Deleted.
+        * runtime/TypeProfiler.h:
+        * runtime/TypeSet.cpp:
+        (JSC::TypeSet::inspectorTypeSet):
+        (JSC::StructureShape::leastCommonAncestor):
+        (JSC::StructureShape::inspectorRepresentation):
+        * runtime/TypeSet.h:
+
 2014-09-10  Akos Kiss  <akiss@inf.u-szeged.hu>
 
         Apply ARM64-specific lowering to load/store instructions in offlineasm
index b76f60d..7c3bae2 100644 (file)
@@ -225,10 +225,25 @@ void InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets(ErrorString* er
         location->getString(ASCIILiteral("sourceID"), &sourceIDAsString);
         location->getNumber(ASCIILiteral("divot"), &divot);
 
-        RefPtr<Inspector::Protocol::Runtime::TypeDescription> typeDescription = Inspector::Protocol::Runtime::TypeDescription::create();
         bool okay;
-        vm.typeProfiler()->getTypesForVariableAtOffsetForInspector(static_cast<TypeProfilerSearchDescriptor>(descriptor), divot, sourceIDAsString.toIntPtrStrict(&okay), typeDescription);
-        typeDescriptions->addItem(typeDescription);
+        TypeLocation* typeLocation = vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor));
+        RefPtr<Inspector::Protocol::Runtime::TypeDescription> description = Inspector::Protocol::Runtime::TypeDescription::create()
+            .setIsValid(!!typeLocation);
+        if (typeLocation) {
+            RefPtr<TypeSet> typeSet;
+            if (typeLocation->m_globalTypeSet && typeLocation->m_globalVariableID != TypeProfilerNoGlobalIDExists)
+                typeSet = typeLocation->m_globalTypeSet;
+            else
+                typeSet = typeLocation->m_instructionTypeSet;
+
+            description->setLeastCommonAncestor(typeSet->leastCommonAncestor());
+            description->setPrimitiveTypeNames(typeSet->allPrimitiveTypeNames());
+            description->setStructures(typeSet->allStructureRepresentations());
+            description->setTypeSet(typeSet->inspectorTypeSet());
+            description->setIsTruncated(typeSet->isOverflown());
+        }
+
+        typeDescriptions->addItem(description);
     }
 
     double end = currentTimeMS();
index 9380d0c..0bbe71f 100644 (file)
             "type": "object",
             "properties": [
                 { "name": "fields", "type": "array",  "items": { "type": "string" }, "optional": true, "description": "Array of strings, where the strings represent object properties." },
+                { "name": "optionalFields", "type": "array",  "items": { "type": "string" }, "optional": true, "description": "Array of strings, where the strings represent optional object properties." },
                 { "name": "constructorName", "type": "string", "optional": true, "description": "Name of the constructor." },
-                { "name": "prototypeStructure", "$ref": "StructureDescription", "optional": true, "description": "Pointer to the StructureRepresentation of the protoype if one exists." }
+                { "name": "prototypeStructure", "$ref": "StructureDescription", "optional": true, "description": "Pointer to the StructureRepresentation of the protoype if one exists." },
+                { "name": "isImprecise", "type": "boolean", "optional": true, "description": "If true, it indicates that the fields in this StructureDescription may be inaccurate. I.e, there might have been fields that have been deleted before it was profiled or it has fields we haven't profiled." }
+            ]
+        },
+        {
+            "id": "TypeSet",
+            "type": "object",
+            "properties": [
+                { "name": "isFunction", "type": "boolean", "description": "Indicates if this type description has been type Function." },
+                { "name": "isUndefined", "type": "boolean", "description": "Indicates if this type description has been type Undefined." },
+                { "name": "isNull", "type": "boolean", "description": "Indicates if this type description has been type Null." },
+                { "name": "isBoolean", "type": "boolean", "description": "Indicates if this type description has been type Boolean." },
+                { "name": "isInteger", "type": "boolean", "description": "Indicates if this type description has been type Integer." },
+                { "name": "isNumber", "type": "boolean", "description": "Indicates if this type description has been type Number." },
+                { "name": "isString", "type": "boolean", "description": "Indicates if this type description has been type String." },
+                { "name": "isObject", "type": "boolean", "description": "Indicates if this type description has been type Object." }
             ]
         },
         {
             "type": "object",
             "description": "Container for type information that has been gathered.",
             "properties": [
-                { "name": "displayTypeName", "type": "string", "optional": true, "description": "What the inspector should display as a simple type." },
-                { "name": "localPrimitiveTypeNames", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Array of type names for primtive types (int, string, etc) seen at an instruction" },
-                { "name": "localObjectTypeNames", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Array of type names for all object seen at an instruction" },
-                { "name": "localStructures", "type": "array", "items": { "$ref": "StructureDescription" }, "optional": true, "description": "Array of descriptions for all structures seen at this this instruction." },
-                { "name": "globalPrimitiveTypeNames", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Array of type names for all primitive types seen globally." },
-                { "name": "globalObjectTypeNames", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Array of type names for all primitive types seen globally." },
-                { "name": "globalStructures", "type": "array", "items": { "$ref": "StructureDescription" }, "optional": true, "description": "Array of descriptions for all structures seen for this variable." }
+                { "name": "isValid", "type": "boolean", "description": "If true, we were able to correlate the offset successfuly with a program location. If false, the offset may be bogus or the offset may be from a CodeBlock that hasn't executed." },
+                { "name": "leastCommonAncestor", "type": "string", "optional": true, "description": "Least common ancestor of all Constructors if the TypeDescription has seen any structures. This string is the display name of the shared constructor function." },
+                { "name": "typeSet", "$ref": "TypeSet", "optional": true, "description": "Set of booleans for determining the aggregate type of this type description." },
+                { "name": "primitiveTypeNames", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Array of type names for primitive types (int, string, etc) seen at an instruction." },
+                { "name": "structures", "type": "array", "items": { "$ref": "StructureDescription" }, "optional": true, "description": "Array of descriptions for all structures seen for this variable." },
+                { "name": "isTruncated", "type": "boolean", "optional": true, "description": "If true, this indicates that no more structures are being profiled because some maximum threshold has been reached and profiling has stopped because of memory pressure." }
             ]
         },
         {
index 1ca3674..421abaf 100644 (file)
@@ -65,23 +65,6 @@ void TypeProfiler::insertNewLocation(TypeLocation* location)
     bucket.append(location);
 }
 
-void TypeProfiler::getTypesForVariableAtOffsetForInspector(TypeProfilerSearchDescriptor descriptor, unsigned divot, intptr_t sourceID, RefPtr<Inspector::Protocol::Runtime::TypeDescription>& description)
-{
-    TypeLocation* location = findLocation(divot, sourceID, descriptor);
-    if (!location)
-        return;
-
-    if (location->m_globalTypeSet && location->m_globalVariableID != TypeProfilerNoGlobalIDExists) {
-        description->setDisplayTypeName(location->m_globalTypeSet->displayName());
-        description->setGlobalPrimitiveTypeNames(location->m_globalTypeSet->allPrimitiveTypeNames());
-        description->setGlobalStructures(location->m_globalTypeSet->allStructureRepresentations());
-    } else
-        description->setDisplayTypeName(location->m_instructionTypeSet->displayName());
-
-    description->setLocalPrimitiveTypeNames(location->m_instructionTypeSet->allPrimitiveTypeNames());
-    description->setLocalStructures(location->m_instructionTypeSet->allStructureRepresentations());
-}
-
 String TypeProfiler::typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor descriptor, unsigned offset, intptr_t sourceID)
 {
     // This returns a JSON string representing an Object with the following properties:
index 478394d..db1c3e5 100644 (file)
@@ -95,14 +95,13 @@ enum TypeProfilerSearchDescriptor {
 class TypeProfiler {
 public:
     void logTypesForTypeLocation(TypeLocation*);
-    void getTypesForVariableAtOffsetForInspector(TypeProfilerSearchDescriptor, unsigned divot, intptr_t sourceID, RefPtr<Inspector::Protocol::Runtime::TypeDescription>&);
     JS_EXPORT_PRIVATE String typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor, unsigned offset, intptr_t sourceID);
     void insertNewLocation(TypeLocation*);
     FunctionHasExecutedCache* functionHasExecutedCache() { return &m_functionHasExecutedCache; }
     TypeLocationCache* typeLocationCache() { return &m_typeLocationCache; }
+    TypeLocation* findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor);
     
 private:
-    TypeLocation* findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor);
     typedef HashMap<intptr_t, Vector<TypeLocation*>> SourceIDToLocationBucketMap;
     SourceIDToLocationBucketMap m_bucketMap;
     FunctionHasExecutedCache m_functionHasExecutedCache;
index 5adb2ca..45bf689 100644 (file)
@@ -265,6 +265,20 @@ String TypeSet::leastCommonAncestor() const
     return StructureShape::leastCommonAncestor(m_structureHistory);
 }
 
+PassRefPtr<Inspector::Protocol::Runtime::TypeSet> TypeSet::inspectorTypeSet() const
+{
+    return Inspector::Protocol::Runtime::TypeSet::create()
+        .setIsFunction(doesTypeConformTo(TypeFunction))
+        .setIsUndefined(doesTypeConformTo(TypeUndefined))
+        .setIsNull(doesTypeConformTo(TypeNull))
+        .setIsBoolean(doesTypeConformTo(TypeBoolean))
+        .setIsInteger(doesTypeConformTo(TypeMachineInt))
+        .setIsNumber(doesTypeConformTo(TypeNumber))
+        .setIsString(doesTypeConformTo(TypeString))
+        .setIsObject(doesTypeConformTo(TypeObject))
+        .release();
+}
+
 String TypeSet::toJSONString() const
 {
     // This returns a JSON string representing an Object with the following properties:
@@ -392,7 +406,7 @@ String StructureShape::propertyHash()
 String StructureShape::leastCommonAncestor(const Vector<RefPtr<StructureShape>> shapes)
 {
     if (!shapes.size())
-        return "";
+        return emptyString();
 
     RefPtr<StructureShape> origin = shapes.at(0);
     for (size_t i = 1; i < shapes.size(); i++) {
@@ -526,13 +540,16 @@ PassRefPtr<Inspector::Protocol::Runtime::StructureDescription> StructureShape::i
 
     while (currentShape) {
         auto fields = Inspector::Protocol::Array<String>::create();
+        auto optionalFields = Inspector::Protocol::Array<String>::create();
         for (auto field : currentShape->m_fields)
             fields->addItem(field.get());
         for (auto field : currentShape->m_optionalFields)
-            fields->addItem(field.get() + String("?"));
+            optionalFields->addItem(field.get());
 
         currentObject->setFields(fields);
+        currentObject->setOptionalFields(optionalFields);
         currentObject->setConstructorName(currentShape->m_constructorName);
+        currentObject->setIsImprecise(currentShape->m_isInDictionaryMode);
 
         if (currentShape->m_proto) {
             RefPtr<Inspector::Protocol::Runtime::StructureDescription> nextObject = Inspector::Protocol::Runtime::StructureDescription::create();
index 849a1c7..14dd9d7 100644 (file)
@@ -38,6 +38,7 @@ template<typename T> class Array;
 
 namespace Runtime {
 class StructureDescription;
+class TypeSet;
 }
 
 }
@@ -105,9 +106,10 @@ public:
     PassRefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::StructureDescription>> allStructureRepresentations() const;
     String toJSONString() const;
     bool isOverflown() const { return m_isOverflown; }
+    String leastCommonAncestor() const;
+    PassRefPtr<Inspector::Protocol::Runtime::TypeSet> inspectorTypeSet() const;
 
 private:
-    String leastCommonAncestor() const;
     void dumpSeenTypes();
     bool doesTypeConformTo(uint32_t test) const;
 
index adaeadc..f220dbd 100644 (file)
@@ -1,3 +1,35 @@
+2014-09-10  Saam Barati  <saambarati1@gmail.com>
+
+        Web Inspector: Modify the type profiler runtime protocol to transfer some computation into the WebInspector
+        https://bugs.webkit.org/show_bug.cgi?id=136500
+
+        Reviewed by Joseph Pecoraro.
+
+        This patch maintains the same UI in the Web Inspector for displaying
+        type information, but now accounts for the new protocol for type profiling.
+        
+        The Inspector now has a TypeSet class that wraps the TypeDescription 
+        protocol object and creates a simple interface for asking protocol objects 
+        about their type.
+
+        * UserInterface/Controllers/TypeTokenAnnotator.js:
+        (WebInspector.TypeTokenAnnotator.prototype._insertTypeTokensForEachNode):
+        * UserInterface/Main.html:
+        * UserInterface/Models/TypeSet.js: Added.
+        (WebInspector.TypeSet):
+        (WebInspector.TypeSet.fromPayload):
+        (WebInspector.TypeSet.prototype.isContainedIn):
+        * UserInterface/Views/SourceCodeTextEditor.js:
+        (WebInspector.SourceCodeTextEditor.prototype._tokenTrackingControllerHighlightedJavaScriptTypeInformation.handler):
+        (WebInspector.SourceCodeTextEditor.prototype._tokenTrackingControllerHighlightedJavaScriptTypeInformation):
+        * UserInterface/Views/TypePropertiesSection.js:
+        (WebInspector.TypePropertiesSection.prototype.onpopulate):
+        (WebInspector.TypePropertyTreeElement.prototype.onpopulate):
+        * UserInterface/Views/TypeTokenView.js:
+        (WebInspector.TypeTokenView.prototype.update):
+        (WebInspector.TypeTokenView.prototype._shouldShowPopover):
+        (WebInspector.TypeTokenView.prototype._displayTypeName):
+
 2014-09-10  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Eliminate typeof "undefined" checks
index 5ede3dc..a5b0862 100644 (file)
@@ -150,23 +150,23 @@ WebInspector.TypeTokenAnnotator.prototype = {
         case WebInspector.ScriptSyntaxTree.NodeType.FunctionDeclaration:
         case WebInspector.ScriptSyntaxTree.NodeType.FunctionExpression:
             for (var param of node.params) {
-                if (!param.attachments.__typeToken && param.attachments.types && param.attachments.types.displayTypeName)
+                if (!param.attachments.__typeToken && param.attachments.types && param.attachments.types.isValid)
                     this._insertToken(param.range[0], param, false, WebInspector.TypeTokenView.TitleType.Variable, param.name);
             }
 
             // If a function does not have an explicit return type, then don't show a return type unless we think it's a constructor.
             var functionReturnType = node.attachments.returnTypes;
-            if (node.attachments.__typeToken || !functionReturnType || !functionReturnType.displayTypeName)
+            if (node.attachments.__typeToken || !functionReturnType || !functionReturnType.isValid)
                 break;
 
-            if (scriptSyntaxTree.containsNonEmptyReturnStatement(node.body) || functionReturnType.displayTypeName !== "Undefined") {
+            if (scriptSyntaxTree.containsNonEmptyReturnStatement(node.body) || !WebInspector.TypeSet.fromPayload(functionReturnType).isContainedIn(WebInspector.TypeSet.TypeBit.Undefined)) {
                 var functionName = node.id ? node.id.name : null;
                 this._insertToken(node.isGetterOrSetter ? node.getterOrSetterRange[0] : node.range[0], node,
                     true, WebInspector.TypeTokenView.TitleType.ReturnStatement, functionName);
             }
             break;
         case WebInspector.ScriptSyntaxTree.NodeType.VariableDeclarator:
-            if (!node.attachments.__typeToken && node.attachments.types && node.attachments.types.displayTypeName)
+            if (!node.attachments.__typeToken && node.attachments.types && node.attachments.types.isValid)
                 this._insertToken(node.id.range[0], node, false, WebInspector.TypeTokenView.TitleType.Variable, node.id.name);
 
             break;
index d894aa1..9db29e4 100644 (file)
     <script src="Models/TextRange.js"></script>
     <script src="Models/TimelineMarker.js"></script>
     <script src="Models/TimelineRecording.js"></script>
+    <script src="Models/TypeSet.js"></script>
     <script src="Models/UnitBezier.js"></script>
 
     <script src="Views/ConsoleMessage.js"></script>
diff --git a/Source/WebInspectorUI/UserInterface/Models/TypeSet.js b/Source/WebInspectorUI/UserInterface/Models/TypeSet.js
new file mode 100644 (file)
index 0000000..d7fd885
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) Saam Barati.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+WebInspector.TypeSet = function(runtimeTypeDescriptionPayload)
+{
+    this._types = runtimeTypeDescriptionPayload;
+
+    var typeSet = this._types.typeSet;
+    var bitString = 0x0;
+    if (typeSet.isFunction)
+        bitString |= WebInspector.TypeSet.TypeBit.Function;
+    if (typeSet.isUndefined)
+        bitString |= WebInspector.TypeSet.TypeBit.Undefined;
+    if (typeSet.isNull)
+        bitString |= WebInspector.TypeSet.TypeBit.Null;
+    if (typeSet.isBoolean)
+        bitString |= WebInspector.TypeSet.TypeBit.Boolean;
+    if (typeSet.isInteger)
+        bitString |= WebInspector.TypeSet.TypeBit.Integer;
+    if (typeSet.isNumber)
+        bitString |= WebInspector.TypeSet.TypeBit.Number;
+    if (typeSet.isString)
+        bitString |= WebInspector.TypeSet.TypeBit.String;
+    if (typeSet.isObject)
+        bitString |= WebInspector.TypeSet.TypeBit.Object;
+
+    this._bitString = bitString;
+};
+
+WebInspector.TypeSet.fromPayload = function(payload)
+{
+    return new WebInspector.TypeSet(payload);
+};
+
+WebInspector.TypeSet.TypeBit = {
+    "Function"    :  0x1,
+    "Undefined"   :  0x2,
+    "Null"        :  0x4,
+    "Boolean"     :  0x8,
+    "Integer"     :  0x10,
+    "Number"      :  0x20,
+    "String"      :  0x40,
+    "Object"      :  0x80
+};
+
+WebInspector.TypeSet.NullOrUndefinedTypeBits = WebInspector.TypeSet.TypeBit.Null | WebInspector.TypeSet.TypeBit.Undefined;
+
+WebInspector.TypeSet.prototype = {
+    constructor: WebInspector.TypeSet,
+    __proto__: WebInspector.Object.prototype,
+
+    isContainedIn: function(test)
+    {
+        // This function checks if types in bitString are contained in the types described by the 'test' bitstring. (i.e we haven't seen more types than 'test').
+        // We have seen fewer or equal number of types as 'test' if ANDing bitString with test doesn't zero out any of our bits.
+
+        // For example:
+
+        // 0b0110 (bitString)
+        // 0b1111 (test)
+        // ------ (AND)
+        // 0b0110 == bitString
+
+        // 0b0110 (bitString)
+        // 0b0010 (test)
+        // ------ (AND)
+        // 0b0010 != bitString
+
+        return (this._bitString & test) === this._bitString;
+    }
+};
index 16c98eb..5f10fd7 100644 (file)
@@ -1240,7 +1240,7 @@ WebInspector.SourceCodeTextEditor.prototype = {
             if (!allTypes.length)
                 return;
             var types = allTypes[0];
-            if (types.displayTypeName) {
+            if (types.isValid) {
                 var popoverTitle = WebInspector.TypeTokenView.titleForPopover(WebInspector.TypeTokenView.TitleType.Variable, candidate.expression);
                 this.showPopoverForTypes(types, null, popoverTitle);
             }
index a4880cd..cd7ada5 100644 (file)
@@ -39,8 +39,8 @@ WebInspector.TypePropertiesSection.prototype = {
     {
         this.propertiesTreeOutline.removeChildren();
 
-        var primitiveTypeNames = this.types.globalPrimitiveTypeNames || this.types.localPrimitiveTypeNames;
-        var structures = this.types.globalStructures || this.types.localStructures;
+        var primitiveTypeNames = this.types.primitiveTypeNames;
+        var structures = this.types.structures;
         var properties = [];
         for (var struct of structures) {
             properties.push({
@@ -56,6 +56,10 @@ WebInspector.TypePropertiesSection.prototype = {
         }
 
         properties.sort(WebInspector.TypePropertiesSection.PropertyComparator);
+
+        if (this.types.isTruncated)
+            properties.push({name: "\u2026", structure: null});
+
         for (var property of properties)
             this.propertiesTreeOutline.appendChild(new WebInspector.TypePropertyTreeElement(property));
 
@@ -152,6 +156,22 @@ WebInspector.TypePropertyTreeElement.prototype = {
 
         properties.sort(WebInspector.TypePropertiesSection.PropertyComparator);
 
+        for (var property of properties)
+            this.appendChild(new WebInspector.TypePropertyTreeElement(property));
+
+        properties = [];
+        for (var fieldName of this.property.structure.optionalFields) {
+            properties.push({
+                name: fieldName + "?",
+                structure: null
+            });
+        }
+
+        properties.sort(WebInspector.TypePropertiesSection.PropertyComparator);
+
+        if (this.property.structure.isImprecise)
+            properties.push({name: "\u2026", structure: null});
+
         if (this.property.structure.prototypeStructure) {
             properties.push({
                 name: this.property.structure.prototypeStructure.constructorName + " (" + WebInspector.UIString("Prototype") + ")",
index 3496e46..147db6e 100644 (file)
@@ -66,7 +66,6 @@ WebInspector.TypeTokenView.TitleType = {
     ReturnStatement: "title-type-return-statement"
 };
 
-// These type strings should be kept in sync with type information in JavaScriptCore/runtime/TypeSet.cpp
 WebInspector.TypeTokenView.ColorClassForType = {
     "String": "type-token-string",
     "Function": "type-token-function",
@@ -89,9 +88,10 @@ WebInspector.TypeTokenView.prototype = {
 
     update: function(types)
     {
-        var title = types.displayTypeName;
-        this.element.textContent = title;
         this._types = types;
+
+        var title = this._displayTypeName();
+        this.element.textContent = title;
         var hashString = title[title.length - 1] === "?" ? title.slice(0, title.length - 1) : title;
 
         if (this._colorClass)
@@ -129,12 +129,65 @@ WebInspector.TypeTokenView.prototype = {
 
     _shouldShowPopover: function()
     {
-        if ((this._types.globalPrimitiveTypeNames && this._types.globalPrimitiveTypeNames.length > 1) || (this._types.localPrimitiveTypeNames && this._types.localPrimitiveTypeNames.length > 1))
+        if (this._types.primitiveTypeNames && this._types.primitiveTypeNames.length > 1)
             return true;
 
-        if ((this._types.globalStructures && this._types.globalStructures.length) || (this._types.localStructures && this._types.localStructures.length))
+        if (this._types.structures && this._types.structures)
             return true;
 
         return false;
+    },
+
+    _displayTypeName: function() 
+    {
+        var typeSet = WebInspector.TypeSet.fromPayload(this._types);
+
+        if (this._types.leastCommonAncestor) {
+            if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object))
+                return this._types.leastCommonAncestor;
+            if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object | WebInspector.TypeSet.NullOrUndefinedTypeBits))
+                return this._types.leastCommonAncestor + "?";
+        }
+
+        // The order of these checks are important. 
+        // For example, if a value is only a function, it is contained in TypeFunction, but it is also contained in (TypeFunction | TypeNull).
+        // Therefore, more specific types must be checked first.
+
+        // The strings returned here should match those in TypeTokenView.ColorClassForType
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Function))
+            return "Function";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Undefined))
+            return "Undefined";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Null))
+            return "Null";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Boolean))
+            return "Boolean";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Integer))
+            return "Integer";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Number | WebInspector.TypeSet.TypeBit.Integer))
+            return "Number";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.String))
+            return "String";
+
+        if (typeSet.isContainedIn(WebInspector.TypeSet.NullOrUndefinedTypeBits))
+            return "(?)";
+
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Function | WebInspector.TypeSet.NullOrUndefinedTypeBits))
+            return "Function?";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Boolean | WebInspector.TypeSet.NullOrUndefinedTypeBits))
+            return "Boolean?";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Integer | WebInspector.TypeSet.NullOrUndefinedTypeBits))
+            return "Integer?";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Number | WebInspector.TypeSet.TypeBit.Integer | WebInspector.TypeSet.NullOrUndefinedTypeBits))
+            return "Number?";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.String | WebInspector.TypeSet.NullOrUndefinedTypeBits))
+            return "String?";
+       
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object | WebInspector.TypeSet.TypeBit.Function | WebInspector.TypeSet.TypeBit.String))
+            return "Object";
+        if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object | WebInspector.TypeSet.TypeBit.Function | WebInspector.TypeSet.TypeBit.String | WebInspector.TypeSet.NullOrUndefinedTypeBits))
+            return "Object?";
+
+        return "(" + WebInspector.UIString("many") + ")";
     }
 };