Bug 918828, part 1 - Change iteration code to call iterable[Symbol.iterator]() rather than iterable["@@iterator"](). r=Waldo with earlier review work by nbp.
authorJason Orendorff <jorendorff@mozilla.com>
Tue, 01 Jul 2014 21:01:21 -0500
changeset 213223 51a1fa4c521fef7fe83a15aeca12cd0dca356aea
parent 213222 da167ffa2060350f4271a272a696d6e94698ef67
child 213224 606dc149145e4d36f96103e1a029fa615537efe1
push id27745
push usercbook@mozilla.com
push dateFri, 31 Oct 2014 13:09:12 +0000
treeherdermozilla-central@6bd2071b373f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs918828
milestone36.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 918828, part 1 - Change iteration code to call iterable[Symbol.iterator]() rather than iterable["@@iterator"](). r=Waldo with earlier review work by nbp.
addon-sdk/source/lib/sdk/util/iteration.js
browser/components/customizableui/CustomizableUI.jsm
browser/devtools/debugger/debugger-panes.js
browser/devtools/framework/gDevTools.jsm
browser/devtools/shared/widgets/VariablesView.jsm
browser/devtools/shared/widgets/ViewHelpers.jsm
dom/bindings/Codegen.py
dom/bindings/test/test_sequence_detection.html
dom/html/test/test_formelements.html
dom/html/test/test_htmlcollection.html
dom/html/test/test_named_options.html
dom/html/test/test_rowscollection.html
js/src/builtin/Array.js
js/src/builtin/MapObject.cpp
js/src/builtin/Utilities.js
js/src/frontend/BytecodeEmitter.cpp
js/src/jit-test/lib/iteration.js
js/src/jit-test/tests/arguments/destructuring-exprbody.js
js/src/jit-test/tests/basic/expression-autopsy.js
js/src/jit-test/tests/collections/WeakSet-error.js
js/src/jit-test/tests/collections/iterator-proto-surfaces.js
js/src/jit-test/tests/for-of/next-3.js
js/src/jit-test/tests/for-of/semantics-08.js
js/src/jit-test/tests/for-of/string-iterator-surfaces.js
js/src/jsapi-tests/testForOfIterator.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsiter.cpp
js/src/jsstr.cpp
js/src/tests/ecma_6/Array/for_of_2.js
js/src/tests/ecma_6/Array/for_of_3.js
js/src/tests/ecma_6/Array/for_of_4.js
js/src/tests/ecma_6/Array/from_errors.js
js/src/tests/ecma_6/Array/from_iterable.js
js/src/tests/ecma_6/Array/from_proxy.js
js/src/tests/ecma_6/Array/from_string.js
js/src/tests/ecma_6/Generators/runtime.js
js/src/tests/ecma_6/Symbol/property-reflection.js
js/src/tests/js1_8/regress/regress-469625-03.js
js/src/tests/shell.js
js/src/vm/GeneratorObject.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/PIC.cpp
js/src/vm/PIC.h
js/src/vm/SelfHosting.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/Xdr.h
js/xpconnect/tests/chrome/test_xrayToJS.xul
toolkit/modules/Promise-backend.js
--- a/addon-sdk/source/lib/sdk/util/iteration.js
+++ b/addon-sdk/source/lib/sdk/util/iteration.js
@@ -2,30 +2,21 @@
  * 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/. */
 'use strict';
 
 module.metadata = {
   "stability": "experimental"
 };
 
