Bug 1114580 - Implement ES6 Symbol.toStringTag. r=jorendorff
authorTom Schuster <evilpies@gmail.com>
Wed, 31 Aug 2016 00:13:10 +0200
changeset 311954 f382d56789de9a9c9324cb4ba401d6fb747e5c0e
parent 311953 4d5428813b26be34ac7420fa5320301372e3b4b3
child 311955 2f9eb93beee93e8cb4fe0525df7e9cc684cfdc3b
push id30627
push userryanvm@gmail.com
push dateWed, 31 Aug 2016 13:15:23 +0000
treeherdermozilla-central@3d9cabea1e56 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1114580
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1114580 - Implement ES6 Symbol.toStringTag. r=jorendorff
js/src/builtin/MapObject.cpp
js/src/builtin/Object.cpp
js/src/builtin/SymbolObject.cpp
js/src/builtin/TypedArray.js
js/src/builtin/WeakMapObject.cpp
js/src/builtin/WeakSetObject.cpp
js/src/jit-test/tests/collections/Map-surfaces-1.js
js/src/jit-test/tests/collections/Set-surfaces-1.js
js/src/jit-test/tests/collections/WeakMap-surfaces.js
js/src/jit-test/tests/collections/WeakSet-surface.js
js/src/jit-test/tests/collections/iterator-proto-surfaces.js
js/src/jit-test/tests/debug/Object-class.js
js/src/jit-test/tests/proxy/operations-on-revoked.js
js/src/jsapi.h
js/src/jsiter.cpp
js/src/jsmath.cpp
js/src/json.cpp
js/src/proxy/ScriptedProxyHandler.cpp
js/src/tests/ecma_6/Generators/runtime.js
js/src/tests/ecma_6/Symbol/toStringTag.js
js/src/tests/ecma_6/shell.js
js/src/tests/js1_8_5/extensions/dataview.js
js/src/vm/ArrayBufferObject.cpp
js/src/vm/CommonPropertyNames.h
js/src/vm/GeneratorObject.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/TypedArrayObject.cpp
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -146,18 +146,21 @@ bool
 GlobalObject::initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
 {
     Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
     if (!base)
         return false;
     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, base));
     if (!proto)
         return false;
-    if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods))
+    if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods) ||
+        !DefineToStringTag(cx, proto, cx->names().MapIterator))
+    {
         return false;
+    }
     global->setReservedSlot(MAP_ITERATOR_PROTO, ObjectValue(*proto));
     return true;
 }
 
 MapIteratorObject*
 MapIteratorObject::create(JSContext* cx, HandleObject mapobj, ValueMap* data,
                           MapObject::IteratorKind kind)
 {
@@ -332,16 +335,20 @@ MapObject::initClass(JSContext* cx, JSOb
         if (!fun)
             return nullptr;
 
         // Define its alias.
         RootedValue funval(cx, ObjectValue(*fun));
         RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
         if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
             return nullptr;
+
+        // Define Map.prototype[@@toStringTag].
+        if (!DefineToStringTag(cx, proto, cx->names().Map))
+            return nullptr;
     }
     return proto;
 }
 
 template <class Range>
 static void
 MarkKey(Range& r, const HashableValue& key, JSTracer* trc)
 {
@@ -888,18 +895,21 @@ bool
 GlobalObject::initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
 {
     Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
     if (!base)
         return false;
     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, base));
     if (!proto)
         return false;
-    if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods))
+    if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods) ||
+        !DefineToStringTag(cx, proto, cx->names().SetIterator))
+    {
         return false;
+    }
     global->setReservedSlot(SET_ITERATOR_PROTO, ObjectValue(*proto));
     return true;
 }
 
 SetIteratorObject*
 SetIteratorObject::create(JSContext* cx, HandleObject setobj, ValueSet* data,
                           SetObject::IteratorKind kind)
 {
@@ -1048,16 +1058,20 @@ SetObject::initClass(JSContext* cx, JSOb
         // Define its aliases.
         RootedValue funval(cx, ObjectValue(*fun));
         if (!JS_DefineProperty(cx, proto, "keys", funval, 0))
             return nullptr;
 
         RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
         if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
             return nullptr;
+
+        // Define Set.prototype[@@toStringTag].
+        if (!DefineToStringTag(cx, proto, cx->names().Set))
+            return nullptr;
     }
     return proto;
 }
 
 
 bool
 SetObject::keys(JSContext* cx, HandleObject obj, JS::MutableHandle<GCVector<JS::Value>> keys)
 {
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -300,73 +300,126 @@ js::ObjectToSource(JSContext* cx, Handle
         return nullptr;
     if (outermost && !buf.append(')'))
         return nullptr;
 
     return buf.finishString();
 }
 #endif /* JS_HAS_TOSOURCE */
 
-JSString*
-JS_BasicObjectToString(JSContext* cx, HandleObject obj)
-{
-    // Some classes are really common, don't allocate new strings for them.
-    // The ordering below is based on the measurements in bug 966264.
-    if (obj->is<PlainObject>())
-        return cx->names().objectObject;
-    if (obj->is<StringObject>())
-        return cx->names().objectString;
-    if (obj->is<ArrayObject>())
-        return cx->names().objectArray;
-    if (obj->is<JSFunction>())
-        return cx->names().objectFunction;
-    if (obj->is<NumberObject>())
-        return cx->names().objectNumber;
-
-    const char* className = GetObjectClassName(cx, obj);
-
-    if (strcmp(className, "Window") == 0)
-        return cx->names().objectWindow;
-
-    StringBuffer sb(cx);
-    if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
-        !sb.append("]"))
-    {
-        return nullptr;
-    }
-    return sb.finishString();
-}
-
-/* ES5 15.2.4.2.  Note steps 1 and 2 are errata. */
+// ES6 19.1.3.6
 bool
 js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    /* Step 1. */
