Bug 645416, part 21 - Add symbol jsids (SYMBOL_TO_JSID), removing the legacy support for object jsids (OBJECT_TO_JSID). r=terrence,r=jimb,r=efaust.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 23 Jun 2014 10:56:52 -0500
changeset 190288 fdcaf5436d38fa38c7d1ad10b6b03485ab425b54
parent 190287 81bd2529fa18cb4f41afcc286ece3491b8aef8f5
child 190289 828bbf42999554ce67dbebcd968885d8c0a75af5
push id27004
push useremorley@mozilla.com
push dateTue, 24 Jun 2014 15:52:34 +0000
treeherdermozilla-central@7b174d47f3cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, jimb, efaust
bugs645416
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 645416, part 21 - Add symbol jsids (SYMBOL_TO_JSID), removing the legacy support for object jsids (OBJECT_TO_JSID). r=terrence,r=jimb,r=efaust. With just this patch, there are not actually any symbol jsids flowing through the system, just as there are not actually any object jsids. But a subsequent patch (part 23) changes this. This patch deletes some code in CTypes.cpp that is simply confused about how element accesses work: Int64 and UInt64 objects were never actually converted to object jsids, so the code being removed here was already dead code.
js/public/Id.h
js/src/builtin/Object.cpp
js/src/ctypes/CTypes.cpp
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/gc/RootMarking.cpp
js/src/gdb/tests/test-jsid.cpp
js/src/gdb/tests/test-jsid.py
js/src/jit/IonMacroAssembler.h
js/src/jsapi.cpp
js/src/jscntxtinlines.h
js/src/jsfriendapi.h
js/src/jspropertytree.cpp
js/src/vm/Debugger.cpp
js/src/vm/Id.cpp
js/src/vm/OldDebugAPI.cpp
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -35,17 +35,17 @@ struct jsid
     bool operator==(jsid rhs) const { return asBits == rhs.asBits; }
     bool operator!=(jsid rhs) const { return asBits != rhs.asBits; }
 };
 #define JSID_BITS(id) (id.asBits)
 
 #define JSID_TYPE_STRING                 0x0
 #define JSID_TYPE_INT                    0x1
 #define JSID_TYPE_VOID                   0x2
-#define JSID_TYPE_OBJECT                 0x4
+#define JSID_TYPE_SYMBOL                 0x4
 #define JSID_TYPE_MASK                   0x7
 
 // Avoid using canonical 'id' for jsid parameters since this is a magic word in
 // Objective-C++ which, apparently, wants to be able to #include jsapi.h.
 #define id iden
 
 static MOZ_ALWAYS_INLINE bool
 JSID_IS_STRING(jsid id)
@@ -93,44 +93,45 @@ INT_TO_JSID(int32_t i)
 {
     jsid id;
     MOZ_ASSERT(INT_FITS_IN_JSID(i));
     JSID_BITS(id) = ((i << 1) | JSID_TYPE_INT);
     return id;
 }
 
 static MOZ_ALWAYS_INLINE bool
-JSID_IS_OBJECT(jsid id)
+JSID_IS_SYMBOL(jsid id)
 {
-    return (JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_OBJECT &&
-           (size_t)JSID_BITS(id) != JSID_TYPE_OBJECT;
+    return (JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_SYMBOL &&
+           JSID_BITS(id) != JSID_TYPE_SYMBOL;
 }
 
-static MOZ_ALWAYS_INLINE JSObject *
-JSID_TO_OBJECT(jsid id)
+static MOZ_ALWAYS_INLINE JS::Symbol *
+JSID_TO_SYMBOL(jsid id)
 {
-    MOZ_ASSERT(JSID_IS_OBJECT(id));
-    return (JSObject *)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
+    MOZ_ASSERT(JSID_IS_SYMBOL(id));
+    return (JS::Symbol *)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
 }
 
 static MOZ_ALWAYS_INLINE jsid
-OBJECT_TO_JSID(JSObject *obj)
+SYMBOL_TO_JSID(JS::Symbol *sym)
 {
     jsid id;
-    MOZ_ASSERT(obj != nullptr);
-    MOZ_ASSERT(((size_t)obj & JSID_TYPE_MASK) == 0);
-    JS_ASSERT(!js::gc::IsInsideNursery(JS::AsCell(obj)));
-    JSID_BITS(id) = ((size_t)obj | JSID_TYPE_OBJECT);
+    MOZ_ASSERT(sym != nullptr);
+    MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0);
+    JS_ASSERT(!js::gc::IsInsideNursery(JS::AsCell(sym)));
+    JS_ASSERT(!JS::IsPoisonedPtr(sym));
+    JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL);
     return id;
 }
 
 static MOZ_ALWAYS_INLINE bool
 JSID_IS_GCTHING(jsid id)
 {
-    return JSID_IS_STRING(id) || JSID_IS_OBJECT(id);
+    return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id);
 }
 
 static MOZ_ALWAYS_INLINE void *
 JSID_TO_GCTHING(jsid id)
 {
     return (void *)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
 }
 
