Implement ES Harmony Proxies (bug 546590, r=mrbkap).

This commit is contained in:
Andreas Gal 2010-05-18 19:21:43 -07:00
Родитель 9990eee027
Коммит 11fd8abaee
27 изменённых файлов: 1909 добавлений и 124 удалений

Просмотреть файл

@ -152,6 +152,7 @@ CPPSRCS = \
json.cpp \ json.cpp \
jsopcode.cpp \ jsopcode.cpp \
jsparse.cpp \ jsparse.cpp \
jsproxy.cpp \
jsprf.cpp \ jsprf.cpp \
jspropertycache.cpp \ jspropertycache.cpp \
jspropertytree.cpp \ jspropertytree.cpp \
@ -208,6 +209,7 @@ INSTALLED_HEADERS = \
jsopcode.h \ jsopcode.h \
jsotypes.h \ jsotypes.h \
jsparse.h \ jsparse.h \
jsproxy.h \
jsprf.h \ jsprf.h \
jspropertycache.h \ jspropertycache.h \
jspropertycacheinlines.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_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_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_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 "jsobj.h"
#include "jsopcode.h" #include "jsopcode.h"
#include "jsparse.h" #include "jsparse.h"
#include "jsproxy.h"
#include "jsregexp.h" #include "jsregexp.h"
#include "jsscan.h" #include "jsscan.h"
#include "jsscope.h" #include "jsscope.h"
@ -1213,7 +1214,8 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
#if JS_HAS_GENERATORS #if JS_HAS_GENERATORS
js_InitIteratorClasses(cx, obj) && js_InitIteratorClasses(cx, obj) &&
#endif #endif
js_InitDateClass(cx, obj); js_InitDateClass(cx, obj) &&
js_InitProxyClass(cx, obj);
} }
#define CLASP(name) (&js_##name##Class) #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(Float64Array), NULL},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray), NULL}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray), NULL},
{js_InitProxyClass, EAGER_ATOM(Proxy), NULL},
{NULL, 0, NULL, NULL} {NULL, 0, NULL, NULL}
}; };
@ -1502,13 +1506,15 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
return JS_TRUE; return JS_TRUE;
} }
static JSIdArray * namespace js {
JSIdArray *
NewIdArray(JSContext *cx, jsint length) NewIdArray(JSContext *cx, jsint length)
{ {
JSIdArray *ida; JSIdArray *ida;
ida = (JSIdArray *) ida = (JSIdArray *)
cx->malloc(offsetof(JSIdArray, vector) + length * sizeof(jsval)); cx->calloc(offsetof(JSIdArray, vector) + length * sizeof(jsval));
if (ida) { if (ida) {
ida->self = ida; ida->self = ida;
ida->length = length; ida->length = length;
@ -1516,6 +1522,8 @@ NewIdArray(JSContext *cx, jsint length)
return ida; return ida;
} }
}
/* /*
* Unlike realloc(3), this function frees ida on failure. * Unlike realloc(3), this function frees ida on failure.
*/ */
@ -1524,13 +1532,13 @@ SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
{ {
JSIdArray *rida; JSIdArray *rida;
JS_ASSERT(ida->self == ida);
rida = (JSIdArray *) rida = (JSIdArray *)
JS_realloc(cx, ida, JS_realloc(cx, ida,
offsetof(JSIdArray, vector) + length * sizeof(jsval)); offsetof(JSIdArray, vector) + length * sizeof(jsval));
if (!rida) { if (!rida) {
JS_DestroyIdArray(cx, ida); JS_DestroyIdArray(cx, ida);
} else { } else {
rida->self = rida;
rida->length = length; rida->length = length;
} }
return rida; return rida;
@ -3120,6 +3128,12 @@ GetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, uintN flags,
? obj2->lockedGetSlot(sprop->slot) ? obj2->lockedGetSlot(sprop->slot)
: JSVAL_VOID; : JSVAL_VOID;
} else { } 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->getter = NULL;
desc->setter = NULL; desc->setter = NULL;
desc->value = JSVAL_VOID; desc->value = JSVAL_VOID;

Просмотреть файл

@ -284,6 +284,8 @@ BOOLEAN_TO_JSVAL(JSBool b)
object that delegates to a prototype object that delegates to a prototype
containing this property */ containing this property */
#define JSPROP_INDEX 0x80 /* name is actually (jsint) index */ #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. */ /* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */
#define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */ #define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */
@ -1821,6 +1823,7 @@ struct JSPropertyDescriptor {
uintN attrs; uintN attrs;
JSPropertyOp getter; JSPropertyOp getter;
JSPropertyOp setter; JSPropertyOp setter;
uintN shortid;
jsval value; jsval value;
}; };

Просмотреть файл

@ -182,6 +182,20 @@ const char *const js_common_atom_names[] = {
js_ExecutionContext_str, /* ExecutionContextAtom */ js_ExecutionContext_str, /* ExecutionContextAtom */
js_current_str, /* currentAtom */ js_current_str, /* currentAtom */
#endif #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 *) == JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==

Просмотреть файл

@ -295,6 +295,20 @@ struct JSAtomState {
JSAtom *currentAtom; JSAtom *currentAtom;
#endif #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. */ /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */
struct { struct {
JSAtom *InfinityAtom; JSAtom *InfinityAtom;
@ -325,6 +339,8 @@ struct JSAtomState {
} lazy; } lazy;
}; };
#define ATOM(name) cx->runtime->atomState.name##Atom
#define ATOM_OFFSET_START offsetof(JSAtomState, emptyAtom) #define ATOM_OFFSET_START offsetof(JSAtomState, emptyAtom)
#define LAZY_ATOM_OFFSET_START offsetof(JSAtomState, lazy) #define LAZY_ATOM_OFFSET_START offsetof(JSAtomState, lazy)
#define ATOM_OFFSET_LIMIT (sizeof(JSAtomState)) #define ATOM_OFFSET_LIMIT (sizeof(JSAtomState))

Просмотреть файл

@ -2174,7 +2174,8 @@ class AutoGCRooter {
XML = -10, /* js::AutoXMLRooter */ XML = -10, /* js::AutoXMLRooter */
OBJECT = -11, /* js::AutoObjectRooter */ OBJECT = -11, /* js::AutoObjectRooter */
ID = -12, /* js::AutoIdRooter */ ID = -12, /* js::AutoIdRooter */
VECTOR = -13 /* js::AutoValueVector */ VECTOR = -13, /* js::AutoValueVector */
DESCRIPTOR = -14 /* js::AutoDescriptor */
}; };
private: private:
@ -2404,11 +2405,17 @@ class AutoIdArray : private AutoGCRooter {
friend void AutoGCRooter::trace(JSTracer *trc); friend void AutoGCRooter::trace(JSTracer *trc);
JSIdArray *steal() {
JSIdArray *copy = idArray;
idArray = NULL;
return copy;
}
protected: protected:
inline void trace(JSTracer *trc); inline void trace(JSTracer *trc);
private: private:
JSIdArray * const idArray; JSIdArray * idArray;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER JS_DECL_USE_GUARD_OBJECT_NOTIFIER
/* No copy or assignment semantics. */ /* No copy or assignment semantics. */
@ -3023,6 +3030,9 @@ class AutoValueVector : private AutoGCRooter
JS_DECL_USE_GUARD_OBJECT_NOTIFIER JS_DECL_USE_GUARD_OBJECT_NOTIFIER
}; };
JSIdArray *
NewIdArray(JSContext *cx, jsint length);
} }
#ifdef _MSC_VER #ifdef _MSC_VER

