#392: Symbol.toStringTag M1114580 (w/o ESClassValue change; w/45ESR boilerplate)

This commit is contained in:
Cameron Kaiser 2018-10-02 11:34:30 -07:00
parent c1f84d6285
commit 3cf6b4057c
34 changed files with 428 additions and 121 deletions

View File

@ -1327,6 +1327,7 @@ static bool
NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result)
{
MOZ_ASSERT(false, "See bug 1114580, 2f9eb93beee9"); // NYI XXX TenFourFox 392
NPObject *npobj = GetNPObject(cx, obj);
if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||

View File

@ -847,6 +847,10 @@ enum ESClassValue {
ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer,
ESClass_Date, ESClass_Set, ESClass_Map,
/** Not yet implemented (TenFourFox issue 392). */
ESClass_Promise, ESClass_MapIterator, ESClass_SetIterator,
ESClass_Arguments, ESClass_Error,
/** None of the above. */
ESClass_Other
};

View File

@ -171,7 +171,8 @@ GlobalObject::initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
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;
@ -320,6 +321,10 @@ MapObject::initClass(JSContext* cx, JSObject* obj)
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;
}
@ -897,7 +902,8 @@ GlobalObject::initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
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;
@ -1051,6 +1057,10 @@ SetObject::initClass(JSContext* cx, JSObject* obj)
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;
}

View File

@ -306,63 +306,141 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
}
#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 (after bug 1114580/TenFourFox issue 392) */
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.
ESClassValue cls;
if (!GetBuiltinClass(cx, obj, &cls))
return false;
switch (cls) {
case ESClass_String:
builtinTag = cx->names().objectString;
break;
case ESClass_Arguments: // NYI XXX
builtinTag = cx->names().objectArguments;
break;
case ESClass_Error: // NYI XXX
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)) {
if (IsProxy(obj)) {
// XXX: This is a dirty hack for jit-test/tests/basic/bug807623.js.
// Just pass through if the property lookup fails; assume that we
// didn't have a tag set in that situation. Proxy::get sets the tag
// to undefined for us in that case. TenFourFox issue 392.
//
// Note that this isn't an issue for current Firefox because
// Proxy.create() was removed. We can't do this because of our
// backwards compatibility, and other tests also rely on it
// currently (see M892903), but fortunately using proper ES6 Proxy
// methods works as expected even without this hack:
//
// var h={}; var p=new Proxy({[Symbol.toStringTag]:"abc"}, h);
// print(Object.prototype.toString.call(p)); /* [object abc] */
//
// Thus, this code path is only entered in the legacy case, which
// shouldn't know about @@toStringTag anyway.
//
RootedValue receiver(cx, ObjectValue(*obj));
(void)proxy_GetProperty(cx, obj, receiver, toStringTagId, &tag);
cx->clearPendingException();
} else
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;
}

View File

@ -79,6 +79,7 @@ SymbolObject::initClass(JSContext* cx, HandleObject obj)
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))
{

View File

@ -1167,3 +1167,25 @@ function TypedArrayStaticOf(/*...items*/) {
// Step 8.
return newObj;
}
// 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]");

View File

@ -455,6 +455,8 @@ InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers)
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))

View File

@ -53,6 +53,7 @@ WeakSetObject::initClass(JSContext* cx, JSObject* obj)
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;

View File

@ -13,11 +13,7 @@ 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);

View File

@ -13,11 +13,7 @@ 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);

View File

@ -11,7 +11,7 @@ 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);

View File

@ -11,7 +11,7 @@ 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);

View File

@ -9,13 +9,9 @@ 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);

View File

@ -12,7 +12,7 @@ dbg.onDebuggerStatement = function (frame) {
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,

View File

@ -11,8 +11,8 @@ 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);

View File

@ -4561,6 +4561,7 @@ GetSymbolDescription(HandleSymbol symbol);
macro(match) \
macro(species) \
macro(toPrimitive) \
macro(toStringTag) \
macro(unscopables)
enum class SymbolCode : uint32_t {

View File

@ -1473,7 +1473,9 @@ GlobalObject::initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global
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));
@ -1492,7 +1494,9 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> globa
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));

View File

@ -1647,6 +1647,8 @@ js::InitMathClass(JSContext* cx, HandleObject obj)
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));

View File