+    // Step 1.
     if (args.thisv().isUndefined()) {
         args.rval().setString(cx->names().objectUndefined);
         return true;
     }
 
-    /* Step 2. */
+    // Step 2.
     if (args.thisv().isNull()) {
         args.rval().setString(cx->names().objectNull);
         return true;
     }
 
-    /* Step 3. */
+    // Step 3.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
-    /* Steps 4-5. */
-    JSString* str = JS_BasicObjectToString(cx, obj);
+    // Step 4.
+    bool isArray;
+    if (!IsArray(cx, obj, &isArray))
+        return false;
+
+    // Step 5.
+    RootedString builtinTag(cx);
+    if (isArray) {
+        builtinTag = cx->names().objectArray;
+    } else {
+        // Steps 6-13.
+        ESClass cls;
+        if (!GetBuiltinClass(cx, obj, &cls))
+            return false;
+
+        switch (cls) {
+          case ESClass::String:
+            builtinTag = cx->names().objectString;
+            break;
+          case ESClass::Arguments:
+            builtinTag = cx->names().objectArguments;
+            break;
+          case ESClass::Error:
+            builtinTag = cx->names().objectError;
+            break;
+          case ESClass::Boolean:
+            builtinTag = cx->names().objectBoolean;
+            break;
+          case ESClass::Number:
+            builtinTag = cx->names().objectNumber;
+            break;
+          case ESClass::Date:
+            builtinTag = cx->names().objectDate;
+            break;
+          case ESClass::RegExp:
+            builtinTag = cx->names().objectRegExp;
+            break;
+          default:
+            if (obj->isCallable()) {
+                // Non-standard: Prevent <object> from showing up as Function.
+                RootedObject unwrapped(cx, CheckedUnwrap(obj));
+                if (!unwrapped || !unwrapped->getClass()->isDOMClass())
+                    builtinTag = cx->names().objectFunction;
+            }
+            break;
+        }
+    }
+    // Step 14.
+    // Currently omitted for non-standard fallback.
+
+    // Step 15.
+    RootedValue tag(cx);
+    RootedId toStringTagId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
+    if (!GetProperty(cx, obj, obj, toStringTagId, &tag))
+        return false;
+
+    // Step 16.
+    if (!tag.isString()) {
+        // Non-standard (bug 1277801): Use ClassName as a fallback in the interim
+        if (!builtinTag) {
+            const char* className = GetObjectClassName(cx, obj);
+
+            StringBuffer sb(cx);
+            if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
+                !sb.append("]"))
+            {
+                return false;
+            }
+
+            builtinTag = sb.finishString();
+            if (!builtinTag)
+                return false;
+        }
+
+        args.rval().setString(builtinTag);
+        return true;
+    }
+
+    // Step 17.
+    StringBuffer sb(cx);
+    if (!sb.append("[object ") || !sb.append(tag.toString()) || !sb.append("]"))
+        return false;
+
+    RootedString str(cx, sb.finishString());
     if (!str)
         return false;
+
     args.rval().setString(str);
     return true;
 }
 
 
 bool
 js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -74,16 +74,17 @@ SymbolObject::initClass(JSContext* cx, H
     for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
         value.setSymbol(wks->get(i));
         if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
             return nullptr;
     }
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
         !DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
+        !DefineToStringTag(cx, proto, cx->names().Symbol) ||
         !DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods) ||
         !GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
     {
         return nullptr;
     }
     return proto;
 }
 
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1433,16 +1433,31 @@ function TypedArrayStaticOf(/*...items*/
 }
 
 // ES 2016 draft Mar 25, 2016 22.2.2.4.
 function TypedArraySpecies() {
     // Step 1.
     return this;
 }
 
+// ES 2017 draft June 2, 2016 22.2.3.32
+function TypedArrayToStringTag() {
+    // Step 1.
+    var O = this;
+
+    // Steps 2-3.
+    if (!IsObject(O) || !IsTypedArray(O))
+        return undefined;
+
+    // Steps 4-6.
+    // Modified to retrieve the [[TypedArrayName]] from the constructor.
+    return _NameForTypedArray(O);
+}
+_SetCanonicalName(TypedArrayToStringTag, "get [Symbol.toStringTag]");
+
 // ES 2016 draft Mar 25, 2016 24.1.4.3.
 function ArrayBufferSlice(start, end) {
     // Step 1.
     var O = this;
 
     // Steps 2-3,
     // This function is not generic.
     if (!IsObject(O) || !IsArrayBuffer(O)) {
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -430,16 +430,18 @@ InitWeakMapClass(JSContext* cx, HandleOb
         return nullptr;
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
 
     if (defineMembers) {
         if (!DefinePropertiesAndFunctions(cx, proto, nullptr, weak_map_methods))
             return nullptr;
+        if (!DefineToStringTag(cx, proto, cx->names().WeakMap))
+            return nullptr;
     }
 
     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, proto))
         return nullptr;
     return proto;
 }
 
 JSObject*
--- a/js/src/builtin/WeakSetObject.cpp
+++ b/js/src/builtin/WeakSetObject.cpp
@@ -46,16 +46,17 @@ WeakSetObject::initClass(JSContext* cx, 
     RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!proto)
         return nullptr;
 
     Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(JSProto_WeakSet, cx), 0));
     if (!ctor ||
         !LinkConstructorAndPrototype(cx, ctor, proto) ||
         !DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
+        !DefineToStringTag(cx, proto, cx->names().WeakSet) ||
         !GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakSet, ctor, proto))
     {
         return nullptr;
     }
     return proto;
 }
 
 WeakSetObject*