-// This is known as @@iterator in the ES6 spec.  Until it is bound to
-// some well-known name, find the @@iterator object by expecting it as
-// the first property accessed on a for-of iterable.
-const iteratorSymbol = (function() {
-  try {
-    for (var _ of Proxy.create({get: function(_, name) { throw name; } }))
-      break;
-  } catch (name) {
-    return name;
-  }
-  throw new TypeError;
-})();
-
-exports.iteratorSymbol = iteratorSymbol;
+// This is known as @@iterator in the ES6 spec. In builds that have ES6
+// Symbols, use Symbol.iterator; otherwise use the legacy method name,
+// "@@iterator".
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+exports.iteratorSymbol = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
 
 // An adaptor that, given an object that is iterable with for-of, is
 // suitable for being bound to __iterator__ in order to make the object
 // iterable in the same way via for-in.
 function forInIterator() {
     for (let item of this)
         yield item;
 }
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -46,16 +46,23 @@ const kPrefWebIDEInNavbar            = "
  * of providing onViewShowing and onViewHiding event handlers.
  */
 const kSubviewEvents = [
   "ViewShowing",
   "ViewHiding"
 ];
 
 /**
+ * The method name to use for ES6 iteration. If Symbols are enabled in
+ * this build, use Symbol.iterator; otherwise "@@iterator".
+ */
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+const kIteratorSymbol = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
+
+/**
  * The current version. We can use this to auto-add new default widgets as necessary.
  * (would be const but isn't because of testing purposes)
  */
 let kVersion = 1;
 
 /**
  * gPalette is a map of every widget that CustomizableUI.jsm knows about, keyed
  * on their IDs.
@@ -2674,17 +2681,17 @@ this.CustomizableUI = {
 
 
   /**
    * An iteratable property of windows managed by CustomizableUI.
    * Note that this can *only* be used as an iterator. ie:
    *     for (let window of CustomizableUI.windows) { ... }
    */
   windows: {
-    "@@iterator": function*() {
+    *[kIteratorSymbol]() {
       for (let [window,] of gBuildWindows)
         yield window;
     }
   },
 
   /**
    * Add a listener object that will get fired for various events regarding
    * customization.
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -3255,20 +3255,25 @@ LineResults.prototype = {
   line: 0,
   _sourceResults: null,
   _store: null,
   _target: null
 };
 
 /**
  * A generator-iterator over the global, source or line results.
+ *
+ * The method name depends on whether symbols are enabled in
+ * this build. If so, use Symbol.iterator; otherwise "@@iterator".
  */
-GlobalResults.prototype["@@iterator"] =
-SourceResults.prototype["@@iterator"] =
-LineResults.prototype["@@iterator"] = function*() {
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
+GlobalResults.prototype[ITERATOR_SYMBOL] =
+SourceResults.prototype[ITERATOR_SYMBOL] =
+LineResults.prototype[ITERATOR_SYMBOL] = function*() {
   yield* this._store;
 };
 
 /**
  * Gets the item associated with the specified element.
  *
  * @param nsIDOMNode aElement
  *        The element used to identify the item.
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -21,16 +21,23 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
 
 const EventEmitter = devtools.require("devtools/toolkit/event-emitter");
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 const MAX_ORDINAL = 99;
 
 /**
+ * The method name to use for ES6 iteration. If symbols are enabled in this
+ * build, use Symbol.iterator; otherwise "@@iterator".
+ */
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
+
+/**
  * DevTools is a class that represents a set of developer tools, it holds a
  * set of tools and keeps track of open toolboxes in the browser.
  */
 this.DevTools = function DevTools() {
   this._tools = new Map();     // Map<toolId, tool>
   this._themes = new Map();    // Map<themeId, theme>
   this._toolboxes = new Map(); // Map<target, toolbox>
 
@@ -481,17 +488,17 @@ DevTools.prototype = {
     // Cleaning down the toolboxes: i.e.
     //   for (let [target, toolbox] of this._toolboxes) toolbox.destroy();
     // Is taken care of by the gDevToolsBrowser.forgetBrowserWindow
   },
 
   /**
    * Iterator that yields each of the toolboxes.
    */
-  '@@iterator': function*() {
+  *[ITERATOR_SYMBOL]() {
     for (let toolbox of this._toolboxes) {
       yield toolbox;
     }
   }
 };
 
 /**
  * gDevTools is a singleton that controls the Firefox Developer Tools.
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -13,16 +13,23 @@ const LAZY_EMPTY_DELAY = 150; // ms
 const LAZY_EXPAND_DELAY = 50; // ms
 const SCROLL_PAGE_SIZE_DEFAULT = 0;
 const APPEND_PAGE_SIZE_DEFAULT = 500;
 const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
 const PAGE_SIZE_MAX_JUMPS = 30;
 const SEARCH_ACTION_MAX_DELAY = 300; // ms
 const ITEM_FLASH_DURATION = 300 // ms
 
+/**
+ * The method name to use for ES6 iteration. If symbols are enabled in
+ * this build, use Symbol.iterator; otherwise "@@iterator".
+ */
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
@@ -3051,20 +3058,20 @@ Property.prototype = Heritage.extend(Var
     this._absoluteName = this.ownerView.absoluteName + "[\"" + this._nameString + "\"]";
     return this._absoluteName;
   }
 });
 
 /**
  * A generator-iterator over the VariablesView, Scopes, Variables and Properties.
  */
-VariablesView.prototype["@@iterator"] =
-Scope.prototype["@@iterator"] =
-Variable.prototype["@@iterator"] =
-Property.prototype["@@iterator"] = function*() {
+VariablesView.prototype[ITERATOR_SYMBOL] =
+Scope.prototype[ITERATOR_SYMBOL] =
+Variable.prototype[ITERATOR_SYMBOL] =
+Property.prototype[ITERATOR_SYMBOL] = function*() {
   yield* this._store;
 };
 
 /**
  * Forget everything recorded about added scopes, variables or properties.
  * @see VariablesView.commitHierarchy
  */
 VariablesView.prototype.clearHierarchy = function() {
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -8,16 +8,23 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const PANE_APPEARANCE_DELAY = 50;
 const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
 const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
 
+/**
+ * The method name to use for ES6 iteration. If symbols are enabled in
+ * this build, use Symbol.iterator; otherwise "@@iterator".
+ */
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
 
 this.EXPORTED_SYMBOLS = [
   "Heritage", "ViewHelpers", "WidgetMethods",
   "setNamedTimeout", "clearNamedTimeout",
@@ -1724,12 +1731,12 @@ this.WidgetMethods = {
   _headerText: "",
   _preferredValue: "",
   _cachedCommandDispatcher: null
 };
 
 /**
  * A generator-iterator over all the items in this container.
  */
-Item.prototype["@@iterator"] =
-WidgetMethods["@@iterator"] = function*() {
+Item.prototype[ITERATOR_SYMBOL] =
+WidgetMethods[ITERATOR_SYMBOL] = function*() {
   yield* this._itemsByElement.values();
 };
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2009,37 +2009,38 @@ class PropertyDefiner:
     def getControllingCondition(interfaceMember, descriptor):
         return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember,
                                                              "Pref"),
                                PropertyDefiner.getStringAttr(interfaceMember,
                                                              "Func"),
                                getAvailableInTestFunc(interfaceMember),
                                descriptor.checkPermissionsIndicesForMembers.get(interfaceMember.identifier.name))
 
-    def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+    def generatePrefableArray(self, array, name, specFormatter, specTerminator,
                               specType, getCondition, getDataTuple, doIdArrays):
         """
         This method generates our various arrays.
 
         array is an array of interface members as passed to generateArray
 
         name is the name as passed to generateArray
 
-        specTemplate is a template for each entry of the spec array
+        specFormatter is a function that takes a single argument, a tuple,
+          and returns a string, a spec array entry
 
         specTerminator is a terminator for the spec array (inserted every time
           our controlling pref changes and at the end of the array)
 
         specType is the actual typename of our spec
 
         getCondition is a callback function that takes an array entry and
           returns the corresponding MemberCondition.
 
         getDataTuple is a callback function that takes an array entry and
-          returns a tuple suitable for substitution into specTemplate.
+          returns a tuple suitable to be passed to specFormatter.
         """
 
         # We want to generate a single list of specs, but with specTerminator
         # inserted at every point where the pref name controlling the member
         # changes.  That will make sure the order of the properties as exposed
         # on the interface and interface prototype objects does not change when
         # pref control is added to members while still allowing us to define all
         # the members in the smallest number of JSAPI calls.
@@ -2072,17 +2073,17 @@ class PropertyDefiner:
             curCondition = getCondition(member, self.descriptor)
             if lastCondition != curCondition:
                 # Terminate previous list
                 specs.append(specTerminator)
                 # And switch to our new pref
                 switchToCondition(self, curCondition)
                 lastCondition = curCondition
             # And the actual spec
-            specs.append(specTemplate % getDataTuple(member))
+            specs.append(specFormatter(getDataTuple(member)))
         specs.append(specTerminator)
         prefableSpecs.append("  { false, nullptr }")
 
         specType = "const " + specType
         arrays = fill(
             """
             static ${specType} ${name}_specs[] = {
             ${specs}
@@ -2349,19 +2350,25 @@ class MethodDefiner(PropertyDefiner):
                     if m.get("returnsPromise", False):
                         jitinfo = "&%s_methodinfo" % accessor
                         accessor = "StaticMethodPromiseWrapper"
                     else:
                         jitinfo = "nullptr"
 
             return (m["name"], accessor, jitinfo, m["length"], flags(m), selfHostedName)
 
+        def formatSpec(fields):
+            if fields[0].startswith("@@"):
+                fields = (fields[0][2:],) + fields[1:]
+                return '  JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)' % fields
+            return '  JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields
+
         return self.generatePrefableArray(
             array, name,
-            '  JS_FNSPEC("%s", %s, %s, %s, %s, %s)',
+            formatSpec,
             '  JS_FS_END',
             'JSFunctionSpec',
             condition, specData, doIdArrays)
 
 
 def IsCrossOriginWritable(attr, descriptor):
     """
     Return whether the IDLAttribute in question is cross-origin writable on the
@@ -2455,17 +2462,17 @@ class AttrDefiner(PropertyDefiner):
                    (accessor, jitinfo)
 
         def specData(attr):
             return (attr.identifier.name, flags(attr), getter(attr),
                     setter(attr))
 
         return self.generatePrefableArray(
             array, name,
-            '  { "%s", %s, %s, %s}',
+            lambda fields: '  { "%s", %s, %s, %s}' % fields,
             '  JS_PS_END',
             'JSPropertySpec',
             PropertyDefiner.getControllingCondition, specData, doIdArrays)
 
 
 class ConstDefiner(PropertyDefiner):
     """
     A class for definining constants on the interface object
@@ -2483,17 +2490,17 @@ class ConstDefiner(PropertyDefiner):
             return ""
 
         def specData(const):
             return (const.identifier.name,
                     convertConstIDLValueToJSVal(const.value))
 
         return self.generatePrefableArray(
             array, name,
-            '  { "%s", %s }',
+            lambda fields: '  { "%s", %s }' % fields,
             '  { 0, JS::UndefinedValue() }',
             'ConstantSpec',
             PropertyDefiner.getControllingCondition, specData, doIdArrays)
 
 
 class PropertyArrays():
     def __init__(self, descriptor):
         self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
--- a/dom/bindings/test/test_sequence_detection.html
+++ b/dom/bindings/test/test_sequence_detection.html
@@ -11,28 +11,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
 
   /** Test for Bug 1066432 **/
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
     var testInterfaceJS = new TestInterfaceJS();
     ok(testInterfaceJS, "got a TestInterfaceJS object");
 
-    var nonIterableObject = { "@@iterator": 5 };
-    if (typeof Symbol === "function") {
-      // Make this test fail if Symbol.iterator is correctly implemented.
-      // This is here to make sure this test is updated when bug 918828 lands,
-      // at which point the correct code will be:
-      //   var JS_HAS_SYMBOLS = typeof Symbol === "function";
-      //   var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
-      //   var nonIterableObject = { [std_iterator]: 5};
-      // Otherwise, fixing bug 918828 would cause this test to silently stop
-      // testing what it's supposed to be testing.
-      nonIterableObject[Symbol.iterator] = Array.prototype[Symbol.iterator];
-    }
+    var JS_HAS_SYMBOLS = typeof Symbol === "function";
+    var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
+    var nonIterableObject = {[std_iterator]: 5};
 
     try {
       testInterfaceJS.testSequenceOverload(nonIterableObject);
       ok(false, "Should have thrown in the overload case");  // see long comment above!
     } catch (e) {
       ise(e.name, "TypeError", "Should get a TypeError for the overload case");
       ok(e.message.contains("not iterable"),
          "Should have a message about being non-iterable in the overload case");
--- a/dom/html/test/test_formelements.html
+++ b/dom/html/test/test_formelements.html
@@ -27,27 +27,33 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 772869 **/
 var x = $("f").elements;
 x.something = "another";
 names = [];
 for (var name in x) {
   names.push(name);
 }
-is(names.length, 10, "Should have 10 enumerated names");
+var JS_HAS_SYMBOLS = typeof Symbol === "function";
+is(names.length, JS_HAS_SYMBOLS ? 9 : 10,
+   "Should have 9 enumerated names (or 10 with '@@iterator')");
 is(names[0], "0", "Enum entry 1");
 is(names[1], "1", "Enum entry 2");
 is(names[2], "2", "Enum entry 3");
 is(names[3], "3", "Enum entry 4");
 is(names[4], "4", "Enum entry 5");
 is(names[5], "something", "Enum entry 6");
 is(names[6], "namedItem", "Enum entry 7");
 is(names[7], "item", "Enum entry 8");
-is(names[8], "@@iterator", "Enum entry 9");
-is(names[9], "length", "Enum entry 10");
+if (JS_HAS_SYMBOLS) {
+  is(names[8], "length", "Enum entry 9");
+} else {
+  is(names[8], "@@iterator", "Enum entry 9");
+  is(names[9], "length", "Enum entry 10");
+}
 
 names = Object.getOwnPropertyNames(x);
 is(names.length, 10, "Should have 10 items");
 // Now sort entries 5 through 8, for comparison purposes.  We don't sort the
 // whole array, because we want to make sure the ordering between the parts
 // is correct
 temp = names.slice(5, 9);
 temp.sort();
--- a/dom/html/test/test_htmlcollection.html
+++ b/dom/html/test/test_htmlcollection.html
@@ -23,26 +23,32 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 772869 **/
 var x = document.getElementsByClassName("foo");
 x.something = "another";
 var names = [];
 for (var name in x) {
   names.push(name);
 }
-is(names.length, 9, "Should have 9 enumerated names");
+var JS_HAS_SYMBOLS = typeof Symbol === "function";
+is(names.length, JS_HAS_SYMBOLS ? 8 : 9,
+   "Should have 8 enumerated names (or 9 with '@@iterator')");
 is(names[0], "0", "Enum entry 1")
 is(names[1], "1", "Enum entry 2")
 is(names[2], "2", "Enum entry 3")
 is(names[3], "3", "Enum entry 4")
 is(names[4], "something", "Enum entry 5")
 is(names[5], "item", "Enum entry 6")
 is(names[6], "namedItem", "Enum entry 7")
-is(names[7], "@@iterator", "Enum entry 8")
-is(names[8], "length", "Enum entry 9")
+if (JS_HAS_SYMBOLS) {
+  is(names[7], "length", "Enum entry 8");
+} else {
+  is(names[7], "@@iterator", "Enum entry 8");
+  is(names[8], "length", "Enum entry 9");
+}
 
 names = Object.getOwnPropertyNames(x);
 is(names.length, 9, "Should have 9 items");
 is(names[0], "0", "Entry 1")
 is(names[1], "1", "Entry 2")
 is(names[2], "2", "Entry 3")
 is(names[3], "3", "Entry 4")
 is(names[4], "x", "Entry 5")
--- a/dom/html/test/test_named_options.html
+++ b/dom/html/test/test_named_options.html
@@ -37,26 +37,30 @@ is(names[5], "y", "Entry 6")
 is(names[6], "z", "Entry 7")
 is(names[7], "w", "Entry 8")
 is(names[8], "loopy", "Entry 9")
 
 var names2 = [];
 for (var name in opt) {
   names2.push(name);
 }
-is(names2.length, 12, "Should have twelve enumerated names");
+var JS_HAS_SYMBOLS = typeof Symbol === "function";
+is(names2.length, JS_HAS_SYMBOLS ? 11 : 12,
+   "Should have eleven enumerated names (or twelve with '@@iterator')");
 is(names2[0], "0", "Enum entry 1")
 is(names2[1], "1", "Enum entry 2")
 is(names2[2], "2", "Enum entry 3")
 is(names2[3], "3", "Enum entry 4")
 is(names2[4], "loopy", "Enum entry 5")
 is(names2[5], "add", "Enum entrry 6")
 is(names2[6], "remove", "Enum entry 7")
 is(names2[7], "length", "Enum entry 8")
 is(names2[8], "selectedIndex", "Enum entry 9")
 is(names2[9], "item", "Enum entry 10")
 is(names2[10], "namedItem", "Enum entry 11")
-is(names2[11], "@@iterator", "Enum entry 12")
+if (!JS_HAS_SYMBOLS) {
+  is(names2[11], "@@iterator", "Enum entry 12");
+}
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/test/test_rowscollection.html
+++ b/dom/html/test/test_rowscollection.html
@@ -33,28 +33,34 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 772869 **/
 var x = $("f").rows;
 x.something = "another";
 var names = [];
 for (var name in x) {
   names.push(name);
 }
-is(names.length, 11, "Should have 11 enumerated names");
+var JS_HAS_SYMBOLS = typeof Symbol === "function";
+is(names.length, JS_HAS_SYMBOLS ? 10 : 11,
+   "Should have 10 enumerated names (or 11 with '@@iterator')");
 is(names[0], "0", "Enum entry 1")
 is(names[1], "1", "Enum entry 2")
 is(names[2], "2", "Enum entry 3")
 is(names[3], "3", "Enum entry 4")
 is(names[4], "4", "Enum entry 5")
 is(names[5], "5", "Enum entry 6")
 is(names[6], "something", "Enum entry 7")
 is(names[7], "item", "Enum entry 8")
 is(names[8], "namedItem", "Enum entry 9")
-is(names[9], "@@iterator", "Enum entry 10")
-is(names[10], "length", "Enum entry 11")
+if (JS_HAS_SYMBOLS) {
+  is(names[9], "length", "Enum entry 10");
+} else {
+  is(names[9], "@@iterator", "Enum entry 10");
+  is(names[10], "length", "Enum entry 11");
+}
 
 names = Object.getOwnPropertyNames(x);
 is(names.length, 11, "Should have 11 items");
 is(names[0], "0", "Entry 1")
 is(names[1], "1", "Entry 2")
 is(names[2], "2", "Entry 3")
 is(names[3], "3", "Entry 4")
 is(names[4], "4", "Entry 5")
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -673,17 +673,17 @@ function ArrayFrom(arrayLike, mapfn=unde
     var mapping = (mapfn !== undefined);
     if (mapping && !IsCallable(mapfn))
         ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn));
 
     // All elements defined by this algorithm have the same attrs:
     var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE;
 
     // Steps 6-8.
-    var usingIterator = items["@@iterator"];
+    var usingIterator = items[std_iterator];
     if (usingIterator !== undefined) {
         // Steps 8.a-c.
         var A = IsConstructor(C) ? new C() : [];
 
         // Steps 8.d-e.
         var iterator = callFunction(usingIterator, items);
 
         // Step 8.f.
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -873,17 +873,17 @@ const Class MapIteratorObject::class_ = 
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     MapIteratorObject::finalize
 };
 
 const JSFunctionSpec MapIteratorObject::methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
 };
 
 inline ValueMap::Range *
 MapIteratorObject::range()
 {
     return static_cast<ValueMap::Range *>(getSlot(RangeSlot).toPrivate());
@@ -1071,18 +1071,24 @@ MapObject::initClass(JSContext *cx, JSOb
     if (proto) {
         // Define the "entries" method.
         JSFunction *fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0);
         if (!fun)
             return nullptr;
 
         // Define its alias.
         RootedValue funval(cx, ObjectValue(*fun));
+#if JS_HAS_SYMBOLS
+        RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
+        if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
+            return nullptr;
+#else
         if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, 0))
             return nullptr;
+#endif
     }
     return proto;
 }
 
 template <class Range>
 static void
 MarkKey(Range &r, const HashableValue &key, JSTracer *trc)
 {
@@ -1523,17 +1529,17 @@ const Class SetIteratorObject::class_ = 
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     SetIteratorObject::finalize
 };
 
 const JSFunctionSpec SetIteratorObject::methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
 };
 
 inline ValueSet::Range *
 SetIteratorObject::range()
 {
     return static_cast<ValueSet::Range *>(getSlot(RangeSlot).toPrivate());
@@ -1697,18 +1703,25 @@ SetObject::initClass(JSContext *cx, JSOb
         JSFunction *fun = JS_DefineFunction(cx, proto, "values", values, 0, 0);
         if (!fun)
             return nullptr;
 
         // Define its aliases.
         RootedValue funval(cx, ObjectValue(*fun));
         if (!JS_DefineProperty(cx, proto, "keys", funval, 0))
             return nullptr;
+
+#if JS_HAS_SYMBOLS
+        RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
+        if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
+            return nullptr;
+#else
         if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, 0))
             return nullptr;
+#endif
     }
     return proto;
 }
 
 
 bool
 SetObject::keys(JSContext *cx, HandleObject obj, JS::AutoValueVector *keys)
 {
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -26,17 +26,16 @@
 #include "SelfHostingDefines.h"
 
 // All C++-implemented standard builtins library functions used in self-hosted
 // code are installed via the std_functions JSFunctionSpec[] in
 // SelfHosting.cpp.
 //
 // The few items below here are either self-hosted or installing them under a
 // std_Foo name would require ugly contortions, so they just get aliased here.
-var std_iterator = '@@iterator'; // FIXME: Change to be a symbol.
 var std_Array_indexOf = ArrayIndexOf;
 // WeakMap is a bare constructor without properties or methods.
 var std_WeakMap = WeakMap;
 // StopIteration is a bare constructor without properties or methods.
 var std_StopIteration = StopIteration;
 
 
 /********** List specification type **********/
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4725,20 +4725,27 @@ EmitWith(ExclusiveContext *cx, BytecodeE
  * It will replace that stack value with the corresponding iterator
  */
 static bool
 EmitIterator(ExclusiveContext *cx, BytecodeEmitter *bce)
 {
     // Convert iterable to iterator.
     if (Emit1(cx, bce, JSOP_DUP) < 0)                          // OBJ OBJ
         return false;
+#ifdef JS_HAS_SYMBOLS
+    if (Emit2(cx, bce, JSOP_SYMBOL, jsbytecode(JS::SymbolCode::iterator)) < 0) // OBJ OBJ @@ITERATOR
+        return false;
+    if (!EmitElemOpBase(cx, bce, JSOP_CALLELEM))               // FN OBJ
+        return false;
+#else
     if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ @@ITERATOR
         return false;
     if (Emit1(cx, bce, JSOP_SWAP) < 0)                         // @@ITERATOR OBJ
         return false;
+#endif
     if (EmitCall(cx, bce, JSOP_CALL, 0) < 0)                   // ITER
         return false;
     CheckTypeSet(cx, bce, JSOP_CALL);
     return true;
 }
 
 static bool
 EmitForInOrOfVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *letDecl)
@@ -5593,27 +5600,18 @@ EmitYield(ExclusiveContext *cx, Bytecode
 static bool
 EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter, ParseNode *gen)
 {
     MOZ_ASSERT(bce->sc->isFunctionBox());
     MOZ_ASSERT(bce->sc->asFunctionBox()->isStarGenerator());
 
     if (!EmitTree(cx, bce, iter))                                // ITERABLE
         return false;
-
-    // Convert iterable to iterator.
-    if (Emit1(cx, bce, JSOP_DUP) < 0)                            // ITERABLE ITERABLE
-        return false;
-    if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // ITERABLE @@ITERATOR
-        return false;
-    if (Emit1(cx, bce, JSOP_SWAP) < 0)                           // @@ITERATOR ITERABLE
-        return false;
-    if (EmitCall(cx, bce, JSOP_CALL, 0, iter) < 0)               // ITER
-        return false;
-    CheckTypeSet(cx, bce, JSOP_CALL);
+    if (!EmitIterator(cx, bce))                                  // ITER
+        return false;
 
     // Initial send value is undefined.
     if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)                      // ITER RECEIVED
         return false;
 
     int depth = bce->stackDepth;
     MOZ_ASSERT(depth >= 2);
 
--- a/js/src/jit-test/lib/iteration.js
+++ b/js/src/jit-test/lib/iteration.js
@@ -1,17 +1,17 @@
 /* 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/. */
 
 
 load(libdir + "asserts.js");
 
-// FIXME: Import from std::iteration.
-const std_iterator = '@@iterator';
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+const std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : '@@iterator';
 
 if (typeof assertIteratorResult === 'undefined') {
     var assertIteratorResult = function assertIteratorResult(result, value, done) {
         assertEq(typeof result, "object");
         var expectedProps = ['done', 'value'];
         var actualProps = Object.getOwnPropertyNames(result);
         actualProps.sort(), expectedProps.sort();
         assertDeepEq(actualProps, expectedProps);
--- a/js/src/jit-test/tests/arguments/destructuring-exprbody.js
+++ b/js/src/jit-test/tests/arguments/destructuring-exprbody.js
@@ -1,7 +1,8 @@
 // See bug 763313
+load(libdir + "iteration.js");
 function f([a]) a
 var i = 0;
-var o = {'@@iterator': function () { i++; return {
+var o = {[std_iterator]: function () { i++; return {
   next: function () { i++; return {value: 42, done: false}; }}}};
 assertEq(f(o), 42);
 assertEq(i, 2);
--- a/js/src/jit-test/tests/basic/expression-autopsy.js
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -1,9 +1,10 @@
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 function check_one(expected, f, err) {
     var failed = true;
     try {
         f();
         failed = false;
     } catch (ex) {
         var s = ex.toString();
@@ -104,13 +105,16 @@ for (let tok of ["|", "^", "&", "==", "!
 check("o[!(o)]");
 check("o[~(o)]");
 check("o[+ (o)]");
 check("o[- (o)]");
 
 // A few one off tests
 check_one("6", (function () { 6() }), " is not a function");
 check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
-check_one("(intermediate value)['@@iterator'](...).next(...).value", function () { var [{ x }] = [null, {}]; }, " is null");
-check_one("(intermediate value)['@@iterator'](...).next(...).value", function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined");
+var ITERATOR = JS_HAS_SYMBOLS ? "(intermediate value)" : "'@@iterator'";
+check_one(`(intermediate value)[${ITERATOR}](...).next(...).value`,
+          function () { var [{ x }] = [null, {}]; }, " is null");
+check_one(`(intermediate value)[${ITERATOR}](...).next(...).value`,
+          function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined");
 
 // Check fallback behavior
 assertThrowsInstanceOf(function () { for (let x of undefined) {} }, TypeError);
--- a/js/src/jit-test/tests/collections/WeakSet-error.js
+++ b/js/src/jit-test/tests/collections/WeakSet-error.js
@@ -1,21 +1,22 @@
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 function testMethod(name) {
     var method = WeakSet.prototype[name];
 
     assertThrowsInstanceOf(function() { method.call(1); }, TypeError);
     assertThrowsInstanceOf(function() { method.call({}); }, TypeError);
     assertThrowsInstanceOf(function() { method.call(new WeakMap); }, TypeError);
     assertThrowsInstanceOf(function() { method.call(WeakSet.prototype); }, TypeError);
 }
 
 testMethod("has");
 testMethod("add");
 testMethod("delete");
 testMethod("clear");
 
 assertThrowsInstanceOf(function() { var ws = new WeakSet(); ws.add(1); }, TypeError);
-assertThrowsInstanceOf(function() { new WeakSet({"@@iterator": 2}) }, TypeError);
-assertEq(typeof []["@@iterator"], "function"); // Make sure we fail when @@iterator is removed
+assertThrowsInstanceOf(function() { new WeakSet({[std_iterator]: 2}) }, TypeError);
+assertEq(typeof [][std_iterator], "function");
 
 assertThrowsInstanceOf(function() { WeakSet(); }, TypeError);
--- a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
@@ -2,17 +2,18 @@
 
 load(libdir + "asserts.js");
 load(libdir + "iteration.js");
 
 function test(constructor) {
     var proto = Object.getPrototypeOf(constructor()[std_iterator]());
     var names = Object.getOwnPropertyNames(proto);
     names.sort();
-    assertDeepEq(names, [std_iterator, 'next']);
+    assertDeepEq(names, JS_HAS_SYMBOLS ? ['next'] : ['@@iterator', 'next']);
+    assertEq(proto.hasOwnProperty(std_iterator), true);
 
     var desc = Object.getOwnPropertyDescriptor(proto, 'next');
     assertEq(desc.configurable, true);
     assertEq(desc.enumerable, false);
     assertEq(desc.writable, true);
 
     assertEq(proto[std_iterator](), proto);
     assertIteratorDone(proto, undefined);
--- a/js/src/jit-test/tests/for-of/next-3.js
+++ b/js/src/jit-test/tests/for-of/next-3.js
@@ -3,11 +3,11 @@
 // compartment's .next method.
 
 // FIXME: 'next' should work cross-realm.  Bug 924059.
 
 load(libdir + "asserts.js");
 load(libdir + "iteration.js");
 
 var g = newGlobal();
-g.eval("var it = [1, 2]['" + std_iterator + "']();");
+g.eval(`var it = [1, 2][${uneval(std_iterator)}]();`);
 assertIteratorNext(g.it, 1);
 assertThrowsInstanceOf([][std_iterator]().next.bind(g.it), TypeError)
--- a/js/src/jit-test/tests/for-of/semantics-08.js
+++ b/js/src/jit-test/tests/for-of/semantics-08.js
@@ -1,9 +1,12 @@
 // Results from another compartment are correctly interpreted by for-of.
 
 load(libdir + "iteration.js");
 
 var g = newGlobal();
-var it = g.eval("({ '" + std_iterator + "': function () { return this; }, " +
-                "next: function () { return { done: true } } });");
-for (x of it)
+g.eval(`
+    var obj = {};
+    obj[${uneval(std_iterator)}] = function () { return this; };
+    obj.next = function () { return { done: true }; };
+`);
+for (x of g.obj)
     throw 'FAIL';
--- a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js
+++ b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js
@@ -52,17 +52,19 @@ assertBuiltinFunction(String.prototype, 
 // Test StringIterator.prototype surface
 var iter = ""[std_iterator]();
 var iterProto = Object.getPrototypeOf(iter);
 
 // StringIterator.prototype inherits from Object.prototype
 assertEq(Object.getPrototypeOf(iterProto), Object.prototype);
 
 // Own properties for StringIterator.prototype: "next" and @@iterator
-arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next", std_iterator].sort());
+arraysEqual(Object.getOwnPropertyNames(iterProto).sort(),
+            JS_HAS_SYMBOLS ? ["next"] : ["@@iterator", "next"]);
+assertEq(iterProto.hasOwnProperty(std_iterator), true);
 
 // StringIterator.prototype[@@iterator] is a built-in function
 assertBuiltinFunction(iterProto, std_iterator, 0);
 
 // StringIterator.prototype.next is a built-in function
 assertBuiltinFunction(iterProto, "next", 0);
 
 // StringIterator.prototype[@@iterator] is generic and returns |this|
--- a/js/src/jsapi-tests/testForOfIterator.cpp
+++ b/js/src/jsapi-tests/testForOfIterator.cpp
@@ -3,46 +3,42 @@
  */
 /* 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 "jsapi-tests/tests.h"
 
 #ifdef JS_HAS_SYMBOLS
-#define IF_JS_HAS_SYMBOLS(x) x
+#define STD_ITERATOR "Symbol.iterator"
 #else
-#define IF_JS_HAS_SYMBOLS(x)
+#define STD_ITERATOR "'@@iterator'"
 #endif
 
 BEGIN_TEST(testForOfIterator_basicNonIterable)
 {
     JS::RootedValue v(cx);
     // Hack to make it simple to produce an object that has a property
     // named Symbol.iterator.
-    EVAL("var obj = {'@@iterator': 5"
-         IF_JS_HAS_SYMBOLS(", [Symbol.iterator]: Array.prototype[Symbol.iterator]")
-         "}; obj;", &v);
+    EVAL("({[" STD_ITERATOR "]: 5})", &v);
     JS::ForOfIterator iter(cx);
     bool ok = iter.init(v);
     CHECK(!ok);
     JS_ClearPendingException(cx);
     return true;
 }
 END_TEST(testForOfIterator_basicNonIterable)
 
 BEGIN_TEST(testForOfIterator_bug515273_part1)
 {
     JS::RootedValue v(cx);
 
     // Hack to make it simple to produce an object that has a property
     // named Symbol.iterator.
-    EVAL("var obj = {'@@iterator': 5"
-         IF_JS_HAS_SYMBOLS(", [Symbol.iterator]: Array.prototype[Symbol.iterator]")
-         "}; obj;", &v);
+    EVAL("({[" STD_ITERATOR "]: 5})", &v);
 
     JS::ForOfIterator iter(cx);
     bool ok = iter.init(v, JS::ForOfIterator::AllowNonIterable);
     CHECK(!ok);
     JS_ClearPendingException(cx);
     return true;
 }
 END_TEST(testForOfIterator_bug515273_part1)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5587,17 +5587,17 @@ 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));
+    return cx->wellKnownSymbols().get(uint32_t(which));
 }
 
 static bool
 PropertySpecNameIsDigits(const char *s) {
     if (JS::PropertySpecNameIsSymbol(s))
         return false;
     if (!*s)
         return false;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2556,34 +2556,42 @@ struct JSFunctionSpec {
  * Initializer macros for a JSFunctionSpec array element. JS_FN (whose name pays
  * homage to the old JSNative/JSFastNative split) simply adds the flag
  * JSFUN_STUB_GSOPS. JS_FNINFO allows the simple adding of
  * JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted function. Finally
  * JS_FNSPEC has slots for all the fields.
  *
  * The _SYM variants allow defining a function with a symbol key rather than a
  * string key. For example, use JS_SYM_FN(iterator, ...) to define an
- * @@iterator method.
+ * @@iterator method. (In builds without ES6 symbols, it defines a method with
+ * the string id "@@iterator".)
  */
 #define JS_FS(name,call,nargs,flags)                                          \
     JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
 #define JS_FN(name,call,nargs,flags)                                          \
     JS_FNSPEC(name, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
 #define JS_SYM_FN(name,call,nargs,flags)                                      \
     JS_SYM_FNSPEC(symbol, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
 #define JS_FNINFO(name,call,info,nargs,flags)                                 \
     JS_FNSPEC(name, call, info, nargs, flags, nullptr)
 #define JS_SELF_HOSTED_FN(name,selfHostedName,nargs,flags)                    \
     JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
 #define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags)           \
     JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
+
+#ifdef JS_HAS_SYMBOLS
 #define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName)       \
     JS_FNSPEC(reinterpret_cast<const char *>(                                 \
                   uint32_t(::JS::SymbolCode::symbol) + 1),                    \
               call, info, nargs, flags, selfHostedName)
+#else
+#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName)       \
+    JS_FNSPEC("@@" #symbol, call, info, nargs, flags, selfHostedName)
+#endif
+
 #define JS_FNSPEC(name,call,info,nargs,flags,selfHostedName)                  \
     {name, {call, info}, nargs, flags, selfHostedName}
 
 extern JS_PUBLIC_API(JSObject *)
 JS_InitClass(JSContext *cx, JS::HandleObject obj, JS::HandleObject parent_proto,
              const JSClass *clasp, JSNative constructor, unsigned nargs,
              const JSPropertySpec *ps, const JSFunctionSpec *fs,
              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3169,17 +3169,17 @@ static const JSFunctionSpec array_method
 
     /* ES6 additions */
     JS_SELF_HOSTED_FN("find",        "ArrayFind",        1,0),
     JS_SELF_HOSTED_FN("findIndex",   "ArrayFindIndex",   1,0),
     JS_SELF_HOSTED_FN("copyWithin",  "ArrayCopyWithin",  3,0),
 
     JS_SELF_HOSTED_FN("fill",        "ArrayFill",        3,0),
 
-    JS_SELF_HOSTED_FN("@@iterator",  "ArrayValues",      0,0),
+    JS_SELF_HOSTED_SYM_FN(iterator,  "ArrayValues",      0,0),
     JS_SELF_HOSTED_FN("entries",     "ArrayEntries",     0,0),
     JS_SELF_HOSTED_FN("keys",        "ArrayKeys",        0,0),
     JS_FS_END
 };
 
 static const JSFunctionSpec array_static_methods[] = {
     JS_FN("isArray",            array_isArray,      1,0),
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -881,17 +881,17 @@ iterator_next_impl(JSContext *cx, CallAr
 static bool
 iterator_next(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args);
 }
 
 static const JSFunctionSpec iterator_methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "LegacyIteratorShim", 0, 0),
     JS_FN("next",      iterator_next,       0, 0),
     JS_FS_END
 };
 
 static JSObject *
 iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly)
 {
     return obj;
@@ -960,17 +960,17 @@ const Class ArrayIteratorObject::class_ 
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr                  /* finalize    */
 };
 
 static const JSFunctionSpec array_iterator_methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "ArrayIteratorIdentity", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "ArrayIteratorIdentity", 0, 0),
     JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
     JS_FS_END
 };
 
 static const Class StringIteratorPrototypeClass = {
     "String Iterator",
     JSCLASS_IMPLEMENTS_BARRIERS,
     JS_PropertyStub,         /* addProperty */
@@ -999,17 +999,17 @@ const Class StringIteratorObject::class_
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr                  /* finalize    */
 };
 
 static const JSFunctionSpec string_iterator_methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "StringIteratorIdentity", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "StringIteratorIdentity", 0, 0),
     JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
     JS_FS_END
 };
 
 bool
 js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
 {
     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
@@ -1364,18 +1364,24 @@ ForOfIterator::init(HandleValue iterable
 
     // The iterator is the result of calling obj[@@iterator]().
     InvokeArgs args(cx);
     if (!args.init(0))
         return false;
     args.setThis(ObjectValue(*iterableObj));
 
     RootedValue callee(cx);
+#ifdef JS_HAS_SYMBOLS
+    RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
+    if (!JSObject::getGeneric(cx, iterableObj, iterableObj, iteratorId, &callee))
+        return false;
+#else
     if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee))
         return false;
+#endif
 
     // If obj[@@iterator] is undefined and we were asked to allow non-iterables,
     // bail out now without setting iterator.  This will make valueIsIterable(),
     // which our caller should check, return false.
     if (nonIterableBehavior == AllowNonIterable && callee.isUndefined())
         return true;
 
     // Throw if obj[@@iterator] isn't callable.
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -4199,17 +4199,17 @@ static const JSFunctionSpec string_metho
     JS_SELF_HOSTED_FN("blink",    "String_blink",      0,0),
     JS_SELF_HOSTED_FN("sup",      "String_sup",        0,0),
     JS_SELF_HOSTED_FN("sub",      "String_sub",        0,0),
     JS_SELF_HOSTED_FN("anchor",   "String_anchor",     1,0),
     JS_SELF_HOSTED_FN("link",     "String_link",       1,0),
     JS_SELF_HOSTED_FN("fontcolor","String_fontcolor",  1,0),
     JS_SELF_HOSTED_FN("fontsize", "String_fontsize",   1,0),
 
-    JS_SELF_HOSTED_FN("@@iterator", "String_iterator", 0,0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "String_iterator", 0,0),
     JS_FS_END
 };
 
 // ES6 rev 27 (2014 Aug 24) 21.1.1
 bool
 js_String(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/tests/ecma_6/Array/for_of_2.js
+++ b/js/src/tests/ecma_6/Array/for_of_2.js
@@ -20,17 +20,17 @@ function TestChangeArrayIteratorNext() {
     var GET_COUNT = 0;
     function getter() {
         GET_COUNT++;
         if (GET_COUNT == MID)
             iterProto.next = NewNext;
         return M2;
     }
 
-    var iter = ([])['@@iterator']();
+    var iter = ([])[std_iterator]();
     var iterProto = Object.getPrototypeOf(iter);
     var OldNext = iterProto.next;
     var NewNext = function () {
         return OldNext.apply(this, arguments);
     };
 
     var TRUE_SUM = 0;
     var N = 100;
--- a/js/src/tests/ecma_6/Array/for_of_3.js
+++ b/js/src/tests/ecma_6/Array/for_of_3.js
@@ -22,17 +22,17 @@ function TestIncreaseArrayLength() {
         GET_COUNT++;
         if (GET_COUNT == MID) {
             ARR_SUM += arr.length;
             arr.push(arr.length);
         }
         return M2;
     }
 
-    var iter = ([])['@@iterator']();
+    var iter = ([])[std_iterator]();
     var iterProto = Object.getPrototypeOf(iter);
     var OldNext = iterProto.next;
     var NewNext = function () {
         return OldNext.apply(this, arguments);
     };
 
     var TRUE_SUM = 0;
     var N = 100;
--- a/js/src/tests/ecma_6/Array/for_of_4.js
+++ b/js/src/tests/ecma_6/Array/for_of_4.js
@@ -21,17 +21,17 @@ function TestDecreaseArrayLength() {
     function getter() {
         GET_COUNT++;
         if (GET_COUNT == MID) {
             arr.length = 0;
         }
         return M2;
     }
 
-    var iter = ([])['@@iterator']();
+    var iter = ([])[std_iterator]();
     var iterProto = Object.getPrototypeOf(iter);
     var OldNext = iterProto.next;
     var NewNext = function () {
         return OldNext.apply(this, arguments);
     };
 
     var TRUE_SUM = 0;
     var N = 100;
--- a/js/src/tests/ecma_6/Array/from_errors.js
+++ b/js/src/tests/ecma_6/Array/from_errors.js
@@ -129,17 +129,17 @@ var exc = {surprise: "ponies"};
 assertThrowsValue(() => Array.from.call(C, arrayish, () => { throw exc; }), exc);
 assertEq(log, "lC0");
 assertEq(obj instanceof C, true);
 
 // It's a TypeError if the iterator's .next() method returns a primitive.
 for (var primitive of [undefined, null, 17]) {
     assertThrowsInstanceOf(
         () => Array.from({
-            "@@iterator": () => {
-                next: () => primitive
+            [std_iterator]() {
+                return {next() { return primitive; }};
             }
         }),
         TypeError);
 }
 
 if (typeof reportCompare === 'function')
     reportCompare(0, 0);
--- a/js/src/tests/ecma_6/Array/from_iterable.js
+++ b/js/src/tests/ecma_6/Array/from_iterable.js
@@ -3,17 +3,17 @@
 
 // Array.from works on arguments objects.
 (function () {
     assertDeepEq(Array.from(arguments), ["arg0", "arg1", undefined]);
 })("arg0", "arg1", undefined);
 
 // If an object has both .length and [@@iterator] properties, [@@iterator] is used.
 var a = ['a', 'e', 'i', 'o', 'u'];
-a["@@iterator"] = function* () {
+a[std_iterator] = function* () {
     for (var i = 5; i--; )
         yield this[i];
 };
 
 var log = '';
 function f(x) {
     log += x;
     return x + x;
--- a/js/src/tests/ecma_6/Array/from_proxy.js
+++ b/js/src/tests/ecma_6/Array/from_proxy.js
@@ -1,52 +1,54 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/ */
 
 // Two tests involving Array.from and a Proxy.
 var log = [];
 function LoggingProxy(target) {
     var h = {
         defineProperty: function (t, id) {
-            log.push("define " + id);
+            log.push("define", id);
             return undefined;
         },
         has: function (t, id) {
-            log.push("has " + id);
+            log.push("has", id);
             return id in t;
         },
         get: function (t, id) {
-            log.push("get " + id);
+            log.push("get", id);
             return t[id];
         },
         set: function (t, id, v) {
-            log.push("set " + id);
+            log.push("set", id);
             t[id] = v;
         }
     };
     return new Proxy(target || [], h);
 }
 
 // When the new object created by Array.from is a Proxy,
 // Array.from calls handler.defineProperty to create new elements
 // but handler.set to set the length.
 LoggingProxy.from = Array.from;
 LoggingProxy.from([3, 4, 5]);
-assertDeepEq(log, ["define 0", "define 1", "define 2", "set length"]);
+assertDeepEq(log, ["define", "0", "define", "1", "define", "2", "set", "length"]);
 
 // When the argument passed to Array.from is a Proxy, Array.from
 // calls handler.get on it.
 log = [];
 assertDeepEq(Array.from(new LoggingProxy([3, 4, 5])), [3, 4, 5]);
-assertDeepEq(log, ["get @@iterator",
-                   "get length", "get 0", "get length", "get 1", "get length", "get 2",
-                   "get length"]);
+assertDeepEq(log, ["get", std_iterator,
+                   "get", "length", "get", "0",
+                   "get", "length", "get", "1",
+                   "get", "length", "get", "2",
+                   "get", "length"]);
 
 // Array-like iteration only gets the length once.
 log = [];
 var arr = [5, 6, 7];
-arr["@@iterator"] = undefined;
+arr[std_iterator] = undefined;
 assertDeepEq(Array.from(new LoggingProxy(arr)), [5, 6, 7]);
-assertDeepEq(log, ["get @@iterator",
-                   "get length", "get 0", "get 1", "get 2"]);
+assertDeepEq(log, ["get", std_iterator,
+                   "get", "length", "get", "0", "get", "1", "get", "2"]);
 
 if (typeof reportCompare === 'function')
     reportCompare(0, 0);
--- a/js/src/tests/ecma_6/Array/from_string.js
+++ b/js/src/tests/ecma_6/Array/from_string.js
@@ -6,18 +6,18 @@ assertDeepEq(Array.from("test string"),
              ['t', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g']);
 
 // Array.from on a string handles surrogate pairs correctly.
 var gclef = "\uD834\uDD1E"; // U+1D11E MUSICAL SYMBOL G CLEF
 assertDeepEq(Array.from(gclef), [gclef]);
 assertDeepEq(Array.from(gclef + " G"), [gclef, " ", "G"]);
 
 // Array.from on a string calls the @@iterator method.
-String.prototype["@@iterator"] = function* () { yield 1; yield 2; };
+String.prototype[std_iterator] = function* () { yield 1; yield 2; };
 assertDeepEq(Array.from("anything"), [1, 2]);
 
 // If the iterator method is deleted, Strings are still arraylike.
-delete String.prototype["@@iterator"];
+delete String.prototype[std_iterator];
 assertDeepEq(Array.from("works"), ['w', 'o', 'r', 'k', 's']);
 assertDeepEq(Array.from(gclef), ['\uD834', '\uDD1E']);
 
 if (typeof reportCompare === 'function')
     reportCompare(0, 0);
--- a/js/src/tests/ecma_6/Generators/runtime.js
+++ b/js/src/tests/ecma_6/Generators/runtime.js
@@ -12,18 +12,16 @@ function assertSyntaxError(str) {
 }
 
 
 function f() { }
 function* g() { yield 1; }
 var GeneratorFunctionPrototype = Object.getPrototypeOf(g);
 var GeneratorFunction = GeneratorFunctionPrototype.constructor;
 var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
-// FIXME: This should be a symbol.
-var std_iterator = "@@iterator";
 
 
 // A generator function should have the same set of properties as any
 // other function.
 function TestGeneratorFunctionInstance() {
     var f_own_property_names = Object.getOwnPropertyNames(f);
     var g_own_property_names = Object.getOwnPropertyNames(g);
 
@@ -61,17 +59,19 @@ TestGeneratorFunctionPrototype();
 // Functions that we associate with generator objects are actually defined by
 // a common prototype.
 function TestGeneratorObjectPrototype() {
     assertEq(Object.getPrototypeOf(GeneratorObjectPrototype),
                Object.prototype);
     assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype),
                GeneratorObjectPrototype);
 
-    var expected_property_names = ["next", "throw", "constructor", std_iterator];
+    var expected_property_names = ["next", "throw", "constructor"];
+    if (!JS_HAS_SYMBOLS)
+        expected_property_names.push(std_iterator);
     var found_property_names =
         Object.getOwnPropertyNames(GeneratorObjectPrototype);
 
     expected_property_names.sort();
     found_property_names.sort();
 
     assertDeepEq(found_property_names, expected_property_names);
 }
--- a/js/src/tests/ecma_6/Symbol/property-reflection.js
+++ b/js/src/tests/ecma_6/Symbol/property-reflection.js
@@ -61,20 +61,18 @@ if (typeof Symbol === "function") {
     desc = Object.getOwnPropertyDescriptor(n, s2);
     assertDeepEq(desc, descs[s2]);
     assertEq(desc.value, descs[s2].value);
 
     // Object.prototype.hasOwnProperty
     assertEq(descs.hasOwnProperty(s1), true);
     assertEq(descs.hasOwnProperty(s2), true);
     assertEq(descs.hasOwnProperty(s3), false);
-    assertEq([].hasOwnProperty(Symbol.iterator), false);
-    if (!("@@iterator" in []))
-        throw new Error("Congratulations on implementing Symbol.iterator! Please update this test.");
-    assertEq(Array.prototype.hasOwnProperty(Symbol.iterator), false);  // should be true
+    assertEq([].hasOwnProperty(std_iterator), false);
+    assertEq(Array.prototype.hasOwnProperty(std_iterator), true);
 
     // Object.prototype.propertyIsEnumerable
     assertEq(n.propertyIsEnumerable(s1), true);
     assertEq(n.propertyIsEnumerable(s2), false);
     assertEq(n.propertyIsEnumerable(s3), false);  // no such property
     assertEq(D.prototype.propertyIsEnumerable(s3), true);
     assertEq(descs.propertyIsEnumerable(s3), false); // inherited properties are not considered
 
--- a/js/src/tests/js1_8/regress/regress-469625-03.js
+++ b/js/src/tests/js1_8/regress/regress-469625-03.js
@@ -21,17 +21,18 @@ function test()
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
   function f(x) {
     var [a, b, [c0, c1]] = [x, x, x];
   }
 
-  expect = 'TypeError: (intermediate value)[\'@@iterator\'](...).next(...).value is null';
+  var ITERATOR = JS_HAS_SYMBOLS ? "(intermediate value)" : "'@@iterator'";
+  expect = `TypeError: (intermediate value)[${ITERATOR}](...).next(...).value is null`;
   actual = 'No Error';
   try
   {
     f(null);
   }
   catch(ex)
   {
     actual = ex + '';
--- a/js/src/tests/shell.js
+++ b/js/src/tests/shell.js
@@ -871,8 +871,11 @@ function GetContext() {
   return Packages.com.netscape.javascript.Context.getCurrentContext();
 }
 function OptLevel( i ) {
   i = Number(i);
   var cx = GetContext();
   cx.setOptimizationLevel(i);
 }
 /* end of Rhino functions */
+
+var JS_HAS_SYMBOLS = typeof Symbol === "function";
+var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -242,26 +242,26 @@ const Class StarGeneratorObject::class_ 
     nullptr,                 /* finalize    */
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     nullptr,                 /* trace       */
 };
 
 static const JSFunctionSpec star_generator_methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_SELF_HOSTED_FN("next", "StarGeneratorNext", 1, 0),
     JS_SELF_HOSTED_FN("throw", "StarGeneratorThrow", 1, 0),
     JS_FS_END
 };
 
 #define JSPROP_ROPERM   (JSPROP_READONLY | JSPROP_PERMANENT)
 
 static const JSFunctionSpec legacy_generator_methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "LegacyGeneratorIteratorShim", 0, 0),
     // "send" is an alias for "next".
     JS_SELF_HOSTED_FN("next", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
     JS_SELF_HOSTED_FN("send", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
     JS_SELF_HOSTED_FN("throw", "LegacyGeneratorThrow", 1, JSPROP_ROPERM),
     JS_SELF_HOSTED_FN("close", "LegacyGeneratorClose", 0, JSPROP_ROPERM),
     JS_FS_END
 };
 
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -319,24 +319,38 @@ InitBareBuiltinCtor(JSContext *cx, Handl
  * The self-hosting global only gets a small subset of all standard classes.
  * Even those are only created as bare constructors without any properties
  * or functions.
  */
 /* static */ bool
 GlobalObject::initSelfHostingBuiltins(JSContext *cx, Handle<GlobalObject*> global,
                                       const JSFunctionSpec *builtins)
 {
-    /* Define a top-level property 'undefined' with the undefined value. */
+    // Define a top-level property 'undefined' with the undefined value.
     if (!JSObject::defineProperty(cx, global, cx->names().undefined, UndefinedHandleValue,
                                   JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
 
+    // Define a top-level property 'std_iterator' with the name of the method
+    // used by for-of loops to create an iterator.
+    RootedValue std_iterator(cx);
+#ifdef JS_HAS_SYMBOLS
+    std_iterator.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::iterator));
+#else
+    std_iterator.setString(cx->names().std_iterator);
+#endif
+    if (!JS_DefineProperty(cx, global, "std_iterator", std_iterator,
+                           JSPROP_PERMANENT | JSPROP_READONLY))
+    {
+        return false;
+    }
+
     return InitBareBuiltinCtor(cx, global, JSProto_Array) &&
            InitBareBuiltinCtor(cx, global, JSProto_TypedArray) &&
            InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
            InitBareBuiltinCtor(cx, global, JSProto_Uint32Array) &&
            InitBareWeakMapCtor(cx, global) &&
            initStopIterationClass(cx, global) &&
            InitSelfHostingCollectionIteratorFunctions(cx, global) &&
            JS_DefineFunctions(cx, global, builtins);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1119,16 +1119,17 @@ HandleError(JSContext *cx, InterpreterRe
 #define REGS                     (activation.regs())
 #define PUSH_COPY(v)             do { *REGS.sp++ = (v); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
 #define PUSH_COPY_SKIP_CHECK(v)  *REGS.sp++ = (v)
 #define PUSH_NULL()              REGS.sp++->setNull()
 #define PUSH_UNDEFINED()         REGS.sp++->setUndefined()
 #define PUSH_BOOLEAN(b)          REGS.sp++->setBoolean(b)
 #define PUSH_DOUBLE(d)           REGS.sp++->setDouble(d)
 #define PUSH_INT32(i)            REGS.sp++->setInt32(i)
+#define PUSH_SYMBOL(s)           REGS.sp++->setSymbol(s)
 #define PUSH_STRING(s)           do { REGS.sp++->setString(s); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
 #define PUSH_OBJECT(obj)         do { REGS.sp++->setObject(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
 #define PUSH_OBJECT_OR_NULL(obj) do { REGS.sp++->setObjectOrNull(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
 #define PUSH_HOLE()              REGS.sp++->setMagic(JS_ELEMENTS_HOLE)
 #define PUSH_UNINITIALIZED()     REGS.sp++->setMagic(JS_UNINITIALIZED_LEXICAL)
 #define POP_COPY_TO(v)           (v) = *--REGS.sp
 #define POP_RETURN_VALUE()       REGS.fp()->setReturnValue(*--REGS.sp)
 
@@ -1596,17 +1597,16 @@ CASE(EnableInterruptsPseudoOpcode)
     /* Commence executing the actual opcode. */
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_UNUSED2)
-CASE(JSOP_UNUSED45)
 CASE(JSOP_UNUSED46)
 CASE(JSOP_UNUSED47)
 CASE(JSOP_UNUSED48)
 CASE(JSOP_UNUSED49)
 CASE(JSOP_UNUSED50)
 CASE(JSOP_UNUSED51)
 CASE(JSOP_UNUSED52)
 CASE(JSOP_UNUSED57)
@@ -2701,16 +2701,20 @@ CASE(JSOP_TOSTRING)
         JSString *operString = ToString<CanGC>(cx, oper);
         if (!operString)
             goto error;
         oper.setString(operString);
     }
 }
 END_CASE(JSOP_TOSTRING)
 
+CASE(JSOP_SYMBOL)
+    PUSH_SYMBOL(cx->wellKnownSymbols().get(GET_UINT8(REGS.pc)));
+END_CASE(JSOP_SYMBOL)
+
 CASE(JSOP_OBJECT)
 {
     RootedNativeObject &ref = rootNativeObject0;
     ref = script->getObject(REGS.pc);
     if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
         JSObject *obj = js::DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject);
         if (!obj)
             goto error;
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -411,17 +411,25 @@ 1234567890123456789012345678901234567890
      *   Category: Operator
      *   Type: Stack Operations
      *   Operands: uint24_t n
      *   Stack: v[n], v[n-1], ..., v[1], v[0] =>
      *          v[n], v[n-1], ..., v[1], v[0], v[n]
      */ \
     macro(JSOP_DUPAT,     44, "dupat",      NULL,         4,  0,  1,  JOF_UINT24) \
     \
-    macro(JSOP_UNUSED45,  45, "unused45",   NULL,         1,  0,  0,  JOF_BYTE) \
+    /*
+     * Push a well-known symbol onto the operand stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: uint8_t n, the JS::SymbolCode of the symbol to use
+     *   Stack: => symbol
+     */ \
+    macro(JSOP_SYMBOL,    45, "symbol",     NULL,         2,  0,  1,  JOF_UINT8) \
+    \
     macro(JSOP_UNUSED46,  46, "unused46",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED47,  47, "unused47",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED48,  48, "unused48",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED49,  49, "unused49",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED50,  50, "unused50",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED51,  51, "unused51",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED52,  52, "unused52",   NULL,         1,  0,  0,  JOF_BYTE) \
     \
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -13,16 +13,22 @@
 #include "vm/SelfHosting.h"
 
 #include "jsobjinlines.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
+#ifdef JS_HAS_SYMBOLS
+#define STD_ITERATOR_ID  SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)
+#else
+#define STD_ITERATOR_ID  ::js::NameToId(cx->names().std_iterator)
+#endif
+
 bool
 js::ForOfPIC::Chain::initialize(JSContext *cx)
 {
     MOZ_ASSERT(!initialized_);
 
     // Get the canonical Array.prototype
     RootedNativeObject arrayProto(cx, GlobalObject::getOrCreateArrayPrototype(cx, cx->global()));
     if (!arrayProto)
@@ -39,18 +45,18 @@ js::ForOfPIC::Chain::initialize(JSContex
     initialized_ = true;
     arrayProto_ = arrayProto;
     arrayIteratorProto_ = arrayIteratorProto;
 
     // Shortcut returns below means Array for-of will never be optimizable,
     // do set disabled_ now, and clear it later when we succeed.
     disabled_ = true;
 
-    // Look up '@@iterator' on Array.prototype, ensure it's a slotful shape.
-    Shape *iterShape = arrayProto->lookup(cx, cx->names().std_iterator);
+    // Look up Array.prototype[@@iterator], ensure it's a slotful shape.
+    Shape *iterShape = arrayProto->lookup(cx, STD_ITERATOR_ID);
     if (!iterShape || !iterShape->hasSlot() || !iterShape->hasDefaultGetter())
         return true;
 
     // Get the referred value, and ensure it holds the canonical ArrayValues function.
     Value iterator = arrayProto->getSlot(iterShape->slot());
     JSFunction *iterFun;
     if (!IsFunctionObject(iterator, &iterFun))
         return true;
@@ -138,18 +144,18 @@ js::ForOfPIC::Chain::tryOptimizeArray(JS
     // churn on these.
     if (numStubs() >= MAX_STUBS)
         eraseChain();
 
     // Ensure array's prototype is the actual Array.prototype
     if (!isOptimizableArray(array))
         return true;
 
-    // Ensure array doesn't define '@@iterator' directly.
-    if (array->lookup(cx, cx->names().std_iterator))
+    // Ensure array doesn't define @@iterator directly.
+    if (array->lookup(cx, STD_ITERATOR_ID))
         return true;
 
     // Good to optimize now, create stub to add.
     RootedShape shape(cx, array->lastProperty());
     stub = cx->new_<Stub>(shape);
     if (!stub)
         return false;
 
@@ -192,17 +198,17 @@ js::ForOfPIC::Chain::isOptimizableArray(
 
 bool
 js::ForOfPIC::Chain::isArrayStateStillSane()
 {
     // Ensure that canonical Array.prototype has matching shape.
     if (arrayProto_->lastProperty() != arrayProtoShape_)
         return false;
 
-    // Ensure that Array.prototype['@@iterator'] contains the
+    // Ensure that Array.prototype[@@iterator] contains the
     // canonical iterator function.
     if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_)
         return false;
 
     // Chain to isArrayNextStillSane.
     return isArrayNextStillSane();
 }
 
--- a/js/src/vm/PIC.h
+++ b/js/src/vm/PIC.h
@@ -125,17 +125,17 @@ struct ForOfPIC
     ForOfPIC() MOZ_DELETE;
     ForOfPIC(const ForOfPIC &other) MOZ_DELETE;
 
     typedef PICStub<ForOfPIC> BaseStub;
     typedef PICChain<ForOfPIC> BaseChain;
 
     /*
      * A ForOfPIC has only one kind of stub for now: one that holds the shape
-     * of an array object that does not override its '@@iterator' property.
+     * of an array object that does not override its @@iterator property.
      */
     class Stub : public BaseStub
     {
       private:
         // Shape of matching array object.
         Shape *shape_;
 
       public:
@@ -159,35 +159,35 @@ struct ForOfPIC
      *
      *  Array.prototype's shape (arrayProtoShape_)
      *      To ensure that Array.prototype has not been modified.
      *
      *  ArrayIterator.prototype (arrayIteratorProto_)
      *  ArrayIterator.prototype's shape (arrayIteratorProtoShape_)
      *      To ensure that an ArrayIterator.prototype has not been modified.
      *
-     *  Array.prototype's slot number for '@@iterator' (arrayProtoIteratorSlot_)
-     *  Array.prototype's canonical value for '@@iterator' (canonicalIteratorFunc_)
+     *  Array.prototype's slot number for @@iterator (arrayProtoIteratorSlot_)
+     *  Array.prototype's canonical value for @@iterator (canonicalIteratorFunc_)
      *      To quickly retreive and ensure that the iterator constructor
      *      stored in the slot has not changed.
      *
      *  ArrayIterator.prototype's slot number for 'next' (arrayIteratorProtoNextSlot_)
      *  ArrayIterator.prototype's canonical value for 'next' (canonicalNextFunc_)
      *      To quickly retreive and ensure that the 'next' method for ArrayIterator
      *      objects has not changed.
      */
     class Chain : public BaseChain
     {
       private:
         // Pointer to canonical Array.prototype and ArrayIterator.prototype
         HeapPtrNativeObject arrayProto_;
         HeapPtrNativeObject arrayIteratorProto_;
 
         // Shape of matching Array.prototype object, and slot containing
-        // the '@@iterator' for it, and the canonical value.
+        // the @@iterator for it, and the canonical value.
         HeapPtrShape arrayProtoShape_;
         uint32_t arrayProtoIteratorSlot_;
         HeapValue canonicalIteratorFunc_;
 
         // Shape of matching ArrayIteratorProto, and slot containing
         // the 'next' property, and the canonical value.
         HeapPtrShape arrayIteratorProtoShape_;
         uint32_t arrayIteratorProtoNextSlot_;
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1496,16 +1496,22 @@ CloneValue(JSContext *cx, HandleValue se
     } else if (selfHostedValue.isString()) {
         if (!selfHostedValue.toString()->isFlat())
             MOZ_CRASH();
         JSFlatString *selfHostedString = &selfHostedValue.toString()->asFlat();
         JSString *clone = CloneString(cx, selfHostedString);
         if (!clone)
             return false;
         vp.setString(clone);
+    } else if (selfHostedValue.isSymbol()) {
+        // Well-known symbols are shared.
+        JS::Symbol *sym = selfHostedValue.toSymbol();
+        MOZ_ASSERT(sym->isWellKnownSymbol());
+        MOZ_ASSERT(cx->wellKnownSymbols().get(size_t(sym->code())) == sym);
+        vp.set(selfHostedValue);
     } else {
         MOZ_CRASH("Self-hosting CloneValue can't clone given value.");
     }
     return true;
 }
 
 bool
 JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, HandlePropertyName name,
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -755,17 +755,17 @@ TypedArrayObject::subarray(JSContext *cx
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<TypedArrayObject::is,
                                 TypedArrayMethods<TypedArrayObject>::subarray>(cx, args);
 }
 
 /* static */ const JSFunctionSpec
 TypedArrayObject::protoFunctions[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),
+    JS_SELF_HOSTED_SYM_FN(iterator, "ArrayValues", 0, 0),                          \
     JS_FN("subarray", TypedArrayObject::subarray, 2, 0),
     JS_FN("set", TypedArrayObject::set, 2, 0),
     JS_FN("copyWithin", TypedArrayObject::copyWithin, 2, 0),
     JS_FS_END
 };
 
 /* static */ const JSFunctionSpec
 TypedArrayObject::staticFunctions[] = {
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -22,18 +22,31 @@ namespace js {
  * deserialization if there is a mismatch between the current and saved
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
+ *
+ * === GREETINGS, FELLOW SUBTRAHEND INCREMENTER! ===
+ * For the time being, please increment the subtrahend by 2 each time it
+ * changes, because we have two flavors of bytecode: with JSOP_SYMBOL (in
+ * Nightly) and without (all others).  FIXME: Bug 1066322 - Enable ES6 symbols
+ * in all builds.
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 188);
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 190;
+static_assert(XDR_BYTECODE_VERSION_SUBTRAHEND % 2 == 0, "see the comment above");
+static const uint32_t XDR_BYTECODE_VERSION =
+    uint32_t(0xb973c0de - (XDR_BYTECODE_VERSION_SUBTRAHEND
+#ifdef JS_HAS_SYMBOLS
+                                                           + 1
+#endif
+                                                              ));
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext *cx)
       : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { }
 
     JSContext *cx() const {
         return context;
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -141,16 +141,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   // prototype, so that we make sure to audit any new ones to make sure they're
   // Xray-safe.
   //
   // DO NOT CHANGE WTIHOUT REVIEW FROM AN XPCONNECT PEER.
   var version = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).version;
   var isNightlyBuild = version.endsWith("a1");
   var isReleaseBuild = !version.contains("a");
   const jsHasSymbols = typeof Symbol === "function";
+  const kIteratorSymbol = jsHasSymbols ? Symbol.iterator : "@@iterator";
   var gPrototypeProperties = {};
   gPrototypeProperties['Date'] =
     ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear",
     "getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay",
     "getHours", "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds",
     "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "setTime",
     "setYear", "setFullYear", "setUTCFullYear", "setMonth", "setUTCMonth",
     "setDate", "setUTCDate", "setHours", "setUTCHours", "setMinutes",
@@ -163,26 +164,26 @@ https://bugzilla.mozilla.org/show_bug.cg
                                       the JS engine filters it out of getOwnPropertyNames */
     ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
      "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
      "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"];
   gPrototypeProperties['Array'] =
     ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
       "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
       "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
-      "findIndex", "copyWithin", "fill", "@@iterator", "entries", "keys", "constructor"];
+      "findIndex", "copyWithin", "fill", kIteratorSymbol, "entries", "keys", "constructor"];
   if (isNightlyBuild) {
     let pjsMethods = ['mapPar', 'reducePar', 'scanPar', 'scatterPar', 'filterPar'];
     gPrototypeProperties['Array'] = gPrototypeProperties['Array'].concat(pjsMethods);
   }
   for (var c of typedArrayClasses) {
     gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
   }
   gPrototypeProperties['TypedArray'] =
-    ["length", "buffer", "byteLength", "byteOffset", "@@iterator", "subarray",
+    ["length", "buffer", "byteLength", "byteOffset", kIteratorSymbol, "subarray",
      "set", "copyWithin"];
 
   for (var c of errorObjectClasses) {
       gPrototypeProperties[c] = ["constructor", "name",
                                  // We don't actually resolve these empty data properties
                                  // onto the Xray prototypes, but we list them here to make
                                  // the test happy.
                                  "lineNumber", "columnNumber", "fileName", "message", "stack"];
@@ -495,20 +496,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   function testTypedArrays() {
     // We don't invoke testXray with %TypedArray%, because that function isn't
     // set up to deal with "anonymous" dependent classes (that is, classes not
     // visible as a global property, which %TypedArray% is not), and fixing it
     // up is more trouble than it's worth.
 
     var typedArrayProto = Object.getPrototypeOf(Int8Array.prototype);
 
-  gPrototypeProperties['TypedArray'] =
-    ["length", "buffer", "byteLength", "byteOffset", "@@iterator", "subarray",
-     "set", "copyWithin"];
-
     var desiredInheritedProps = Object.getOwnPropertyNames(typedArrayProto).sort();
     var inheritedProps =
       filterOut(desiredInheritedProps, ["BYTES_PER_ELEMENT", "constructor"]);
 
     var inheritedCallables =
       inheritedProps.filter(name => (propertyIsGetter(typedArrayProto, name) ||
                                      typeof typedArrayProto[name] === "function") &&
                                     name !== "constructor");
--- a/toolkit/modules/Promise-backend.js
+++ b/toolkit/modules/Promise-backend.js
@@ -32,16 +32,19 @@ const STATUS_REJECTED = 2;
 // This N_INTERNALS name allow internal properties of the Promise to be
 // accessed only by this module, while still being visible on the object
 // manually when using a debugger.  This doesn't strictly guarantee that the
 // properties are inaccessible by other code, but provide enough protection to
 // avoid using them by mistake.
 const salt = Math.floor(Math.random() * 100);
 const N_INTERNALS = "{private:internals:" + salt + "}";
 
+const JS_HAS_SYMBOLS = typeof Symbol === "function";
+const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
+
 /////// Warn-upon-finalization mechanism
 //
 // One of the difficult problems with promises is locating uncaught
 // rejections. We adopt the following strategy: if a promise is rejected
 // at the time of its garbage-collection *and* if the promise is at the
 // end of a promise chain (i.e. |thatPromise.then| has never been
 // called), then we print a warning.
 //
@@ -506,17 +509,17 @@ Promise.reject = function (aReason)
  *         that is rejected when any of the values are rejected. Its
  *         resolution value will be an array of all resolved values in the
  *         given order, or undefined if aValues is an empty array. The reject
  *         reason will be forwarded from the first promise in the list of
  *         given promises to be rejected.
  */
 Promise.all = function (aValues)
 {
-  if (aValues == null || typeof(aValues["@@iterator"]) != "function") {
+  if (aValues == null || typeof(aValues[ITERATOR_SYMBOL]) != "function") {
     throw new Error("Promise.all() expects an iterable.");
   }
 
   return new Promise((resolve, reject) => {
     let values = Array.isArray(aValues) ? aValues : [...aValues];
     let countdown = values.length;
     let resolutionValues = new Array(countdown);
 
@@ -557,17 +560,17 @@ Promise.all = function (aValues)
  *        be resolved or rejected as to the given value or reason.
  *
  * @return A new promise that is fulfilled when any values are resolved or
  *         rejected. Its resolution value will be forwarded from the resolution
  *         value or rejection reason.
  */
 Promise.race = function (aValues)
 {
-  if (aValues == null || typeof(aValues["@@iterator"]) != "function") {
+  if (aValues == null || typeof(aValues[ITERATOR_SYMBOL]) != "function") {
     throw new Error("Promise.race() expects an iterable.");
   }
 
   return new Promise((resolve, reject) => {
     for (let value of aValues) {
       Promise.resolve(value).then(resolve, reject);
     }
   });