mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1187234 - Throw a TypeError when Array.isArray is passed a revoked proxy. r=efaust
This commit is contained in:
parent
905e9955b1
commit
5205e57e71
@ -91,6 +91,9 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
|
||||
bool* result) {
|
||||
return Answer::RecvObjectClassIs(ObjectId::deserialize(objId), classValue, result);
|
||||
}
|
||||
bool RecvIsArray(const uint64_t& objId, ReturnStatus* rs, uint32_t* answer) {
|
||||
return Answer::RecvIsArray(ObjectId::deserialize(objId), rs, answer);
|
||||
}
|
||||
bool RecvClassName(const uint64_t& objId, nsCString* result) {
|
||||
return Answer::RecvClassName(ObjectId::deserialize(objId), result);
|
||||
}
|
||||
@ -179,6 +182,10 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
|
||||
bool* result) {
|
||||
return Base::SendObjectClassIs(objId.serialize(), classValue, result);
|
||||
}
|
||||
bool SendIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* answer)
|
||||
{
|
||||
return Base::SendIsArray(objId.serialize(), rs, answer);
|
||||
}
|
||||
bool SendClassName(const ObjectId& objId, nsCString* result) {
|
||||
return Base::SendClassName(objId.serialize(), result);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ both:
|
||||
prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
|
||||
prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
|
||||
prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
|
||||
prio(high) sync IsArray(uint64_t objId) returns (ReturnStatus rs, uint32_t ans);
|
||||
prio(high) sync ClassName(uint64_t objId) returns (nsCString name);
|
||||
prio(high) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
|
||||
prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "js/Class.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
using namespace JS;
|
||||
@ -521,6 +522,32 @@ WrapperAnswer::RecvObjectClassIs(const ObjectId& objId, const uint32_t& classVal
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
|
||||
uint32_t* ans)
|
||||
{
|
||||
*ans = uint32_t(IsArrayAnswer::NotArray);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
|
||||
return false;
|
||||
jsapi.TakeOwnershipOfErrorReporting();
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
|
||||
LOG("%s.isArray()", ReceiverObj(objId));
|
||||
|
||||
IsArrayAnswer answer;
|
||||
if (!JS::IsArray(cx, obj, &answer))
|
||||
return fail(jsapi, rs);
|
||||
|
||||
*ans = uint32_t(answer);
|
||||
return ok(rs);
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name)
|
||||
{
|
||||
|
@ -53,6 +53,7 @@ class WrapperAnswer : public virtual JavaScriptShared
|
||||
bool RecvHasInstance(const ObjectId& objId, const JSVariant& v, ReturnStatus* rs, bool* bp);
|
||||
bool RecvObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
|
||||
bool* result);
|
||||
bool RecvIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* ans);
|
||||
bool RecvClassName(const ObjectId& objId, nsCString* result);
|
||||
bool RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result);
|
||||
bool RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source, uint32_t* flags);
|
||||
|
@ -127,6 +127,8 @@ class CPOWProxyHandler : public BaseProxyHandler
|
||||
MutableHandleValue v, bool* bp) const override;
|
||||
virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
|
||||
JSContext* cx) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject obj,
|
||||
IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
|
||||
@ -741,6 +743,33 @@ WrapperOwner::objectClassIs(JSContext* cx, HandleObject proxy, js::ESClassValue
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy,
|
||||
IsArrayAnswer* answer) const
|
||||
{
|
||||
FORWARD(isArray, (cx, proxy, answer));
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer)
|
||||
{
|
||||
ObjectId objId = idOf(proxy);
|
||||
|
||||
uint32_t ans;
|
||||
ReturnStatus status;
|
||||
if (!SendIsArray(objId, &status, &ans))
|
||||
return ipcfail(cx);
|
||||
|
||||
LOG_STACK();
|
||||
|
||||
*answer = IsArrayAnswer(ans);
|
||||
MOZ_ASSERT(*answer == IsArrayAnswer::Array ||
|
||||
*answer == IsArrayAnswer::NotArray ||
|
||||
*answer == IsArrayAnswer::RevokedProxy);
|
||||
|
||||
return ok(cx, status);
|
||||
}
|
||||
|
||||
const char*
|
||||
CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const
|
||||
{
|
||||
|
@ -55,6 +55,7 @@ class WrapperOwner : public virtual JavaScriptShared
|
||||
JS::AutoIdVector& props);
|
||||
bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp);
|
||||
bool objectClassIs(JSContext* cx, JS::HandleObject obj, js::ESClassValue classValue);
|
||||
bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer);
|
||||
const char* className(JSContext* cx, JS::HandleObject proxy);
|
||||
bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
|
||||
|
||||
@ -140,6 +141,8 @@ class WrapperOwner : public virtual JavaScriptShared
|
||||
ReturnStatus* rs, bool* bp) = 0;
|
||||
virtual bool SendObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
|
||||
bool* result) = 0;
|
||||
virtual bool SendIsArray(const ObjectId& objId, ReturnStatus* rs,
|
||||
uint32_t* answer) = 0;
|
||||
virtual bool SendClassName(const ObjectId& objId, nsCString* result) = 0;
|
||||
virtual bool SendGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result) = 0;
|
||||
virtual bool SendRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source,
|
||||
|
@ -46,6 +46,37 @@ template <typename T>
|
||||
class AutoVectorRooter;
|
||||
typedef AutoVectorRooter<jsid> AutoIdVector;
|
||||
|
||||
// The answer to a successful query as to whether an object is an Array per
|
||||
// ES6's internal |IsArray| operation (as exposed by |Array.isArray|).
|
||||
enum class IsArrayAnswer
|
||||
{
|
||||
Array,
|
||||
NotArray,
|
||||
RevokedProxy
|
||||
};
|
||||
|
||||
// ES6 7.2.2.
|
||||
//
|
||||
// Returns false on failure, otherwise returns true and sets |*isArray|
|
||||
// indicating whether the object passes ECMAScript's IsArray test. This is the
|
||||
// same test performed by |Array.isArray|.
|
||||
//
|
||||
// This is NOT the same as asking whether |obj| is an Array or a wrapper around
|
||||
// one. If |obj| is a proxy created by |Proxy.revocable()| and has been
|
||||
// revoked, or if |obj| is a proxy whose target (at any number of hops) is a
|
||||
// revoked proxy, this method throws a TypeError and returns false.
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsArray(JSContext* cx, HandleObject obj, bool* isArray);
|
||||
|
||||
// Identical to IsArray above, but the nature of the object (if successfully
|
||||
// determined) is communicated via |*answer|. In particular this method
|
||||
// returns true and sets |*answer = IsArrayAnswer::RevokedProxy| when called on
|
||||
// a revoked proxy.
|
||||
//
|
||||
// Most users will want the overload above, not this one.
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer);
|
||||
|
||||
/*
|
||||
* Per ES6, the [[DefineOwnProperty]] internal method has three different
|
||||
* possible outcomes:
|
||||
@ -800,11 +831,7 @@ Valueify(const JSClass* c)
|
||||
enum ESClassValue {
|
||||
ESClass_Object, ESClass_Array, ESClass_Number, ESClass_String,
|
||||
ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer,
|
||||
ESClass_Date, ESClass_Set, ESClass_Map,
|
||||
|
||||
// Special snowflake for the ES6 IsArray method.
|
||||
// Please don't use it without calling that function.
|
||||
ESClass_IsArray
|
||||
ESClass_Date, ESClass_Set, ESClass_Map
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -323,6 +323,7 @@ class JS_FRIEND_API(BaseProxyHandler)
|
||||
const CallArgs& args) const;
|
||||
virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const;
|
||||
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
|
||||
@ -414,6 +415,8 @@ class JS_FRIEND_API(DirectProxyHandler) : public BaseProxyHandler
|
||||
bool* bp) const override;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
|
||||
JSContext* cx) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject proxy,
|
||||
JS::IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||
unsigned indent) const override;
|
||||
|
@ -57,8 +57,40 @@ using mozilla::IsNaN;
|
||||
using mozilla::UniquePtr;
|
||||
|
||||
using JS::AutoCheckCannotGC;
|
||||
using JS::IsArrayAnswer;
|
||||
using JS::ToUint32;
|
||||
|
||||
bool
|
||||
JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
|
||||
{
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
|
||||
*answer = IsArrayAnswer::Array;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->is<ProxyObject>())
|
||||
return Proxy::isArray(cx, obj, answer);
|
||||
|
||||
*answer = IsArrayAnswer::NotArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JS::IsArray(JSContext* cx, HandleObject obj, bool* isArray)
|
||||
{
|
||||
IsArrayAnswer answer;
|
||||
if (!IsArray(cx, obj, &answer))
|
||||
return false;
|
||||
|
||||
if (answer == IsArrayAnswer::RevokedProxy) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
|
||||
return false;
|
||||
}
|
||||
|
||||
*isArray = answer == IsArrayAnswer::Array;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp)
|
||||
{
|
||||
@ -1996,8 +2028,13 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
|
||||
// SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
|
||||
// length for boxed and unboxed arrays. Handle updates to the length of
|
||||
// non-arrays here.
|
||||
if (!IsArray(obj, cx))
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
|
||||
if (!isArray)
|
||||
return SetLengthProperty(cx, obj, newlength);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2590,7 +2627,10 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
RootedObject narr(cx);
|
||||
uint32_t length;
|
||||
if (IsArray(aobj, cx) && !ObjectMayHaveExtraIndexedProperties(aobj)) {
|
||||
bool isArray;
|
||||
if (!IsArray(cx, aobj, &isArray))
|
||||
return false;
|
||||
if (isArray && !ObjectMayHaveExtraIndexedProperties(aobj)) {
|
||||
if (!GetLengthProperty(cx, aobj, &length))
|
||||
return false;
|
||||
|
||||
@ -2615,7 +2655,10 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
|
||||
// This should be IsConcatSpreadable
|
||||
if (!IsArray(obj, cx) || ObjectMayHaveExtraIndexedProperties(obj))
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
if (!isArray || ObjectMayHaveExtraIndexedProperties(obj))
|
||||
break;
|
||||
|
||||
uint32_t argLength;
|
||||
@ -2671,7 +2714,10 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
|
||||
if (v.isObject()) {
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
// This should be IsConcatSpreadable
|
||||
if (IsArray(obj, cx)) {
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
if (isArray) {
|
||||
uint32_t alength;
|
||||
if (!GetLengthProperty(cx, obj, &alength))
|
||||
return false;
|
||||
@ -2994,7 +3040,8 @@ array_isArray(JSContext* cx, unsigned argc, Value* vp)
|
||||
bool isArray = false;
|
||||
if (args.get(0).isObject()) {
|
||||
RootedObject obj(cx, &args[0].toObject());
|
||||
isArray = IsArray(obj, cx);
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
}
|
||||
args.rval().setBoolean(isArray);
|
||||
return true;
|
||||
|
@ -819,8 +819,6 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx)
|
||||
switch (classValue) {
|
||||
case ESClass_Object: return obj->is<PlainObject>() || obj->is<UnboxedPlainObject>();
|
||||
case ESClass_Array:
|
||||
case ESClass_IsArray:
|
||||
// The difference between Array and IsArray is only relevant for proxies.
|
||||
return obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>();
|
||||
case ESClass_Number: return obj->is<NumberObject>();
|
||||
case ESClass_String: return obj->is<StringObject>();
|
||||
@ -844,16 +842,6 @@ IsObjectWithClass(const Value& v, ESClassValue classValue, JSContext* cx)
|
||||
return ObjectClassIs(obj, classValue, cx);
|
||||
}
|
||||
|
||||
// ES6 7.2.2
|
||||
inline bool
|
||||
IsArray(HandleObject obj, JSContext* cx)
|
||||
{
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
|
||||
return ObjectClassIs(obj, ESClass_IsArray, cx);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp)
|
||||
{
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
|
||||
#include "jsarray.h"
|
||||
#include "jsatom.h"
|
||||
@ -531,14 +532,13 @@ Str(JSContext* cx, const Value& v, StringifyContext* scx)
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
|
||||
scx->depth++;
|
||||
bool ok;
|
||||
if (IsArray(obj, cx))
|
||||
ok = JA(cx, obj, scx);
|
||||
else
|
||||
ok = JO(cx, obj, scx);
|
||||
scx->depth--;
|
||||
auto dec = mozilla::MakeScopeExit([&] { scx->depth--; });
|
||||
|
||||
return ok;
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
|
||||
return isArray ? JA(cx, obj, scx) : JO(cx, obj, scx);
|
||||
}
|
||||
|
||||
/* ES5 15.12.3. */
|
||||
@ -552,9 +552,12 @@ js::Stringify(JSContext* cx, MutableHandleValue vp, JSObject* replacer_, Value s
|
||||
/* Step 4. */
|
||||
AutoIdVector propertyList(cx);
|
||||
if (replacer) {
|
||||
bool isArray;
|
||||
if (replacer->isCallable()) {
|
||||
/* Step 4a(i): use replacer to transform values. */
|
||||
} else if (IsArray(replacer, cx)) {
|
||||
} else if (!IsArray(cx, replacer, &isArray)) {
|
||||
return false;
|
||||
} else if (isArray) {
|
||||
/*
|
||||
* Step 4b: The spec algorithm is unhelpfully vague about the exact
|
||||
* steps taken when the replacer is an array, regarding the exact
|
||||
@ -721,7 +724,11 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut
|
||||
if (val.isObject()) {
|
||||
RootedObject obj(cx, &val.toObject());
|
||||
|
||||
if (IsArray(obj, cx)) {
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
|
||||
if (isArray) {
|
||||
/* Step 2a(ii). */
|
||||
uint32_t length;
|
||||
if (!GetLengthProperty(cx, obj, &length))
|
||||
|
@ -206,6 +206,8 @@ class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrap
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
|
||||
AutoIdVector& props) const override;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject obj,
|
||||
JS::IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
|
||||
virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const override;
|
||||
@ -247,6 +249,7 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
|
||||
const CallArgs& args) const override;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
|
||||
JSContext* cx) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
||||
virtual bool defaultValue(JSContext* cx, HandleObject wrapper, JSType hint,
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
using namespace js;
|
||||
|
||||
using JS::IsArrayAnswer;
|
||||
|
||||
bool
|
||||
BaseProxyHandler::enter(JSContext* cx, HandleObject wrapper, HandleId id, Action act,
|
||||
bool* bp) const
|
||||
@ -322,6 +324,13 @@ BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSC
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BaseProxyHandler::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer) const
|
||||
{
|
||||
*answer = IsArrayAnswer::NotArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BaseProxyHandler::trace(JSTracer* trc, JSObject* proxy) const
|
||||
{
|
||||
|
@ -120,6 +120,13 @@ DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSCont
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DeadObjectProxy::isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswer* answer) const
|
||||
{
|
||||
ReportDead(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char*
|
||||
DeadObjectProxy::className(JSContext* cx, HandleObject wrapper) const
|
||||
{
|
||||
|
@ -45,6 +45,7 @@ class DeadObjectProxy : public BaseProxyHandler
|
||||
bool* bp) const override;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
|
||||
JSContext* cx) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
|
||||
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
|
||||
|
@ -161,6 +161,13 @@ DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
|
||||
return ObjectClassIs(target, classValue, cx);
|
||||
}
|
||||
|
||||
bool
|
||||
DirectProxyHandler::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
return IsArray(cx, target, answer);
|
||||
}
|
||||
|
||||
const char*
|
||||
DirectProxyHandler::className(JSContext* cx, HandleObject proxy) const
|
||||
{
|
||||
|
@ -157,6 +157,14 @@ OpaqueCrossCompartmentWrapper::objectClassIs(HandleObject obj, ESClassValue clas
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
OpaqueCrossCompartmentWrapper::isArray(JSContext* cx, HandleObject obj,
|
||||
JS::IsArrayAnswer* answer) const
|
||||
{
|
||||
*answer = JS::IsArrayAnswer::NotArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char*
|
||||
OpaqueCrossCompartmentWrapper::className(JSContext* cx,
|
||||
HandleObject proxy) const
|
||||
|
@ -461,6 +461,12 @@ Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext* cx)
|
||||
return proxy->as<ProxyObject>().handler()->objectClassIs(proxy, classValue, cx);
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer)
|
||||
{
|
||||
return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
|
||||
}
|
||||
|
||||
const char*
|
||||
Proxy::className(JSContext* cx, HandleObject proxy)
|
||||
{
|
||||
|
@ -57,6 +57,7 @@ class Proxy
|
||||
const CallArgs& args);
|
||||
static bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp);
|
||||
static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx);
|
||||
static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
|
||||
static const char* className(JSContext* cx, HandleObject proxy);
|
||||
static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent);
|
||||
static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using JS::IsArrayAnswer;
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
static inline bool
|
||||
@ -1103,20 +1105,19 @@ bool
|
||||
ScriptedDirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
|
||||
JSContext* cx) const
|
||||
{
|
||||
// Special case IsArray. In every other instance ES wants to have exactly
|
||||
// one object type and not a proxy around it, so return false.
|
||||
if (classValue != ESClass_IsArray)
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// In ES6 IsArray is supposed to poke at the Proxy target, instead we do this here.
|
||||
// The reason for this is that we have proxies for which looking at the target might
|
||||
// be impossible. So instead we use our little objectClassIs function that just works
|
||||
// already across different wrappers.
|
||||
bool
|
||||
ScriptedDirectProxyHandler::isArray(JSContext* cx, HandleObject proxy,
|
||||
IsArrayAnswer* answer) const
|
||||
{
|
||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||
if (!target)
|
||||
return false;
|
||||
if (target)
|
||||
return JS::IsArray(cx, target, answer);
|
||||
|
||||
return IsArray(target, cx);
|
||||
*answer = IsArrayAnswer::RevokedProxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char*
|
||||
|
@ -72,6 +72,8 @@ class ScriptedDirectProxyHandler : public BaseProxyHandler {
|
||||
bool* bp) const override;
|
||||
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
|
||||
JSContext* cx) const override;
|
||||
virtual bool isArray(JSContext* cx, HandleObject proxy,
|
||||
JS::IsArrayAnswer* answer) const override;
|
||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||
unsigned indent) const override;
|
||||
|
@ -92,6 +92,15 @@ SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue,
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswer* answer) const
|
||||
{
|
||||
// This should ReportUnwrapDenied(cx), but bug 849730 disagrees. :-(
|
||||
*answer = JS::IsArrayAnswer::NotArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
bool
|
||||
SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj, RegExpGuard* g) const
|
||||
|
@ -1,30 +1,67 @@
|
||||
assertEq(Array.isArray([]), true);
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/ */
|
||||
|
||||
var proxy = new Proxy([], {});
|
||||
assertEq(Array.isArray(proxy), true);
|
||||
var global = this;
|
||||
var otherGlobal = newGlobal();
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
proxy = new Proxy(proxy, {});
|
||||
var thisGlobal = () => global;
|
||||
var alternateGlobals = (function(i) {
|
||||
return () => (i++ % 2) === 0 ? global : otherGlobal;
|
||||
})(0);
|
||||
|
||||
function performTests(pickGlobal)
|
||||
{
|
||||
// Base case.
|
||||
assertEq(Array.isArray([]), true);
|
||||
|
||||
// Simple case: proxy to an array.
|
||||
var proxy = new (pickGlobal()).Proxy([], {});
|
||||
assertEq(Array.isArray(proxy), true);
|
||||
|
||||
// Recursive proxy ultimately terminating in an array.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
proxy = new (pickGlobal()).Proxy(proxy, {});
|
||||
assertEq(Array.isArray(proxy), true);
|
||||
}
|
||||
|
||||
// Revocable proxy to an array.
|
||||
var revocable = (pickGlobal()).Proxy.revocable([], {});
|
||||
proxy = revocable.proxy;
|
||||
assertEq(Array.isArray(proxy), true);
|
||||
|
||||
// Recursive proxy ultimately terminating in a revocable proxy to an array.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
proxy = new (pickGlobal()).Proxy(proxy, {});
|
||||
assertEq(Array.isArray(proxy), true);
|
||||
}
|
||||
|
||||
// Revoked proxy to (formerly) an array.
|
||||
revocable.revoke();
|
||||
assertThrowsInstanceOf(() => Array.isArray(revocable.proxy), TypeError);
|
||||
|
||||
// Recursive proxy ultimately terminating in a revoked proxy to an array.
|
||||
assertThrowsInstanceOf(() => Array.isArray(proxy), TypeError);
|
||||
|
||||
}
|
||||
|
||||
var revocable = Proxy.revocable([], {});
|
||||
proxy = revocable.proxy;
|
||||
assertEq(Array.isArray(proxy), true);
|
||||
performTests(thisGlobal);
|
||||
performTests(alternateGlobals);
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
proxy = new Proxy(proxy, {});
|
||||
assertEq(Array.isArray(proxy), true);
|
||||
function crossGlobalTest()
|
||||
{
|
||||
var array = new otherGlobal.Array();
|
||||
|
||||
// Array from another global.
|
||||
assertEq(Array.isArray(array), true);
|
||||
|
||||
// Proxy to an array from another global.
|
||||
assertEq(Array.isArray(new Proxy(array, {})), true);
|
||||
|
||||
// Other-global proxy to an array from that selfsame global.
|
||||
assertEq(Array.isArray(new otherGlobal.Proxy(array, {})), true);
|
||||
}
|
||||
|
||||
revocable.revoke();
|
||||
assertEq(Array.isArray(revocable.proxy), false);
|
||||
assertEq(Array.isArray(proxy), false);
|
||||
crossGlobalTest();
|
||||
|
||||
var global = newGlobal();
|
||||
var array = global.Array();
|
||||
assertEq(Array.isArray(array), true);
|
||||
assertEq(Array.isArray(new Proxy(array, {})), true);
|
||||
assertEq(Array.isArray(new global.Proxy(array, {})), true);
|
||||
|
||||
reportCompare(true, true);
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
Loading…
Reference in New Issue
Block a user