--- a/js/src/jit-test/tests/collections/Map-surfaces-1.js
+++ b/js/src/jit-test/tests/collections/Map-surfaces-1.js
@@ -8,21 +8,17 @@ assertEq(desc.configurable, true);
 assertEq(desc.writable, true);
 
 assertEq(typeof Map, 'function');
 assertEq(Object.keys(Map).length, 0);
 assertEq(Map.length, 0);
 assertEq(Map.name, "Map");
 
 assertEq(Object.getPrototypeOf(Map.prototype), Object.prototype);
-assertEq("toStringTag" in Symbol, false,
-         "if this fails, congratulations!  implement " +
-         "Map.prototype[Symbol.toStringTag] = 'Map' in SpiderMonkey and make " +
-         "the next test check for '[object Map]' again");
-assertEq(Object.prototype.toString.call(Map.prototype), "[object Object]");
+assertEq(Object.prototype.toString.call(Map.prototype), "[object Map]");
 assertEq(Object.prototype.toString.call(new Map()), "[object Map]");
 assertEq(Object.keys(Map.prototype).join(), "");
 assertEq(Map.prototype.constructor, Map);
 
 function checkMethod(name, arity) { 
     var desc = Object.getOwnPropertyDescriptor(Map.prototype, name);
     assertEq(desc.enumerable, false);
     assertEq(desc.configurable, true);
--- a/js/src/jit-test/tests/collections/Set-surfaces-1.js
+++ b/js/src/jit-test/tests/collections/Set-surfaces-1.js
@@ -8,21 +8,17 @@ assertEq(desc.configurable, true);
 assertEq(desc.writable, true);
 
 assertEq(typeof Set, 'function');
 assertEq(Object.keys(Set).length, 0);
 assertEq(Set.length, 0);
 assertEq(Set.name, "Set");
 
 assertEq(Object.getPrototypeOf(Set.prototype), Object.prototype);
-assertEq("toStringTag" in Symbol, false,
-         "if this fails, congratulations!  implement " +
-         "Set.prototype[Symbol.toStringTag] = 'Set' in SpiderMonkey and make " +
-         "the next test check for '[object Set]' again");
-assertEq(Object.prototype.toString.call(Set.prototype), "[object Object]");
+assertEq(Object.prototype.toString.call(Set.prototype), "[object Set]");
 assertEq(Object.prototype.toString.call(new Set()), "[object Set]");
 assertEq(Object.keys(Set.prototype).join(), "");
 assertEq(Set.prototype.constructor, Set);
 
 function checkMethod(name, arity) { 
     var desc = Object.getOwnPropertyDescriptor(Set.prototype, name);
     assertEq(desc.enumerable, false);
     assertEq(desc.configurable, true);
--- a/js/src/jit-test/tests/collections/WeakMap-surfaces.js
+++ b/js/src/jit-test/tests/collections/WeakMap-surfaces.js
@@ -6,17 +6,17 @@ assertEq(desc.configurable, true);
 assertEq(desc.writable, true);
 
 assertEq(typeof WeakMap, 'function');
 assertEq(Object.keys(WeakMap).length, 0);
 assertEq(WeakMap.length, 0);
 assertEq(WeakMap.name, "WeakMap");
 
 assertEq(Object.getPrototypeOf(WeakMap.prototype), Object.prototype);
-assertEq(Object.prototype.toString.call(WeakMap.prototype), "[object Object]");
+assertEq(Object.prototype.toString.call(WeakMap.prototype), "[object WeakMap]");
 assertEq(Object.prototype.toString.call(new WeakMap()), "[object WeakMap]");
 assertEq(Object.keys(WeakMap.prototype).join(), "");
 assertEq(WeakMap.prototype.constructor, WeakMap);
 
 function checkMethod(name, arity) {
     var desc = Object.getOwnPropertyDescriptor(WeakMap.prototype, name);
     assertEq(desc.enumerable, false);
     assertEq(desc.configurable, true);
--- a/js/src/jit-test/tests/collections/WeakSet-surface.js
+++ b/js/src/jit-test/tests/collections/WeakSet-surface.js
@@ -6,17 +6,17 @@ assertEq(desc.configurable, true);
 assertEq(desc.writable, true);
 
 assertEq(typeof WeakSet, 'function');
 assertEq(Object.keys(WeakSet).length, 0);
 assertEq(WeakSet.length, 0);
 assertEq(WeakSet.name, "WeakSet");
 
 assertEq(Object.getPrototypeOf(WeakSet.prototype), Object.prototype);
-assertEq(Object.prototype.toString.call(WeakSet.prototype), "[object Object]");
+assertEq(Object.prototype.toString.call(WeakSet.prototype), "[object WeakSet]");
 assertEq(Object.prototype.toString.call(new WeakSet), "[object WeakSet]");
 assertEq(Object.keys(WeakSet.prototype).length, 0);
 assertEq(WeakSet.prototype.constructor, WeakSet);
 
 function checkMethod(name, arity) {
     var desc = Object.getOwnPropertyDescriptor(WeakSet.prototype, name);
     assertEq(desc.enumerable, false);
     assertEq(desc.configurable, true);
--- a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
@@ -4,23 +4,19 @@ load(libdir + "asserts.js");
 load(libdir + "iteration.js");
 
 var iterProto = null;
 
 function test(constructor) {
     var iter = new constructor()[Symbol.iterator]();
     assertDeepEq(Reflect.ownKeys(iter), []);
 
-    // Iterator prototypes only have a .next property.
-    // At least until we support @@toStringTag.
+    // Iterator prototypes only have a .next and @@toStringTag property.
     var proto1 = Object.getPrototypeOf(iter);
-
-    var names = Reflect.ownKeys(proto1);
-    names.sort();
-    assertDeepEq(Reflect.ownKeys(proto1), ['next']);
+    assertDeepEq(Reflect.ownKeys(proto1), ['next', Symbol.toStringTag]);
 
     var desc = Object.getOwnPropertyDescriptor(proto1, 'next');
     assertEq(desc.configurable, true);
     assertEq(desc.enumerable, false);
     assertEq(desc.writable, true);
 
     // %IteratorPrototype%
     var proto2 = Object.getPrototypeOf(proto1);
--- a/js/src/jit-test/tests/debug/Object-class.js
+++ b/js/src/jit-test/tests/debug/Object-class.js
@@ -7,17 +7,17 @@ g.eval('function f() { debugger; }');
 dbg.onDebuggerStatement = function (frame) {
     var arr = frame.arguments;
     assertEq(arr[0].class, "Object");
     assertEq(arr[1].class, "Array");
     assertEq(arr[2].class, "Function");
     assertEq(arr[3].class, "Date");
     assertEq(arr[4].class, "Object");
     assertEq(arr[5].class, "Function");
-    assertEq(arr[6].class, "Date");
+    assertEq(arr[6].class, "Object");
     hits++;
 };
 g.f(Object.prototype, [], eval, new Date,
     new Proxy({}, {}), new Proxy(eval, {}), new Proxy(new Date, {}));
 assertEq(hits, 1);
 
 // Debugger.Object.prototype.class should see through cross-compartment
 // wrappers.
--- a/js/src/jit-test/tests/proxy/operations-on-revoked.js
+++ b/js/src/jit-test/tests/proxy/operations-on-revoked.js
@@ -6,13 +6,13 @@ r.revoke();
 r2.revoke();
 
 var p = r.proxy;
 var p2 = r2.proxy;
 
 assertThrowsInstanceOf(() => ({} instanceof p), TypeError);
 assertThrowsInstanceOf(() => ({} instanceof p2), TypeError);
 
-assertEq(Object.prototype.toString.call(p), "[object Object]");
-assertEq(Object.prototype.toString.call(p2), "[object Function]");
+assertThrowsInstanceOf(() => Object.prototype.toString.call(p), TypeError);
+assertThrowsInstanceOf(() => Object.prototype.toString.call(p2), TypeError);
 
 assertThrowsInstanceOf(() => RegExp.prototype.exec.call(p, ""), TypeError);
 assertThrowsInstanceOf(() => RegExp.prototype.exec.call(p2, ""), TypeError);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4962,16 +4962,17 @@ GetSymbolDescription(HandleSymbol symbol
     macro(iterator) \
     macro(match) \
     macro(replace) \
     macro(search) \
     macro(species) \
     macro(hasInstance) \
     macro(split) \
     macro(toPrimitive) \
+    macro(toStringTag) \
     macro(unscopables)
 
 enum class SymbolCode : uint32_t {
     // There is one SymbolCode for each well-known symbol.
 #define JS_DEFINE_SYMBOL_ENUM(name) name,
     JS_FOR_EACH_WELL_KNOWN_SYMBOL(JS_DEFINE_SYMBOL_ENUM)  // SymbolCode::iterator, etc.
 #undef JS_DEFINE_SYMBOL_ENUM
     Limit,
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1490,18 +1490,22 @@ GlobalObject::initArrayIteratorProto(JSC
         return true;
 
     RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
     if (!iteratorProto)
         return false;
 
     const Class* cls = &ArrayIteratorPrototypeClass;
     RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto));
-    if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, array_iterator_methods))
+    if (!proto ||
+        !DefinePropertiesAndFunctions(cx, proto, nullptr, array_iterator_methods) ||
+        !DefineToStringTag(cx, proto, cx->names().ArrayIterator))
+    {
         return false;
+    }
 
     global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
     return true;
 }
 
 /* static */ bool
 GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
 {
@@ -1509,18 +1513,22 @@ GlobalObject::initStringIteratorProto(JS
         return true;
 
     RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
     if (!iteratorProto)
         return false;
 
     const Class* cls = &StringIteratorPrototypeClass;
     RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto));
-    if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods))
+    if (!proto ||
+        !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods) ||
+        !DefineToStringTag(cx, proto, cx->names().StringIterator))
+    {
         return false;
+    }
 
     global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
     return true;
 }
 
 JSObject*
 js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj)
 {
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -1383,13 +1383,15 @@ js::InitMathClass(JSContext* cx, HandleO
                            JS_STUBGETTER, JS_STUBSETTER))
     {
         return nullptr;
     }
     if (!JS_DefineFunctions(cx, Math, math_static_methods))
         return nullptr;
     if (!JS_DefineConstDoubles(cx, Math, math_constants))
         return nullptr;
