Bug 645416, part 5 - Add the Symbol constructor and Symbol wrapper objects. r=efaust.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 23 Jun 2014 10:55:52 -0500
changeset 190272 5d71e73ce8d4187d6d9e9d017895ac03f1a08c3c
parent 190271 df1552da0b8f6209de02980dafa715740ad4d07c
child 190273 e08a6942e21cbd09bd9a976f76115386e4b48fb9
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersefaust
bugs645416
milestone33.0a1
Bug 645416, part 5 - Add the Symbol constructor and Symbol wrapper objects. r=efaust. This exposes a new primitive type to scripts for the first time since JavaScript first shipped in Netscape 2, over 13 years ago. The tests focus on identity, equality, and being able to pass a symbol around as a value. Of course the point of symbols is that they can be property keys, but that will have to wait for a later patch in this series.
dom/tests/mochitest/general/test_interfaces.html
js/src/builtin/SymbolObject.cpp
js/src/builtin/SymbolObject.h
js/src/jit-test/tests/debug/Debugger-debuggees-06.js
js/src/jit-test/tests/debug/Environment-find-03.js
js/src/jit/IonTypes.h
js/src/jsapi.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/jsprototypes.h
js/src/moz.build
js/src/tests/ecma_6/Symbol/browser.js
js/src/tests/ecma_6/Symbol/constructor.js
js/src/tests/ecma_6/Symbol/equality.js
js/src/tests/ecma_6/Symbol/errors.js
js/src/tests/ecma_6/Symbol/shell.js
js/src/tests/ecma_6/Symbol/surfaces.js
js/src/tests/ecma_6/Symbol/typeof.js
js/src/vm/GlobalObject.cpp
js/src/vm/Interpreter.cpp
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -73,16 +73,17 @@ var ecmaGlobals =
     "RangeError",
     "ReferenceError",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
     {name: "SIMD", nightly: true},
     "StopIteration",
     "String",