@@ -140,45 +141,45 @@ JSID_IS_VOID(const jsid id)
     MOZ_ASSERT_IF(((size_t)JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_VOID,
                  JSID_BITS(id) == JSID_TYPE_VOID);
     return ((size_t)JSID_BITS(id) == JSID_TYPE_VOID);
 }
 
 static MOZ_ALWAYS_INLINE bool
 JSID_IS_EMPTY(const jsid id)
 {
-    return ((size_t)JSID_BITS(id) == JSID_TYPE_OBJECT);
+    return ((size_t)JSID_BITS(id) == JSID_TYPE_SYMBOL);
 }
 
-#undef id
-
 extern JS_PUBLIC_DATA(const jsid) JSID_VOID;
 extern JS_PUBLIC_DATA(const jsid) JSID_EMPTY;
 
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE;
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE;
 
 namespace js {
 
 inline bool
-IsPoisonedId(jsid iden)
+IsPoisonedId(jsid id)
 {
-    if (JSID_IS_STRING(iden))
-        return JS::IsPoisonedPtr(JSID_TO_STRING(iden));
-    if (JSID_IS_OBJECT(iden))
-        return JS::IsPoisonedPtr(JSID_TO_OBJECT(iden));
+    if (JSID_IS_STRING(id))
+        return JS::IsPoisonedPtr(JSID_TO_STRING(id));
+    if (JSID_IS_SYMBOL(id))
+        return JS::IsPoisonedPtr(JSID_TO_SYMBOL(id));
     return false;
 }
 
 template <> struct GCMethods<jsid>
 {
     static jsid initial() { return JSID_VOID; }
     static bool poisoned(jsid id) { return IsPoisonedId(id); }
     static bool needsPostBarrier(jsid id) { return false; }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(jsid *idp) {}
     static void relocate(jsid *idp) {}
 #endif
 };
 
+#undef id
+
 }
 
 #endif /* js_Id_h */
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -826,47 +826,53 @@ obj_getOwnPropertyDescriptor(JSContext *
     if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyDescriptor", &obj))
         return false;
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args.get(1), &id))
         return false;
     return GetOwnPropertyDescriptor(cx, obj, id, args.rval());
 }
 
+// ES6 draft rev25 (2014/05/22) 19.1.2.14 Object.keys(O)
 static bool
 obj_keys(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
+
+    // steps 1-2
     RootedObject obj(cx);
     if (!GetFirstArgumentAsObject(cx, args, "Object.keys", &obj))
         return false;
 
+    // Steps 3-10. Since JSITER_SYMBOLS and JSITER_HIDDEN are not passed,
+    // GetPropertyNames performs the type check in step 10.c. and the
+    // [[Enumerable]] check specified in step 10.c.iii.
     AutoIdVector props(cx);
     if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
         return false;
 
-    AutoValueVector vals(cx);
-    if (!vals.reserve(props.length()))
+    AutoValueVector namelist(cx);
+    if (!namelist.reserve(props.length()))
         return false;
     for (size_t i = 0, len = props.length(); i < len; i++) {
         jsid id = props[i];
+        JSString *str;
         if (JSID_IS_STRING(id)) {
-            vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
-        } else if (JSID_IS_INT(id)) {
-            JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
+            str = JSID_TO_STRING(id);
+        } else {
+            str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
             if (!str)
                 return false;
-            vals.infallibleAppend(StringValue(str));
-        } else {
-            JS_ASSERT(JSID_IS_OBJECT(id));
         }
+        namelist.infallibleAppend(StringValue(str));
     }
 
+    // step 11
     JS_ASSERT(props.length() <= UINT32_MAX);
-    JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
+    JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(namelist.length()), namelist.begin());
     if (!aobj)
         return false;
 
     args.rval().setObject(*aobj);
     return true;
 }
 
 /* ES6 draft 15.2.3.16 */
