Implement iterate trap for proxy handlers (568413, r=brendan).

This commit is contained in:
Andreas Gal 2010-05-27 12:03:25 -07:00
parent e85c97aafb
commit 22db0647cc
8 changed files with 507 additions and 275 deletions

View File

@ -3900,7 +3900,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj)
CHECK_REQUEST(cx);
JSIdArray *ida;
if (!EnumerateOwnProperties(cx, obj, &ida))
if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &ida))
return false;
for (size_t n = 0; n < size_t(ida->length); ++n)
JS_ASSERT(js_CheckForStringIndex(ida->vector[n]) == ida->vector[n]);

View File

@ -191,11 +191,12 @@ const char *const js_common_atom_names[] = {
"delete", /* deleteAtom */
"getOwnPropertyNames", /* getOwnPropertyNames */
"enumerate", /* enumerateAtom */
"fix",
"fix", /* fixAtom */
"has", /* hasAtom */
"hasOwn", /* hasOwnAtom */
"enumerateOwn" /* enumerateOwnAtom */
"enumerateOwn", /* enumerateOwnAtom */
"iterate" /* iterateAtom */
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==

View File

@ -308,6 +308,7 @@ struct JSAtomState {
JSAtom *hasAtom;
JSAtom *hasOwnAtom;
JSAtom *enumerateOwnAtom;
JSAtom *iterateAtom;
/* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */
struct {

View File

@ -233,15 +233,15 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint
return true;
}
static bool
MakeNativeIterator(JSContext *cx, uintN flags, uint32 *sarray, uint32 slength, uint32 key,
jsval *parray, uint32 plength, NativeIterator **nip)
NativeIterator *
NativeIterator::allocate(JSContext *cx, uintN flags, uint32 *sarray, uint32 slength, uint32 key,
jsval *parray, uint32 plength)
{
NativeIterator *ni = (NativeIterator *)
cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsval) + slength * sizeof(uint32));
if (!ni) {
JS_ReportOutOfMemory(cx);
return false;
return NULL;
}
ni->props_array = ni->props_cursor = (jsval *) (ni + 1);
ni->props_end = ni->props_array + plength;
@ -253,19 +253,15 @@ MakeNativeIterator(JSContext *cx, uintN flags, uint32 *sarray, uint32 slength, u
ni->flags = flags;
if (slength)
memcpy(ni->shapes_array, sarray, slength * sizeof(uint32));
*nip = ni;
return true;
return ni;
}
static bool
InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength,
uint32 key, NativeIterator **nip)
static NativeIterator *
Snapshot(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength, uint32 key)
{
HashSet<jsid> ht(cx);
if (!(flags & JSITER_OWNONLY) && !ht.init(32))
return false;
return NULL;
AutoValueVector props(cx);
@ -276,45 +272,45 @@ InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, ui
pobj->map->ops->enumerate == js_Enumerate &&
!(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
if (!clasp->enumerate(cx, pobj))
return false;
return NULL;
if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
return false;
return NULL;
} else if (pobj->isDenseArray()) {
if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props))
return false;
return NULL;
} else {
if (pobj->isProxy()) {
JSIdArray *ida;
if (flags & JSITER_OWNONLY) {
if (!JSProxy::enumerateOwn(cx, pobj, &ida))
return false;
return NULL;
} else {
if (!JSProxy::enumerate(cx, pobj, &ida))
return false;
return NULL;
}
AutoIdArray idar(cx, ida);
for (size_t n = 0; n < size_t(ida->length); ++n) {
if (!Enumerate(cx, obj, pobj, ida->vector[n], true, flags, ht, props))
return false;
return NULL;
}
/* Proxy objects enumerate the prototype on their own, so we are done here. */
break;
}
jsval state;
if (!pobj->enumerate(cx, JSENUMERATE_INIT, &state, NULL))
return false;
return NULL;
if (state == JSVAL_NATIVE_ENUMERATE_COOKIE) {
if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
return false;
return NULL;
} else {
while (true) {
jsid id;
if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
return false;
return NULL;
if (state == JSVAL_NULL)
break;
if (!Enumerate(cx, obj, pobj, id, true, flags, ht, props))
return false;
return NULL;
}
}
}
@ -325,7 +321,7 @@ InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, ui
pobj = pobj->getProto();
}
return MakeNativeIterator(cx, flags, sarray, slength, key, props.begin(), props.length(), nip);
return NativeIterator::allocate(cx, flags, sarray, slength, key, props.begin(), props.length());
}
bool
@ -335,7 +331,7 @@ NativeIteratorToJSIdArray(JSContext *cx, NativeIterator *ni, JSIdArray **idap)
JS_ASSERT(sizeof(NativeIterator) > sizeof(JSIdArray));
JS_ASSERT(ni->props_array == (jsid *) (ni + 1));
size_t length = size_t(ni->props_end - ni->props_array);
JSIdArray *ida = (JSIdArray *) (uintptr_t(ni->props_array) - (sizeof(JSIdArray) - sizeof(jsid)));
JSIdArray *ida = (JSIdArray *) uintptr_t(ni->props_array) - (sizeof(JSIdArray) - sizeof(jsid));
ida->self = ni;
ida->length = length;
JS_ASSERT(&ida->vector[0] == &ni->props_array[0]);
@ -344,28 +340,10 @@ NativeIteratorToJSIdArray(JSContext *cx, NativeIterator *ni, JSIdArray **idap)
}
bool
EnumerateOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap)
GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, JSIdArray **idap)
{
NativeIterator *ni;
if (!InitNativeIterator(cx, obj, JSITER_OWNONLY, NULL, 0, true, &ni))
return false;
return NativeIteratorToJSIdArray(cx, ni, idap);
}
bool
EnumerateAllProperties(JSContext *cx, JSObject *obj, JSIdArray **idap)
{
NativeIterator *ni;
if (!InitNativeIterator(cx, obj, 0, NULL, 0, true, &ni))
return false;
return NativeIteratorToJSIdArray(cx, ni, idap);
}
bool
GetOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap)
{
NativeIterator *ni;
if (!InitNativeIterator(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, NULL, 0, true, &ni))
NativeIterator *ni = Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), NULL, 0, true);
if (!ni)
return false;
return NativeIteratorToJSIdArray(cx, ni, idap);
}
@ -413,16 +391,40 @@ Compare(T *a, T *b, size_t c)
return true;
}
static inline bool
static JSObject *
NewIteratorObject(JSContext *cx, uintN flags)
{
return !(flags & JSITER_ENUMERATE)
? NewObject(cx, &js_IteratorClass.base, NULL, NULL)
: NewObjectWithGivenProto(cx, &js_IteratorClass.base, NULL, NULL);
}
bool
JSIdArrayToIterator(JSContext *cx, uintN flags, JSIdArray *ida, jsval *vp)
{
JSObject *iterobj = NewIteratorObject(cx, flags);
if (!iterobj)
return false;
*vp = OBJECT_TO_JSVAL(iterobj);
NativeIterator *ni = NativeIterator::allocate(cx, flags, NULL, 0, 0,
ida->vector, ida->length);
if (!ni)
return false;
iterobj->setNativeIterator(ni);
return true;
}
bool
GetIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
{
uint32 hash;
JSObject **hp;
NativeIterator *ni;
Vector<uint32, 8> shapes(cx);
uint32 key = 0;
bool escaping = !(flags & JSITER_ENUMERATE);
bool keysOnly = (flags == JSITER_ENUMERATE);
if (obj) {
@ -452,7 +454,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash];
JSObject *iterobj = *hp;
if (iterobj) {
ni = iterobj->getNativeIterator();
NativeIterator *ni = iterobj->getNativeIterator();
if (ni->shapes_key == key &&
ni->shapes_length == shapes.length() &&
Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
@ -464,27 +466,26 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
}
miss:
if (!obj->isProxy()) {
if (!GetCustomIterator(cx, obj, flags, vp))
return false;
if (*vp != JSVAL_VOID)
return true;
}
if (obj->isProxy())
return JSProxy::iterate(cx, obj, flags, vp);
if (!GetCustomIterator(cx, obj, flags, vp))
return false;
if (*vp != JSVAL_VOID)
return true;
}
JSObject *iterobj = escaping
? NewObject(cx, &js_IteratorClass.base, NULL, NULL)
: NewObjectWithGivenProto(cx, &js_IteratorClass.base, NULL, NULL);
JSObject *iterobj = NewIteratorObject(cx, flags);
if (!iterobj)
return false;
/* Store in *vp to protect it from GC (callers must root vp). */
*vp = OBJECT_TO_JSVAL(iterobj);
if (!InitNativeIterator(cx, obj, flags, shapes.begin(), shapes.length(), key, &ni))
NativeIterator *ni = Snapshot(cx, obj, flags, shapes.begin(), shapes.length(), key);
if (!ni)
return false;
iterobj->setNativeIterator(ni);
iterobj->setNativeIterator(ni);
return true;
}
@ -629,7 +630,7 @@ js_CloseIterator(JSContext *cx, jsval v)
/* Cache the iterator object if possible. */
NativeIterator *ni = obj->getNativeIterator();
if (ni->shapes_length) {
uint32 hash = ni->shapes_key % JS_ARRAY_LENGTH(JS_THREAD_DATA(cx)->cachedNativeIterators);
uint32 hash = ni->shapes_key % NATIVE_ITER_CACHE_SIZE;
JSObject **hp = &JS_THREAD_DATA(cx)->cachedNativeIterators[hash];
ni->props_cursor = ni->props_array;
ni->next = *hp;

View File

@ -70,6 +70,10 @@ struct NativeIterator {
uintN flags;
JSObject *next;
static NativeIterator *allocate(JSContext *cx, uintN flags,
uint32 *sarray, uint32 slength, uint32 key,
jsval *parray, uint32 plength);
void mark(JSTracer *trc);
};
@ -81,13 +85,13 @@ struct NativeIterator {
static const jsval JSVAL_NATIVE_ENUMERATE_COOKIE = SPECIAL_TO_JSVAL(0x220576);
bool
EnumerateOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap);
GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, JSIdArray **idap);
bool
EnumerateAllProperties(JSContext *cx, JSObject *obj, JSIdArray **idap);
GetIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp);
bool
GetOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap);
JSIdArrayToIterator(JSContext *cx, uintN flags, JSIdArray *ida, jsval *vp);
/*
* Convert the value stored in *vp to its iteration object. The flags should

View File

@ -170,6 +170,16 @@ JSProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap)
return true;
}
bool
JSProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
{
JSIdArray *ida;
if (!enumerate(cx, proxy, &ida))
return false;
AutoIdArray idar(cx, ida);
return JSIdArrayToIterator(cx, flags, ida, vp);
}
void
JSProxyHandler::finalize(JSContext *cx, JSObject *proxy)
{
@ -189,20 +199,23 @@ JSNoopProxyHandler::~JSNoopProxyHandler()
}
bool
JSNoopProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
JSNoopProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc)
{
JSObject *wobj = wrappedObject(proxy);
return JS_GetPropertyDescriptorById(cx, wobj, id, JSRESOLVE_QUALIFIED, desc);
}
bool
JSNoopProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
JSNoopProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc)
{
return JS_GetPropertyDescriptorById(cx, wrappedObject(proxy), id, JSRESOLVE_QUALIFIED, desc);
}
bool
JSNoopProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
JSNoopProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc)
{
return JS_DefinePropertyById(cx, wrappedObject(proxy), id, desc->value,
desc->getter, desc->setter, desc->attrs);
@ -211,7 +224,7 @@ JSNoopProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPr
bool
JSNoopProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
return GetOwnProperties(cx, wrappedObject(proxy), idap);
return GetPropertyNames(cx, wrappedObject(proxy), JSITER_OWNONLY | JSITER_HIDDEN, idap);
}
bool
@ -227,7 +240,7 @@ JSNoopProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
bool
JSNoopProxyHandler::enumerate(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
return EnumerateAllProperties(cx, wrappedObject(proxy), idap);
return GetPropertyNames(cx, wrappedObject(proxy), 0, idap);
}
bool
@ -273,7 +286,13 @@ JSNoopProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid
bool
JSNoopProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
return EnumerateOwnProperties(cx, wrappedObject(proxy), idap);
return GetPropertyNames(cx, wrappedObject(proxy), JSITER_OWNONLY, idap);
}
bool
JSNoopProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
{
return GetIterator(cx, wrappedObject(proxy), flags, vp);
}
void
@ -290,7 +309,7 @@ JSNoopProxyHandler::trace(JSTracer *trc, JSObject *proxy)
JS_CALL_OBJECT_TRACER(trc, mWrappedObject, "wrappedObject");
}
void *
const void *
JSNoopProxyHandler::family()
{
return &singleton;
@ -301,17 +320,7 @@ JSNoopProxyHandler JSNoopProxyHandler::singleton(NULL);
static bool
GetTrap(JSContext *cx, JSObject *handler, JSAtom *atom, jsval *fvalp)
{
if (!handler->getProperty(cx, ATOM_TO_JSID(atom), fvalp)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNDEFINED_PROP,
JS_GetStringBytes(JSVAL_TO_STRING(ID_TO_VALUE(atom))));
return false;
}
if (!js_IsCallable(*fvalp)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_FUNCTION,
JS_GetStringBytes(JSVAL_TO_STRING(ID_TO_VALUE(atom))));
return false;
}
return true;
return handler->getProperty(cx, ATOM_TO_JSID(atom), fvalp);
}
static bool
@ -325,39 +334,65 @@ TryHandlerTrap(JSContext *cx, JSObject *proxy, bool ok = true)
}
static bool
Trap(JSContext *cx, JSObject *handler, JSAtom *atom, uintN argc, jsval* argv, jsval *rval)
FundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, jsval *fvalp)
{
if (!GetTrap(cx, handler, atom, fvalp))
return false;
if (!js_IsCallable(*fvalp)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_FUNCTION,
js_AtomToPrintableString(cx, atom));
return false;
}
return true;
}
static bool
DerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, jsval *fvalp)
{
JS_ASSERT(atom == ATOM(has) ||
atom == ATOM(hasOwn) ||
atom == ATOM(get) ||
atom == ATOM(set) ||
atom == ATOM(enumerateOwn) ||
atom == ATOM(iterate));
return GetTrap(cx, handler, atom, fvalp);
}
static bool
Trap(JSContext *cx, JSObject *handler, jsval fval, uintN argc, jsval* argv, jsval *rval)
{
JS_CHECK_RECURSION(cx, return false);
jsval fval;
if (!GetTrap(cx, handler, atom, &fval))
return false;
return js_InternalCall(cx, handler, fval, argc, argv, rval);
}
static bool
Trap1(JSContext *cx, JSObject *handler, JSAtom *atom, jsid id, jsval *rval)
Trap1(JSContext *cx, JSObject *handler, jsval fval, jsid id, jsval *rval)
{
JSString *str = js_ValueToString(cx, ID_TO_VALUE(id));
if (!str)
return false;
*rval = STRING_TO_JSVAL(str);
return Trap(cx, handler, atom, 1, rval, rval);
return Trap(cx, handler, fval, 1, rval, rval);
}
static bool
Trap2(JSContext *cx, JSObject *handler, JSAtom *atom, jsid id, jsval v, jsval *rval)
Trap2(JSContext *cx, JSObject *handler, jsval fval, jsid id, jsval v, jsval *rval)
{
JSString *str = js_ValueToString(cx, ID_TO_VALUE(id));
if (!str)
return false;
*rval = STRING_TO_JSVAL(str);
jsval argv[2] = { *rval, v };
return Trap(cx, handler, atom, 2, argv, rval);
return Trap(cx, handler, fval, 2, argv, rval);
}
static bool
ParsePropertyDescriptorObject(JSContext *cx, JSObject *obj, jsid id, jsval v, JSPropertyDescriptor *desc)
ParsePropertyDescriptorObject(JSContext *cx, JSObject *obj, jsid id, jsval v,
JSPropertyDescriptor *desc)
{
AutoDescriptorArray descs(cx);
PropertyDescriptor *d = descs.append();
@ -386,80 +421,18 @@ MakePropertyDescriptorObject(JSContext *cx, jsid id, JSPropertyDescriptor *desc,
return js_NewPropertyDescriptorObject(cx, id, attrs, getter, setter, desc->value, vp);
}
bool
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
static bool
ValueToBool(JSContext *cx, jsval v, bool *bp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
return TryHandlerTrap(cx, proxy, Trap1(cx, JSVAL_TO_OBJECT(handler), ATOM(getPropertyDescriptor), id, tvr.addr())) &&
ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc);
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->getPropertyDescriptor(cx, proxy, id, desc));
JSBool b;
if (!JS_ValueToBoolean(cx, v, &b))
return false;
*bp = !!b;
return true;
}
bool
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, jsval *vp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
return TryHandlerTrap(cx, proxy, Trap1(cx, JSVAL_TO_OBJECT(handler), ATOM(getPropertyDescriptor), id, vp));
}
AutoDescriptor desc(cx);
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->getPropertyDescriptor(cx, proxy, id, &desc)) &&
MakePropertyDescriptorObject(cx, id, &desc, vp);
}
bool
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
return TryHandlerTrap(cx, proxy, Trap1(cx, JSVAL_TO_OBJECT(handler), ATOM(getOwnPropertyDescriptor), id, tvr.addr())) &&
ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc);
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->getOwnPropertyDescriptor(cx, proxy, id, desc));
}
bool
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, jsval *vp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler))
return TryHandlerTrap(cx, proxy, Trap1(cx, JSVAL_TO_OBJECT(handler), ATOM(getOwnPropertyDescriptor), id, vp));
AutoDescriptor desc(cx);
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->getOwnPropertyDescriptor(cx, proxy, id, &desc)) &&
MakePropertyDescriptorObject(cx, id, &desc, vp);
}
bool
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
return MakePropertyDescriptorObject(cx, id, desc, tvr.addr()) &&
TryHandlerTrap(cx, proxy, Trap2(cx, JSVAL_TO_OBJECT(handler), ATOM(defineProperty), id, tvr.value(), tvr.addr()));
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->defineProperty(cx, proxy, id, desc));
}
bool
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, jsval v)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
return TryHandlerTrap(cx, proxy, Trap2(cx, JSVAL_TO_OBJECT(handler), ATOM(defineProperty), id, v, tvr.addr()));
}
AutoDescriptor desc(cx);
return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->defineProperty(cx, proxy, id, &desc));
}
bool
ArrayToJSIDArray(JSContext *cx, jsval array, JSIdArray **idap)
ArrayToJSIdArray(JSContext *cx, jsval array, JSIdArray **idap)
{
if (JSVAL_IS_PRIMITIVE(array))
return (*idap = NewIdArray(cx, 0)) != NULL;
@ -488,48 +461,298 @@ ArrayToJSIDArray(JSContext *cx, jsval array, JSIdArray **idap)
return true;
}
/* Derived class for all scripted proxy handlers. */
class JSScriptedProxyHandler : public JSProxyHandler {
public:
JSScriptedProxyHandler();
virtual ~JSScriptedProxyHandler();
/* ES5 Harmony fundamental proxy traps. */
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, JSIdArray **idap);
virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual bool enumerate(JSContext *cx, JSObject *proxy, JSIdArray **idap);
virtual bool fix(JSContext *cx, JSObject *proxy, jsval *vp);
/* ES5 Harmony derived proxy traps. */
virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap);
virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
/* Spidermonkey extensions. */
virtual const void *family();
static JSScriptedProxyHandler singleton;
};
JSScriptedProxyHandler::JSScriptedProxyHandler()
{
}
JSScriptedProxyHandler::~JSScriptedProxyHandler()
{
}
bool
JSScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
return FundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), tvr.addr()) &&
TryHandlerTrap(cx, proxy, Trap1(cx, handler, tvr.value(), id, tvr.addr())) &&
ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc);
}
bool
JSScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
return FundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), tvr.addr()) &&
TryHandlerTrap(cx, proxy, Trap1(cx, handler, tvr.value(), id, tvr.addr())) &&
ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc);
}
bool
JSScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
AutoValueRooter fval(cx);
return FundamentalTrap(cx, handler, ATOM(defineProperty), fval.addr()) &&
MakePropertyDescriptorObject(cx, id, desc, tvr.addr()) &&
TryHandlerTrap(cx, proxy, Trap2(cx, handler, fval.value(), id, tvr.value(), tvr.addr()));
}
bool
JSScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
return FundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), tvr.addr()) &&
TryHandlerTrap(cx, proxy, Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr())) &&
ArrayToJSIdArray(cx, tvr.value(), idap);
}
bool
JSScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
return FundamentalTrap(cx, handler, ATOM(delete), tvr.addr()) &&
TryHandlerTrap(cx, proxy, Trap1(cx, handler, tvr.value(), id, tvr.addr())) &&
ValueToBool(cx, tvr.value(), bp);
}
bool
JSScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
return FundamentalTrap(cx, handler, ATOM(enumerate), tvr.addr()) &&
TryHandlerTrap(cx, proxy, Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr())) &&
ArrayToJSIdArray(cx, tvr.value(), idap);
}
bool
JSScriptedProxyHandler::fix(JSContext *cx, JSObject *proxy, jsval *vp)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
return FundamentalTrap(cx, handler, ATOM(fix), vp) &&
Trap(cx, handler, *vp, 0, NULL, vp);
}
bool
JSScriptedProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
if (!DerivedTrap(cx, handler, ATOM(has), tvr.addr()))
return false;
if (!js_IsCallable(tvr.value()))
return JSProxyHandler::has(cx, proxy, id, bp);
return TryHandlerTrap(cx, proxy, Trap1(cx, handler, tvr.value(), id, tvr.addr())) &&
ValueToBool(cx, tvr.value(), bp);
}
bool
JSScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
if (!DerivedTrap(cx, handler, ATOM(hasOwn), tvr.addr()))
return false;
if (!js_IsCallable(tvr.value()))
return JSProxyHandler::hasOwn(cx, proxy, id, bp);
return TryHandlerTrap(cx, proxy, Trap1(cx, handler, tvr.value(), id, tvr.addr())) &&
ValueToBool(cx, tvr.value(), bp);
}
bool
JSScriptedProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
JSString *str = js_ValueToString(cx, ID_TO_VALUE(id));
if (!str)
return false;
AutoValueRooter tvr(cx, STRING_TO_JSVAL(str));
jsval argv[] = { OBJECT_TO_JSVAL(receiver), tvr.value() };
AutoValueRooter fval(cx);
if (!DerivedTrap(cx, handler, ATOM(get), fval.addr()))
return false;
if (!js_IsCallable(fval.value()))
return JSProxyHandler::get(cx, proxy, receiver, id, vp);
return TryHandlerTrap(cx, proxy, Trap(cx, handler, fval.value(), 2, argv, vp));
}
bool
JSScriptedProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
JSString *str = js_ValueToString(cx, ID_TO_VALUE(id));
if (!str)
return false;
AutoValueRooter tvr(cx, STRING_TO_JSVAL(str));
jsval argv[] = { OBJECT_TO_JSVAL(receiver), tvr.value(), *vp };
AutoValueRooter fval(cx);
if (!DerivedTrap(cx, handler, ATOM(set), fval.addr()))
return false;
if (!js_IsCallable(fval.value()))
return JSProxyHandler::set(cx, proxy, receiver, id, vp);
return TryHandlerTrap(cx, proxy, Trap(cx, handler, fval.value(), 3, argv, tvr.addr()));
}
bool
JSScriptedProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
if (!DerivedTrap(cx, handler, ATOM(enumerateOwn), tvr.addr()))
return false;
if (!js_IsCallable(tvr.value()))
return JSProxyHandler::enumerateOwn(cx, proxy, idap);
return TryHandlerTrap(cx, proxy, Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr())) &&
ArrayToJSIdArray(cx, tvr.value(), idap);
}
bool
JSScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
{
JSObject *handler = JSVAL_TO_OBJECT(proxy->getProxyHandler());
AutoValueRooter tvr(cx);
if (!DerivedTrap(cx, handler, ATOM(iterate), tvr.addr()))
return false;
if (!js_IsCallable(tvr.value()))
return JSProxyHandler::iterate(cx, proxy, flags, vp);
return TryHandlerTrap(cx, proxy, Trap(cx, handler, tvr.value(), 0, NULL, vp));
}
const void *
JSScriptedProxyHandler::family()
{
return &singleton;
}
JSScriptedProxyHandler JSScriptedProxyHandler::singleton;
static JSProxyHandler *
JSVAL_TO_HANDLER(jsval handler)
{
return (JSProxyHandler *) JSVAL_TO_PRIVATE(handler);
}
bool
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.getPropertyDescriptor(cx, proxy, id, desc);
return TryHandlerTrap(cx, proxy,
JSVAL_TO_HANDLER(handler)->getPropertyDescriptor(cx, proxy, id, desc));
}
bool
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, jsval *vp)
{
AutoDescriptor desc(cx);
return JSProxy::getPropertyDescriptor(cx, proxy, id, &desc) &&
MakePropertyDescriptorObject(cx, id, &desc, vp);
}
bool
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.getOwnPropertyDescriptor(cx, proxy, id, desc);
return TryHandlerTrap(cx, proxy,
JSVAL_TO_HANDLER(handler)->getOwnPropertyDescriptor(cx, proxy, id, desc));
}
bool
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, jsval *vp)
{
AutoDescriptor desc(cx);
return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, &desc) &&
MakePropertyDescriptorObject(cx, id, &desc, vp);
}
bool
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.defineProperty(cx, proxy, id, desc);
return TryHandlerTrap(cx, proxy,
JSVAL_TO_HANDLER(handler)->defineProperty(cx, proxy, id, desc));
}
bool
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, jsval v)
{
AutoDescriptor desc(cx);
return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
JSProxy::defineProperty(cx, proxy, id, &desc);
}
bool
JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
if (!TryHandlerTrap(cx, proxy, Trap(cx, JSVAL_TO_OBJECT(handler), ATOM(getOwnPropertyNames),
0, NULL, tvr.addr())))
return false;
return ArrayToJSIDArray(cx, tvr.value(), idap);
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->getOwnPropertyNames(cx, proxy, idap));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.getOwnPropertyNames(cx, proxy, idap);
return TryHandlerTrap(cx, proxy,
JSVAL_TO_HANDLER(handler)->getOwnPropertyNames(cx, proxy, idap));
}
bool
JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
if (!TryHandlerTrap(cx, proxy, Trap1(cx, JSVAL_TO_OBJECT(handler), ATOM(delete), id, tvr.addr())))
return false;
JSBool deleted;
if (!JS_ValueToBoolean(cx, tvr.value(), &deleted))
return false;
*bp = !!deleted;
return true;
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->delete_(cx, proxy, id, bp));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.delete_(cx, proxy, id, bp);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->delete_(cx, proxy, id, bp));
}
bool
JSProxy::enumerate(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
if (!TryHandlerTrap(cx, proxy, Trap(cx, JSVAL_TO_OBJECT(handler), ATOM(enumerate), 0, NULL, tvr.addr())))
return false;
return ArrayToJSIDArray(cx, tvr.value(), idap);
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->enumerate(cx, proxy, idap));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.enumerate(cx, proxy, idap);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->enumerate(cx, proxy, idap));
}
bool
@ -537,85 +760,62 @@ JSProxy::fix(JSContext *cx, JSObject *proxy, jsval *vp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler))
return Trap(cx, JSVAL_TO_OBJECT(handler), ATOM(fix), 0, NULL, vp);
return ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->fix(cx, proxy, vp);
return JSScriptedProxyHandler::singleton.fix(cx, proxy, vp);
return JSVAL_TO_HANDLER(handler)->fix(cx, proxy, vp);
}
bool
JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
if (!TryHandlerTrap(cx, proxy, Trap1(cx, JSVAL_TO_OBJECT(handler), ATOM(has), id, tvr.addr())))
return false;
JSBool has;
if (!JS_ValueToBoolean(cx, tvr.value(), &has))
return false;
*bp = !!has;
return true;
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->has(cx, proxy, id, bp));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.has(cx, proxy, id, bp);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->has(cx, proxy, id, bp));
}
bool
JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter tvr(cx);
if (!TryHandlerTrap(cx, proxy, Trap1(cx, JSVAL_TO_OBJECT(handler), ATOM(hasOwn), id, tvr.addr())))
return false;
JSBool has;
if (!JS_ValueToBoolean(cx, tvr.value(), &has))
return false;
*bp = !!has;
return true;
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->hasOwn(cx, proxy, id, bp));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.hasOwn(cx, proxy, id, bp);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->hasOwn(cx, proxy, id, bp));
}
bool
JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
JSString *str = js_ValueToString(cx, ID_TO_VALUE(id));
if (!str)
return false;
AutoValueRooter tvr(cx, STRING_TO_JSVAL(str));
jsval argv[] = { OBJECT_TO_JSVAL(receiver), tvr.value() };
return TryHandlerTrap(cx, proxy, Trap(cx, JSVAL_TO_OBJECT(handler), ATOM(get), 2, argv, vp));
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->get(cx, proxy, receiver, id, vp));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.get(cx, proxy, receiver, id, vp);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->get(cx, proxy, receiver, id, vp));
}
bool
JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
JSString *str = js_ValueToString(cx, ID_TO_VALUE(id));
if (!str)
return false;
AutoValueRooter tvr(cx, STRING_TO_JSVAL(str));
jsval argv[] = { OBJECT_TO_JSVAL(receiver), tvr.value(), *vp };
return TryHandlerTrap(cx, proxy, Trap(cx, JSVAL_TO_OBJECT(handler), ATOM(set), 3, argv, tvr.addr()));
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->set(cx, proxy, receiver, id, vp));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.set(cx, proxy, receiver, id, vp);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->set(cx, proxy, receiver, id, vp));
}
bool
JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler)) {
AutoValueRooter rval(cx);
if (!TryHandlerTrap(cx, proxy, Trap(cx, JSVAL_TO_OBJECT(handler), ATOM(enumerateOwn), 0, NULL, rval.addr())))
return false;
return ArrayToJSIDArray(cx, rval.value(), idap);
}
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->enumerateOwn(cx, proxy, idap));
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.enumerateOwn(cx, proxy, idap);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->enumerateOwn(cx, proxy, idap));
}
bool
JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
{
jsval handler = proxy->getProxyHandler();
if (JSVAL_IS_OBJECT(handler))
return JSScriptedProxyHandler::singleton.iterate(cx, proxy, flags, vp);
return TryHandlerTrap(cx, proxy, JSVAL_TO_HANDLER(handler)->iterate(cx, proxy, flags, vp));
}
JS_FRIEND_API(JSBool)
@ -743,7 +943,7 @@ proxy_TraceObject(JSTracer *trc, JSObject *obj)
if (!JSVAL_IS_PRIMITIVE(handler))
JS_CALL_OBJECT_TRACER(trc, JSVAL_TO_OBJECT(handler), "handler");
else
((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->trace(trc, obj);
JSVAL_TO_HANDLER(handler)->trace(trc, obj);
if (obj->isFunctionProxy()) {
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_CALL], "call");
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_CONSTRUCT], "construct");
@ -788,9 +988,8 @@ obj_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
}
JS_FRIEND_API(JSClass) ObjectProxyClass = {
"ObjectProxy",
JSCLASS_HAS_RESERVED_SLOTS(3) |
JSCLASS_NEW_ENUMERATE,
"Proxy",
JSCLASS_HAS_RESERVED_SLOTS(3),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
obj_proxy_getObjectOps, NULL, NULL, NULL,
@ -812,11 +1011,17 @@ proxy_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rv
JS_ASSERT(proxy->isProxy());
jsval fval = proxy->fslots[JSSLOT_PROXY_CONSTRUCT];
if (fval == JSVAL_VOID) {
/* We don't have an explicit constructor trap so allocate a new object and use the call trap. */
/*
* We don't have an explicit constructor trap so allocate a new
* object and use the call trap.
*/
fval = proxy->fslots[JSSLOT_PROXY_CALL];
JS_ASSERT(JSVAL_IS_OBJECT(fval));
/* proxy is the constructor, so get proxy.prototype as the proto of the new object. */
/*
* proxy is the constructor, so get proxy.prototype as the proto
* of the new object.
*/
if (!JSProxy::get(cx, proxy, obj, ATOM_TO_JSID(ATOM(classPrototype)), rval))
return false;
JSObject *proto = !JSVAL_IS_PRIMITIVE(*rval) ? JSVAL_TO_OBJECT(*rval) : NULL;
@ -867,9 +1072,8 @@ fun_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
}
JS_FRIEND_API(JSClass) FunctionProxyClass = {
"FunctionProxy",
JSCLASS_HAS_RESERVED_SLOTS(3) |
JSCLASS_NEW_ENUMERATE,
"Proxy",
JSCLASS_HAS_RESERVED_SLOTS(3),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
fun_proxy_getObjectOps, NULL, NULL, NULL,
@ -916,7 +1120,7 @@ proxy_create(JSContext *cx, uintN argc, jsval *vp)
{
if (argc < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
"Proxy.create", "0", "s");
"create", "0", "s");
return false;
}
JSObject *handler;
@ -945,7 +1149,7 @@ proxy_createFunction(JSContext *cx, uintN argc, jsval *vp)
{
if (argc < 2) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
"Proxy.createFunction", "1", "");
"createFunction", "1", "");
return false;
}
JSObject *handler;
@ -967,7 +1171,8 @@ proxy_createFunction(JSContext *cx, uintN argc, jsval *vp)
return false;
}
JSObject *proxy = NewFunctionProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, call, construct);
JSObject *proxy = NewFunctionProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent,
call, construct);
if (!proxy)
return false;
@ -982,7 +1187,7 @@ proxy_isTrapping(JSContext *cx, uintN argc, jsval *vp)
{
if (argc < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
"Proxy.isTrapping", "0", "s");
"isTrapping", "0", "s");
return false;
}
JSObject *obj;
@ -997,7 +1202,7 @@ proxy_fix(JSContext *cx, uintN argc, jsval *vp)
{
if (argc < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
"Proxy.fix", "0", "s");
"fix", "0", "s");
return false;
}
JSObject *obj;
@ -1071,9 +1276,8 @@ callable_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval
}
JSClass CallableObjectClass = {
"CallableObject",
JSCLASS_HAS_RESERVED_SLOTS(2) |
JSCLASS_NEW_ENUMERATE,
"Function",
JSCLASS_HAS_RESERVED_SLOTS(2),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
NULL, NULL, callable_Call, callable_Construct,

View File

@ -53,9 +53,12 @@ class JSProxyHandler {
virtual ~JSProxyHandler();
/* ES5 Harmony fundamental proxy traps. */
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) = 0;
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) = 0;
virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) = 0;
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc) = 0;
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc) = 0;
virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc) = 0;
virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, JSIdArray **idap) = 0;
virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) = 0;
virtual bool enumerate(JSContext *cx, JSObject *proxy, JSIdArray **idap) = 0;
@ -67,11 +70,12 @@ class JSProxyHandler {
virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap);
virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
/* Spidermonkey extensions. */
virtual void finalize(JSContext *cx, JSObject *proxy);
virtual void trace(JSTracer *trc, JSObject *proxy);
virtual void *family() = 0;
virtual const void *family() = 0;
};
/* No-op wrapper handler base class. */
@ -85,10 +89,14 @@ class JSNoopProxyHandler {
JS_FRIEND_API(virtual ~JSNoopProxyHandler());
/* ES5 Harmony fundamental proxy traps. */
virtual JS_FRIEND_API(bool) getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc);
virtual JS_FRIEND_API(bool) getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc);
virtual JS_FRIEND_API(bool) defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc);
virtual JS_FRIEND_API(bool) getOwnPropertyNames(JSContext *cx, JSObject *proxy, JSIdArray **idap);
virtual JS_FRIEND_API(bool) getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
virtual JS_FRIEND_API(bool) getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
virtual JS_FRIEND_API(bool) defineProperty(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
virtual JS_FRIEND_API(bool) getOwnPropertyNames(JSContext *cx, JSObject *proxy,
JSIdArray **idap);
virtual JS_FRIEND_API(bool) delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual JS_FRIEND_API(bool) enumerate(JSContext *cx, JSObject *proxy, JSIdArray **idap);
virtual JS_FRIEND_API(bool) fix(JSContext *cx, JSObject *proxy, jsval *vp);
@ -96,19 +104,23 @@ class JSNoopProxyHandler {
/* ES5 Harmony derived proxy traps. */
virtual JS_FRIEND_API(bool) has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual JS_FRIEND_API(bool) hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
virtual JS_FRIEND_API(bool) get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
virtual JS_FRIEND_API(bool) set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
virtual JS_FRIEND_API(bool) get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id,
jsval *vp);
virtual JS_FRIEND_API(bool) set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id,
jsval *vp);
virtual JS_FRIEND_API(bool) enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap);
virtual JS_FRIEND_API(bool) iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
/* Spidermonkey extensions. */
virtual JS_FRIEND_API(void) finalize(JSContext *cx, JSObject *proxy);
virtual JS_FRIEND_API(void) trace(JSTracer *trc, JSObject *proxy);
virtual JS_FRIEND_API(void) *family();
virtual JS_FRIEND_API(const void *) family();
static JSNoopProxyHandler singleton;
template <class T>
static JSObject *wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, JSString *className);
static JSObject *wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
JSString *className);
inline JSObject *wrappedObject(JSObject *proxy) {
return mWrappedObject ? mWrappedObject : JSVAL_TO_OBJECT(proxy->getProxyPrivate());
@ -119,9 +131,11 @@ class JSNoopProxyHandler {
class JSProxy {
public:
/* ES5 Harmony fundamental proxy traps. */
static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc);
static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, jsval *vp);
static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc);
static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
JSPropertyDescriptor *desc);
static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, jsval *vp);
static bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc);
static bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, jsval v);
@ -136,6 +150,7 @@ class JSProxy {
static bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
static bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
static bool enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap);
static bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
};
/* Shared between object and function proxies. */
@ -197,10 +212,12 @@ JSObject::setProxyPrivate(jsval priv)
namespace js {
JS_FRIEND_API(JSObject *)
NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className);
NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
JSString *className);
JS_FRIEND_API(JSObject *)
NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSObject *call, JSObject *construct);
NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
JSObject *call, JSObject *construct);
JS_FRIEND_API(JSBool)
GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep);
@ -210,18 +227,21 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
template <class T>
JSObject *
JSNoopProxyHandler::wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, JSString *className)
JSNoopProxyHandler::wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
JSString *className)
{
if (obj->isCallable()) {
JSNoopProxyHandler *handler = new T(obj);
if (!handler)
return NULL;
JSObject *wrapper = NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent, obj, NULL);
JSObject *wrapper = NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent,
obj, NULL);
if (!wrapper)
delete handler;
return wrapper;
}
JSObject *wrapper = NewObjectProxy(cx, PRIVATE_TO_JSVAL(&T::singleton), proto, parent, className);
JSObject *wrapper = NewObjectProxy(cx, PRIVATE_TO_JSVAL(&T::singleton), proto, parent,
className);
if (wrapper)
wrapper->setProxyPrivate(OBJECT_TO_JSVAL(obj));
return wrapper;

View File

@ -69,6 +69,7 @@
#define INT_TO_JSID(i) ((jsid)INT_TO_JSVAL(i))
#define INT_JSVAL_TO_JSID(v) ((jsid)(v))
#define INT_JSID_TO_JSVAL(id) ((jsval)(id))
#define INT_FITS_IN_JSID(i) INT_FITS_IN_JSVAL(i)
#define JSID_IS_OBJECT(id) JSVAL_IS_OBJECT((jsval)(id))
#define JSID_TO_OBJECT(id) JSVAL_TO_OBJECT((jsval)(id))