Backed out 7 changesets (bug 1206168, bug 1177318, bug 1054756) for hazard build failures CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 23 Sep 2015 12:31:19 -0700
changeset 264035 027ddfe2c4afed797a06e5ef74ca9aa91d3da86b
parent 264034 949d31ea7ce7529eb3316f7094d51ff56e099c82
child 264085 b00078e693a0fc6c5d45e410519c62db0f05bd00
push id65511
push userkwierso@gmail.com
push dateWed, 23 Sep 2015 19:31:32 +0000
treeherdermozilla-inbound@027ddfe2c4af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1206168, 1177318, 1054756
milestone44.0a1
backs oute892727a373a884f2ecb09d8c8c9b9a7ceb44d60
6c93d1044b7e66f9c9c368ebfcd9c9da3d481081
105433ce195b39f10f9f0b939c8786a0aff6a70f
13128a88f2b91f31b6f79963768218c3997db41e
c250abf4fd1778496b8b0e8b58ff8b6554fe9ba2
fc9fef646a97505196b39f3719b15157dba66af6
c8897f109a08a2965497a7407f061b5009c19359
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
Backed out 7 changesets (bug 1206168, bug 1177318, bug 1054756) for hazard build failures CLOSED TREE Backed out changeset e892727a373a (bug 1206168) Backed out changeset 6c93d1044b7e (bug 1054756) Backed out changeset 105433ce195b (bug 1054756) Backed out changeset 13128a88f2b9 (bug 1054756) Backed out changeset c250abf4fd17 (bug 1054756) Backed out changeset fc9fef646a97 (bug 1054756) Backed out changeset c8897f109a08 (bug 1177318)
devtools/shared/heapsnapshot/tests/gtest/DevTools.h
dom/bindings/Codegen.py
dom/indexedDB/ActorsParent.cpp
dom/plugins/base/nsJSNPRuntime.cpp
dom/workers/WorkerScope.cpp
dom/xbl/nsXBLBinding.cpp
js/public/Class.h
js/public/Proxy.h
js/src/asmjs/AsmJSModule.cpp
js/src/builtin/Intl.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/ModuleObject.cpp
js/src/builtin/Object.cpp
js/src/builtin/SIMD.cpp
js/src/builtin/SymbolObject.cpp
js/src/builtin/SymbolObject.h
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TypedObject.cpp
js/src/ctypes/CTypes.cpp
js/src/ctypes/Library.cpp
js/src/gdb/gdb-tests.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/js.msg
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testChromeBuffer.cpp
js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
js/src/jsapi-tests/testNewObject.cpp
js/src/jsapi-tests/testOps.cpp
js/src/jsapi-tests/testPersistentRooted.cpp
js/src/jsapi-tests/testResolveRecursion.cpp
js/src/jsapi-tests/testUbiNode.cpp
js/src/jsapi-tests/testWeakMap.cpp
js/src/jsapi-tests/tests.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscript.cpp
js/src/jsweakmap.cpp
js/src/jswrapper.h
js/src/perf/jsperf.cpp
js/src/proxy/BaseProxyHandler.cpp
js/src/proxy/CrossCompartmentWrapper.cpp
js/src/proxy/DeadObjectProxy.cpp
js/src/proxy/DeadObjectProxy.h
js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/Proxy.h
js/src/proxy/SecurityWrapper.cpp
js/src/proxy/Wrapper.cpp
js/src/shell/js.cpp
js/src/tests/ecma_6/Date/toPrimitive.js
js/src/tests/ecma_6/Object/toPrimitive-callers.js
js/src/tests/ecma_6/Object/toPrimitive.js
js/src/tests/ecma_6/Reflect/propertyKeys.js
js/src/tests/ecma_6/Symbol/conversions.js
js/src/tests/ecma_6/Symbol/toPrimitive.js
js/src/vm/ArgumentsObject.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/CommonPropertyNames.h
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/PIC.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/Runtime.h
js/src/vm/SavedStacks.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedTypedArrayObject.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/UnboxedObject.cpp
js/src/vm/Xdr.h
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/tests/chrome/test_bug1042436.xul
js/xpconnect/tests/chrome/test_bug1065185.html
js/xpconnect/tests/chrome/test_xrayToJS.xul
js/xpconnect/wrappers/FilteringWrapper.cpp
js/xpconnect/wrappers/FilteringWrapper.h
js/xpconnect/wrappers/XrayWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.h
netwerk/base/ProxyAutoConfig.cpp
toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -97,17 +97,17 @@ struct DevTools : public ::testing::Test
     return JS_NewContext(rt, 8192);
   }
 
   static const JSClass* getGlobalClass() {
     static const JSClass globalClass = {
       "global", JSCLASS_GLOBAL_FLAGS,
       nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, nullptr, nullptr,
-      nullptr, nullptr, nullptr,
+      nullptr, nullptr, nullptr, nullptr,
       JS_GlobalObjectTraceHook
     };
     return &globalClass;
   }
 
   JSObject* createGlobal()
   {
     /* Create the global object. */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -480,16 +480,17 @@ class CGDOMJSClass(CGThing):
                 ${flags},
                 ${addProperty}, /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 ${enumerate}, /* enumerate */
                 ${resolve}, /* resolve */
                 ${mayResolve}, /* mayResolve */
+                nullptr,               /* convert */
                 ${finalize}, /* finalize */
                 ${call}, /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 ${trace}, /* trace */
                 JS_NULL_CLASS_SPEC,
                 $*{classExtensionAndObjectOps}
               },
@@ -622,16 +623,17 @@ class CGPrototypeJSClass(CGThing):
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 nullptr,               /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 nullptr,               /* enumerate */
                 nullptr,               /* resolve */
                 nullptr,               /* mayResolve */
+                nullptr,               /* convert */
                 nullptr,               /* finalize */
                 nullptr,               /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 nullptr,               /* trace */
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
                 JS_NULL_OBJECT_OPS
@@ -718,16 +720,17 @@ class CGInterfaceObjectJSClass(CGThing):
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 nullptr,               /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 nullptr,               /* enumerate */
                 nullptr,               /* resolve */
                 nullptr,               /* mayResolve */
+                nullptr,               /* convert */
                 nullptr,               /* finalize */
                 ${ctorname}, /* call */
                 ${hasInstance}, /* hasInstance */
                 ${ctorname}, /* construct */
                 nullptr,               /* trace */
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
                 JS_NULL_OBJECT_OPS
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -23537,16 +23537,17 @@ const JSClass NormalJSRuntime::kGlobalCl
   JSCLASS_GLOBAL_FLAGS,
   /* addProperty */ nullptr,
   /* delProperty */ nullptr,
   /* getProperty */ nullptr,
   /* setProperty */ nullptr,
   /* enumerate */ nullptr,
   /* resolve */ nullptr,
   /* mayResolve */ nullptr,
+  /* convert */ nullptr,
   /* finalize */ nullptr,
   /* call */ nullptr,
   /* hasInstance */ nullptr,
   /* construct */ nullptr,
   /* trace */ JS_GlobalObjectTraceHook
 };
 
 bool
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -178,47 +178,48 @@ NPObjWrapper_GetProperty(JSContext *cx, 
 static bool
 NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties,
                        bool enumerableOnly);
 
 static bool
 NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                      bool *resolvedp);
 
+static bool
+NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
+
 static void
 NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj);
 
 static void
 NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old);
 
 static bool
 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static bool
 NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static bool
-NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
-
-static bool
 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
                      JS::MutableHandle<JS::Value> vp);
 
 const static js::Class sNPObjectJSWrapperClass =
   {
     NPRUNTIME_JSCLASS_NAME,
     JSCLASS_HAS_PRIVATE,
     NPObjWrapper_AddProperty,
     NPObjWrapper_DelProperty,
     NPObjWrapper_GetProperty,
     NPObjWrapper_SetProperty,
     nullptr,
     NPObjWrapper_Resolve,
     nullptr,                                                /* mayResolve */
+    NPObjWrapper_Convert,
     NPObjWrapper_Finalize,
     NPObjWrapper_Call,
     nullptr,                                                /* hasInstance */
     NPObjWrapper_Construct,
     nullptr,                                                /* trace */
     JS_NULL_CLASS_SPEC,
     {
       nullptr,                                              /* outerObject */
@@ -245,36 +246,32 @@ const static js::Class sNPObjectJSWrappe
 typedef struct NPObjectMemberPrivate {
     JS::Heap<JSObject *> npobjWrapper;
     JS::Heap<JS::Value> fieldValue;
     JS::Heap<jsid> methodName;
     NPP   npp;
 } NPObjectMemberPrivate;
 
 static bool
-NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                           JS::MutableHandleValue vp);
+NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
 
 static void
 NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
 
 static bool
 NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
-static bool
-NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
-
 static const JSClass sNPObjectMemberClass =
   {
     "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, NPObjectMember_GetProperty, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, NPObjectMember_Convert,
     NPObjectMember_Finalize, NPObjectMember_Call,
     nullptr, nullptr, NPObjectMember_Trace
   };
 
 static void
 OnWrapperDestroyed();
 
 static void
@@ -1390,30 +1387,16 @@ NPObjWrapper_GetProperty(JSContext *cx, 
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->hasMethod || !npobj->_class->getProperty) {
     ThrowJSException(cx, "Bad NPObject as private data!");
 
     return false;
   }
 
-  if (JSID_IS_SYMBOL(id)) {
-    JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
-    if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
-      JS::RootedObject obj(cx, JS_GetFunctionObject(
-                                 JS_NewFunction(
-                                   cx, NPObjWrapper_toPrimitive, 1, 0,
-                                   "Symbol.toPrimitive")));
-      if (!obj)
-        return false;
-      vp.setObject(*obj);
-      return true;
-    }
-  }
-
   // Find out what plugin (NPP) is the owner of the object we're
   // manipulating, and make it own any JSObject wrappers created here.
   NPP npp = LookupNPP(npobj);
   if (!npp) {
     ThrowJSException(cx, "No NPP found for NPObject!");
 
     return false;
   }
@@ -1725,16 +1708,52 @@ NPObjWrapper_Resolve(JSContext *cx, JS::
 
     return fnc != nullptr;
   }
 
   // no property or method
   return true;
 }
 
+static bool
+NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType hint, JS::MutableHandle<JS::Value> vp)
+{
+  MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
+
+  // Plugins do not simply use the default [[DefaultValue]] behavior, because
+  // that behavior involves calling toString or valueOf on objects which
+  // weren't designed to accommodate this.  Usually this wouldn't be a problem,
+  // because the absence of either property, or the presence of either property
+  // with a value that isn't callable, will cause that property to simply be
+  // ignored.  But there is a problem in one specific case: Java, specifically
+  // java.lang.Integer.  The Integer class has static valueOf methods, none of
+  // which are nullary, so the JS-reflected method will behave poorly when
+  // called with no arguments.  We work around this problem by giving plugins a
+  // [[DefaultValue]] which uses only toString and not valueOf.
+
+  JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
+  if (!JS_GetProperty(cx, obj, "toString", &v))
+    return false;
+  if (!v.isPrimitive() && JS::IsCallable(v.toObjectOrNull())) {
+    if (!JS_CallFunctionValue(cx, obj, v, JS::HandleValueArray::empty(), vp))
+      return false;
+    if (vp.isPrimitive())
+      return true;
+  }
+
+  JS_ReportErrorNumber(cx, js::GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
+                       JS_GetClass(obj)->name,
+                       hint == JSTYPE_VOID
+                       ? "primitive type"
+                       : hint == JSTYPE_NUMBER
+                       ? "number"
+                       : "string");
+  return false;
+}
+
 static void
 NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
 {
   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
   if (npobj) {
     if (sNPObjWrappers) {
       sNPObjWrappers->Remove(npobj);
     }
@@ -1781,53 +1800,16 @@ NPObjWrapper_Call(JSContext *cx, unsigne
 static bool
 NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JS::Rooted<JSObject*> obj(cx, &args.callee());
   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, true);
 }
 
-static bool
-NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp)
-{
-  // Plugins do not simply use the default OrdinaryToPrimitive behavior,
-  // because that behavior involves calling toString or valueOf on objects
-  // which weren't designed to accommodate this.  Usually this wouldn't be a
-  // problem, because the absence of either property, or the presence of either
-  // property with a value that isn't callable, will cause that property to
-  // simply be ignored.  But there is a problem in one specific case: Java,
-  // specifically java.lang.Integer.  The Integer class has static valueOf
-  // methods, none of which are nullary, so the JS-reflected method will behave
-  // poorly when called with no arguments.  We work around this problem by
-  // giving plugins a [Symbol.toPrimitive]() method which uses only toString
-  // and not valueOf.
-
-  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  JS::RootedValue thisv(cx, args.thisv());
-  if (thisv.isPrimitive())
-    return true;
-
-  JS::RootedObject obj(cx, &thisv.toObject());
-  JS::RootedValue v(cx);
-  if (!JS_GetProperty(cx, obj, "toString", &v))
-    return false;
-  if (v.isObject() && JS::IsCallable(&v.toObject())) {
-    if (!JS_CallFunctionValue(cx, obj, v, JS::HandleValueArray::empty(), args.rval()))
-      return false;
-    if (args.rval().isPrimitive())
-      return true;
-  }
-
-  JS_ReportErrorNumber(cx, js::GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
-                       JS_GetClass(obj)->name,
-                       "primitive type");
-  return false;
-}
-
 bool
 nsNPObjWrapper::IsWrapper(JSObject *obj)
 {
   return js::GetObjectClass(obj) == &sNPObjectJSWrapperClass;
 }
 
 // An NPObject is going away, make sure we null out the JS object's
 // private data in case this is an NPObject that came from a plugin
@@ -2136,34 +2118,48 @@ CreateNPObjectMember(NPP npp, JSContext 
   memberPrivate->fieldValue = fieldValue;
   memberPrivate->methodName = id;
   memberPrivate->npp = npp;
 
   return true;
 }
 
 static bool
-NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                           JS::MutableHandleValue vp)
+NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp)
 {
-  if (JSID_IS_SYMBOL(id)) {
-    JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
-    if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
-      JS::RootedObject obj(cx, JS_GetFunctionObject(
-                                 JS_NewFunction(
-                                   cx, NPObjectMember_toPrimitive, 1, 0,
-                                   "Symbol.toPrimitive")));
-      if (!obj)
-        return false;
-      vp.setObject(*obj);
-      return true;
+  NPObjectMemberPrivate *memberPrivate =
+    (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
+                                                     &sNPObjectMemberClass,
+                                                     nullptr);
+  if (!memberPrivate) {
+    NS_ERROR("no Ambiguous Member Private data!");
+    return false;
+  }
+
+  switch (type) {
+  case JSTYPE_VOID:
+  case JSTYPE_STRING:
+  case JSTYPE_NUMBER:
+    vp.set(memberPrivate->fieldValue);
+    if (vp.isObject()) {
+      JS::Rooted<JSObject*> objVal(cx, &vp.toObject());
+      return JS_DefaultValue(cx, objVal, type, vp);
     }
+    return true;
+  case JSTYPE_BOOLEAN:
+  case JSTYPE_OBJECT:
+    vp.set(memberPrivate->fieldValue);
+    return true;
+  case JSTYPE_FUNCTION:
+    // Leave this to NPObjectMember_Call.
+    return true;
+  default:
+    NS_ERROR("illegal operation on JSObject prototype object");
+    return false;
   }
-
-  return true;
 }
 
 static void
 NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj)
 {
   NPObjectMemberPrivate *memberPrivate;
 
   memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
@@ -2274,46 +2270,16 @@ NPObjectMember_Trace(JSTracer *trc, JSOb
   // NPObject, so make sure to mark the NPObject wrapper to keep the
   // NPObject alive as long as this NPObjectMember is alive.
   if (memberPrivate->npobjWrapper) {
     JS_CallObjectTracer(trc, &memberPrivate->npobjWrapper,
                         "NPObject Member => npobjWrapper");
   }
 }
 
-static bool
-NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp)
-{
-  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  JS::RootedValue thisv(cx, args.thisv());
-  if (thisv.isPrimitive()) {
-    args.rval().set(thisv);
-    return true;
-  }
-
-  JS::RootedObject obj(cx, &thisv.toObject());
-  NPObjectMemberPrivate *memberPrivate =
-    (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
-                                                     &sNPObjectMemberClass,
-                                                     &args);
-  if (!memberPrivate)
-    return false;
-
-  JSType hint;
-  if (!JS::GetFirstArgumentAsTypeHint(cx, args, &hint))
-    return false;
-
-  args.rval().set(memberPrivate->fieldValue);
-  if (args.rval().isObject()) {
-    JS::Rooted<JSObject*> objVal(cx, &args.rval().toObject());
-    return JS::ToPrimitive(cx, objVal, hint, args.rval());
-  }
-  return true;
-}
-
 // static
 bool
 nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
 {
   NPP npp = NPPStack::Peek();
   dom::AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject(npp)))) {
     return false;
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -735,16 +735,28 @@ workerdebuggersandbox_enumerate(JSContex
 
 static bool
 workerdebuggersandbox_resolve(JSContext *cx, JS::Handle<JSObject *> obj,
                               JS::Handle<jsid> id, bool *resolvedp)
 {
   return JS_ResolveStandardClass(cx, obj, id, resolvedp);
 }
 
+static bool
+workerdebuggersandbox_convert(JSContext *cx, JS::Handle<JSObject *> obj,
+                              JSType type, JS::MutableHandle<JS::Value> vp)
+{
+  if (type == JSTYPE_OBJECT) {
+    vp.setObject(*obj);
+    return true;
+  }
+
+  return JS::OrdinaryToPrimitive(cx, obj, type, vp);
+}
+
 static void
 workerdebuggersandbox_finalize(js::FreeOp *fop, JSObject *obj)
 {
   nsIGlobalObject *globalObject =
     static_cast<nsIGlobalObject *>(JS_GetPrivate(obj));
   NS_RELEASE(globalObject);
 }
 
@@ -758,16 +770,17 @@ const js::Class workerdebuggersandbox_cl
     JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     workerdebuggersandbox_enumerate,
     workerdebuggersandbox_resolve,
     nullptr, /* mayResolve */
+    workerdebuggersandbox_convert,
     workerdebuggersandbox_finalize,
     nullptr,
     nullptr,
     nullptr,
     JS_GlobalObjectTraceHook,
     JS_NULL_CLASS_SPEC, {
       nullptr,
       nullptr,
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -87,17 +87,17 @@ XBLEnumerate(JSContext *cx, JS::Handle<J
 }
 
 static const JSClass gPrototypeJSClass = {
     "XBL prototype JSClass",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     // Our one reserved slot holds the relevant nsXBLPrototypeBinding
     JSCLASS_HAS_RESERVED_SLOTS(1),
     nullptr, nullptr, nullptr, nullptr,
-    XBLEnumerate, nullptr,
+    XBLEnumerate, nullptr, nullptr,
     nullptr, XBLFinalize,
     nullptr, nullptr, nullptr, nullptr
 };
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -325,16 +325,22 @@ typedef bool
 // hook.
 //
 // maybeObj, if non-null, is the object on which we're doing the lookup. This
 // can be nullptr: during JIT compilation we sometimes know the Class but not
 // the object.
 typedef bool
 (* JSMayResolveOp)(const JSAtomState& names, jsid id, JSObject* maybeObj);
 
+// Convert obj to the given type, returning true with the resulting value in
+// *vp on success, and returning false on error or exception.
+typedef bool
+(* JSConvertOp)(JSContext* cx, JS::HandleObject obj, JSType type,
+                JS::MutableHandleValue vp);
+
 // Finalize obj, which the garbage collector has determined to be unreachable
 // from other live objects or from GC roots.  Obviously, finalizers must never
 // store a reference to obj.
 typedef void
 (* JSFinalizeOp)(JSFreeOp* fop, JSObject* obj);
 
 // Finalizes external strings created by JS_NewExternalString.
 struct JSStringFinalizer {
@@ -459,16 +465,17 @@ typedef void
     /* Function pointer members (may be null). */                             \
     JSAddPropertyOp     addProperty;                                          \
     JSDeletePropertyOp  delProperty;                                          \
     JSGetterOp          getProperty;                                          \
     JSSetterOp          setProperty;                                          \
     JSEnumerateOp       enumerate;                                            \
     JSResolveOp         resolve;                                              \
     JSMayResolveOp      mayResolve;                                           \
+    JSConvertOp         convert;                                              \
     FinalizeOpType      finalize;                                             \
     JSNative            call;                                                 \
     JSHasInstanceOp     hasInstance;                                          \
     JSNative            construct;                                            \
     JSTraceOp           trace
 
 // Callback for the creation of constructor and prototype objects.
 typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key);
@@ -786,16 +793,18 @@ static_assert(offsetof(JSClass, getPrope
 static_assert(offsetof(JSClass, setProperty) == offsetof(Class, setProperty),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, enumerate) == offsetof(Class, enumerate),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, resolve) == offsetof(Class, resolve),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, mayResolve) == offsetof(Class, mayResolve),
               "Class and JSClass must be consistent");
+static_assert(offsetof(JSClass, convert) == offsetof(Class, convert),
+              "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, finalize) == offsetof(Class, finalize),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, call) == offsetof(Class, call),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, construct) == offsetof(Class, construct),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, hasInstance) == offsetof(Class, hasInstance),
               "Class and JSClass must be consistent");
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -324,16 +324,17 @@ class JS_FRIEND_API(BaseProxyHandler)
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
                                  ESClassValue* classValue) const;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
     virtual const char* className(JSContext* cx, HandleObject proxy) const;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
+    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
     virtual void trace(JSTracer* trc, JSObject* proxy) const;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const;
 
     // Allow proxies, wrappers in particular, to specify callability at runtime.
     // Note: These do not take const JSObject*, but they do in spirit.
     //       We are not prepared to do this, as there's little const correctness
     //       in the external APIs that handle proxies.
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -1008,16 +1008,17 @@ const Class AsmJSModuleObject::class_ = 
     JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     AsmJSModuleObject_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     AsmJSModuleObject_trace
 };
 
 AsmJSModuleObject*
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -564,16 +564,17 @@ static const Class CollatorClass = {
     JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     collator_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 collator_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1058,16 +1059,17 @@ static const Class NumberFormatClass = {
     JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     numberFormat_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1527,16 +1529,17 @@ static const Class DateTimeFormatClass =
     JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     dateTimeFormat_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 dateTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -109,16 +109,17 @@ const Class MapIteratorObject::class_ = 
     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     MapIteratorObject::finalize
 };
 
 const JSFunctionSpec MapIteratorObject::methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0),
     JS_FS_END
 };
@@ -225,16 +226,17 @@ const Class MapObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
     nullptr, // mayResolve
+    nullptr, // convert
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
 const JSPropertySpec MapObject::properties[] = {
@@ -831,16 +833,17 @@ const Class SetIteratorObject::class_ = 
     JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     SetIteratorObject::finalize
 };
 
 const JSFunctionSpec SetIteratorObject::methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
 };
@@ -971,16 +974,17 @@ const Class SetObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
     nullptr, // mayResolve
+    nullptr, // convert
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
 const JSPropertySpec SetObject::properties[] = {
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -229,17 +229,18 @@ ModuleObject::class_ = {
     JSCLASS_IS_ANONYMOUS,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
-    nullptr,        /* finalize    */
+    nullptr,        /* convert     */
+    ModuleObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ModuleObject::trace
 };
 
 #define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot)                           \
     ArrayObject&                                                              \
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1116,16 +1116,17 @@ const Class PlainObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     nullptr,  /* addProperty */
     nullptr,  /* delProperty */
     nullptr,  /* getProperty */
     nullptr,  /* setProperty */
     nullptr,  /* enumerate */
     nullptr,  /* resolve */
     nullptr,  /* mayResolve */
+    nullptr,  /* convert */
     nullptr,  /* finalize */
     nullptr,  /* call */
     nullptr,  /* hasInstance */
     nullptr,  /* construct */
     nullptr,  /* trace */
     {
         CreateObjectConstructor,
         CreateObjectPrototype,
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -170,16 +170,17 @@ const Class SimdTypeDescr::class_ = {
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     TypeDescr::finalize,
     call
 };
 
 namespace {
 
 // These classes just exist to group together various properties and so on.
 class Int8x16Defn {
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -13,17 +13,25 @@
 
 #include "vm/NativeObject-inl.h"
 
 using JS::Symbol;
 using namespace js;
 
 const Class SymbolObject::class_ = {
     "Symbol",
-    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol)
+    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol),
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    convert
 };
 
 SymbolObject*
 SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol)
 {
     JSObject* obj = NewBuiltinClassInstance(cx, &class_);
     if (!obj)
         return nullptr;
@@ -34,17 +42,16 @@ SymbolObject::create(JSContext* cx, JS::
 
 const JSPropertySpec SymbolObject::properties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec SymbolObject::methods[] = {
     JS_FN(js_toString_str, toString, 0, 0),
     JS_FN(js_valueOf_str, valueOf, 0, 0),
-    JS_SYM_FN(toPrimitive, toPrimitive, 1, JSPROP_READONLY),
     JS_FS_END
 };
 
 const JSFunctionSpec SymbolObject::staticMethods[] = {
     JS_FN("for", for_, 1, 0),
     JS_FN("keyFor", keyFor, 1, 0),
     JS_FS_END
 };
@@ -112,16 +119,24 @@ SymbolObject::construct(JSContext* cx, u
     // step 4
     RootedSymbol symbol(cx, JS::Symbol::new_(cx, JS::SymbolCode::UniqueSymbol, desc));
     if (!symbol)
         return false;
     args.rval().setSymbol(symbol);
     return true;
 }
 
+// Stand-in for Symbol.prototype[@@toPrimitive], ES6 rev 26 (2014 Jul 18) 19.4.3.4
+bool
+SymbolObject::convert(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
+{
+    vp.setSymbol(obj->as<SymbolObject>().unbox());
+    return true;
+}
+
 // ES6 rev 24 (2014 Apr 27) 19.4.2.2
 bool
 SymbolObject::for_(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // steps 1-2
     RootedString stringKey(cx, ToString(cx, args.get(0)));
@@ -210,24 +225,13 @@ SymbolObject::valueOf_impl(JSContext* cx
 
 bool
 SymbolObject::valueOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
 }
 
-// ES6 19.4.3.4
-bool
-SymbolObject::toPrimitive(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // The specification gives exactly the same algorithm for @@toPrimitive as
-    // for valueOf, so reuse the valueOf implementation.
-    return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
-}
-
 JSObject*
 js::InitSymbolClass(JSContext* cx, HandleObject obj)
 {
     return SymbolObject::initClass(cx, obj);
 }
--- a/js/src/builtin/SymbolObject.h
+++ b/js/src/builtin/SymbolObject.h
@@ -36,26 +36,27 @@ class SymbolObject : public NativeObject
 
   private:
     inline void setPrimitiveValue(JS::Symbol* symbol) {
         setFixedSlot(PRIMITIVE_VALUE_SLOT, SymbolValue(symbol));
     }
 
     static bool construct(JSContext* cx, unsigned argc, Value* vp);
 
+    static bool convert(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue vp);
+
     // Static methods.
     static bool for_(JSContext* cx, unsigned argc, Value* vp);
     static bool keyFor(JSContext* cx, unsigned argc, Value* vp);
 
     // Methods defined on Symbol.prototype.
     static bool toString_impl(JSContext* cx, const CallArgs& args);
     static bool toString(JSContext* cx, unsigned argc, Value* vp);
     static bool valueOf_impl(JSContext* cx, const CallArgs& args);
     static bool valueOf(JSContext* cx, unsigned argc, Value* vp);
-    static bool toPrimitive(JSContext* cx, unsigned argc, Value* vp);
 
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec staticMethods[];
 };
 
 extern JSObject*
 InitSymbolClass(JSContext* cx, HandleObject obj);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1106,16 +1106,17 @@ static const JSClass FinalizeCounterClas
     "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     finalize_counter_finalize
 };
 
 static bool
 MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -1798,16 +1799,17 @@ const Class CloneBufferObject::class_ = 
     "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     Finalize
 };
 
 const JSPropertySpec CloneBufferObject::props_[] = {
     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
     JS_PS_END
 };
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -220,16 +220,17 @@ const Class js::ScalarTypeDescr::class_ 
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     TypeDescr::finalize,
     ScalarTypeDescr::call
 };
 
 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
@@ -317,16 +318,17 @@ const Class js::ReferenceTypeDescr::clas
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     TypeDescr::finalize,
     ReferenceTypeDescr::call
 };
 
 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