@@ -904,17 +910,17 @@ obj_getOwnPropertyNames(JSContext *cx, u
          if (JSID_IS_INT(id)) {
              JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
              if (!str)
                  return false;
              vals[i].setString(str);
          } else if (JSID_IS_ATOM(id)) {
              vals[i].setString(JSID_TO_STRING(id));
          } else {
-             vals[i].setObject(*JSID_TO_OBJECT(id));
+             vals[i].setSymbol(JSID_TO_SYMBOL(id));
          }
     }
 
     JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
     if (!aobj)
         return false;
 
     args.rval().setObject(*aobj);
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1868,51 +1868,35 @@ jsvalToSize(JSContext* cx, jsval val, bo
 }
 
 // Implicitly convert val to IntegerType, allowing int, double,
 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
 template<class IntegerType>
 static bool
 jsidToBigInteger(JSContext* cx,
-                  jsid val,
-                  bool allowString,
-                  IntegerType* result)
+                 jsid val,
+                 bool allowString,
+                 IntegerType* result)
 {
   JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
 
   if (JSID_IS_INT(val)) {
     // Make sure the integer fits in the alotted precision, and has the right
     // sign.
     int32_t i = JSID_TO_INT(val);
     return ConvertExact(i, result);
   }
   if (allowString && JSID_IS_STRING(val)) {
     // Allow conversion from base-10 or base-16 strings, provided the result
     // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
     // to the JS array element operator, which will automatically call
     // toString() on the object for us.)
     return StringToInteger(cx, JSID_TO_STRING(val), result);
   }
-  if (JSID_IS_OBJECT(val)) {
-    // Allow conversion from an Int64 or UInt64 object directly.
-    JSObject* obj = JSID_TO_OBJECT(val);
-
-    if (UInt64::IsUInt64(obj)) {
-      // Make sure the integer fits in IntegerType.
-      uint64_t i = Int64Base::GetInt(obj);
-      return ConvertExact(i, result);
-    }
-
-    if (Int64::IsInt64(obj)) {
-      // Make sure the integer fits in IntegerType.
-      int64_t i = Int64Base::GetInt(obj);
-      return ConvertExact(i, result);
-    }
-  }
   return false;
 }
 
 // Implicitly convert val to a size value, where the size value is represented
 // by size_t but must also fit in a double.
 static bool
 jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
 {
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -176,22 +176,24 @@ namespace gc {
 template <typename T>
 void
 MarkUnbarriered(JSTracer *trc, T **thingp, const char *name);
 
 // Direct value access used by the write barriers and the jits.
 void
 MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
 
-// These two declarations are also present in gc/Marking.h, via the DeclMarker
+// These three declarations are also present in gc/Marking.h, via the DeclMarker
 // macro.  Not great, but hard to avoid.
 void
 MarkObjectUnbarriered(JSTracer *trc, JSObject **obj, const char *name);
 void
 MarkStringUnbarriered(JSTracer *trc, JSString **str, const char *name);
+void
+MarkSymbolUnbarriered(JSTracer *trc, JS::Symbol **sym, const char *name);
 
 // Note that some subclasses (e.g. ObjectImpl) specialize some of these
 // methods.
 template <typename T>
 class BarrieredCell : public gc::Cell
 {
   public:
     MOZ_ALWAYS_INLINE JS::Zone *zone() const { return tenuredZone(); }
@@ -386,34 +388,34 @@ struct InternalGCMethods<Value>
     }
 
     static void readBarrier(const Value &v) { ValueReadBarrier(v); }
 };
 
 template <>
 struct InternalGCMethods<jsid>
 {
-    static bool isMarkable(jsid id) { return JSID_IS_OBJECT(id) || JSID_IS_STRING(id); }
+    static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); }
 
     static void preBarrier(jsid id) {
 #ifdef JSGC_INCREMENTAL
-        if (JSID_IS_OBJECT(id)) {
-            JSObject *obj = JSID_TO_OBJECT(id);
-            JS::shadow::Zone *shadowZone = ShadowZoneOfObjectFromAnyThread(obj);
-            if (shadowZone->needsBarrier()) {
-                js::gc::MarkObjectUnbarriered(shadowZone->barrierTracer(), &obj, "write barrier");
-                JS_ASSERT(obj == JSID_TO_OBJECT(id));
-            }
-        } else if (JSID_IS_STRING(id)) {
+        if (JSID_IS_STRING(id)) {
             JSString *str = JSID_TO_STRING(id);
             JS::shadow::Zone *shadowZone = ShadowZoneOfStringFromAnyThread(str);
             if (shadowZone->needsBarrier()) {
                 js::gc::MarkStringUnbarriered(shadowZone->barrierTracer(), &str, "write barrier");
                 JS_ASSERT(str == JSID_TO_STRING(id));
             }
+        } else if (JSID_IS_SYMBOL(id)) {
+            JS::Symbol *sym = JSID_TO_SYMBOL(id);
+            JS::shadow::Zone *shadowZone = ShadowZoneOfSymbolFromAnyThread(sym);
+            if (shadowZone->needsBarrier()) {
+                js::gc::MarkSymbolUnbarriered(shadowZone->barrierTracer(), &sym, "write barrier");
+                JS_ASSERT(sym == JSID_TO_SYMBOL(id));
+            }
         }
 #endif
     }
     static void preBarrier(Zone *zone, jsid id) { preBarrier(id); }
 
     static void postBarrier(jsid *idp) {}
     static void postBarrierRelocate(jsid *idp) {}
     static void postBarrierRemove(jsid *idp) {}
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -674,21 +674,21 @@ gc::MarkGCThingUnbarriered(JSTracer *trc
 static inline void
 MarkIdInternal(JSTracer *trc, jsid *id)
 {
     if (JSID_IS_STRING(*id)) {
         JSString *str = JSID_TO_STRING(*id);
         trc->setTracingLocation((void *)id);
         MarkInternal(trc, &str);
         *id = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
-    } else if (MOZ_UNLIKELY(JSID_IS_OBJECT(*id))) {
-        JSObject *obj = JSID_TO_OBJECT(*id);
+    } else if (JSID_IS_SYMBOL(*id)) {
+        JS::Symbol *sym = JSID_TO_SYMBOL(*id);
         trc->setTracingLocation((void *)id);
-        MarkInternal(trc, &obj);
-        *id = OBJECT_TO_JSID(obj);
+        MarkInternal(trc, &sym);
+        *id = SYMBOL_TO_JSID(sym);
     } else {
         /* Unset realLocation manually if we do not call MarkInternal. */
         trc->unsetTracingLocation();
     }
 }
 
 void
 gc::MarkId(JSTracer *trc, BarrieredBase<jsid> *id, const char *name)
@@ -1089,18 +1089,18 @@ static void
 ScanShape(GCMarker *gcmarker, Shape *shape)
 {
   restart:
     PushMarkStack(gcmarker, shape->base());
 
     const BarrieredBase<jsid> &id = shape->propidRef();
     if (JSID_IS_STRING(id))
         PushMarkStack(gcmarker, JSID_TO_STRING(id));
-    else if (MOZ_UNLIKELY(JSID_IS_OBJECT(id)))
-        PushMarkStack(gcmarker, JSID_TO_OBJECT(id));
+    else if (JSID_IS_SYMBOL(id))
+        PushMarkStack(gcmarker, JSID_TO_SYMBOL(id));
 
     shape = shape->previous();
     if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
         goto restart;
 }
 
 static inline void
 ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -165,17 +165,17 @@ IsAddressableGCThing(JSRuntime *rt, uint
 {
     /*
      * We assume that the compiler never uses sub-word alignment to store
      * pointers and does not tag pointers on its own. Additionally, the value
      * representation for all values and the jsid representation for GC-things
      * do not touch the low two bits. Thus any word with the low two bits set
      * is not a valid GC-thing.
      */
-    JS_STATIC_ASSERT(JSID_TYPE_STRING == 0 && JSID_TYPE_OBJECT == 4);
+    JS_STATIC_ASSERT(JSID_TYPE_STRING == 0 && JSID_TYPE_SYMBOL == 4);
     if (w & 0x3)
         return CGCT_LOWBITSET;
 
     /*
      * An object jsid has its low bits tagged. In the value representation on
      * 64-bit, the high bits are tagged.
      */
     const uintptr_t JSID_PAYLOAD_MASK = ~uintptr_t(JSID_TYPE_MASK);
--- a/js/src/gdb/tests/test-jsid.cpp
+++ b/js/src/gdb/tests/test-jsid.cpp
@@ -1,27 +1,34 @@
 #include "gdb-tests.h"
 #include "jsapi.h"
 
 FRAGMENT(jsid, simple) {
   JS::Rooted<JSString *> string(cx, JS_NewStringCopyZ(cx, "moon"));
   JS::Rooted<JSString *> interned(cx, JS_InternJSString(cx, string));
   JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned));
   jsid int_id = INT_TO_JSID(1729);
+  JS::Rooted<jsid> unique_symbol_id(
+      cx, SYMBOL_TO_JSID(JS::NewSymbol(cx, interned)));
+  JS::Rooted<jsid> registry_symbol_id(
+      cx, SYMBOL_TO_JSID(JS::GetSymbolFor(cx, interned)));
+  JS::Rooted<jsid> well_known_symbol_id(
+      cx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator)));
   jsid void_id = JSID_VOID;
-  JS::Rooted<jsid> object_id(cx, OBJECT_TO_JSID(JS::CurrentGlobalOrNull(cx)));
+  jsid empty_id = JSID_EMPTY;
 
   breakpoint();
 
-  (void) string;
-  (void) interned;
   (void) string_id;
   (void) int_id;
+  (void) unique_symbol_id;
+  (void) registry_symbol_id;
+  (void) well_known_symbol_id;
   (void) void_id;
-  (void) object_id;
+  (void) empty_id;
 }
 
 void
 jsid_handles(JS::Handle<jsid> jsid_handle,
              JS::MutableHandle<jsid> mutable_jsid_handle)
 {
   // Prevent the linker from unifying this function with others that are
   // equivalent in machine code but not type.
--- a/js/src/gdb/tests/test-jsid.py
+++ b/js/src/gdb/tests/test-jsid.py
@@ -1,15 +1,19 @@
 # Tests for jsid pretty-printing
 
 assert_subprinter_registered('SpiderMonkey', 'jsid')
 
 run_fragment('jsid.simple')
 
 assert_pretty('string_id', '$jsid("moon")')
 assert_pretty('int_id', '$jsid(1729)')
+unique_symbol_pretty = str(gdb.parse_and_eval('unique_symbol_id')).split('@')[0]
+assert_eq(unique_symbol_pretty, '$jsid(Symbol("moon"))')
+assert_pretty('registry_symbol_id', '$jsid(Symbol.for("moon"))')
+assert_pretty('well_known_symbol_id', '$jsid(Symbol.iterator)')
 assert_pretty('void_id', 'JSID_VOID')
-assert_pretty('object_id', '$jsid((JSObject *)  [object global] delegate)')
+assert_pretty('empty_id', 'JSID_EMPTY')
 
 run_fragment('jsid.handles')
 
 assert_pretty('jsid_handle',         '$jsid("shovel")')
 assert_pretty('mutable_jsid_handle', '$jsid("shovel")')
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -537,29 +537,27 @@ class MacroAssembler : public MacroAssem
 
     void Push(jsid id, Register scratchReg) {
         if (JSID_IS_GCTHING(id)) {
             // If we're pushing a gcthing, then we can't just push the tagged jsid
             // value since the GC won't have any idea that the push instruction
             // carries a reference to a gcthing.  Need to unpack the pointer,
             // push it using ImmGCPtr, and then rematerialize the id at runtime.
 
-            // double-checking this here to ensure we don't lose sync
-            // with implementation of JSID_IS_GCTHING.
-            if (JSID_IS_OBJECT(id)) {
-                JSObject *obj = JSID_TO_OBJECT(id);
-                movePtr(ImmGCPtr(obj), scratchReg);
-                JS_ASSERT(((size_t)obj & JSID_TYPE_MASK) == 0);
-                orPtr(Imm32(JSID_TYPE_OBJECT), scratchReg);
-                Push(scratchReg);
-            } else {
+            if (JSID_IS_STRING(id)) {
                 JSString *str = JSID_TO_STRING(id);
                 JS_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0);
                 JS_ASSERT(JSID_TYPE_STRING == 0x0);
                 Push(ImmGCPtr(str));
+            } else {
+                MOZ_ASSERT(JSID_IS_SYMBOL(id));
+                JS::Symbol *sym = JSID_TO_SYMBOL(id);
+                movePtr(ImmGCPtr(sym), scratchReg);
+                orPtr(Imm32(JSID_TYPE_SYMBOL), scratchReg);
+                Push(scratchReg);
             }
         } else {
             Push(ImmWord(JSID_BITS(id)));
         }
     }
 
     void Push(TypedOrValueRegister v) {
         if (v.hasValue()) {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1072,19 +1072,19 @@ JS_WrapValue(JSContext *cx, MutableHandl
 JS_PUBLIC_API(bool)
 JS_WrapId(JSContext *cx, JS::MutableHandleId idp)
 {
   AssertHeapIsIdle(cx);
   CHECK_REQUEST(cx);
   jsid id = idp.get();
   if (JSID_IS_STRING(id))
       JS::ExposeGCThingToActiveJS(JSID_TO_STRING(id), JSTRACE_STRING);
-  else if (JSID_IS_OBJECT(id))
-      JS::ExposeGCThingToActiveJS(JSID_TO_OBJECT(id), JSTRACE_OBJECT);
-  return cx->compartment()->wrapId(cx, idp.address());
+  else if (JSID_IS_SYMBOL(id))
+      JS::ExposeGCThingToActiveJS(JSID_TO_STRING(id), JSTRACE_STRING);
+  return true;
 }
 
 /*
  * Identity remapping. Not for casual consumers.
  *
  * Normally, an object's contents and its identity are inextricably linked.
  * Identity is determined by the address of the JSObject* in the heap, and
  * the contents are what is located at that address. Transplanting allows these
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -121,29 +121,17 @@ class CompartmentChecker
             check(arr[i]);
     }
 
     void check(const CallArgs &args) {
         for (Value *p = args.base(); p != args.end(); ++p)
             check(*p);
     }
 
-    void check(jsid id) {
-        if (JSID_IS_OBJECT(id))
-            check(JSID_TO_OBJECT(id));
-    }
-
-    void check(JSIdArray *ida) {
-        if (ida) {
-            for (int i = 0; i < ida->length; i++) {
-                if (JSID_IS_OBJECT(ida->vector[i]))
-                    check(ida->vector[i]);
-            }
-        }
-    }
+    void check(jsid id) {}
 
     void check(JSScript *script) {
         if (script)
             check(script->compartment());
     }
 
     void check(InterpreterFrame *fp);
     void check(AbstractFramePtr frame);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2051,20 +2051,20 @@ JS_STATIC_ASSERT(sizeof(jsid) == sizeof(
 
 namespace js {
 
 static MOZ_ALWAYS_INLINE JS::Value
 IdToValue(jsid id)
 {
     if (JSID_IS_STRING(id))
         return JS::StringValue(JSID_TO_STRING(id));
-    if (MOZ_LIKELY(JSID_IS_INT(id)))
+    if (JSID_IS_INT(id))
         return JS::Int32Value(JSID_TO_INT(id));
-    if (MOZ_LIKELY(JSID_IS_OBJECT(id)))
-        return JS::ObjectValue(*JSID_TO_OBJECT(id));
+    if (JSID_IS_SYMBOL(id))
+        return JS::SymbolValue(JSID_TO_SYMBOL(id));
     JS_ASSERT(JSID_IS_VOID(id));
     return JS::UndefinedValue();
 }
 
 /*
  * If the embedder has registered a default JSContext callback, returns the
  * result of the callback. Otherwise, asserts that |rt| has exactly one
  * JSContext associated with it, and returns that context.
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -292,27 +292,24 @@ Shape::dump(JSContext *cx, FILE *fp) con
     JS_ASSERT(!JSID_IS_VOID(propid));
 
     if (JSID_IS_INT(propid)) {
         fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid));
     } else {
         JSLinearString *str;
         if (JSID_IS_ATOM(propid)) {
             str = JSID_TO_ATOM(propid);
+            if (!str)
+                fputs("<error>", fp);
+            else
+                FileEscapedString(fp, str, '"');
         } else {
-            JS_ASSERT(JSID_IS_OBJECT(propid));
-            Value v = IdToValue(propid);
-            JSString *s = ToStringSlow<NoGC>(cx, v);
-            fputs("object ", fp);
-            str = s ? s->ensureLinear(cx) : nullptr;
+            JS_ASSERT(JSID_IS_SYMBOL(propid));
+            JSID_TO_SYMBOL(propid)->dump(fp);
         }
-        if (!str)
-            fputs("<error>", fp);
-        else
-            FileEscapedString(fp, str, '"');
     }
 
     fprintf(fp, " g/s %p/%p slot %d attrs %x ",
             JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter),
             JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter),
             hasSlot() ? slot() : -1, attrs);
 
     if (attrs) {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -5300,22 +5300,18 @@ DebuggerObject_getOwnPropertyNames(JSCon
          jsid id = keys[i];
          if (JSID_IS_INT(id)) {
              JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
              if (!str)
                  return false;
              vals[i].setString(str);
          } else if (JSID_IS_ATOM(id)) {
              vals[i].setString(JSID_TO_STRING(id));
-             if (!cx->compartment()->wrap(cx, vals[i]))
-                 return false;
          } else {
-             vals[i].setObject(*JSID_TO_OBJECT(id));
-             if (!dbg->wrapDebuggeeValue(cx, vals[i]))
-                 return false;
+             MOZ_ASSERT_UNREACHABLE("GetPropertyNames must return only string and int jsids");
          }
     }
 
     JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
     if (!aobj)
         return false;
     args.rval().setObject(*aobj);
     return true;
--- a/js/src/vm/Id.cpp
+++ b/js/src/vm/Id.cpp
@@ -3,15 +3,15 @@
  * 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 "js/Id.h"
 #include "js/RootingAPI.h"
 
 const jsid JSID_VOID  = { size_t(JSID_TYPE_VOID) };
-const jsid JSID_EMPTY = { size_t(JSID_TYPE_OBJECT) };
+const jsid JSID_EMPTY = { size_t(JSID_TYPE_SYMBOL) };
 
 static const jsid voidIdValue = JSID_VOID;
 static const jsid emptyIdValue = JSID_EMPTY;
 const JS::HandleId JSID_VOIDHANDLE = JS::HandleId::fromMarkedLocation(&voidIdValue);
 const JS::HandleId JSID_EMPTYHANDLE = JS::HandleId::fromMarkedLocation(&emptyIdValue);
 
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -296,54 +296,41 @@ JS_SetWatchPoint(JSContext *cx, HandleOb
                  JSWatchPointHandler handler, HandleObject closure)
 {
     assertSameCompartment(cx, origobj);
 
     RootedObject obj(cx, GetInnerObject(origobj));
     if (!obj)
         return false;
 
-    RootedId propid(cx);
-
-    if (JSID_IS_INT(id)) {
-        propid = id;
-    } else if (JSID_IS_OBJECT(id)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH_PROP);
-        return false;
-    } else {
-        RootedValue val(cx, IdToValue(id));
-        if (!ValueToId<CanGC>(cx, val, &propid))
-            return false;
-    }
-
     if (!obj->isNative() || obj->is<TypedArrayObject>()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
                              obj->getClass()->name);
         return false;
     }
 
     /*
      * Use sparse indexes for watched objects, as dense elements can be written
      * to without checking the watchpoint map.
      */
     if (!JSObject::sparsifyDenseElements(cx, obj))
         return false;
 
-    types::MarkTypePropertyNonData(cx, obj, propid);
+    types::MarkTypePropertyNonData(cx, obj, id);
 
     WatchpointMap *wpmap = cx->compartment()->watchpointMap;
     if (!wpmap) {
         wpmap = cx->runtime()->new_<WatchpointMap>();
         if (!wpmap || !wpmap->init()) {
             js_ReportOutOfMemory(cx);
             return false;
         }
         cx->compartment()->watchpointMap = wpmap;
     }
-    return wpmap->watch(cx, obj, propid, handler, closure);
+    return wpmap->watch(cx, obj, id, handler, closure);
 }
 
 JS_PUBLIC_API(bool)
 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
                    JSWatchPointHandler *handlerp, JSObject **closurep)
 {
     assertSameCompartment(cx, obj, id);