Bug 645417, part 10 - Well-known symbols. r=terrence,r=efaust.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 23 Jun 2014 10:56:49 -0500
changeset 190277 d8e2600e9aa3abe063ef443781f13586f023687f
parent 190276 82afa573b28538462fef8d78c06e3fbd39033d5a
child 190278 edcb8617d09fe1f1eec5749e3c4e2912688fe49f
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersterrence, efaust
bugs645417, 918828
milestone33.0a1
Bug 645417, part 10 - Well-known symbols. r=terrence,r=efaust. At present there is only one, Symbol.iterator, and it is not hooked up to anything (this happens in bug 918828). ES6 defines 8 well-known symbols. Each one is attached to a feature, so we'll add the symbols as we add features. Symbol.create will appear when @@create semantics are implemented.
js/src/builtin/SymbolObject.cpp
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/RootMarking.cpp
js/src/jsapi-tests/testSymbol.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/tests/ecma_6/Symbol/well-known.js
js/src/vm/CommonPropertyNames.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Symbol.cpp
js/src/vm/Symbol.h
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -57,18 +57,31 @@ SymbolObject::initClass(JSContext *cx, H
     // 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) ||
+    if (!ctor)
+        return nullptr;
+
+    // Define the well-known symbol properties, such as Symbol.iterator.
+    ImmutablePropertyNamePtr *names = &cx->names().iterator;
+    RootedValue value(cx);
+    unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
+    WellKnownSymbols *wks = cx->runtime()->wellKnownSymbols;
+    for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
+        value.setSymbol(wks->get(i));
+        if (!DefineNativeProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
+            return nullptr;
+    }
+
+    if (!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;
 }
@@ -91,17 +104,17 @@ SymbolObject::construct(JSContext *cx, u
     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, false, desc));
+    RootedSymbol symbol(cx, JS::Symbol::new_(cx, JS::SymbolCode::UniqueSymbol, desc));
     if (!symbol)
         return false;
     args.rval().setSymbol(symbol);
     return true;
 }
 
 // ES6 rev 24 (2014 Apr 27) 19.4.2.2
 bool
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -603,16 +603,18 @@ class ImmutableTenuredPtr
     operator Handle<T>() const {
         return Handle<T>::fromMarkedLocation(&value);
     }
 
     void init(T ptr) {
         JS_ASSERT(ptr->isTenured());
         value = ptr;
     }