@ -954,6 +954,9 @@ js::InitJSONClass(JSContext* cx, HandleObject obj)
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;

View File

@ -1128,12 +1128,9 @@ const char*
ScriptedDirectProxyHandler::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 GetObjectClassName(cx, target);
return BaseProxyHandler::className(cx, proxy);
}
JSString*
ScriptedDirectProxyHandler::fun_toString(JSContext* cx, HandleObject proxy,
unsigned indent) const

View File

@ -74,9 +74,7 @@ function TestGeneratorObjectPrototype() {
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();

View File

@ -0,0 +1,158 @@
/* 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 ]
// Not yet implemented!
if (typeof Promise !== "undefined")
testDefault(Promise.prototype, "Promise");
// AsyncFunction.prototype [ @@toStringTag ]
// Not yet implemented!
// testDefault(async function() {}.constructor.prototype, "AsyncFunction");
reportCompare(true, true);

View File

@ -82,7 +82,8 @@ if (typeof assertDeepEq === 'undefined') {
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);
}
}

View File

@ -1562,7 +1562,7 @@ function test() {
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

View File

@ -1650,6 +1650,9 @@ js::InitArrayBufferClass(JSContext* cx, HandleObject obj)
if (!JS_DefineFunctions(cx, arrayBufferProto, ArrayBufferObject::jsfuncs))
return nullptr;
if (!DefineToStringTag(cx, arrayBufferProto, cx->names().ArrayBuffer))
return nullptr;
return arrayBufferProto;
}

View File

@ -18,6 +18,7 @@
macro(apply, apply, "apply") \
macro(arguments, arguments, "arguments") \
macro(as, as, "as") \
macro(ArrayIterator, ArrayIterator, "Array Iterator") \
macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
macro(ArrayType, ArrayType, "ArrayType") \
macro(ArrayValues, ArrayValues, "ArrayValues") \
@ -99,6 +100,7 @@
macro(frame, frame, "frame") \
macro(from, from, "from") \
macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \
macro(Generator, Generator, "Generator") \
macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \
macro(get, get, "get") \
macro(getInternals, getInternals, "getInternals") \
@ -139,6 +141,7 @@
macro(keys, keys, "keys") \
macro(label, label, "label") \
macro(lastIndex, lastIndex, "lastIndex") \
macro(LegacyGenerator, LegacyGenerator, "LegacyGenerator") \
macro(LegacyGeneratorCloseInternal, LegacyGeneratorCloseInternal, "LegacyGeneratorCloseInternal") \
macro(length, length, "length") \
macro(let, let, "let") \
@ -148,6 +151,7 @@
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") \
@ -171,15 +175,18 @@
macro(NumberFormat, NumberFormat, "NumberFormat") \
macro(NumberFormatFormatGet, NumberFormatFormatGet, "Intl_NumberFormat_format_get") \
macro(numeric, numeric, "numeric") \
macro(objectArray, objectArray, "[object Array]") \
macro(objectFunction, objectFunction, "[object Function]") \
macro(objectNull, objectNull, "[object Null]") \
macro(objectNumber, objectNumber, "[object Number]") \
macro(objectObject, objectObject, "[object Object]") \
macro(objects, objects, "objects") \
macro(objectString, objectString, "[object String]") \
macro(objectUndefined, objectUndefined, "[object Undefined]") \
macro(objectWindow, objectWindow, "[object Window]") \
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(objectError, objectError, "[object Error]") \
macro(objectBoolean, objectBoolean, "[object Boolean]") \
macro(objectNumber, objectNumber, "[object Number]") \
macro(objectDate, objectDate, "[object Date]") \
macro(objectRegExp, objectRegExp, "[object RegExp]") \
macro(objects, objects, "objects") \
macro(of, of, "of") \
macro(offset, offset, "offset") \
macro(optimizedOut, optimizedOut, "optimizedOut") \
@ -204,6 +211,7 @@
macro(scripts, scripts, "scripts") \
macro(sensitivity, sensitivity, "sensitivity") \
macro(set, set, "set") \
macro(SetIterator, SetIterator, "Set Iterator") \
macro(shape, shape, "shape") \
macro(signMask, signMask, "signMask") \
macro(size, size, "size") \
@ -215,6 +223,7 @@
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") \
@ -277,6 +286,7 @@
macro(match, match, "match") \
macro(species, species, "species") \
macro(toPrimitive, toPrimitive, "toPrimitive") \
macro(toStringTag, toStringTag, "toStringTag") \
macro(unscopables, unscopables, "unscopables") \
/* Same goes for the descriptions of the well-known symbols. */ \
macro(Symbol_hasInstance, Symbol_hasInstance, "Symbol.hasInstance") \
@ -285,6 +295,7 @@
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") \
/* Function names for properties named by symbols. */ \
macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \

View File

@ -282,7 +282,14 @@ GlobalObject::initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> glob
RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
if (!proto || !proto->setDelegate(cx))
return false;
#if(0)
/* XXX: Stub @@toStringTag for legacy generators.
TenFourFox issue 392 */
if (!DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods) ||
!DefineToStringTag(cx, proto, cx->names().LegacyGenerator))
#else
if (!DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
#endif
return false;
global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
@ -304,13 +311,15 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
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));

