bug 730221 - delegating serialization of script principals to the embedding. r=:luke,:bz

Currently to serialize principals stored in JSScript we have a rather complex
schema. First there is the transcode callback that the embedding must provide
to transcode principals using XDR API. Second we use rather complex glue code
to implement that callback in terms of writing/reading nsIObjectOutputStream/
nsIObjectInputStream. This glue code is duplicated in 3 places. All this can
be avoided if we simply delegate transcoding of principals to the caller. In
addition, at least in the case of the cached startup scripts we do not even
need to transcode the principals as the the cached scripts always have the
system principal so we can skip all the transcode complexity there.

The patch implemnts this idea. In particular, the code in JS engine
responsible for transcoding of principals is replaced by the single API
function JS_XDRSetPrincipals that the embedding can use to set principals for
decoded scripts and functions. Then the startup cache uses this to set the
principals for the decoded script to the system principals. The other two
places in nsJSContext::Serialize and  XBL_SerializeFunction that need to
serialize principals together with a function or script now uses common
utilities in nsXPConnect so the serialization complexity resides in the single
 place.
This commit is contained in:
Igor Bukanov 2012-02-13 14:10:04 +01:00
parent af2f6c1900
commit c8154dcd0e
22 changed files with 326 additions and 558 deletions

View File

