Web Inspector: ES6: Better support for Symbol types in Type Profiler
https://bugs.webkit.org/show_bug.cgi?id=141257

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

ES6 introduces the new primitive type Symbol. This patch makes JSC's 
type profiler support this new primitive type.

* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* inspector/protocol/Runtime.json:
* runtime/RuntimeType.cpp:
(JSC::runtimeTypeForValue):
* runtime/RuntimeType.h:
(JSC::runtimeTypeIsPrimitive):
* runtime/TypeSet.cpp:
(JSC::TypeSet::addTypeInformation):
(JSC::TypeSet::dumpTypes):
(JSC::TypeSet::doesTypeConformTo):
(JSC::TypeSet::displayName):
(JSC::TypeSet::inspectorTypeSet):
(JSC::TypeSet::toJSONString):
* runtime/TypeSet.h:
(JSC::TypeSet::seenTypes):
* tests/typeProfiler/driver/driver.js:
* tests/typeProfiler/symbol.js: Added.
(wrapper.foo):
(wrapper.bar):
(wrapper.bar.bar.baz):
(wrapper):

Source/WebInspectorUI:

The Web Inspector's visualization of JSC's type profiler
should have support for the Symbol type.

* UserInterface/Models/TypeSet.js:
(WebInspector.TypeSet):
(WebInspector.TypeSet.prototype.get primitiveTypeNames):
* UserInterface/Views/TypeTokenView.css:
TypeTokenView will display Symbol type tokens using the same color that 
Symbol values are displayed as formatted values.

(.type-token-symbol):
* UserInterface/Views/TypeTokenView.js:
(WebInspector.TypeTokenView.prototype._displayTypeName):
(WebInspector.TypeTokenView):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@182114 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index c03b067..25af2cb 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,36 @@
+2015-03-28  Saam Barati  <saambarati1@gmail.com>
+
+        Web Inspector: ES6: Better support for Symbol types in Type Profiler
+        https://bugs.webkit.org/show_bug.cgi?id=141257
+
+        Reviewed by Joseph Pecoraro.
+
+        ES6 introduces the new primitive type Symbol. This patch makes JSC's 
+        type profiler support this new primitive type.
+
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * inspector/protocol/Runtime.json:
+        * runtime/RuntimeType.cpp:
+        (JSC::runtimeTypeForValue):
+        * runtime/RuntimeType.h:
+        (JSC::runtimeTypeIsPrimitive):
+        * runtime/TypeSet.cpp:
+        (JSC::TypeSet::addTypeInformation):
+        (JSC::TypeSet::dumpTypes):
+        (JSC::TypeSet::doesTypeConformTo):
+        (JSC::TypeSet::displayName):
+        (JSC::TypeSet::inspectorTypeSet):
+        (JSC::TypeSet::toJSONString):
+        * runtime/TypeSet.h:
+        (JSC::TypeSet::seenTypes):
+        * tests/typeProfiler/driver/driver.js:
+        * tests/typeProfiler/symbol.js: Added.
+        (wrapper.foo):
+        (wrapper.bar):
+        (wrapper.bar.bar.baz):
+        (wrapper):
+
 2015-03-27  Saam Barati  <saambarati1@gmail.com>
 
         Deconstruction parameters are bound too late
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 146ee5c..537faac 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1148,7 +1148,7 @@
             // (The other direction does not hold in general).
 
             RefPtr<TypeSet> typeSet = node->typeLocation()->m_instructionTypeSet;