View File

@ -529,6 +529,14 @@ js::DefinePropertiesAndFunctions(JSContext* cx, HandleObject obj,
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)
{

View File

@ -905,6 +905,9 @@ DefinePropertiesAndFunctions(JSContext* cx, HandleObject obj,
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.

View File

@ -40,6 +40,7 @@
#include "vm/String.h"
#include "vm/TypedArrayCommon.h"
#include "jsatominlines.h"
#include "jsfuninlines.h"
#include "jsscriptinlines.h"
@ -1281,6 +1282,23 @@ intrinsic_ConstructorForTypedArray(JSContext* cx, unsigned argc, Value* vp)
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;
}
static bool
intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp)
{
@ -1531,6 +1549,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0),
JS_FN("MakeConstructible", intrinsic_MakeConstructible, 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("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
JS_FN("LocalTZA", intrinsic_LocalTZA, 0,0),

View File

@ -832,6 +832,7 @@ TypedArrayObject::protoAccessors[] = {
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
};
@ -2148,6 +2149,9 @@ DataViewObject::initClass(JSContext* cx)
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

View File

@ -2220,6 +2220,13 @@ XrayWrapper<Base, Traits>::construct(JSContext* cx, HandleObject wrapper, const
return Traits::construct(cx, wrapper, args, Base::singleton);
}
template <typename Base, typename Traits>
bool
XrayWrapper<Base, Traits>::getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, js::ESClassValue* cls) const
{
return Traits::getBuiltinClass(cx, wrapper, Base::singleton, cls);
}
template <typename Base, typename Traits>
const char*
XrayWrapper<Base, Traits>::className(JSContext* cx, HandleObject wrapper) const

View File

@ -83,6 +83,11 @@ public:
return result.succeed();
}
static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance,
js::ESClassValue* cls) {
return baseInstance.getBuiltinClass(cx, wrapper, cls);
}
static const char* className(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance) {
return baseInstance.className(cx, wrapper);
}
@ -407,6 +412,12 @@ public:
return JS_WrapObject(cx, protop);
}
static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance,
js::ESClassValue* cls) {
*cls = js::ESClass_Other;
return true;
}
static const char* className(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance) {
return "Opaque";
}
@ -473,6 +484,7 @@ class XrayWrapper : public Base {
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
JS::AutoIdVector& props) const override;
virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject wapper, js::ESClassValue* cls) const override;
virtual const char* className(JSContext* cx, JS::HandleObject proxy) const override;
static const XrayWrapper singleton;

View File