+
+    const T * address() { return &value; }
 };
 
 /*
  * A pre- and post-barriered heap pointer, for use inside the JS engine.
  *
  * Unlike HeapPtr<T>, it can be used in memory that is not managed by the GC,
  * i.e. in C++ containers.  It is, however, somewhat slower, so should only be
  * used in contexts where this ability is necessary.
@@ -821,16 +823,17 @@ typedef PreBarriered<Value> PreBarriered
 typedef RelocatablePtr<Value> RelocatableValue;
 typedef HeapPtr<Value> HeapValue;
 
 typedef PreBarriered<jsid> PreBarrieredId;
 typedef RelocatablePtr<jsid> RelocatableId;
 typedef HeapPtr<jsid> HeapId;
 
 typedef ImmutableTenuredPtr<PropertyName*> ImmutablePropertyNamePtr;
+typedef ImmutableTenuredPtr<JS::Symbol*> ImmutableSymbolPtr;
 
 typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject;
 typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
 typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
 typedef ReadBarriered<JSObject*> ReadBarrieredObject;
 typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
 typedef ReadBarriered<Shape*> ReadBarrieredShape;
 typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -146,16 +146,17 @@ AsGCMarker(JSTracer *trc)
 }
 
 template <typename T> bool ThingIsPermanentAtom(T *thing) { return false; }
 template <> bool ThingIsPermanentAtom<JSString>(JSString *str) { return str->isPermanentAtom(); }
 template <> bool ThingIsPermanentAtom<JSFlatString>(JSFlatString *str) { return str->isPermanentAtom(); }
 template <> bool ThingIsPermanentAtom<JSLinearString>(JSLinearString *str) { return str->isPermanentAtom(); }
 template <> bool ThingIsPermanentAtom<JSAtom>(JSAtom *atom) { return atom->isPermanent(); }
 template <> bool ThingIsPermanentAtom<PropertyName>(PropertyName *name) { return name->isPermanent(); }
+template <> bool ThingIsPermanentAtom<JS::Symbol>(JS::Symbol *sym) { return sym->isWellKnownSymbol(); }
 
 template<typename T>
 static inline void
 CheckMarkedThing(JSTracer *trc, T **thingp)
 {
     JS_ASSERT(trc);
     JS_ASSERT(thingp);
 
@@ -331,16 +332,40 @@ MarkPermanentAtom(JSTracer *trc, JSAtom 
         trc->callback(trc, &thing, JSTRACE_STRING);
         JS_ASSERT(thing == atom);
         trc->unsetTracingLocation();
     }
 
     trc->clearTracingDetails();
 }
 
+void
+MarkWellKnownSymbol(JSTracer *trc, JS::Symbol *sym)
+{
+    if (!sym)
+        return;
+
+    trc->setTracingName("wellKnownSymbols");
+
+    MOZ_ASSERT(sym->isWellKnownSymbol());
+    CheckMarkedThing(trc, &sym);
+    if (!trc->callback) {
+        // Permanent atoms are marked before well-known symbols.
+        MOZ_ASSERT(sym->description()->isMarked());
+        sym->markIfUnmarked();
+    } else {
+        void *thing = sym;
+        trc->callback(trc, &thing, JSTRACE_SYMBOL);
+        MOZ_ASSERT(thing == sym);
+        trc->unsetTracingLocation();
+    }
+
+    trc->clearTracingDetails();
+}
+
 } /* namespace gc */
 } /* namespace js */
 
 template <typename T>
 static void
 MarkRoot(JSTracer *trc, T **thingp, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
@@ -1211,23 +1236,34 @@ PushMarkStack(GCMarker *gcmarker, JSStri
      * using the explicit stack when navigating the rope tree to avoid
      * dealing with strings on the stack in drainMarkStack.
      */
     if (str->markIfUnmarked())
         ScanString(gcmarker, str);
 }
 
 static inline void
+ScanSymbol(GCMarker *gcmarker, JS::Symbol *sym)
+{
+    if (JSString *desc = sym->description())
+        PushMarkStack(gcmarker, desc);
+}
+
+static inline void
 PushMarkStack(GCMarker *gcmarker, JS::Symbol *sym)
 {
+    // Well-known symbols might not be associated with this runtime.
+    if (sym->isWellKnownSymbol())
+        return;
+
     JS_COMPARTMENT_ASSERT_SYM(gcmarker->runtime(), sym);
-    if (sym->markIfUnmarked()) {
-        if (JSString *desc = sym->description())
-            ScanString(gcmarker, desc);
-    }
+    JS_ASSERT(!IsInsideNursery(sym));
+
+    if (sym->markIfUnmarked())
+        ScanSymbol(gcmarker, sym);
 }
 
 void
 gc::MarkChildren(JSTracer *trc, JSObject *obj)
 {
     obj->markChildren(trc);
 }
 
@@ -1615,16 +1651,24 @@ GCMarker::processMarkStackTop(SliceBudge
             JSObject *obj2 = &v.toObject();
             JS_COMPARTMENT_ASSERT(runtime(), obj2);
             JS_ASSERT(obj->compartment() == obj2->compartment());
             if (obj2->markIfUnmarked(getMarkColor())) {
                 pushValueArray(obj, vp, end);
                 obj = obj2;
                 goto scan_obj;
             }
+        } else if (v.isSymbol()) {
+            JS::Symbol *sym = v.toSymbol();
+            if (!sym->isWellKnownSymbol()) {
+                JS_COMPARTMENT_ASSERT_SYM(runtime(), sym);
+                MOZ_ASSERT(runtime()->isAtomsZone(sym->zone()) || sym->zone() == obj->zone());
+                if (sym->markIfUnmarked())
+                    ScanSymbol(this, sym);
+            }
         }
     }
     return;
 
   scan_obj:
     {
         JS_COMPARTMENT_ASSERT(runtime(), obj);
 
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -123,16 +123,19 @@ DeclMarker(String, PropertyName)
 DeclMarker(Symbol, JS::Symbol)
 DeclMarker(TypeObject, types::TypeObject)
 
 #undef DeclMarker
 
 void
 MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name);
 