@@ -496,16 +498,17 @@ const Class ArrayTypeDescr::class_ = {
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
@@ -724,16 +727,17 @@ const Class StructTypeDescr::class_ = {
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
@@ -2249,16 +2253,17 @@ OutlineTransparentTypedObject::getOrCrea
         Class::NON_NATIVE, \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* getProperty */                \
         nullptr,        /* setProperty */                \
         nullptr,        /* enumerate   */                \
         nullptr,        /* resolve     */                \
         nullptr,        /* mayResolve  */                \
+        nullptr,        /* convert     */                \
         nullptr,        /* finalize    */                \
         nullptr,        /* call        */                \
         nullptr,        /* hasInstance */                \
         nullptr,        /* construct   */                \
         Trace,                                           \
         JS_NULL_CLASS_SPEC,                              \
         JS_NULL_CLASS_EXT,                               \
         {                                                \
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -541,49 +541,49 @@ static const JSClass sCABIClass = {
 };
 
 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
 // This exists to give said prototypes a class of "CType", and to provide
 // reserved slots for stashing various other prototype objects.
 static const JSClass sCTypeProtoClass = {
   "CType",
   JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
   ConstructAbstract, nullptr, ConstructAbstract
 };
 
 // Class representing ctypes.CData.prototype and the 'prototype' properties
 // of CTypes. This exists to give said prototypes a class of "CData".
 static const JSClass sCDataProtoClass = {
   "CData",
   0
 };
 
 static const JSClass sCTypeClass = {
   "CType",
   JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CType::Finalize,
   CType::ConstructData, CType::HasInstance, CType::ConstructData,
   CType::Trace
 };
 
 static const JSClass sCDataClass = {
   "CData",
   JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
   nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
-  nullptr, nullptr, nullptr, CData::Finalize,
+  nullptr, nullptr, nullptr, nullptr, CData::Finalize,
   FunctionType::Call, nullptr, FunctionType::Call
 };
 
 static const JSClass sCClosureClass = {
   "CClosure",
   JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CClosure::Finalize,
   nullptr, nullptr, nullptr, CClosure::Trace
 };
 
 /*
  * Class representing the prototype of CDataFinalizer.
  */
 static const JSClass sCDataFinalizerProtoClass = {
@@ -595,17 +595,17 @@ static const JSClass sCDataFinalizerProt
  * Class representing instances of CDataFinalizer.
  *
  * Instances of CDataFinalizer have both private data (with type
  * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
  */
 static const JSClass sCDataFinalizerClass = {
   "CDataFinalizer",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CDataFinalizer::Finalize
 };
 
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CTYPESCTOR_FLAGS \
@@ -781,24 +781,24 @@ static const JSClass sInt64ProtoClass = 
 static const JSClass sUInt64ProtoClass = {
   "UInt64",
   0
 };
 
 static const JSClass sInt64Class = {
   "Int64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Int64Base::Finalize
 };
 
 static const JSClass sUInt64Class = {
   "UInt64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Int64Base::Finalize
 };
 
 static const JSFunctionSpec sInt64StaticFunctions[] = {
   JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
   JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
   JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
   // "join" is defined specially; see InitInt64Class.
@@ -5219,18 +5219,16 @@ ArrayType::Getter(JSContext* cx, HandleO
   if (CType::GetTypeCode(typeObj) != TYPE_array)
     return true;
 
   // Convert the index to a size_t and bounds-check it.
   size_t index;
   size_t length = GetLength(typeObj);
   bool ok = jsidToSize(cx, idval, true, &index);
   int32_t dummy;
-  if (!ok && JSID_IS_SYMBOL(idval))
-    return true;
   if (!ok && JSID_IS_STRING(idval) &&
       !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
     // String either isn't a number, or doesn't fit in size_t.
     // Chances are it's a regular property lookup, so return.
     return true;
   }
   if (!ok || index >= length) {
     JS_ReportError(cx, "invalid index");
@@ -5259,18 +5257,16 @@ ArrayType::Setter(JSContext* cx, HandleO
   if (CType::GetTypeCode(typeObj) != TYPE_array)
     return result.succeed();
 
   // Convert the index to a size_t and bounds-check it.
   size_t index;
   size_t length = GetLength(typeObj);
   bool ok = jsidToSize(cx, idval, true, &index);
   int32_t dummy;
-  if (!ok && JSID_IS_SYMBOL(idval))
-    return true;
   if (!ok && JSID_IS_STRING(idval) &&
       !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
     // String either isn't a number, or doesn't fit in size_t.
     // Chances are it's a regular property lookup, so return.
     return result.succeed();
   }
   if (!ok || index >= length) {
     JS_ReportError(cx, "invalid index");
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -30,17 +30,17 @@ namespace Library
 ** JSObject implementation
 *******************************************************************************/
 
 typedef Rooted<JSFlatString*>    RootedFlatString;
 
 static const JSClass sLibraryClass = {
   "Library",
   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Library::Finalize
 };
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 static const JSFunctionSpec sLibraryFunctions[] = {
   JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -12,17 +12,17 @@
 
 using namespace JS;
 
 /* The class of the global object. */
 const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 template<typename T>
 static inline T*
 checkPtr(T* ptr)
 {
   if (! ptr)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -694,17 +694,16 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
               case JSOP_IN:
               case JSOP_INSTANCEOF:
                 type = MIRType_Boolean;
                 break;
               case JSOP_DOUBLE:
                 type = MIRType_Double;
                 break;
               case JSOP_STRING:
-              case JSOP_TOSTRING:
               case JSOP_TYPEOF:
               case JSOP_TYPEOFEXPR:
                 type = MIRType_String;
                 break;
               case JSOP_SYMBOL:
                 type = MIRType_Symbol;
                 break;
               case JSOP_ADD:
@@ -1520,17 +1519,16 @@ IonBuilder::traverseBytecode()
               case JSOP_INITLEXICAL:
               case JSOP_SETRVAL:
               case JSOP_VOID:
                 // Don't require SSA uses for values popped by these ops.
                 break;
 
               case JSOP_POS:
               case JSOP_TOID:
-              case JSOP_TOSTRING:
                 // These ops may leave their input on the stack without setting
                 // the ImplicitlyUsed flag. If this value will be popped immediately,
                 // we may replace it with |undefined|, but the difference is
                 // not observable.
                 MOZ_ASSERT(i == 0);
                 if (current->peek(-1) == popped[0])
                     break;
                 // FALL THROUGH
@@ -1662,19 +1660,16 @@ IonBuilder::inspectOpcode(JSOp op)
         return jsop_pow();
 
       case JSOP_POS:
         return jsop_pos();
 
       case JSOP_NEG:
         return jsop_neg();
 
-      case JSOP_TOSTRING:
-        return jsop_tostring();
-
       case JSOP_AND:
       case JSOP_OR:
         return jsop_andor(op);
 
       case JSOP_DEFVAR:
       case JSOP_DEFCONST:
         return jsop_defvar(GET_UINT32_INDEX(pc));
 
@@ -4548,17 +4543,16 @@ IonBuilder::jsop_bitnot()
     // Not possible to optimize. Do a slow vm call.
     MBitNot* ins = MBitNot::New(alloc(), input);
 
     current->add(ins);
     current->push(ins);
     MOZ_ASSERT(ins->isEffectful());
     return resumeAfter(ins);
 }
-
 bool
 IonBuilder::jsop_bitop(JSOp op)
 {
     // Pop inputs.
     MDefinition* right = current->pop();
     MDefinition* left = current->pop();
 
     MBinaryBitwiseInstruction* ins;
@@ -4866,30 +4860,16 @@ IonBuilder::jsop_neg()
     MConstant* negator = MConstant::New(alloc(), Int32Value(-1));
     current->add(negator);
 
     MDefinition* right = current->pop();
 
     return jsop_binary_arith(JSOP_MUL, negator, right);
 }
 
-bool
-IonBuilder::jsop_tostring()
-{
-    if (current->peek(-1)->type() == MIRType_String)
-        return true;
-
-    MDefinition* value = current->pop();
-    MToString* ins = MToString::New(alloc(), value);
-    current->add(ins);
-    current->push(ins);
-    MOZ_ASSERT(!ins->isEffectful());
-    return true;
-}
-
 class AutoAccumulateReturns
 {
     MIRGraph& graph_;
     MIRGraphReturns* prev_;
 
   public:
     AutoAccumulateReturns(MIRGraph& graph, MIRGraphReturns& returns)
       : graph_(graph)
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -644,17 +644,16 @@ class IonBuilder
     bool jsop_add(MDefinition* left, MDefinition* right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary_arith(JSOp op);
     bool jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right);
     bool jsop_pow();
     bool jsop_pos();
     bool jsop_neg();
-    bool jsop_tostring();
     bool jsop_setarg(uint32_t arg);
     bool jsop_defvar(uint32_t index);
     bool jsop_deffun(uint32_t index);
     bool jsop_notearg();
     bool jsop_checklexical();
     bool jsop_checkaliasedlet(ScopeCoordinate sc);
     bool jsop_funcall(uint32_t argc);
     bool jsop_funapply(uint32_t argc);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -49,18 +49,16 @@ MSG_DEF(JSMSG_NO_CONSTRUCTOR,          1
 MSG_DEF(JSMSG_BAD_SORT_ARG,            0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
 MSG_DEF(JSMSG_CANT_WATCH,              1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}")
 MSG_DEF(JSMSG_READ_ONLY,               1, JSEXN_TYPEERR, "{0} is read-only")
 MSG_DEF(JSMSG_CANT_DELETE,             1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
 MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY,     0, JSEXN_TYPEERR, "can't delete non-configurable array element")
 MSG_DEF(JSMSG_NOT_FUNCTION,            1, JSEXN_TYPEERR, "{0} is not a function")
 MSG_DEF(JSMSG_NOT_CONSTRUCTOR,         1, JSEXN_TYPEERR, "{0} is not a constructor")
 MSG_DEF(JSMSG_CANT_CONVERT_TO,         2, JSEXN_TYPEERR, "can't convert {0} to {1}")
-MSG_DEF(JSMSG_TOPRIMITIVE_NOT_CALLABLE, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] property is not a function")
-MSG_DEF(JSMSG_TOPRIMITIVE_RETURNED_OBJECT, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] method returned an object")
 MSG_DEF(JSMSG_NO_PROPERTIES,           1, JSEXN_TYPEERR, "{0} has no properties")
 MSG_DEF(JSMSG_BAD_REGEXP_FLAG,         1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}")
 MSG_DEF(JSMSG_ARG_INDEX_OUT_OF_RANGE,  1, JSEXN_RANGEERR, "argument {0} accesses an index that is out of range")
 MSG_DEF(JSMSG_SPREAD_TOO_LARGE,        0, JSEXN_RANGEERR, "array too large due to spread operand(s)")
 MSG_DEF(JSMSG_BAD_WEAKMAP_KEY,         0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
 MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER,    1, JSEXN_TYPEERR, "invalid {0} usage")
 MSG_DEF(JSMSG_BAD_ARRAY_LENGTH,        0, JSEXN_RANGEERR, "invalid array length")
 MSG_DEF(JSMSG_REDECLARED_VAR,          2, JSEXN_TYPEERR, "redeclaration of {0} {1}")
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -56,16 +56,17 @@ UNIFIED_SOURCES += [
     'testLooselyEqual.cpp',
     'testMappedArrayBuffer.cpp',
     'testMutedErrors.cpp',
     'testNewObject.cpp',
     'testNewTargetInvokeConstructor.cpp',
     'testNullRoot.cpp',
     'testObjectEmulatingUndefined.cpp',
     'testOOM.cpp',
+    'testOps.cpp',
     'testParseJSON.cpp',
     'testPersistentRooted.cpp',
     'testPreserveJitCode.cpp',
     'testProfileStrings.cpp',
     'testPropCache.cpp',
     'testRegExp.cpp',
     'testResolveRecursion.cpp',
     'tests.cpp',
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -17,16 +17,17 @@ static const JSClass global_class = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JS::PersistentRootedObject trusted_glob;
 static JS::PersistentRootedObject trusted_fun;
 
 static bool
 CallTrusted(JSContext* cx, unsigned argc, JS::Value* vp)
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -19,18 +19,18 @@ GlobalResolve(JSContext* cx, JS::HandleO
     return JS_ResolveStandardClass(cx, obj, id, resolvedp);
 }
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
     static const JSClass cls = {
         "global", JSCLASS_GLOBAL_FLAGS,
         nullptr, nullptr, nullptr, nullptr,
-        GlobalEnumerate, GlobalResolve, nullptr, nullptr,
-        nullptr, nullptr, nullptr,
+        GlobalEnumerate, GlobalResolve, nullptr,
+        nullptr, nullptr, nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
     options.setVersion(JSVERSION_LATEST);
     JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
     if (!g)
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -98,17 +98,17 @@ BEGIN_TEST(testNewObject_1)
     CHECK(v.isInt32(N - 1));
 
     // With JSClass.construct.
     static const JSClass cls = {
         "testNewObject_1",
         0,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, constructHook
+        nullptr, nullptr, nullptr, constructHook
     };
     JS::RootedObject ctor(cx, JS_NewObject(cx, &cls));
     CHECK(ctor);
     JS::RootedValue rt2(cx, JS::ObjectValue(*ctor));
     obj = JS_New(cx, ctor, JS::HandleValueArray::subarray(argv, 0, 3));
     CHECK(obj);
     CHECK(JS_GetElement(cx, ctor, 0, &v));
     CHECK(v.isInt32(0));
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testOps.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Tests for operators and implicit type conversion.
+ */
+/* 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"
+
+static bool
+my_convert(JSContext* context, JS::HandleObject obj, JSType type, JS::MutableHandleValue rval)
+{
+    if (type == JSTYPE_VOID || type == JSTYPE_STRING || type == JSTYPE_NUMBER || type == JSTYPE_BOOLEAN) {
+        rval.set(JS_NumberValue(123));
+        return true;
+    }
+    return false;
+}
+
+static const JSClass myClass = {
+    "MyClass",
+    0,
+    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, my_convert
+};
+
+static bool
+createMyObject(JSContext* context, unsigned argc, JS::Value* vp)
+{
+    JS_BeginRequest(context);
+
+    //JS_GC(context); //<- if we make GC here, all is ok
+
+    JSObject* myObject = JS_NewObject(context, &myClass);
+    *vp = JS::ObjectOrNullValue(myObject);
+
+    JS_EndRequest(context);
+
+    return true;
+}
+
+static const JSFunctionSpec s_functions[] =
+{
+    JS_FN("createMyObject", createMyObject, 0, 0),
+    JS_FS_END
+};
+
+BEGIN_TEST(testOps_bug559006)
+{
+    CHECK(JS_DefineFunctions(cx, global, s_functions));
+
+    EXEC("function main() { while(1) return 0 + createMyObject(); }");
+
+    for (int i = 0; i < 9; i++) {
+        JS::RootedValue rval(cx);
+        CHECK(JS_CallFunctionName(cx, global, "main", JS::HandleValueArray::empty(),
+                                  &rval));
+        CHECK(rval.isInt32(123));
+    }
+    return true;
+}
+END_TEST(testOps_bug559006)
+
--- a/js/src/jsapi-tests/testPersistentRooted.cpp
+++ b/js/src/jsapi-tests/testPersistentRooted.cpp
@@ -26,16 +26,17 @@ const JSClass BarkWhenTracedClass::class
     0,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     finalize,
     nullptr,
     nullptr,
     nullptr,
     trace
 };
 
 struct Kennel {
--- a/js/src/jsapi-tests/testResolveRecursion.cpp
+++ b/js/src/jsapi-tests/testResolveRecursion.cpp
@@ -155,16 +155,17 @@ const JSClass* getGlobalClass() override
         JSCLASS_GLOBAL_FLAGS,
         nullptr, // add
         nullptr, // delete
         nullptr, // get
         nullptr, // set
         nullptr, // enumerate
         my_resolve,
         nullptr, // mayResolve
+        nullptr, // convert
         nullptr, // finalize
         nullptr, // call
         nullptr, // hasInstance
         nullptr, // construct
         JS_GlobalObjectTraceHook
     };
 
     return &myGlobalClass;
--- a/js/src/jsapi-tests/testUbiNode.cpp
+++ b/js/src/jsapi-tests/testUbiNode.cpp
@@ -5,17 +5,16 @@
 #include "builtin/TestingFunctions.h"
 #include "js/UbiNode.h"
 #include "jsapi-tests/tests.h"
 #include "vm/SavedFrame.h"
 
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
-using namespace js;
 
 // ubi::Node::zone works
 BEGIN_TEST(test_ubiNodeZone)
 {
     RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
     CHECK(global1);
     CHECK(JS::ubi::Node(global1).zone() == cx->zone());
 
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -153,16 +153,17 @@ JSObject* newKey()
         JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
         nullptr, /* resolve */
         nullptr, /* mayResolve */
+        nullptr, /* convert */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         nullptr, /* trace */
         JS_NULL_CLASS_SPEC,
         {
             nullptr,
@@ -209,16 +210,17 @@ JSObject* newDelegate()
         JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
         nullptr, /* resolve */
         nullptr, /* mayResolve */
+        nullptr, /* convert */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         JS_GlobalObjectTraceHook,
         JS_NULL_CLASS_SPEC,
         {
             nullptr,
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -225,17 +225,17 @@ class JSAPITest
 
     JSAPITestString messages() const { return msgs; }
 
     static const JSClass * basicGlobalClass() {
         static const JSClass c = {
             "global", JSCLASS_GLOBAL_FLAGS,
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr, nullptr,
-            nullptr, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr,
             JS_GlobalObjectTraceHook
         };
         return &c;
     }
 
   protected:
     static bool
     print(JSContext* cx, unsigned argc, JS::Value* vp)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1798,67 +1798,23 @@ JS_IdToValue(JSContext* cx, jsid id, Mut
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     vp.set(IdToValue(id));
     assertSameCompartment(cx, vp);
     return true;
 }
 
 JS_PUBLIC_API(bool)
-JS::ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
+JS_DefaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     MOZ_ASSERT(obj != nullptr);
     MOZ_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER);
-    vp.setObject(*obj);
-    return ToPrimitiveSlow(cx, hint, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS::GetFirstArgumentAsTypeHint(JSContext* cx, CallArgs args, JSType *result)
-{
-    if (!args.get(0).isString()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
-                             "Symbol.toPrimitive",
-                             "\"string\", \"number\", or \"default\"",
-                             InformalValueTypeName(args.get(0)));
-        return false;
-    }
-
-    RootedString str(cx, args.get(0).toString());
-    bool match;
-
-    if (!EqualStrings(cx, str, cx->names().default_, &match))
-        return false;
-    if (match) {
-        *result = JSTYPE_VOID;
-        return true;
-    }
-
-    if (!EqualStrings(cx, str, cx->names().string, &match))
-        return false;
-    if (match) {
-        *result = JSTYPE_STRING;
-        return true;
-    }
-
-    if (!EqualStrings(cx, str, cx->names().number, &match))
-        return false;
-    if (match) {
-        *result = JSTYPE_NUMBER;
-        return true;
-    }
-
-    JSAutoByteString bytes;
-    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
-                         "Symbol.toPrimitive",
-                         "\"string\", \"number\", or \"default\"",
-                         ValueToSourceForError(cx, args.get(0), bytes));
-    return false;
+    return ToPrimitive(cx, obj, hint, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_PropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
     return true;
 }
 
@@ -3356,16 +3312,32 @@ JS_NewFunction(JSContext* cx, JSNative n
     }
 
     return (flags & JSFUN_CONSTRUCTOR)
            ? NewNativeConstructor(cx, native, nargs, atom)
            : NewNativeFunction(cx, native, nargs, atom);
 }
 
 JS_PUBLIC_API(JSFunction*)
+JS_NewFunctionById(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
+                   HandleId id)
+{
+    MOZ_ASSERT(JSID_IS_STRING(id));
+    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    MOZ_ASSERT(native);
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+
+    RootedAtom name(cx, JSID_TO_ATOM(id));
+    return (flags & JSFUN_CONSTRUCTOR)
+           ? NewNativeConstructor(cx, native, nargs, name)
+           : NewNativeFunction(cx, native, nargs, name);
+}
+
+JS_PUBLIC_API(JSFunction*)
 JS::GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id, unsigned nargs)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     RootedAtom name(cx, IdToFunctionName(cx, id));
     if (!name)
@@ -3376,62 +3348,16 @@ JS::GetSelfHostedFunction(JSContext* cx,
         return nullptr;
     RootedPropertyName shName(cx, shAtom->asPropertyName());
     RootedValue funVal(cx);
     if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, name, nargs, &funVal))
         return nullptr;
     return &funVal.toObject().as<JSFunction>();
 }
 
-JS_PUBLIC_API(JSFunction*)
-JS::NewFunctionFromSpec(JSContext* cx, const JSFunctionSpec* fs, HandleId id)
-{
-    // Delay cloning self-hosted functions until they are called. This is
-    // achieved by passing DefineFunction a nullptr JSNative which produces an
-    // interpreted JSFunction where !hasScript. Interpreted call paths then
-    // call InitializeLazyFunctionScript if !hasScript.
-    if (fs->selfHostedName) {
-        MOZ_ASSERT(!fs->call.op);
-        MOZ_ASSERT(!fs->call.info);
-
-        JSAtom* shAtom = Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName));
-        if (!shAtom)
-            return nullptr;
-        RootedPropertyName shName(cx, shAtom->asPropertyName());
-        RootedAtom name(cx, IdToFunctionName(cx, id));
-        if (!name)
-            return nullptr;
-        RootedValue funVal(cx);
-        if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, name, fs->nargs,
-                                                 &funVal))
-        {
-            return nullptr;
-        }
-        return &funVal.toObject().as<JSFunction>();
-    }
-
-    RootedAtom atom(cx, IdToFunctionName(cx, id));
-    if (!atom)
-        return nullptr;
-
-    JSFunction* fun;
-    if (!fs->call.op)
-        fun = NewScriptedFunction(cx, fs->nargs, JSFunction::INTERPRETED_LAZY, atom);
-    else if (fs->flags & JSFUN_CONSTRUCTOR)
-        fun = NewNativeConstructor(cx, fs->call.op, fs->nargs, atom);
-    else
-        fun = NewNativeFunction(cx, fs->call.op, fs->nargs, atom);
-    if (!fun)
-        return nullptr;
-
-    if (fs->call.info)
-        fun->setJitInfo(fs->call.info);
-    return fun;
-}
-
 static bool
 CreateNonSyntacticScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                              MutableHandleObject dynamicScopeObj,
                              MutableHandle<ScopeObject*> staticScopeObj)
 {
     if (!js::CreateScopeObjectsForScopeChain(cx, scopeChain, cx->global(), dynamicScopeObj))
         return false;
 
@@ -3646,95 +3572,104 @@ GenericNativeMethodDispatcher(JSContext*
     memmove(vp + 1, vp + 2, argc * sizeof(Value));
 
     /* Clear the last parameter in case too few arguments were passed. */
     vp[2 + --argc].setUndefined();
 
     return fs->call.op(cx, argc, vp);
 }
 
-static bool
-DefineFunctionFromSpec(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, unsigned flags)
-{
-    GetterOp gop;
-    SetterOp sop;
-    if (flags & JSFUN_STUB_GSOPS) {
-        // JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
-        // the defined property's attributes.
-        flags &= ~JSFUN_STUB_GSOPS;
-        gop = nullptr;
-        sop = nullptr;
-    } else {
-        gop = obj->getClass()->getProperty;
-        sop = obj->getClass()->setProperty;
-        MOZ_ASSERT(gop != JS_PropertyStub);
-        MOZ_ASSERT(sop != JS_StrictPropertyStub);
-    }
-
-    RootedId id(cx);
-    if (!PropertySpecNameToId(cx, fs->name, &id))
-        return false;
-
-    // Define a generic arity N+1 static method for the arity N prototype
-    // method if flags contains JSFUN_GENERIC_NATIVE.
-    if (flags & JSFUN_GENERIC_NATIVE) {
-        // We require that any consumers using JSFUN_GENERIC_NATIVE stash
-        // the prototype and constructor in the global slots before invoking
-        // JS_DefineFunctions on the proto.
-        JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
-        MOZ_ASSERT(obj == &obj->global().getPrototype(key).toObject());
-        RootedObject ctor(cx, &obj->global().getConstructor(key).toObject());
-
-        flags &= ~JSFUN_GENERIC_NATIVE;
-        JSFunction* fun = DefineFunction(cx, ctor, id,
-                                         GenericNativeMethodDispatcher,
-                                         fs->nargs + 1, flags,
-                                         gc::AllocKind::FUNCTION_EXTENDED);
-        if (!fun)
-            return false;
-
-        // As jsapi.h notes, fs must point to storage that lives as long
-        // as fun->object lives.
-        fun->setExtendedSlot(0, PrivateValue(const_cast<JSFunctionSpec*>(fs)));
-    }
-
-    JSFunction* fun = NewFunctionFromSpec(cx, fs, id);
-    if (!fun)
-        return false;
-
-    RootedValue funVal(cx, ObjectValue(*fun));
-    return DefineProperty(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK);
-}
-
 JS_PUBLIC_API(bool)
 JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
                    PropertyDefinitionBehavior behavior)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
+    RootedId id(cx);
     for (; fs->name; fs++) {
+        if (!PropertySpecNameToId(cx, fs->name, &id))
+            return false;
+
         unsigned flags = fs->flags;
         switch (behavior) {
           case DefineAllProperties:
             break;
           case OnlyDefineLateProperties:
             if (!(flags & JSPROP_DEFINE_LATE))
                 continue;
             break;
           default:
             MOZ_ASSERT(behavior == DontDefineLateProperties);
             if (flags & JSPROP_DEFINE_LATE)
                 continue;
         }
-
-        if (!DefineFunctionFromSpec(cx, obj, fs, flags & ~JSPROP_DEFINE_LATE))
-            return false;
+        flags &= ~JSPROP_DEFINE_LATE;
+
+        /*
+         * Define a generic arity N+1 static method for the arity N prototype
+         * method if flags contains JSFUN_GENERIC_NATIVE.
+         */
+        if (flags & JSFUN_GENERIC_NATIVE) {
+            // We require that any consumers using JSFUN_GENERIC_NATIVE stash
+            // the prototype and constructor in the global slots before invoking
+            // JS_DefineFunctions on the proto.
+            JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
+            MOZ_ASSERT(obj == &obj->global().getPrototype(key).toObject());
+            RootedObject ctor(cx, &obj->global().getConstructor(key).toObject());
+
+            flags &= ~JSFUN_GENERIC_NATIVE;
+            JSFunction* fun = DefineFunction(cx, ctor, id,
+                                             GenericNativeMethodDispatcher,
+                                             fs->nargs + 1, flags,
+                                             gc::AllocKind::FUNCTION_EXTENDED);
+            if (!fun)
+                return false;
+
+            /*
+             * As jsapi.h notes, fs must point to storage that lives as long
+             * as fun->object lives.
+             */
+            fun->setExtendedSlot(0, PrivateValue(const_cast<JSFunctionSpec*>(fs)));
+        }
+
+        /*
+         * Delay cloning self-hosted functions until they are called. This is
+         * achieved by passing DefineFunction a nullptr JSNative which
+         * produces an interpreted JSFunction where !hasScript. Interpreted
+         * call paths then call InitializeLazyFunctionScript if !hasScript.
+         */
+        if (fs->selfHostedName) {
+            MOZ_ASSERT(!fs->call.op);
+            MOZ_ASSERT(!fs->call.info);
+
+            JSAtom* shAtom = Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName));
+            if (!shAtom)
+                return false;
+            RootedPropertyName shName(cx, shAtom->asPropertyName());
+            RootedAtom name(cx, IdToFunctionName(cx, id));
+            if (!name)
+                return false;
+            RootedValue funVal(cx);
+            if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, name, fs->nargs,
+                                                     &funVal))
+            {
+                return false;
+            }
+            if (!DefineProperty(cx, obj, id, funVal, nullptr, nullptr, flags))
+                return false;
+        } else {
+            JSFunction* fun = DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags);
+            if (!fun)
+                return false;
+            if (fs->call.info)
+                fun->setJitInfo(fs->call.info);
+        }
     }
     return true;
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_DefineFunction(JSContext* cx, HandleObject obj, const char* name, JSNative call,
                   unsigned nargs, unsigned attrs)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1881,41 +1881,25 @@ extern JS_PUBLIC_API(bool)
 JS_ValueToId(JSContext* cx, JS::HandleValue v, JS::MutableHandleId idp);
 
 extern JS_PUBLIC_API(bool)
 JS_StringToId(JSContext* cx, JS::HandleString s, JS::MutableHandleId idp);
 
 extern JS_PUBLIC_API(bool)
 JS_IdToValue(JSContext* cx, jsid id, JS::MutableHandle<JS::Value> vp);
 
-namespace JS {
-
-/**
- * Convert obj to a primitive value. On success, store the result in vp and
- * return true.
- *
- * The hint argument must be JSTYPE_STRING, JSTYPE_NUMBER, or JSTYPE_VOID (no
- * hint).
- *
- * Implements: ES6 7.1.1 ToPrimitive(input, [PreferredType]).
+/*
+ * Invoke the [[DefaultValue]] hook (see ES5 8.6.2) with the provided hint on
+ * the specified object, computing a primitive default value for the object.
+ * The hint must be JSTYPE_STRING, JSTYPE_NUMBER, or JSTYPE_VOID (no hint).  On
+ * success the resulting value is stored in *vp.
  */
 extern JS_PUBLIC_API(bool)
-ToPrimitive(JSContext* cx, JS::HandleObject obj, JSType hint, JS::MutableHandleValue vp);
-
-/**
- * If args.get(0) is one of the strings "string", "number", or "default", set
- * *result to JSTYPE_STRING, JSTYPE_NUMBER, or JSTYPE_VOID accordingly and
- * return true. Otherwise, return false with a TypeError pending.
- *
- * This can be useful in implementing a @@toPrimitive method.
- */
-extern JS_PUBLIC_API(bool)
-GetFirstArgumentAsTypeHint(JSContext* cx, CallArgs args, JSType *result);
-
-} /* namespace JS */
+JS_DefaultValue(JSContext* cx, JS::Handle<JSObject*> obj, JSType hint,
+                JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(bool)
 JS_PropertyStub(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                 JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_StrictPropertyStub(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                       JS::MutableHandleValue vp, JS::ObjectOpResult& result);
@@ -2112,17 +2096,17 @@ struct JSFunctionSpec {
  * @@iterator method.
  */
 #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_INLINABLE_FN(name,call,nargs,flags,native)                         \
     JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
-#define JS_SYM_FN(symbol,call,nargs,flags)                                    \
+#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)
 #define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName)       \
@@ -3123,33 +3107,30 @@ JS_SetReservedSlot(JSObject* obj, uint32
 
 /*
  * Functions and scripts.
  */
 extern JS_PUBLIC_API(JSFunction*)
 JS_NewFunction(JSContext* cx, JSNative call, unsigned nargs, unsigned flags,
                const char* name);
 
+/*
+ * Create the function with the name given by the id. JSID_IS_STRING(id) must
+ * be true.
+ */
+extern JS_PUBLIC_API(JSFunction*)
+JS_NewFunctionById(JSContext* cx, JSNative call, unsigned nargs, unsigned flags,
+                   JS::Handle<jsid> id);
+
 namespace JS {
 
 extern JS_PUBLIC_API(JSFunction*)
-GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id,
+GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, JS::Handle<jsid> id,
                       unsigned nargs);
 
-/**
- * Create a new function based on the given JSFunctionSpec, *fs.
- * id is the result of a successful call to
- * `PropertySpecNameToPermanentId(cx, fs->name, &id)`.
- *
- * Unlike JS_DefineFunctions, this does not treat fs as an array.
- * *fs must not be JS_FS_END.
- */
-extern JS_PUBLIC_API(JSFunction*)
-NewFunctionFromSpec(JSContext* cx, const JSFunctionSpec* fs, HandleId id);
-
 } /* namespace JS */
 
 extern JS_PUBLIC_API(JSObject*)
 JS_GetFunctionObject(JSFunction* fun);
 
 /*
  * Return the function's identifier as a JSString, or null if fun is unnamed.
  * The returned string lives as long as fun, so you don't need to root a saved
@@ -4381,26 +4362,25 @@ GetSymbolFor(JSContext* cx, HandleString
  * 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. */
 enum class SymbolCode : uint32_t {
-    iterator,                       // well-known symbols
-    match,
-    species,
-    toPrimitive,
+    iterator,                       // Symbol.iterator
+    match,                          // Symbol.match
+    species,                        // Symbol.species
     InSymbolRegistry = 0xfffffffe,  // created by Symbol.for() or JS::GetSymbolFor()
     UniqueSymbol = 0xffffffff       // created by Symbol() or JS::NewSymbol()
 };
 
 /* For use in loops that iterate over the well-known symbols. */
-const size_t WellKnownSymbolLimit = 4;
+const size_t WellKnownSymbolLimit = 3;
 
 /*
  * 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);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3269,16 +3269,17 @@ const Class ArrayObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_CALLBACK,
     array_addProperty,
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     {
         GenericCreateConstructor<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
         CreateArrayPrototype,
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -513,16 +513,25 @@ MakeTime(double hour, double min, double
     /* Steps 6-7. */
     return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
 }
 
 /**
  * end of ECMA 'support' functions
  */
 
+static bool
+date_convert(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
+{
+    MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
+    MOZ_ASSERT(obj->is<DateObject>());
+
+    return JS::OrdinaryToPrimitive(cx, obj, hint == JSTYPE_VOID ? JSTYPE_STRING : hint, vp);
+}
+
 /* for use by date_parse */
 
 static const char* const wtb[] = {
     "am", "pm",
     "monday", "tuesday", "wednesday", "thursday", "friday",
     "saturday", "sunday",
     "january", "february", "march", "april", "may", "june",
     "july", "august", "september", "october", "november", "december",
@@ -2898,40 +2907,16 @@ date_valueOf_impl(JSContext* cx, const C
 
 bool
 js::date_valueOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
 }
 
-// ES6 20.3.4.45 Date.prototype[@@toPrimitive]
-static bool
-date_toPrimitive(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // Steps 1-2.
-    if (!args.thisv().isObject()) {
-        ReportIncompatible(cx, args);
-        return false;
-    }
-
-    // Steps 3-5.
-    JSType hint;
-    if (!GetFirstArgumentAsTypeHint(cx, args, &hint))
-        return false;
-    if (hint == JSTYPE_VOID)
-        hint = JSTYPE_STRING;
-
-    args.rval().set(args.thisv());
-    RootedObject obj(cx, &args.thisv().toObject());
-    return OrdinaryToPrimitive(cx, obj, hint, args.rval());
-}
-
 static const JSFunctionSpec date_static_methods[] = {
     JS_FN("UTC",                 date_UTC,                7,0),
     JS_FN("parse",               date_parse,              1,0),
     JS_FN("now",                 date_now,                0,0),
     JS_FS_END
 };
 
 static const JSFunctionSpec date_methods[] = {
@@ -2985,17 +2970,16 @@ static const JSFunctionSpec date_methods
     JS_FN("toTimeString",        date_toTimeString,       0,0),
     JS_FN("toISOString",         date_toISOString,        0,0),
     JS_FN(js_toJSON_str,         date_toJSON,             1,0),
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,       date_toSource,           0,0),
 #endif
     JS_FN(js_toString_str,       date_toString,           0,0),
     JS_FN(js_valueOf_str,        date_valueOf,            0,0),
-    JS_SYM_FN(toPrimitive,       date_toPrimitive,        1,JSPROP_READONLY),
     JS_FS_END
 };
 
 static bool
 NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t)
 {
     JSObject* obj = NewDateObjectMsec(cx, t);
     if (!obj)
@@ -3177,16 +3161,17 @@ const Class DateObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    date_convert,
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     {
         GenericCreateConstructor<DateConstructor, 7, gc::AllocKind::FUNCTION>,
         CreateDatePrototype,
@@ -3203,16 +3188,17 @@ const Class DateObject::protoClass_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace  */
     {
         DELEGATED_CLASSSPEC(&DateObject::class_.spec),
         nullptr,
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -72,16 +72,17 @@ static const JSFunctionSpec exception_me
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
         nullptr,                 /* addProperty */ \
         nullptr,                 /* delProperty */ \
         nullptr,                 /* getProperty */ \
         nullptr,                 /* setProperty */ \
         nullptr,                 /* enumerate */ \
         nullptr,                 /* resolve */ \
         nullptr,                 /* mayResolve */ \
+        nullptr,                 /* convert */ \
         exn_finalize, \
         nullptr,                 /* call        */ \
         nullptr,                 /* hasInstance */ \
         nullptr,                 /* construct   */ \
         nullptr,                 /* trace       */ \
         { \
             ErrorObject::createConstructor, \
             ErrorObject::createProto, \
@@ -102,16 +103,17 @@ ErrorObject::classes[JSEXN_LIMIT] = {
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
         nullptr,                 /* addProperty */
         nullptr,                 /* delProperty */
         nullptr,                 /* getProperty */
         nullptr,                 /* setProperty */
         nullptr,                 /* enumerate */
         nullptr,                 /* resolve */
         nullptr,                 /* mayResolve */
+        nullptr,                 /* convert */
         exn_finalize,
         nullptr,                 /* call        */
         nullptr,                 /* hasInstance */
         nullptr,                 /* construct   */
         nullptr,                 /* trace       */
         {
             ErrorObject::createConstructor,
             ErrorObject::createProto,
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -327,16 +327,17 @@ namespace js {
             flags,                                                                      \
         nullptr,                 /* addProperty */                                      \
         nullptr,                 /* delProperty */                                      \
         nullptr,                 /* getProperty */                                      \
         nullptr,                 /* setProperty */                                      \
         nullptr,                 /* enumerate */                                        \
         nullptr,                 /* resolve */                                          \
         nullptr,                 /* mayResolve */                                       \
+        js::proxy_Convert,                                                              \
         js::proxy_Finalize,      /* finalize    */                                      \
         nullptr,                 /* call        */                                      \
         js::proxy_HasInstance,   /* hasInstance */                                      \
         nullptr,                 /* construct   */                                      \
         js::proxy_Trace,         /* trace       */                                      \
         JS_NULL_CLASS_SPEC,                                                             \
         ext,                                                                            \
         {                                                                               \
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -816,16 +816,17 @@ const Class JSFunction::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     fun_enumerate,
     fun_resolve,
     fun_mayResolve,
+    nullptr,                 /* convert     */
     nullptr,                 /* finalize    */
     nullptr,                 /* call        */
     fun_hasInstance,
     nullptr,                 /* construct   */
     fun_trace,
     {
         CreateFunctionConstructor,
         CreateFunctionPrototype,
@@ -2217,17 +2218,18 @@ js::IdToFunctionName(JSContext* cx, Hand
     }
 
     RootedValue idv(cx, IdToValue(id));
     return ToAtom<CanGC>(cx, idv);
 }
 
 JSFunction*
 js::DefineFunction(JSContext* cx, HandleObject obj, HandleId id, Native native,
-                   unsigned nargs, unsigned flags, AllocKind allocKind /* = AllocKind::FUNCTION */)
+                   unsigned nargs, unsigned flags, AllocKind allocKind /* = AllocKind::FUNCTION */,
+                   NewObjectKind newKind /* = GenericObject */)
 {
     GetterOp gop;
     SetterOp sop;
     if (flags & JSFUN_STUB_GSOPS) {
         /*
          * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
          * the defined property's attributes. This allows us to encode another,
          * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
@@ -2246,21 +2248,21 @@ js::DefineFunction(JSContext* cx, Handle
     RootedAtom atom(cx, IdToFunctionName(cx, id));
     if (!atom)
         return nullptr;
 
     RootedFunction fun(cx);
     if (!native)
         fun = NewScriptedFunction(cx, nargs,
                                   JSFunction::INTERPRETED_LAZY, atom,
-                                  allocKind, GenericObject, obj);
+                                  allocKind, newKind, obj);
     else if (flags & JSFUN_CONSTRUCTOR)
-        fun = NewNativeConstructor(cx, native, nargs, atom, allocKind);
+        fun = NewNativeConstructor(cx, native, nargs, atom, allocKind, newKind);
     else
-        fun = NewNativeFunction(cx, native, nargs, atom, allocKind);
+        fun = NewNativeFunction(cx, native, nargs, atom, allocKind, newKind);
 
     if (!fun)
         return nullptr;
 
     RootedValue funVal(cx, ObjectValue(*fun));
     if (!DefineProperty(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK))
         return nullptr;
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -631,17 +631,18 @@ NewFunctionWithProto(ExclusiveContext* c
                      NewObjectKind newKind = GenericObject);
 
 extern JSAtom*
 IdToFunctionName(JSContext* cx, HandleId id);
 
 extern JSFunction*
 DefineFunction(JSContext* cx, HandleObject obj, HandleId id, JSNative native,
                unsigned nargs, unsigned flags,
-               gc::AllocKind allocKind = gc::AllocKind::FUNCTION);
+               gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
+               NewObjectKind newKind = GenericObject);
 
 bool
 FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
 
 extern bool
 fun_toString(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -999,16 +999,17 @@ const Class PropertyIteratorObject::clas
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     finalize,
     nullptr, /* call        */
     nullptr, /* hasInstance */
     nullptr, /* construct   */
     trace
 };
 
 static const Class ArrayIteratorPrototypeClass = {
@@ -1360,16 +1361,17 @@ const Class StopIterationObject::class_ 
     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     stopiter_hasInstance
 };
 
 /* static */ bool
 GlobalObject::initIteratorClasses(JSContext* cx, Handle<GlobalObject*> global)
 {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2752,16 +2752,28 @@ js::GetPropertyDescriptor(JSContext* cx,
             return false;
     }
 
     MOZ_ASSERT(!desc.object());
     return true;
 }
 
 bool
+js::ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
+{
+    bool ok;
+    if (JSConvertOp op = obj->getClass()->convert)
+        ok = op(cx, obj, hint, vp);
+    else
+        ok = JS::OrdinaryToPrimitive(cx, obj, hint, vp);
+    MOZ_ASSERT_IF(ok, vp.isPrimitive());
+    return ok;
+}
+
+bool
 js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
 {
     RootedObject obj(cx, GetInnerObject(origObj));
     if (obj->isNative()) {
         // Use sparse indexes for watched objects, as dense elements can be
         // written to without checking the watchpoint map.
         if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
             return false;
@@ -2855,62 +2867,36 @@ js::HasDataProperty(JSContext* cx, Nativ
             *vp = obj->getSlot(shape->slot());
             return true;
         }
     }
 
     return false;
 }
 
-
-/*** ToPrimitive *************************************************************/
-
 /*
- * Gets |obj[id]|.  If that value's not callable, returns true and stores an
- * object value in *vp.  If it's callable, calls it with no arguments and |obj|
- * as |this|, returning the result in *vp.
+ * Gets |obj[id]|.  If that value's not callable, returns true and stores a
+ * non-primitive value in *vp.  If it's callable, calls it with no arguments
+ * and |obj| as |this|, returning the result in *vp.
  *
- * This is a mini-abstraction for ES6 draft rev 36 (2015 Mar 17),
- * 7.1.1, second algorithm (OrdinaryToPrimitive), steps 5.a-c.
+ * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
+ * or steps 3-4.
  */
 static bool
 MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
     if (!GetProperty(cx, obj, obj, id, vp))
         return false;
     if (!IsCallable(vp)) {
         vp.setObject(*obj);
         return true;
     }
     return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
 }
 
-static bool
-ReportCantConvert(JSContext* cx, unsigned errorNumber, HandleObject obj, JSType hint)
-{
-    const Class* clasp = obj->getClass();
-
-    // Avoid recursive death when decompiling in ReportValueError.
-    RootedString str(cx);
-    if (hint == JSTYPE_STRING) {
-        str = JS_AtomizeAndPinString(cx, clasp->name);
-        if (!str)
-            return false;
-    } else {
-        str = nullptr;
-    }
-
-    RootedValue val(cx, ObjectValue(*obj));
-    ReportValueError2(cx, errorNumber, JSDVG_SEARCH_STACK, val, str,
-                      hint == JSTYPE_VOID
-                      ? "primitive type"
-                      : hint == JSTYPE_STRING ? "string" : "number");
-    return false;
-}
-
 bool
 JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
 {
     MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
 
     Rooted<jsid> id(cx);
 
     const Class* clasp = obj->getClass();
@@ -2932,96 +2918,69 @@ JS::OrdinaryToPrimitive(JSContext* cx, H
             return true;
 
         id = NameToId(cx->names().valueOf);
         if (!MaybeCallMethod(cx, obj, id, vp))
             return false;
         if (vp.isPrimitive())
             return true;
     } else {
-        id = NameToId(cx->names().valueOf);
 
         /* Optimize new String(...).valueOf(). */
         if (clasp == &StringObject::class_) {
+            id = NameToId(cx->names().valueOf);
             StringObject* nobj = &obj->as<StringObject>();
             if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
                 vp.setString(nobj->unbox());
                 return true;
             }
         }
 
         /* Optimize new Number(...).valueOf(). */
         if (clasp == &NumberObject::class_) {
+            id = NameToId(cx->names().valueOf);
             NumberObject* nobj = &obj->as<NumberObject>();
             if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) {
                 vp.setNumber(nobj->unbox());
                 return true;
             }
         }
 
+        id = NameToId(cx->names().valueOf);
         if (!MaybeCallMethod(cx, obj, id, vp))
             return false;
         if (vp.isPrimitive())
             return true;
 
         id = NameToId(cx->names().toString);
         if (!MaybeCallMethod(cx, obj, id, vp))
             return false;
         if (vp.isPrimitive())
             return true;
     }
 
-    return ReportCantConvert(cx, JSMSG_CANT_CONVERT_TO, obj, hint);
+    /* Avoid recursive death when decompiling in ReportValueError. */
+    RootedString str(cx);
+    if (hint == JSTYPE_STRING) {
+        str = JS_AtomizeAndPinString(cx, clasp->name);
+        if (!str)
+            return false;
+    } else {
+        str = nullptr;
+    }
+
+    RootedValue val(cx, ObjectValue(*obj));
+    ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, val, str,
+                      hint == JSTYPE_VOID
+                      ? "primitive type"
+                      : hint == JSTYPE_STRING ? "string" : "number");
+    return false;
 }
 
 bool
-js::ToPrimitiveSlow(JSContext* cx, JSType preferredType, MutableHandleValue vp)
-{
-    // Step numbers refer to the first algorithm listed in ES6 draft rev 36
-    // (2015 Mar 17) 7.1.1 ToPrimitive.
-    MOZ_ASSERT(preferredType == JSTYPE_VOID ||
-               preferredType == JSTYPE_STRING ||
-               preferredType == JSTYPE_NUMBER);
-    RootedObject obj(cx, &vp.toObject());
-
-    // Steps 4-5.
-    RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toPrimitive));
-    RootedValue method(cx);
-    if (!GetProperty(cx, obj, obj, id, &method))
-        return false;
-
-    // Step 6.
-    if (!method.isUndefined()) {
-        // Step 6 of GetMethod. Invoke() below would do this check and throw a
-        // TypeError anyway, but this produces a better error message.
-        if (!IsCallable(method))
-            return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_NOT_CALLABLE, obj, preferredType);
-
-        // Steps 1-3.
-        RootedValue hint(cx, StringValue(preferredType == JSTYPE_STRING ? cx->names().string :
-                                         preferredType == JSTYPE_NUMBER ? cx->names().number :
-                                         cx->names().default_));
-
-        // Steps 6.a-b.
-        if (!Invoke(cx, vp, method, 1, hint.address(), vp))
-            return false;
-
-        // Steps 6.c-d.
-        if (vp.isObject())
-            return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_RETURNED_OBJECT, obj, preferredType);
-        return true;
-    }
-
-    return OrdinaryToPrimitive(cx, obj, preferredType, vp);
-}
-
-
-/* * */
-
-bool
 js::IsDelegate(JSContext* cx, HandleObject obj, const js::Value& v, bool* result)
 {
     if (v.isPrimitive()) {
         *result = false;
         return true;
     }
     return IsDelegateOfObject(cx, obj, &v.toObject(), result);
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1003,35 +1003,28 @@ HasOwnProperty(JSContext* cx, HandleObje
  */
 extern bool
 WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
 
 /* Clear a watchpoint. */
 extern bool
 UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id);
 
-/* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
+/*
+ * ToPrimitive support, currently implemented like an internal method (JSClass::convert).
+ * In ES6 this is just a method, @@toPrimitive. See bug 1054756.
+ */
 extern bool
-ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
+ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp);
 
-inline bool
-ToPrimitive(JSContext* cx, MutableHandleValue vp)
-{
-    if (vp.isPrimitive())
-        return true;
-    return ToPrimitiveSlow(cx, JSTYPE_VOID, vp);
-}
+MOZ_ALWAYS_INLINE bool
+ToPrimitive(JSContext* cx, MutableHandleValue vp);
 
-inline bool
-ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
-{
-    if (vp.isPrimitive())
-        return true;
-    return ToPrimitiveSlow(cx, preferredType, vp);
-}
+MOZ_ALWAYS_INLINE bool
+ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp);
 
 /*
  * toString support. (This isn't called GetClassName because there's a macro in
  * <windows.h> with that name.)
  */
 extern const char*
 GetObjectClassName(JSContext* cx, HandleObject obj);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -562,16 +562,60 @@ HasObjectValueOf(JSObject* obj, JSContex
         obj = obj->getProto();
         if (!obj || obj->is<ProxyObject>() || !obj->isNative())
             return false;
     }
 
     return IsNativeFunction(v, obj_valueOf);
 }
 
+/* ES5 9.1 ToPrimitive(input). */
+MOZ_ALWAYS_INLINE bool
+ToPrimitive(JSContext* cx, MutableHandleValue vp)
+{
+    if (vp.isPrimitive())
+        return true;
+
+    JSObject* obj = &vp.toObject();
+
+    /* Optimize new String(...).valueOf(). */
+    if (obj->is<StringObject>()) {
+        jsid id = NameToId(cx->names().valueOf);
+        StringObject* nobj = &obj->as<StringObject>();
+        if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
+            vp.setString(nobj->unbox());
+            return true;
+        }
+    }
+
+    /* Optimize new Number(...).valueOf(). */
+    if (obj->is<NumberObject>()) {
+        jsid id = NameToId(cx->names().valueOf);
+        NumberObject* nobj = &obj->as<NumberObject>();
+        if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) {
+            vp.setNumber(nobj->unbox());
+            return true;
+        }
+    }
+
+    RootedObject objRoot(cx, obj);
+    return ToPrimitive(cx, objRoot, JSTYPE_VOID, vp);
+}
+
+/* ES5 9.1 ToPrimitive(input, PreferredType). */
+MOZ_ALWAYS_INLINE bool
+ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
+{
+    MOZ_ASSERT(preferredType != JSTYPE_VOID); /* Use the other ToPrimitive! */
+    if (vp.isPrimitive())
+        return true;
+    RootedObject obj(cx, &vp.toObject());
+    return ToPrimitive(cx, obj, preferredType, vp);
+}
+
 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
 inline bool
 ToPropertyKey(JSContext* cx, Value argument, MutableHandleId result)
 {
     // Steps 1-2.
     RootedValue key(cx, argument);
     if (!ToPrimitive(cx, JSTYPE_STRING, &key))
         return false;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1512,16 +1512,17 @@ const Class ScriptSourceObject::class_ =
     JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     trace
 };
 
 ScriptSourceObject*
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -617,16 +617,17 @@ const Class WeakMapObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     WeakMap_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WeakMap_mark
 };
 
 static const JSFunctionSpec weak_map_methods[] = {
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -59,16 +59,19 @@ class JS_FRIEND_API(Wrapper) : public Di
   public:
     using BaseProxyHandler::Action;
 
     enum Flags {
         CROSS_COMPARTMENT = 1 << 0,
         LAST_USED_FLAG = CROSS_COMPARTMENT
     };
 
+    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint,
+                              MutableHandleValue vp) const override;
+
     static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
                          const WrapperOptions& options = WrapperOptions());
 
     static JSObject* Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler);
 
     static const Wrapper* wrapperHandler(JSObject* wrapper);
 
     static JSObject* wrappedObject(JSObject* wrapper);