@ -35,14 +35,6 @@ function do_check_throws(f, type, stack)
do_throw("expected " + type.name + " exception, none thrown", stack);
}
function do_check_class(obj, classname, stack)
{
if (!stack)
stack = Components.stack.caller;
do_check_eq(Object.prototype.toString.call(obj), "[object " + classname + "]", stack);
}
function run_test()
{
// Test ctypes.CType and ctypes.CData are set up correctly.
@ -231,10 +223,6 @@ function run_abstract_class_tests()
do_check_throws(function() { ctypes.CType(); }, Error);
do_check_throws(function() { new ctypes.CType() }, Error);
// Test that classes and prototypes are set up correctly.
do_check_class(ctypes.CType, "Function");
do_check_class(ctypes.CType.prototype, "CType");
do_check_true(ctypes.CType.hasOwnProperty("prototype"));
do_check_throws(function() { ctypes.CType.prototype(); }, Error);
do_check_throws(function() { new ctypes.CType.prototype() }, Error);
@ -269,10 +257,6 @@ function run_abstract_class_tests()
do_check_throws(function() { ctypes.CData(); }, Error);
do_check_throws(function() { new ctypes.CData() }, Error);
// Test that classes and prototypes are set up correctly.
do_check_class(ctypes.CData, "Function");
do_check_class(ctypes.CData.prototype, "CData");
do_check_true(ctypes.CData.__proto__ === ctypes.CType.prototype);
do_check_true(ctypes.CData instanceof ctypes.CType);
@ -302,10 +286,6 @@ function run_abstract_class_tests()
function run_Int64_tests() {
do_check_throws(function() { ctypes.Int64(); }, TypeError);
// Test that classes and prototypes are set up correctly.
do_check_class(ctypes.Int64, "Function");
do_check_class(ctypes.Int64.prototype, "Int64");
do_check_true(ctypes.Int64.hasOwnProperty("prototype"));
do_check_true(ctypes.Int64.prototype.hasOwnProperty("constructor"));
do_check_true(ctypes.Int64.prototype.constructor === ctypes.Int64);
@ -473,10 +453,6 @@ function run_Int64_tests() {
function run_UInt64_tests() {
do_check_throws(function() { ctypes.UInt64(); }, TypeError);
// Test that classes and prototypes are set up correctly.
do_check_class(ctypes.UInt64, "Function");
do_check_class(ctypes.UInt64.prototype, "UInt64");
do_check_true(ctypes.UInt64.hasOwnProperty("prototype"));
do_check_true(ctypes.UInt64.prototype.hasOwnProperty("constructor"));
do_check_true(ctypes.UInt64.prototype.constructor === ctypes.UInt64);
@ -738,10 +714,6 @@ function offsetof(struct, member) {
// Test the class and prototype hierarchy for a given basic type 't'.
function run_basic_class_tests(t)
{
// Test that classes and prototypes are set up correctly.
do_check_class(t, "CType");
do_check_class(t.prototype, "CData");
do_check_true(t.__proto__ === ctypes.CType.prototype);
do_check_true(t instanceof ctypes.CType);
@ -762,7 +734,6 @@ function run_basic_class_tests(t)
// Test that an instance 'd' of 't' is a CData.
let d = t();
do_check_class(d, "CData");
do_check_true(d.__proto__ === t.prototype);
do_check_true(d instanceof t);
do_check_true(d.constructor === t);
@ -1283,10 +1254,6 @@ function run_char16_tests(library, t, name, limits) {
// Test the class and prototype hierarchy for a given type constructor 'c'.
function run_type_ctor_class_tests(c, t, t2, props=[], fns=[], instanceProps=[], instanceFns=[], specialProps=[])
{
// Test that classes and prototypes are set up correctly on the type ctor 'c'.
do_check_class(c, "Function");
do_check_class(c.prototype, "CType");
do_check_true(c.prototype.__proto__ === ctypes.CType.prototype);
do_check_true(c.prototype instanceof ctypes.CType);
do_check_true(c.prototype.constructor === c);
@ -1303,15 +1270,9 @@ function run_type_ctor_class_tests(c, t, t2, props=[], fns=[], instanceProps=[],
for (let f of fns)
do_check_throws(function() { c.prototype[f](); }, Error);
// Test that classes and prototypes are set up correctly on a constructed
// type 't'.
do_check_class(t, "CType");
do_check_class(t.prototype, "CData");
do_check_true(t.__proto__ === c.prototype);
do_check_true(t instanceof c);
do_check_class(t.prototype.__proto__, "CData");
// 't.prototype.__proto__' is the common ancestor of all types constructed
// from 'c'; while not available from 'c' directly, it should be identically
// equal to 't2.prototype.__proto__' where 't2' is a different CType
@ -1359,7 +1320,6 @@ function run_type_ctor_class_tests(c, t, t2, props=[], fns=[], instanceProps=[],
// Test that an instance 'd' of 't' is a CData.
if (t.__proto__ != ctypes.FunctionType.prototype) {
let d = t();
do_check_class(d, "CData");
do_check_true(d.__proto__ === t.prototype);
do_check_true(d instanceof t);
do_check_true(d.constructor === t);