@ -47,7 +47,6 @@ struct nsJSPrincipals : nsIPrincipal, JSPrincipals
{
static JSBool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
static void Destroy(JSPrincipals *jsprin);
static JSBool Transcode(JSXDRState *xdr, JSPrincipals **jsprinp);
/*
* Get a weak reference to nsIPrincipal associated with the given JS

View File

@ -501,7 +501,7 @@ private:
// when this happens -- this means that there was no script. Callers MUST
// pass in a non-null rv here.
static nsIPrincipal*
GetScriptPrincipal(JSContext* cx, JSScript* script, nsresult* rv);
GetScriptPrincipal(JSScript* script, nsresult* rv);
// Returns null if a principal cannot be found. Note that rv can be NS_OK
// when this happens -- this means that there was no script associated

View File

@ -86,70 +86,6 @@ nsJSPrincipals::Destroy(JSPrincipals *jsprin)
nsjsprin->Release();
}
/* static */ JSBool
nsJSPrincipals::Transcode(JSXDRState *xdr, JSPrincipals **jsprinp)
{
nsresult rv;
if (xdr->mode == JSXDR_ENCODE) {
nsIObjectOutputStream *stream =
reinterpret_cast<nsIObjectOutputStream*>(xdr->userdata);
// Flush xdr'ed data to the underlying object output stream.
uint32_t size;
char *data = (char*) ::JS_XDRMemGetData(xdr, &size);
rv = stream->Write32(size);
if (NS_SUCCEEDED(rv)) {
rv = stream->WriteBytes(data, size);
if (NS_SUCCEEDED(rv)) {
::JS_XDRMemResetData(xdr);
rv = stream->WriteObject(nsJSPrincipals::get(*jsprinp), true);
}
}
} else {
NS_ASSERTION(JS_XDRMemDataLeft(xdr) == 0, "XDR out of sync?!");
nsIObjectInputStream *stream =
reinterpret_cast<nsIObjectInputStream*>(xdr->userdata);
nsCOMPtr<nsIPrincipal> prin;
rv = stream->ReadObject(true, getter_AddRefs(prin));
if (NS_SUCCEEDED(rv)) {
PRUint32 size;
rv = stream->Read32(&size);
if (NS_SUCCEEDED(rv)) {
char *data = nsnull;
if (size != 0)
rv = stream->ReadBytes(size, &data);
if (NS_SUCCEEDED(rv)) {
char *olddata;
uint32_t oldsize;
// Any decode-mode JSXDRState whose userdata points to an
// nsIObjectInputStream instance must use nsMemory to Alloc
// and Free its data buffer. Swap the new buffer we just
// read for the old, exhausted data.
olddata = (char*) ::JS_XDRMemGetData(xdr, &oldsize);
nsMemory::Free(olddata);
::JS_XDRMemSetData(xdr, data, size);
*jsprinp = nsJSPrincipals::get(prin);
JS_HoldPrincipals(*jsprinp);
}
}
}
}
if (NS_FAILED(rv)) {
::JS_ReportError(xdr->cx, "can't %scode principals (failure code %x)",
(xdr->mode == JSXDR_ENCODE) ? "en" : "de",
(unsigned int) rv);
return JS_FALSE;
}
return JS_TRUE;
}
#ifdef DEBUG
// Defined here so one can do principals->dump() in the debugger

View File

@ -2170,8 +2170,7 @@ nsScriptSecurityManager::GetPrincipalFromContext(JSContext *cx,
// static
nsIPrincipal*
nsScriptSecurityManager::GetScriptPrincipal(JSContext *cx,
JSScript *script,
nsScriptSecurityManager::GetScriptPrincipal(JSScript *script,
nsresult* rv)
{
NS_PRECONDITION(rv, "Null out param");
@ -2180,7 +2179,7 @@ nsScriptSecurityManager::GetScriptPrincipal(JSContext *cx,
{
return nsnull;
}
JSPrincipals *jsp = JS_GetScriptPrincipals(cx, script);
JSPrincipals *jsp = JS_GetScriptPrincipals(script);
if (!jsp) {
*rv = NS_ERROR_FAILURE;
NS_ERROR("Script compiled without principals!");
@ -2253,7 +2252,7 @@ nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
return result;
}
return GetScriptPrincipal(cx, script, rv);
return GetScriptPrincipal(script, rv);
}
nsIPrincipal*
@ -2267,7 +2266,7 @@ nsScriptSecurityManager::GetFramePrincipal(JSContext *cx,
{
// Must be in a top-level script. Get principal from the script.
JSScript *script = JS_GetFrameScript(cx, fp);
return GetScriptPrincipal(cx, script, rv);
return GetScriptPrincipal(script, rv);
}
nsIPrincipal* result = GetFunctionObjectPrincipal(cx, obj, fp, rv);
@ -3381,7 +3380,6 @@ nsresult nsScriptSecurityManager::Init()
static const JSSecurityCallbacks securityCallbacks = {
CheckObjectAccess,
nsJSPrincipals::Subsume,
nsJSPrincipals::Transcode,
ObjectPrincipalFinder,
ContentSecurityPolicyPermitsJSAction
};

View File

@ -38,7 +38,6 @@
#include "nsXBLSerialize.h"
#include "nsDOMScriptObjectHolder.h"
#include "nsContentUtils.h"
#include "jsxdrapi.h"
nsresult
XBL_SerializeFunction(nsIScriptContext* aContext,
@ -46,69 +45,14 @@ XBL_SerializeFunction(nsIScriptContext* aContext,
JSObject* aFunctionObject)
{
JSContext* cx = aContext->GetNativeContext();
JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
if (!xdr)
return NS_ERROR_OUT_OF_MEMORY;
xdr->userdata = static_cast<void*>(aStream);
JSAutoRequest ar(cx);
nsresult rv;
if (!JS_XDRFunctionObject(xdr, &aFunctionObject)) {
rv = NS_ERROR_FAILURE;
} else {
uint32_t size;
const char* data = reinterpret_cast<const char*>
(JS_XDRMemGetData(xdr, &size));
NS_ASSERTION(data, "no decoded JSXDRState data!");
rv = aStream->Write32(size);
if (NS_SUCCEEDED(rv))
rv = aStream->WriteBytes(data, size);
}
JS_XDRDestroy(xdr);
return rv;
return nsContentUtils::XPConnect()->WriteFunction(aStream, cx, aFunctionObject);
}
// static
nsresult
XBL_DeserializeFunction(nsIScriptContext* aContext,
nsIObjectInputStream* aStream,
JSObject** aFunctionObject)
JSObject** aFunctionObjectp)
{
*aFunctionObject = nsnull;
PRUint32 size;
nsresult rv = aStream->Read32(&size);
if (NS_FAILED(rv))
return rv;
char* data;
rv = aStream->ReadBytes(size, &data);
if (NS_FAILED(rv))
return rv;
JSContext* cx = aContext->GetNativeContext();
JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
if (!xdr) {
rv = NS_ERROR_OUT_OF_MEMORY;
} else {
xdr->userdata = static_cast<void*>(aStream);
JSAutoRequest ar(cx);
JS_XDRMemSetData(xdr, data, size);
if (!JS_XDRFunctionObject(xdr, aFunctionObject)) {
rv = NS_ERROR_FAILURE;
}
uint32_t junk;
data = static_cast<char*>(JS_XDRMemGetData(xdr, &junk));
JS_XDRMemSetData(xdr, NULL, 0);
JS_XDRDestroy(xdr);
}
nsMemory::Free(data);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
return nsContentUtils::XPConnect()->ReadFunction(aStream, cx, aFunctionObjectp);
}

View File

@ -78,7 +78,6 @@
#include "jsdbgapi.h" // for JS_ClearWatchPointsForObject
#include "jswrapper.h"
#include "jsxdrapi.h"
#include "nsIArray.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
@ -1965,119 +1964,23 @@ nsJSContext::BindCompiledEventHandler(nsISupports* aTarget, JSObject* aScope,
nsresult
nsJSContext::Serialize(nsIObjectOutputStream* aStream, JSScript* aScriptObject)
{
if (!aScriptObject)
return NS_ERROR_FAILURE;
if (!aScriptObject)
return NS_ERROR_FAILURE;
nsresult rv;
JSContext* cx = mContext;
JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
if (! xdr)
return NS_ERROR_OUT_OF_MEMORY;
xdr->userdata = (void*) aStream;
JSAutoRequest ar(cx);
if (! ::JS_XDRScript(xdr, &aScriptObject)) {
rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
} else {
// Get the encoded JSXDRState data and write it. The JSXDRState owns
// this buffer memory and will free it beneath ::JS_XDRDestroy.
//
// If an XPCOM object needs to be written in the midst of the JS XDR
// encoding process, the C++ code called back from the JS engine (e.g.,
// nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
// from the JSXDRState to aStream, then write the object, then return
// to JS XDR code with xdr reset so new JS data is encoded at the front
// of the xdr's data buffer.
//
// However many XPCOM objects are interleaved with JS XDR data in the
// stream, when control returns here from ::JS_XDRScript, we'll have
// one last buffer of data to write to aStream.
uint32_t size;
const char* data = reinterpret_cast<const char*>
(::JS_XDRMemGetData(xdr, &size));
NS_ASSERTION(data, "no decoded JSXDRState data!");
rv = aStream->Write32(size);
if (NS_SUCCEEDED(rv))
rv = aStream->WriteBytes(data, size);
}
::JS_XDRDestroy(xdr);
if (NS_FAILED(rv)) return rv;
return rv;
return nsContentUtils::XPConnect()->WriteScript(aStream, mContext, aScriptObject);
}
nsresult
nsJSContext::Deserialize(nsIObjectInputStream* aStream,
nsScriptObjectHolder<JSScript>& aResult)
{
NS_TIME_FUNCTION_MIN(1.0);
PRUint32 size;
nsresult rv = aStream->Read32(&size);
if (NS_FAILED(rv)) return rv;
char* data;
rv = aStream->ReadBytes(size, &data);
if (NS_FAILED(rv)) return rv;
JSContext* cx = mContext;
JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
JSScript *result = nsnull;
if (! xdr) {
rv = NS_ERROR_OUT_OF_MEMORY;
} else {
xdr->userdata = (void*) aStream;
JSAutoRequest ar(cx);
::JS_XDRMemSetData(xdr, data, size);
if (! ::JS_XDRScript(xdr, &result)) {
rv = NS_ERROR_FAILURE; // principals deserialization error?
}
// Update data in case ::JS_XDRScript called back into C++ code to
// read an XPCOM object.
//
// In that case, the serialization process must have flushed a run
// of counted bytes containing JS data at the point where the XPCOM
// object starts, after which an encoding C++ callback from the JS
// XDR code must have written the XPCOM object directly into the
// nsIObjectOutputStream.
//
// The deserialization process will XDR-decode counted bytes up to
// but not including the XPCOM object, then call back into C++ to
// read the object, then read more counted bytes and hand them off
// to the JSXDRState, so more JS data can be decoded.
//
// This interleaving of JS XDR data and XPCOM object data may occur
// several times beneath the call to ::JS_XDRScript, above. At the
// end of the day, we need to free (via nsMemory) the data owned by
// the JSXDRState. So we steal it back, nulling xdr's buffer so it
// doesn't get passed to ::JS_free by ::JS_XDRDestroy.
uint32_t junk;
data = (char*) ::JS_XDRMemGetData(xdr, &junk);
if (data)
::JS_XDRMemSetData(xdr, NULL, 0);
::JS_XDRDestroy(xdr);
}
// If data is null now, it must have been freed while deserializing an
// XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
if (data)
nsMemory::Free(data);
NS_ASSERTION(aResult.getScriptTypeID()==JAVASCRIPT,
"Expecting JS script object holder");
// Now that we've cleaned up, handle the case when rv is a failure
// code, which could happen for all sorts of reasons above.
NS_ENSURE_SUCCESS(rv, rv);
return aResult.set(result);
NS_TIME_FUNCTION_MIN(1.0);
JSScript *script;
nsresult rv = nsContentUtils::XPConnect()->ReadScript(aStream, mContext, &script);
if (NS_FAILED(rv)) return rv;
return aResult.set(script);
}
nsIScriptGlobalObject *

View File

@ -117,7 +117,7 @@ BEGIN_TEST(test_cloneScriptWithPrincipals)
JSScript *script;
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsA);
CHECK(JS_GetScriptPrincipals(script) == principalsA);
CHECK(obj = JS_GetFunctionObject(fun));
}
@ -136,7 +136,7 @@ BEGIN_TEST(test_cloneScriptWithPrincipals)
JSScript *script;
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
CHECK(JS_GetScriptPrincipals(script) == principalsB);
JS::Value v;
JS::Value args[] = { JS::Int32Value(1) };
@ -147,7 +147,7 @@ BEGIN_TEST(test_cloneScriptWithPrincipals)
CHECK(JS_ObjectIsFunction(cx, funobj));
CHECK(fun = JS_ValueToFunction(cx, v));
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
CHECK(JS_GetScriptPrincipals(script) == principalsB);
}
return true;

View File

@ -11,7 +11,6 @@ ObjectPrincipalsFinder(JSObject *)
}
static const JSSecurityCallbacks seccb = {
NULL,
NULL,
NULL,
ObjectPrincipalsFinder,
@ -101,8 +100,8 @@ testInner(const char *asciiChars, JSPrincipals *principal, JSPrincipals *originP
CHECK(eval(asciiChars, principal, originPrincipal, &rval));
JSScript *script = JS_GetFunctionScript(cx, JSVAL_TO_OBJECT(rval)->toFunction());
CHECK(JS_GetScriptPrincipals(cx, script) == principal);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == originPrincipal);
CHECK(JS_GetScriptPrincipals(script) == principal);
CHECK(JS_GetScriptOriginPrincipals(script) == originPrincipal);
return true;
}

View File

@ -54,12 +54,27 @@ FreezeThawImpl(JSContext *cx, T *thing, JSBool (*xdrAction)(JSXDRState *xdr, T *
// thaw
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, memory, nbytes);
JSScript *script = GetScript(cx, thing);
JS_XDRSetPrincipals(r, script->principals, script->originPrincipals);
if (!xdrAction(r, &thing))
thing = NULL;
JS_XDRDestroy(r); // this frees `memory
return thing;
}
static JSScript *
GetScript(JSContext *cx, JSScript *script)
{
return script;
}
static JSScript *
GetScript(JSContext *cx, JSObject *funobj)
{
return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
}
static JSScript *
FreezeThaw(JSContext *cx, JSScript *script)
{
@ -77,76 +92,39 @@ static JSPrincipals testPrincipals[] = {
{ 1 },
};
static JSBool
TranscodePrincipals(JSXDRState *xdr, JSPrincipals **principalsp)
{
uint32_t index;
if (xdr->mode == JSXDR_ENCODE) {
JSPrincipals *p = *principalsp;
for (index = 0; ; ++index) {
if (index == mozilla::ArrayLength(testPrincipals))
return false;
if (p == &testPrincipals[index])
break;
}
}
if (!JS_XDRUint32(xdr, &index))
return false;
if (xdr->mode == JSXDR_DECODE) {
if (index >= mozilla::ArrayLength(testPrincipals))
return false;
*principalsp = &testPrincipals[index];
JS_HoldPrincipals(*principalsp);
}
return true;
}
BEGIN_TEST(testXDR_principals)
{
static const JSSecurityCallbacks seccb = {
NULL,
NULL,
TranscodePrincipals,
NULL,
NULL
};
JS_SetSecurityCallbacks(rt, &seccb);
JSScript *script;
for (int i = TEST_FIRST; i != TEST_END; ++i) {
script = createScriptViaXDR(NULL, NULL, i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(!JS_GetScriptOriginPrincipals(cx, script));
CHECK(!JS_GetScriptPrincipals(script));
CHECK(!JS_GetScriptOriginPrincipals(script));
script = createScriptViaXDR(NULL, NULL, i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(!JS_GetScriptOriginPrincipals(cx, script));
CHECK(!JS_GetScriptPrincipals(script));
CHECK(!JS_GetScriptOriginPrincipals(script));
script = createScriptViaXDR(&testPrincipals[0], NULL, i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[0]);
script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[0], i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[0]);
script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[1], i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[1]);
script = createScriptViaXDR(NULL, &testPrincipals[1], i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
CHECK(!JS_GetScriptPrincipals(script));
CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[1]);
}
return true;
@ -190,7 +168,7 @@ JSScript *createScriptViaXDR(JSPrincipals *prin, JSPrincipals *orig, int testCas
if (!funobj)
return NULL;
}
return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
return GetScript(cx, funobj);
}
END_TEST(testXDR_principals)

View File

@ -1530,16 +1530,6 @@ typedef void
typedef JSBool
(* JSSubsumePrincipalsOp)(JSPrincipals *principals1, JSPrincipals *principals2);
/*
* XDR-encode or -decode a principals instance, based on whether xdr->mode is
* JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE,
* in which case implementations must return a held (via JSPRINCIPALS_HOLD),
* non-null *principalsp out parameter. Return true on success, false on any
* error, which the implementation must have reported.
*/
typedef JSBool
(* JSPrincipalsTranscoder)(JSXDRState *xdr, JSPrincipals **principalsp);
/*
* Return a weak reference to the principals associated with obj, possibly via
* the immutable parent chain leading from obj to a top-level container (e.g.,
@ -4127,7 +4117,6 @@ JS_DropPrincipals(JSRuntime *rt, JSPrincipals *principals);
struct JSSecurityCallbacks {
JSCheckAccessOp checkObjectAccess;
JSSubsumePrincipalsOp subsumePrincipals;
JSPrincipalsTranscoder principalsTranscoder;
JSObjectPrincipalsFinder findObjectPrincipals;
JSCSPEvalChecker contentSecurityPolicyAllows;
};

View File

@ -482,13 +482,13 @@ JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
}
JS_PUBLIC_API(JSPrincipals *)
JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
JS_GetScriptPrincipals(JSScript *script)
{
return script->principals;
}
JS_PUBLIC_API(JSPrincipals *)
JS_GetScriptOriginPrincipals(JSContext *cx, JSScript *script)
JS_GetScriptOriginPrincipals(JSScript *script)
{
return script->originPrincipals;
}

View File

@ -220,10 +220,10 @@ extern JS_PUBLIC_API(JSNative)
JS_GetFunctionNative(JSContext *cx, JSFunction *fun);
extern JS_PUBLIC_API(JSPrincipals *)
JS_GetScriptPrincipals(JSContext *cx, JSScript *script);
JS_GetScriptPrincipals(JSScript *script);
extern JS_PUBLIC_API(JSPrincipals *)
JS_GetScriptOriginPrincipals(JSContext *cx, JSScript *script);
JS_GetScriptOriginPrincipals(JSScript *script);
/*
* Stack Frame Iterator

View File

@ -527,7 +527,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
prologLength = script->mainOffset;
JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
version = (uint32_t)script->getVersion() | (script->nfixed << 16);
lineno = (uint32_t)script->lineno;
lineno = script->lineno;
nslots = (uint32_t)script->nslots;
nslots = (uint32_t)((script->staticLevel << 16) | script->nslots);
natoms = script->natoms;
@ -665,9 +665,9 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
Foreground::free_(filename);
if (!script->filename)
return false;
if (!xdr->sharedFilename)
xdr->sharedFilename = script->filename;
}
if (!xdr->sharedFilename)
xdr->sharedFilename = script->filename;
} else if (scriptBits & (1 << SharedFilename)) {
JS_ASSERT(xdr->sharedFilename);
if (xdr->mode == JSXDR_DECODE)
@ -675,11 +675,14 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
}
if (xdr->mode == JSXDR_DECODE) {
JS_ASSERT(!script->principals);
JS_ASSERT(!script->originPrincipals);
script->lineno = lineno;
script->nslots = uint16_t(nslots);
script->staticLevel = uint16_t(nslots >> 16);
/* The origin principals must be normalized at this point. */
JS_ASSERT_IF(script->principals, script->originPrincipals);
JS_ASSERT_IF(xdr->principals, xdr->originPrincipals);
JS_ASSERT(!script->principals);
JS_ASSERT(!script->originPrincipals);
if (xdr->principals) {
script->principals = xdr->principals;
JS_HoldPrincipals(xdr->principals);
@ -690,12 +693,6 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
}
}
if (xdr->mode == JSXDR_DECODE) {
script->lineno = (unsigned)lineno;
script->nslots = uint16_t(nslots);
script->staticLevel = uint16_t(nslots >> 16);
}
for (i = 0; i != natoms; ++i) {
if (!js_XDRAtom(xdr, &script->atoms[i]))
return false;
@ -1711,9 +1708,7 @@ CloneScript(JSContext *cx, JSScript *script)
JS_XDRMemSetData(r, p, nbytes);
JS_XDRMemSetData(w, NULL, 0);
r->principals = cx->compartment->principals;
r->originPrincipals = JSScript::normalizeOriginPrincipals(cx->compartment->principals,
script->originPrincipals);
JS_XDRSetPrincipals(r, cx->compartment->principals, script->originPrincipals);
JSScript *newScript = NULL;
if (!XDRScript(r, &newScript))
return NULL;

View File

@ -236,7 +236,6 @@ JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx)
{
xdr->mode = mode;
xdr->cx = cx;
xdr->userdata = NULL;
xdr->sharedFilename = NULL;
xdr->principals = NULL;
xdr->originPrincipals = NULL;
@ -307,6 +306,14 @@ JS_XDRDestroy(JSXDRState *xdr)
cx->free_(xdr);
}
JS_PUBLIC_API(void)
JS_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals)
{
JS_ASSERT(xdr->mode == JSXDR_DECODE);
xdr->principals = principals;
xdr->originPrincipals = JSScript::normalizeOriginPrincipals(principals, originPrincipals);
}
JS_PUBLIC_API(JSBool)
JS_XDRUint8(JSXDRState *xdr, uint8_t *b)
{
@ -532,95 +539,10 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp)
return JS_TRUE;
}
static bool
XDRPrincipals(JSXDRState *xdr)
{
const uint8_t HAS_PRINCIPALS = 1;
const uint8_t HAS_ORIGIN = 2;
uint8_t flags = 0;
if (xdr->mode == JSXDR_ENCODE) {
if (xdr->principals)
flags |= HAS_PRINCIPALS;
/*
* For the common case when principals == originPrincipals we want to
* avoid serializing the same principal twice. As originPrincipals are
* normalized and principals imply originPrincipals we simply set
* HAS_ORIGIN only if originPrincipals is set and different from
* principals. During decoding we re-normalize originPrincipals.
*/
JS_ASSERT_IF(xdr->principals, xdr->originPrincipals);
if (xdr->originPrincipals && xdr->originPrincipals != xdr->principals)
flags |= HAS_ORIGIN;
}
if (!JS_XDRUint8(xdr, &flags))
return false;
if (flags & (HAS_PRINCIPALS | HAS_ORIGIN)) {
const JSSecurityCallbacks *scb = JS_GetSecurityCallbacks(xdr->cx->runtime);
if (xdr->mode == JSXDR_DECODE) {
if (!scb || !scb->principalsTranscoder) {
JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
JSMSG_CANT_DECODE_PRINCIPALS);
return false;
}
} else {
JS_ASSERT(scb);
JS_ASSERT(scb->principalsTranscoder);
}
if (flags & HAS_PRINCIPALS) {
if (!scb->principalsTranscoder(xdr, &xdr->principals))
return false;
}
if (flags & HAS_ORIGIN) {
if (!scb->principalsTranscoder(xdr, &xdr->originPrincipals))
return false;
} else if (xdr->mode == JSXDR_DECODE && xdr->principals) {
xdr->originPrincipals = xdr->principals;
JS_HoldPrincipals(xdr->principals);
}
}
return true;
}
namespace {
struct AutoDropXDRPrincipals {
JSXDRState *const xdr;
AutoDropXDRPrincipals(JSXDRState *xdr)
: xdr(xdr) { }
~AutoDropXDRPrincipals() {
if (xdr->mode == JSXDR_DECODE) {
if (xdr->principals)
JS_DropPrincipals(xdr->cx->runtime, xdr->principals);
if (xdr->originPrincipals)
JS_DropPrincipals(xdr->cx->runtime, xdr->originPrincipals);
}
xdr->principals = NULL;
xdr->originPrincipals = NULL;
}
};
} /* namespace anonymous */
JS_PUBLIC_API(JSBool)
JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
{
AutoDropXDRPrincipals drop(xdr);
if (xdr->mode == JSXDR_ENCODE) {
JSScript *script = (*objp)->toFunction()->script();
xdr->principals = script->principals;
xdr->originPrincipals = script->originPrincipals;
}
return XDRPrincipals(xdr) && XDRFunctionObject(xdr, objp);
return XDRFunctionObject(xdr, objp);
}
JS_PUBLIC_API(JSBool)
@ -650,16 +572,8 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
return false;
}
{
AutoDropXDRPrincipals drop(xdr);
if (xdr->mode == JSXDR_ENCODE) {
xdr->principals = script->principals;
xdr->originPrincipals = script->originPrincipals;
}
if (!XDRPrincipals(xdr) || !XDRScript(xdr, &script))
return false;
}
if (!XDRScript(xdr, &script))
return false;
if (xdr->mode == JSXDR_DECODE) {
JS_ASSERT(!script->compileAndGo);

View File

@ -109,7 +109,6 @@ struct JSXDRState {
JSXDRMode mode;
JSXDROps *ops;
JSContext *cx;
void *userdata;
const char *sharedFilename;
JSPrincipals *principals;
JSPrincipals *originPrincipals;
@ -136,6 +135,16 @@ JS_XDRMemResetData(JSXDRState *xdr);
extern JS_PUBLIC_API(void)
JS_XDRDestroy(JSXDRState *xdr);
/*
* Set principals that should be assigned to decoded scripts and functions.
* The principals is not held via JS_HoldPrincipals/JS_DropPrincipals unless
* they are stored in a decoded script. Thus the caller must either ensure
* that the principals outlive the XDR instance or are explicitly set to NULL
* before they release by the caller.
*/
extern JS_PUBLIC_API(void)
JS_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals);
extern JS_PUBLIC_API(JSBool)
JS_XDRUint8(JSXDRState *xdr, uint8_t *b);

View File

@ -4859,7 +4859,6 @@ JSSecurityCallbacks securityCallbacks = {
CheckObjectAccess,
NULL,
NULL,
NULL,
NULL
};

View File

@ -50,6 +50,8 @@
#include "nsIInterfaceInfoManager.idl"
#include "nsIExceptionService.idl"
#include "nsIVariant.idl"
#include "nsIObjectOutputStream.idl"
#include "nsIObjectInputStream.idl"
%{ C++
#include "jspubtd.h"
@ -70,6 +72,7 @@ class nsWrapperCache;
[ptr] native JSValConstPtr(const jsval);
native JSPropertyOp(JSPropertyOp);
native JSEqualityOp(JSEqualityOp);
[ptr] native JSScriptPtr(JSScript);
[ptr] native voidPtrPtr(void*);
[ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer);
[ref] native nsCCTraversalCallbackRef(nsCycleCollectionTraversalCallback);
@ -405,7 +408,7 @@ enum nsGCType {
};
%}
[uuid(0213cb40-2dd5-4ac8-a9d3-157bd53c3824)]
[uuid(08ad2253-3ed6-40ed-beec-6472f2d8dc7f)]
interface nsIXPConnect : nsISupports
{
%{ C++
@ -801,4 +804,18 @@ interface nsIXPConnect : nsISupports
*/
[noscript] void setDebugModeWhenPossible(in boolean mode,
in boolean allowSyncDisable);
[noscript] void writeScript(in nsIObjectOutputStream aStream,
in JSContextPtr aJSContext,
in JSScriptPtr aJSScript);
[noscript] JSScriptPtr readScript(in nsIObjectInputStream aStream,
in JSContextPtr aJSContext);
[noscript] void writeFunction(in nsIObjectOutputStream aStream,
in JSContextPtr aJSContext,
in JSObjectPtr aJSObject);
[noscript] JSObjectPtr readFunction(in nsIObjectInputStream aStream,
in JSContextPtr aJSContext);
};

View File

@ -751,7 +751,7 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
NS_ENSURE_SUCCESS(rv, rv);
if (cache) {
rv = ReadCachedScript(cache, cachePath, cx, &script);
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
if (NS_SUCCEEDED(rv)) {
LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
} else {
@ -934,7 +934,7 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
if (writeToCache) {
// We successfully compiled the script, so cache it.
rv = WriteCachedScript(cache, cachePath, cx, script);
rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
// Don't treat failure to write as fatal, since we might be working
// with a read-only cache.

View File

@ -39,153 +39,70 @@
#include "nsScriptLoader.h"
#include "jsapi.h"
#include "jsdbgapi.h"
#include "jsxdrapi.h"
#include "nsJSPrincipals.h"
#include "mozilla/scache/StartupCache.h"
#include "mozilla/scache/StartupCacheUtils.h"
using namespace mozilla::scache;
static nsresult
ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
JSScript **script)
{
*script = nsnull;
PRUint32 size;
nsresult rv = stream->Read32(&size);
NS_ENSURE_SUCCESS(rv, rv);
char *data;
rv = stream->ReadBytes(size, &data);
NS_ENSURE_SUCCESS(rv, rv);
JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
xdr->userdata = stream;
JS_XDRMemSetData(xdr, data, size);
if (!JS_XDRScript(xdr, script)) {
rv = NS_ERROR_FAILURE;
}
// Update data in case ::JS_XDRScript called back into C++ code to
// read an XPCOM object.
//
// In that case, the serialization process must have flushed a run
// of counted bytes containing JS data at the point where the XPCOM
// object starts, after which an encoding C++ callback from the JS
// XDR code must have written the XPCOM object directly into the
// nsIObjectOutputStream.
//
// The deserialization process will XDR-decode counted bytes up to
// but not including the XPCOM object, then call back into C++ to
// read the object, then read more counted bytes and hand them off
// to the JSXDRState, so more JS data can be decoded.
//
// This interleaving of JS XDR data and XPCOM object data may occur
// several times beneath the call to ::JS_XDRScript, above. At the
// end of the day, we need to free (via nsMemory) the data owned by
// the JSXDRState. So we steal it back, nulling xdr's buffer so it
// doesn't get passed to ::JS_free by ::JS_XDRDestroy.
uint32_t length;
data = static_cast<char*>(JS_XDRMemGetData(xdr, &length));
JS_XDRMemSetData(xdr, nsnull, 0);
JS_XDRDestroy(xdr);
// If data is null now, it must have been freed while deserializing an
// XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
nsMemory::Free(data);
return rv;
}
static nsresult
WriteScriptToStream(JSContext *cx, JSScript *script,
nsIObjectOutputStream *stream)
{
JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
xdr->userdata = stream;
nsresult rv = NS_OK;
if (JS_XDRScript(xdr, &script)) {
// Get the encoded JSXDRState data and write it. The JSXDRState owns
// this buffer memory and will free it beneath ::JS_XDRDestroy.
//
// If an XPCOM object needs to be written in the midst of the JS XDR
// encoding process, the C++ code called back from the JS engine (e.g.,
// nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
// from the JSXDRState to aStream, then write the object, then return
// to JS XDR code with xdr reset so new JS data is encoded at the front
// of the xdr's data buffer.
//
// However many XPCOM objects are interleaved with JS XDR data in the
// stream, when control returns here from ::JS_XDRScript, we'll have
// one last buffer of data to write to aStream.
uint32_t size;
const char* data = reinterpret_cast<const char*>
(JS_XDRMemGetData(xdr, &size));
NS_ASSERTION(data, "no decoded JSXDRState data!");
rv = stream->Write32(size);
if (NS_SUCCEEDED(rv)) {
rv = stream->WriteBytes(data, size);
}
} else {
rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
}
JS_XDRDestroy(xdr);
return rv;
}
// We only serialize scripts with system principals. So we don't serialize the
// principals when writing a script. Instead, when reading it back, we set the
// principals to the system principals.
nsresult
ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSScript **script)
ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx,
nsIPrincipal *systemPrincipal, JSScript **script)
{
nsresult rv;
nsAutoArrayPtr<char> buf;
PRUint32 len;
rv = cache->GetBuffer(PromiseFlatCString(uri).get(), getter_Transfers(buf),
&len);
nsresult rv = cache->GetBuffer(PromiseFlatCString(uri).get(),
getter_Transfers(buf), &len);
if (NS_FAILED(rv)) {
return rv; // don't warn since NOT_AVAILABLE is an ok error
}
nsCOMPtr<nsIObjectInputStream> ois;
rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
NS_ENSURE_SUCCESS(rv, rv);
buf.forget();
JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
if (!xdr) {
return NS_ERROR_OUT_OF_MEMORY;
}
return ReadScriptFromStream(cx, ois, script);
::JS_XDRMemSetData(xdr, buf, len);
::JS_XDRSetPrincipals(xdr, nsJSPrincipals::get(systemPrincipal), nsnull);
JSBool ok = ::JS_XDRScript(xdr, script);
// Prevent XDR from automatically freeing the buffer.
::JS_XDRMemSetData(xdr, NULL, 0);
::JS_XDRDestroy(xdr);
return ok ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
nsresult
WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSScript *script)
WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx,
nsIPrincipal *systemPrincipal, JSScript *script)
{
MOZ_ASSERT(JS_GetScriptPrincipals(script) == nsJSPrincipals::get(systemPrincipal));
MOZ_ASSERT(JS_GetScriptOriginPrincipals(script) == nsJSPrincipals::get(systemPrincipal));
JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
if (!xdr) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv;
if (!::JS_XDRScript(xdr, &script)) {
rv = NS_ERROR_OUT_OF_MEMORY;
} else {
uint32_t size;
char* data = static_cast<char *>(::JS_XDRMemGetData(xdr, &size));
MOZ_ASSERT(size);
rv = cache->PutBuffer(PromiseFlatCString(uri).get(), data, size);
}
nsCOMPtr<nsIObjectOutputStream> oos;
nsCOMPtr<nsIStorageStream> storageStream;
rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
getter_AddRefs(storageStream),
true);
NS_ENSURE_SUCCESS(rv, rv);
rv = WriteScriptToStream(cx, script, oos);
oos->Close();
NS_ENSURE_SUCCESS(rv, rv);
nsAutoArrayPtr<char> buf;
PRUint32 len;
rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
NS_ENSURE_SUCCESS(rv, rv);
rv = cache->PutBuffer(PromiseFlatCString(uri).get(), buf, len);
::JS_XDRDestroy(xdr);
return rv;
}

