mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1054756, part 3 - Implement Symbol.toPrimitive. Replace existing convert hooks with methods. r=jandem.
JSClass::convert is no longer used after this, but to minimize the noise, it will be deleted in a separate patch. However all non-nullptr convert hook implementations must be replaced with [@@toPrimitive] methods in this patch to avoid changing the behavior. The changes in XrayWrapper.cpp fix a pre-existing bug: when an Xray wrapper tries to emit the "Silently denied access" warning, if id is a symbol, the existing code triggers an error trying to convert it to a string for the warning message. Implementing Symbol.toPrimitive revealed this bug; the fix is straightforward.
This commit is contained in:
parent
69142fd9c8
commit
f92ef8629a
@ -183,9 +183,6 @@ static bool
|
|||||||
NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||||
bool *resolvedp);
|
bool *resolvedp);
|
||||||
|
|
||||||
static bool
|
|
||||||
NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj);
|
NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj);
|
||||||
|
|
||||||
@ -198,6 +195,9 @@ NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp);
|
|||||||
static bool
|
static bool
|
||||||
NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
|
NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||||
|
|
||||||
|
static bool
|
||||||
|
NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
|
CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
|
||||||
JS::Handle<jsid> id, NPVariant* getPropertyResult,
|
JS::Handle<jsid> id, NPVariant* getPropertyResult,
|
||||||
@ -214,7 +214,7 @@ const static js::Class sNPObjectJSWrapperClass =
|
|||||||
nullptr,
|
nullptr,
|
||||||
NPObjWrapper_Resolve,
|
NPObjWrapper_Resolve,
|
||||||
nullptr, /* mayResolve */
|
nullptr, /* mayResolve */
|
||||||
NPObjWrapper_Convert,
|
nullptr, /* convert */
|
||||||
NPObjWrapper_Finalize,
|
NPObjWrapper_Finalize,
|
||||||
NPObjWrapper_Call,
|
NPObjWrapper_Call,
|
||||||
nullptr, /* hasInstance */
|
nullptr, /* hasInstance */
|
||||||
@ -251,7 +251,8 @@ typedef struct NPObjectMemberPrivate {
|
|||||||
} NPObjectMemberPrivate;
|
} NPObjectMemberPrivate;
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
|
NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
|
||||||
|
JS::MutableHandleValue vp);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
|
NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
|
||||||
@ -262,11 +263,14 @@ NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
|
|||||||
static void
|
static void
|
||||||
NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
|
NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
|
||||||
|
|
||||||
|
static bool
|
||||||
|
NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||||
|
|
||||||
static const JSClass sNPObjectMemberClass =
|
static const JSClass sNPObjectMemberClass =
|
||||||
{
|
{
|
||||||
"NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
|
"NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
|
||||||
|
nullptr, nullptr, NPObjectMember_GetProperty, nullptr,
|
||||||
nullptr, nullptr, nullptr, nullptr,
|
nullptr, nullptr, nullptr, nullptr,
|
||||||
nullptr, nullptr, nullptr, NPObjectMember_Convert,
|
|
||||||
NPObjectMember_Finalize, NPObjectMember_Call,
|
NPObjectMember_Finalize, NPObjectMember_Call,
|
||||||
nullptr, nullptr, NPObjectMember_Trace
|
nullptr, nullptr, NPObjectMember_Trace
|
||||||
};
|
};
|
||||||
@ -1392,6 +1396,20 @@ NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<js
|
|||||||
return false;
|
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
|
// Find out what plugin (NPP) is the owner of the object we're
|
||||||
// manipulating, and make it own any JSObject wrappers created here.
|
// manipulating, and make it own any JSObject wrappers created here.
|
||||||
NPP npp = LookupNPP(npobj);
|
NPP npp = LookupNPP(npobj);
|
||||||
@ -1713,42 +1731,6 @@ NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid>
|
|||||||
return true;
|
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
|
static void
|
||||||
NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
|
NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
|
||||||
{
|
{
|
||||||
@ -1805,6 +1787,43 @@ NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp)
|
|||||||
return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, true);
|
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
|
bool
|
||||||
nsNPObjWrapper::IsWrapper(JSObject *obj)
|
nsNPObjWrapper::IsWrapper(JSObject *obj)
|
||||||
{
|
{
|
||||||
@ -2123,38 +2142,24 @@ CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp)
|
NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
|
||||||
|
JS::MutableHandleValue vp)
|
||||||
{
|
{
|
||||||
NPObjectMemberPrivate *memberPrivate =
|
if (JSID_IS_SYMBOL(id)) {
|
||||||
(NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
|
JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
|
||||||
&sNPObjectMemberClass,
|
if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
|
||||||
nullptr);
|
JS::RootedObject obj(cx, JS_GetFunctionObject(
|
||||||
if (!memberPrivate) {
|
JS_NewFunction(
|
||||||
NS_ERROR("no Ambiguous Member Private data!");
|
cx, NPObjectMember_toPrimitive, 1, 0,
|
||||||
return false;
|
"Symbol.toPrimitive")));
|
||||||
|
if (!obj)
|
||||||
|
return false;
|
||||||
|
vp.setObject(*obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2275,6 +2280,36 @@ NPObjectMember_Trace(JSTracer *trc, JSObject *obj)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 type;
|
||||||
|
if (!JS::GetFirstArgumentAsTypeHint(cx, args, &type))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().set(memberPrivate->fieldValue);
|
||||||
|
if (args.rval().isObject()) {
|
||||||
|
JS::Rooted<JSObject*> objVal(cx, &args.rval().toObject());
|
||||||
|
return JS_DefaultValue(cx, objVal, type, args.rval());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool
|
bool
|
||||||
nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
|
nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
|
||||||
|
@ -740,18 +740,6 @@ workerdebuggersandbox_resolve(JSContext *cx, JS::Handle<JSObject *> obj,
|
|||||||
return JS_ResolveStandardClass(cx, obj, id, 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
|
static void
|
||||||
workerdebuggersandbox_finalize(js::FreeOp *fop, JSObject *obj)
|
workerdebuggersandbox_finalize(js::FreeOp *fop, JSObject *obj)
|
||||||
{
|
{
|
||||||
@ -775,7 +763,7 @@ const js::Class workerdebuggersandbox_class = {
|
|||||||
workerdebuggersandbox_enumerate,
|
workerdebuggersandbox_enumerate,
|
||||||
workerdebuggersandbox_resolve,
|
workerdebuggersandbox_resolve,
|
||||||
nullptr, /* mayResolve */
|
nullptr, /* mayResolve */
|
||||||
workerdebuggersandbox_convert,
|
nullptr, /* convert */
|
||||||
workerdebuggersandbox_finalize,
|
workerdebuggersandbox_finalize,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
|
@ -18,15 +18,7 @@ using namespace js;
|
|||||||
|
|
||||||
const Class SymbolObject::class_ = {
|
const Class SymbolObject::class_ = {
|
||||||
"Symbol",
|
"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*
|
||||||
@ -47,6 +39,7 @@ const JSPropertySpec SymbolObject::properties[] = {
|
|||||||
const JSFunctionSpec SymbolObject::methods[] = {
|
const JSFunctionSpec SymbolObject::methods[] = {
|
||||||
JS_FN(js_toString_str, toString, 0, 0),
|
JS_FN(js_toString_str, toString, 0, 0),
|
||||||
JS_FN(js_valueOf_str, valueOf, 0, 0),
|
JS_FN(js_valueOf_str, valueOf, 0, 0),
|
||||||
|
JS_SYM_FN(toPrimitive, toPrimitive, 1, JSPROP_READONLY),
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,14 +117,6 @@ SymbolObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
return true;
|
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
|
// ES6 rev 24 (2014 Apr 27) 19.4.2.2
|
||||||
bool
|
bool
|
||||||
SymbolObject::for_(JSContext* cx, unsigned argc, Value* vp)
|
SymbolObject::for_(JSContext* cx, unsigned argc, Value* vp)
|
||||||
@ -230,6 +215,17 @@ SymbolObject::valueOf(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
|
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*
|
JSObject*
|
||||||
js::InitSymbolClass(JSContext* cx, HandleObject obj)
|
js::InitSymbolClass(JSContext* cx, HandleObject obj)
|
||||||
{
|
{
|
||||||
|
@ -41,8 +41,6 @@ class SymbolObject : public NativeObject
|
|||||||
|
|
||||||
static bool construct(JSContext* cx, unsigned argc, Value* vp);
|
static bool construct(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
static bool convert(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue vp);
|
|
||||||
|
|
||||||
// Static methods.
|
// Static methods.
|
||||||
static bool for_(JSContext* cx, unsigned argc, Value* vp);
|
static bool for_(JSContext* cx, unsigned argc, Value* vp);
|
||||||
static bool keyFor(JSContext* cx, unsigned argc, Value* vp);
|
static bool keyFor(JSContext* cx, unsigned argc, Value* vp);
|
||||||
@ -52,6 +50,7 @@ class SymbolObject : public NativeObject
|
|||||||
static bool toString(JSContext* cx, unsigned argc, Value* vp);
|
static bool toString(JSContext* cx, unsigned argc, Value* vp);
|
||||||
static bool valueOf_impl(JSContext* cx, const CallArgs& args);
|
static bool valueOf_impl(JSContext* cx, const CallArgs& args);
|
||||||
static bool valueOf(JSContext* cx, unsigned argc, Value* vp);
|
static bool valueOf(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
static bool toPrimitive(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
static const JSPropertySpec properties[];
|
static const JSPropertySpec properties[];
|
||||||
static const JSFunctionSpec methods[];
|
static const JSFunctionSpec methods[];
|
||||||
|
@ -5224,6 +5224,8 @@ ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandle
|
|||||||
size_t length = GetLength(typeObj);
|
size_t length = GetLength(typeObj);
|
||||||
bool ok = jsidToSize(cx, idval, true, &index);
|
bool ok = jsidToSize(cx, idval, true, &index);
|
||||||
int32_t dummy;
|
int32_t dummy;
|
||||||
|
if (!ok && JSID_IS_SYMBOL(idval))
|
||||||
|
return true;
|
||||||
if (!ok && JSID_IS_STRING(idval) &&
|
if (!ok && JSID_IS_STRING(idval) &&
|
||||||
!StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
|
!StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
|
||||||
// String either isn't a number, or doesn't fit in size_t.
|
// String either isn't a number, or doesn't fit in size_t.
|
||||||
@ -5262,6 +5264,8 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandle
|
|||||||
size_t length = GetLength(typeObj);
|
size_t length = GetLength(typeObj);
|
||||||
bool ok = jsidToSize(cx, idval, true, &index);
|
bool ok = jsidToSize(cx, idval, true, &index);
|
||||||
int32_t dummy;
|
int32_t dummy;
|
||||||
|
if (!ok && JSID_IS_SYMBOL(idval))
|
||||||
|
return true;
|
||||||
if (!ok && JSID_IS_STRING(idval) &&
|
if (!ok && JSID_IS_STRING(idval) &&
|
||||||
!StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
|
!StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
|
||||||
// String either isn't a number, or doesn't fit in size_t.
|
// String either isn't a number, or doesn't fit in size_t.
|
||||||
|
@ -54,6 +54,8 @@ MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-confi
|
|||||||
MSG_DEF(JSMSG_NOT_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a function")
|
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_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_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_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_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_ARG_INDEX_OUT_OF_RANGE, 1, JSEXN_RANGEERR, "argument {0} accesses an index that is out of range")
|
||||||
|
@ -61,7 +61,6 @@ UNIFIED_SOURCES += [
|
|||||||
'testNullRoot.cpp',
|
'testNullRoot.cpp',
|
||||||
'testObjectEmulatingUndefined.cpp',
|
'testObjectEmulatingUndefined.cpp',
|
||||||
'testOOM.cpp',
|
'testOOM.cpp',
|
||||||
'testOps.cpp',
|
|
||||||
'testParseJSON.cpp',
|
'testParseJSON.cpp',
|
||||||
'testPersistentRooted.cpp',
|
'testPersistentRooted.cpp',
|
||||||
'testPreserveJitCode.cpp',
|
'testPreserveJitCode.cpp',
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
/* -*- 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)
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
|||||||
using JS::RootedObject;
|
using JS::RootedObject;
|
||||||
using JS::RootedScript;
|
using JS::RootedScript;
|
||||||
using JS::RootedString;
|
using JS::RootedString;
|
||||||
|
using namespace js;
|
||||||
|
|
||||||
// ubi::Node::zone works
|
// ubi::Node::zone works
|
||||||
BEGIN_TEST(test_ubiNodeZone)
|
BEGIN_TEST(test_ubiNodeZone)
|
||||||
|
@ -1809,7 +1809,51 @@ JS_DefaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue
|
|||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_ASSERT(obj != nullptr);
|
MOZ_ASSERT(obj != nullptr);
|
||||||
MOZ_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER);
|
MOZ_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER);
|
||||||
return ToPrimitive(cx, obj, hint, vp);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(bool)
|
JS_PUBLIC_API(bool)
|
||||||
|
@ -1886,15 +1886,32 @@ JS_StringToId(JSContext* cx, JS::HandleString s, JS::MutableHandleId idp);
|
|||||||
extern JS_PUBLIC_API(bool)
|
extern JS_PUBLIC_API(bool)
|
||||||
JS_IdToValue(JSContext* cx, jsid id, JS::MutableHandle<JS::Value> vp);
|
JS_IdToValue(JSContext* cx, jsid id, JS::MutableHandle<JS::Value> vp);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Invoke the [[DefaultValue]] hook (see ES5 8.6.2) with the provided hint on
|
* Convert obj to a primitive value. On success, store the result in vp and
|
||||||
* the specified object, computing a primitive default value for the object.
|
* return true.
|
||||||
* The hint must be JSTYPE_STRING, JSTYPE_NUMBER, or JSTYPE_VOID (no hint). On
|
*
|
||||||
* success the resulting value is stored in *vp.
|
* The hint argument must be JSTYPE_STRING, JSTYPE_NUMBER, or JSTYPE_VOID (no
|
||||||
|
* hint).
|
||||||
|
*
|
||||||
|
* Implements: ES6 7.1.1 ToPrimitive(input, [PreferredType]).
|
||||||
*/
|
*/
|
||||||
extern JS_PUBLIC_API(bool)
|
extern JS_PUBLIC_API(bool)
|
||||||
JS_DefaultValue(JSContext* cx, JS::Handle<JSObject*> obj, JSType hint,
|
JS_DefaultValue(JSContext* cx, JS::HandleObject obj, JSType hint,
|
||||||
JS::MutableHandle<JS::Value> vp);
|
JS::MutableHandleValue vp);
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 */
|
||||||
|
|
||||||
extern JS_PUBLIC_API(bool)
|
extern JS_PUBLIC_API(bool)
|
||||||
JS_PropertyStub(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
JS_PropertyStub(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||||
@ -2101,7 +2118,7 @@ struct JSFunctionSpec {
|
|||||||
JS_FNSPEC(name, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
|
JS_FNSPEC(name, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
|
||||||
#define JS_INLINABLE_FN(name,call,nargs,flags,native) \
|
#define JS_INLINABLE_FN(name,call,nargs,flags,native) \
|
||||||
JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
|
JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
|
||||||
#define JS_SYM_FN(name,call,nargs,flags) \
|
#define JS_SYM_FN(symbol,call,nargs,flags) \
|
||||||
JS_SYM_FNSPEC(symbol, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
|
JS_SYM_FNSPEC(symbol, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
|
||||||
#define JS_FNINFO(name,call,info,nargs,flags) \
|
#define JS_FNINFO(name,call,info,nargs,flags) \
|
||||||
JS_FNSPEC(name, call, info, nargs, flags, nullptr)
|
JS_FNSPEC(name, call, info, nargs, flags, nullptr)
|
||||||
@ -4370,15 +4387,16 @@ GetSymbolDescription(HandleSymbol symbol);
|
|||||||
|
|
||||||
/* Well-known symbols. */
|
/* Well-known symbols. */
|
||||||
enum class SymbolCode : uint32_t {
|
enum class SymbolCode : uint32_t {
|
||||||
iterator, // Symbol.iterator
|
iterator, // well-known symbols
|
||||||
match, // Symbol.match
|
match,
|
||||||
species, // Symbol.species
|
species,
|
||||||
|
toPrimitive,
|
||||||
InSymbolRegistry = 0xfffffffe, // created by Symbol.for() or JS::GetSymbolFor()
|
InSymbolRegistry = 0xfffffffe, // created by Symbol.for() or JS::GetSymbolFor()
|
||||||
UniqueSymbol = 0xffffffff // created by Symbol() or JS::NewSymbol()
|
UniqueSymbol = 0xffffffff // created by Symbol() or JS::NewSymbol()
|
||||||
};
|
};
|
||||||
|
|
||||||
/* For use in loops that iterate over the well-known symbols. */
|
/* For use in loops that iterate over the well-known symbols. */
|
||||||
const size_t WellKnownSymbolLimit = 3;
|
const size_t WellKnownSymbolLimit = 4;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the SymbolCode telling what sort of symbol `symbol` is.
|
* Return the SymbolCode telling what sort of symbol `symbol` is.
|
||||||
|
@ -518,15 +518,6 @@ MakeTime(double hour, double min, double sec, double ms)
|
|||||||
* end of ECMA 'support' functions
|
* 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 */
|
/* for use by date_parse */
|
||||||
|
|
||||||
static const char* const wtb[] = {
|
static const char* const wtb[] = {
|
||||||
@ -2912,6 +2903,30 @@ js::date_valueOf(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
|
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[] = {
|
static const JSFunctionSpec date_static_methods[] = {
|
||||||
JS_FN("UTC", date_UTC, 7,0),
|
JS_FN("UTC", date_UTC, 7,0),
|
||||||
JS_FN("parse", date_parse, 1,0),
|
JS_FN("parse", date_parse, 1,0),
|
||||||
@ -2975,6 +2990,7 @@ static const JSFunctionSpec date_methods[] = {
|
|||||||
#endif
|
#endif
|
||||||
JS_FN(js_toString_str, date_toString, 0,0),
|
JS_FN(js_toString_str, date_toString, 0,0),
|
||||||
JS_FN(js_valueOf_str, date_valueOf, 0,0),
|
JS_FN(js_valueOf_str, date_valueOf, 0,0),
|
||||||
|
JS_SYM_FN(toPrimitive, date_toPrimitive, 1,JSPROP_READONLY),
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3166,7 +3182,7 @@ const Class DateObject::class_ = {
|
|||||||
nullptr, /* enumerate */
|
nullptr, /* enumerate */
|
||||||
nullptr, /* resolve */
|
nullptr, /* resolve */
|
||||||
nullptr, /* mayResolve */
|
nullptr, /* mayResolve */
|
||||||
date_convert,
|
nullptr, /* convert */
|
||||||
nullptr, /* finalize */
|
nullptr, /* finalize */
|
||||||
nullptr, /* call */
|
nullptr, /* call */
|
||||||
nullptr, /* hasInstance */
|
nullptr, /* hasInstance */
|
||||||
|
@ -332,7 +332,7 @@ namespace js {
|
|||||||
nullptr, /* enumerate */ \
|
nullptr, /* enumerate */ \
|
||||||
nullptr, /* resolve */ \
|
nullptr, /* resolve */ \
|
||||||
nullptr, /* mayResolve */ \
|
nullptr, /* mayResolve */ \
|
||||||
js::proxy_Convert, \
|
nullptr, /* convert */ \
|
||||||
js::proxy_Finalize, /* finalize */ \
|
js::proxy_Finalize, /* finalize */ \
|
||||||
nullptr, /* call */ \
|
nullptr, /* call */ \
|
||||||
js::proxy_HasInstance, /* hasInstance */ \
|
js::proxy_HasInstance, /* hasInstance */ \
|
||||||
|
132
js/src/jsobj.cpp
132
js/src/jsobj.cpp
@ -2860,13 +2860,16 @@ js::HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*** ToPrimitive *************************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets |obj[id]|. If that value's not callable, returns true and stores a
|
* Gets |obj[id]|. If that value's not callable, returns true and stores an
|
||||||
* non-primitive value in *vp. If it's callable, calls it with no arguments
|
* object value in *vp. If it's callable, calls it with no arguments and |obj|
|
||||||
* and |obj| as |this|, returning the result in *vp.
|
* as |this|, returning the result in *vp.
|
||||||
*
|
*
|
||||||
* This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
|
* This is a mini-abstraction for ES6 draft rev 36 (2015 Mar 17),
|
||||||
* or steps 3-4.
|
* 7.1.1, second algorithm (OrdinaryToPrimitive), steps 5.a-c.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
|
||||||
@ -2880,6 +2883,29 @@ MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue
|
|||||||
return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
|
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
|
bool
|
||||||
JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
|
JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
|
||||||
{
|
{
|
||||||
@ -2911,10 +2937,10 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
|
|||||||
if (vp.isPrimitive())
|
if (vp.isPrimitive())
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
id = NameToId(cx->names().valueOf);
|
||||||
|
|
||||||
/* Optimize new String(...).valueOf(). */
|
/* Optimize new String(...).valueOf(). */
|
||||||
if (clasp == &StringObject::class_) {
|
if (clasp == &StringObject::class_) {
|
||||||
id = NameToId(cx->names().valueOf);
|
|
||||||
StringObject* nobj = &obj->as<StringObject>();
|
StringObject* nobj = &obj->as<StringObject>();
|
||||||
if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
|
if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
|
||||||
vp.setString(nobj->unbox());
|
vp.setString(nobj->unbox());
|
||||||
@ -2924,7 +2950,6 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
|
|||||||
|
|
||||||
/* Optimize new Number(...).valueOf(). */
|
/* Optimize new Number(...).valueOf(). */
|
||||||
if (clasp == &NumberObject::class_) {
|
if (clasp == &NumberObject::class_) {
|
||||||
id = NameToId(cx->names().valueOf);
|
|
||||||
NumberObject* nobj = &obj->as<NumberObject>();
|
NumberObject* nobj = &obj->as<NumberObject>();
|
||||||
if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) {
|
if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) {
|
||||||
vp.setNumber(nobj->unbox());
|
vp.setNumber(nobj->unbox());
|
||||||
@ -2932,7 +2957,6 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id = NameToId(cx->names().valueOf);
|
|
||||||
if (!MaybeCallMethod(cx, obj, id, vp))
|
if (!MaybeCallMethod(cx, obj, id, vp))
|
||||||
return false;
|
return false;
|
||||||
if (vp.isPrimitive())
|
if (vp.isPrimitive())
|
||||||
@ -2945,64 +2969,52 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Avoid recursive death when decompiling in ReportValueError. */
|
return ReportCantConvert(cx, JSMSG_CANT_CONVERT_TO, obj, hint);
|
||||||
RootedString str(cx);
|
}
|
||||||
if (hint == JSTYPE_STRING) {
|
|
||||||
str = JS_AtomizeAndPinString(cx, clasp->name);
|
bool
|
||||||
if (!str)
|
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;
|
return false;
|
||||||
} else {
|
|
||||||
str = nullptr;
|
// Steps 6.c-d.
|
||||||
|
if (vp.isObject())
|
||||||
|
return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_RETURNED_OBJECT, obj, preferredType);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RootedValue val(cx, ObjectValue(*obj));
|
return OrdinaryToPrimitive(cx, obj, preferredType, vp);
|
||||||
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::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::ToPrimitiveSlow(JSContext* cx, MutableHandleValue vp)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::IsDelegate(JSContext* cx, HandleObject obj, const js::Value& v, bool* result)
|
js::IsDelegate(JSContext* cx, HandleObject obj, const js::Value& v, bool* result)
|
||||||
|
@ -1008,29 +1008,24 @@ WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callabl
|
|||||||
extern bool
|
extern bool
|
||||||
UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id);
|
UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id);
|
||||||
|
|
||||||
/* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp, preferredType) */
|
/* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
|
||||||
extern bool
|
extern bool
|
||||||
ToPrimitiveSlow(JSContext* cx, MutableHandleValue vp);
|
ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
ToPrimitive(JSContext* cx, MutableHandleValue vp)
|
ToPrimitive(JSContext* cx, MutableHandleValue vp)
|
||||||
{
|
{
|
||||||
if (vp.isPrimitive())
|
if (vp.isPrimitive())
|
||||||
return true;
|
return true;
|
||||||
return ToPrimitiveSlow(cx, vp);
|
return ToPrimitiveSlow(cx, JSTYPE_VOID, vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern bool
|
|
||||||
ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp);
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
|
ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(preferredType != JSTYPE_VOID); // Use the other ToPrimitive!
|
|
||||||
if (vp.isPrimitive())
|
if (vp.isPrimitive())
|
||||||
return true;
|
return true;
|
||||||
RootedObject obj(cx, &vp.toObject());
|
return ToPrimitiveSlow(cx, preferredType, vp);
|
||||||
return ToPrimitive(cx, obj, preferredType, vp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -513,13 +513,6 @@ Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp
|
|||||||
return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
|
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);
|
JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
@ -680,13 +673,6 @@ js::proxy_WeakmapKeyDelegate(JSObject* obj)
|
|||||||
return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
|
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
|
void
|
||||||
js::proxy_Finalize(FreeOp* fop, JSObject* obj)
|
js::proxy_Finalize(FreeOp* fop, JSObject* obj)
|
||||||
{
|
{
|
||||||
|
62
js/src/tests/ecma_6/Date/toPrimitive.js
Normal file
62
js/src/tests/ecma_6/Date/toPrimitive.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// 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);
|
57
js/src/tests/ecma_6/Object/toPrimitive-callers.js
Normal file
57
js/src/tests/ecma_6/Object/toPrimitive-callers.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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);
|
101
js/src/tests/ecma_6/Object/toPrimitive.js
Normal file
101
js/src/tests/ecma_6/Object/toPrimitive.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// 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);
|
@ -38,14 +38,15 @@ var keys = [
|
|||||||
valueOf() { return "fallback"; }
|
valueOf() { return "fallback"; }
|
||||||
},
|
},
|
||||||
expected: "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) {
|
for (var {value, expected} of keys) {
|
||||||
if (expected === undefined)
|
if (expected === undefined)
|
||||||
expected = value;
|
expected = value;
|
||||||
|
@ -7,27 +7,7 @@ var symbols = [
|
|||||||
Symbol.iterator
|
Symbol.iterator
|
||||||
];
|
];
|
||||||
|
|
||||||
if (Symbol.toPrimitive in Symbol.prototype) {
|
function testSymbolConversions(sym) {
|
||||||
// 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
|
// 7.1.2 ToBoolean
|
||||||
assertEq(Boolean(sym), true);
|
assertEq(Boolean(sym), true);
|
||||||
assertEq(!sym, false);
|
assertEq(!sym, false);
|
||||||
@ -41,12 +21,11 @@ for (var sym of symbols) {
|
|||||||
// 7.1.12 ToString
|
// 7.1.12 ToString
|
||||||
assertThrowsInstanceOf(() => "" + sym, TypeError);
|
assertThrowsInstanceOf(() => "" + sym, TypeError);
|
||||||
assertThrowsInstanceOf(() => sym + "", TypeError);
|
assertThrowsInstanceOf(() => sym + "", TypeError);
|
||||||
assertThrowsInstanceOf(() => "" + [1, 2, Symbol()], TypeError);
|
assertThrowsInstanceOf(() => "" + [1, 2, sym], TypeError);
|
||||||
assertThrowsInstanceOf(() => ["simple", "thimble", Symbol()].join(), TypeError);
|
assertThrowsInstanceOf(() => ["simple", "thimble", sym].join(), TypeError);
|
||||||
|
|
||||||
// 21.1.1.1 String()
|
// 21.1.1.1 String()
|
||||||
assertEq(String(sym), sym.toString());
|
assertEq(String(sym), sym.toString());
|
||||||
assertThrowsInstanceOf(() => String(Object(sym)), TypeError);
|
|
||||||
|
|
||||||
// 21.1.1.2 new String()
|
// 21.1.1.2 new String()
|
||||||
assertThrowsInstanceOf(() => new String(sym), TypeError);
|
assertThrowsInstanceOf(() => new String(sym), TypeError);
|
||||||
@ -62,5 +41,49 @@ for (var sym of symbols) {
|
|||||||
assertEq(f.call(sym) === f.call(sym), false); // new object each time
|
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")
|
if (typeof reportCompare === "function")
|
||||||
reportCompare(0, 0);
|
reportCompare(0, 0);
|
||||||
|
39
js/src/tests/ecma_6/Symbol/toPrimitive.js
Normal file
39
js/src/tests/ecma_6/Symbol/toPrimitive.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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);
|
@ -267,8 +267,8 @@
|
|||||||
macro(iterator, iterator, "iterator") \
|
macro(iterator, iterator, "iterator") \
|
||||||
macro(match, match, "match") \
|
macro(match, match, "match") \
|
||||||
macro(species, species, "species") \
|
macro(species, species, "species") \
|
||||||
|
macro(toPrimitive, toPrimitive, "toPrimitive") \
|
||||||
/* Same goes for the descriptions of the well-known symbols. */ \
|
/* 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_hasInstance, Symbol_hasInstance, "Symbol.hasInstance") \
|
||||||
macro(Symbol_isConcatSpreadable, Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") \
|
macro(Symbol_isConcatSpreadable, Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") \
|
||||||
macro(Symbol_iterator, Symbol_iterator, "Symbol.iterator") \
|
macro(Symbol_iterator, Symbol_iterator, "Symbol.iterator") \
|
||||||
|
@ -444,6 +444,7 @@ struct WellKnownSymbols
|
|||||||
js::ImmutableSymbolPtr iterator;
|
js::ImmutableSymbolPtr iterator;
|
||||||
js::ImmutableSymbolPtr match;
|
js::ImmutableSymbolPtr match;
|
||||||
js::ImmutableSymbolPtr species;
|
js::ImmutableSymbolPtr species;
|
||||||
|
js::ImmutableSymbolPtr toPrimitive;
|
||||||
|
|
||||||
const ImmutableSymbolPtr& get(size_t u) const {
|
const ImmutableSymbolPtr& get(size_t u) const {
|
||||||
MOZ_ASSERT(u < JS::WellKnownSymbolLimit);
|
MOZ_ASSERT(u < JS::WellKnownSymbolLimit);
|
||||||
|
@ -29,11 +29,11 @@ namespace js {
|
|||||||
*
|
*
|
||||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||||
*/
|
*/
|
||||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 309;
|
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 310;
|
||||||
static const uint32_t XDR_BYTECODE_VERSION =
|
static const uint32_t XDR_BYTECODE_VERSION =
|
||||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||||
|
|
||||||
static_assert(JSErr_Limit == 413,
|
static_assert(JSErr_Limit == 415,
|
||||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||||
"removed MSG_DEFs from js.msg, you should increment "
|
"removed MSG_DEFs from js.msg, you should increment "
|
||||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||||
|
@ -440,17 +440,6 @@ sandbox_moved(JSObject* obj, const JSObject* old)
|
|||||||
static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
|
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
|
static bool
|
||||||
writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
|
||||||
JS::MutableHandleValue vp, JS::ObjectOpResult& result)
|
JS::MutableHandleValue vp, JS::ObjectOpResult& result)
|
||||||
@ -569,7 +558,8 @@ static const js::Class SandboxClass = {
|
|||||||
nullptr, nullptr, nullptr, nullptr,
|
nullptr, nullptr, nullptr, nullptr,
|
||||||
sandbox_enumerate, sandbox_resolve,
|
sandbox_enumerate, sandbox_resolve,
|
||||||
nullptr, /* mayResolve */
|
nullptr, /* mayResolve */
|
||||||
sandbox_convert, sandbox_finalize,
|
nullptr, /* convert */
|
||||||
|
sandbox_finalize,
|
||||||
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
|
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
|
||||||
JS_NULL_CLASS_SPEC,
|
JS_NULL_CLASS_SPEC,
|
||||||
{
|
{
|
||||||
@ -590,7 +580,8 @@ static const js::Class SandboxWriteToProtoClass = {
|
|||||||
sandbox_addProperty, nullptr, nullptr, nullptr,
|
sandbox_addProperty, nullptr, nullptr, nullptr,
|
||||||
sandbox_enumerate, sandbox_resolve,
|
sandbox_enumerate, sandbox_resolve,
|
||||||
nullptr, /* mayResolve */
|
nullptr, /* mayResolve */
|
||||||
sandbox_convert, sandbox_finalize,
|
nullptr, /* convert */
|
||||||
|
sandbox_finalize,
|
||||||
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
|
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
|
||||||
JS_NULL_CLASS_SPEC,
|
JS_NULL_CLASS_SPEC,
|
||||||
{
|
{
|
||||||
|
@ -776,7 +776,6 @@ XPCWrappedNative::Init(const XPCNativeScriptableCreateInfo* sci)
|
|||||||
jsclazz->name &&
|
jsclazz->name &&
|
||||||
jsclazz->flags &&
|
jsclazz->flags &&
|
||||||
jsclazz->resolve &&
|
jsclazz->resolve &&
|
||||||
jsclazz->convert &&
|
|
||||||
jsclazz->finalize, "bad class");
|
jsclazz->finalize, "bad class");
|
||||||
|
|
||||||
// XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
|
// XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
|
||||||
|
@ -96,6 +96,44 @@ XPC_WN_Shared_ToSource(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
return true;
|
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
|
// A "double wrapped object" is a user JSObject that has been wrapped as a
|
||||||
@ -233,15 +271,17 @@ DefinePropertyIfFound(XPCCallContext& ccx,
|
|||||||
{
|
{
|
||||||
call = XPC_WN_Shared_ToString;
|
call = XPC_WN_Shared_ToString;
|
||||||
name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING);
|
name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING);
|
||||||
id = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING);
|
|
||||||
} else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) {
|
} else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) {
|
||||||
call = XPC_WN_Shared_ToSource;
|
call = XPC_WN_Shared_ToSource;
|
||||||
name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE);
|
name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE);
|
||||||
id = rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE);
|
} else if (id == SYMBOL_TO_JSID(
|
||||||
}
|
JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive)))
|
||||||
|
{
|
||||||
else
|
call = XPC_WN_Shared_toPrimitive;
|
||||||
|
name = "[Symbol.toPrimitive]";
|
||||||
|
} else {
|
||||||
call = nullptr;
|
call = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (call) {
|
if (call) {
|
||||||
RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name));
|
RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name));
|
||||||
@ -450,63 +490,6 @@ XPC_WN_CannotModifySetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
|
|||||||
return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
|
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
|
static bool
|
||||||
XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj)
|
XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj)
|
||||||
{
|
{
|
||||||
@ -652,7 +635,7 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
|
|||||||
XPC_WN_Shared_Enumerate, // enumerate
|
XPC_WN_Shared_Enumerate, // enumerate
|
||||||
XPC_WN_NoHelper_Resolve, // resolve
|
XPC_WN_NoHelper_Resolve, // resolve
|
||||||
nullptr, // mayResolve
|
nullptr, // mayResolve
|
||||||
XPC_WN_Shared_Convert, // convert
|
nullptr, // convert
|
||||||
XPC_WN_NoHelper_Finalize, // finalize
|
XPC_WN_NoHelper_Finalize, // finalize
|
||||||
|
|
||||||
/* Optionally non-null members start here. */
|
/* Optionally non-null members start here. */
|
||||||
@ -1046,8 +1029,6 @@ XPCNativeScriptableShared::PopulateJSClass()
|
|||||||
// We have to figure out resolve strategy at call time
|
// We have to figure out resolve strategy at call time
|
||||||
mJSClass.base.resolve = XPC_WN_Helper_Resolve;
|
mJSClass.base.resolve = XPC_WN_Helper_Resolve;
|
||||||
|
|
||||||
mJSClass.base.convert = XPC_WN_Shared_Convert;
|
|
||||||
|
|
||||||
if (mFlags.WantFinalize())
|
if (mFlags.WantFinalize())
|
||||||
mJSClass.base.finalize = XPC_WN_Helper_Finalize;
|
mJSClass.base.finalize = XPC_WN_Helper_Finalize;
|
||||||
else
|
else
|
||||||
@ -1521,7 +1502,7 @@ const js::Class XPC_WN_Tearoff_JSClass = {
|
|||||||
XPC_WN_TearOff_Enumerate, // enumerate;
|
XPC_WN_TearOff_Enumerate, // enumerate;
|
||||||
XPC_WN_TearOff_Resolve, // resolve;
|
XPC_WN_TearOff_Resolve, // resolve;
|
||||||
nullptr, // mayResolve;
|
nullptr, // mayResolve;
|
||||||
XPC_WN_Shared_Convert, // convert;
|
nullptr, // convert;
|
||||||
XPC_WN_TearOff_Finalize, // finalize;
|
XPC_WN_TearOff_Finalize, // finalize;
|
||||||
|
|
||||||
/* Optionally non-null members start here. */
|
/* Optionally non-null members start here. */
|
||||||
|
@ -40,8 +40,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1042436
|
|||||||
Cu.evalInSandbox('contentObjWithGetter.getterProp; contentObjWithGetter.valueProp; contentObjWithGetter.getterProp;',
|
Cu.evalInSandbox('contentObjWithGetter.getterProp; contentObjWithGetter.valueProp; contentObjWithGetter.getterProp;',
|
||||||
chromeSb, "1.7", "http://phony.example.com/file.js", 99);
|
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 "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 "getterProp" \(reason: property has accessor/, sourceName: /phony/, lineNumber: 99, isWarning: true } ],
|
||||||
SimpleTest.finish.bind(SimpleTest));
|
SimpleTest.finish.bind(SimpleTest));
|
||||||
|
|
||||||
]]>
|
]]>
|
||||||
|
@ -33,7 +33,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1065185
|
|||||||
endMonitor();
|
endMonitor();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
doMonitor([/access to property a/i]);
|
doMonitor([/access to property "a"/i]);
|
||||||
window[0].wrappedJSObject.probe = { a: 2 };
|
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");
|
||||||
is(window[0].eval('probe.a'), undefined, "Non-exposed prop undefined again");
|
is(window[0].eval('probe.a'), undefined, "Non-exposed prop undefined again");
|
||||||
|
@ -159,7 +159,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
|||||||
"setUTCMilliseconds", "toUTCString", "toLocaleFormat", "toLocaleString",
|
"setUTCMilliseconds", "toUTCString", "toLocaleFormat", "toLocaleString",
|
||||||
"toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
|
"toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
|
||||||
"toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
|
"toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
|
||||||
"toGMTString"];
|
"toGMTString", Symbol.toPrimitive];
|
||||||
gPrototypeProperties['Object'] =
|
gPrototypeProperties['Object'] =
|
||||||
["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
|
["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
|
||||||
"unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
|
"unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
|
||||||
|
@ -196,14 +196,20 @@ ReportWrapperDenial(JSContext* cx, HandleId id, WrapperDenialType type, const ch
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
nsAutoJSString propertyName;
|
nsAutoJSString propertyName;
|
||||||
if (!propertyName.init(cx, id))
|
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))
|
||||||
return false;
|
return false;
|
||||||
AutoFilename filename;
|
AutoFilename filename;
|
||||||
unsigned line = 0, column = 0;
|
unsigned line = 0, column = 0;
|
||||||
DescribeScriptedCaller(cx, &filename, &line, &column);
|
DescribeScriptedCaller(cx, &filename, &line, &column);
|
||||||
|
|
||||||
// Warn to the terminal for the logs.
|
// 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,
|
NS_LossyConvertUTF16toASCII(propertyName).get(), reason,
|
||||||
filename.get(), line, column).get());
|
filename.get(), line, column).get());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user