Bug 563099 - Compartments and wrappers API. r=gal.

--HG--
extra : rebase_source : cb34d0d0fc689fc6401d67e7f719344cd8e27655
This commit is contained in:
Jason Orendorff 2010-06-23 16:35:10 -05:00
parent b73234dea0
commit c79641e0db
20 changed files with 1122 additions and 523 deletions

View File

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

View File

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

View File

@ -2232,42 +2232,3 @@ JSContext::purge()
FreeOldArenas(runtime, &regexpPool);
}
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;
}
}

View File

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

View File

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

View File

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

View File

@ -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___ */

View File

@ -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());
}
}

View File

@ -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___ */

View File

@ -211,6 +211,8 @@ struct JSStackFrame
}
return false;
}
bool isDummyFrame() const { return !script && !fun; }
};
namespace js {

View File

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

View File

@ -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___ */

View File

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

View File

@ -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);
}
{

View File

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

View File

@ -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. */

View File

@ -344,6 +344,11 @@ class LazilyConstructed
JS_ASSERT(constructed);
return asT();
}
void destroy() {
ref().~T();
constructed = false;
}
};

View File

@ -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 &regs, 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;

View File

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

View File

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