+void
+MarkWellKnownSymbol(JSTracer *trc, JS::Symbol *sym);
+
 /* Return true if the pointer is nullptr, or if it is a tagged pointer to
  * nullptr.
  */
 MOZ_ALWAYS_INLINE bool
 IsNullTaggedPointer(void *p)
 {
     return uintptr_t(p) < 32;
 }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -766,16 +766,17 @@ js::gc::GCRuntime::markRuntime(JSTracer 
         for (size_t i = 0; i < vec.length(); i++)
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
     if (!rt->isBeingDestroyed() && !trc->runtime()->isHeapMinorCollecting()) {
         if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()) {
             MarkPermanentAtoms(trc);
             MarkAtoms(trc);
+            MarkWellKnownSymbols(trc);
 #ifdef JS_ION
             jit::JitRuntime::Mark(trc);
 #endif
         }
     }
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
--- a/js/src/jsapi-tests/testSymbol.cpp
+++ b/js/src/jsapi-tests/testSymbol.cpp
@@ -50,8 +50,33 @@ BEGIN_TEST(testSymbol_GetSymbolFor)
 
     // But SymbolNew always produces a new distinct Symbol.
     CHECK(sym2 = NewSymbol(cx, desc));
     CHECK(sym2 != sym1);
 
     return true;
 }
 END_TEST(testSymbol_GetSymbolFor)
+
+BEGIN_TEST(testSymbol_GetWellKnownSymbol)
+{
+    using namespace JS;
+
+    Rooted<Symbol*> sym1(cx);
+    CHECK(sym1 = GetWellKnownSymbol(cx, SymbolCode::iterator));
+    RootedValue v(cx);
+    EVAL("Symbol.iterator", &v);
+    CHECK_SAME(v, SymbolValue(sym1));
+
+    // The description of a well-known symbol is as specified.
+    RootedString desc(cx);
+    CHECK(desc = JS_NewStringCopyZ(cx, "Symbol.iterator"));
+    CHECK_SAME(StringValue(GetSymbolDescription(sym1)), StringValue(desc));
+
+    // GetSymbolFor never returns a well-known symbol.
+    Rooted<Symbol*> sym2(cx);
+    CHECK(sym2 = GetSymbolFor(cx, desc));
+    CHECK(sym2 != sym1);
+
+    return true;
+}
+END_TEST(testSymbol_GetWellKnownSymbol)
+
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5621,17 +5621,17 @@ JS_EncodeStringToBuffer(JSContext *cx, J
 JS_PUBLIC_API(JS::Symbol *)
 JS::NewSymbol(JSContext *cx, HandleString description)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (description)
         assertSameCompartment(cx, description);
 
-    return Symbol::new_(cx, false, description);
+    return Symbol::new_(cx, SymbolCode::UniqueSymbol, description);
 }
 
 JS_PUBLIC_API(JS::Symbol *)
 JS::GetSymbolFor(JSContext *cx, HandleString key)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, key);
@@ -5640,16 +5640,28 @@ JS::GetSymbolFor(JSContext *cx, HandleSt
 }
 
 JS_PUBLIC_API(JSString *)
 JS::GetSymbolDescription(HandleSymbol symbol)
 {
     return symbol->description();
 }
 