+    if (!DefineToStringTag(cx, Math, cx->names().Math))
+        return nullptr;
 
     obj->as<GlobalObject>().setConstructor(JSProto_Math, ObjectValue(*Math));
 
     return Math;
 }
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -985,12 +985,15 @@ js::InitJSONClass(JSContext* cx, HandleO
                            JS_STUBGETTER, JS_STUBSETTER))
     {
         return nullptr;
     }
 
     if (!JS_DefineFunctions(cx, JSON, json_static_methods))
         return nullptr;
 
+    if (!DefineToStringTag(cx, JSON, cx->names().JSON))
+        return nullptr;
+
     global->setConstructor(JSProto_JSON, ObjectValue(*JSON));
 
     return JSON;
 }
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -1248,22 +1248,19 @@ ScriptedProxyHandler::isArray(JSContext*
     *answer = IsArrayAnswer::RevokedProxy;
     return true;
 }
 
 const char*
 ScriptedProxyHandler::className(JSContext* cx, HandleObject proxy) const
 {
     // Right now the caller is not prepared to handle failures.
-    RootedObject target(cx, proxy->as<ProxyObject>().target());
-    if (!target)
-        return BaseProxyHandler::className(cx, proxy);
+    return BaseProxyHandler::className(cx, proxy);
+}
 