@@ -147,16 +150,18 @@ class JS_FRIEND_API(CrossCompartmentWrap
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
                              bool* bp) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
+    virtual bool defaultValue(JSContext* cx, HandleObject wrapper, JSType hint,
+                              MutableHandleValue vp) const override;
 
     static const CrossCompartmentWrapper singleton;
     static const CrossCompartmentWrapper singletonWithPrototype;
 };
 
 class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrapper
 {
   public:
@@ -201,16 +206,17 @@ class JS_FRIEND_API(OpaqueCrossCompartme
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
                                               AutoIdVector& props) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
                                  ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject obj,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
+    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const override;
 
     static const OpaqueCrossCompartmentWrapper singleton;
 };
 
 /*
  * Base class for security wrappers. A security wrapper is potentially hiding
  * all or part of some wrapped object thus SecurityWrapper defaults to denying
  * access to the wrappee. This is the opposite of Wrapper which tries to be
@@ -242,16 +248,18 @@ class JS_FRIEND_API(SecurityWrapper) : p
 
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
                                  ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
+    virtual bool defaultValue(JSContext* cx, HandleObject wrapper, JSType hint,
+                              MutableHandleValue vp) const override;
 
     // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
     // against.
 
     virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                        JS::HandleObject callable) const override;
     virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
 
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -158,17 +158,17 @@ static const struct pm_const {
 
 #undef CONSTANT
 
 static bool pm_construct(JSContext* cx, unsigned argc, Value* vp);
 static void pm_finalize(JSFreeOp* fop, JSObject* obj);
 
 static const JSClass pm_class = {
     "PerfMeasurement", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, pm_finalize
 };
 
 // Constructor and destructor
 
 static bool
 pm_construct(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -288,16 +288,23 @@ BaseProxyHandler::regexp_toShared(JSCont
 bool
 BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const
 {
     vp.setUndefined();
     return true;
 }
 
 bool
+BaseProxyHandler::defaultValue(JSContext* cx, HandleObject proxy, JSType hint,
+                               MutableHandleValue vp) const
+{
+    return OrdinaryToPrimitive(cx, proxy, hint, vp);
+}
+
+bool
 BaseProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                              const CallArgs& args) const
 {
     ReportIncompatible(cx, args);
     return false;
 }
 
 bool
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -412,16 +412,26 @@ bool
 CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
 {
     PIERCE(cx, wrapper,
            NOTHING,
            Wrapper::boxedValue_unbox(cx, wrapper, vp),
            cx->compartment()->wrap(cx, vp));
 }
 
+bool
+CrossCompartmentWrapper::defaultValue(JSContext* cx, HandleObject wrapper, JSType hint,
+                                      MutableHandleValue vp) const
+{
+    PIERCE(cx, wrapper,
+           NOTHING,
+           Wrapper::defaultValue(cx, wrapper, hint, vp),
+           cx->compartment()->wrap(cx, vp));
+}
+
 const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
 
 bool
 js::IsCrossCompartmentWrapper(JSObject* obj)
 {
     return IsWrapper(obj) &&
            !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -143,16 +143,24 @@ DeadObjectProxy::fun_toString(JSContext*
 
 bool
 DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
 {
     ReportDead(cx);
     return false;
 }
 
+bool
+DeadObjectProxy::defaultValue(JSContext* cx, HandleObject obj, JSType hint,
+                              MutableHandleValue vp) const
+{
+    ReportDead(cx);
+    return false;
+}
+
 const char DeadObjectProxy::family = 0;
 const DeadObjectProxy DeadObjectProxy::singleton;
 
 
 bool
 js::IsDeadProxyObject(JSObject* obj)
 {
     return obj->is<ProxyObject>() &&
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -44,16 +44,18 @@ class DeadObjectProxy : public BaseProxy
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
                                  ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
+    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint,
+                              MutableHandleValue vp) const override;
 
     static const char family;
     static const DeadObjectProxy singleton;
 };
 
 bool
 IsDeadProxyObject(JSObject* obj);
 
--- a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
+++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
@@ -177,9 +177,16 @@ JSString*
 OpaqueCrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject proxy,
                                             unsigned indent) const
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, js_Function_str,
                          js_toString_str, "object");
     return nullptr;
 }
 
+bool
+OpaqueCrossCompartmentWrapper::defaultValue(JSContext* cx, HandleObject wrapper, JSType hint,
+                                            MutableHandleValue vp) const
+{
+    return OrdinaryToPrimitive(cx, wrapper, hint, vp);
+}
+
 const OpaqueCrossCompartmentWrapper OpaqueCrossCompartmentWrapper::singleton;
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -508,16 +508,23 @@ Proxy::regexp_toShared(JSContext* cx, Ha
 
 bool
 Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp)
 {
     JS_CHECK_RECURSION(cx, return false);
     return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
 }
 
+bool
+Proxy::defaultValue(JSContext* cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
+{
+    JS_CHECK_RECURSION(cx, return false);
+    return proxy->as<ProxyObject>().handler()->defaultValue(cx, proxy, hint, vp);
+}
+
 JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
 
 /* static */ bool
 Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
 {
     JS_CHECK_RECURSION(cx, return false);
     return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
 }