+    "Symbol",
     "SyntaxError",
     {name: "TypedObject", nightly: true},
     "TypeError",
     "Uint16Array",
     "Uint32Array",
     "Uint8Array",
     "Uint8ClampedArray",
     "URIError",
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/SymbolObject.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "builtin/SymbolObject.h"
+
+#include "jsobjinlines.h"
+
+#include "vm/Symbol-inl.h"
+
+using namespace js;
+
+const Class SymbolObject::class_ = {
+    "Symbol",
+    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol),
+    JS_PropertyStub,         /* addProperty */
+    JS_DeletePropertyStub,   /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub
+};
+
+SymbolObject *
+SymbolObject::create(JSContext *cx, JS::Symbol *symbol)
+{
+    JSObject *obj = NewBuiltinClassInstance(cx, &class_);
+    if (!obj)
+        return nullptr;
+    SymbolObject &symobj = obj->as<SymbolObject>();
+    symobj.setPrimitiveValue(symbol);
+    return &symobj;
+}
+
+const JSPropertySpec SymbolObject::properties[] = {
+    JS_PS_END
+};
+
+const JSFunctionSpec SymbolObject::methods[] = {
+    JS_FS_END
+};
+
+const JSFunctionSpec SymbolObject::staticMethods[] = {
+    JS_FS_END
+};
+
+JSObject *
+SymbolObject::initClass(JSContext *cx, HandleObject obj)
+{
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+
+    // This uses &JSObject::class_ because: "The Symbol prototype object is an
+    // ordinary object. It is not a Symbol instance and does not have a
+    // [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3)
+    RootedObject proto(cx, global->createBlankPrototype(cx, &JSObject::class_));
+    if (!proto)
+        return nullptr;
+
+    RootedFunction ctor(cx, global->createConstructor(cx, construct,
+                                                      ClassName(JSProto_Symbol, cx), 1));
+    if (!ctor ||
+        !LinkConstructorAndPrototype(cx, ctor, proto) ||
+        !DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
+        !DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods) ||
+        !GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
+    {
+        return nullptr;
+    }
+    return proto;
+}
+
+// ES6 rev 24 (2014 Apr 27) 19.4.1.1 and 19.4.1.2
+bool
+SymbolObject::construct(JSContext *cx, unsigned argc, Value *vp)
+{
+    // According to a note in the draft standard, "Symbol has ordinary
+    // [[Construct]] behaviour but the definition of its @@create method causes
+    // `new Symbol` to throw a TypeError exception." We do not support @@create
+    // yet, so just throw a TypeError.
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.isConstructing()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR, "Symbol");
+        return false;
+    }
+
+    // steps 1-3
+    RootedString desc(cx);
+    if (!args.get(0).isUndefined()) {
+        desc = ToString(cx, args.get(0));
+        if (!desc)
+            return false;
+    }
+
+    // step 4
+    RootedSymbol symbol(cx, JS::Symbol::new_(cx, desc));
+    if (!symbol)
+        return false;
+    args.rval().setSymbol(symbol);
+    return true;
+}
+
+JSObject *
+js_InitSymbolClass(JSContext *cx, HandleObject obj)
+{
+    return SymbolObject::initClass(cx, obj);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/SymbolObject.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef builtin_SymbolObject_h
+#define builtin_SymbolObject_h
+
+#include "jsobj.h"
+
+#include "vm/Symbol.h"
+
+namespace js {
+
+class SymbolObject : public JSObject
+{
+    /* Stores this Symbol object's [[PrimitiveValue]]. */
+    static const unsigned PRIMITIVE_VALUE_SLOT = 0;
+
+  public:
+    static const unsigned RESERVED_SLOTS = 1;
+
+    static const Class class_;
+
+    static JSObject *initClass(JSContext *cx, js::HandleObject obj);
+
+    /*
+     * Creates a new Symbol object boxing the given primitive Symbol.  The
+     * object's [[Prototype]] is determined from context.
+     */
+    static SymbolObject *create(JSContext *cx, JS::Symbol *symbol);
+
+    JS::Symbol *unbox() const {
+        return getFixedSlot(PRIMITIVE_VALUE_SLOT).toSymbol();
+    }
+
+  private:
+    inline void setPrimitiveValue(JS::Symbol *symbol) {
+        setFixedSlot(PRIMITIVE_VALUE_SLOT, SymbolValue(symbol));
+    }
+
+    static bool construct(JSContext *cx, unsigned argc, Value *vp);
+
+    static const JSPropertySpec properties[];
+    static const JSFunctionSpec methods[];
+    static const JSFunctionSpec staticMethods[];
+};
+
+} /* namespace js */
+
+extern JSObject *
+js_InitSymbolClass(JSContext *cx, js::HandleObject obj);
+
+#endif /* builtin_SymbolObject_h */
--- a/js/src/jit-test/tests/debug/Debugger-debuggees-06.js
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-06.js
@@ -12,15 +12,16 @@ function check(val) {
 
 // Primitive values are invalid.
 check(undefined);
 check(null);
 check(false);
 check(1);
 check(NaN);
 check("ok");
+check(Symbol("ok"));
 
 // A Debugger.Object that belongs to a different Debugger object is invalid.
 var g = newGlobal();
 var dbg2 = new Debugger;
 var w = dbg2.addDebuggee(g);
 assertEq(w instanceof Debugger.Object, true);
 check(w);
--- a/js/src/jit-test/tests/debug/Environment-find-03.js
+++ b/js/src/jit-test/tests/debug/Environment-find-03.js
@@ -1,9 +1,9 @@
-// env.find() finds noneumerable properties in with statements.
+// env.find() finds nonenumerable properties in with statements.
 
 var g = newGlobal();
 var dbg = Debugger(g);
 var hits = 0;
 g.h = function () {
     var frame = dbg.getNewestFrame();
     var target = frame.eval("obj").return;
     var env = frame.environment.find("PI");
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -232,16 +232,17 @@ enum MIRType
 {
     MIRType_Undefined,
     MIRType_Null,
     MIRType_Boolean,
     MIRType_Int32,
     MIRType_Double,
     MIRType_Float32,
     MIRType_String,
+    MIRType_Symbol,
     MIRType_Object,
     MIRType_MagicOptimizedArguments, // JS_OPTIMIZED_ARGUMENTS magic value.
     MIRType_MagicOptimizedOut,       // JS_OPTIMIZED_OUT magic value.
     MIRType_MagicHole,               // JS_ELEMENTS_HOLE magic value.
     MIRType_MagicIsConstructing,     // JS_IS_CONSTRUCTING magic value.
     MIRType_Value,
     MIRType_None,                    // Invalid, used as a placeholder.
     MIRType_Slots,                   // A slots vector
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -42,16 +42,17 @@
 #include "jsweakmap.h"
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Intl.h"
 #include "builtin/MapObject.h"
 #include "builtin/RegExp.h"
+#include "builtin/SymbolObject.h"
 #ifdef ENABLE_BINARYDATA
 #include "builtin/SIMD.h"
 #include "builtin/TypedObject.h"
 #endif
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/Marking.h"
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1966,16 +1966,18 @@ js::ReportIncompatibleMethod(JSContext *
                   !thisv.toObject().getProto() ||
                   thisv.toObject().getProto()->getClass() != clasp);
     } else if (thisv.isString()) {
         JS_ASSERT(clasp != &StringObject::class_);
     } else if (thisv.isNumber()) {
         JS_ASSERT(clasp != &NumberObject::class_);
     } else if (thisv.isBoolean()) {
         JS_ASSERT(clasp != &BooleanObject::class_);
+    } else if (thisv.isSymbol()) {
+        JS_ASSERT(clasp != &SymbolObject::class_);
     } else {
         JS_ASSERT(thisv.isUndefined() || thisv.isNull());
     }
 #endif
 
     if (JSFunction *fun = ReportIfNotFunction(cx, call.calleev())) {
         JSAutoByteString funNameBytes;
         if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -173,16 +173,18 @@ types::TypeString(Type type)
           case JSVAL_TYPE_BOOLEAN:
             return "bool";
           case JSVAL_TYPE_INT32:
             return "int";
           case JSVAL_TYPE_DOUBLE:
             return "float";
           case JSVAL_TYPE_STRING:
             return "string";
+          case JSVAL_TYPE_SYMBOL:
+            return "symbol";
           case JSVAL_TYPE_MAGIC:
             return "lazyargs";
           default:
             MOZ_ASSUME_UNREACHABLE("Bad type");
         }
     }
     if (type.isUnknown())
         return "unknown";
@@ -327,16 +329,18 @@ TypeSet::mightBeMIRType(jit::MIRType typ
         return baseFlags() & TYPE_FLAG_BOOLEAN;
       case jit::MIRType_Int32:
         return baseFlags() & TYPE_FLAG_INT32;
       case jit::MIRType_Float32: // Fall through, there's no JSVAL for Float32.
       case jit::MIRType_Double:
         return baseFlags() & TYPE_FLAG_DOUBLE;
       case jit::MIRType_String:
         return baseFlags() & TYPE_FLAG_STRING;
+      case jit::MIRType_Symbol:
+        return baseFlags() & TYPE_FLAG_SYMBOL;
       case jit::MIRType_MagicOptimizedArguments:
         return baseFlags() & TYPE_FLAG_LAZYARGS;
       case jit::MIRType_MagicHole:
       case jit::MIRType_MagicIsConstructing:
         // These magic constants do not escape to script and are not observed
         // in the type sets.
         //
         // The reason we can return false here is subtle: if Ion is asking the
@@ -1319,16 +1323,18 @@ GetMIRTypeFromTypeFlags(TypeFlags flags)
       case TYPE_FLAG_BOOLEAN:
         return jit::MIRType_Boolean;
       case TYPE_FLAG_INT32:
         return jit::MIRType_Int32;
       case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
         return jit::MIRType_Double;
       case TYPE_FLAG_STRING:
         return jit::MIRType_String;
+      case TYPE_FLAG_SYMBOL:
+        return jit::MIRType_Symbol;
       case TYPE_FLAG_LAZYARGS:
         return jit::MIRType_MagicOptimizedArguments;
       case TYPE_FLAG_ANYOBJECT:
         return jit::MIRType_Object;
       default:
         return jit::MIRType_Value;
     }
 }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -372,64 +372,66 @@ public:
      * If the data this constraint refers to is still live, copy it into the
      * zone's new allocator. Type constraints only hold weak references.
      */
     virtual bool sweep(TypeZone &zone, TypeConstraint **res) = 0;
 };
 
 /* Flags and other state stored in TypeSet::flags */
 enum MOZ_ENUM_TYPE(uint32_t) {
-    TYPE_FLAG_UNDEFINED =  0x1,
-    TYPE_FLAG_NULL      =  0x2,
-    TYPE_FLAG_BOOLEAN   =  0x4,
-    TYPE_FLAG_INT32     =  0x8,
-    TYPE_FLAG_DOUBLE    = 0x10,
-    TYPE_FLAG_STRING    = 0x20,
-    TYPE_FLAG_LAZYARGS  = 0x40,
-    TYPE_FLAG_ANYOBJECT = 0x80,
+    TYPE_FLAG_UNDEFINED =   0x1,
+    TYPE_FLAG_NULL      =   0x2,
+    TYPE_FLAG_BOOLEAN   =   0x4,
+    TYPE_FLAG_INT32     =   0x8,
+    TYPE_FLAG_DOUBLE    =  0x10,
+    TYPE_FLAG_STRING    =  0x20,
+    TYPE_FLAG_SYMBOL    =  0x40,
+    TYPE_FLAG_LAZYARGS  =  0x80,
+    TYPE_FLAG_ANYOBJECT = 0x100,
 
     /* Mask containing all primitives */
     TYPE_FLAG_PRIMITIVE = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_BOOLEAN |
-                          TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING,
+                          TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING |
+                          TYPE_FLAG_SYMBOL,
 
     /* Mask/shift for the number of objects in objectSet */
-    TYPE_FLAG_OBJECT_COUNT_MASK   = 0x1f00,
-    TYPE_FLAG_OBJECT_COUNT_SHIFT  = 8,
+    TYPE_FLAG_OBJECT_COUNT_MASK   = 0x3e00,
+    TYPE_FLAG_OBJECT_COUNT_SHIFT  = 9,
     TYPE_FLAG_OBJECT_COUNT_LIMIT  =
         TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,
 
     /* Whether the contents of this type set are totally unknown. */
-    TYPE_FLAG_UNKNOWN             = 0x00002000,
+    TYPE_FLAG_UNKNOWN             = 0x00004000,
 
     /* Mask of normal type flags on a type set. */
-    TYPE_FLAG_BASE_MASK           = 0x000020ff,
+    TYPE_FLAG_BASE_MASK           = 0x000041ff,
 
     /* Additional flags for HeapTypeSet sets. */
 
     /*
      * Whether the property has ever been deleted or reconfigured to behave
      * differently from a plain data property, other than making the property
      * non-writable.
      */
-    TYPE_FLAG_NON_DATA_PROPERTY = 0x00004000,
+    TYPE_FLAG_NON_DATA_PROPERTY = 0x00008000,
 
     /* Whether the property has ever been made non-writable. */
-    TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00008000,
+    TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00010000,
 
     /*
      * Whether the property is definitely in a particular slot on all objects
      * from which it has not been deleted or reconfigured. For singletons
      * this may be a fixed or dynamic slot, and for other objects this will be
      * a fixed slot.
      *
      * If the property is definite, mask and shift storing the slot + 1.
      * Otherwise these bits are clear.
      */
-    TYPE_FLAG_DEFINITE_MASK       = 0xffff0000,
-    TYPE_FLAG_DEFINITE_SHIFT      = 16
+    TYPE_FLAG_DEFINITE_MASK       = 0xfffe0000,
+    TYPE_FLAG_DEFINITE_SHIFT      = 17
 };
 typedef uint32_t TypeFlags;
 
 /* Flags and other state stored in TypeObject::flags */
 enum MOZ_ENUM_TYPE(uint32_t) {
     /* Whether this type object is associated with some allocation site. */
     OBJECT_FLAG_FROM_ALLOCATION_SITE  = 0x1,
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -8,16 +8,17 @@
 
 #ifndef jsinferinlines_h
 #define jsinferinlines_h
 
 #include "jsinfer.h"
 
 #include "mozilla/PodOperations.h"
 
+#include "builtin/SymbolObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/BooleanObject.h"
 #include "vm/NumberObject.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/StringObject.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jscntxtinlines.h"
@@ -157,16 +158,18 @@ PrimitiveTypeFlag(JSValueType type)
       case JSVAL_TYPE_BOOLEAN:
         return TYPE_FLAG_BOOLEAN;
       case JSVAL_TYPE_INT32:
         return TYPE_FLAG_INT32;
       case JSVAL_TYPE_DOUBLE:
         return TYPE_FLAG_DOUBLE;
       case JSVAL_TYPE_STRING:
         return TYPE_FLAG_STRING;
+      case JSVAL_TYPE_SYMBOL:
+        return TYPE_FLAG_SYMBOL;
       case JSVAL_TYPE_MAGIC:
         return TYPE_FLAG_LAZYARGS;
       default:
         MOZ_ASSUME_UNREACHABLE("Bad type");
     }
 }
 
 inline JSValueType
