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

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

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

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

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

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

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,
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.");

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