Bug 1187234 - Throw a TypeError when Array.isArray is passed a revoked proxy. r=efaust

This commit is contained in:
Jeff Walden 2015-08-23 01:10:24 -07:00
parent 905e9955b1
commit 5205e57e71
23 changed files with 294 additions and 63 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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
{

View File

@ -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,

View File

@ -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
};
/*

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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))

View File

@ -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,

View File

@ -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
{

View File

@ -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
{

View File

@ -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;

View File

@ -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
{

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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*

View File

@ -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;

View File

@ -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

View File

@ -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);