Просмотреть файл

@ -235,6 +235,7 @@ AutoGCRooter::trace(JSTracer *trc)
for (size_t i = 0, len = descriptors.length(); i < len; i++) { for (size_t i = 0, len = descriptors.length(); i < len; i++) {
PropertyDescriptor &desc = descriptors[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.value, "PropertyDescriptor::value");
JS_CALL_VALUE_TRACER(trc, desc.get, "PropertyDescriptor::get"); JS_CALL_VALUE_TRACER(trc, desc.get, "PropertyDescriptor::get");
JS_CALL_VALUE_TRACER(trc, desc.set, "PropertyDescriptor::set"); JS_CALL_VALUE_TRACER(trc, desc.set, "PropertyDescriptor::set");
@ -243,6 +244,17 @@ AutoGCRooter::trace(JSTracer *trc)
return; 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: { case NAMESPACES: {
JSXMLArray &array = static_cast<AutoNamespaces *>(this)->array; JSXMLArray &array = static_cast<AutoNamespaces *>(this)->array;
TraceObjectVector(trc, reinterpret_cast<JSObject **>(array.vector), array.length); TraceObjectVector(trc, reinterpret_cast<JSObject **>(array.vector), array.length);

Просмотреть файл

@ -73,6 +73,7 @@
#include "jsnum.h" #include "jsnum.h"
#include "jsobj.h" #include "jsobj.h"
#include "jsparse.h" #include "jsparse.h"
#include "jsproxy.h"
#include "jsscope.h" #include "jsscope.h"
#include "jsscript.h" #include "jsscript.h"
#include "jsstaticcheck.h" #include "jsstaticcheck.h"
@ -2384,6 +2385,12 @@ FinalizeObject(JSContext *cx, JSObject *obj, unsigned thingKind)
static_cast<JSEmptyScope *>(scope)->dropFromGC(cx); static_cast<JSEmptyScope *>(scope)->dropFromGC(cx);
else else
scope->destroy(cx); 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()) if (obj->hasSlotsArray())
obj->freeSlotsArray(cx); obj->freeSlotsArray(cx);

Просмотреть файл

@ -63,6 +63,7 @@
#include "jsnum.h" #include "jsnum.h"
#include "jsobj.h" #include "jsobj.h"
#include "jsopcode.h" #include "jsopcode.h"
#include "jsproxy.h"
#include "jsscan.h" #include "jsscan.h"
#include "jsscope.h" #include "jsscope.h"
#include "jsscript.h" #include "jsscript.h"
@ -163,7 +164,7 @@ Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
if (pobj->getProto() && !ht.add(p, id)) if (pobj->getProto() && !ht.add(p, id))
return false; return false;
} }
if (enumerable) { if (enumerable || (flags & JSITER_HIDDEN)) {
if (!vec.append(ID_TO_VALUE(id))) { if (!vec.append(ID_TO_VALUE(id))) {
JS_ReportOutOfMemory(cx); JS_ReportOutOfMemory(cx);
return false; return false;
@ -232,6 +233,32 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint
return true; 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 static bool
InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength, InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, uint32 slength,
uint32 key, NativeIterator **nip) 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)) if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props))
return false; return false;
} else { } 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; jsval state;
if (!pobj->enumerate(cx, JSENUMERATE_INIT, &state, NULL)) if (!pobj->enumerate(cx, JSENUMERATE_INIT, &state, NULL))
return false; return false;
@ -281,37 +325,12 @@ InitNativeIterator(JSContext *cx, JSObject *obj, uintN flags, uint32 *sarray, ui
pobj = pobj->getProto(); pobj = pobj->getProto();
} }
size_t plength = props.length(); return MakeNativeIterator(cx, flags, sarray, slength, key, props.begin(), props.length(), 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, 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;
} }
bool 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. */ /* Morph the NativeIterator into a JSIdArray. The caller will deallocate it. */
JS_ASSERT(sizeof(NativeIterator) > sizeof(JSIdArray)); JS_ASSERT(sizeof(NativeIterator) > sizeof(JSIdArray));
JS_ASSERT(ni->props_array == (jsid *) (ni + 1)); JS_ASSERT(ni->props_array == (jsid *) (ni + 1));
@ -324,6 +343,33 @@ EnumerateOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap)
return true; 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 static inline bool
GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp) GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
{ {
@ -418,10 +464,12 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp)
} }
miss: miss:
if (!GetCustomIterator(cx, obj, flags, vp)) if (!obj->isProxy()) {
return false; if (!GetCustomIterator(cx, obj, flags, vp))
if (*vp != JSVAL_VOID) return false;
return true; if (*vp != JSVAL_VOID)
return true;
}
} }
JSObject *iterobj = escaping JSObject *iterobj = escaping

