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 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, efaust
bugs645417, 918828
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 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 */