@@ -668,16 +675,23 @@ ProxyObject::trace(JSTracer* trc, JSObje
 
 JSObject*
 js::proxy_WeakmapKeyDelegate(JSObject* obj)
 {
     MOZ_ASSERT(obj->is<ProxyObject>());
     return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
 }
 
+bool
+js::proxy_Convert(JSContext* cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
+{
+    MOZ_ASSERT(proxy->is<ProxyObject>());
+    return Proxy::defaultValue(cx, proxy, hint, vp);
+}
+
 void
 js::proxy_Finalize(FreeOp* fop, JSObject* obj)
 {
     // Suppress a bogus warning about finalize().
     JS::AutoSuppressGCAnalysis nogc;
 
     MOZ_ASSERT(obj->is<ProxyObject>());
     obj->as<ProxyObject>().handler()->finalize(fop, obj);
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -57,16 +57,17 @@ class Proxy
                            const CallArgs& args);
     static bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp);
     static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClassValue* classValue);
     static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
     static const char* className(JSContext* cx, HandleObject proxy);
     static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent);
     static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
     static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
+    static bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp);
 
     static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable);
     static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id);
 
     static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,
                             ElementAdder* adder);
 
     static void trace(JSTracer* trc, JSObject* obj);
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -69,16 +69,27 @@ template <class Base>
 bool
 SecurityWrapper<Base>::isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const
 {
     // See above.
     *extensible = true;
     return true;
 }
 
+// For security wrappers, we run the OrdinaryToPrimitive algorithm on the wrapper
+// itself, which means that the existing security policy on operations like
+// toString() will take effect and do the right thing here.
+template <class Base>
+bool
+SecurityWrapper<Base>::defaultValue(JSContext* cx, HandleObject wrapper,
+                                    JSType hint, MutableHandleValue vp) const
+{
+    return OrdinaryToPrimitive(cx, wrapper, hint, vp);
+}
+
 template <class Base>
 bool
 SecurityWrapper<Base>::getBuiltinClass(JSContext* cx, HandleObject wrapper,
                                        ESClassValue* classValue) const
 {
     *classValue = ESClass_Other;
     return true;
 }
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -11,16 +11,32 @@
 
 #include "vm/ErrorObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