-    return GetObjectClassName(cx, target);
-}
 JSString*
 ScriptedProxyHandler::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;
 }
 
--- a/js/src/tests/ecma_6/Generators/runtime.js
+++ b/js/src/tests/ecma_6/Generators/runtime.js
@@ -69,19 +69,17 @@ function TestGeneratorObjectPrototype() 
     var expected_property_names = ["next", "return", "throw", "constructor"];
     var found_property_names =
         Object.getOwnPropertyNames(GeneratorObjectPrototype);
 
     expected_property_names.sort();
     found_property_names.sort();
 
     assertDeepEq(found_property_names, expected_property_names);
-
-    // No symbol properties, at least until we have @@toStringTag.
-    assertEq(Object.getOwnPropertySymbols(GeneratorObjectPrototype).length, 0);
+    assertDeepEq(Object.getOwnPropertySymbols(GeneratorObjectPrototype), [Symbol.toStringTag]);
 }
 TestGeneratorObjectPrototype();
 
 
 // This tests the object that would be called "GeneratorFunction", if it were
 // like "Function".
 function TestGeneratorFunction() {
     assertEq(GeneratorFunctionPrototype, GeneratorFunction.prototype);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Symbol/toStringTag.js
@@ -0,0 +1,153 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// ES6 19.1.3.6 Object.prototype.toString ( )
+function testToString() {
+    var tests = [
+        [undefined, "[object Undefined]"],
+        [null, "[object Null]"],
+        [[], "[object Array]"],
+        [new String("abc"), "[object String]"],
+        [(function () {return arguments;})(), "[object Arguments]"],
+        [(function () {"use strict"; return arguments;})(), "[object Arguments]"],
+        [function() {}, "[object Function]"],
+        [new Error("abc"), "[object Error]"],
+        [true, "[object Boolean]"],
+        [5, "[object Number]"],
+        [new Date(), "[object Date]"],
+        [/regexp/, "[object RegExp]"],
+        [{[Symbol.toStringTag]: "abc"}, "[object abc]"],
+        [Object.create(JSON), "[object JSON]"],
+        [Object.create(new Number), "[object Object]"],
+        [Object.create(new Number, {[Symbol.toStringTag]: {value: "abc"}}), "[object abc]"],
+        [(function() { var x = new Number(); x[Symbol.toStringTag] = "abc"; return x; })(), "[object abc]"],
+        [[], "[object Array]"]
+    ];
+
+    // Testing if the values are obtained the right way.
+    for (let [value, expected] of tests) {
+        let result = Object.prototype.toString.call(value);
+        assertEq(result, expected);
+    }
+}
+testToString();
+
+function testProxy() {
+    var count = 0;
+    var metaHandler = new Proxy({}, {
+        get(target, property, receiver) {
+            assertEq(property, "get");
+
+            return function(target, property, receiver) {
+                assertEq(property, Symbol.toStringTag);
+                count++;
+                return undefined;
+            }
+        }
+    });
+
+    assertEq(Object.prototype.toString.call(new Proxy({}, metaHandler)), "[object Object]")
+    assertEq(Object.prototype.toString.call(new Proxy(new Date, metaHandler)), "[object Object]")
+    assertEq(Object.prototype.toString.call(new Proxy([], metaHandler)), "[object Array]")
+    assertEq(Object.prototype.toString.call(new Proxy(function() {}, metaHandler)), "[object Function]")
+    var {proxy, revoke} = Proxy.revocable({}, metaHandler);
+    revoke();
+    assertThrowsInstanceOf(() => Object.prototype.toString.call(proxy), TypeError);
+
+    assertEq(count, 4);
+}
+testProxy();
+
+// Tests the passed objects toStringTag values and ensures it's
+// desc is writable: false, enumerable: false, configurable: true
+function testDefault(object, expected) {
+    let desc = Object.getOwnPropertyDescriptor(object, Symbol.toStringTag);
+    assertEq(desc.value, expected);
+    assertEq(desc.writable, false);
+    assertEq(desc.enumerable, false);
+    assertEq(desc.configurable, true);
+}
+
+// ES6 19.4.3.5 Symbol.prototype [ @@toStringTag ]
+testDefault(Symbol.prototype, "Symbol");
+
+// ES6 20.2.1.9 Math [ @@toStringTag ]
+testDefault(Math, "Math");
+
+// ES6 21.1.5.2.2 %StringIteratorPrototype% [ @@toStringTag ]
+testDefault(""[Symbol.iterator]().__proto__, "String Iterator")
+
+// ES6 22.1.5.2.2 %ArrayIteratorPrototype% [ @@toStringTag ]
+testDefault([][Symbol.iterator]().__proto__, "Array Iterator")
+
+// ES6 22.2.3.31 get %TypedArray%.prototype [ @@toStringTag ]
+function testTypedArray() {
+    let ta = (new Uint8Array(0)).__proto__.__proto__;
+    let desc = Object.getOwnPropertyDescriptor(ta, Symbol.toStringTag);
+    assertEq(desc.enumerable, false);
+    assertEq(desc.configurable, true);
+    assertEq(desc.set, undefined);
+
+    let get = desc.get;
+    assertEq(get.name, "get [Symbol.toStringTag]");
+    assertEq(get.call(3.14), undefined);
+    assertEq(get.call({}), undefined);
+    assertEq(get.call(ta), undefined);
+
+    let types = [
+        Int8Array,
+        Uint8Array,
+        Int16Array,
+        Uint16Array,
+        Int32Array,
+        Uint32Array,
+        Float32Array,
+        Float64Array
+    ];
+
+    for (let type of types) {
+        let array = new type(0);
+        assertEq(get.call(array), type.name);
+        assertEq(Object.prototype.toString.call(array), `[object ${type.name}]`);
+    }
+}
+testTypedArray();
+
+// ES6 23.1.3.13 Map.prototype [ @@toStringTag ]
+testDefault(Map.prototype, "Map");
+
+// ES6 23.1.5.2.2 %MapIteratorPrototype% [ @@toStringTag ]
+testDefault(new Map()[Symbol.iterator]().__proto__, "Map Iterator")
+
+// ES6 23.2.3.12 Set.prototype [ @@toStringTag ]
+testDefault(Set.prototype, "Set");
+
+// ES6 23.2.5.2.2 %SetIteratorPrototype% [ @@toStringTag ]
+testDefault(new Set()[Symbol.iterator]().__proto__, "Set Iterator")
+
+// ES6 23.3.3.6 WeakMap.prototype [ @@toStringTag ]
+testDefault(WeakMap.prototype, "WeakMap");
+
+// ES6 23.4.3.5 WeakSet.prototype [ @@toStringTag ]
+testDefault(WeakSet.prototype, "WeakSet");
+
+// ES6 24.1.4.4 ArrayBuffer.prototype [ @@toStringTag ]
+testDefault(ArrayBuffer.prototype, "ArrayBuffer");
+
+// ES6 24.2.4.21 DataView.prototype[ @@toStringTag ]
+testDefault(DataView.prototype, "DataView");
+
+// ES6 24.3.3 JSON [ @@toStringTag ]
+testDefault(JSON, "JSON");
+
+// ES6 25.2.3.3 GeneratorFunction.prototype [ @@toStringTag ]
+testDefault(function* () {}.constructor.prototype, "GeneratorFunction");
+
+// ES6 25.3.1.5 Generator.prototype [ @@toStringTag ]
+testDefault(function* () {}().__proto__.__proto__, "Generator");
+
+// ES6 25.4.5.4 Promise.prototype [ @@toStringTag ]
+// testDefault(Promise.prototype, "Promise");
+// Promise is not yet implemented.
+
+reportCompare(true, true);
--- a/js/src/tests/ecma_6/shell.js
+++ b/js/src/tests/ecma_6/shell.js
@@ -94,17 +94,18 @@ if (typeof assertDeepEq === 'undefined')
             }
         }
 
         function assertSameClass(a, b, msg) {
             var ac = Object_toString(a), bc = Object_toString(b);
             assertSameValue(ac, bc, msg);
             switch (ac) {
             case "[object Function]":
-                assertSameValue(Function_toString(a), Function_toString(b), msg);
+                if (typeof isProxy !== "undefined" && !isProxy(a) && !isProxy(b))
+                    assertSameValue(Function_toString(a), Function_toString(b), msg);
             }
         }
 
         function at(prevmsg, segment) {
             return prevmsg ? prevmsg + segment : "at _" + segment;
         }
 
         // Assert that the arguments a and b are thoroughly structurally equivalent.