Просмотреть файл

@ -58,6 +58,7 @@ JS_BEGIN_EXTERN_C
#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ #define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */
#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ #define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */ #define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
struct NativeIterator { struct NativeIterator {
jsval *props_array; jsval *props_array;
@ -82,6 +83,12 @@ static const jsval JSVAL_NATIVE_ENUMERATE_COOKIE = SPECIAL_TO_JSVAL(0x220576);
bool bool
EnumerateOwnProperties(JSContext *cx, JSObject *obj, JSIdArray **idap); 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 * Convert the value stored in *vp to its iteration object. The flags should
* contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating

Просмотреть файл

@ -69,6 +69,7 @@
#include "jsobj.h" #include "jsobj.h"
#include "jsopcode.h" #include "jsopcode.h"
#include "jsparse.h" #include "jsparse.h"
#include "jsproxy.h"
#include "jsscope.h" #include "jsscope.h"
#include "jsscript.h" #include "jsscript.h"
#include "jsstaticcheck.h" #include "jsstaticcheck.h"
@ -871,8 +872,13 @@ obj_toString(JSContext *cx, uintN argc, jsval *vp)
obj = JS_THIS_OBJECT(cx, vp); obj = JS_THIS_OBJECT(cx, vp);
if (!obj) if (!obj)
return JS_FALSE; return JS_FALSE;
obj = js_GetWrappedObject(cx, obj); if (obj->isProxy()) {
clazz = obj->getClass()->name; 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 ]" */ nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar)); chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
if (!chars) if (!chars)
@ -1472,7 +1478,16 @@ js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
JSObject *obj = JS_THIS_OBJECT(cx, vp); JSObject *obj = JS_THIS_OBJECT(cx, vp);
JSObject *obj2; JSObject *obj2;
JSProperty *prop; 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; return JS_FALSE;
if (prop) { if (prop) {
*vp = JSVAL_TRUE; *vp = JSVAL_TRUE;
@ -1755,9 +1770,49 @@ obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
JSACC_PROTO, vp, &attrs); 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 JSBool
js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp) js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{ {
if (obj->isProxy()) {
if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, vp))
return false;
}
JSObject *pobj; JSObject *pobj;
JSProperty *prop; JSProperty *prop;
if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &pobj, &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; return false;
} }
jsval roots[] = { JSVAL_VOID, JSVAL_VOID }; jsval roots[] = { JSVAL_VOID, JSVAL_VOID, JSVAL_VOID };
AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots); AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
if (obj->isNative()) { if (obj->isNative()) {
@ -1788,41 +1843,16 @@ js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
} else { } else {
pobj->dropProperty(cx, prop); pobj->dropProperty(cx, prop);
if (!obj->getProperty(cx, id, &roots[0])) if (!obj->getProperty(cx, id, &roots[2]))
return false; return false;
} }
return js_NewPropertyDescriptorObject(cx, id,
/* We have our own property, so start creating the descriptor. */ attrs,
JSObject *desc = NewObject(cx, &js_ObjectClass, NULL, NULL); roots[0], /* getter */
if (!desc) roots[1], /* setter */
return false; roots[2], /* value */
*vp = OBJECT_TO_JSVAL(desc); /* Root and return. */ vp);
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);
} }
static JSBool static JSBool
@ -1849,6 +1879,7 @@ obj_keys(JSContext *cx, uintN argc, jsval *vp)
} }
JSObject *obj = JSVAL_TO_OBJECT(v); JSObject *obj = JSVAL_TO_OBJECT(v);
AutoIdArray ida(cx, JS_Enumerate(cx, obj)); AutoIdArray ida(cx, JS_Enumerate(cx, obj));
if (!ida) if (!ida)
return JS_FALSE; return JS_FALSE;
@ -1902,7 +1933,8 @@ HasProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp, JSBool* answerp)
} }
PropertyDescriptor::PropertyDescriptor() PropertyDescriptor::PropertyDescriptor()
: id(INT_JSVAL_TO_JSID(JSVAL_ZERO)), : pd(JSVAL_VOID),
id(INT_JSVAL_TO_JSID(JSVAL_ZERO)),
value(JSVAL_VOID), value(JSVAL_VOID),
get(JSVAL_VOID), get(JSVAL_VOID),
set(JSVAL_VOID), set(JSVAL_VOID),
@ -1928,6 +1960,9 @@ PropertyDescriptor::initialize(JSContext* cx, jsid id, jsval v)
} }
JSObject* desc = JSVAL_TO_OBJECT(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. */ /* Start with the proper defaults. */
attrs = JSPROP_PERMANENT | JSPROP_READONLY; attrs = JSPROP_PERMANENT | JSPROP_READONLY;
@ -2375,8 +2410,11 @@ DefineProperty(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc, boo
if (obj->isArray()) if (obj->isArray())
return DefinePropertyOnArray(cx, obj, desc, throwError, rval); 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 Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
}
return DefinePropertyOnObject(cx, obj, desc, 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); 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) */ /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
static JSBool static JSBool
obj_defineProperties(JSContext* cx, uintN argc, jsval* vp) obj_defineProperties(JSContext* cx, uintN argc, jsval* vp)
@ -2430,43 +2503,23 @@ obj_defineProperties(JSContext* cx, uintN argc, jsval* vp)
if (argc < 2) { if (argc < 2) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
"Object.defineProperties", "0", "s"); "Object.defineProperties", "0", "s");
return JS_FALSE; return false;
} }
*vp = vp[2]; *vp = vp[2];
if (JSVAL_IS_PRIMITIVE(vp[2])) { if (JSVAL_IS_PRIMITIVE(vp[2])) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
return JS_FALSE; return false;
} }
JSObject* props = js_ValueToNonNullObject(cx, vp[3]); JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
if (!props) if (!props)
return JS_FALSE; return false;
vp[3] = OBJECT_TO_JSVAL(props); 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); 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]) */ /* 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); 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 #if JS_HAS_XDR
#define NO_PARENT_INDEX ((uint32)-1) #define NO_PARENT_INDEX ((uint32)-1)
@ -6166,15 +6245,6 @@ JSObject::getGlobal()
return obj; return obj;
} }
bool
JSObject::isCallable()
{
if (isNative())
return isFunction() || getClass()->call;
return !!map->ops->call;
}
JSBool JSBool
js_ReportGetterOnlyAssignment(JSContext *cx) js_ReportGetterOnlyAssignment(JSContext *cx)
{ {

Просмотреть файл

@ -62,10 +62,9 @@ namespace js { class AutoDescriptorArray; }
struct PropertyDescriptor { struct PropertyDescriptor {
friend class js::AutoDescriptorArray; friend class js::AutoDescriptorArray;
private:
PropertyDescriptor(); PropertyDescriptor();
public: public:
/* 8.10.5 ToPropertyDescriptor(Obj) */ /* 8.10.5 ToPropertyDescriptor(Obj) */
bool initialize(JSContext* cx, jsid id, jsval v); bool initialize(JSContext* cx, jsid id, jsval v);
@ -120,6 +119,7 @@ struct PropertyDescriptor {
static void traceDescriptorArray(JSTracer* trc, JSObject* obj); static void traceDescriptorArray(JSTracer* trc, JSObject* obj);
static void finalizeDescriptorArray(JSContext* cx, JSObject* obj); static void finalizeDescriptorArray(JSContext* cx, JSObject* obj);
jsval pd;
jsid id; jsid id;
jsval value, get, set; jsval value, get, set;
@ -578,11 +578,19 @@ struct JSObject {
inline jsval getQNameLocalName() const; inline jsval getQNameLocalName() const;
inline void setQNameLocalName(jsval decl); 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. * Back to generic stuff.
*/ */
bool isCallable(); inline bool isCallable();
/* The map field is not initialized here and should be set separately. */ /* The map field is not initialized here and should be set separately. */
void init(JSClass *clasp, JSObject *proto, JSObject *parent, void init(JSClass *clasp, JSObject *proto, JSObject *parent,
@ -678,6 +686,8 @@ struct JSObject {
map->ops->dropProperty(cx, this, prop); map->ops->dropProperty(cx, this, prop);
} }
void swap(JSObject *obj);
inline bool isArguments() const; inline bool isArguments() const;
inline bool isArray() const; inline bool isArray() const;
inline bool isDenseArray() const; inline bool isDenseArray() const;
@ -693,6 +703,10 @@ struct JSObject {
inline bool isNamespace() const; inline bool isNamespace() const;
inline bool isQName() const; inline bool isQName() const;
inline bool isProxy() const;
inline bool isObjectProxy() const;
inline bool isFunctionProxy() const;
inline bool unbrand(JSContext *cx); inline bool unbrand(JSContext *cx);
}; };
@ -867,6 +881,9 @@ extern JSBool
js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id, js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp); JSObject **objp, JSProperty **propp);
extern JSBool
js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs, jsval getter, jsval setter, jsval value, jsval *vp);
extern JSBool extern JSBool
js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp); js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
@ -919,6 +936,9 @@ extern JSObject*
js_NewObjectWithClassProto(JSContext *cx, JSClass *clasp, JSObject *proto, js_NewObjectWithClassProto(JSContext *cx, JSClass *clasp, JSObject *proto,
jsval privateSlotValue); jsval privateSlotValue);
extern JSBool
js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props);
/* /*
* Fast access to immutable standard objects (constructors and prototypes). * Fast access to immutable standard objects (constructors and prototypes).
*/ */
@ -1281,11 +1301,6 @@ extern const char *
js_ComputeFilename(JSContext *cx, JSStackFrame *caller, js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
JSPrincipals *principals, uintN *linenop); JSPrincipals *principals, uintN *linenop);
static inline bool
js_IsCallable(jsval v) {
return !JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isCallable();
}
extern JSBool extern JSBool
js_ReportGetterOnlyAssignment(JSContext *cx); js_ReportGetterOnlyAssignment(JSContext *cx);