+/*
+ * Wrapper forwards this call directly to the wrapped object for efficiency
+ * and transparency. In particular, the hint is needed to properly stringify
+ * Date objects in certain cases - see bug 646129. Note also the
+ * SecurityWrapper overrides this trap to avoid information leaks. See bug
+ * 720619.
+ */
+bool
+Wrapper::defaultValue(JSContext* cx, HandleObject proxy, JSType hint, MutableHandleValue vp) const
+{
+    vp.set(ObjectValue(*proxy->as<ProxyObject>().target()));
+    if (hint == JSTYPE_VOID)
+        return ToPrimitive(cx, vp);
+    return ToPrimitive(cx, hint, vp);
+}
+
 JSObject*
 Wrapper::New(JSContext* cx, JSObject* obj, const Wrapper* handler,
              const WrapperOptions& options)
 {
     RootedValue priv(cx, ObjectValue(*obj));
     return NewProxyObject(cx, handler, priv, options.proto(), options);
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2391,17 +2391,17 @@ sandbox_resolve(JSContext* cx, HandleObj
     return true;
 }
 
 static const JSClass sandbox_class = {
     "sandbox",
     JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
-    nullptr, nullptr,
+    nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JSObject*
 NewSandbox(JSContext* cx, bool lazy)
 {
     RootedObject obj(cx, JS_NewGlobalObject(cx, &sandbox_class, nullptr,
@@ -5188,17 +5188,17 @@ global_mayResolve(const JSAtomState& nam
 {
     return JS_MayResolveStandardClass(names, id, maybeObj);
 }
 
 static const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     global_enumerate, global_resolve, global_mayResolve,
-    nullptr,
+    nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 /*
  * Define a FakeDOMObject constructor. It returns an object with a getter,
  * setter and method with attached JitInfo. This object can be used to test
  * IonMonkey DOM optimizations in the shell.
deleted file mode 100644
--- a/js/src/tests/ecma_6/Date/toPrimitive.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// ES6 20.3.4.45 Date.prototype[@@toPrimitive](hint)
-
-// The toPrimitive method throws if the this value isn't an object.
-var toPrimitive = Date.prototype[Symbol.toPrimitive];
-assertThrowsInstanceOf(() => toPrimitive.call(undefined, "default"), TypeError);
-assertThrowsInstanceOf(() => toPrimitive.call(3, "default"), TypeError);
-
-// It doesn't have to be a Date object, though.
-var obj = {
-    toString() { return "str"; },
-    valueOf() { return "val"; }
-};
-assertEq(toPrimitive.call(obj, "number"), "val");
-assertEq(toPrimitive.call(obj, "string"), "str");
-assertEq(toPrimitive.call(obj, "default"), "str");
-
-// It throws if the hint argument is missing or not one of the three allowed values.
-assertThrowsInstanceOf(() => toPrimitive.call(obj), TypeError);
-assertThrowsInstanceOf(() => toPrimitive.call(obj, undefined), TypeError);
-assertThrowsInstanceOf(() => toPrimitive.call(obj, "boolean"), TypeError);
-assertThrowsInstanceOf(() => toPrimitive.call(obj, ["number"]), TypeError);
-assertThrowsInstanceOf(() => toPrimitive.call(obj, {toString() { throw "FAIL"; }}), TypeError);
-
-// The next few tests cover the OrdinaryToPrimitive algorithm, specified in
-// ES6 7.1.1 ToPrimitive(input [, PreferredType]).
-
-// Date.prototype.toString or .valueOf can be overridden.
-var dateobj = new Date();
-Date.prototype.toString = function () {
-    assertEq(this, dateobj);
-    return 14;
-};
-Date.prototype.valueOf = function () {
-    return "92";
-};
-assertEq(dateobj[Symbol.toPrimitive]("number"), "92");
-assertEq(dateobj[Symbol.toPrimitive]("string"), 14);
-assertEq(dateobj[Symbol.toPrimitive]("default"), 14);
-assertEq(dateobj == 14, true);  // equality comparison: passes "default"
-
-// If this.toString is a non-callable value, this.valueOf is called instead.
-Date.prototype.toString = {};
-assertEq(dateobj[Symbol.toPrimitive]("string"), "92");
-assertEq(dateobj[Symbol.toPrimitive]("default"), "92");
-
-// And vice versa.
-Date.prototype.toString = function () { return 15; };
-Date.prototype.valueOf = "ponies";
-assertEq(dateobj[Symbol.toPrimitive]("number"), 15);
-
-// If neither is callable, it throws a TypeError.
-Date.prototype.toString = "ponies";
-assertThrowsInstanceOf(() => dateobj[Symbol.toPrimitive]("default"), TypeError);
-
-// Surface features.
-assertEq(toPrimitive.name, "[Symbol.toPrimitive]");
-var desc = Object.getOwnPropertyDescriptor(Date.prototype, Symbol.toPrimitive);
-assertEq(desc.configurable, true);
-assertEq(desc.enumerable, false);
-assertEq(desc.writable, false);
-
-reportCompare(0, 0);
deleted file mode 100644
--- a/js/src/tests/ecma_6/Object/toPrimitive-callers.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Check all the algorithms that call ToPrimitive. Confirm that they're passing
-// the correct hint, per spec.
-
-var STRING = "xyzzy";
-var NUMBER = 42;
-
-function assertCallsToPrimitive(f, expectedHint, expectedResult) {
-    var hint = undefined;
-    var testObj = {
-        [Symbol.toPrimitive](h) {
-            assertEq(hint, undefined);
-            hint = h;
-            return h === "number" ? NUMBER : STRING;
-        }
-    };
-    var result = f(testObj);
-    assertEq(hint, expectedHint, String(f));
-    assertEq(result, expectedResult, String(f));
-}
-
-// ToNumber
-assertCallsToPrimitive(Number, "number", NUMBER);
-
-// ToString
-assertCallsToPrimitive(String, "string", STRING);
-
-// ToPropertyKey
-var obj = {[STRING]: "pass"};
-assertCallsToPrimitive(key => obj[key], "string", "pass");
-
-// Abstract Relational Comparison
-assertCallsToPrimitive(x => x >= 42, "number", true);
-assertCallsToPrimitive(x => x > "42", "number", false);
-
-// Abstract Equality Comparison
-assertCallsToPrimitive(x => x != STRING, "default", false);
-assertCallsToPrimitive(x => STRING == x, "default", true);
-assertCallsToPrimitive(x => x == NUMBER, "default", false);
-assertCallsToPrimitive(x => NUMBER != x, "default", true);
-
-// Addition
-assertCallsToPrimitive(x => 1 + x, "default", "1" + STRING);
-assertCallsToPrimitive(x => "" + x, "default", STRING);
-
-// Date constructor
-assertCallsToPrimitive(x => (new Date(x)).valueOf(), "default", Number(STRING));
-
-// Date.prototype.toJSON
-var expected = "a suffusion of yellow";
-function testJSON(x) {
-    x.toJSON = Date.prototype.toJSON;
-    x.toISOString = function () { return expected; };
-    return JSON.stringify(x);
-}
-assertCallsToPrimitive(testJSON, "number", JSON.stringify(expected));
-
-reportCompare(0, 0);
deleted file mode 100644
--- a/js/src/tests/ecma_6/Object/toPrimitive.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// ES6 7.1.1 ToPrimitive(input [, PreferredType]) specifies a new extension
-// point in the language. Objects can override the behavior of ToPrimitive
-// somewhat by supporting the method obj[@@toPrimitive](hint).
-//
-// (Rationale: ES5 had a [[DefaultValue]] internal method, overridden only by
-// Date objects. The change in ES6 is to make [[DefaultValue]] a plain old
-// method. This allowed ES6 to eliminate the [[DefaultValue]] internal method,
-// simplifying the meta-object protocol and thus proxies.)
-
-// obj[Symbol.toPrimitive]() is called whenever the ToPrimitive algorithm is invoked.
-var expectedThis, expectedHint;
-var obj = {
-    [Symbol.toPrimitive](hint, ...rest) {
-        assertEq(this, expectedThis);
-        assertEq(hint, expectedHint);
-        assertEq(rest.length, 0);
-        return 2015;
-    }
-};
-expectedThis = obj;
-expectedHint = "string";
-assertEq(String(obj), "2015");
-expectedHint = "number";
-assertEq(Number(obj), 2015);
-
-// It is called even through proxies.
-var proxy = new Proxy(obj, {});
-expectedThis = proxy;
-expectedHint = "default";
-assertEq("ES" + proxy, "ES2015");
-
-// It is called even through additional proxies and the prototype chain.
-proxy = new Proxy(Object.create(proxy), {});
-expectedThis = proxy;
-expectedHint = "default";
-assertEq("ES" + (proxy + 1), "ES2016");
-
-// It is not called if the operand is already a primitive.
-var ok = true;
-for (var constructor of [Boolean, Number, String, Symbol]) {
-    constructor.prototype[Symbol.toPrimitive] = function () {
-        ok = false;
-        throw "FAIL";
-    };
-}
-assertEq(Number(true), 1);
-assertEq(Number(77.7), 77.7);
-assertEq(Number("123"), 123);
-assertThrowsInstanceOf(() => Number(Symbol.iterator), TypeError);
-assertEq(String(true), "true");
-assertEq(String(77.7), "77.7");
-assertEq(String("123"), "123");
-assertEq(String(Symbol.iterator), "Symbol(Symbol.iterator)");
-assertEq(ok, true);
-
-// Converting a primitive symbol to another primitive type throws even if you
-// delete the @@toPrimitive method from Symbol.prototype.
-delete Symbol.prototype[Symbol.toPrimitive];
-var sym = Symbol("ok");
-assertThrowsInstanceOf(() => `${sym}`, TypeError);
-assertThrowsInstanceOf(() => Number(sym), TypeError);
-assertThrowsInstanceOf(() => "" + sym, TypeError);
-
-// However, having deleted that method, converting a Symbol wrapper object does
-// work: it calls Symbol.prototype.toString().
-obj = Object(sym);
-assertEq(String(obj), "Symbol(ok)");
-assertEq(`${obj}`, "Symbol(ok)");
-
-// Deleting valueOf as well makes numeric conversion also call toString().
-delete Symbol.prototype.valueOf;
-delete Object.prototype.valueOf;
-assertEq(Number(obj), NaN);
-Symbol.prototype.toString = function () { return "2060"; };
-assertEq(Number(obj), 2060);
-
-// Deleting Date.prototype[Symbol.toPrimitive] changes the result of addition
-// involving Date objects.
-var d = new Date;
-assertEq(0 + d, 0 + d.toString());
-delete Date.prototype[Symbol.toPrimitive];
-assertEq(0 + d, 0 + d.valueOf());
-
-// If @@toPrimitive, .toString, and .valueOf are all missing, we get a
-// particular sequence of property accesses, followed by a TypeError exception.
-var log = [];
-function doGet(target, propertyName, receiver) {
-    log.push(propertyName);
-}
-var handler = new Proxy({}, {
-    get(target, trapName, receiver) {
-        if (trapName !== "get")
-            throw `FAIL: system tried to access handler method: ${uneval(trapName)}`;
-        return doGet;
-    }
-});
-proxy = new Proxy(Object.create(null), handler);
-assertThrowsInstanceOf(() => proxy == 0, TypeError);
-assertDeepEq(log, [Symbol.toPrimitive, "valueOf", "toString"]);
-
-reportCompare(0, 0);
--- a/js/src/tests/ecma_6/Reflect/propertyKeys.js
+++ b/js/src/tests/ecma_6/Reflect/propertyKeys.js
@@ -33,25 +33,24 @@ var keys = [
         expected: "key"
     },
     {
         value: {
             toString: undefined,
             valueOf() { return "fallback"; }
         },
         expected: "fallback"
-    },
-    {
-        value: {
-            [Symbol.toPrimitive](hint) { return hint; }
-        },
-        expected: "string"
     }
 ];
 
+if ("toPrimitive" in Symbol) {
+    throw new Error("Congratulations on implementing Symbol.toPrimitive! " +
+                    "Please add an object with an @@toPrimitive method in the list above.");
+}
+
 for (var {value, expected} of keys) {
     if (expected === undefined)
         expected = value;
 
     var obj = {};
     assertEq(Reflect.defineProperty(obj, value, {value: 1, configurable: true}), true);
     assertDeepEq(Reflect.ownKeys(obj), [expected]);
     assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, value),
--- a/js/src/tests/ecma_6/Symbol/conversions.js
+++ b/js/src/tests/ecma_6/Symbol/conversions.js
@@ -2,88 +2,65 @@
 
 var symbols = [
     Symbol(),
     Symbol("one"),
     Symbol.for("two"),
     Symbol.iterator
 ];
 
-function testSymbolConversions(sym) {
+if (Symbol.toPrimitive in Symbol.prototype) {
+    // We should test that deleting Symbol.prototype[@@toPrimitive] changes the
+    // behavior of ToPrimitive on Symbol objects, but @@toPrimitive is not
+    // implemented yet.
+    throw new Error("Congratulations on implementing @@toPrimitive! Please update this test.");
+}
+
+for (var sym of symbols) {
+    // 7.1.1 ToPrimitive
+    var symobj = Object(sym);
+    assertThrowsInstanceOf(() => Number(symobj), TypeError);
+    assertThrowsInstanceOf(() => String(symobj), TypeError);
+    assertThrowsInstanceOf(() => symobj < 0, TypeError);
+    assertThrowsInstanceOf(() => 0 < symobj, TypeError);
+    assertThrowsInstanceOf(() => symobj + 1, TypeError);
+    assertThrowsInstanceOf(() => "" + symobj, TypeError);
+    assertEq(sym == symobj, true);
+    assertEq(sym === symobj, false);
+    assertEq(symobj == 0, false);
+    assertEq(0 != symobj, true);
+
     // 7.1.2 ToBoolean
     assertEq(Boolean(sym), true);
     assertEq(!sym, false);
     assertEq(sym || 13, sym);
     assertEq(sym && 13, 13);
 
     // 7.1.3 ToNumber
     assertThrowsInstanceOf(() => +sym, TypeError);
     assertThrowsInstanceOf(() => sym | 0, TypeError);
 
     // 7.1.12 ToString
     assertThrowsInstanceOf(() => "" + sym, TypeError);
     assertThrowsInstanceOf(() => sym + "", TypeError);
-    assertThrowsInstanceOf(() => "" + [1, 2, sym], TypeError);
-    assertThrowsInstanceOf(() => ["simple", "thimble", sym].join(), TypeError);
+    assertThrowsInstanceOf(() => "" + [1, 2, Symbol()], TypeError);
+    assertThrowsInstanceOf(() => ["simple", "thimble", Symbol()].join(), TypeError);
 
     // 21.1.1.1 String()
     assertEq(String(sym), sym.toString());
+    assertThrowsInstanceOf(() => String(Object(sym)), TypeError);
 
     // 21.1.1.2 new String()
     assertThrowsInstanceOf(() => new String(sym), TypeError);
 
     // 7.1.13 ToObject
     var obj = Object(sym);
     assertEq(typeof obj, "object");
     assertEq(Object.prototype.toString.call(obj), "[object Symbol]");
     assertEq(Object.getPrototypeOf(obj), Symbol.prototype);
     assertEq(Object.getOwnPropertyNames(obj).length, 0);
     assertEq(Object(sym) === Object(sym), false);  // new object each time
     var f = function () { return this; };
     assertEq(f.call(sym) === f.call(sym), false);  // new object each time
 }
 
-
-for (var sym of symbols) {
-    testSymbolConversions(sym);
-
-    // 7.1.1 ToPrimitive
-    var symobj = Object(sym);
-    assertThrowsInstanceOf(() => Number(symobj), TypeError);
-    assertThrowsInstanceOf(() => String(symobj), TypeError);
-    assertThrowsInstanceOf(() => symobj < 0, TypeError);
-    assertThrowsInstanceOf(() => 0 < symobj, TypeError);
-    assertThrowsInstanceOf(() => symobj + 1, TypeError);
-    assertThrowsInstanceOf(() => "" + symobj, TypeError);
-    assertEq(sym == symobj, true);
-    assertEq(sym === symobj, false);
-    assertEq(symobj == 0, false);
-    assertEq(0 != symobj, true);
-
-    // 7.1.12 ToString
-    assertThrowsInstanceOf(() => String(Object(sym)), TypeError);
-}
-
-// Deleting Symbol.prototype[@@toPrimitive] does not change the behavior of
-// conversions from a symbol to other types.
-delete Symbol.prototype[Symbol.toPrimitive];
-assertEq(Symbol.toPrimitive in Symbol.prototype, false);
-testSymbolConversions(symbols[0]);
-
-// It does change the behavior of ToPrimitive on Symbol objects, though.
-// It causes the default algorithm (OrdinaryToPrimitive) to be used.
-var VALUEOF_CALLED = 117.25;
-Symbol.prototype.valueOf =  function () { return VALUEOF_CALLED; };
-Symbol.prototype.toString = function () { return "toString called"; };
-for (var sym of symbols) {
-    var symobj = Object(sym);
-    assertEq(Number(symobj), VALUEOF_CALLED);
-    assertEq(String(symobj), "toString called");
-    assertEq(symobj < 0, VALUEOF_CALLED < 0);
-    assertEq(0 < symobj, 0 < VALUEOF_CALLED);
-    assertEq(symobj + 1, VALUEOF_CALLED + 1);
-    assertEq("" + symobj, "" + VALUEOF_CALLED);
-    assertEq(symobj == 0, false);
-    assertEq(0 != symobj, true);
-}
-
 if (typeof reportCompare === "function")
     reportCompare(0, 0);
deleted file mode 100644
--- a/js/src/tests/ecma_6/Symbol/toPrimitive.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// ES6 19.4.3.4 Symbol.prototype[@@toPrimitive](hint)
-
-// This method gets the primitive symbol from a Symbol wrapper object.
-var sym = Symbol.for("truth")
-var obj = Object(sym);
-assertEq(obj[Symbol.toPrimitive]("default"), sym);
-
-// The hint argument is ignored.
-assertEq(obj[Symbol.toPrimitive]("number"), sym);
-assertEq(obj[Symbol.toPrimitive]("string"), sym);
-assertEq(obj[Symbol.toPrimitive](), sym);
-assertEq(obj[Symbol.toPrimitive](Math.atan2), sym);
-
-// The this value can also be a primitive symbol.
-assertEq(sym[Symbol.toPrimitive](), sym);
-
-// Or a wrapper to a Symbol object in another compartment.
-var obj2 = newGlobal().Object(sym);
-assertEq(obj2[Symbol.toPrimitive]("default"), sym);
-
-// Otherwise a TypeError is thrown.
-var symbolToPrimitive = Symbol.prototype[Symbol.toPrimitive];
-var nonSymbols = [
-    undefined, null, true, 13, NaN, "justice", {}, [sym],
-    symbolToPrimitive,
-    new Proxy(obj, {})
-];
-for (var value of nonSymbols) {
-    assertThrowsInstanceOf(() => symbolToPrimitive.call(value, "string"), TypeError);
-}
-
-// Surface features:
-assertEq(symbolToPrimitive.name, "[Symbol.toPrimitive]");
-var desc = Object.getOwnPropertyDescriptor(Symbol.prototype, Symbol.toPrimitive);
-assertEq(desc.configurable, true);
-assertEq(desc.enumerable, false);
-assertEq(desc.writable, false);
-
-reportCompare(0, 0);
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -641,16 +641,17 @@ const Class MappedArgumentsObject::class
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     MappedArgumentsObject::obj_enumerate,
     MappedArgumentsObject::obj_resolve,
     nullptr,                 /* mayResolve  */
+    nullptr,                 /* convert     */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
 
 /*
@@ -666,14 +667,15 @@ const Class UnmappedArgumentsObject::cla
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     UnmappedArgumentsObject::obj_enumerate,
     UnmappedArgumentsObject::obj_resolve,
     nullptr,                 /* mayResolve  */
+    nullptr,                 /* convert     */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -106,16 +106,17 @@ const Class ArrayBufferObject::class_ = 
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     nullptr,                 /* enumerate */
     nullptr,                 /* resolve */
     nullptr,                 /* mayResolve */
+    nullptr,                 /* convert */
     ArrayBufferObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::trace,
     JS_NULL_CLASS_SPEC,
     {
         nullptr,    /* outerObject */
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -262,18 +262,18 @@
     macro(boolean, boolean, "boolean") \
     macro(null, null, "null") \
     macro(symbol, symbol, "symbol") \
     /* Well-known atom names must be continuous and ordered, matching \
      * enum JS::SymbolCode in jsapi.h. */ \
     macro(iterator, iterator, "iterator") \
     macro(match, match, "match") \
     macro(species, species, "species") \
-    macro(toPrimitive, toPrimitive, "toPrimitive") \
     /* 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_iterator, Symbol_iterator, "Symbol.iterator") \
     macro(Symbol_match,    Symbol_match,    "Symbol.match") \
     macro(Symbol_species,  Symbol_species,  "Symbol.species") \
     macro(Symbol_toPrimitive, Symbol_toPrimitive, "Symbol.toPrimitive") \
     macro(Symbol_toStringTag, Symbol_toStringTag, "Symbol.toStringTag") \
     macro(Symbol_unscopables, Symbol_unscopables, "Symbol.unscopables") \
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2652,17 +2652,17 @@ Debugger::finalize(FreeOp* fop, JSObject
         return;
     fop->delete_(dbg);
 }
 
 const Class Debugger::jsclass = {
     "Debugger",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, Debugger::finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     Debugger::traceObject
 };
 
 /* static */ Debugger*
@@ -4578,17 +4578,17 @@ DebuggerScript_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(script);
     }
 }
 
 const Class DebuggerScript_class = {
     "Script",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerScript_trace
 };
 
 JSObject*
@@ -5668,17 +5668,17 @@ DebuggerSource_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerSource_class = {
     "Source",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerSource_trace
 };
 
 JSObject*
@@ -6018,17 +6018,17 @@ DebuggerFrame_maybeDecrementFrameScriptS
 static void
 DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
 {
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
 }
 
 const Class DebuggerFrame_class = {
     "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, DebuggerFrame_finalize
 };
 
 static NativeObject*
 CheckThisFrame(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
@@ -6794,17 +6794,17 @@ DebuggerObject_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerObject_class = {
     "Object",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerObject_trace
 };
 
 static NativeObject*
@@ -7740,17 +7740,17 @@ DebuggerEnv_trace(JSTracer* trc, JSObjec
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerEnv_class = {
     "Environment",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerEnv_trace
 };
 
 static NativeObject*
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -524,17 +524,17 @@ static void
 GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
 {
     fop->delete_((GlobalObject::DebuggerVector*) obj->as<NativeObject>().getPrivate());
 }
 
 static const Class
 GlobalDebuggees_class = {
     "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, GlobalDebuggees_finalize
 };
 
 GlobalObject::DebuggerVector*
 GlobalObject::getDebuggers() const
 {
     Value debuggers = getReservedSlot(DEBUGGERS);
     if (debuggers.isUndefined())
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -188,17 +188,17 @@ js::CancelOffThreadIonCompile(JSCompartm
         builder = next;
     }
 }
 
 static const JSClass parseTaskGlobalClass = {
     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx,
                      const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -291,17 +291,17 @@ static void
 ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
 {
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
         chain->mark(trc);
 }
 
 const Class ForOfPIC::jsclass = {
     "ForOfPIC", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, ForOfPIC_finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     ForOfPIC_traceObject
 };
 
 /* static */ NativeObject*
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -234,16 +234,17 @@ const Class RegExpObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     RegExpObject::trace,
 
     // ClassSpec
     {
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -40,16 +40,17 @@ const Class RegExpStaticsObject::class_ 
     JSCLASS_HAS_PRIVATE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     resc_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     resc_trace
 };
 
 RegExpStaticsObject*
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -439,17 +439,16 @@ namespace js {
  * Well-known symbols are never GC'd. The description() of each well-known
  * symbol is a permanent atom.
  */
 struct WellKnownSymbols
 {
     js::ImmutableSymbolPtr iterator;
     js::ImmutableSymbolPtr match;
     js::ImmutableSymbolPtr species;
-    js::ImmutableSymbolPtr toPrimitive;
 
     const ImmutableSymbolPtr& get(size_t u) const {
         MOZ_ASSERT(u < JS::WellKnownSymbolLimit);
         const ImmutableSymbolPtr* symbols = reinterpret_cast<const ImmutableSymbolPtr*>(this);
         return symbols[u];
     }
 
     const ImmutableSymbolPtr& get(JS::SymbolCode code) const {
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -289,16 +289,17 @@ SavedFrame::finishSavedFrameInit(JSConte
     JSCLASS_IS_ANONYMOUS,
     nullptr,                    // addProperty
     nullptr,                    // delProperty
     nullptr,                    // getProperty
     nullptr,                    // setProperty
     nullptr,                    // enumerate
     nullptr,                    // resolve
     nullptr,                    // mayResolve
+    nullptr,                    // convert
     SavedFrame::finalize,       // finalize
     nullptr,                    // call
     nullptr,                    // hasInstance
     nullptr,                    // construct
     nullptr,                    // trace
 
     // ClassSpec
     {
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -321,16 +321,17 @@ const Class ModuleEnvironmentObject::cla
     JSCLASS_IS_ANONYMOUS,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
+    nullptr,        /* convert     */
     nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     nullptr,        /* trace       */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     {
@@ -715,16 +716,17 @@ const Class DynamicWithObject::class_ = 
     JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     {
@@ -1177,16 +1179,17 @@ const Class UninitializedLexicalObject::
     JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     {
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1637,17 +1637,17 @@ JSRuntime::createSelfHostingGlobal(JSCon
     JSCompartment* compartment = NewCompartment(cx, nullptr, nullptr, options);
     if (!compartment)
         return nullptr;
 
     static const Class shgClass = {
         "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr,
+        nullptr, nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     AutoCompartment ac(cx, compartment);
     Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass));
     if (!shg)
         return nullptr;
 
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -316,16 +316,17 @@ const Class SharedArrayBufferObject::cla
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     SharedArrayBufferObject::Finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT
 };
--- a/js/src/vm/SharedTypedArrayObject.cpp
+++ b/js/src/vm/SharedTypedArrayObject.cpp
@@ -711,16 +711,17 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPP
     JSCLASS_HAS_CACHED_PROTO(JSProto_Shared##_typedArray),                     \
     nullptr,                 /* addProperty */                                 \
     nullptr,                 /* delProperty */                                 \
     nullptr,                 /* getProperty */                                 \
     nullptr,                 /* setProperty */                                 \
     nullptr,                 /* enumerate   */                                 \
     nullptr,                 /* resolve     */                                 \
     nullptr,                 /* mayResolve  */                                 \
+    nullptr,                 /* convert     */                                 \
     nullptr,                 /* finalize    */                                 \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     nullptr,                 /* trace  */                                      \
     SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray)                                 \
 }
 
@@ -732,16 +733,17 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPP
     JSCLASS_HAS_CACHED_PROTO(JSProto_Shared##_typedArray),                     \
     nullptr,                 /* addProperty */                                 \
     nullptr,                 /* delProperty */                                 \
     nullptr,                 /* getProperty */                                 \
     nullptr,                 /* setProperty */                                 \
     nullptr,                 /* enumerate   */                                 \
     nullptr,                 /* resolve     */                                 \
     nullptr,                 /* mayResolve  */                                 \
+    nullptr,                 /* convert     */                                 \
     nullptr,                 /* finalize    */                                 \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     nullptr,                 /* trace  */                                      \
     SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray)                                 \
 }
 
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -827,16 +827,17 @@ TypedArrayObject::sharedTypedArrayProtot
     JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
     nullptr,                /* addProperty */
     nullptr,                /* delProperty */
     nullptr,                /* getProperty */
     nullptr,                /* setProperty */
     nullptr,                /* enumerate */
     nullptr,                /* resolve */
     nullptr,                /* mayResolve */
+    nullptr,                /* convert */
     nullptr,                /* finalize */
     nullptr,                /* call */
     nullptr,                /* hasInstance */
     nullptr,                /* construct */
     nullptr,                /* trace */
     {
         GenericCreateConstructor<TypedArrayConstructor, 3, gc::AllocKind::FUNCTION>,
         GenericCreatePrototype,
@@ -1793,16 +1794,17 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Flo
     JSCLASS_DELAY_METADATA_CALLBACK,                                           \
     nullptr,                 /* addProperty */                                 \
     nullptr,                 /* delProperty */                                 \
     nullptr,                 /* getProperty */                                 \
     nullptr,                 /* setProperty */                                 \
     nullptr,                 /* enumerate   */                                 \
     nullptr,                 /* resolve     */                                 \
     nullptr,                 /* mayResolve  */                                 \
+    nullptr,                 /* convert     */                                 \
     nullptr,                 /* finalize    */                                 \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     TypedArrayObject::trace, /* trace  */                                      \
     TYPED_ARRAY_CLASS_SPEC(_typedArray)                                        \
 }
 
@@ -1838,16 +1840,17 @@ const Class TypedArrayObject::classes[Sc
     JSCLASS_HAS_CACHED_PROTO(JSProto_##typedArray), \
     nullptr, /* addProperty */ \
     nullptr, /* delProperty */ \
     nullptr, /* getProperty */ \
     nullptr, /* setProperty */ \
     nullptr, /* enumerate */ \
     nullptr, /* resolve */ \
     nullptr, /* mayResolve */ \
+    nullptr, /* convert */ \
     nullptr, /* finalize */ \
     nullptr, /* call */ \
     nullptr, /* hasInstance */ \
     nullptr, /* construct */ \
     nullptr, /* trace  */ \
     { \
         DELEGATED_CLASSSPEC(&TypedArrayObject::classes[i].spec), \
         nullptr, \
@@ -1892,16 +1895,17 @@ const Class DataViewObject::class_ = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
+    nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     ArrayBufferViewObject::trace
 };
 
 const JSFunctionSpec DataViewObject::jsfuncs[] = {
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -938,16 +938,17 @@ const Class UnboxedPlainObject::class_ =
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
+    nullptr,        /* convert     */
     nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     UnboxedPlainObject::trace,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     {
@@ -1623,16 +1624,17 @@ const Class UnboxedArrayObject::class_ =
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
+    nullptr,        /* convert     */
     UnboxedArrayObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     UnboxedArrayObject::trace,
     JS_NULL_CLASS_SPEC,
     {
         nullptr,    /* outerObject */
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * 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
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 310;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 309;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 415,
+static_assert(JSErr_Limit == 413,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -436,16 +436,27 @@ sandbox_moved(JSObject* obj, const JSObj
     // do.
     nsIScriptObjectPrincipal* sop =
         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
     if (sop)
         static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
 }
 
 static bool
+sandbox_convert(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue vp)
+{
+    if (type == JSTYPE_OBJECT) {
+        vp.setObject(*obj);
+        return true;
+    }
+
+    return OrdinaryToPrimitive(cx, obj, type, vp);
+}
+
+static bool
 writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                          JS::MutableHandleValue vp, JS::ObjectOpResult& result)
 {
     RootedObject proto(cx);
     if (!JS_GetPrototype(cx, obj, &proto))
         return false;
 
     RootedValue receiver(cx, ObjectValue(*proto));
@@ -553,17 +564,17 @@ sandbox_addProperty(JSContext* cx, Handl
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
 static const js::Class SandboxClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
     nullptr, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
     nullptr,        /* mayResolve */
-    sandbox_finalize,
+    sandbox_convert,  sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
     JS_NULL_CLASS_SPEC,
     {
       nullptr,      /* outerObject */
       nullptr,      /* innerObject */
       false,        /* isWrappedNative */
       nullptr,      /* weakmapKeyDelegateOp */
       sandbox_moved /* objectMovedOp */
@@ -574,17 +585,17 @@ static const js::Class SandboxClass = {
 // Note to whomever comes here to remove addProperty hooks: billm has promised
 // to do the work for this class.
 static const js::Class SandboxWriteToProtoClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
     sandbox_addProperty, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
     nullptr,        /* mayResolve */
-    sandbox_finalize,
+    sandbox_convert,  sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
     JS_NULL_CLASS_SPEC,
     {
       nullptr,      /* outerObject */
       nullptr,      /* innerObject */
       false,        /* isWrappedNative */
       nullptr,      /* weakmapKeyDelegateOp */
       sandbox_moved /* objectMovedOp */
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -1416,16 +1416,17 @@ static const JSClass XPCOutParamClass = 
     0,
     nullptr,   /* addProperty */
     nullptr,   /* delProperty */
     nullptr,   /* getProperty */
     nullptr,   /* setProperty */
     nullptr,   /* enumerate */
     nullptr,   /* resolve */
     nullptr,   /* mayResolve */
+    nullptr,   /* convert */
     FinalizeStub,
     nullptr,   /* call */
     nullptr,   /* hasInstance */
     nullptr,   /* construct */
     nullptr    /* trace */
 };
 
 bool
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -771,16 +771,17 @@ XPCWrappedNative::Init(const XPCNativeSc
 
     // We should have the global jsclass flag if and only if we're a global.
     MOZ_ASSERT_IF(si, !!si->GetFlags().IsGlobalObject() == !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
 
     MOZ_ASSERT(jsclazz &&
                jsclazz->name &&
                jsclazz->flags &&
                jsclazz->resolve &&
+               jsclazz->convert &&
                jsclazz->finalize, "bad class");
 
     // XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
     // that this object is same-compartment with cx, which means it could just
     // use the cx global...
     RootedObject global(cx, CurrentGlobalOrNull(cx));
     RootedObject protoJSObject(cx, HasProto() ?
                                    GetProto()->GetJSProtoObject() :
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -91,54 +91,16 @@ XPC_WN_Shared_ToSource(JSContext* cx, un
     JSString* str = JS_NewStringCopyN(cx, empty, sizeof(empty)-1);
     if (!str)
         return false;
     args.rval().setString(str);
 
     return true;
 }
 
-static bool
-XPC_WN_Shared_toPrimitive(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedObject obj(cx);
-    if (!JS_ValueToObject(cx, args.thisv(), &obj))
-        return false;
-    XPCCallContext ccx(JS_CALLER, cx, obj);
-    XPCWrappedNative* wrapper = ccx.GetWrapper();
-    THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
-
-    JSType hint;
-    if (!GetFirstArgumentAsTypeHint(cx, args, &hint))
-        return false;
-
-    if (hint == JSTYPE_NUMBER) {
-        args.rval().set(JS_GetNaNValue(cx));
-        return true;
-    }
-
-    MOZ_ASSERT(hint == JSTYPE_STRING || hint == JSTYPE_VOID);
-    ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
-    ccx.SetArgsAndResultPtr(0, nullptr, args.rval().address());
-
-    XPCNativeMember* member = ccx.GetMember();
-    if (member && member->IsMethod()) {
-        if (!XPCWrappedNative::CallMethod(ccx))
-            return false;
-
-        if (args.rval().isPrimitive())
-            return true;
-    }
-
-    // else...
-    return ToStringGuts(ccx);
-}
-
 /***************************************************************************/
 
 // A "double wrapped object" is a user JSObject that has been wrapped as a
 // wrappedJS in order to be used by native code and then re-wrapped by a
 // wrappedNative wrapper to be used by JS code. One might think of it as:
 //    wrappedNative(wrappedJS(underlying_JSObject))
 // This is done (as opposed to just unwrapping the wrapped JS and automatically
 // returning the underlying JSObject) so that JS callers will see what looks
@@ -266,27 +228,25 @@ DefinePropertyIfFound(XPCCallContext& cc
             bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT)
                 || Preferences::GetBool("dom.XPCToStringForDOMClasses", false);
 
             if(id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING)
                 && overwriteToString)
             {
                 call = XPC_WN_Shared_ToString;
                 name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING);
+                id   = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING);
             } else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) {
                 call = XPC_WN_Shared_ToSource;
                 name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE);
-            } else if (id == SYMBOL_TO_JSID(
-                               JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive)))
-            {
-                call = XPC_WN_Shared_toPrimitive;
-                name = "[Symbol.toPrimitive]";
-            } else {
+                id   = rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE);
+            }
+
+            else
                 call = nullptr;
-            }
 
             if (call) {
                 RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name));
                 if (!fun) {
                     JS_ReportOutOfMemory(ccx);
                     return false;
                 }
 
@@ -486,16 +446,73 @@ XPC_WN_CantDeletePropertyStub(JSContext*
 static bool
 XPC_WN_CannotModifySetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
                                    MutableHandleValue vp, ObjectOpResult& result)
 {
     return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
 }
 
 static bool
+XPC_WN_Shared_Convert(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue vp)
+{
+    if (type == JSTYPE_OBJECT) {
+        vp.setObject(*obj);
+        return true;
+    }
+
+    XPCCallContext ccx(JS_CALLER, cx, obj);
+    XPCWrappedNative* wrapper = ccx.GetWrapper();
+    THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+    switch (type) {
+        case JSTYPE_FUNCTION:
+            {
+                if (!ccx.GetTearOff()) {
+                    XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
+                    if (si && (si->GetFlags().WantCall() ||
+                               si->GetFlags().WantConstruct())) {
+                        vp.setObject(*obj);
+                        return true;
+                    }
+                }
+            }
+            return Throw(NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN, cx);
+        case JSTYPE_NUMBER:
+            vp.set(JS_GetNaNValue(cx));
+            return true;
+        case JSTYPE_BOOLEAN:
+            vp.setBoolean(true);
+            return true;
+        case JSTYPE_VOID:
+        case JSTYPE_STRING:
+        {
+            ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
+            ccx.SetArgsAndResultPtr(0, nullptr, vp.address());
+
+            XPCNativeMember* member = ccx.GetMember();
+            if (member && member->IsMethod()) {
+                if (!XPCWrappedNative::CallMethod(ccx))
+                    return false;
+
+                if (vp.isPrimitive())
+                    return true;
+            }
+
+            // else...
+            return ToStringGuts(ccx);
+        }
+        default:
+            NS_ERROR("bad type in conversion");
+            return false;
+    }
+    NS_NOTREACHED("huh?");
+    return false;
+}
+
+static bool
 XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj)
 {
     XPCCallContext ccx(JS_CALLER, cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     // Since we aren't going to enumerate tearoff names and the prototype
     // handles non-mutated members, we can do this potential short-circuit.
@@ -630,16 +647,17 @@ const XPCWrappedNativeJSClass XPC_WN_NoH
     XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
     XPC_WN_CantDeletePropertyStub,     // delProperty
     nullptr,                           // getProperty
     nullptr,                           // setProperty
 
     XPC_WN_Shared_Enumerate,           // enumerate
     XPC_WN_NoHelper_Resolve,           // resolve
     nullptr,                           // mayResolve
+    XPC_WN_Shared_Convert,             // convert
     XPC_WN_NoHelper_Finalize,          // finalize
 
     /* Optionally non-null members start here. */
     nullptr,                         // call
     nullptr,                         // construct
     nullptr,                         // hasInstance
     XPCWrappedNative::Trace,         // trace
     JS_NULL_CLASS_SPEC,
@@ -1023,16 +1041,18 @@ XPCNativeScriptableShared::PopulateJSCla
     else if (mFlags.WantEnumerate())
         mJSClass.base.enumerate = XPC_WN_Helper_Enumerate;
     else
         mJSClass.base.enumerate = XPC_WN_Shared_Enumerate;
 
     // We have to figure out resolve strategy at call time
     mJSClass.base.resolve = XPC_WN_Helper_Resolve;
 
+    mJSClass.base.convert = XPC_WN_Shared_Convert;
+
     if (mFlags.WantFinalize())
         mJSClass.base.finalize = XPC_WN_Helper_Finalize;
     else
         mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
 
     js::ObjectOps* ops = &mJSClass.base.ops;
     if (mFlags.WantNewEnumerate())
         ops->enumerate = XPC_WN_JSOp_Enumerate;
@@ -1269,16 +1289,17 @@ const js::Class XPC_WN_ModsAllowed_WithC
     /* Function pointer members. */
     nullptr,                        // addProperty;
     nullptr,                        // delProperty;
     nullptr,                        // getProperty;
     nullptr,                        // setProperty;
     XPC_WN_Shared_Proto_Enumerate,  // enumerate;
     XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
     nullptr,                        // mayResolve;
+    nullptr,                        // convert;
     XPC_WN_Shared_Proto_Finalize,   // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                        // call;
     nullptr,                        // construct;
     nullptr,                        // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
 
@@ -1294,16 +1315,17 @@ const js::Class XPC_WN_ModsAllowed_NoCal
     /* Function pointer members. */
     nullptr,                        // addProperty;
     nullptr,                        // delProperty;
     nullptr,                        // getProperty;
     nullptr,                        // setProperty;
     XPC_WN_Shared_Proto_Enumerate,  // enumerate;
     XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
     nullptr,                        // mayResolve;
+    nullptr,                        // convert;
     XPC_WN_Shared_Proto_Finalize,   // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                         // call;
     nullptr,                         // construct;
     nullptr,                         // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
 
@@ -1372,16 +1394,17 @@ const js::Class XPC_WN_NoMods_WithCall_P
     /* Mandatory non-null function pointer members. */
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty;
     XPC_WN_CantDeletePropertyStub,             // delProperty;
     nullptr,                                   // getProperty;
     nullptr,                                   // setProperty;
     XPC_WN_Shared_Proto_Enumerate,             // enumerate;
     XPC_WN_NoMods_Proto_Resolve,               // resolve;
     nullptr,                                   // mayResolve;
+    nullptr,                                   // convert;
     XPC_WN_Shared_Proto_Finalize,              // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                         // call;
     nullptr,                         // construct;
     nullptr,                         // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
 
@@ -1397,16 +1420,17 @@ const js::Class XPC_WN_NoMods_NoCall_Pro
     /* Mandatory non-null function pointer members. */
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty;
     XPC_WN_CantDeletePropertyStub,             // delProperty;
     nullptr,                                   // getProperty;
     nullptr,                                   // setProperty;
     XPC_WN_Shared_Proto_Enumerate,             // enumerate;
     XPC_WN_NoMods_Proto_Resolve,               // resolve;
     nullptr,                                   // mayResolve;
+    nullptr,                                   // convert;
     XPC_WN_Shared_Proto_Finalize,              // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                         // call;
     nullptr,                         // construct;
     nullptr,                         // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
 
@@ -1492,16 +1516,17 @@ const js::Class XPC_WN_Tearoff_JSClass =
     JSCLASS_HAS_RESERVED_SLOTS(XPC_WN_TEAROFF_RESERVED_SLOTS), // flags;
     XPC_WN_OnlyIWrite_AddPropertyStub,         // addProperty;
     XPC_WN_CantDeletePropertyStub,             // delProperty;
     nullptr,                                   // getProperty;
     nullptr,                                   // setProperty;
     XPC_WN_TearOff_Enumerate,                  // enumerate;
     XPC_WN_TearOff_Resolve,                    // resolve;
     nullptr,                                   // mayResolve;
+    XPC_WN_Shared_Convert,                     // convert;
     XPC_WN_TearOff_Finalize,                   // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                                   // call
     nullptr,                                   // construct
     nullptr,                                   // hasInstance
     nullptr,                                   // trace
     JS_NULL_CLASS_SPEC,
--- a/js/xpconnect/tests/chrome/test_bug1042436.xul
+++ b/js/xpconnect/tests/chrome/test_bug1042436.xul
@@ -35,15 +35,15 @@ https://bugzilla.mozilla.org/show_bug.cg
       var contentObjWithGetter = contentSb.eval('({ get getterProp() {return 42;}, valueProp: 42 })');
       is(contentObjWithGetter.wrappedJSObject.getterProp, 42, "Getter prop set up correctly");
       is(contentObjWithGetter.getterProp, undefined, "Xrays work right");
       is(contentObjWithGetter.valueProp, 42, "Getter prop set up correctly");
       chromeSb.contentObjWithGetter = contentObjWithGetter;
       Cu.evalInSandbox('contentObjWithGetter.getterProp; contentObjWithGetter.valueProp; contentObjWithGetter.getterProp;',
                        chromeSb, "1.7", "http://phony.example.com/file.js", 99);
   },
-  [{ errorMessage: /property "someExpandoProperty" \(reason: object is not safely Xrayable/, sourceName: /test_bug1042436/, isWarning: true },
-   { errorMessage: /property "getterProp" \(reason: property has accessor/, sourceName: /phony/, lineNumber: 99, isWarning: true } ],
+  [{ errorMessage: /property someExpandoProperty \(reason: object is not safely Xrayable/, sourceName: /test_bug1042436/, isWarning: true },
+   { errorMessage: /property getterProp \(reason: property has accessor/, sourceName: /phony/, lineNumber: 99, isWarning: true } ],
   SimpleTest.finish.bind(SimpleTest));
 
   ]]>
   </script>
 </window>
--- a/js/xpconnect/tests/chrome/test_bug1065185.html
+++ b/js/xpconnect/tests/chrome/test_bug1065185.html
@@ -28,17 +28,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     switch(++gLoadCount) {
       case 0:
         doMonitor([]);
         window[0].wrappedJSObject.probe = { a: 2, __exposedProps__: { 'a': 'r' } };
         is(window[0].eval('probe.a'), 2, "Accessed exposed prop");
         endMonitor();
         break;
       case 1:
-        doMonitor([/access to property "a"/i]);
+        doMonitor([/access to property a/i]);
         window[0].wrappedJSObject.probe = { a: 2 };
         is(window[0].eval('probe.a'), undefined, "Non-exposed prop undefined");
         is(window[0].eval('probe.a'), undefined, "Non-exposed prop undefined again");
         endMonitor();
         break;
       case 2:
         SimpleTest.finish();
         break;
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -154,17 +154,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     "getHours", "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds",
     "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "setTime",
     "setYear", "setFullYear", "setUTCFullYear", "setMonth", "setUTCMonth",
     "setDate", "setUTCDate", "setHours", "setUTCHours", "setMinutes",
     "setUTCMinutes", "setSeconds", "setUTCSeconds", "setMilliseconds",
     "setUTCMilliseconds", "toUTCString", "toLocaleFormat", "toLocaleString",
     "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
     "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
-    "toGMTString", Symbol.toPrimitive];
+    "toGMTString"];
   gPrototypeProperties['Object'] =
     ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
      "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
      "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
      "__proto__"];
   gPrototypeProperties['Array'] =
     ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
       "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -151,16 +151,24 @@ FilteringWrapper<Base, Policy>::nativeCa
 {
     if (Policy::allowNativeCall(cx, test, impl))
         return Base::Permissive::nativeCall(cx, test, impl, args);
     return Base::Restrictive::nativeCall(cx, test, impl, args);
 }
 
 template <typename Base, typename Policy>
 bool
+FilteringWrapper<Base, Policy>::defaultValue(JSContext* cx, HandleObject obj,
+                                             JSType hint, MutableHandleValue vp) const
+{
+    return Base::defaultValue(cx, obj, hint, vp);
+}
+
+template <typename Base, typename Policy>
+bool
 FilteringWrapper<Base, Policy>::getPrototype(JSContext* cx, JS::HandleObject wrapper,
                                              JS::MutableHandleObject protop) const
 {
     // Filtering wrappers do not allow access to the prototype.
     protop.set(nullptr);
     return true;
 }
 
--- a/js/xpconnect/wrappers/FilteringWrapper.h
+++ b/js/xpconnect/wrappers/FilteringWrapper.h
@@ -47,16 +47,19 @@ class FilteringWrapper : public Base {
     virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
                       const JS::CallArgs& args) const override;
     virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
                            const JS::CallArgs& args) const override;
 
     virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl,
                             const JS::CallArgs& args) const override;
 
+    virtual bool defaultValue(JSContext* cx, JS::Handle<JSObject*> obj, JSType hint,
+                              JS::MutableHandleValue vp) const override;
+
     virtual bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
                               JS::MutableHandleObject protop) const override;
 
     static const FilteringWrapper singleton;
 };
 
 /*
  * The HTML5 spec mandates very particular object behavior for cross-origin DOM
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -191,30 +191,24 @@ ReportWrapperDenial(JSContext* cx, Handl
     // The browser console warning is only emitted for the first violation,
     // whereas the (debug-only) NS_WARNING is emitted for each violation.
 #ifndef DEBUG
     if (alreadyWarnedOnce)
         return true;
 #endif
 
     nsAutoJSString propertyName;
-    RootedValue idval(cx);
-    if (!JS_IdToValue(cx, id, &idval))
-        return false;
-    JSString* str = JS_ValueToSource(cx, idval);
-    if (!str)
-        return false;
-    if (!propertyName.init(cx, str))
+    if (!propertyName.init(cx, id))
         return false;
     AutoFilename filename;
     unsigned line = 0, column = 0;
     DescribeScriptedCaller(cx, &filename, &line, &column);
 
     // Warn to the terminal for the logs.
-    NS_WARNING(nsPrintfCString("Silently denied access to property %s: %s (@%s:%u:%u)",
+    NS_WARNING(nsPrintfCString("Silently denied access to property |%s|: %s (@%s:%u:%u)",
                                NS_LossyConvertUTF16toASCII(propertyName).get(), reason,
                                filename.get(), line, column).get());
 
     // If this isn't the first warning on this topic for this global, we've
     // already bailed out in opt builds. Now that the NS_WARNING is done, bail
     // out in debug builds as well.
     if (alreadyWarnedOnce)
         return true;
@@ -519,17 +513,22 @@ JSXrayTraits::resolveOwnProperty(JSConte
     for (const JSFunctionSpec* fs = clasp->spec.prototypeFunctions(); fs && fs->name; ++fs) {
         if (PropertySpecNameEqualsId(fs->name, id)) {
             fsMatch = fs;
             break;
         }
     }
     if (fsMatch) {
         // Generate an Xrayed version of the method.
-        RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id));
+        RootedFunction fun(cx);
+        if (fsMatch->selfHostedName) {
+            fun = JS::GetSelfHostedFunction(cx, fsMatch->selfHostedName, id, fsMatch->nargs);
+        } else {
+            fun = JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs, 0, id);
+        }
         if (!fun)
             return false;
 
         // The generic Xray machinery only defines non-own properties of the target on
         // the holder. This is broken, and will be fixed at some point, but for now we
         // need to cache the value explicitly. See the corresponding call to
         // JS_GetOwnPropertyDescriptorById at the top of this function.
         RootedObject funObj(cx, JS_GetFunctionObject(fun));
@@ -889,17 +888,17 @@ ExpandoObjectFinalize(JSFreeOp* fop, JSO
     // Release the principal.
     nsIPrincipal* principal = GetExpandoObjectPrincipal(obj);
     NS_RELEASE(principal);
 }
 
 const JSClass ExpandoObjectClass = {
     "XrayExpandoObject",
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, ExpandoObjectFinalize
 };
 
 bool
 XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
                                          HandleObject expandoObject,
                                          nsIPrincipal* consumerOrigin,
                                          HandleObject exclusiveGlobal)
@@ -2179,16 +2178,29 @@ template <typename Base, typename Traits
 const char*
 XrayWrapper<Base, Traits>::className(JSContext* cx, HandleObject wrapper) const
 {
     return Traits::className(cx, wrapper, Base::singleton);
 }
 
 template <typename Base, typename Traits>
 bool
+XrayWrapper<Base, Traits>::defaultValue(JSContext* cx, HandleObject wrapper,
+                                        JSType hint, MutableHandleValue vp) const
+{
+    // Even if this isn't a security wrapper, Xray semantics dictate that we
+    // run the OrdinaryToPrimitive algorithm directly on the Xray wrapper.
+    //
+    // NB: We don't have to worry about things with special [[DefaultValue]]
+    // behavior like Date because we'll never have an XrayWrapper to them.
+    return OrdinaryToPrimitive(cx, wrapper, hint, vp);
+}
+
+template <typename Base, typename Traits>
+bool
 XrayWrapper<Base, Traits>::getPrototype(JSContext* cx, JS::HandleObject wrapper,
                                         JS::MutableHandleObject protop) const
 {
     // We really only want this override for non-SecurityWrapper-inheriting
     // |Base|. But doing that statically with templates requires partial method
     // specializations (and therefore a helper class), which is all more trouble
     // than it's worth. Do a dynamic check.
     if (Base::hasSecurityPolicy())
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -460,16 +460,19 @@ class XrayWrapper : public Base {
     virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                                        JS::MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                         bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                               JS::AutoIdVector& props) const override;
 
     virtual const char* className(JSContext* cx, JS::HandleObject proxy) const override;
+    virtual bool defaultValue(JSContext* cx, JS::HandleObject wrapper,
+                              JSType hint, JS::MutableHandleValue vp)
+                              const override;
 
     static const XrayWrapper singleton;
 
   private:
     template <bool HasPrototype>
     typename mozilla::EnableIf<HasPrototype, bool>::Type
         getPrototypeHelper(JSContext* cx, JS::HandleObject wrapper,
                            JS::HandleObject target, JS::MutableHandleObject protop) const
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -665,17 +665,17 @@ private:
   }
 };
 
 const JSClass JSRuntimeWrapper::sGlobalClass = {
   "PACResolutionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr,
   JS_GlobalObjectTraceHook
 };
 
 void
 ProxyAutoConfig::SetThreadLocalIndex(uint32_t index)
 {
   sRunningIndex = index;
 }
--- a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
+++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
@@ -121,16 +121,17 @@ static const JSClass sWitnessClass = {
   JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS),
   nullptr /* addProperty */,
   nullptr /* delProperty */,
   nullptr /* getProperty */,
   nullptr /* setProperty */,
   nullptr /* enumerate */,
   nullptr /* resolve */,
   nullptr /* mayResolve */,
+  nullptr /* convert */,
   Finalize /* finalize */
 };
 
 bool IsWitness(JS::Handle<JS::Value> v)
 {
   return v.isObject() && JS_GetClass(&v.toObject()) == &sWitnessClass;
 }
 
--- a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
+++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
@@ -83,17 +83,17 @@ RunTest(JSRuntime* rt, JSContext* cx, Ar
 
 static void
 CreateGlobalAndRunTest(JSRuntime* rt, JSContext* cx)
 {
   static const JSClass GlobalClass = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
   };
 
   JS::CompartmentOptions options;
   options.setVersion(JSVERSION_LATEST);
   JS::PersistentRootedObject global(cx);
   global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options);
   ASSERT_TRUE(global != nullptr);