--- a/js/src/tests/js1_8_5/extensions/dataview.js
+++ b/js/src/tests/js1_8_5/extensions/dataview.js
@@ -1527,17 +1527,17 @@ function test() {
     assertEq(view[0], 3);
     assertEq(view.getUint8(0), 1);
 
     // Test WebIDL-specific class and prototype class names
     assertEq(Object.prototype.toString.apply(new Uint8Array(0)), "[object Uint8Array]");
     assertEq(Object.prototype.toString.apply(new Float32Array(0)), "[object Float32Array]");
     assertEq(Object.prototype.toString.apply(new ArrayBuffer()), "[object ArrayBuffer]");
     assertEq(Object.prototype.toString.apply(new DataView(view.buffer)), "[object DataView]");
-    assertEq(Object.prototype.toString.apply(DataView.prototype), "[object DataViewPrototype]");
+    assertEq(Object.prototype.toString.apply(DataView.prototype), "[object DataView]");
 
     // Technically the spec requires these throw a TypeError -- right now.  It's
     // not clear this is desirable.  Once we implement @@toStringTag we can see
     // whether ES6's desired semantics will work.
     assertEq(Object.prototype.toString.apply(Uint8Array.prototype), "[object Uint8ArrayPrototype]");
     assertEq(Object.prototype.toString.apply(Float32Array.prototype), "[object Float32ArrayPrototype]");
 
     // Same applies for %TypedArray%.prototype, except because of its newness we
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1766,16 +1766,19 @@ js::InitArrayBufferClass(JSContext* cx, 
         return nullptr;
 
     if (!JS_DefineProperties(cx, ctor, ArrayBufferObject::jsstaticprops))
         return nullptr;
 
     if (!JS_DefineFunctions(cx, arrayBufferProto, ArrayBufferObject::jsfuncs))
         return nullptr;
 
+    if (!DefineToStringTag(cx, arrayBufferProto, cx->names().ArrayBuffer))
+        return nullptr;
+
     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_ArrayBuffer,
                                               ctor, arrayBufferProto))
     {
         return nullptr;
     }
 
     return arrayBufferProto;
 }
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -15,16 +15,17 @@
     macro(add, add, "add") \
     macro(allowContentSpread, allowContentSpread, "allowContentSpread") \
     macro(anonymous, anonymous, "anonymous") \
     macro(Any, Any, "Any") \
     macro(apply, apply, "apply") \
     macro(arguments, arguments, "arguments") \
     macro(as, as, "as") \
     macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \
+    macro(ArrayIterator, ArrayIterator, "Array Iterator") \
     macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
     macro(ArraySpecies, ArraySpecies, "ArraySpecies") \
     macro(ArraySpeciesCreate, ArraySpeciesCreate, "ArraySpeciesCreate") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(ArrayValues, ArrayValues, "ArrayValues") \
     macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
     macro(Async, Async, "Async") \
     macro(Bool8x16, Bool8x16, "Bool8x16") \