-            uint8_t seenTypes = typeSet->seenTypes();
+            RuntimeTypeMask seenTypes = typeSet->seenTypes();
             if (typeSet->doesTypeConformTo(TypeMachineInt)) {
                 node->convertToCheck();
                 if (node->child1()->shouldSpeculateInt32())
diff --git a/Source/JavaScriptCore/inspector/protocol/Runtime.json b/Source/JavaScriptCore/inspector/protocol/Runtime.json
index e3bb91a..a7dfb7e 100644
--- a/Source/JavaScriptCore/inspector/protocol/Runtime.json
+++ b/Source/JavaScriptCore/inspector/protocol/Runtime.json
@@ -154,7 +154,8 @@
                 { "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." }
+                { "name": "isObject", "type": "boolean", "description": "Indicates if this type description has been type Object." },
+                { "name": "isSymbol", "type": "boolean", "description": "Indicates if this type description has been type Symbol." }
             ]
         },
         {
diff --git a/Source/JavaScriptCore/runtime/RuntimeType.cpp b/Source/JavaScriptCore/runtime/RuntimeType.cpp
index 1d55d9b..4e7b3a7 100644
--- a/Source/JavaScriptCore/runtime/RuntimeType.cpp
+++ b/Source/JavaScriptCore/runtime/RuntimeType.cpp
@@ -51,6 +51,8 @@
         return TypeObject;
     if (value.isFunction())
         return TypeFunction;
+    if (value.isSymbol())
+        return TypeSymbol;
 
     return TypeNothing;
 }
