зеркало из https://github.com/mozilla/gecko-dev.git
Implement ES Harmony Proxies (bug 546590, r=mrbkap).
This commit is contained in:
Родитель
ce1ca3330e
Коммит
9f8b556775
|
@ -152,6 +152,7 @@ CPPSRCS = \
|
|||
json.cpp \
|
||||
jsopcode.cpp \
|
||||
jsparse.cpp \
|
||||
jsproxy.cpp \
|
||||
jsprf.cpp \
|
||||
jspropertycache.cpp \
|
||||
jspropertytree.cpp \
|
||||
|
@ -208,6 +209,7 @@ INSTALLED_HEADERS = \
|
|||
jsopcode.h \
|
||||
jsotypes.h \
|
||||
jsparse.h \
|
||||
jsproxy.h \
|
||||
jsprf.h \
|
||||
jspropertycache.h \
|
||||
jspropertycacheinlines.h \
|
||||
|
|
|
@ -328,3 +328,4 @@ MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be
|
|||
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 246, 0, JSEXN_ERR, "invalid arguments")
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blocked by CSP")
|
||||
MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 248, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
|
||||
MSG_DEF(JSMSG_BAD_PROXY_FIX, 249, 0, JSEXN_TYPEERR, "proxy was fixed while executing the handler")
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsparse.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsregexp.h"
|
||||
#include "jsscan.h"
|
||||
#include "jsscope.h"
|
||||
|
@ -1213,7 +1214,8 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
|
|||
#if JS_HAS_GENERATORS
|
||||
js_InitIteratorClasses(cx, obj) &&
|
||||
#endif
|
||||
js_InitDateClass(cx, obj);
|
||||
js_InitDateClass(cx, obj) &&
|
||||
js_InitProxyClass(cx, obj);
|
||||
}
|
||||
|
||||
#define CLASP(name) (&js_##name##Class)
|
||||
|
@ -1340,6 +1342,8 @@ static JSStdName standard_class_names[] = {
|
|||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Float64Array), NULL},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray), NULL},
|
||||
|
||||
{js_InitProxyClass, EAGER_ATOM(Proxy), NULL},
|
||||
|
||||
{NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -1502,13 +1506,15 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSIdArray *
|
||||
namespace js {
|
||||
|
||||
JSIdArray *
|
||||
NewIdArray(JSContext *cx, jsint length)
|
||||
{
|
||||
JSIdArray *ida;
|
||||
|
||||
ida = (JSIdArray *)
|
||||
cx->malloc(offsetof(JSIdArray, vector) + length * sizeof(jsval));
|
||||
cx->calloc(offsetof(JSIdArray, vector) + length * sizeof(jsval));
|
||||
if (ida) {
|
||||
ida->self = ida;
|
||||
ida->length = length;
|
||||
|
@ -1516,6 +1522,8 @@ NewIdArray(JSContext *cx, jsint length)
|
|||
return ida;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlike realloc(3), this function frees ida on failure.
|
||||
*/
|
||||
|
@ -1524,13 +1532,13 @@ SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
|
|||
{
|
||||
JSIdArray *rida;
|
||||
|
||||
JS_ASSERT(ida->self == ida);
|
||||
rida = (JSIdArray *)
|
||||
JS_realloc(cx, ida,
|
||||
offsetof(JSIdArray, vector) + length * sizeof(jsval));
|
||||
if (!rida) {
|
||||
JS_DestroyIdArray(cx, ida);
|
||||
} else {
|
||||
rida->self = rida;
|
||||
rida->length = length;
|
||||
}
|
||||
return rida;
|
||||
|
@ -3120,6 +3128,12 @@ GetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||
? obj2->lockedGetSlot(sprop->slot)
|
||||
: JSVAL_VOID;
|
||||
} else {
|
||||
if (obj->isProxy()) {
|
||||
JSAutoResolveFlags rf(cx, flags);
|
||||
return own
|
||||
? JSProxy::getOwnPropertyDescriptor(cx, obj, id, desc)
|
||||
: JSProxy::getPropertyDescriptor(cx, obj, id, desc);
|
||||
}
|
||||
desc->getter = NULL;
|
||||
desc->setter = NULL;
|
||||
desc->value = JSVAL_VOID;
|
||||
|
|
|
@ -284,6 +284,8 @@ BOOLEAN_TO_JSVAL(JSBool b)
|
|||
object that delegates to a prototype
|
||||
containing this property */
|
||||
#define JSPROP_INDEX 0x80 /* name is actually (jsint) index */
|
||||
#define JSPROP_SHORTID 0x100 /* set in JSPropertyDescriptor.attrs
|
||||
if getters/setters use a shortid */
|
||||
|
||||
/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */
|
||||
#define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */
|
||||
|
@ -1821,6 +1823,7 @@ struct JSPropertyDescriptor {
|
|||
uintN attrs;
|
||||
JSPropertyOp getter;
|
||||
JSPropertyOp setter;
|
||||
uintN shortid;
|
||||
jsval value;
|
||||
};
|
||||
|
||||
|
|
|
@ -182,6 +182,20 @@ const char *const js_common_atom_names[] = {
|
|||
js_ExecutionContext_str, /* ExecutionContextAtom */
|
||||
js_current_str, /* currentAtom */
|
||||
#endif
|
||||
|
||||
"Proxy", /* ProxyAtom */
|
||||
|
||||
"getOwnPropertyDescriptor", /* getOwnPropertyDescriptorAtom */
|
||||
"getPropertyDescriptor", /* getPropertyDescriptorAtom */
|
||||
"defineProperty", /* definePropertyAtom */
|
||||
"delete", /* deleteAtom */
|
||||
"getOwnPropertyNames", /* getOwnPropertyNames */
|
||||
"enumerate", /* enumerateAtom */
|
||||
"fix",
|
||||
|
||||
"has", /* hasAtom */
|
||||
"hasOwn", /* hasOwnAtom */
|
||||
"enumerateOwn" /* enumerateOwnAtom */
|
||||
};
|
||||
|
||||
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
|
||||
|
|
|
@ -295,6 +295,20 @@ struct JSAtomState {
|
|||
JSAtom *currentAtom;
|
||||
#endif
|
||||
|
||||
JSAtom *ProxyAtom;
|
||||
|
||||
JSAtom *getOwnPropertyDescriptorAtom;
|
||||
JSAtom *getPropertyDescriptorAtom;
|
||||
JSAtom *definePropertyAtom;
|
||||
JSAtom *deleteAtom;
|
||||
JSAtom *getOwnPropertyNamesAtom;
|
||||
JSAtom *enumerateAtom;
|
||||
JSAtom *fixAtom;
|
||||
|
||||
JSAtom *hasAtom;
|
||||
JSAtom *hasOwnAtom;
|
||||
JSAtom *enumerateOwnAtom;
|
||||
|
||||
/* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */
|
||||
struct {
|
||||
JSAtom *InfinityAtom;
|
||||
|
@ -325,6 +339,8 @@ struct JSAtomState {
|
|||
} lazy;
|
||||
};
|
||||
|
||||
#define ATOM(name) cx->runtime->atomState.name##Atom
|
||||
|
||||
#define ATOM_OFFSET_START offsetof(JSAtomState, emptyAtom)
|
||||
#define LAZY_ATOM_OFFSET_START offsetof(JSAtomState, lazy)
|
||||
#define ATOM_OFFSET_LIMIT (sizeof(JSAtomState))
|
||||
|
|
|
@ -2174,7 +2174,8 @@ class AutoGCRooter {
|
|||
XML = -10, /* js::AutoXMLRooter */
|
||||
OBJECT = -11, /* js::AutoObjectRooter */
|
||||
ID = -12, /* js::AutoIdRooter */
|
||||
VECTOR = -13 /* js::AutoValueVector */
|
||||
VECTOR = -13, /* js::AutoValueVector */
|
||||
DESCRIPTOR = -14 /* js::AutoDescriptor */
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -2404,11 +2405,17 @@ class AutoIdArray : private AutoGCRooter {
|
|||
|
||||
friend void AutoGCRooter::trace(JSTracer *trc);
|
||||
|
||||
JSIdArray *steal() {
|
||||
JSIdArray *copy = idArray;
|
||||
idArray = NULL;
|
||||
return copy;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline void trace(JSTracer *trc);
|
||||
|
||||
private:
|
||||
JSIdArray * const idArray;
|
||||
JSIdArray * idArray;
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
||||
/* No copy or assignment semantics. */
|
||||
|
@ -3023,6 +3030,9 @@ class AutoValueVector : private AutoGCRooter
|
|||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
JSIdArray *
|
||||
NewIdArray(JSContext *cx, jsint length);
|
||||
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -235,6 +235,7 @@ AutoGCRooter::trace(JSTracer *trc)
|
|||
for (size_t i = 0, len = descriptors.length(); i < len; i++) {
|
||||
PropertyDescriptor &desc = descriptors[i];
|
||||
|
||||
JS_CALL_VALUE_TRACER(trc, desc.pd, "PropertyDescriptor::pd");
|
||||
JS_CALL_VALUE_TRACER(trc, desc.value, "PropertyDescriptor::value");
|
||||
JS_CALL_VALUE_TRACER(trc, desc.get, "PropertyDescriptor::get");
|
||||
JS_CALL_VALUE_TRACER(trc, desc.set, "PropertyDescriptor::set");
|
||||
|
@ -243,6 +244,17 @@ AutoGCRooter::trace(JSTracer *trc)
|
|||
return;
|
||||
}
|
||||
|
||||
case DESCRIPTOR : {
|
||||
AutoDescriptor &desc = *static_cast<AutoDescriptor *>(this);
|
||||
JS_CALL_OBJECT_TRACER(trc, desc.obj, "Descriptor::obj");
|
||||
JS_CALL_VALUE_TRACER(trc, desc.value, "Descriptor::value");
|
||||
if (desc.attrs & JSPROP_GETTER)
|
||||
JS_CALL_VALUE_TRACER(trc, jsval(desc.getter), "Descriptor::get");
|
||||
if (desc.attrs & JSPROP_SETTER)
|
||||
JS_CALL_VALUE_TRACER(trc, jsval(desc.setter), "Descriptor::set");
|
||||
return;
|
||||
}
|
||||
|
||||
case NAMESPACES: {
|
||||
JSXMLArray &array = static_cast<AutoNamespaces *>(this)->array;
|
||||
TraceObjectVector(trc, reinterpret_cast<JSObject **>(array.vector), array.length);
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsparse.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsstaticcheck.h"
|
||||
|
@ -2384,6 +2385,12 @@ FinalizeObject(JSContext *cx, JSObject *obj, unsigned thingKind)
|
|||
static_cast<JSEmptyScope *>(scope)->dropFromGC(cx);
|
||||
else
|
||||
scope->destroy(cx);
|
||||
} else {
|
||||
if (obj->isProxy()) {
|
||||
jsval handler = obj->getProxyHandler();
|
||||
if (JSVAL_IS_PRIMITIVE(handler))
|
||||
((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->finalize(cx, obj);
|
||||
}
|
||||
}
|
||||
if (obj->hasSlotsArray())
|
||||
obj->freeSlotsArray(cx);
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsscan.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsscript.h"
|
||||
|
@ -163,7 +164,7 @@ Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
|
|||
if (pobj->getProto() && !ht.add(p, id))
|
||||
return false;
|
||||
}
|
||||
if (enumerable) {
|
||||
if (enumerable || (flags & JSITER_HIDDEN)) {
|
||||
if (!vec.append(ID_TO_VALUE(id))) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -232,6 +233,32 @@ 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 *ni = (NativeIterator *)
|
||||
cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsval) + slength * sizeof(uint32));
|
||||
if (!ni) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
ni->props_array = ni->props_cursor = (jsval *) (ni + 1);
|
||||
ni->props_end = ni->props_array + plength;
|
||||
if (plength)
|
||||
memcpy(ni->props_array, parray, plength * sizeof(jsval));
|
||||
ni->shapes_array = (uint32 *) ni->props_end;
|
||||
ni->shapes_length = slength;
|
||||
ni->shapes_key = key;
|
||||
ni->flags = flags;
|
||||
if (slength)
|
||||
memcpy(ni->shapes_array, sarray, slength * sizeof(uint32));
|
||||
|
||||
*nip = ni;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength,
|
||||
uint32 key, NativeIterator **nip)
|
||||
|
@ -256,6 +283,23 @@ InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, ui
|
|||
if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props))
|
||||
return false;
|
||||
} else {
|
||||
if (pobj->isProxy()) {
|
||||
JSIdArray *ida;
|
||||
if (flags & JSITER_OWNONLY) {
|
||||
if (!JSProxy::enumerateOwn(cx, pobj, &ida))
|
||||
return false;
|
||||
} else {
|
||||
if (!JSProxy::enumerate(cx, pobj, &ida))
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
/* 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;
|
||||
|
@ -281,37 +325,12 @@ InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, ui
|
|||
pobj = pobj->getProto();
|
||||
}
|
||||
|
||||
size_t plength = props.length();
|
||||
|
||||
NativeIterator *ni = (NativeIterator *)
|
||||
cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsval) + slength * sizeof(uint32));
|
||||
if (!ni) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
ni->props_array = ni->props_cursor = (jsval *) (ni + 1);
|
||||
ni->props_end = ni->props_array + plength;
|
||||
if (plength)
|
||||
memcpy(ni->props_array, props.begin(), plength * sizeof(jsval));
|
||||
ni->shapes_array = (uint32 *) ni->props_end;
|
||||
ni->shapes_length = slength;
|
||||
ni->shapes_key = key;
|
||||
ni->flags = flags;
|
||||
if (slength)
|
||||
memcpy(ni->shapes_array, sarray, slength * sizeof(uint32));
|
||||
|
||||
*nip = ni;
|
||||
|
||||
return true;
|
||||
return MakeNativeIterator(cx, flags, sarray, slength, key, props.begin(), props.length(), nip);
|
||||
}
|
||||
|
||||
bool
|
||||
EnumerateOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap)
|
||||
NativeIteratorToJSIdArray(JSContext *cx, NativeIterator *ni, JSIdArray **idap)
|
||||
{
|
||||
NativeIterator *ni;
|
||||
if (!InitNativeIterator(cx, obj, JSITER_OWNONLY, NULL, 0, true, &ni))
|
||||
return false;
|
||||
|
||||
/* Morph the NativeIterator into a JSIdArray. The caller will deallocate it. */
|
||||
JS_ASSERT(sizeof(NativeIterator) > sizeof(JSIdArray));
|
||||
JS_ASSERT(ni->props_array == (jsid *) (ni + 1));
|
||||
|
@ -324,6 +343,33 @@ EnumerateOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EnumerateOwnProperties(JSContext *cx, JSObject *obj, 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))
|
||||
return false;
|
||||
return NativeIteratorToJSIdArray(cx, ni, idap);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
|
||||
{
|
||||
|
@ -418,10 +464,12 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
|
|||
}
|
||||
|
||||
miss:
|
||||
if (!GetCustomIterator(cx, obj, flags, vp))
|
||||
return false;
|
||||
if (*vp != JSVAL_VOID)
|
||||
return true;
|
||||
if (!obj->isProxy()) {
|
||||
if (!GetCustomIterator(cx, obj, flags, vp))
|
||||
return false;
|
||||
if (*vp != JSVAL_VOID)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
JSObject *iterobj = escaping
|
||||
|
|
|
@ -58,6 +58,7 @@ JS_BEGIN_EXTERN_C
|
|||
#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */
|
||||
#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */
|
||||
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
|
||||
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
|
||||
|
||||
struct NativeIterator {
|
||||
jsval *props_array;
|
||||
|
@ -82,6 +83,12 @@ static const jsval JSVAL_NATIVE_ENUMERATE_COOKIE = SPECIAL_TO_JSVAL(0x220576);
|
|||
bool
|
||||
EnumerateOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap);
|
||||
|
||||
bool
|
||||
EnumerateAllProperties(JSContext *cx, JSObject *obj, JSIdArray **idap);
|
||||
|
||||
bool
|
||||
GetOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap);
|
||||
|
||||
/*
|
||||
* Convert the value stored in *vp to its iteration object. The flags should
|
||||
* contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating
|
||||
|
|
212
js/src/jsobj.cpp
212
js/src/jsobj.cpp
|
@ -69,6 +69,7 @@
|
|||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsparse.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsstaticcheck.h"
|
||||
|
@ -871,8 +872,13 @@ obj_toString(JSContext *cx, uintN argc, jsval *vp)
|
|||
obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
obj = js_GetWrappedObject(cx, obj);
|
||||
clazz = obj->getClass()->name;
|
||||
if (obj->isProxy()) {
|
||||
if (!JS_GetProxyObjectClass(cx, obj, &clazz))
|
||||
return false;
|
||||
} else {
|
||||
obj = js_GetWrappedObject(cx, obj);
|
||||
clazz = obj->getClass()->name;
|
||||
}
|
||||
nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
|
||||
chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
|
||||
if (!chars)
|
||||
|
@ -1472,7 +1478,16 @@ js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
|
|||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
if (!obj || !js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
|
||||
if (!obj)
|
||||
return false;
|
||||
if (obj->isProxy()) {
|
||||
bool has;
|
||||
if (!JSProxy::hasOwn(cx, obj, id, &has))
|
||||
return false;
|
||||
*vp = BOOLEAN_TO_JSVAL(has);
|
||||
return true;
|
||||
}
|
||||
if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
|
||||
return JS_FALSE;
|
||||
if (prop) {
|
||||
*vp = JSVAL_TRUE;
|
||||
|
@ -1755,9 +1770,49 @@ obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
|
|||
JSACC_PROTO, vp, &attrs);
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs, jsval getter, jsval setter, jsval value, jsval *vp)
|
||||
{
|
||||
/* We have our own property, so start creating the descriptor. */
|
||||
JSObject *desc = NewObject(cx, &js_ObjectClass, NULL, NULL);
|
||||
if (!desc)
|
||||
return false;
|
||||
*vp = OBJECT_TO_JSVAL(desc); /* Root and return. */
|
||||
|
||||
const JSAtomState &atomState = cx->runtime->atomState;
|
||||
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
||||
if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), getter,
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) ||
|
||||
!desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), setter,
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), value,
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) ||
|
||||
!desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom),
|
||||
BOOLEAN_TO_JSVAL((attrs & JSPROP_READONLY) == 0),
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom),
|
||||
BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0),
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) &&
|
||||
desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom),
|
||||
BOOLEAN_TO_JSVAL((attrs & JSPROP_PERMANENT) == 0),
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
if (obj->isProxy()) {
|
||||
if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, vp))
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &pobj, &prop))
|
||||
|
@ -1773,7 +1828,7 @@ js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
jsval roots[] = { JSVAL_VOID, JSVAL_VOID };
|
||||
jsval roots[] = { JSVAL_VOID, JSVAL_VOID, JSVAL_VOID };
|
||||
AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
|
||||
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
||||
if (obj->isNative()) {
|
||||
|
@ -1788,41 +1843,16 @@ js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|||
} else {
|
||||
pobj->dropProperty(cx, prop);
|
||||
|
||||
if (!obj->getProperty(cx, id, &roots[0]))
|
||||
if (!obj->getProperty(cx, id, &roots[2]))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* We have our own property, so start creating the descriptor. */
|
||||
JSObject *desc = NewObject(cx, &js_ObjectClass, NULL, NULL);
|
||||
if (!desc)
|
||||
return false;
|
||||
*vp = OBJECT_TO_JSVAL(desc); /* Root and return. */
|
||||
|
||||
const JSAtomState &atomState = cx->runtime->atomState;
|
||||
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
||||
if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), roots[0],
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) ||
|
||||
!desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), roots[1],
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), roots[0],
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) ||
|
||||
!desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom),
|
||||
BOOLEAN_TO_JSVAL((attrs & JSPROP_READONLY) == 0),
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom),
|
||||
BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0),
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) &&
|
||||
desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom),
|
||||
BOOLEAN_TO_JSVAL((attrs & JSPROP_PERMANENT) == 0),
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE);
|
||||
return js_NewPropertyDescriptorObject(cx, id,
|
||||
attrs,
|
||||
roots[0], /* getter */
|
||||
roots[1], /* setter */
|
||||
roots[2], /* value */
|
||||
vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -1849,6 +1879,7 @@ obj_keys(JSContext *cx, uintN argc, jsval *vp)
|
|||
}
|
||||
|
||||
JSObject *obj = JSVAL_TO_OBJECT(v);
|
||||
|
||||
AutoIdArray ida(cx, JS_Enumerate(cx, obj));
|
||||
if (!ida)
|
||||
return JS_FALSE;
|
||||
|
@ -1902,7 +1933,8 @@ HasProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp, JSBool* answerp)
|
|||
}
|
||||
|
||||
PropertyDescriptor::PropertyDescriptor()
|
||||
: id(INT_JSVAL_TO_JSID(JSVAL_ZERO)),
|
||||
: pd(JSVAL_VOID),
|
||||
id(INT_JSVAL_TO_JSID(JSVAL_ZERO)),
|
||||
value(JSVAL_VOID),
|
||||
get(JSVAL_VOID),
|
||||
set(JSVAL_VOID),
|
||||
|
@ -1928,6 +1960,9 @@ PropertyDescriptor::initialize(JSContext* cx, jsid id, jsval v)
|
|||
}
|
||||
JSObject* desc = JSVAL_TO_OBJECT(v);
|
||||
|
||||
/* Make a copy of the descriptor. We might need it later. */
|
||||
pd = v;
|
||||
|
||||
/* Start with the proper defaults. */
|
||||
attrs = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
|
||||
|
@ -2375,8 +2410,11 @@ DefineProperty(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc, boo
|
|||
if (obj->isArray())
|
||||
return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
|
||||
|
||||
if (obj->map->ops->lookupProperty != js_LookupProperty)
|
||||
if (obj->map->ops->lookupProperty != js_LookupProperty) {
|
||||
if (obj->isProxy())
|
||||
return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
|
||||
return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
|
||||
}
|
||||
|
||||
return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
|
||||
}
|
||||
|
@ -2422,6 +2460,41 @@ obj_defineProperty(JSContext* cx, uintN argc, jsval* vp)
|
|||
return js_DefineOwnProperty(cx, obj, nameidr.id(), descval, &junk);
|
||||
}
|
||||
|
||||
static bool
|
||||
DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
|
||||
{
|
||||
AutoIdArray ida(cx, JS_Enumerate(cx, props));
|
||||
if (!ida)
|
||||
return false;
|
||||
|
||||
AutoDescriptorArray descs(cx);
|
||||
size_t len = ida.length();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
jsid id = ida[i];
|
||||
PropertyDescriptor* desc = descs.append();
|
||||
AutoValueRooter tvr(cx);
|
||||
if (!desc ||
|
||||
!JS_GetPropertyById(cx, props, id, tvr.addr()) ||
|
||||
!desc->initialize(cx, id, tvr.value())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool dummy;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!DefineProperty(cx, obj, descs[i], true, &dummy))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
|
||||
{
|
||||
return DefineProperties(cx, newborn, props);
|
||||
}
|
||||
|
||||
/* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
|
||||
static JSBool
|
||||
obj_defineProperties(JSContext* cx, uintN argc, jsval* vp)
|
||||
|
@ -2430,43 +2503,23 @@ obj_defineProperties(JSContext* cx, uintN argc, jsval* vp)
|
|||
if (argc < 2) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
|
||||
"Object.defineProperties", "0", "s");
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
*vp = vp[2];
|
||||
if (JSVAL_IS_PRIMITIVE(vp[2])) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
|
||||
if (!props)
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
vp[3] = OBJECT_TO_JSVAL(props);
|
||||
|
||||
AutoIdArray ida(cx, JS_Enumerate(cx, props));
|
||||
if (!ida)
|
||||
return JS_FALSE;
|
||||
|
||||
AutoDescriptorArray descs(cx);
|
||||
size_t len = ida.length();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
jsid id = ida[i];
|
||||
PropertyDescriptor* desc = descs.append();
|
||||
if (!desc || !JS_GetPropertyById(cx, props, id, &vp[1]) ||
|
||||
!desc->initialize(cx, id, vp[1])) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
JSObject *obj = JSVAL_TO_OBJECT(*vp);
|
||||
bool dummy;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!DefineProperty(cx, obj, descs[i], true, &dummy))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
return DefineProperties(cx, obj, props);
|
||||
}
|
||||
|
||||
/* ES5 15.2.3.5: Object.create(O [, Properties]) */
|
||||
|
@ -3127,6 +3180,32 @@ js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index)
|
|||
JSScopeProperty::HAS_SHORTID, index, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this method with extreme caution. It trades the guts of two objects and updates
|
||||
* scope ownership. This operation is not thread-safe, just as fast array to slow array
|
||||
* transitions are inherently not thread-safe. Don't perform a swap operation on objects
|
||||
* shared across threads or, or bad things will happen. You have been warned.
|
||||
*/
|
||||
void
|
||||
JSObject::swap(JSObject *other)
|
||||
{
|
||||
/* For both objects determine whether they own their respective scopes. */
|
||||
bool thisOwns = this->isNative() && scope()->object == this;
|
||||
bool otherOwns = other->isNative() && other->scope()->object == other;
|
||||
|
||||
/* Trade the guts of the objects. */
|
||||
JSObject tmp;
|
||||
memcpy(&tmp, this, sizeof(JSObject));
|
||||
memcpy(this, other, sizeof(JSObject));
|
||||
memcpy(other, &tmp, sizeof(JSObject));
|
||||
|
||||
/* Fixup scope ownerships. */
|
||||
if (otherOwns)
|
||||
scope()->object = this;
|
||||
if (thisOwns)
|
||||
other->scope()->object = other;
|
||||
}
|
||||
|
||||
#if JS_HAS_XDR
|
||||
|
||||
#define NO_PARENT_INDEX ((uint32)-1)
|
||||
|
@ -6166,15 +6245,6 @@ JSObject::getGlobal()
|
|||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::isCallable()
|
||||
{
|
||||
if (isNative())
|
||||
return isFunction() || getClass()->call;
|
||||
|
||||
return !!map->ops->call;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_ReportGetterOnlyAssignment(JSContext *cx)
|
||||
{
|
||||
|
|
|
@ -62,10 +62,9 @@ namespace js { class AutoDescriptorArray; }
|
|||
struct PropertyDescriptor {
|
||||
friend class js::AutoDescriptorArray;
|
||||
|
||||
private:
|
||||
PropertyDescriptor();
|
||||
|
||||
public:
|
||||
|
||||
/* 8.10.5 ToPropertyDescriptor(Obj) */
|
||||
bool initialize(JSContext* cx, jsid id, jsval v);
|
||||
|
||||
|
@ -120,6 +119,7 @@ struct PropertyDescriptor {
|
|||
static void traceDescriptorArray(JSTracer* trc, JSObject* obj);
|
||||
static void finalizeDescriptorArray(JSContext* cx, JSObject* obj);
|
||||
|
||||
jsval pd;
|
||||
jsid id;
|
||||
jsval value, get, set;
|
||||
|
||||
|
@ -578,11 +578,19 @@ struct JSObject {
|
|||
inline jsval getQNameLocalName() const;
|
||||
inline void setQNameLocalName(jsval decl);
|
||||
|
||||
/*
|
||||
* Proxy-specific getters and setters.
|
||||
*/
|
||||
|
||||
inline jsval getProxyHandler() const;
|
||||
inline jsval getProxyPrivate() const;
|
||||
inline void setProxyPrivate(jsval priv);
|
||||
|
||||
/*
|
||||
* Back to generic stuff.
|
||||
*/
|
||||
|
||||
bool isCallable();
|
||||
inline bool isCallable();
|
||||
|
||||
/* The map field is not initialized here and should be set separately. */
|
||||
void init(JSClass *clasp, JSObject *proto, JSObject *parent,
|
||||
|
@ -678,6 +686,8 @@ struct JSObject {
|
|||
map->ops->dropProperty(cx, this, prop);
|
||||
}
|
||||
|
||||
void swap(JSObject *obj);
|
||||
|
||||
inline bool isArguments() const;
|
||||
inline bool isArray() const;
|
||||
inline bool isDenseArray() const;
|
||||
|
@ -693,6 +703,10 @@ struct JSObject {
|
|||
inline bool isNamespace() const;
|
||||
inline bool isQName() const;
|
||||
|
||||
inline bool isProxy() const;
|
||||
inline bool isObjectProxy() const;
|
||||
inline bool isFunctionProxy() const;
|
||||
|
||||
inline bool unbrand(JSContext *cx);
|
||||
};
|
||||
|
||||
|
@ -867,6 +881,9 @@ extern JSBool
|
|||
js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
|
||||
JSObject **objp, JSProperty **propp);
|
||||
|
||||
extern JSBool
|
||||
js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs, jsval getter, jsval setter, jsval value, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
|
||||
|
||||
|
@ -919,6 +936,9 @@ extern JSObject*
|
|||
js_NewObjectWithClassProto(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
jsval privateSlotValue);
|
||||
|
||||
extern JSBool
|
||||
js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props);
|
||||
|
||||
/*
|
||||
* Fast access to immutable standard objects (constructors and prototypes).
|
||||
*/
|
||||
|
@ -1281,11 +1301,6 @@ extern const char *
|
|||
js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
|
||||
JSPrincipals *principals, uintN *linenop);
|
||||
|
||||
static inline bool
|
||||
js_IsCallable(jsval v) {
|
||||
return !JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isCallable();
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
js_ReportGetterOnlyAssignment(JSContext *cx);
|
||||
|
||||
|
|
|
@ -500,6 +500,21 @@ JSObject::unbrand(JSContext *cx)
|
|||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::isCallable()
|
||||
{
|
||||
if (isNative())
|
||||
return isFunction() || getClass()->call;
|
||||
|
||||
return !!map->ops->call;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
js_IsCallable(jsval v)
|
||||
{
|
||||
return !JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isCallable();
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
typedef Vector<PropertyDescriptor, 1> PropertyDescriptorArray;
|
||||
|
@ -528,6 +543,19 @@ class AutoDescriptorArray : private AutoGCRooter
|
|||
PropertyDescriptorArray descriptors;
|
||||
};
|
||||
|
||||
class AutoDescriptor : private AutoGCRooter, public JSPropertyDescriptor
|
||||
{
|
||||
public:
|
||||
AutoDescriptor(JSContext *cx) : AutoGCRooter(cx, DESCRIPTOR) {
|
||||
obj = NULL;
|
||||
attrs = 0;
|
||||
getter = setter = (JSPropertyOp) NULL;
|
||||
value = JSVAL_VOID;
|
||||
}
|
||||
|
||||
friend void AutoGCRooter::trace(JSTracer *trc);
|
||||
};
|
||||
|
||||
static inline bool
|
||||
InitScopeForObject(JSContext* cx, JSObject* obj, JSClass *clasp, JSObject* proto, JSObjectOps* ops)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "jspropertytree.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
#include "jsnum.h"
|
||||
#include "jsscopeinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,232 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
|
||||
* May 28, 2008.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Andreas Gal <gal@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef jsproxy_h___
|
||||
#define jsproxy_h___
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsobj.h"
|
||||
|
||||
/* Base class for all C++ proxy handlers. */
|
||||
class JSProxyHandler {
|
||||
public:
|
||||
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 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;
|
||||
virtual bool fix(JSContext *cx, JSObject *proxy, jsval *vp) = 0;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual void finalize(JSContext *cx, JSObject *proxy);
|
||||
virtual void *family() = 0;
|
||||
};
|
||||
|
||||
/* No-op wrapper handler base class. */
|
||||
class JSNoopProxyHandler {
|
||||
JSObject *mWrappedObject;
|
||||
|
||||
protected:
|
||||
JS_FRIEND_API(JSNoopProxyHandler(JSObject *));
|
||||
|
||||
public:
|
||||
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) 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);
|
||||
|
||||
/* 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) enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual JS_FRIEND_API(void) finalize(JSContext *cx, JSObject *proxy);
|
||||
virtual JS_FRIEND_API(void) *family();
|
||||
|
||||
static JSNoopProxyHandler singleton;
|
||||
|
||||
template <class T>
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
/* Dispatch point for handlers that executes the appropriate C++ or scripted traps. */
|
||||
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, jsval *vp);
|
||||
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);
|
||||
static bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, JSIdArray **idap);
|
||||
static bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
|
||||
static bool enumerate(JSContext *cx, JSObject *proxy, JSIdArray **idap);
|
||||
static bool fix(JSContext *cx, JSObject *proxy, jsval *vp);
|
||||
|
||||
/* ES5 Harmony derived proxy traps. */
|
||||
static bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
|
||||
static bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
|
||||
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);
|
||||
};
|
||||
|
||||
/* Shared between object and function proxies. */
|
||||
const uint32 JSSLOT_PROXY_HANDLER = JSSLOT_PRIVATE + 0;
|
||||
/* Object proxies only. */
|
||||
const uint32 JSSLOT_PROXY_CLASS = JSSLOT_PRIVATE + 1;
|
||||
const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 2;
|
||||
/* Function proxies only. */
|
||||
const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 1;
|
||||
const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 2;
|
||||
|
||||
extern JS_FRIEND_API(JSClass) js_ObjectProxyClass;
|
||||
extern JS_FRIEND_API(JSClass) js_FunctionProxyClass;
|
||||
extern JSClass js_CallableObjectClass;
|
||||
|
||||
inline bool
|
||||
JSObject::isObjectProxy() const
|
||||
{
|
||||
return getClass() == &js_ObjectProxyClass;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::isFunctionProxy() const
|
||||
{
|
||||
return getClass() == &js_FunctionProxyClass;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::isProxy() const
|
||||
{
|
||||
return isObjectProxy() || isFunctionProxy();
|
||||
}
|
||||
|
||||
inline jsval
|
||||
JSObject::getProxyHandler() const
|
||||
{
|
||||
JS_ASSERT(isProxy());
|
||||
jsval handler = fslots[JSSLOT_PROXY_HANDLER];
|
||||
JS_ASSERT(JSVAL_IS_OBJECT(handler) || JSVAL_IS_INT(handler));
|
||||
return handler;
|
||||
}
|
||||
|
||||
inline jsval
|
||||
JSObject::getProxyPrivate() const
|
||||
{
|
||||
JS_ASSERT(isObjectProxy());
|
||||
return fslots[JSSLOT_PROXY_PRIVATE];
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setProxyPrivate(jsval priv)
|
||||
{
|
||||
JS_ASSERT(isObjectProxy());
|
||||
fslots[JSSLOT_PROXY_PRIVATE] = priv;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className);
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSObject *call, JSObject *construct);
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep);
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_Becomes(JSContext *cx, JSObject *obj, JSObject *obj2);
|
||||
|
||||
template <class T>
|
||||
JSObject *
|
||||
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 = JS_NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent, obj, NULL);
|
||||
if (!wrapper)
|
||||
delete handler;
|
||||
return wrapper;
|
||||
}
|
||||
JSObject *wrapper = JS_NewObjectProxy(cx, PRIVATE_TO_JSVAL(&T::singleton), proto, parent, className);
|
||||
if (wrapper)
|
||||
wrapper->setProxyPrivate(OBJECT_TO_JSVAL(obj));
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
js_InitProxyClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
#endif
|
|
@ -675,13 +675,7 @@ struct JSScopeProperty {
|
|||
};
|
||||
|
||||
JSScopeProperty(jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
|
||||
uintN attrs, uintN flags, intN shortid)
|
||||
: id(id), rawGetter(getter), rawSetter(setter), slot(slot), attrs(uint8(attrs)),
|
||||
flags(uint8(flags)), shortid(int16(shortid))
|
||||
{
|
||||
JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER), getterObj->isCallable());
|
||||
JS_ASSERT_IF(setter && (attrs & JSPROP_SETTER), setterObj->isCallable());
|
||||
}
|
||||
uintN attrs, uintN flags, intN shortid);
|
||||
|
||||
bool marked() const { return (flags & MARK) != 0; }
|
||||
void mark() { flags |= MARK; }
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
#include "jsobj.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
inline JSEmptyScope *
|
||||
JSScope::createEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
{
|
||||
|
@ -209,6 +211,16 @@ JSScope::trace(JSTracer *trc)
|
|||
}
|
||||
}
|
||||
|
||||
inline
|
||||
JSScopeProperty::JSScopeProperty(jsid id, JSPropertyOp getter, JSPropertyOp setter,
|
||||
uint32 slot, uintN attrs, uintN flags, intN shortid)
|
||||
: id(id), rawGetter(getter), rawSetter(setter), slot(slot), attrs(uint8(attrs)),
|
||||
flags(uint8(flags)), shortid(int16(shortid))
|
||||
{
|
||||
JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER), getterObj->isCallable());
|
||||
JS_ASSERT_IF(setter && (attrs & JSPROP_SETTER), setterObj->isCallable());
|
||||
}
|
||||
|
||||
inline JSDHashNumber
|
||||
JSScopeProperty::hash() const
|
||||
{
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "jsxdrapi.h"
|
||||
#endif
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
#include "jsstr.h"
|
||||
#include "jsxdrapi.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x) x
|
||||
#else
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "jsarena.h"
|
||||
#include "jsutil.h"
|
||||
#include "jsprf.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsarray.h"
|
||||
#include "jsatom.h"
|
||||
|
@ -86,6 +87,8 @@
|
|||
|
||||
#include "jsworkers.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#ifdef XP_UNIX
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -3858,6 +3861,23 @@ Snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
Wrap(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
|
||||
if (JSVAL_IS_PRIMITIVE(v)) {
|
||||
JS_SET_RVAL(cx, vp, v);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *wrapped = JSNoopProxyHandler::wrap<JSNoopProxyHandler>(cx, JSVAL_TO_OBJECT(v), NULL, NULL, NULL);
|
||||
if (!wrapped)
|
||||
return false;
|
||||
|
||||
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
|
||||
static JSFunctionSpec shell_functions[] = {
|
||||
JS_FS("version", Version, 0,0,0),
|
||||
|
@ -3942,6 +3962,7 @@ static JSFunctionSpec shell_functions[] = {
|
|||
JS_FN("timeout", Timeout, 1,0),
|
||||
JS_FN("elapsed", Elapsed, 0,0),
|
||||
JS_FN("parent", Parent, 1,0),
|
||||
JS_FN("wrap", Wrap, 1,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
@ -4051,6 +4072,7 @@ static const char *const shell_help_messages[] = {
|
|||
" A negative value (default) means that the execution time is unlimited.",
|
||||
"elapsed() Execution time elapsed for the current context.",
|
||||
"parent(obj) Returns the parent of obj.\n",
|
||||
"wrap(obj) Wrap an object into a noop wrapper.\n"
|
||||
};
|
||||
|
||||
/* Help messages must match shell functions. */
|
||||
|
|
|
@ -14,3 +14,4 @@ include js1_7/jstests.list
|
|||
include js1_8/jstests.list
|
||||
include js1_8_1/jstests.list
|
||||
include js1_8_5/jstests.list
|
||||
include proxies/jstests.list
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
url-prefix ../../jsreftest.html?test=proxies/
|
||||
script scripted.js
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
* Contributor: Andreas Gal
|
||||
*/
|
||||
|
||||
/* Test object proxies. */
|
||||
|
||||
function noopHandlerMaker(obj) {
|
||||
return {
|
||||
getOwnPropertyDescriptor: function(name) {
|
||||
var desc = Object.getOwnPropertyDescriptor(obj);
|
||||
// a trapping proxy's properties must always be configurable
|
||||
desc.configurable = true;
|
||||
return desc;
|
||||
},
|
||||
getPropertyDescriptor: function(name) {
|
||||
var desc = Object.getPropertyDescriptor(obj); // assumed
|
||||
// a trapping proxy's properties must always be configurable
|
||||
desc.configurable = true;
|
||||
return desc;
|
||||
},
|
||||
getOwnPropertyNames: function() {
|
||||
return Object.getOwnPropertyNames(obj);
|
||||
},
|
||||
defineProperty: function(name, desc) {
|
||||
return Object.defineProperty(obj, name, desc);
|
||||
},
|
||||
delete: function(name) { return delete obj[name]; },
|
||||
fix: function() {
|
||||
// As long as obj is not frozen, the proxy won't allow itself to be fixed
|
||||
// if (!Object.isFrozen(obj)) [not implemented in SpiderMonkey]
|
||||
// return undefined;
|
||||
// return Object.getOwnProperties(obj); // assumed [not implemented in SpiderMonkey]
|
||||
var props = {};
|
||||
for (x in obj)
|
||||
props[x] = Object.getOwnPropertyDescriptor(obj, x);
|
||||
return props;
|
||||
},
|
||||
has: function(name) { return name in obj; },
|
||||
hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); },
|
||||
get: function(receiver, name) { return obj[name]; },
|
||||
set: function(receiver, name, val) { obj[name] = val; return true; }, // bad behavior when set fails in non-strict mode
|
||||
enumerate: function() {
|
||||
var result = [];
|
||||
for (name in obj) { result.push(name); };
|
||||
return result;
|
||||
},
|
||||
enumerateOwn: function() { return Object.keys(obj); }
|
||||
};
|
||||
};
|
||||
|
||||
function testNoopHandler(obj, proxy) {
|
||||
/* Check that both objects see the same properties. */
|
||||
for (x in obj)
|
||||
assertEq(obj[x], proxy[x]);
|
||||
for (x in proxy)
|
||||
assertEq(obj[x], proxy[x]);
|
||||
/* Check that the iteration order is the same. */
|
||||
var a = [], b = [];
|
||||
for (x in obj)
|
||||
a.push(x);
|
||||
for (x in proxy)
|
||||
b.push(x);
|
||||
assertEq(uneval(a), uneval(b));
|
||||
}
|
||||
|
||||
function testObj(obj) {
|
||||
var proxy = Proxy.create(noopHandlerMaker(obj));
|
||||
testNoopHandler(obj, proxy);
|
||||
assertEq(typeof proxy, "object");
|
||||
if ("isTrapping" in Proxy) {
|
||||
assertEq(Proxy.isTrapping(proxy), true);
|
||||
assertEq(Proxy.fix(proxy), true);
|
||||
assertEq(Proxy.isTrapping(proxy), false);
|
||||
assertEq(typeof proxy, "object");
|
||||
testNoopHandler(obj, proxy);
|
||||
}
|
||||
}
|
||||
|
||||
testObj({ foo: 1, bar: 2 });
|
||||
testObj({ 1: 2, 3: 4 });
|
||||
testObj([ 1, 2, 3 ]);
|
||||
testObj(new Date());
|
||||
testObj(new Array());
|
||||
testObj(new RegExp());
|
||||
testObj(Date);
|
||||
testObj(Array);
|
||||
testObj(RegExp);
|
||||
|
||||
reportCompare(0, 0, "Proxies: Object proxies.");
|
||||
|
||||
/* Test function proxies. */
|
||||
|
||||
var proxy = Proxy.createFunction({
|
||||
get: function(obj,name) { return Function.prototype[name]; },
|
||||
fix: function() {
|
||||
return ({});
|
||||
}
|
||||
}, function() { return "call"; });
|
||||
|
||||
assertEq(proxy(), "call");
|
||||
assertEq(typeof proxy, "function");
|
||||
if ("isTrapping" in Proxy) {
|
||||
assertEq(Proxy.isTrapping(proxy), true);
|
||||
assertEq(Proxy.fix(proxy), true);
|
||||
assertEq(Proxy.isTrapping(proxy), false);
|
||||
assertEq(typeof proxy, "function");
|
||||
assertEq(proxy(), "call");
|
||||
}
|
||||
|
||||
reportCompare(0, 0, "Proxies: Function proxies.");
|
||||
|
||||
/* Test function proxies as constructors. */
|
||||
|
||||
var proxy = Proxy.createFunction({
|
||||
get: function(obj, name) { return Function.prototype[name]; },
|
||||
fix: function() { return ({}); }
|
||||
},
|
||||
function() { var x = {}; x.origin = "call"; return x; },
|
||||
function() { var x = {}; x.origin = "new"; return x; })
|
||||
|
||||
assertEq(proxy().origin, "call");
|
||||
assertEq((new proxy()).origin, "new");
|
||||
if ("fix" in Proxy) {
|
||||
assertEq(Proxy.fix(proxy), true);
|
||||
assertEq(proxy().origin, "call");
|
||||
assertEq((new proxy()).origin, "new");
|
||||
}
|
||||
|
||||
reportCompare(0, 0, "Proxies: Function proxies as constructor.");
|
||||
|
||||
/* Test fallback on call if no construct trap was given. */
|
||||
|
||||
var proxy = Proxy.createFunction({
|
||||
get: function(obj, name) { return Function.prototype[name]; },
|
||||
fix: function() { return ({}); }
|
||||
},
|
||||
function() { this.origin = "new"; return "new-ret"; });
|
||||
|
||||
assertEq((new proxy()).origin, "new");
|
||||
if ("fix" in Proxy) {
|
||||
assertEq(Proxy.fix(proxy), true);
|
||||
assertEq((new proxy()).origin, "new");
|
||||
}
|
||||
|
||||
reportCompare(0, 0, "Proxies: No constructor trap supplied.");
|
||||
|
||||
/* Test invoke. */
|
||||
|
||||
var proxy = Proxy.create({ get: function(obj,name) { return function(a,b,c) { return name + uneval([a,b,c]); } }});
|
||||
assertEq(proxy.foo(1,2,3), "foo[1, 2, 3]");
|
||||
|
||||
reportCompare(0, 0, "Proxies: Test invoke.");
|
Загрузка…
Ссылка в новой задаче