Просмотреть файл

@ -500,6 +500,21 @@ JSObject::unbrand(JSContext *cx)
return true; 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 { namespace js {
typedef Vector<PropertyDescriptor, 1> PropertyDescriptorArray; typedef Vector<PropertyDescriptor, 1> PropertyDescriptorArray;
@ -528,6 +543,19 @@ class AutoDescriptorArray : private AutoGCRooter
PropertyDescriptorArray descriptors; 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 static inline bool
InitScopeForObject(JSContext* cx, JSObject* obj, JSClass *clasp, JSObject* proto, JSObjectOps* ops) InitScopeForObject(JSContext* cx, JSObject* obj, JSClass *clasp, JSObject* proto, JSObjectOps* ops)
{ {

Просмотреть файл

@ -47,6 +47,7 @@
#include "jspropertytree.h" #include "jspropertytree.h"
#include "jsscope.h" #include "jsscope.h"
#include "jsnum.h"
#include "jsscopeinlines.h" #include "jsscopeinlines.h"
using namespace js; using namespace js;

1117
js/src/jsproxy.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

232
js/src/jsproxy.h Normal file
Просмотреть файл

@ -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, JSScopeProperty(jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
uintN attrs, uintN flags, intN shortid) 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());
}
bool marked() const { return (flags & MARK) != 0; } bool marked() const { return (flags & MARK) != 0; }
void mark() { flags |= MARK; } void mark() { flags |= MARK; }

Просмотреть файл

@ -46,6 +46,8 @@
#include "jsobj.h" #include "jsobj.h"
#include "jsscope.h" #include "jsscope.h"
#include "jsobjinlines.h"
inline JSEmptyScope * inline JSEmptyScope *
JSScope::createEmptyScope(JSContext *cx, JSClass *clasp) 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 inline JSDHashNumber
JSScopeProperty::hash() const JSScopeProperty::hash() const
{ {

Просмотреть файл

@ -65,6 +65,7 @@
#include "jsxdrapi.h" #include "jsxdrapi.h"
#endif #endif
#include "jsobjinlines.h"
#include "jsscriptinlines.h" #include "jsscriptinlines.h"
using namespace js; using namespace js;

Просмотреть файл

@ -54,6 +54,8 @@
#include "jsstr.h" #include "jsstr.h"
#include "jsxdrapi.h" #include "jsxdrapi.h"
#include "jsobjinlines.h"
#ifdef DEBUG #ifdef DEBUG
#define DBG(x) x #define DBG(x) x
#else #else

Просмотреть файл

@ -53,6 +53,7 @@
#include "jsarena.h" #include "jsarena.h"
#include "jsutil.h" #include "jsutil.h"
#include "jsprf.h" #include "jsprf.h"
#include "jsproxy.h"
#include "jsapi.h" #include "jsapi.h"
#include "jsarray.h" #include "jsarray.h"
#include "jsatom.h" #include "jsatom.h"
@ -86,6 +87,8 @@
#include "jsworkers.h" #include "jsworkers.h"
#include "jsobjinlines.h"
#ifdef XP_UNIX #ifdef XP_UNIX
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
@ -3858,6 +3861,23 @@ Snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_TRUE; 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. */ /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
static JSFunctionSpec shell_functions[] = { static JSFunctionSpec shell_functions[] = {
JS_FS("version", Version, 0,0,0), JS_FS("version", Version, 0,0,0),
@ -3942,6 +3962,7 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("timeout", Timeout, 1,0), JS_FN("timeout", Timeout, 1,0),
JS_FN("elapsed", Elapsed, 0,0), JS_FN("elapsed", Elapsed, 0,0),
JS_FN("parent", Parent, 1,0), JS_FN("parent", Parent, 1,0),
JS_FN("wrap", Wrap, 1,0),
JS_FS_END 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.", " A negative value (default) means that the execution time is unlimited.",
"elapsed() Execution time elapsed for the current context.", "elapsed() Execution time elapsed for the current context.",
"parent(obj) Returns the parent of obj.\n", "parent(obj) Returns the parent of obj.\n",
"wrap(obj) Wrap an object into a noop wrapper.\n"
}; };
/* Help messages must match shell functions. */ /* Help messages must match shell functions. */

Просмотреть файл

@ -14,3 +14,4 @@ include js1_7/jstests.list
include js1_8/jstests.list include js1_8/jstests.list
include js1_8_1/jstests.list include js1_8_1/jstests.list
include js1_8_5/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.");

Просмотреть файл