@@ -116,16 +117,17 @@
     macro(format, format, "format") \
     macro(formatToParts, formatToParts, "formatToParts") \
     macro(frame, frame, "frame") \
     macro(from, from, "from") \
     macro(futexOK, futexOK, "ok") \
     macro(futexNotEqual, futexNotEqual, "not-equal") \
     macro(futexTimedOut, futexTimedOut, "timed-out") \
     macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \
+    macro(Generator, Generator, "Generator") \
     macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \
     macro(get, get, "get") \
     macro(getPrefix, getPrefix, "get ") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
     macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
     macro(getPrototypeOf, getPrototypeOf, "getPrototypeOf") \
@@ -170,16 +172,17 @@
     macro(let, let, "let") \
     macro(line, line, "line") \
     macro(lineNumber, lineNumber, "lineNumber") \
     macro(literal, literal, "literal") \
     macro(loc, loc, "loc") \
     macro(locale, locale, "locale") \
     macro(lookupGetter, lookupGetter, "__lookupGetter__") \
     macro(lookupSetter, lookupSetter, "__lookupSetter__") \
+    macro(MapIterator, MapIterator, "Map Iterator") \
     macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \
     macro(maximumSignificantDigits, maximumSignificantDigits, "maximumSignificantDigits") \
     macro(message, message, "message") \
     macro(minimumFractionDigits, minimumFractionDigits, "minimumFractionDigits") \
     macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \
     macro(minimumSignificantDigits, minimumSignificantDigits, "minimumSignificantDigits") \
     macro(minute, minute, "minute") \
     macro(missingArguments, missingArguments, "missingArguments") \
@@ -198,36 +201,40 @@
     macro(NFKC, NFKC, "NFKC") \
     macro(NFKD, NFKD, "NFKD") \
     macro(nonincrementalReason, nonincrementalReason, "nonincrementalReason") \
     macro(noFilename, noFilename, "noFilename") \
     macro(noStack, noStack, "noStack") \
     macro(NumberFormat, NumberFormat, "NumberFormat") \
     macro(NumberFormatFormatGet, NumberFormatFormatGet, "Intl_NumberFormat_format_get") \
     macro(numeric, numeric, "numeric") \
+    macro(objectUndefined, objectUndefined, "[object Undefined]") \
+    macro(objectNull, objectNull, "[object Null]") \
     macro(objectArray, objectArray, "[object Array]") \
+    macro(objectString, objectString, "[object String]") \
+    macro(objectArguments, objectArguments, "[object Arguments]") \
     macro(objectFunction, objectFunction, "[object Function]") \
-    macro(objectNull, objectNull, "[object Null]") \
+    macro(objectError, objectError, "[object Error]") \
+    macro(objectBoolean, objectBoolean, "[object Boolean]") \
     macro(objectNumber, objectNumber, "[object Number]") \
-    macro(objectObject, objectObject, "[object Object]") \
+    macro(objectDate, objectDate, "[object Date]") \
+    macro(objectRegExp, objectRegExp, "[object RegExp]") \
     macro(objects, objects, "objects") \
-    macro(objectString, objectString, "[object String]") \
-    macro(objectUndefined, objectUndefined, "[object Undefined]") \
-    macro(objectWindow, objectWindow, "[object Window]") \
     macro(of, of, "of") \
     macro(offset, offset, "offset") \
     macro(optimizedOut, optimizedOut, "optimizedOut") \
     macro(other, other, "other") \
     macro(outOfMemory, outOfMemory, "out of memory") \
     macro(ownKeys, ownKeys, "ownKeys") \
     macro(parseFloat, parseFloat, "parseFloat") \
     macro(parseInt, parseInt, "parseInt") \
     macro(pattern, pattern, "pattern") \
     macro(preventExtensions, preventExtensions, "preventExtensions") \
     macro(promise, promise, "promise") \
+    macro(SetIterator, SetIterator, "Set Iterator") \
     macro(state, state, "state") \
     macro(pending, pending, "pending") \
     macro(fulfillHandler, fulfillHandler, "fulfillHandler") \
     macro(fulfilled, fulfilled, "fulfilled") \
     macro(reject, reject, "reject") \
     macro(rejected, rejected, "rejected") \
     macro(rejectHandler, rejectHandler, "rejectHandler") \
     macro(resolve, resolve, "resolve") \
@@ -255,16 +262,17 @@
     macro(SpeciesConstructor, SpeciesConstructor, "SpeciesConstructor") \
     macro(stack, stack, "stack") \
     macro(star, star, "*") \
     macro(starDefaultStar, starDefaultStar, "*default*") \
     macro(startTimestamp, startTimestamp, "startTimestamp") \
     macro(static, static_, "static") \
     macro(sticky, sticky, "sticky") \
     macro(strings, strings, "strings") \
+    macro(StringIterator, StringIterator, "String Iterator") \
     macro(StructType, StructType, "StructType") \
     macro(style, style, "style") \
     macro(super, super, "super") \
     macro(target, target, "target") \
     macro(test, test, "test") \
     macro(then, then, "then") \
     macro(throw, throw_, "throw") \
     macro(timestamp, timestamp, "timestamp") \
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -295,24 +295,30 @@ GlobalObject::initStarGenerators(JSConte
     if (!iteratorProto)
         return false;
 
     RootedObject genObjectProto(cx, global->createBlankPrototypeInheriting(cx,
                                                                            &PlainObject::class_,
                                                                            iteratorProto));
     if (!genObjectProto)
         return false;
-    if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods))
+    if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods) ||
+        !DefineToStringTag(cx, genObjectProto, cx->names().Generator))
+    {
         return false;
+    }
 
     RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
     if (!genFunctionProto || !genFunctionProto->setDelegate(cx))
         return false;