+JS_PUBLIC_API(JS::SymbolCode)
+JS::GetSymbolCode(Handle<Symbol*> symbol)
+{
+    return symbol->code();
+}
+
+JS_PUBLIC_API(JS::Symbol *)
+JS::GetWellKnownSymbol(JSContext *cx, JS::SymbolCode which)
+{
+    return cx->runtime()->wellKnownSymbols->get(uint32_t(which));
+}
+
 JS_PUBLIC_API(bool)
 JS_Stringify(JSContext *cx, MutableHandleValue vp, HandleObject replacer,
              HandleValue space, JSONWriteCallback callback, void *data)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, replacer, space);
     StringBuffer sb(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -7,16 +7,17 @@
 /* JavaScript API. */
 
 #ifndef jsapi_h
 #define jsapi_h
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/RangedPtr.h"
+#include "mozilla/TypedEnum.h"
 
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include "jsalloc.h"
 #include "jspubtd.h"
@@ -4382,16 +4383,43 @@ GetSymbolFor(JSContext *cx, HandleString
  * Get the [[Description]] attribute of the given symbol.
  *
  * This function is infallible. If it returns null, that means the symbol's
  * [[Description]] is undefined.
  */
 JS_PUBLIC_API(JSString *)
 GetSymbolDescription(HandleSymbol symbol);
 
+/* Well-known symbols. */
+MOZ_BEGIN_ENUM_CLASS(SymbolCode, uint32_t)
+    iterator,                       // well-known Symbol.iterator
+    InSymbolRegistry = 0xfffffffe,  // created by Symbol.for() or JS::GetSymbolFor()
+    UniqueSymbol = 0xffffffff       // created by Symbol() or JS::NewSymbol()
+MOZ_END_ENUM_CLASS(SymbolCode)
+
+/* For use in loops that iterate over the well-known symbols. */
+const size_t WellKnownSymbolLimit = 1;
+
+/*
+ * Return the SymbolCode telling what sort of symbol `symbol` is.
+ *
+ * A symbol's SymbolCode never changes once it is created.
+ */
+JS_PUBLIC_API(SymbolCode)
+GetSymbolCode(Handle<Symbol*> symbol);
+
+/*
+ * Get one of the well-known symbols defined by ES6. A single set of well-known
+ * symbols is shared by all compartments in a JSRuntime.
+ *
+ * `which` must be in the range [0, WellKnownSymbolLimit).
+ */
+JS_PUBLIC_API(Symbol *)
+GetWellKnownSymbol(JSContext *cx, SymbolCode which);
+
 } /* namespace JS */
 
 /************************************************************************/
 /*
  * JSON functions
  */
 typedef bool (* JSONWriteCallback)(const jschar *buf, uint32_t len, void *data);
 
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -121,16 +121,17 @@ JSRuntime::initializeAtoms(JSContext *cx
     if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
         return false;
 
     if (parentRuntime) {
         staticStrings = parentRuntime->staticStrings;
         commonNames = parentRuntime->commonNames;
         emptyString = parentRuntime->emptyString;
         permanentAtoms = parentRuntime->permanentAtoms;
+        wellKnownSymbols = parentRuntime->wellKnownSymbols;
         return true;
     }
 
     permanentAtoms = cx->new_<AtomSet>();
     if (!permanentAtoms || !permanentAtoms->init(JS_STRING_HASH_COUNT))
         return false;
 
     staticStrings = cx->new_<StaticStrings>();
@@ -155,40 +156,53 @@ JSRuntime::initializeAtoms(JSContext *cx
         JSAtom *atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom);
         if (!atom)
             return false;
         names->init(atom->asPropertyName());
     }
     JS_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
 
     emptyString = commonNames->empty;
+
+    // Create the well-known symbols.
+    wellKnownSymbols = cx->new_<WellKnownSymbols>();
+    if (!wellKnownSymbols)
+        return false;
+
+    ImmutablePropertyNamePtr *descriptions = &commonNames->Symbol_iterator;
+    ImmutableSymbolPtr *symbols = reinterpret_cast<ImmutableSymbolPtr *>(wellKnownSymbols);
+    for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
+        JS::Symbol *symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]);
+        if (!symbol) {
+            js_ReportOutOfMemory(cx);
+            return false;
+        }
+        symbols[i].init(symbol);
+    }
+
     return true;
 }
 
 void
 JSRuntime::finishAtoms()
 {
-    if (atoms_)
-        js_delete(atoms_);
+    js_delete(atoms_);
 
     if (!parentRuntime) {
-        if (staticStrings)
-            js_delete(staticStrings);
-
-        if (commonNames)
-            js_delete(commonNames);
-
-        if (permanentAtoms)
-            js_delete(permanentAtoms);
+        js_delete(staticStrings);
+        js_delete(commonNames);
+        js_delete(permanentAtoms);
+        js_delete(wellKnownSymbols);
     }
 
     atoms_ = nullptr;
     staticStrings = nullptr;
     commonNames = nullptr;
     permanentAtoms = nullptr;
+    wellKnownSymbols = nullptr;
     emptyString = nullptr;
 }
 
 void
 js::MarkAtoms(JSTracer *trc)
 {
     JSRuntime *rt = trc->runtime();
     for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
@@ -223,16 +237,30 @@ js::MarkPermanentAtoms(JSTracer *trc)
 
             JSAtom *atom = entry.asPtr();
             MarkPermanentAtom(trc, atom, "permanent_table");
         }
     }
 }
 
 void