View File

@ -52,9 +52,12 @@ class StartupCache;
nsresult
ReadCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri,
JSContext *cx, JSScript **script);
JSContext *cx, nsIPrincipal *systemPrincipal,
JSScript **script);
nsresult
WriteCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri,
JSContext *cx, JSScript *script);
JSContext *cx, nsIPrincipal *systemPrincipal,
JSScript *script);
#endif /* mozJSLoaderUtils_h */

View File

@ -329,7 +329,7 @@ mozJSSubScriptLoader::LoadSubScript(const nsAString& url,
script = nsnull;
if (cache)
rv = ReadCachedScript(cache, cachePath, cx, &script);
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
if (!script) {
rv = ReadScript(uri, cx, targetObj, charset,
static_cast<const char*>(uriStr.get()), serv,
@ -349,7 +349,7 @@ mozJSSubScriptLoader::LoadSubScript(const nsAString& url,
}
if (cache && ok && writeScript) {
WriteCachedScript(cache, cachePath, cx, script);
WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
}
return NS_OK;

View File

@ -53,6 +53,7 @@
#include "jsatom.h"
#include "jsfriendapi.h"
#include "jsgc.h"
#include "jsxdrapi.h"
#include "dom_quickstubs.h"
#include "nsNullPrincipal.h"
#include "nsIURI.h"
@ -508,7 +509,7 @@ struct NoteWeakMapsTracer : public js::WeakMapTracer
};
static void
TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
void *k, JSGCTraceKind kkind,
void *v, JSGCTraceKind vkind)
{
@ -595,7 +596,7 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
#endif
GetRuntime()->AddXPConnectRoots(cb);
NoteWeakMapsTracer trc(GetRuntime()->GetJSRuntime(), TraceWeakMapping, cb);
js::TraceWeakMaps(&trc);
@ -2794,6 +2795,173 @@ nsXPConnect::NotifyDidPaint()
return NS_OK;
}
const PRUint8 HAS_PRINCIPALS_FLAG = 1;
const PRUint8 HAS_ORIGIN_PRINCIPALS_FLAG = 2;
static nsresult
WriteScriptOrFunction(nsIObjectOutputStream *stream, JSContext *cx,
JSScript *script, JSObject *functionObj)
{
// Exactly one of script or functionObj must be given
MOZ_ASSERT(!script != !functionObj);
if (!script)
script = JS_GetFunctionScript(cx, JS_GetObjectFunction(functionObj));
nsIPrincipal *principal =
nsJSPrincipals::get(JS_GetScriptPrincipals(script));
nsIPrincipal *originPrincipal =
nsJSPrincipals::get(JS_GetScriptOriginPrincipals(script));
PRUint8 flags = 0;
if (principal)
flags |= HAS_PRINCIPALS_FLAG;
// Optimize for the common case when originPrincipals == principals. As
// originPrincipals is set to principals when the former is null we can
// simply skip the originPrincipals when they are the same as principals.
if (originPrincipal && originPrincipal != principal)
flags |= HAS_ORIGIN_PRINCIPALS_FLAG;
nsresult rv = stream->Write8(flags);
if (NS_FAILED(rv))
return rv;
if (flags & HAS_PRINCIPALS_FLAG) {
rv = stream->WriteObject(principal, true);
if (NS_FAILED(rv))
return rv;
}
if (flags & HAS_ORIGIN_PRINCIPALS_FLAG) {
rv = stream->WriteObject(originPrincipal, true);
if (NS_FAILED(rv))
return rv;
}
JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
if (!xdr)
return NS_ERROR_OUT_OF_MEMORY;
JSBool ok;
{
JSAutoRequest ar(cx);
if (functionObj)
ok = JS_XDRFunctionObject(xdr, &functionObj);
else
ok = JS_XDRScript(xdr, &script);
}
if (!ok) {
rv = NS_ERROR_OUT_OF_MEMORY;
} else {
// Get the encoded JSXDRState data and write it. The JSXDRState owns
// this buffer memory and will free it beneath JS_XDRDestroy.
uint32_t size;
const char* data = reinterpret_cast<const char*>(::JS_XDRMemGetData(xdr, &size));
NS_ASSERTION(data, "no decoded JSXDRState data!");
rv = stream->Write32(size);
if (NS_SUCCEEDED(rv))
rv = stream->WriteBytes(data, size);
}
JS_XDRDestroy(xdr);
return rv;
}
static nsresult
ReadScriptOrFunction(nsIObjectInputStream *stream, JSContext *cx,
JSScript **scriptp, JSObject **functionObjp)
{
// Exactly one of script or functionObj must be given
MOZ_ASSERT(!scriptp != !functionObjp);
PRUint8 flags;
nsresult rv = stream->Read8(&flags);
if (NS_FAILED(rv))
return rv;
nsJSPrincipals* principal = nsnull;
nsCOMPtr<nsIPrincipal> readPrincipal;
if (flags & HAS_PRINCIPALS_FLAG) {
rv = stream->ReadObject(true, getter_AddRefs(readPrincipal));
if (NS_FAILED(rv))
return rv;
principal = nsJSPrincipals::get(readPrincipal);
}
nsJSPrincipals* originPrincipal = nsnull;
nsCOMPtr<nsIPrincipal> readOriginPrincipal;
if (flags & HAS_ORIGIN_PRINCIPALS_FLAG) {
rv = stream->ReadObject(true, getter_AddRefs(readOriginPrincipal));
if (NS_FAILED(rv))
return rv;
originPrincipal = nsJSPrincipals::get(readOriginPrincipal);
}
PRUint32 size;
rv = stream->Read32(&size);
if (NS_FAILED(rv))
return rv;
char* data;
rv = stream->ReadBytes(size, &data);
if (NS_FAILED(rv))
return rv;
JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
if (!xdr) {
nsMemory::Free(data);
return NS_ERROR_OUT_OF_MEMORY;
}
JS_XDRMemSetData(xdr, data, size);
JS_XDRSetPrincipals(xdr, principal, originPrincipal);
JSBool ok;
{
JSAutoRequest ar(cx);
if (scriptp)
ok = JS_XDRScript(xdr, scriptp);
else
ok = JS_XDRFunctionObject(xdr, functionObjp);
}
// We cannot rely on XDR automatically freeing the data memory as we must
// use nsMemory::Free to release it.
JS_XDRMemSetData(xdr, NULL, 0);
JS_XDRDestroy(xdr);
nsMemory::Free(data);
return ok ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsXPConnect::WriteScript(nsIObjectOutputStream *stream, JSContext *cx, JSScript *script)
{
return WriteScriptOrFunction(stream, cx, script, nsnull);
}
NS_IMETHODIMP
nsXPConnect::ReadScript(nsIObjectInputStream *stream, JSContext *cx, JSScript **scriptp)
{
return ReadScriptOrFunction(stream, cx, scriptp, nsnull);
}
NS_IMETHODIMP
nsXPConnect::WriteFunction(nsIObjectOutputStream *stream, JSContext *cx, JSObject *functionObj)
{
return WriteScriptOrFunction(stream, cx, nsnull, functionObj);
}
NS_IMETHODIMP
nsXPConnect::ReadFunction(nsIObjectInputStream *stream, JSContext *cx, JSObject **functionObjp)
{
return ReadScriptOrFunction(stream, cx, nsnull, functionObjp);
}
/* These are here to be callable from a debugger */
JS_BEGIN_EXTERN_C
JS_EXPORT_API(void) DumpJSStack()