-    if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
+    if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) ||
+        !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction))
+    {
         return false;
+    }
 
     RootedValue function(cx, global->getConstructor(JSProto_Function));
     if (!function.toObjectOrNull())
         return false;
     RootedObject proto(cx, &function.toObject());
     RootedAtom name(cx, cx->names().GeneratorFunction);
     RootedObject genFunction(cx, NewFunctionWithProto(cx, Generator, 1,
                                                       JSFunction::NATIVE_CTOR, nullptr, name,
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -652,16 +652,24 @@ js::DefinePropertiesAndFunctions(JSConte
 {
     if (ps && !JS_DefineProperties(cx, obj, ps))
         return false;
     if (fs && !JS_DefineFunctions(cx, obj, fs))
         return false;
     return true;
 }
 
+bool
+js::DefineToStringTag(JSContext *cx, HandleObject obj, JSAtom* tag)
+{
+    RootedId toStringTagId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
+    RootedValue tagString(cx, StringValue(tag));
+    return DefineProperty(cx, obj, toStringTagId, tagString, nullptr, nullptr, JSPROP_READONLY);
+}
+
 static void
 GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->maybeOffMainThread());
     fop->delete_((GlobalObject::DebuggerVector*) obj->as<NativeObject>().getPrivate());
 }
 
 static const ClassOps
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -948,16 +948,19 @@ LinkConstructorAndPrototype(JSContext* c
  * may be null.
  */
 extern bool
 DefinePropertiesAndFunctions(JSContext* cx, HandleObject obj,
                              const JSPropertySpec* ps, const JSFunctionSpec* fs);
 
 typedef HashSet<GlobalObject*, DefaultHasher<GlobalObject*>, SystemAllocPolicy> GlobalObjectSet;
 
+extern bool
+DefineToStringTag(JSContext *cx, HandleObject obj, JSAtom* tag);
+
 /*
  * Convenience templates to generic constructor and prototype creation functions
  * for ClassSpecs.
  */
 
 template<JSNative ctor, unsigned length, gc::AllocKind kind, const JSJitInfo* jitInfo = nullptr>
 JSObject*
 GenericCreateConstructor(JSContext* cx, JSProtoKey key)
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -41,16 +41,17 @@
 #include "vm/Compression.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/RegExpObject.h"
 #include "vm/String.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
 
+#include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/BooleanObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/NumberObject-inl.h"
 #include "vm/StringObject-inl.h"
@@ -1965,16 +1966,33 @@ intrinsic_ConstructorForTypedArray(JSCon
     RootedObject ctor(cx);
     if (!GetBuiltinConstructor(cx, protoKey, &ctor))
         return false;
 
     args.rval().setObject(*ctor);
     return true;
 }
 
+static bool
+intrinsic_NameForTypedArray(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+    MOZ_ASSERT(args[0].isObject());
+
+    RootedObject object(cx, &args[0].toObject());
+    MOZ_ASSERT(object->is<TypedArrayObject>());
+
+    JSProtoKey protoKey = StandardProtoKeyOrNull(object);
+    MOZ_ASSERT(protoKey);
+
+    args.rval().setString(ClassName(protoKey, cx));
+    return true;
+}
+
 /**
  * Returns an object created in the embedding-provided incumbent global.
  *
  * Really, we want the incumbent global itself so we can pass it to other
  * embedding hooks which need it. Specifically, the enqueue promise hook
  * takes an incumbent global so it can set that on the PromiseCallbackJob
  * it creates.
  *
@@ -2287,16 +2305,17 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("ThrowTypeError",          intrinsic_ThrowTypeError,          4,0),
     JS_FN("ThrowSyntaxError",        intrinsic_ThrowSyntaxError,        4,0),
     JS_FN("ThrowInternalError",      intrinsic_ThrowInternalError,      4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
     JS_FN("DumpMessage",             intrinsic_DumpMessage,             1,0),
     JS_FN("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
     JS_FN("MakeDefaultConstructor",  intrinsic_MakeDefaultConstructor,  2,0),
     JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
+    JS_FN("_NameForTypedArray",      intrinsic_NameForTypedArray, 1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 4,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
     JS_FN("LocalTZA",                intrinsic_LocalTZA,                0,0),
     JS_FN("AddContentTelemetry",     intrinsic_AddContentTelemetry,     2,0),
 
     JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing,        0,0,
                     IntrinsicIsConstructing),
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1334,16 +1334,17 @@ js::TypedArray_bufferGetter(JSContext* c
 }
 
 /* static */ const JSPropertySpec
 TypedArrayObject::protoAccessors[] = {
     JS_PSG("length", TypedArray_lengthGetter, 0),
     JS_PSG("buffer", TypedArray_bufferGetter, 0),
     JS_PSG("byteLength", TypedArray_byteLengthGetter, 0),
     JS_PSG("byteOffset", TypedArray_byteOffsetGetter, 0),
+    JS_SELF_HOSTED_SYM_GET(toStringTag, "TypedArrayToStringTag", 0),
     JS_PS_END
 };
 
 /* static */ bool
 TypedArrayObject::set(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<TypedArrayObject::is,
@@ -2727,16 +2728,19 @@ DataViewObject::initClass(JSContext* cx)
         return false;
 
     if (!defineGetter<byteOffsetValue>(cx, cx->names().byteOffset, proto))
         return false;
 
     if (!JS_DefineFunctions(cx, proto, DataViewObject::jsfuncs))
         return false;
 
+    if (!DefineToStringTag(cx, proto, cx->names().DataView))
+        return false;
+
     /*
      * Create a helper function to implement the craziness of
      * |new DataView(new otherWindow.ArrayBuffer())|, and install it in the
      * global for use by the DataViewObject constructor.
      */
     RootedFunction fun(cx, NewNativeFunction(cx, ArrayBufferObject::createDataViewForThis,
                                              0, nullptr));
     if (!fun)