@@ -180,16 +183,18 @@ TypeFlagPrimitive(TypeFlags flags)
       case TYPE_FLAG_BOOLEAN:
         return JSVAL_TYPE_BOOLEAN;
       case TYPE_FLAG_INT32:
         return JSVAL_TYPE_INT32;
       case TYPE_FLAG_DOUBLE:
         return JSVAL_TYPE_DOUBLE;
       case TYPE_FLAG_STRING:
         return JSVAL_TYPE_STRING;
+      case TYPE_FLAG_SYMBOL:
+        return JSVAL_TYPE_SYMBOL;
       case TYPE_FLAG_LAZYARGS:
         return JSVAL_TYPE_MAGIC;
       default:
         MOZ_ASSUME_UNREACHABLE("Bad type");
     }
 }
 
 /*
@@ -326,16 +331,18 @@ GetClassForProtoKey(JSProtoKey key)
         return &ArrayObject::class_;
 
       case JSProto_Number:
         return &NumberObject::class_;
       case JSProto_Boolean:
         return &BooleanObject::class_;
       case JSProto_String:
         return &StringObject::class_;
+      case JSProto_Symbol:
+        return &SymbolObject::class_;
       case JSProto_RegExp:
         return &RegExpObject::class_;
 
       case JSProto_Int8Array:
       case JSProto_Uint8Array:
       case JSProto_Int16Array:
       case JSProto_Uint16Array:
       case JSProto_Int32Array:
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -33,16 +33,17 @@
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
+#include "builtin/SymbolObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/AsmJSModule.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "js/OldDebugAPI.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
@@ -238,16 +239,18 @@ js::NonNullObject(JSContext *cx, const V
 
 const char *
 js::InformalValueTypeName(const Value &v)
 {
     if (v.isObject())
         return v.toObject().getClass()->name;
     if (v.isString())
         return "string";
+    if (v.isSymbol())
+        return "symbol";
     if (v.isNumber())
         return "number";
     if (v.isBoolean())
         return "boolean";
     if (v.isNull())
         return "null";
     if (v.isUndefined())
         return "undefined";
@@ -5637,22 +5640,23 @@ JSObject *
 js::PrimitiveToObject(JSContext *cx, const Value &v)
 {
     if (v.isString()) {
         Rooted<JSString*> str(cx, v.toString());
         return StringObject::create(cx, str);
     }
     if (v.isNumber())
         return NumberObject::create(cx, v.toNumber());
-
-    JS_ASSERT(v.isBoolean());
-    return BooleanObject::create(cx, v.toBoolean());
-}
-
-/* Callers must handle the already-object case . */
+    if (v.isBoolean())
+        return BooleanObject::create(cx, v.toBoolean());
+    JS_ASSERT(v.isSymbol());
+    return SymbolObject::create(cx, v.toSymbol());
+}
+
+/* Callers must handle the already-object case. */
 JSObject *
 js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
 {
     JS_ASSERT(!val.isMagic());
     JS_ASSERT(!val.isObject());
 
     if (val.isNullOrUndefined()) {
         if (reportScanStack) {
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -92,17 +92,18 @@
     real(Float32Array,          28,     js_InitViaClassSpec,       TYPED_ARRAY_CLASP(TYPE_FLOAT32)) \
     real(Float64Array,          29,     js_InitViaClassSpec,       TYPED_ARRAY_CLASP(TYPE_FLOAT64)) \
     real(Uint8ClampedArray,     30,     js_InitViaClassSpec,       TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)) \
     real(Proxy,                 31,     js_InitProxyClass,         &ProxyObject::uncallableClass_) \
     real(WeakMap,               32,     js_InitWeakMapClass,       OCLASP(WeakMap)) \
     real(Map,                   33,     js_InitMapClass,           OCLASP(Map)) \
     real(Set,                   34,     js_InitSetClass,           OCLASP(Set)) \
     real(DataView,              35,     js_InitDataViewClass,      OCLASP(DataView)) \
-IF_SAB(real,imaginary)(SharedArrayBuffer,       36,     js_InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \
-IF_INTL(real,imaginary) (Intl,                  37,     js_InitIntlClass,          CLASP(Intl)) \
-IF_BDATA(real,imaginary)(TypedObject,           38,     js_InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
-    imaginary(GeneratorFunction,     39,     js_InitIteratorClasses, dummy) \
-IF_BDATA(real,imaginary)(SIMD,                  40,     js_InitSIMDClass, OCLASP(SIMD)) \
+    real(Symbol,                36,     js_InitSymbolClass,        &js::SymbolObject::class_) \
+IF_SAB(real,imaginary)(SharedArrayBuffer,       37,     js_InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \
+IF_INTL(real,imaginary) (Intl,                  38,     js_InitIntlClass,          CLASP(Intl)) \
+IF_BDATA(real,imaginary)(TypedObject,           39,     js_InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
+    imaginary(GeneratorFunction,     40,     js_InitIteratorClasses, dummy) \
+IF_BDATA(real,imaginary)(SIMD,                  41,     js_InitSIMDClass, OCLASP(SIMD)) \
 
 #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
 
 #endif /* jsprototypes_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -97,16 +97,17 @@ EXPORTS.js += [
 UNIFIED_SOURCES += [
     'assembler/jit/ExecutableAllocator.cpp',
     'builtin/Eval.cpp',
     'builtin/Intl.cpp',
     'builtin/MapObject.cpp',
     'builtin/Object.cpp',
     'builtin/Profilers.cpp',
     'builtin/SIMD.cpp',
+    'builtin/SymbolObject.cpp',
     'builtin/TestingFunctions.cpp',
     'builtin/TypedObject.cpp',
     'devtools/sharkctl.cpp',
     'ds/LifoAlloc.cpp',
     'frontend/BytecodeCompiler.cpp',
     'frontend/BytecodeEmitter.cpp',
     'frontend/FoldConstants.cpp',
     'frontend/NameFunctions.cpp',
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Symbol/constructor.js
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// More tests will be added here when Symbol.prototype.toString is added.
+
+assertEq(Object.getPrototypeOf(Symbol.prototype), Object.prototype);
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Symbol/equality.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Several distinct Symbol values.
+var symbols = [
+    Symbol(),
+    Symbol("Symbol.iterator"),
+    Symbol("Symbol.iterator")   // distinct new symbol with the same description
+];
+
+// Distinct symbols are never equal to each other, even if they have the same
+// description.
+for (var i = 0; i < symbols.length; i++) {
+    for (var j = i; j < symbols.length; j++) {
+        var expected = (i === j);
+        assertEq(symbols[i] == symbols[j], expected);
+        assertEq(symbols[i] != symbols[j], !expected);
+        assertEq(symbols[i] === symbols[j], expected);
+        assertEq(symbols[i] !== symbols[j], !expected);
+        assertEq(Object.is(symbols[i], symbols[j]), expected);
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Symbol/errors.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Section numbers cite ES6 rev 24 (2014 April 27).
+
+var sym = Symbol();
+
+// 7.2.2 IsCallable
+assertThrowsInstanceOf(() => sym(), TypeError);
+assertThrowsInstanceOf(() => Function.prototype.call.call(sym), TypeError);
+
+// 7.2.5 IsConstructor
+assertThrowsInstanceOf(() => new sym(), TypeError);
+assertThrowsInstanceOf(() => new Symbol(), TypeError);
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Symbol/surfaces.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Test superficial properties of the Symbol constructor and prototype.
+
+var desc = Object.getOwnPropertyDescriptor(this, "Symbol");
+assertEq(desc.configurable, true);
+assertEq(desc.enumerable, false);
+assertEq(desc.writable, true);
+assertEq(typeof Symbol, "function");
+assertEq(Symbol.length, 1);
+
+desc = Object.getOwnPropertyDescriptor(Symbol, "prototype");
+assertEq(desc.configurable, false);
+assertEq(desc.enumerable, false);
+assertEq(desc.writable, false);
+
+assertEq(Symbol.prototype.constructor, Symbol);
+desc = Object.getOwnPropertyDescriptor(Symbol.prototype, "constructor");
+assertEq(desc.configurable, true);
+assertEq(desc.enumerable, false);
+assertEq(desc.writable, true);
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Symbol/typeof.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+assertEq(typeof Symbol(), "symbol");
+assertEq(typeof Symbol("ponies"), "symbol");
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -18,16 +18,17 @@
 #include "builtin/Eval.h"
 #if EXPOSE_INTL_API
 # include "builtin/Intl.h"
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/Object.h"
 #include "builtin/RegExp.h"
 #include "builtin/SIMD.h"
+#include "builtin/SymbolObject.h"
 #include "builtin/TypedObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/PIC.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StopIterationObject.h"
 #include "vm/WeakMapObject.h"
 
 #include "jscompartmentinlines.h"
@@ -312,17 +313,16 @@ GlobalObject::createConstructor(JSContex
     RootedAtom name(cx, nameArg);
     RootedObject self(cx, this);
     return NewFunction(cx, NullPtr(), ctor, length, JSFunction::NATIVE_CTOR, self, name, kind);
 }
 
 static JSObject *
 CreateBlankProto(JSContext *cx, const Class *clasp, JSObject &proto, GlobalObject &global)
 {
-    JS_ASSERT(clasp != &JSObject::class_);
     JS_ASSERT(clasp != &JSFunction::class_);
 
     RootedObject blankProto(cx, NewObjectWithGivenProto(cx, clasp, &proto, &global, SingletonObject));
     if (!blankProto || !blankProto->setDelegate(cx))
         return nullptr;
 
     return blankProto;
 }
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -656,42 +656,41 @@ js::HasInstance(JSContext *cx, HandleObj
         return clasp->hasInstance(cx, obj, &local, bp);
 
     RootedValue val(cx, ObjectValue(*obj));
     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                         JSDVG_SEARCH_STACK, val, NullPtr());
     return false;
 }
 
+static inline bool
+EqualGivenSameType(JSContext *cx, const Value &lval, const Value &rval, bool *equal)
+{
+    MOZ_ASSERT(SameType(lval, rval));
+
+    if (lval.isString())
+        return EqualStrings(cx, lval.toString(), rval.toString(), equal);
+    if (lval.isDouble()) {
+        *equal = (lval.toDouble() == rval.toDouble());
+        return true;
+    }
+    if (lval.isGCThing()) {  // objects or symbols
+        *equal = (lval.toGCThing() == rval.toGCThing());
+        return true;
+    }
+    *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
+    MOZ_ASSERT_IF(lval.isUndefined(), *equal);
+    return true;
+}
+
 bool
 js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *result)
 {
-    if (SameType(lval, rval)) {
-        if (lval.isString()) {
-            JSString *l = lval.toString();
-            JSString *r = rval.toString();
-            return EqualStrings(cx, l, r, result);
-        }
-
-        if (lval.isDouble()) {
-            double l = lval.toDouble(), r = rval.toDouble();
-            *result = (l == r);
-            return true;
-        }
-
-        if (lval.isObject()) {
-            JSObject *l = &lval.toObject();
-            JSObject *r = &rval.toObject();
-            *result = l == r;
-            return true;
-        }
-
-        *result = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
-        return true;
-    }
+    if (SameType(lval, rval))
+        return EqualGivenSameType(cx, lval, rval, result);
 
     if (lval.isNullOrUndefined()) {
         *result = rval.isNullOrUndefined() ||
                   (rval.isObject() && EmulatesUndefined(&rval.toObject()));
         return true;
     }
 
     if (rval.isNullOrUndefined()) {
@@ -719,34 +718,18 @@ js::LooselyEqual(JSContext *cx, const Va
     *result = (l == r);
     return true;
 }
 
 bool
 js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, bool *equal)
 {
     Value lval = lref, rval = rref;
-    if (SameType(lval, rval)) {
-        if (lval.isString())
-            return EqualStrings(cx, lval.toString(), rval.toString(), equal);
-        if (lval.isDouble()) {
-            *equal = (lval.toDouble() == rval.toDouble());
-            return true;
-        }
-        if (lval.isObject()) {
-            *equal = lval.toObject() == rval.toObject();
-            return true;
-        }
-        if (lval.isUndefined()) {
-            *equal = true;
-            return true;
-        }
-        *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
-        return true;
-    }
+    if (SameType(lval, rval))
+        return EqualGivenSameType(cx, lval, rval, equal);
 
     if (lval.isDouble() && rval.isInt32()) {
         double ld = lval.toDouble();
         double rd = rval.toInt32();
         *equal = (ld == rd);
         return true;
     }
     if (lval.isInt32() && rval.isDouble()) {