+js::MarkWellKnownSymbols(JSTracer *trc)
+{
+    JSRuntime *rt = trc->runtime();
+
+    if (rt->parentRuntime)
+        return;
+
+    if (WellKnownSymbols *wks = rt->wellKnownSymbols) {
+        for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++)
+            MarkWellKnownSymbol(trc, wks->get(i));
+    }
+}
+
+void
 JSRuntime::sweepAtoms()
 {
     if (!atoms_)
         return;
 
     for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
         AtomStateEntry entry = e.front();
         JSAtom *atom = entry.asPtr();
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -176,22 +176,25 @@ extern const char js_with_str[];
 
 namespace js {
 
 extern const char * const TypeStrings[];
 
 /*
  * Atom tracing and garbage collection hooks.
  */
-extern void
+void
 MarkAtoms(JSTracer *trc);
 
-extern void
+void
 MarkPermanentAtoms(JSTracer *trc);
 
+void
+MarkWellKnownSymbols(JSTracer *trc);
+
 /* N.B. must correspond to boolean tagging behavior. */
 enum InternBehavior
 {
     DoNotInternAtom = false,
     InternAtom = true
 };
 
 extern JSAtom *
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Symbol/well-known.js
@@ -0,0 +1,19 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Well-known symbols exist.
+assertEq(typeof Symbol.iterator, "symbol");
+
+// They are never in the registry.
+assertEq(Symbol.iterator !== Symbol.for("Symbol.iterator"), true);
+
+// They are shared across realms.
+if (typeof Realm === 'function')
+    throw new Error("please update this test to use Realms");
+if (typeof newGlobal === 'function') {
+    var g = newGlobal();
+    assertEq(Symbol.iterator, g.Symbol.iterator);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -98,17 +98,16 @@
     macro(isNaN, isNaN, "isNaN") \
     macro(isPrototypeOf, isPrototypeOf, "isPrototypeOf") \
     macro(iterate, iterate, "iterate") \
     macro(Infinity, Infinity, "Infinity") \
     macro(int8, int8, "int8") \
     macro(int16, int16, "int16") \
     macro(int32, int32, "int32") \
     macro(isExtensible, isExtensible, "isExtensible") \
-    macro(iterator, iterator, "iterator") \
     macro(iteratorIntrinsic, iteratorIntrinsic, "__iterator__") \
     macro(join, join, "join") \
     macro(keys, keys, "keys") \
     macro(lastIndex, lastIndex, "lastIndex") \
     macro(length, length, "length") \
     macro(let, let, "let") \
     macro(line, line, "line") \
     macro(lineNumber, lineNumber, "lineNumber") \
@@ -205,11 +204,23 @@
     /* Type names must be contiguous and ordered; see js::TypeName. */ \
     macro(undefined, undefined, "undefined") \
     macro(object, object, "object") \
     macro(function, function, "function") \
     macro(string, string, "string") \
     macro(number, number, "number") \
     macro(boolean, boolean, "boolean") \
     macro(null, null, "null") \
-    macro(symbol, symbol, "symbol")
+    macro(symbol, symbol, "symbol") \
+    /* Well-known atom names must be continuous and ordered, matching \
+     * enum JS::SymbolCode in jsapi.h. */ \
+    macro(iterator, iterator, "iterator") \
+    /* Same goes for the descriptions of the well-known symbols. */ \
+    macro(Symbol_create, Symbol_create, "Symbol.create") \
+    macro(Symbol_hasInstance, Symbol_hasInstance, "Symbol.hasInstance") \
+    macro(Symbol_isConcatSpreadable, Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") \
+    macro(Symbol_isRegExp, Symbol_isRegExp, "Symbol.isRegExp") \
+    macro(Symbol_iterator, Symbol_iterator, "Symbol.iterator") \
+    macro(Symbol_toPrimitive, Symbol_toPrimitive, "Symbol.toPrimitive") \
+    macro(Symbol_toStringTag, Symbol_toStringTag, "Symbol.toStringTag") \
+    macro(Symbol_unscopables, Symbol_unscopables, "Symbol.unscopables")
 
 #endif /* vm_CommonPropertyNames_h */
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -206,16 +206,17 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
     keepAtoms_(0),
     trustedPrincipals_(nullptr),
     beingDestroyed_(false),
     atoms_(nullptr),
     atomsCompartment_(nullptr),
     staticStrings(nullptr),
     commonNames(nullptr),
     permanentAtoms(nullptr),
+    wellKnownSymbols(nullptr),
     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
     preserveWrapperCallback(nullptr),
     jitSupportsFloatingPoint(false),
     ionPcScriptCache(nullptr),
     threadPool(this),
     defaultJSContextCallback(nullptr),
     ctypesActivityCallback(nullptr),
     forkJoinWarmup(0),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -409,16 +409,36 @@ struct JSAtomState
 #undef PROPERTYNAME_FIELD
 #define PROPERTYNAME_FIELD(name, code, init, clasp) js::ImmutablePropertyNamePtr name;
     JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
 #undef PROPERTYNAME_FIELD
 };
 
 namespace js {
 
+/*
+ * Storage for well-known symbols. It's a separate struct from the Runtime so
+ * that it can be shared across multiple runtimes. As in JSAtomState, each
+ * field is a smart pointer that's immutable once initialized.
+ * `rt->wellKnownSymbols.iterator` is convertible to Handle<Symbol*>.
+ *
+ * Well-known symbols are never GC'd. The description() of each well-known
+ * symbol is a permanent atom.
+ */
+struct WellKnownSymbols
+{
+    js::ImmutableSymbolPtr iterator;
+
+    ImmutableSymbolPtr &get(size_t i) {
+        MOZ_ASSERT(i < JS::WellKnownSymbolLimit);
+        ImmutableSymbolPtr *symbols = reinterpret_cast<ImmutableSymbolPtr *>(this);
+        return symbols[i];
+    }
+};
+
 #define NAME_OFFSET(name)       offsetof(JSAtomState, name)
 
 inline HandlePropertyName
 AtomStateOffsetToName(const JSAtomState &atomState, size_t offset)
 {
     return *reinterpret_cast<js::ImmutablePropertyNamePtr *>((char*)&atomState + offset);
 }
 
@@ -1211,16 +1231,20 @@ struct JSRuntime : public JS::shadow::Ru
     // Cached pointers to various permanent property names.
     JSAtomState *commonNames;
 
     // All permanent atoms in the runtime, other than those in staticStrings.
     js::AtomSet *permanentAtoms;
 
     bool transformToPermanentAtoms();
 
+    // Cached well-known symbols (ES6 rev 24 6.1.5.1). Like permanent atoms,
+    // these are shared with the parentRuntime, if any.
+    js::WellKnownSymbols *wellKnownSymbols;
+
     const JSWrapObjectCallbacks            *wrapObjectCallbacks;
     js::PreserveWrapperCallback            preserveWrapperCallback;
 
     // Table of bytecode and other data that may be shared across scripts
     // within the runtime. This may be modified by threads with an
     // ExclusiveContext and requires a lock.
   private:
     js::ScriptDataTable scriptDataTable_;
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/Symbol.cpp
@@ -13,45 +13,45 @@
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 
 using JS::Symbol;
 using namespace js;
 
 Symbol *
-Symbol::newInternal(ExclusiveContext *cx, bool inRegistry, JSAtom *description)
+Symbol::newInternal(ExclusiveContext *cx, JS::SymbolCode code, JSAtom *description)
 {
     MOZ_ASSERT(cx->compartment() == cx->atomsCompartment());
     MOZ_ASSERT(cx->atomsCompartment()->runtimeFromAnyThread()->currentThreadHasExclusiveAccess());
 
     // Following js::AtomizeString, we grudgingly forgo last-ditch GC here.
     Symbol *p = gc::AllocateNonObject<Symbol, NoGC>(cx);
     if (!p) {
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
-    return new (p) Symbol(inRegistry, description);
+    return new (p) Symbol(code, description);
 }
 
 Symbol *
-Symbol::new_(ExclusiveContext *cx, bool inRegistry, JSString *description)
+Symbol::new_(ExclusiveContext *cx, JS::SymbolCode code, JSString *description)
 {
     RootedAtom atom(cx);
     if (description) {
         atom = AtomizeString(cx, description);
         if (!atom)
             return nullptr;
     }
 
     // Lock to allocate. If symbol allocation becomes a bottleneck, this can
     // probably be replaced with an assertion that we're on the main thread.
     AutoLockForExclusiveAccess lock(cx);
     AutoCompartment ac(cx, cx->atomsCompartment());
-    return newInternal(cx, inRegistry, atom);
+    return newInternal(cx, code, atom);
 }
 
 Symbol *
 Symbol::for_(js::ExclusiveContext *cx, HandleString description)
 {
     JSAtom *atom = AtomizeString(cx, description);
     if (!atom)
         return nullptr;
@@ -59,17 +59,17 @@ Symbol::for_(js::ExclusiveContext *cx, H
     AutoLockForExclusiveAccess lock(cx);
 
     SymbolRegistry &registry = cx->symbolRegistry();
     SymbolRegistry::AddPtr p = registry.lookupForAdd(atom);
     if (p)
         return *p;
 
     AutoCompartment ac(cx, cx->atomsCompartment());
-    Symbol *sym = newInternal(cx, true, atom);
+    Symbol *sym = newInternal(cx, SymbolCode::InSymbolRegistry, atom);
     if (!sym)
         return nullptr;
 
     // p is still valid here because we have held the lock since the
     // lookupForAdd call, and newInternal can't GC.
     if (!registry.add(p, sym)) {
         // SystemAllocPolicy does not report OOM.
         js_ReportOutOfMemory(cx);
--- a/js/src/vm/Symbol.h
+++ b/js/src/vm/Symbol.h
@@ -5,52 +5,53 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_Symbol_h
 #define vm_Symbol_h
 
 #include "mozilla/Attributes.h"
 
 #include "jsalloc.h"
+#include "jsapi.h"
 
 #include "gc/Barrier.h"
 
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 
 namespace JS {
 
 class Symbol : public js::gc::BarrieredCell<Symbol>
 {
   private:
-    // This is only a boolean for now, but a later patch in the stack changes
-    // it to an enum.
-    uint32_t inSymbolRegistry_;
+    SymbolCode code_;
     JSAtom *description_;
 
     // The minimum allocation size is sizeof(JSString): 16 bytes on 32-bit
     // architectures and 24 bytes on 64-bit.  8 bytes of padding makes Symbol
     // the minimum size on both.
     uint64_t unused2_;
 
-    Symbol(bool inRegistry, JSAtom *desc)
-        : inSymbolRegistry_(inRegistry), description_(desc) {}
+    Symbol(SymbolCode code, JSAtom *desc)
+        : code_(code), description_(desc) {}
 
     Symbol(const Symbol &) MOZ_DELETE;
     void operator=(const Symbol &) MOZ_DELETE;
 
     static Symbol *
-    newInternal(js::ExclusiveContext *cx, bool inRegistry, JSAtom *description);
+    newInternal(js::ExclusiveContext *cx, SymbolCode code, JSAtom *description);
 
   public:
-    static Symbol *new_(js::ExclusiveContext *cx, bool inRegistry, JSString *description);
+    static Symbol *new_(js::ExclusiveContext *cx, SymbolCode code, JSString *description);
     static Symbol *for_(js::ExclusiveContext *cx, js::HandleString description);
 
     JSAtom *description() const { return description_; }
-    bool isInSymbolRegistry() const { return inSymbolRegistry_; }
+    SymbolCode code() const { return code_; }
+
+    bool isWellKnownSymbol() const { return uint32_t(code_) < WellKnownSymbolLimit; }
 
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SYMBOL; }
     inline void markChildren(JSTracer *trc);
     inline void finalize(js::FreeOp *) {}
 };
 
 } /* namespace JS */