diff --git a/Source/JavaScriptCore/runtime/RuntimeType.h b/Source/JavaScriptCore/runtime/RuntimeType.h
index 5f81e62..d57b8fb 100644
--- a/Source/JavaScriptCore/runtime/RuntimeType.h
+++ b/Source/JavaScriptCore/runtime/RuntimeType.h
@@ -31,7 +31,7 @@
 
 namespace JSC {
 
-enum RuntimeType : uint8_t {
+enum RuntimeType : uint16_t {
     TypeNothing            = 0x0,
     TypeFunction           = 0x1,
     TypeUndefined          = 0x2,
@@ -40,13 +40,21 @@
     TypeMachineInt         = 0x10,
     TypeNumber             = 0x20,
     TypeString             = 0x40,
-    TypeObject             = 0x80
+    TypeObject             = 0x80,
+    TypeSymbol             = 0x100
 };
 
+typedef uint16_t RuntimeTypeMask;
+
 class JSValue;
 RuntimeType runtimeTypeForValue(JSValue);
 String runtimeTypeAsString(RuntimeType);
 
+ALWAYS_INLINE bool runtimeTypeIsPrimitive(RuntimeTypeMask type)
+{
+    return type & ~(TypeFunction | TypeObject);
+}
+
 } // namespace JSC
 
 #endif // RuntimeType_h
diff --git a/Source/JavaScriptCore/runtime/TypeSet.cpp b/Source/JavaScriptCore/runtime/TypeSet.cpp
index 3161581..8107388 100644
--- a/Source/JavaScriptCore/runtime/TypeSet.cpp
+++ b/Source/JavaScriptCore/runtime/TypeSet.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2014 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -47,7 +47,7 @@
     RefPtr<StructureShape> newShape = prpNewShape;
     m_seenTypes = m_seenTypes | type;
 
-    if (structure && newShape && type != TypeString) {
+    if (structure && newShape && !runtimeTypeIsPrimitive(type)) {
         if (!m_structureSet.contains(structure)) {
             m_structureSet.add(structure);
             // Make one more pass making sure that: 
@@ -93,21 +93,23 @@
     StringBuilder seen;
 
     if (m_seenTypes & TypeFunction)
-         seen.append("Function ");
+        seen.appendLiteral("Function ");
     if (m_seenTypes & TypeUndefined)
-         seen.append("Undefined ");
+        seen.appendLiteral("Undefined ");
     if (m_seenTypes & TypeNull)
-         seen.append("Null ");
+        seen.appendLiteral("Null ");
     if (m_seenTypes & TypeBoolean)
-         seen.append("Boolean ");
+        seen.appendLiteral("Boolean ");
     if (m_seenTypes & TypeMachineInt)
-         seen.append("MachineInt ");
+        seen.appendLiteral("MachineInt ");
     if (m_seenTypes & TypeNumber)
-         seen.append("Number ");
+        seen.appendLiteral("Number ");
     if (m_seenTypes & TypeString)
-         seen.append("String ");
+        seen.appendLiteral("String ");
     if (m_seenTypes & TypeObject)
-         seen.append("Object ");
+        seen.appendLiteral("Object ");
+    if (m_seenTypes & TypeSymbol)
+        seen.appendLiteral("Symbol ");
 
     for (size_t i = 0; i < m_structureHistory.size(); i++) {
         RefPtr<StructureShape> shape = m_structureHistory.at(i);
@@ -116,7 +118,7 @@
     }
 
     if (m_structureHistory.size()) 
-        seen.append("\nStructures:[ ");
+        seen.appendLiteral("\nStructures:[ ");
     for (size_t i = 0; i < m_structureHistory.size(); i++) {
         seen.append(m_structureHistory.at(i)->stringRepresentation());
         seen.append(' ');
@@ -125,14 +127,14 @@
         seen.append(']');
 
     if (m_structureHistory.size()) {
-        seen.append("\nLeast Common Ancestor: ");
+        seen.appendLiteral("\nLeast Common Ancestor: ");
         seen.append(leastCommonAncestor());
     }
 
     return seen.toString();
 }
 
-bool TypeSet::doesTypeConformTo(uint8_t test) const
+bool TypeSet::doesTypeConformTo(RuntimeTypeMask test) const
 {
     // This function checks if our seen types conform  to the types described by the test bitstring. (i.e we haven't seen more types than test).
     // We are <= to those types if ANDing with the bitstring doesn't zero out any of our bits.
@@ -183,6 +185,8 @@
         return ASCIILiteral("Number");
     if (doesTypeConformTo(TypeString))
         return ASCIILiteral("String");
+    if (doesTypeConformTo(TypeSymbol))
+        return ASCIILiteral("Symbol");
 
     if (doesTypeConformTo(TypeNull | TypeUndefined))
         return ASCIILiteral("(?)");
@@ -197,6 +201,8 @@
         return ASCIILiteral("Number?");
     if (doesTypeConformTo(TypeString | TypeNull | TypeUndefined))
         return ASCIILiteral("String?");
+    if (doesTypeConformTo(TypeSymbol | TypeNull | TypeUndefined))
+        return ASCIILiteral("Symbol?");
    
     if (doesTypeConformTo(TypeObject | TypeFunction | TypeString))
         return ASCIILiteral("Object");
@@ -232,6 +238,7 @@
         .setIsNumber((m_seenTypes & TypeNumber) != TypeNothing)
         .setIsString((m_seenTypes & TypeString) != TypeNothing)
         .setIsObject((m_seenTypes & TypeObject) != TypeNothing)
+        .setIsSymbol((m_seenTypes & TypeSymbol) != TypeNothing)
         .release();
 }
 
@@ -288,6 +295,12 @@
         hasAnItem = true;
         json.append("\"String\"");
     }
+    if (m_seenTypes & TypeSymbol) {
+        if (hasAnItem)
+            json.append(",");
+        hasAnItem = true;
+        json.append("\"Symbol\"");
+    }
     json.append("]");
 
     json.append(",");
diff --git a/Source/JavaScriptCore/runtime/TypeSet.h b/Source/JavaScriptCore/runtime/TypeSet.h
index 9737602..2297f1d 100644
--- a/Source/JavaScriptCore/runtime/TypeSet.h
+++ b/Source/JavaScriptCore/runtime/TypeSet.h
@@ -94,12 +94,12 @@
     String leastCommonAncestor() const;
     Ref<Inspector::Protocol::Runtime::TypeSet> inspectorTypeSet() const;
     bool isEmpty() const { return m_seenTypes == TypeNothing; }
-    bool doesTypeConformTo(uint8_t test) const;
-    uint8_t seenTypes() const { return m_seenTypes; }
+    bool doesTypeConformTo(RuntimeTypeMask test) const;
+    RuntimeTypeMask seenTypes() const { return m_seenTypes; }
     StructureSet structureSet() const { return m_structureSet; };
 
 private:
-    uint8_t m_seenTypes;
+    RuntimeTypeMask m_seenTypes;
     bool m_isOverflown;
     Vector<RefPtr<StructureShape>> m_structureHistory;
     StructureSet m_structureSet;
diff --git a/Source/JavaScriptCore/tests/typeProfiler/driver/driver.js b/Source/JavaScriptCore/tests/typeProfiler/driver/driver.js
index a48ee10..4ea44ad 100644
--- a/Source/JavaScriptCore/tests/typeProfiler/driver/driver.js
+++ b/Source/JavaScriptCore/tests/typeProfiler/driver/driver.js
@@ -7,6 +7,7 @@
     Many: "(many)",
     String: "String",
     Undefined: "Undefined",
+    Symbol: "Symbol",
     UndefinedOrNull: "(?)"
 };
 
@@ -14,7 +15,8 @@
     Boolean:"Boolean?",
     Integer: "Integer?",
     Number: "Number?",
-    String: "String?"
+    String: "String?",
+    Symbol: "Symbol?"
 };
 
 function assert(condition, reason) {
diff --git a/Source/JavaScriptCore/tests/typeProfiler/symbol.js b/Source/JavaScriptCore/tests/typeProfiler/symbol.js
new file mode 100644
index 0000000..0686faa
--- /dev/null
+++ b/Source/JavaScriptCore/tests/typeProfiler/symbol.js
@@ -0,0 +1,44 @@
+load("./driver/driver.js");
+
+function wrapper() {
+    var s1 = Symbol();
+
+    var sCaptured = Symbol();
+    function foo() {
+        sCaptured = null;
+    }
+    foo();
+
+    function bar(s3) { return s3; }
+    for (var i = 0; i < 1000; i++)
+        bar(i)
+    bar(Symbol())
+
+    function baz(s4) { return s4; }
+    for (var i = 0; i < 1000; i++)
+        baz(Symbol())
+    baz("hello")
+}
+
+wrapper();
+
+// ====== End test cases ======
+
+var types = findTypeForExpression(wrapper, "s1"); 
+assert(types.instructionTypeSet.primitiveTypeNames.length === 1, "Primitive type names should contain one type");
+assert(types.instructionTypeSet.primitiveTypeNames.indexOf(T.Symbol) !== -1, "Primitive type names should contain 'Symbol'");
+
+types = findTypeForExpression(wrapper, "sCaptured");
+assert(types.globalTypeSet.primitiveTypeNames.length === 2, "Primitive type names should contain two items.");
+assert(types.globalTypeSet.primitiveTypeNames.indexOf(T.Symbol) !== -1, "Primitive type names should contain 'Symbol'");
+assert(types.globalTypeSet.primitiveTypeNames.indexOf(T.Null) !== -1, "Primitive type names should contain 'Null'");
+
+types = findTypeForExpression(wrapper, "s3");
+assert(types.instructionTypeSet.primitiveTypeNames.length === 2, "Primitive type names should contain 2 items");
+assert(types.instructionTypeSet.primitiveTypeNames.indexOf(T.Integer) !== -1, "Primitive type names should contain 'Integer'");
+assert(types.instructionTypeSet.primitiveTypeNames.indexOf(T.Symbol) !== -1, "Primitive type names should contain 'Symbol'");
+
+types = findTypeForExpression(wrapper, "s4");
+assert(types.instructionTypeSet.primitiveTypeNames.length === 2, "Primitive type names should contain 2 items");
+assert(types.instructionTypeSet.primitiveTypeNames.indexOf(T.String) !== -1, "Primitive type names should contain 'String'");
+assert(types.instructionTypeSet.primitiveTypeNames.indexOf(T.Symbol) !== -1, "Primitive type names should contain 'Symbol'");