mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Bug 563099 - Compartments and wrappers API. r=gal.
--HG-- extra : rebase_source : cb34d0d0fc689fc6401d67e7f719344cd8e27655
This commit is contained in:
parent
b73234dea0
commit
c79641e0db
@ -86,6 +86,7 @@
|
||||
#include "prmjtime.h"
|
||||
#include "jsstaticcheck.h"
|
||||
#include "jsvector.h"
|
||||
#include "jswrapper.h"
|
||||
#include "jstypedarray.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
@ -566,8 +567,10 @@ JSRuntime::JSRuntime()
|
||||
bool
|
||||
JSRuntime::init(uint32 maxbytes)
|
||||
{
|
||||
if (!(defaultCompartment = new JSCompartment(this)))
|
||||
if (!(defaultCompartment = new JSCompartment(this)) ||
|
||||
!defaultCompartment->init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!js_InitGC(this, maxbytes) || !js_InitAtomState(this))
|
||||
return false;
|
||||
@ -1089,6 +1092,30 @@ JS_GetImplementationVersion(void)
|
||||
return "JavaScript-C 1.8.0 pre-release 1 2007-10-03";
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSCrossCompartmentCall *)
|
||||
JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
AutoCompartment *call = new AutoCompartment(cx, target);
|
||||
if (!call)
|
||||
return NULL;
|
||||
if (!call->enter()) {
|
||||
delete call;
|
||||
return NULL;
|
||||
}
|
||||
return reinterpret_cast<JSCrossCompartmentCall *>(call);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
|
||||
{
|
||||
AutoCompartment *realcall = reinterpret_cast<AutoCompartment *>(call);
|
||||
CHECK_REQUEST(realcall->context);
|
||||
realcall->leave();
|
||||
delete realcall;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetGlobalObject(JSContext *cx)
|
||||
{
|
||||
@ -2886,6 +2913,22 @@ JS_NewGlobalObject(JSContext *cx, JSClass *clasp)
|
||||
return obj;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
JSCompartment *compartment = NewCompartment(cx);
|
||||
if (!compartment)
|
||||
return NULL;
|
||||
|
||||
JSCompartment *saved = cx->compartment;
|
||||
cx->compartment = compartment;
|
||||
JSObject *obj = JS_NewGlobalObject(cx, clasp);
|
||||
cx->compartment = saved;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
|
||||
{
|
||||
@ -4869,11 +4912,12 @@ JS_IsRunning(JSContext *cx)
|
||||
#ifdef JS_TRACER
|
||||
JS_ASSERT_IF(JS_TRACE_MONITOR(cx).tracecx == cx, cx->fp);
|
||||
#endif
|
||||
return cx->fp != NULL;
|
||||
JSStackFrame *fp = cx->fp;
|
||||
while (fp && fp->isDummyFrame())
|
||||
fp = fp->down;
|
||||
return fp != NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_IsConstructing(JSContext *cx)
|
||||
{
|
||||
|
@ -770,6 +770,12 @@ JS_ToggleOptions(JSContext *cx, uint32 options);
|
||||
extern JS_PUBLIC_API(const char *)
|
||||
JS_GetImplementationVersion(void);
|
||||
|
||||
extern JS_PUBLIC_API(JSCrossCompartmentCall *)
|
||||
JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_GetGlobalObject(JSContext *cx);
|
||||
|
||||
@ -1664,6 +1670,9 @@ JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp);
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_NewGlobalObject(JSContext *cx, JSClass *clasp);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);
|
||||
|
||||
|
@ -2232,42 +2232,3 @@ JSContext::purge()
|
||||
FreeOldArenas(runtime, ®expPool);
|
||||
}
|
||||
|
||||
JSCompartment::JSCompartment(JSRuntime *rt) : rt(rt), marked(false)
|
||||
{
|
||||
}
|
||||
|
||||
JSCompartment::~JSCompartment()
|
||||
{
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
AutoNewCompartment::AutoNewCompartment(JSContext *cx) :
|
||||
cx(cx), compartment(cx->compartment)
|
||||
{
|
||||
cx->compartment = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
AutoNewCompartment::init()
|
||||
{
|
||||
return !!(cx->compartment = NewCompartment(cx));
|
||||
}
|
||||
|
||||
AutoNewCompartment::~AutoNewCompartment()
|
||||
{
|
||||
cx->compartment = compartment;
|
||||
}
|
||||
|
||||
AutoCompartment::AutoCompartment(JSContext *cx, JSObject *obj) :
|
||||
cx(cx), compartment(cx->compartment)
|
||||
{
|
||||
cx->compartment = obj->getCompartment(cx);
|
||||
}
|
||||
|
||||
AutoCompartment::~AutoCompartment()
|
||||
{
|
||||
cx->compartment = compartment;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1136,7 +1136,7 @@ struct GCPtrHasher
|
||||
static HashNumber hash(void *key) {
|
||||
return HashNumber(uintptr_t(key) >> JSVAL_TAGBITS);
|
||||
}
|
||||
|
||||
|
||||
static bool match(void *l, void *k) {
|
||||
return l == k;
|
||||
}
|
||||
@ -1144,15 +1144,45 @@ struct GCPtrHasher
|
||||
|
||||
typedef HashMap<void *, const char *, GCPtrHasher, SystemAllocPolicy> GCRoots;
|
||||
typedef HashMap<void *, uint32, GCPtrHasher, SystemAllocPolicy> GCLocks;
|
||||
|
||||
|
||||
struct WrapperHasher
|
||||
{
|
||||
typedef jsval Lookup;
|
||||
|
||||
static HashNumber hash(jsval key) {
|
||||
return GCPtrHasher::hash(JSVAL_TO_GCTHING(key));
|
||||
}
|
||||
|
||||
static bool match(jsval l, jsval k) {
|
||||
return l == k;
|
||||
}
|
||||
};
|
||||
|
||||
typedef HashMap<jsval, jsval, WrapperHasher, SystemAllocPolicy> WrapperMap;
|
||||
|
||||
class AutoValueVector;
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
struct JSCompartment {
|
||||
JSRuntime *rt;
|
||||
bool marked;
|
||||
js::WrapperMap crossCompartmentWrappers;
|
||||
|
||||
JSCompartment(JSRuntime *cx);
|
||||
~JSCompartment();
|
||||
|
||||
bool init();
|
||||
|
||||
bool wrap(JSContext *cx, jsval *vp);
|
||||
bool wrap(JSContext *cx, JSString **strp);
|
||||
bool wrap(JSContext *cx, JSObject **objp);
|
||||
bool wrapId(JSContext *cx, jsid *idp);
|
||||
bool wrap(JSContext *cx, JSPropertyDescriptor *desc);
|
||||
bool wrap(JSContext *cx, js::AutoValueVector &props);
|
||||
bool wrapException(JSContext *cx);
|
||||
|
||||
void sweep(JSContext *cx);
|
||||
};
|
||||
|
||||
struct JSRuntime {
|
||||
@ -2154,24 +2184,6 @@ FrameAtomBase(JSContext *cx, JSStackFrame *fp)
|
||||
|
||||
namespace js {
|
||||
|
||||
class AutoNewCompartment {
|
||||
JSContext *cx;
|
||||
JSCompartment *compartment;
|
||||
public:
|
||||
JS_FRIEND_API(AutoNewCompartment(JSContext *cx));
|
||||
JS_FRIEND_API(~AutoNewCompartment());
|
||||
|
||||
JS_FRIEND_API(bool) init();
|
||||
};
|
||||
|
||||
class AutoCompartment {
|
||||
JSContext *cx;
|
||||
JSCompartment *compartment;
|
||||
public:
|
||||
JS_FRIEND_API(AutoCompartment(JSContext *cx, JSObject *obj));
|
||||
JS_FRIEND_API(~AutoCompartment());
|
||||
};
|
||||
|
||||
class AutoGCRooter {
|
||||
public:
|
||||
AutoGCRooter(JSContext *cx, ptrdiff_t tag)
|
||||
|
@ -218,6 +218,7 @@ class CompartmentChecker
|
||||
*/
|
||||
static void fail(JSCompartment *c1, JSCompartment *c2) {
|
||||
printf("*** Compartment mismatch %p vs. %p\n", (void *) c1, (void *) c2);
|
||||
JS_NOT_REACHED("compartment mismatch");
|
||||
}
|
||||
|
||||
void check(JSCompartment *c) {
|
||||
|
@ -63,6 +63,7 @@
|
||||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsparse.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsscan.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsscript.h"
|
||||
@ -1818,54 +1819,70 @@ JS_FRIEND_DATA(JSClass) js_FunctionClass = {
|
||||
JS_CLASS_TRACE(fun_trace), NULL
|
||||
};
|
||||
|
||||
static JSBool
|
||||
fun_toStringHelper(JSContext *cx, uint32_t indent, uintN argc, jsval *vp)
|
||||
namespace js {
|
||||
|
||||
JSString *
|
||||
fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent)
|
||||
{
|
||||
jsval fval;
|
||||
JSObject *obj;
|
||||
JSFunction *fun;
|
||||
JSString *str;
|
||||
|
||||
fval = JS_THIS(cx, vp);
|
||||
if (JSVAL_IS_NULL(fval))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!VALUE_IS_FUNCTION(cx, fval)) {
|
||||
if (!obj->isFunction()) {
|
||||
if (obj->isProxy())
|
||||
return JSProxy::fun_toString(cx, obj, indent);
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_INCOMPATIBLE_PROTO,
|
||||
js_Function_str, js_toString_str,
|
||||
JS_GetTypeName(cx, JS_TypeOfValue(cx, fval)));
|
||||
return JS_FALSE;
|
||||
"object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = JSVAL_TO_OBJECT(fval);
|
||||
if (argc != 0) {
|
||||
if (!ValueToECMAUint32(cx, vp[2], &indent))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JS_ASSERT(JS_ObjectIsFunction(cx, obj));
|
||||
fun = GET_FUNCTION_PRIVATE(cx, obj);
|
||||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
|
||||
if (!fun)
|
||||
return JS_TRUE;
|
||||
str = JS_DecompileFunction(cx, fun, (uintN)indent);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
return JS_TRUE;
|
||||
return NULL;
|
||||
return JS_DecompileFunction(cx, fun, indent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static JSBool
|
||||
fun_toString(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
return fun_toStringHelper(cx, 0, argc, vp);
|
||||
JS_ASSERT(VALUE_IS_FUNCTION(cx, vp[0]));
|
||||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(vp[0]));
|
||||
JS_ASSERT(!FUN_INTERPRETED(fun));
|
||||
uint32_t indent = 0;
|
||||
|
||||
if (argc != 0 && !ValueToECMAUint32(cx, vp[2], &indent))
|
||||
return false;
|
||||
|
||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
JSString *str = fun_toStringHelper(cx, obj, indent);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static JSBool
|
||||
fun_toSource(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp);
|
||||
JS_ASSERT(VALUE_IS_FUNCTION(cx, vp[0]));
|
||||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(vp[0]));
|
||||
JS_ASSERT(!FUN_INTERPRETED(fun));
|
||||
|
||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
JSString *str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -516,4 +516,11 @@ js_fun_call(JSContext *cx, uintN argc, jsval *vp);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
namespace js {
|
||||
|
||||
extern JSString *
|
||||
fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
|
||||
|
||||
}
|
||||
|
||||
#endif /* jsfun_h___ */
|
||||
|
@ -2789,12 +2789,6 @@ 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);
|
||||
@ -3204,6 +3198,27 @@ BackgroundSweepTask::run()
|
||||
|
||||
#endif /* JS_THREADSAFE */
|
||||
|
||||
static void
|
||||
SweepCompartments(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JSCompartment **read = rt->compartments.begin();
|
||||
JSCompartment **end = rt->compartments.end();
|
||||
JSCompartment **write = read;
|
||||
while (read < end) {
|
||||
JSCompartment *compartment = (*read++);
|
||||
if (compartment->marked) {
|
||||
compartment->marked = false;
|
||||
*write++ = compartment;
|
||||
/* Remove dead wrappers from the compartment map. */
|
||||
compartment->sweep(cx);
|
||||
} else {
|
||||
delete compartment;
|
||||
}
|
||||
}
|
||||
rt->compartments.resize(write - rt->compartments.begin());
|
||||
}
|
||||
|
||||
/*
|
||||
* Common cache invalidation and so forth that must be done before GC. Even if
|
||||
* GCUntilDone calls GC several times, this work only needs to be done once.
|
||||
@ -3370,7 +3385,7 @@ GC(JSContext *cx GCTIMER_PARAM)
|
||||
SweepDoubles(rt);
|
||||
TIMESTAMP(sweepDoubleEnd);
|
||||
|
||||
js::SweepCompartments(cx);
|
||||
SweepCompartments(cx);
|
||||
|
||||
/*
|
||||
* Sweep the runtime's property tree after finalizing objects, in case any
|
||||
@ -3793,7 +3808,7 @@ NewCompartment(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JSCompartment *compartment = new JSCompartment(rt);
|
||||
if (!compartment) {
|
||||
if (!compartment || !compartment->init()) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
@ -3809,24 +3824,4 @@ NewCompartment(JSContext *cx)
|
||||
return compartment;
|
||||
}
|
||||
|
||||
void
|
||||
SweepCompartments(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JSCompartment **read = rt->compartments.begin();
|
||||
JSCompartment **end = rt->compartments.end();
|
||||
JSCompartment **write = read;
|
||||
while (read < end) {
|
||||
JSCompartment *compartment = (*read);
|
||||
if (compartment->marked) {
|
||||
compartment->marked = false;
|
||||
*write++ = compartment;
|
||||
} else {
|
||||
delete compartment;
|
||||
}
|
||||
++read;
|
||||
}
|
||||
rt->compartments.resize(write - rt->compartments.begin());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -564,9 +564,6 @@ TraceValues(JSTracer *trc, size_t len, jsval *vec, const char *name)
|
||||
JSCompartment *
|
||||
NewCompartment(JSContext *cx);
|
||||
|
||||
void
|
||||
SweepCompartments(JSContext *cx);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsgc_h___ */
|
||||
|
@ -211,6 +211,8 @@ struct JSStackFrame
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isDummyFrame() const { return !script && !fun; }
|
||||
};
|
||||
|
||||
namespace js {
|
||||
|
@ -817,31 +817,21 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp)
|
||||
}
|
||||
#endif /* JS_HAS_TOSOURCE */
|
||||
|
||||
static JSBool
|
||||
obj_toString(JSContext *cx, uintN argc, jsval *vp)
|
||||
namespace js {
|
||||
|
||||
JSString *
|
||||
obj_toStringHelper(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *obj;
|
||||
jschar *chars;
|
||||
size_t nchars;
|
||||
const char *clazz, *prefix;
|
||||
JSString *str;
|
||||
if (obj->isProxy())
|
||||
return JSProxy::obj_toString(cx, obj);
|
||||
|
||||
obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
if (obj->isProxy()) {
|
||||
if (!GetProxyObjectClass(cx, obj, &clazz))
|
||||
return false;
|
||||
} else {
|
||||
obj = obj->wrappedObject(cx);
|
||||
clazz = obj->getClass()->name;
|
||||
}
|
||||
nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
|
||||
chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
|
||||
const char *clazz = obj->wrappedObject(cx)->getClass()->name;
|
||||
size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
|
||||
jschar *chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
|
||||
if (!chars)
|
||||
return JS_FALSE;
|
||||
return NULL;
|
||||
|
||||
prefix = "[object ";
|
||||
const char *prefix = "[object ";
|
||||
nchars = 0;
|
||||
while ((chars[nchars] = (jschar)*prefix) != 0)
|
||||
nchars++, prefix++;
|
||||
@ -850,13 +840,27 @@ obj_toString(JSContext *cx, uintN argc, jsval *vp)
|
||||
chars[nchars++] = ']';
|
||||
chars[nchars] = 0;
|
||||
|
||||
str = js_NewString(cx, chars, nchars);
|
||||
if (!str) {
|
||||
JSString *str = js_NewString(cx, chars, nchars);
|
||||
if (!str)
|
||||
cx->free(chars);
|
||||
return JS_FALSE;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static JSBool
|
||||
obj_toString(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
JSString *str = js::obj_toStringHelper(cx, obj);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
return JS_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -3631,20 +3635,20 @@ JSObject::shrinkSlots(JSContext *cx, size_t nslots)
|
||||
bool
|
||||
js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved)
|
||||
{
|
||||
JS_ASSERT(obj->isNative());
|
||||
|
||||
uintN nslots = JSSLOT_FREE(obj->getClass()) + nreserved;
|
||||
if (nslots > obj->numSlots() && !obj->allocSlots(cx, nslots))
|
||||
return false;
|
||||
|
||||
JSScope *scope = obj->scope();
|
||||
if (!scope->isSharedEmpty()) {
|
||||
if (obj->isNative()) {
|
||||
JSScope *scope = obj->scope();
|
||||
if (!scope->isSharedEmpty()) {
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(scope->title.ownercx->thread == cx->thread);
|
||||
JS_ASSERT(scope->title.ownercx->thread == cx->thread);
|
||||
#endif
|
||||
JS_ASSERT(scope->freeslot == JSSLOT_FREE(obj->getClass()));
|
||||
if (scope->freeslot < nslots)
|
||||
scope->freeslot = nslots;
|
||||
JS_ASSERT(scope->freeslot == JSSLOT_FREE(obj->getClass()));
|
||||
if (scope->freeslot < nslots)
|
||||
scope->freeslot = nslots;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -6115,8 +6119,10 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
|
||||
else if (IS_GC_MARKING_TRACER(trc))
|
||||
(void) clasp->mark(cx, obj, trc);
|
||||
}
|
||||
if (clasp->flags & JSCLASS_IS_GLOBAL)
|
||||
obj->getCompartment(cx)->marked = true;
|
||||
if (clasp->flags & JSCLASS_IS_GLOBAL) {
|
||||
JSCompartment *compartment = obj->getCompartment(cx);
|
||||
compartment->marked = true;
|
||||
}
|
||||
|
||||
obj->traceProtoAndParent(trc);
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
||||
#include "jsprvtd.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
namespace js { class AutoDescriptorArray; }
|
||||
namespace js { class AutoDescriptorArray; class JSProxyHandler; }
|
||||
|
||||
/*
|
||||
* A representation of ECMA-262 ed. 5's internal property descriptor data
|
||||
@ -591,7 +591,7 @@ struct JSObject {
|
||||
* Proxy-specific getters and setters.
|
||||
*/
|
||||
|
||||
inline jsval getProxyHandler() const;
|
||||
inline js::JSProxyHandler *getProxyHandler() const;
|
||||
inline jsval getProxyPrivate() const;
|
||||
inline void setProxyPrivate(jsval priv);
|
||||
|
||||
@ -716,6 +716,9 @@ struct JSObject {
|
||||
inline bool isObjectProxy() const;
|
||||
inline bool isFunctionProxy() const;
|
||||
|
||||
bool isCrossCompartmentWrapper() const;
|
||||
JSObject *unwrap();
|
||||
|
||||
inline bool unbrand(JSContext *cx);
|
||||
};
|
||||
|
||||
@ -1343,4 +1346,11 @@ SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles);
|
||||
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
extern JSString *
|
||||
obj_toStringHelper(JSContext *cx, JSObject *obj);
|
||||
|
||||
}
|
||||
|
||||
#endif /* jsobj_h___ */
|
||||
|
@ -564,6 +564,14 @@ class AutoDescriptor : private AutoGCRooter, public JSPropertyDescriptor
|
||||
value = JSVAL_VOID;
|
||||
}
|
||||
|
||||
AutoDescriptor(JSContext *cx, JSPropertyDescriptor *desc) : AutoGCRooter(cx, DESCRIPTOR) {
|
||||
obj = desc->obj;
|
||||
attrs = desc->attrs;
|
||||
getter = desc->getter;
|
||||
setter = desc->setter;
|
||||
value = desc->value;
|
||||
}
|
||||
|
||||
friend void AutoGCRooter::trace(JSTracer *trc);
|
||||
};
|
||||
|
||||
|
@ -54,6 +54,19 @@ using namespace js;
|
||||
|
||||
namespace js {
|
||||
|
||||
static jsval
|
||||
GetCall(JSObject *proxy) {
|
||||
JS_ASSERT(proxy->isFunctionProxy());
|
||||
return proxy->getSlot(JSSLOT_PROXY_CALL);
|
||||
}
|
||||
|
||||
static jsval
|
||||
GetConstruct(JSObject *proxy) {
|
||||
if (proxy->numSlots() <= JSSLOT_PROXY_CONSTRUCT)
|
||||
return JSVAL_VOID;
|
||||
return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT);
|
||||
}
|
||||
|
||||
static bool
|
||||
OperationInProgress(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
@ -200,6 +213,82 @@ JSProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
|
||||
return false;
|
||||
return IdVectorToIterator(cx, proxy, flags, props, vp);
|
||||
}
|
||||
|
||||
JSString *
|
||||
JSProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
JS_ASSERT(proxy->isProxy());
|
||||
|
||||
return JS_NewStringCopyZ(cx, proxy->isFunctionProxy()
|
||||
? "[object Function]"
|
||||
: "[object Object]");
|
||||
}
|
||||
|
||||
JSString *
|
||||
JSProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
||||
{
|
||||
JS_ASSERT(proxy->isProxy());
|
||||
jsval fval = GetCall(proxy);
|
||||
if (proxy->isFunctionProxy() &&
|
||||
(JSVAL_IS_PRIMITIVE(fval) ||
|
||||
!JSVAL_TO_OBJECT(fval)->isFunction())) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_INCOMPATIBLE_PROTO,
|
||||
js_Function_str, js_toString_str,
|
||||
"object");
|
||||
return NULL;
|
||||
}
|
||||
return fun_toStringHelper(cx, JSVAL_TO_OBJECT(fval), indent);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp)
|
||||
{
|
||||
JS_ASSERT(OperationInProgress(cx, proxy));
|
||||
AutoValueRooter rval(cx);
|
||||
JSBool ok = js_InternalInvoke(cx, vp[1], GetCall(proxy), 0, argc, JS_ARGV(cx, vp),
|
||||
rval.addr());
|
||||
if (ok)
|
||||
JS_SET_RVAL(cx, vp, rval.value());
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxyHandler::construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
|
||||
uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
JS_ASSERT(OperationInProgress(cx, proxy));
|
||||
jsval fval = GetConstruct(proxy);
|
||||
if (fval == JSVAL_VOID) {
|
||||
/*
|
||||
* proxy is the constructor, so get proxy.prototype as the proto
|
||||
* of the new object.
|
||||
*/
|
||||
if (!JSProxy::get(cx, proxy, proxy, ATOM_TO_JSID(ATOM(classPrototype)), rval))
|
||||
return false;
|
||||
|
||||
JSObject *proto;
|
||||
if (!JSVAL_IS_PRIMITIVE(*rval)) {
|
||||
proto = JSVAL_TO_OBJECT(*rval);
|
||||
} else {
|
||||
if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
|
||||
|
||||
*rval = OBJECT_TO_JSVAL(newobj);
|
||||
|
||||
/* If the call returns an object, return that, otherwise the original newobj. */
|
||||
if (!js_InternalCall(cx, newobj, GetCall(proxy), argc, argv, rval))
|
||||
return false;
|
||||
if (JSVAL_IS_PRIMITIVE(*rval))
|
||||
*rval = OBJECT_TO_JSVAL(newobj);
|
||||
|
||||
return true;
|
||||
}
|
||||
return js_InternalCall(cx, receiver, fval, argc, argv, rval);
|
||||
}
|
||||
|
||||
void
|
||||
JSProxyHandler::finalize(JSContext *cx, JSObject *proxy)
|
||||
@ -372,9 +461,6 @@ class JSScriptedProxyHandler : public JSProxyHandler {
|
||||
virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props);
|
||||
virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual const void *family();
|
||||
|
||||
static JSScriptedProxyHandler singleton;
|
||||
};
|
||||
|
||||
@ -402,7 +488,7 @@ static JSObject *
|
||||
GetProxyHandlerObject(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
JS_ASSERT(OperationInProgress(cx, proxy));
|
||||
return JSVAL_TO_OBJECT(proxy->getProxyHandler());
|
||||
return JSVAL_TO_OBJECT(proxy->getProxyPrivate());
|
||||
}
|
||||
|
||||
bool
|
||||
@ -565,20 +651,8 @@ JSScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsv
|
||||
ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
|
||||
}
|
||||
|
||||
const void *
|
||||
JSScriptedProxyHandler::family()
|
||||
{
|
||||
return &singleton;
|
||||
}
|
||||
|
||||
JSScriptedProxyHandler JSScriptedProxyHandler::singleton;
|
||||
|
||||
static JSProxyHandler *
|
||||
JSVAL_TO_HANDLER(jsval handler)
|
||||
{
|
||||
return (JSProxyHandler *) JSVAL_TO_PRIVATE(handler);
|
||||
}
|
||||
|
||||
class AutoPendingProxyOperation {
|
||||
JSThreadData *data;
|
||||
JSPendingProxyOperation op;
|
||||
@ -599,10 +673,7 @@ bool
|
||||
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.getPropertyDescriptor(cx, proxy, id, desc);
|
||||
return JSVAL_TO_HANDLER(handler)->getPropertyDescriptor(cx, proxy, id, desc);
|
||||
return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -619,10 +690,7 @@ JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
|
||||
JSPropertyDescriptor *desc)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.getOwnPropertyDescriptor(cx, proxy, id, desc);
|
||||
return JSVAL_TO_HANDLER(handler)->getOwnPropertyDescriptor(cx, proxy, id, desc);
|
||||
return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -638,10 +706,7 @@ bool
|
||||
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.defineProperty(cx, proxy, id, desc);
|
||||
return JSVAL_TO_HANDLER(handler)->defineProperty(cx, proxy, id, desc);
|
||||
return proxy->getProxyHandler()->defineProperty(cx, proxy, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -657,126 +722,99 @@ bool
|
||||
JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.getOwnPropertyNames(cx, proxy, props);
|
||||
return JSVAL_TO_HANDLER(handler)->getOwnPropertyNames(cx, proxy, props);
|
||||
return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.delete_(cx, proxy, id, bp);
|
||||
return JSVAL_TO_HANDLER(handler)->delete_(cx, proxy, id, bp);
|
||||
return proxy->getProxyHandler()->delete_(cx, proxy, id, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.enumerate(cx, proxy, props);
|
||||
return JSVAL_TO_HANDLER(handler)->enumerate(cx, proxy, props);
|
||||
return proxy->getProxyHandler()->enumerate(cx, proxy, props);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::fix(JSContext *cx, JSObject *proxy, jsval *vp)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.fix(cx, proxy, vp);
|
||||
return JSVAL_TO_HANDLER(handler)->fix(cx, proxy, vp);
|
||||
return proxy->getProxyHandler()->fix(cx, proxy, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.has(cx, proxy, id, bp);
|
||||
return JSVAL_TO_HANDLER(handler)->has(cx, proxy, id, bp);
|
||||
return proxy->getProxyHandler()->has(cx, proxy, id, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.hasOwn(cx, proxy, id, bp);
|
||||
return JSVAL_TO_HANDLER(handler)->hasOwn(cx, proxy, id, bp);
|
||||
return proxy->getProxyHandler()->hasOwn(cx, proxy, id, bp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.get(cx, proxy, receiver, id, vp);
|
||||
return JSVAL_TO_HANDLER(handler)->get(cx, proxy, receiver, id, vp);
|
||||
return proxy->getProxyHandler()->get(cx, proxy, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.set(cx, proxy, receiver, id, vp);
|
||||
return JSVAL_TO_HANDLER(handler)->set(cx, proxy, receiver, id, vp);
|
||||
return proxy->getProxyHandler()->set(cx, proxy, receiver, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.enumerateOwn(cx, proxy, props);
|
||||
return JSVAL_TO_HANDLER(handler)->enumerateOwn(cx, proxy, props);
|
||||
return proxy->getProxyHandler()->enumerateOwn(cx, proxy, props);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval handler = proxy->getProxyHandler();
|
||||
if (JSVAL_IS_OBJECT(handler))
|
||||
return JSScriptedProxyHandler::singleton.iterate(cx, proxy, flags, vp);
|
||||
return JSVAL_TO_HANDLER(handler)->iterate(cx, proxy, flags, vp);
|
||||
return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep)
|
||||
bool
|
||||
JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp)
|
||||
{
|
||||
if (!proxy->isProxy()) {
|
||||
char *bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
|
||||
OBJECT_TO_JSVAL(proxy), NULL);
|
||||
if (!bytes)
|
||||
return JS_FALSE;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_UNEXPECTED_TYPE, bytes, "not a proxy");
|
||||
return false;
|
||||
}
|
||||
if (proxy->isFunctionProxy()) {
|
||||
*namep = "Function";
|
||||
return true;
|
||||
}
|
||||
jsval nameval = proxy->fslots[JSSLOT_PROXY_CLASS];
|
||||
if (nameval == JSVAL_VOID) {
|
||||
*namep ="Object";
|
||||
return true;
|
||||
}
|
||||
JS_ASSERT(JSVAL_IS_STRING(nameval));
|
||||
*namep = JS_GetStringBytesZ(cx, JSVAL_TO_STRING(nameval));
|
||||
return *namep != NULL;
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->call(cx, proxy, argc, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSProxy::construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
|
||||
uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->construct(cx, proxy, receiver, argc, argv, rval);
|
||||
}
|
||||
|
||||
JSString *
|
||||
JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->obj_toString(cx, proxy);
|
||||
}
|
||||
|
||||
JSString *
|
||||
JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
||||
{
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -871,17 +909,11 @@ proxy_TraceObject(JSTracer *trc, JSObject *obj)
|
||||
}
|
||||
|
||||
obj->traceProtoAndParent(trc);
|
||||
|
||||
jsval handler = obj->fslots[JSSLOT_PROXY_HANDLER];
|
||||
if (!JSVAL_IS_PRIMITIVE(handler))
|
||||
JS_CALL_OBJECT_TRACER(trc, JSVAL_TO_OBJECT(handler), "handler");
|
||||
else
|
||||
JSVAL_TO_HANDLER(handler)->trace(trc, obj);
|
||||
obj->getProxyHandler()->trace(trc, obj);
|
||||
JS_CALL_VALUE_TRACER(trc, obj->getProxyPrivate(), "private");
|
||||
if (obj->isFunctionProxy()) {
|
||||
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_CALL], "call");
|
||||
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_CONSTRUCT], "construct");
|
||||
} else {
|
||||
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_PRIVATE], "private");
|
||||
JS_CALL_VALUE_TRACER(trc, GetCall(obj), "call");
|
||||
JS_CALL_VALUE_TRACER(trc, GetConstruct(obj), "construct");
|
||||
}
|
||||
}
|
||||
|
||||
@ -891,6 +923,14 @@ proxy_TypeOf_obj(JSContext *cx, JSObject *obj)
|
||||
return JSTYPE_OBJECT;
|
||||
}
|
||||
|
||||
void
|
||||
proxy_Finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->isProxy());
|
||||
if (obj->getSlot(JSSLOT_PROXY_HANDLER) != JSVAL_VOID)
|
||||
obj->getProxyHandler()->finalize(cx, obj);
|
||||
}
|
||||
|
||||
extern JSObjectOps js_ObjectProxyObjectOps;
|
||||
|
||||
static const JSObjectMap SharedObjectProxyMap(&js_ObjectProxyObjectOps, JSObjectMap::SHAPELESS);
|
||||
@ -912,7 +952,7 @@ JSObjectOps js_ObjectProxyObjectOps = {
|
||||
NULL, /* call */
|
||||
NULL, /* construct */
|
||||
js_HasInstance,
|
||||
NULL
|
||||
proxy_Finalize
|
||||
};
|
||||
|
||||
static JSObjectOps *
|
||||
@ -923,7 +963,7 @@ obj_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
|
||||
|
||||
JS_FRIEND_API(JSClass) ObjectProxyClass = {
|
||||
"Proxy",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(3),
|
||||
JSCLASS_HAS_RESERVED_SLOTS(2),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
|
||||
obj_proxy_getObjectOps, NULL, NULL, NULL,
|
||||
@ -935,16 +975,7 @@ proxy_Call(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
JSObject *proxy = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
|
||||
JS_ASSERT(proxy->isProxy());
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
JSObject *obj = cx->fp->getThisObject(cx);
|
||||
if (!obj)
|
||||
return false;
|
||||
AutoValueRooter rval(cx);
|
||||
JSBool ok = js_InternalInvoke(cx, vp[1], proxy->fslots[JSSLOT_PROXY_CALL], 0,
|
||||
argc, JS_ARGV(cx, vp), rval.addr());
|
||||
if (ok)
|
||||
JS_SET_RVAL(cx, vp, rval.value());
|
||||
return ok;
|
||||
return JSProxy::call(cx, proxy, argc, vp);
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -952,43 +983,7 @@ proxy_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rv
|
||||
{
|
||||
JSObject *proxy = JSVAL_TO_OBJECT(argv[-2]);
|
||||
JS_ASSERT(proxy->isProxy());
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
jsval fval = proxy->fslots[JSSLOT_PROXY_CONSTRUCT];
|
||||
if (fval == JSVAL_VOID) {
|
||||
/*
|
||||
* We don't have an explicit constructor trap so allocate a new
|
||||
* object and use the call trap.
|
||||
*/
|
||||
fval = proxy->fslots[JSSLOT_PROXY_CALL];
|
||||
JS_ASSERT(JSVAL_IS_OBJECT(fval));
|
||||
|
||||
/*
|
||||
* proxy is the constructor, so get proxy.prototype as the proto
|
||||
* of the new object.
|
||||
*/
|
||||
if (!JSProxy::get(cx, proxy, obj, ATOM_TO_JSID(ATOM(classPrototype)), rval))
|
||||
return false;
|
||||
|
||||
JSObject *proto;
|
||||
if (!JSVAL_IS_PRIMITIVE(*rval)) {
|
||||
proto = JSVAL_TO_OBJECT(*rval);
|
||||
} else {
|
||||
if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
|
||||
*rval = OBJECT_TO_JSVAL(newobj);
|
||||
|
||||
/* If the call returns an object, return that, otherwise the original newobj. */
|
||||
if (!js_InternalCall(cx, newobj, proxy->fslots[JSSLOT_PROXY_CALL], argc, argv, rval))
|
||||
return false;
|
||||
if (JSVAL_IS_PRIMITIVE(*rval))
|
||||
*rval = OBJECT_TO_JSVAL(newobj);
|
||||
|
||||
return true;
|
||||
}
|
||||
return js_InternalCall(cx, obj, fval, argc, argv, rval);
|
||||
return JSProxy::construct(cx, proxy, obj, argc, argv, rval);
|
||||
}
|
||||
|
||||
static JSType
|
||||
@ -1031,7 +1026,7 @@ fun_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
|
||||
|
||||
JS_FRIEND_API(JSClass) FunctionProxyClass = {
|
||||
"Proxy",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(3),
|
||||
JSCLASS_HAS_RESERVED_SLOTS(4),
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
|
||||
fun_proxy_getObjectOps, NULL, NULL, NULL,
|
||||
@ -1039,27 +1034,21 @@ JS_FRIEND_API(JSClass) FunctionProxyClass = {
|
||||
};
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className)
|
||||
NewProxyObject(JSContext *cx, JSProxyHandler *handler, jsval priv, JSObject *proto, JSObject *parent,
|
||||
JSObject *call, JSObject *construct)
|
||||
{
|
||||
JSObject *obj = NewObjectWithGivenProto(cx, &ObjectProxyClass, proto, parent);
|
||||
if (!obj)
|
||||
bool fun = call || construct;
|
||||
JSClass *clasp = fun ? &FunctionProxyClass : &ObjectProxyClass;
|
||||
JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent);
|
||||
if (!obj || (construct && !js_EnsureReservedSlots(cx, obj, 0)))
|
||||
return NULL;
|
||||
obj->fslots[JSSLOT_PROXY_HANDLER] = handler;
|
||||
obj->fslots[JSSLOT_PROXY_CLASS] = className ? STRING_TO_JSVAL(className) : JSVAL_VOID;
|
||||
obj->fslots[JSSLOT_PROXY_PRIVATE] = JSVAL_VOID;
|
||||
return obj;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
|
||||
JSObject *call, JSObject *construct)
|
||||
{
|
||||
JSObject *obj = NewObjectWithGivenProto(cx, &FunctionProxyClass, proto, parent);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
obj->fslots[JSSLOT_PROXY_HANDLER] = handler;
|
||||
obj->fslots[JSSLOT_PROXY_CALL] = call ? OBJECT_TO_JSVAL(call) : JSVAL_VOID;
|
||||
obj->fslots[JSSLOT_PROXY_CONSTRUCT] = construct ? OBJECT_TO_JSVAL(construct) : JSVAL_VOID;
|
||||
obj->setSlot(JSSLOT_PROXY_HANDLER, PRIVATE_TO_JSVAL(handler));
|
||||
obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
|
||||
if (fun) {
|
||||
obj->setSlot(JSSLOT_PROXY_CALL, call ? OBJECT_TO_JSVAL(call) : JSVAL_VOID);
|
||||
if (construct)
|
||||
obj->setSlot(JSSLOT_PROXY_CONSTRUCT, construct ? OBJECT_TO_JSVAL(construct) : JSVAL_VOID);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -1093,8 +1082,8 @@ proxy_create(JSContext *cx, uintN argc, jsval *vp)
|
||||
proto = NULL;
|
||||
parent = JSVAL_TO_OBJECT(vp[0])->getParent();
|
||||
}
|
||||
JSString *className = (argc > 2 && JSVAL_IS_STRING(vp[4])) ? JSVAL_TO_STRING(vp[4]) : NULL;
|
||||
JSObject *proxy = NewObjectProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, className);
|
||||
JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, OBJECT_TO_JSVAL(handler),
|
||||
proto, parent);
|
||||
if (!proxy)
|
||||
return false;
|
||||
|
||||
@ -1129,8 +1118,8 @@ proxy_createFunction(JSContext *cx, uintN argc, jsval *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *proxy = NewFunctionProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent,
|
||||
call, construct);
|
||||
JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, OBJECT_TO_JSVAL(handler),
|
||||
proto, parent, call, construct);
|
||||
if (!proxy)
|
||||
return false;
|
||||
|
||||
@ -1282,8 +1271,8 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
|
||||
AutoValueRooter tvr2(cx, newborn);
|
||||
|
||||
if (clasp == &CallableObjectClass) {
|
||||
newborn->fslots[JSSLOT_CALLABLE_CALL] = proxy->fslots[JSSLOT_PROXY_CALL];
|
||||
newborn->fslots[JSSLOT_CALLABLE_CONSTRUCT] = proxy->fslots[JSSLOT_PROXY_CONSTRUCT];
|
||||
newborn->fslots[JSSLOT_CALLABLE_CALL] = GetCall(proxy);
|
||||
newborn->fslots[JSSLOT_CALLABLE_CONSTRUCT] = GetConstruct(proxy);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -74,9 +74,13 @@ class JSProxyHandler {
|
||||
virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual bool call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp);
|
||||
virtual bool construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
|
||||
uintN argc, jsval *argv, jsval *rval);
|
||||
virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
|
||||
virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
virtual void finalize(JSContext *cx, JSObject *proxy);
|
||||
virtual void trace(JSTracer *trc, JSObject *proxy);
|
||||
virtual const void *family() = 0;
|
||||
};
|
||||
|
||||
/* Dispatch point for handlers that executes the appropriate C++ or scripted traps. */
|
||||
@ -103,16 +107,21 @@ class JSProxy {
|
||||
static bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp);
|
||||
static bool enumerateOwn(JSContext *cx, JSObject *proxy, js::AutoValueVector &props);
|
||||
static bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
static bool call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp);
|
||||
static bool construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
|
||||
uintN argc, jsval *argv, jsval *rval);
|
||||
static JSString *obj_toString(JSContext *cx, JSObject *proxy);
|
||||
static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
};
|
||||
|
||||
/* 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;
|
||||
const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 1;
|
||||
/* Function proxies only. */
|
||||
const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 1;
|
||||
const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 2;
|
||||
const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 2;
|
||||
const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 3;
|
||||
|
||||
extern JS_FRIEND_API(JSClass) ObjectProxyClass;
|
||||
extern JS_FRIEND_API(JSClass) FunctionProxyClass;
|
||||
@ -138,41 +147,33 @@ JSObject::isProxy() const
|
||||
return isObjectProxy() || isFunctionProxy();
|
||||
}
|
||||
|
||||
inline jsval
|
||||
inline js::JSProxyHandler *
|
||||
JSObject::getProxyHandler() const
|
||||
{
|
||||
JS_ASSERT(isProxy());
|
||||
jsval handler = fslots[js::JSSLOT_PROXY_HANDLER];
|
||||
JS_ASSERT(JSVAL_IS_OBJECT(handler) || JSVAL_IS_INT(handler));
|
||||
return handler;
|
||||
jsval handler = getSlot(js::JSSLOT_PROXY_HANDLER);
|
||||
return (js::JSProxyHandler *) JSVAL_TO_PRIVATE(handler);
|
||||
}
|
||||
|
||||
inline jsval
|
||||
JSObject::getProxyPrivate() const
|
||||
{
|
||||
JS_ASSERT(isObjectProxy());
|
||||
return fslots[js::JSSLOT_PROXY_PRIVATE];
|
||||
JS_ASSERT(isProxy());
|
||||
return getSlot(js::JSSLOT_PROXY_PRIVATE);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setProxyPrivate(jsval priv)
|
||||
{
|
||||
JS_ASSERT(isObjectProxy());
|
||||
fslots[js::JSSLOT_PROXY_PRIVATE] = priv;
|
||||
JS_ASSERT(isProxy());
|
||||
setSlot(js::JSSLOT_PROXY_PRIVATE, priv);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
|
||||
JSString *className);
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
|
||||
JSObject *call, JSObject *construct);
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep);
|
||||
NewProxyObject(JSContext *cx, JSProxyHandler *handler, jsval priv, JSObject *proto, JSObject *parent,
|
||||
JSObject *call = NULL, JSObject *construct = NULL);
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
|
||||
|
@ -155,6 +155,7 @@ typedef struct JSLocaleCallbacks JSLocaleCallbacks;
|
||||
typedef struct JSSecurityCallbacks JSSecurityCallbacks;
|
||||
typedef struct JSONParser JSONParser;
|
||||
typedef struct JSCompartment JSCompartment;
|
||||
typedef struct JSCrossCompartmentCall JSCrossCompartmentCall;
|
||||
|
||||
/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */
|
||||
|
||||
|
@ -344,6 +344,11 @@ class LazilyConstructed
|
||||
JS_ASSERT(constructed);
|
||||
return asT();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
ref().~T();
|
||||
constructed = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
||||
|
||||
using namespace js;
|
||||
|
||||
JSWrapper::JSWrapper(JSObject *obj) : mWrappedObject(obj)
|
||||
JSWrapper::JSWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ JSWrapper::~JSWrapper()
|
||||
|
||||
bool
|
||||
JSWrapper::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
|
||||
JSPropertyDescriptor *desc)
|
||||
JSPropertyDescriptor *desc)
|
||||
{
|
||||
JSObject *wobj = wrappedObject(proxy);
|
||||
return JS_GetPropertyDescriptorById(cx, wobj, id, JSRESOLVE_QUALIFIED, desc);
|
||||
@ -67,14 +67,14 @@ JSWrapper::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
|
||||
|
||||
bool
|
||||
JSWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
|
||||
JSPropertyDescriptor *desc)
|
||||
JSPropertyDescriptor *desc)
|
||||
{
|
||||
return JS_GetPropertyDescriptorById(cx, wrappedObject(proxy), id, JSRESOLVE_QUALIFIED, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
JSWrapper::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
|
||||
JSPropertyDescriptor *desc)
|
||||
JSPropertyDescriptor *desc)
|
||||
{
|
||||
return JS_DefinePropertyById(cx, wrappedObject(proxy), id, desc->value,
|
||||
desc->getter, desc->setter, desc->attrs);
|
||||
@ -155,45 +155,520 @@ JSWrapper::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
|
||||
}
|
||||
|
||||
void
|
||||
JSWrapper::finalize(JSContext *cx, JSObject *proxy)
|
||||
JSWrapper::trace(JSTracer *trc, JSObject *proxy)
|
||||
{
|
||||
if (mWrappedObject)
|
||||
delete this;
|
||||
JS_CALL_OBJECT_TRACER(trc, wrappedObject(proxy), "wrappedObject");
|
||||
}
|
||||
|
||||
JSWrapper JSWrapper::singleton;
|
||||
|
||||
JSObject *
|
||||
JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
|
||||
JSProxyHandler *handler)
|
||||
{
|
||||
return NewProxyObject(cx, handler, OBJECT_TO_JSVAL(obj), proto, parent,
|
||||
obj->isCallable() ? obj : NULL, NULL);
|
||||
}
|
||||
|
||||
/* Base class for all C++ proxy handlers. */
|
||||
class JSCrossCompartmentWrapper : public JSWrapper {
|
||||
protected:
|
||||
JSCrossCompartmentWrapper();
|
||||
|
||||
public:
|
||||
virtual ~JSCrossCompartmentWrapper();
|
||||
|
||||
/* ES5 Harmony fundamental proxy traps. */
|
||||
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
|
||||
JSPropertyDescriptor *desc);
|
||||
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
|
||||
JSPropertyDescriptor *desc);
|
||||
virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
|
||||
JSPropertyDescriptor *desc);
|
||||
virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props);
|
||||
virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
|
||||
virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props);
|
||||
|
||||
/* 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, AutoValueVector &props);
|
||||
virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual bool call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp);
|
||||
virtual bool construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
|
||||
uintN argc, jsval *argv, jsval *rval);
|
||||
virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
|
||||
virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
|
||||
|
||||
static JSCrossCompartmentWrapper singleton;
|
||||
};
|
||||
|
||||
bool
|
||||
JSObject::isCrossCompartmentWrapper() const
|
||||
{
|
||||
return isProxy() && getProxyHandler() == &JSCrossCompartmentWrapper::singleton;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
JSObject::unwrap()
|
||||
{
|
||||
JSObject *wrapped = this;
|
||||
while (wrapped->isCrossCompartmentWrapper())
|
||||
wrapped = JSVAL_TO_OBJECT(wrapped->getProxyPrivate());
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
/* Compartments. */
|
||||
|
||||
JSCompartment::JSCompartment(JSRuntime *rt) : rt(rt), marked(false)
|
||||
{
|
||||
}
|
||||
|
||||
JSCompartment::~JSCompartment()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::init()
|
||||
{
|
||||
return crossCompartmentWrappers.init();
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, jsval *vp)
|
||||
{
|
||||
JS_ASSERT(cx->compartment == this);
|
||||
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
/* Only GC things have to be wrapped or copied. */
|
||||
if (JSVAL_IS_NULL(*vp) || !JSVAL_IS_GCTHING(*vp))
|
||||
return true;
|
||||
|
||||
/* Static strings do not have to be wrapped. */
|
||||
if (JSVAL_IS_STRING(*vp) && JSString::isStatic(JSVAL_TO_STRING(*vp)))
|
||||
return true;
|
||||
|
||||
/* Identity is no issue for doubles, so simply always copy them. */
|
||||
if (JSVAL_IS_DOUBLE(*vp))
|
||||
return js_NewNumberInRootedValue(cx, *JSVAL_TO_DOUBLE(*vp), vp);
|
||||
|
||||
/* Unwrap incoming objects. */
|
||||
if (!JSVAL_IS_PRIMITIVE(*vp)) {
|
||||
JSObject *obj = JSVAL_TO_OBJECT(*vp)->unwrap();
|
||||
*vp = OBJECT_TO_JSVAL(obj);
|
||||
/* If the wrapped object is already in this compartment, we are done. */
|
||||
if (obj->getCompartment(cx) == this)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we already have a wrapper for this value, use it. */
|
||||
if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) {
|
||||
*vp = p->value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (JSVAL_IS_STRING(*vp)) {
|
||||
JSString *str = JSVAL_TO_STRING(*vp);
|
||||
JSString *wrapped = js_NewStringCopyN(cx, str->chars(), str->length());
|
||||
if (!wrapped)
|
||||
return false;
|
||||
return crossCompartmentWrappers.put(*vp, *vp = STRING_TO_JSVAL(wrapped));
|
||||
}
|
||||
|
||||
JSObject *obj = JSVAL_TO_OBJECT(*vp);
|
||||
|
||||
/*
|
||||
* Recurse to wrap the prototype. Long prototype chains will run out of
|
||||
* stack, causing an error in CHECK_RECURSE.
|
||||
*
|
||||
* Wrapping the proto before creating the new wrapper and adding it to the
|
||||
* cache helps avoid leaving a bad entry in the cache on OOM. But note that
|
||||
* if we wrapped both proto and parent, we would get infinite recursion
|
||||
* here (since Object.prototype->parent->proto leads to Object.prototype
|
||||
* itself).
|
||||
*/
|
||||
AutoObjectRooter proto(cx, obj->getProto());
|
||||
if (!wrap(cx, proto.addr()))
|
||||
return false;
|
||||
JSObject *wrapper = JSWrapper::New(cx, obj, proto.object(), NULL,
|
||||
&JSCrossCompartmentWrapper::singleton);
|
||||
if (!wrapper)
|
||||
return false;
|
||||
*vp = OBJECT_TO_JSVAL(wrapper);
|
||||
if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Wrappers should really be parented to the wrapped parent of the wrapped
|
||||
* object, but in that case a wrapped global object would have a NULL
|
||||
* parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
|
||||
* we parent all wrappers to the global object in their home compartment.
|
||||
* This loses us some transparency, and is generally very cheesy.
|
||||
*/
|
||||
JSObject *global = cx->fp ? cx->fp->scopeChain->getGlobal() : cx->globalObject;
|
||||
wrapper->setParent(global);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, JSString **strp)
|
||||
{
|
||||
AutoValueRooter tvr(cx, *strp);
|
||||
if (!wrap(cx, tvr.addr()))
|
||||
return false;
|
||||
*strp = JSVAL_TO_STRING(tvr.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, JSObject **objp)
|
||||
{
|
||||
if (!*objp)
|
||||
return true;
|
||||
AutoValueRooter tvr(cx, *objp);
|
||||
if (!wrap(cx, tvr.addr()))
|
||||
return false;
|
||||
*objp = JSVAL_TO_OBJECT(tvr.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrapId(JSContext *cx, jsid *idp) {
|
||||
if (JSID_IS_INT(*idp))
|
||||
return true;
|
||||
AutoValueRooter tvr(cx, ID_TO_VALUE(*idp));
|
||||
if (!wrap(cx, tvr.addr()))
|
||||
return false;
|
||||
return JS_ValueToId(cx, tvr.value(), idp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, JSPropertyDescriptor *desc) {
|
||||
return wrap(cx, &desc->obj) &&
|
||||
(!(desc->attrs & JSPROP_GETTER) || wrap(cx, (jsval *) &desc->getter)) &&
|
||||
(!(desc->attrs & JSPROP_SETTER) || wrap(cx, (jsval *) &desc->setter)) &&
|
||||
wrap(cx, &desc->value);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, AutoValueVector &props) {
|
||||
jsid *vector = props.begin();
|
||||
jsint length = props.length();
|
||||
for (size_t n = 0; n < size_t(length); ++n) {
|
||||
if (!wrap(cx, &vector[n]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrapException(JSContext *cx) {
|
||||
JS_ASSERT(cx->compartment == this);
|
||||
|
||||
if (cx->throwing) {
|
||||
AutoValueRooter tvr(cx, cx->exception);
|
||||
cx->throwing = false;
|
||||
cx->exception = JSVAL_NULL;
|
||||
if (wrap(cx, tvr.addr())) {
|
||||
cx->throwing = true;
|
||||
cx->exception = tvr.value();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSWrapper::trace(JSTracer *trc, JSObject *proxy)
|
||||
JSCompartment::sweep(JSContext *cx)
|
||||
{
|
||||
if (mWrappedObject)
|
||||
JS_CALL_OBJECT_TRACER(trc, mWrappedObject, "wrappedObject");
|
||||
}
|
||||
|
||||
const void *
|
||||
JSWrapper::family()
|
||||
{
|
||||
return &singleton;
|
||||
}
|
||||
|
||||
JSWrapper JSWrapper::singleton(NULL);
|
||||
|
||||
JSObject *
|
||||
JSWrapper::wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, JSString *className)
|
||||
{
|
||||
JSObject *wobj;
|
||||
if (obj->isCallable()) {
|
||||
JSWrapper *handler = new JSWrapper(obj);
|
||||
if (!handler)
|
||||
return NULL;
|
||||
wobj = NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent, obj, NULL);
|
||||
if (!wobj) {
|
||||
delete handler;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
wobj = NewObjectProxy(cx, PRIVATE_TO_JSVAL(&singleton), proto, parent, className);
|
||||
if (!wobj)
|
||||
return NULL;
|
||||
wobj->setProxyPrivate(OBJECT_TO_JSVAL(obj));
|
||||
/* Remove dead wrappers from the table. */
|
||||
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
if (js_IsAboutToBeFinalized(JSVAL_TO_GCTHING(e.front().value)))
|
||||
e.removeFront();
|
||||
}
|
||||
return wobj;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetupFakeFrame(JSContext *cx, ExecuteFrameGuard &frame, JSFrameRegs ®s, JSObject *obj)
|
||||
{
|
||||
const uintN vplen = 2;
|
||||
const uintN nfixed = 0;
|
||||
if (!cx->stack().getExecuteFrame(cx, js_GetTopStackFrame(cx), vplen, nfixed, frame))
|
||||
return false;
|
||||
|
||||
jsval *vp = frame.getvp();
|
||||
vp[0] = JSVAL_VOID;
|
||||
vp[1] = JSVAL_VOID;
|
||||
|
||||
JSStackFrame *fp = frame.getFrame();
|
||||
PodZero(fp); // fp->fun and fp->script are both NULL
|
||||
fp->argv = vp + 2;
|
||||
fp->scopeChain = obj->getGlobal();
|
||||
|
||||
regs.pc = NULL;
|
||||
regs.sp = fp->slots();
|
||||
|
||||
cx->stack().pushExecuteFrame(cx, frame, regs, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
|
||||
: context(cx),
|
||||
origin(cx->compartment),
|
||||
target(target),
|
||||
destination(target->getCompartment(cx))
|
||||
{
|
||||
JS_ASSERT(origin != destination); // necessary for entered() implementation
|
||||
}
|
||||
|
||||
AutoCompartment::~AutoCompartment()
|
||||
{
|
||||
if (entered())
|
||||
leave();
|
||||
}
|
||||
|
||||
bool
|
||||
AutoCompartment::enter()
|
||||
{
|
||||
JS_ASSERT(!entered());
|
||||
context->compartment = destination;
|
||||
frame.construct();
|
||||
bool ok = SetupFakeFrame(context, frame.ref(), regs, target);
|
||||
if (!ok) {
|
||||
frame.destroy();
|
||||
context->compartment = origin;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
AutoCompartment::leave()
|
||||
{
|
||||
JS_ASSERT(entered());
|
||||
frame.destroy();
|
||||
context->compartment = origin;
|
||||
origin->wrapException(context);
|
||||
}
|
||||
|
||||
#define PIERCE(cx, pre, op, post) \
|
||||
JS_BEGIN_MACRO \
|
||||
AutoCompartment call(cx, wrappedObject(proxy)); \
|
||||
if (!call.enter() || !(pre) || !(op)) \
|
||||
return false; \
|
||||
call.leave(); \
|
||||
return (post); \
|
||||
JS_END_MACRO
|
||||
|
||||
#define NOTHING (true)
|
||||
|
||||
/* Cross compartment wrappers. */
|
||||
|
||||
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
|
||||
{
|
||||
PIERCE(cx,
|
||||
call.destination->wrapId(cx, &id),
|
||||
JSWrapper::getPropertyDescriptor(cx, proxy, id, desc),
|
||||
call.origin->wrap(cx, desc));
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
|
||||
{
|
||||
PIERCE(cx,
|
||||
call.destination->wrapId(cx, &id),
|
||||
JSWrapper::getOwnPropertyDescriptor(cx, proxy, id, desc),
|
||||
call.origin->wrap(cx, desc));
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
|
||||
{
|
||||
AutoDescriptor desc2(cx, desc);
|
||||
PIERCE(cx,
|
||||
call.destination->wrapId(cx, &id) && call.destination->wrap(cx, &desc2),
|
||||
JSWrapper::getOwnPropertyDescriptor(cx, proxy, id, &desc2),
|
||||
NOTHING);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props)
|
||||
{
|
||||
PIERCE(cx,
|
||||
NOTHING,
|
||||
JSWrapper::getOwnPropertyNames(cx, proxy, props),
|
||||
call.origin->wrap(cx, props));
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
PIERCE(cx,
|
||||
call.destination->wrapId(cx, &id),
|
||||
JSWrapper::delete_(cx, proxy, id, bp),
|
||||
NOTHING);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props)
|
||||
{
|
||||
PIERCE(cx,
|
||||
NOTHING,
|
||||
JSWrapper::enumerate(cx, proxy, props),
|
||||
call.origin->wrap(cx, props));
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
PIERCE(cx,
|
||||
call.destination->wrapId(cx, &id),
|
||||
JSWrapper::has(cx, proxy, id, bp),
|
||||
NOTHING);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
PIERCE(cx,
|
||||
call.destination->wrapId(cx, &id),
|
||||
JSWrapper::hasOwn(cx, proxy, id, bp),
|
||||
NOTHING);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
|
||||
{
|
||||
PIERCE(cx,
|
||||
call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id),
|
||||
JSWrapper::get(cx, proxy, receiver, id, vp),
|
||||
call.origin->wrap(cx, vp));
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp)
|
||||
{
|
||||
AutoValueRooter tvr(cx, *vp);
|
||||
PIERCE(cx,
|
||||
call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id) && call.destination->wrap(cx, tvr.addr()),
|
||||
JSWrapper::set(cx, proxy, receiver, id, tvr.addr()),
|
||||
NOTHING);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props)
|
||||
{
|
||||
PIERCE(cx,
|
||||
NOTHING,
|
||||
JSWrapper::enumerateOwn(cx, proxy, props),
|
||||
call.origin->wrap(cx, props));
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp)
|
||||
{
|
||||
PIERCE(cx,
|
||||
NOTHING,
|
||||
JSWrapper::iterate(cx, proxy, flags, vp),
|
||||
call.origin->wrap(cx, vp));
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *proxy, uintN argc, jsval *vp)
|
||||
{
|
||||
AutoCompartment call(cx, wrappedObject(proxy));
|
||||
if (!call.enter())
|
||||
return false;
|
||||
|
||||
vp[0] = OBJECT_TO_JSVAL(call.target);
|
||||
if (!call.destination->wrap(cx, &vp[1]))
|
||||
return false;
|
||||
jsval *argv = JS_ARGV(cx, vp);
|
||||
for (size_t n = 0; n < argc; ++n) {
|
||||
if (!call.destination->wrap(cx, &argv[n]))
|
||||
return false;
|
||||
}
|
||||
jsval *fakevp = call.getvp();
|
||||
fakevp[0] = vp[0];
|
||||
fakevp[1] = vp[1];
|
||||
if (!JSWrapper::call(cx, proxy, argc, vp))
|
||||
return false;
|
||||
|
||||
call.leave();
|
||||
return call.origin->wrap(cx, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *proxy, JSObject *receiver,
|
||||
uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
AutoCompartment call(cx, wrappedObject(proxy));
|
||||
if (!call.enter())
|
||||
return false;
|
||||
|
||||
for (size_t n = 0; n < argc; ++n) {
|
||||
if (!call.destination->wrap(cx, &argv[n]))
|
||||
return false;
|
||||
}
|
||||
jsval *vp = call.getvp();
|
||||
vp[0] = OBJECT_TO_JSVAL(call.target);
|
||||
if (!call.destination->wrap(cx, &receiver) ||
|
||||
!JSWrapper::construct(cx, proxy, receiver, argc, argv, rval)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
call.leave();
|
||||
return call.origin->wrap(cx, rval) &&
|
||||
call.origin->wrapException(cx);
|
||||
}
|
||||
|
||||
JSString *
|
||||
JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
AutoCompartment call(cx, wrappedObject(proxy));
|
||||
if (!call.enter())
|
||||
return NULL;
|
||||
|
||||
JSString *str = JSWrapper::obj_toString(cx, proxy);
|
||||
if (!str)
|
||||
return NULL;
|
||||
AutoValueRooter tvr(cx, str);
|
||||
|
||||
call.leave();
|
||||
if (!call.origin->wrap(cx, &str))
|
||||
return NULL;
|
||||
return str;
|
||||
}
|
||||
|
||||
JSString *
|
||||
JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
||||
{
|
||||
AutoCompartment call(cx, wrappedObject(proxy));
|
||||
if (!call.enter())
|
||||
return NULL;
|
||||
|
||||
JSString *str = JSWrapper::fun_toString(cx, proxy, indent);
|
||||
if (!str)
|
||||
return NULL;
|
||||
AutoValueRooter tvr(cx, str);
|
||||
|
||||
call.leave();
|
||||
if (!call.origin->wrap(cx, &str))
|
||||
return NULL;
|
||||
return str;
|
||||
}
|
||||
|
||||
JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton;
|
||||
|
@ -49,9 +49,8 @@ namespace js {
|
||||
|
||||
/* No-op wrapper handler base class. */
|
||||
class JSWrapper : public JSProxyHandler {
|
||||
JSObject *mWrappedObject;
|
||||
|
||||
JS_FRIEND_API(JSWrapper(JSObject *));
|
||||
protected:
|
||||
JS_FRIEND_API(JSWrapper());
|
||||
|
||||
public:
|
||||
JS_FRIEND_API(virtual ~JSWrapper());
|
||||
@ -80,21 +79,49 @@ class JSWrapper : public JSProxyHandler {
|
||||
virtual JS_FRIEND_API(bool) iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp);
|
||||
|
||||
/* Spidermonkey extensions. */
|
||||
virtual JS_FRIEND_API(void) finalize(JSContext *cx, JSObject *proxy);
|
||||
virtual JS_FRIEND_API(void) trace(JSTracer *trc, JSObject *proxy);
|
||||
virtual JS_FRIEND_API(const void *) family();
|
||||
|
||||
static JS_FRIEND_API(JSWrapper) singleton;
|
||||
|
||||
static JS_FRIEND_API(JSObject *) wrap(JSContext *cx, JSObject *obj,
|
||||
JSObject *proto, JSObject *parent,
|
||||
JSString *className);
|
||||
static JS_FRIEND_API(JSObject *) New(JSContext *cx, JSObject *obj,
|
||||
JSObject *proto, JSObject *parent,
|
||||
JSProxyHandler *handler);
|
||||
|
||||
inline JSObject *wrappedObject(JSObject *proxy) {
|
||||
return mWrappedObject ? mWrappedObject : JSVAL_TO_OBJECT(proxy->getProxyPrivate());
|
||||
static inline JSObject *wrappedObject(JSObject *proxy) {
|
||||
return JSVAL_TO_OBJECT(proxy->getProxyPrivate());
|
||||
}
|
||||
};
|
||||
|
||||
class AutoCompartment
|
||||
{
|
||||
public:
|
||||
JSContext * const context;
|
||||
JSCompartment * const origin;
|
||||
JSObject * const target;
|
||||
JSCompartment * const destination;
|
||||
private:
|
||||
LazilyConstructed<ExecuteFrameGuard> frame;
|
||||
JSFrameRegs regs;
|
||||
|
||||
public:
|
||||
AutoCompartment(JSContext *cx, JSObject *target);
|
||||
~AutoCompartment();
|
||||
|
||||
bool entered() const { return context->compartment == destination; }
|
||||
bool enter();
|
||||
void leave();
|
||||
|
||||
jsval *getvp() {
|
||||
JS_ASSERT(entered());
|
||||
return frame.ref().getvp();
|
||||
}
|
||||
|
||||
private:
|
||||
// Prohibit copying.
|
||||
AutoCompartment(const AutoCompartment &);
|
||||
AutoCompartment & operator=(const AutoCompartment &);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -216,6 +216,15 @@ JS_EXTERN_API(void) add_history(char *line);
|
||||
JS_END_EXTERN_C
|
||||
#endif
|
||||
|
||||
static void
|
||||
ReportException(JSContext *cx)
|
||||
{
|
||||
if (JS_IsExceptionPending(cx)) {
|
||||
if (!JS_ReportPendingException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
}
|
||||
|
||||
class ToString {
|
||||
public:
|
||||
ToString(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
|
||||
@ -223,10 +232,8 @@ public:
|
||||
, mThrow(aThrow)
|
||||
{
|
||||
mStr = JS_ValueToString(cx, v);
|
||||
if (!aThrow && !mStr && JS_IsExceptionPending(cx)) {
|
||||
if (!JS_ReportPendingException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
if (!aThrow && !mStr)
|
||||
ReportException(cx);
|
||||
JS_AddNamedStringRoot(cx, &mStr, "Value ToString helper");
|
||||
}
|
||||
~ToString() {
|
||||
@ -1647,10 +1654,7 @@ SrcNotes(JSContext *cx, JSScript *script)
|
||||
if (str) {
|
||||
bytes = JS_GetStringBytes(str);
|
||||
} else {
|
||||
if (JS_IsExceptionPending(cx)) {
|
||||
if (!JS_ReportPendingException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
ReportException(cx);
|
||||
bytes = "N/A";
|
||||
}
|
||||
fprintf(gOutFile, " function %u (%s)", index, bytes);
|
||||
@ -2268,10 +2272,7 @@ ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
if (tmpstr) {
|
||||
func = JS_GetStringBytes(tmpstr);
|
||||
} else {
|
||||
if (JS_IsExceptionPending(cx)) {
|
||||
if (!JS_ReportPendingException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
ReportException(cx);
|
||||
func = "error decompiling fun";
|
||||
}
|
||||
fprintf(gOutFile,
|
||||
@ -2926,98 +2927,100 @@ static JSClass sandbox_class = {
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||
};
|
||||
|
||||
static JSObject *
|
||||
NewSandbox(JSContext *cx, bool lazy, bool split)
|
||||
{
|
||||
JSObject *obj = JS_NewCompartmentAndGlobalObject(cx, &sandbox_class);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
JSCrossCompartmentCall *call = JS_EnterCrossCompartmentCall(cx, obj);
|
||||
if (!call)
|
||||
return NULL;
|
||||
|
||||
bool ok = true;
|
||||
if (split) {
|
||||
obj = split_setup(cx, JS_TRUE);
|
||||
ok = !!obj;
|
||||
}
|
||||
if (!lazy)
|
||||
ok = ok && JS_InitStandardClasses(cx, obj);
|
||||
|
||||
AutoValueRooter root(cx, BOOLEAN_TO_JSVAL(lazy));
|
||||
ok = ok && JS_SetProperty(cx, obj, "lazy", root.addr());
|
||||
if (ok && split)
|
||||
obj = split_outerObject(cx, obj);
|
||||
|
||||
JS_LeaveCrossCompartmentCall(call);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
|
||||
AutoObjectRooter objroot(cx, obj);
|
||||
if (!cx->compartment->wrap(cx, objroot.addr()))
|
||||
return NULL;
|
||||
return objroot.object();
|
||||
}
|
||||
|
||||
static JSBool
|
||||
EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
bool ok;
|
||||
JSString *str;
|
||||
JSObject *sobj;
|
||||
JSContext *scx;
|
||||
const jschar *src;
|
||||
size_t srclen;
|
||||
JSBool lazy, split, ok;
|
||||
JSStackFrame *fp;
|
||||
|
||||
sobj = NULL;
|
||||
JSObject *sobj = NULL;
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
|
||||
scx = NewContext(JS_GetRuntime(cx));
|
||||
if (!scx) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
JS_SetOptions(scx, JS_GetOptions(cx));
|
||||
|
||||
JS_TransferRequest(cx, scx);
|
||||
src = JS_GetStringChars(str);
|
||||
srclen = JS_GetStringLength(str);
|
||||
split = lazy = JS_FALSE;
|
||||
const jschar *src = JS_GetStringChars(str);
|
||||
size_t srclen = JS_GetStringLength(str);
|
||||
bool split = false, lazy = false;
|
||||
if (srclen == 4) {
|
||||
if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
|
||||
lazy = JS_TRUE;
|
||||
lazy = true;
|
||||
srclen = 0;
|
||||
}
|
||||
} else if (srclen == 5) {
|
||||
if (src[0] == 's' && src[1] == 'p' && src[2] == 'l' && src[3] == 'i' && src[4] == 't') {
|
||||
split = lazy = JS_TRUE;
|
||||
split = lazy = true;
|
||||
srclen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sobj) {
|
||||
sobj = split
|
||||
? split_setup(scx, JS_TRUE)
|
||||
: JS_NewGlobalObject(scx, &sandbox_class);
|
||||
if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
AutoValueRooter root(scx, BOOLEAN_TO_JSVAL(lazy));
|
||||
ok = JS_SetProperty(scx, sobj, "lazy", root.addr());
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (split)
|
||||
sobj = split_outerObject(scx, sobj);
|
||||
sobj = NewSandbox(cx, lazy, split);
|
||||
if (!sobj)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (srclen == 0) {
|
||||
*rval = OBJECT_TO_JSVAL(sobj);
|
||||
ok = JS_TRUE;
|
||||
} else {
|
||||
fp = JS_GetScriptedCaller(cx, NULL);
|
||||
JS_SetGlobalObject(scx, sobj);
|
||||
JS_ToggleOptions(scx, JSOPTION_DONT_REPORT_UNCAUGHT);
|
||||
OBJ_TO_INNER_OBJECT(scx, sobj);
|
||||
if (!sobj) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
|
||||
JS_TransferRequest(scx, cx);
|
||||
JS_ReportError(cx, "Invalid scope argument to evalcx");
|
||||
DestroyContext(scx, false);
|
||||
return JS_FALSE;
|
||||
}
|
||||
*rval = OBJECT_TO_JSVAL(sobj);
|
||||
if (srclen == 0)
|
||||
return true;
|
||||
|
||||
ok = JS_EvaluateUCScript(scx, sobj, src, srclen,
|
||||
fp->script->filename,
|
||||
JS_PCToLineNumber(cx, fp->script,
|
||||
fp->pc(cx)),
|
||||
rval);
|
||||
JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
|
||||
JSCrossCompartmentCall *call = NULL;
|
||||
if (sobj->isCrossCompartmentWrapper()) {
|
||||
sobj = sobj->unwrap();
|
||||
call = JS_EnterCrossCompartmentCall(cx, sobj);
|
||||
if (!call)
|
||||
return false;
|
||||
}
|
||||
|
||||
out:
|
||||
jsval exceptionValue = JSVAL_NULL;
|
||||
JSBool exception = !ok && JS_GetPendingException(scx, &exceptionValue);
|
||||
OBJ_TO_INNER_OBJECT(cx, sobj);
|
||||
ok = !!sobj;
|
||||
if (ok && !(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
|
||||
JS_ReportError(cx, "Invalid scope argument to evalcx");
|
||||
ok = false;
|
||||
}
|
||||
ok = ok && JS_EvaluateUCScript(cx, sobj, src, srclen,
|
||||
fp->script->filename,
|
||||
JS_PCToLineNumber(cx, fp->script, fp->pc(cx)),
|
||||
rval);
|
||||
|
||||
JS_TransferRequest(scx, cx);
|
||||
if (exception)
|
||||
JS_SetPendingException(cx, exceptionValue);
|
||||
else if (!ok)
|
||||
JS_ClearPendingException(cx);
|
||||
if (call) {
|
||||
JS_LeaveCrossCompartmentCall(call);
|
||||
ok = ok && cx->compartment->wrap(cx, rval);
|
||||
}
|
||||
|
||||
DestroyContext(scx, false);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -3848,7 +3851,9 @@ Wrap(JSContext *cx, uintN argc, jsval *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *wrapped = JSWrapper::wrap(cx, JSVAL_TO_OBJECT(v), NULL, NULL, NULL);
|
||||
JSObject *obj = JSVAL_TO_OBJECT(v);
|
||||
JSObject *wrapped = JSWrapper::New(cx, obj, obj->getProto(), obj->getParent(),
|
||||
&JSWrapper::singleton);
|
||||
if (!wrapped)
|
||||
return false;
|
||||
|
||||
@ -4911,36 +4916,53 @@ DestroyContext(JSContext *cx, bool withGC)
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
NewGlobalObject(JSContext *cx)
|
||||
NewGlobalObject(JSContext *cx, JSCrossCompartmentCall **callp)
|
||||
{
|
||||
JSObject *glob = JS_NewGlobalObject(cx, &global_class);
|
||||
JSCrossCompartmentCall *call = NULL;
|
||||
JSObject *glob;
|
||||
|
||||
if (callp) {
|
||||
glob = JS_NewCompartmentAndGlobalObject(cx, &global_class);
|
||||
if (!glob)
|
||||
return NULL;
|
||||
call = JS_EnterCrossCompartmentCall(cx, glob);
|
||||
if (!call)
|
||||
return NULL;
|
||||
} else {
|
||||
glob = JS_NewGlobalObject(cx, &global_class);
|
||||
if (!glob)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef LAZY_STANDARD_CLASSES
|
||||
JS_SetGlobalObject(cx, glob);
|
||||
#else
|
||||
if (!JS_InitStandardClasses(cx, glob))
|
||||
return NULL;
|
||||
goto bad;
|
||||
#endif
|
||||
#ifdef JS_HAS_CTYPES
|
||||
if (!JS_InitCTypesClass(cx, glob))
|
||||
return NULL;
|
||||
goto bad;
|
||||
#endif
|
||||
if (!JS_DefineFunctions(cx, glob, shell_functions))
|
||||
return NULL;
|
||||
goto bad;
|
||||
|
||||
JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
|
||||
if (!it)
|
||||
return NULL;
|
||||
if (!JS_DefineProperties(cx, it, its_props))
|
||||
return NULL;
|
||||
if (!JS_DefineFunctions(cx, it, its_methods))
|
||||
return NULL;
|
||||
{
|
||||
JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
|
||||
if (!it)
|
||||
goto bad;
|
||||
if (!JS_DefineProperties(cx, it, its_props))
|
||||
goto bad;
|
||||
if (!JS_DefineFunctions(cx, it, its_methods))
|
||||
goto bad;
|
||||
|
||||
if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
|
||||
its_setter, 0))
|
||||
return NULL;
|
||||
if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
|
||||
its_setter, JSPROP_READONLY))
|
||||
return NULL;
|
||||
if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
|
||||
its_setter, 0))
|
||||
goto bad;
|
||||
if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
|
||||
its_setter, JSPROP_READONLY))
|
||||
goto bad;
|
||||
}
|
||||
|
||||
#ifdef NARCISSUS
|
||||
{
|
||||
@ -4948,39 +4970,43 @@ NewGlobalObject(JSContext *cx)
|
||||
static const char Object_prototype[] = "Object.prototype";
|
||||
|
||||
if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0))
|
||||
return NULL;
|
||||
goto bad;
|
||||
|
||||
if (!JS_EvaluateScript(cx, glob,
|
||||
Object_prototype, sizeof Object_prototype - 1,
|
||||
NULL, 0, &v)) {
|
||||
return NULL;
|
||||
goto bad;
|
||||
}
|
||||
if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__",
|
||||
defineProperty, 5, 0)) {
|
||||
return NULL;
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (callp)
|
||||
*callp = call;
|
||||
return glob;
|
||||
|
||||
bad:
|
||||
if (call)
|
||||
JS_LeaveCrossCompartmentCall(call);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
{
|
||||
AutoNewCompartment compartment(cx);
|
||||
if (!compartment.init())
|
||||
return 1;
|
||||
|
||||
JS_BeginRequest(cx);
|
||||
|
||||
JSObject *glob = NewGlobalObject(cx);
|
||||
JSCrossCompartmentCall *call;
|
||||
JSObject *glob = NewGlobalObject(cx, &call);
|
||||
if (!glob)
|
||||
return 1;
|
||||
|
||||
JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
|
||||
if (!envobj || !JS_SetPrivate(cx, envobj, envp))
|
||||
return 1;
|
||||
goto bad;
|
||||
|
||||
#ifdef JSDEBUGGER
|
||||
/*
|
||||
@ -4988,7 +5014,7 @@ shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
*/
|
||||
jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
|
||||
if (!jsdc)
|
||||
return 1;
|
||||
goto bad;
|
||||
JSD_JSContextInUse(jsdc, cx);
|
||||
#ifdef JSD_LOWLEVEL_SOURCE
|
||||
JS_SetSourceHandler(rt, SendSourceToJSDebugger, jsdc);
|
||||
@ -4996,7 +5022,7 @@ shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
#ifdef JSDEBUGGER_JAVA_UI
|
||||
jsdjc = JSDJ_CreateContext();
|
||||
if (! jsdjc)
|
||||
return 1;
|
||||
goto bad;
|
||||
JSDJ_SetJSDContext(jsdjc, jsdc);
|
||||
java_env = JSDJ_CreateJavaVMAndStartDebugger(jsdjc);
|
||||
/*
|
||||
@ -5013,16 +5039,17 @@ shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
#ifdef JS_THREADSAFE
|
||||
class ShellWorkerHooks : public js::workers::WorkerHooks {
|
||||
public:
|
||||
JSObject *newGlobalObject(JSContext *cx) { return NewGlobalObject(cx); }
|
||||
JSObject *newGlobalObject(JSContext *cx) { return NewGlobalObject(cx, NULL); }
|
||||
};
|
||||
ShellWorkerHooks hooks;
|
||||
if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") ||
|
||||
!js::workers::init(cx, &hooks, glob, &gWorkers)) {
|
||||
return 1;
|
||||
goto bad;
|
||||
}
|
||||
#endif
|
||||
|
||||
int result = ProcessArgs(cx, glob, argv, argc);
|
||||
int result;
|
||||
result = ProcessArgs(cx, glob, argv, argc);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
js::workers::finish(cx, gWorkers);
|
||||
@ -5041,9 +5068,14 @@ shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
}
|
||||
#endif /* JSDEBUGGER */
|
||||
|
||||
out:
|
||||
JS_LeaveCrossCompartmentCall(call);
|
||||
JS_EndRequest(cx);
|
||||
|
||||
return result;
|
||||
|
||||
bad:
|
||||
result = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int
|
||||
|
Loading…
Reference in New Issue
Block a user