bug 725576 - serialize principals only once per top-level script. r=luke

--HG--
extra : rebase_source : 92b82ff8a8d03d6176bf1e43329a59b77ca83de4
This commit is contained in:
Igor Bukanov 2012-02-10 13:40:34 +01:00
parent 180e2b965e
commit 3b9873045c
10 changed files with 586 additions and 212 deletions

View File

@ -155,7 +155,7 @@ js::IsIdentifier(JSLinearString *str)
/* Initialize members that aren't initialized in |init|. */ /* Initialize members that aren't initialized in |init|. */
TokenStream::TokenStream(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin) TokenStream::TokenStream(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin)
: tokens(), cursor(), lookahead(), flags(), listenerTSData(), tokenbuf(cx), : tokens(), cursor(), lookahead(), flags(), listenerTSData(), tokenbuf(cx),
cx(cx), originPrincipals(originPrin ? originPrin : prin) cx(cx), originPrincipals(JSScript::normalizeOriginPrincipals(prin, originPrin))
{ {
if (originPrincipals) if (originPrincipals)
JSPRINCIPALS_HOLD(cx, originPrincipals); JSPRINCIPALS_HOLD(cx, originPrincipals);

View File

@ -6,6 +6,8 @@
#include "tests.h" #include "tests.h"
#include "jsapi.h" #include "jsapi.h"
#include "jsdbgapi.h"
#include "jsxdrapi.h"
BEGIN_TEST(test_cloneScript) BEGIN_TEST(test_cloneScript)
{ {
@ -48,3 +50,122 @@ BEGIN_TEST(test_cloneScript)
return true; return true;
} }
END_TEST(test_cloneScript) END_TEST(test_cloneScript)
void
DestroyPrincipals(JSContext *cx, JSPrincipals *principals)
{
delete principals;
}
struct Principals : public JSPrincipals
{
public:
Principals(const char *name)
{
refcount = 0;
codebase = const_cast<char *>(name);
destroy = DestroyPrincipals;
subsume = NULL;
}
};
class AutoDropPrincipals
{
JSContext *cx;
JSPrincipals *principals;
public:
AutoDropPrincipals(JSContext *cx, JSPrincipals *principals)
: cx(cx), principals(principals)
{
JSPRINCIPALS_HOLD(cx, principals);
}
~AutoDropPrincipals()
{
JSPRINCIPALS_DROP(cx, principals);
}
};
JSBool
TranscodePrincipals(JSXDRState *xdr, JSPrincipals **principalsp)
{
return JS_XDRBytes(xdr, reinterpret_cast<char *>(principalsp), sizeof(*principalsp));
}
BEGIN_TEST(test_cloneScriptWithPrincipals)
{
JSSecurityCallbacks cbs = {
NULL,
TranscodePrincipals,
NULL,
NULL
};
JS_SetRuntimeSecurityCallbacks(rt, &cbs);
JSPrincipals *principalsA = new Principals("A");
AutoDropPrincipals dropA(cx, principalsA);
JSPrincipals *principalsB = new Principals("B");
AutoDropPrincipals dropB(cx, principalsB);
JSObject *A, *B;
CHECK(A = createGlobal(principalsA));
CHECK(B = createGlobal(principalsB));
const char *argnames[] = { "arg" };
const char *source = "return function() { return arg; }";
JSObject *obj;
// Compile in A
{
JSAutoEnterCompartment a;
if (!a.enter(cx, A))
return false;
JSFunction *fun;
CHECK(fun = JS_CompileFunctionForPrincipals(cx, A, principalsA, "f",
mozilla::ArrayLength(argnames), argnames,
source, strlen(source), __FILE__, 1));
JSScript *script;
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsA);
CHECK(obj = JS_GetFunctionObject(fun));
}
// Clone into B
{
JSAutoEnterCompartment b;
if (!b.enter(cx, B))
return false;
JSObject *cloned;
CHECK(cloned = JS_CloneFunctionObject(cx, obj, B));
JSFunction *fun;
CHECK(fun = JS_ValueToFunction(cx, JS::ObjectValue(*cloned)));
JSScript *script;
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
JS::Value v;
JS::Value args[] = { JS::Int32Value(1) };
CHECK(JS_CallFunctionValue(cx, B, JS::ObjectValue(*cloned), 1, args, &v));
CHECK(v.isObject());
JSObject *funobj = &v.toObject();
CHECK(JS_ObjectIsFunction(cx, funobj));
CHECK(fun = JS_ValueToFunction(cx, v));
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
}
return true;
}
END_TEST(test_cloneScriptWithPrincipals)

View File

@ -157,10 +157,40 @@ Error(JSContext *cx, const char (&input)[N])
AutoInflatedString str(cx); AutoInflatedString str(cx);
jsval dummy; jsval dummy;
str = input; str = input;
CHECK(!JS_ParseJSON(cx, str.chars(), str.length(), &dummy));
JS_ClearPendingException(cx); ContextPrivate p = {0, 0};
CHECK(!JS_GetContextPrivate(cx));
JS_SetContextPrivate(cx, &p);
JSErrorReporter old = JS_SetErrorReporter(cx, reportJSONEror);
JSBool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
JS_SetErrorReporter(cx, old);
JS_SetContextPrivate(cx, NULL);
CHECK(!ok);
CHECK(!p.unexpectedErrorCount);
CHECK(p.expectedErrorCount == 1);
/* We do not execute JS, so there should be no exception thrown. */
CHECK(!JS_IsExceptionPending(cx));
return true; return true;
} }
struct ContextPrivate {
unsigned unexpectedErrorCount;
unsigned expectedErrorCount;
};
static void
reportJSONEror(JSContext *cx, const char *message, JSErrorReport *report)
{
ContextPrivate *p = static_cast<ContextPrivate *>(JS_GetContextPrivate(cx));
if (report->errorNumber == JSMSG_JSON_BAD_PARSE)
p->expectedErrorCount++;
else
p->unexpectedErrorCount++;
}
END_TEST(testParseJSON_error) END_TEST(testParseJSON_error)
static JSBool static JSBool

View File

@ -6,6 +6,236 @@
#include "jsscript.h" #include "jsscript.h"
#include "jsxdrapi.h" #include "jsxdrapi.h"
static JSScript *
CompileScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,
JSPrincipals *principals, JSPrincipals *originPrincipals,
const char *bytes, size_t nbytes,
const char *filename, uintN lineno,
JSVersion version)
{
size_t nchars;
if (!JS_DecodeBytes(cx, bytes, nbytes, NULL, &nchars))
return NULL;
jschar *chars = static_cast<jschar *>(JS_malloc(cx, nchars * sizeof(jschar)));
if (!chars)
return NULL;
JS_ALWAYS_TRUE(JS_DecodeBytes(cx, bytes, nbytes, chars, &nchars));
JSScript *script = JS_CompileUCScriptForPrincipalsVersionOrigin(cx, obj,
principals, originPrincipals,
chars, nchars,
filename, lineno, version);
free(chars);
return script;
}
template<typename T>
T *
FreezeThawImpl(JSContext *cx, T *thing, JSBool (*xdrAction)(JSXDRState *xdr, T **))
{
// freeze
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
if (!w)
return NULL;
void *memory = NULL;
uint32_t nbytes;
if (xdrAction(w, &thing)) {
void *p = JS_XDRMemGetData(w, &nbytes);
if (p) {
memory = JS_malloc(cx, nbytes);
if (memory)
memcpy(memory, p, nbytes);
}
}
JS_XDRDestroy(w);
if (!memory)
return NULL;
// thaw
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, memory, nbytes);
if (!xdrAction(r, &thing))
thing = NULL;
JS_XDRDestroy(r); // this frees `memory
return thing;
}
static JSScript *
FreezeThaw(JSContext *cx, JSScript *script)
{
return FreezeThawImpl(cx, script, JS_XDRScript);
}
static JSObject *
FreezeThaw(JSContext *cx, JSObject *funobj)
{
return FreezeThawImpl(cx, funobj, JS_XDRFunctionObject);
}
static JSBool
SubsumePrincipals(JSPrincipals *, JSPrincipals *)
{
return true;
}
static JSPrincipals testPrincipals[] = {
{ const_cast<char *>("foo.bar"), 1, NULL, SubsumePrincipals },
{ const_cast<char *>("dot.com"), 1, NULL, SubsumePrincipals },
};
static JSBool
CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp)
{
return true;
}
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];
JSPRINCIPALS_HOLD(xdr->cx, *principalsp);
}
return true;
}
BEGIN_TEST(testXDR_principals)
{
static JSSecurityCallbacks seccb = {
CheckAccess,
TranscodePrincipals,
NULL,
NULL
};
JS_SetRuntimeSecurityCallbacks(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));
script = createScriptViaXDR(NULL, NULL, i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(!JS_GetScriptOriginPrincipals(cx, script));
script = createScriptViaXDR(&testPrincipals[0], NULL, i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, 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]);
script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[1], i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
script = createScriptViaXDR(NULL, &testPrincipals[1], i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
}
return true;
}
enum TestCase {
TEST_FIRST,
TEST_SCRIPT = TEST_FIRST,
TEST_FUNCTION,
TEST_SERIALIZED_FUNCTION,
TEST_END
};
JSScript *createScriptViaXDR(JSPrincipals *prin, JSPrincipals *orig, int testCase)
{
const char src[] =
"function f() { return 1; }\n"
"f;\n";
JSScript *script = CompileScriptForPrincipalsVersionOrigin(cx, global, prin, orig,
src, strlen(src), "test", 1,
JSVERSION_DEFAULT);
if (!script)
return NULL;
if (testCase == TEST_SCRIPT || testCase == TEST_SERIALIZED_FUNCTION) {
script = FreezeThaw(cx, script);
if (!script)
return NULL;
if (testCase == TEST_SCRIPT)
return script;
}
JS::Value v;
JSBool ok = JS_ExecuteScript(cx, global, script, &v);
if (!ok || !v.isObject())
return NULL;
JSObject *funobj = &v.toObject();
if (testCase == TEST_FUNCTION) {
funobj = FreezeThaw(cx, funobj);
if (!funobj)
return NULL;
}
return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
}
END_TEST(testXDR_principals)
BEGIN_TEST(testXDR_atline)
{
JS_ToggleOptions(cx, JSOPTION_ATLINE);
CHECK(JS_GetOptions(cx) & JSOPTION_ATLINE);
const char src[] =
"//@line 100 \"foo\"\n"
"function nested() { }\n"
"//@line 200 \"bar\"\n"
"nested;\n";
JSScript *script = JS_CompileScript(cx, global, src, strlen(src), "internal", 1);
CHECK(script);
CHECK(script = FreezeThaw(cx, script));
CHECK(!strcmp("bar", JS_GetScriptFilename(cx, script)));
JS::Value v;
JSBool ok = JS_ExecuteScript(cx, global, script, &v);
CHECK(ok);
CHECK(v.isObject());
JSObject *funobj = &v.toObject();
script = JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
CHECK(!strcmp("foo", JS_GetScriptFilename(cx, script)));
return true;
}
END_TEST(testXDR_atline)
BEGIN_TEST(testXDR_bug506491) BEGIN_TEST(testXDR_bug506491)
{ {
const char *s = const char *s =
@ -19,24 +249,8 @@ BEGIN_TEST(testXDR_bug506491)
JSScript *script = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__); JSScript *script = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__);
CHECK(script); CHECK(script);
// freeze script = FreezeThaw(cx, script);
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE); CHECK(script);
CHECK(w);
CHECK(JS_XDRScript(w, &script));
uint32_t nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
CHECK(p);
void *frozen = JS_malloc(cx, nbytes);
CHECK(frozen);
js_memcpy(frozen, p, nbytes);
JS_XDRDestroy(w);
// thaw
script = NULL;
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, frozen, nbytes);
CHECK(JS_XDRScript(r, &script));
JS_XDRDestroy(r); // this frees `frozen`
// execute // execute
jsvalRoot v2(cx); jsvalRoot v2(cx);
@ -59,24 +273,8 @@ BEGIN_TEST(testXDR_bug516827)
JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__); JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
CHECK(script); CHECK(script);
// freeze script = FreezeThaw(cx, script);
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE); CHECK(script);
CHECK(w);
CHECK(JS_XDRScript(w, &script));
uint32_t nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
CHECK(p);
void *frozen = JS_malloc(cx, nbytes);
CHECK(frozen);
js_memcpy(frozen, p, nbytes);
JS_XDRDestroy(w);
// thaw
script = NULL;
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, frozen, nbytes);
CHECK(JS_XDRScript(r, &script));
JS_XDRDestroy(r); // this frees `frozen`
// execute with null result meaning no result wanted // execute with null result meaning no result wanted
CHECK(JS_ExecuteScript(cx, global, script, NULL)); CHECK(JS_ExecuteScript(cx, global, script, NULL));

View File

@ -4835,7 +4835,8 @@ JS_OPTIONS_TO_TCFLAGS(JSContext *cx)
} }
static JSScript * static JSScript *
CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *principals, CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj,
JSPrincipals *principals, JSPrincipals *originPrincipals,
const jschar *chars, size_t length, const jschar *chars, size_t length,
const char *filename, uintN lineno, JSVersion version) const char *filename, uintN lineno, JSVersion version)
{ {
@ -4846,7 +4847,7 @@ CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *p
AutoLastFrameCheck lfc(cx); AutoLastFrameCheck lfc(cx);
uint32_t tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_SCRIPT_GLOBAL; uint32_t tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_SCRIPT_GLOBAL;
return frontend::CompileScript(cx, obj, NULL, principals, NULL, tcflags, return frontend::CompileScript(cx, obj, NULL, principals, originPrincipals, tcflags,
chars, length, filename, lineno, version); chars, length, filename, lineno, version);
} }
@ -4858,8 +4859,21 @@ JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
JSVersion version) JSVersion version)
{ {
AutoVersionAPI avi(cx, version); AutoVersionAPI avi(cx, version);
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, chars, length, filename, lineno, return CompileUCScriptForPrincipalsCommon(cx, obj, principals, NULL, chars, length,
avi.version()); filename, lineno, avi.version());
}
extern JS_PUBLIC_API(JSScript *)
JS_CompileUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
JSPrincipals *originPrincipals,
const jschar *chars, size_t length,
const char *filename, uintN lineno,
JSVersion version)
{
AutoVersionAPI avi(cx, version);
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, originPrincipals,
chars, length, filename, lineno, avi.version());
} }
JS_PUBLIC_API(JSScript *) JS_PUBLIC_API(JSScript *)
@ -4867,8 +4881,8 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *prin
const jschar *chars, size_t length, const jschar *chars, size_t length,
const char *filename, uintN lineno) const char *filename, uintN lineno)
{ {
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, chars, length, filename, lineno, return CompileUCScriptForPrincipalsCommon(cx, obj, principals, NULL, chars, length,
cx->findVersion()); filename, lineno, cx->findVersion());
} }
JS_PUBLIC_API(JSScript *) JS_PUBLIC_API(JSScript *)

View File

@ -4258,6 +4258,17 @@ JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
const jschar *chars, size_t length, const jschar *chars, size_t length,
const char *filename, uintN lineno, const char *filename, uintN lineno,
JSVersion version); JSVersion version);
/*
* If originPrincipals is null, then the value of principals is used as origin
* principals for the compiled script.
*/
extern JS_PUBLIC_API(JSScript *)
JS_CompileUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
JSPrincipals *originPrincipals,
const jschar *chars, size_t length,
const char *filename, uintN lineno,
JSVersion version);
extern JS_PUBLIC_API(JSScript *) extern JS_PUBLIC_API(JSScript *)
JS_CompileUTF8File(JSContext *cx, JSObject *obj, const char *filename); JS_CompileUTF8File(JSContext *cx, JSObject *obj, const char *filename);
@ -4422,7 +4433,8 @@ JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
* A script's originPrincipals may be retrieved through the debug API (via * A script's originPrincipals may be retrieved through the debug API (via
* JS_GetScriptOriginPrincipals) and the originPrincipals are transitively * JS_GetScriptOriginPrincipals) and the originPrincipals are transitively
* assigned to any nested scripts (including scripts dynamically created via * assigned to any nested scripts (including scripts dynamically created via
* eval and the Function constructor). * eval and the Function constructor). If originPrincipals is null, then the
* value of principals is used as origin principals for the script.
*/ */
extern JS_PUBLIC_API(JSBool) extern JS_PUBLIC_API(JSBool)
JS_EvaluateUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj, JS_EvaluateUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,

View File

@ -441,7 +441,9 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
SavedCallerFun, SavedCallerFun,
StrictModeCode, StrictModeCode,
UsesEval, UsesEval,
UsesArguments UsesArguments,
OwnFilename,
SharedFilename
}; };
uint32_t length, lineno, nslots; uint32_t length, lineno, nslots;
@ -449,17 +451,12 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
uint32_t prologLength, version, encodedClosedCount; uint32_t prologLength, version, encodedClosedCount;
uint16_t nClosedArgs = 0, nClosedVars = 0; uint16_t nClosedArgs = 0, nClosedVars = 0;
uint32_t nTypeSets = 0; uint32_t nTypeSets = 0;
uint32_t encodeable, sameOriginPrincipals;
JSSecurityCallbacks *callbacks;
uint32_t scriptBits = 0; uint32_t scriptBits = 0;
JSContext *cx = xdr->cx; JSContext *cx = xdr->cx;
JSScript *script; JSScript *script;
nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0; nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
jssrcnote *notes = NULL; jssrcnote *notes = NULL;
XDRScriptState *state = xdr->state;
JS_ASSERT(state);
/* XDR arguments, local vars, and upvars. */ /* XDR arguments, local vars, and upvars. */
uint16_t nargs, nvars, nupvars; uint16_t nargs, nvars, nupvars;
@ -611,6 +608,12 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
scriptBits |= (1 << UsesEval); scriptBits |= (1 << UsesEval);
if (script->usesArguments) if (script->usesArguments)
scriptBits |= (1 << UsesArguments); scriptBits |= (1 << UsesArguments);
if (script->filename) {
scriptBits |= (script->filename != xdr->sharedFilename)
? (1 << OwnFilename)
: (1 << SharedFilename);
}
JS_ASSERT(!script->compileAndGo); JS_ASSERT(!script->compileAndGo);
JS_ASSERT(!script->hasSingletons); JS_ASSERT(!script->hasSingletons);
} }
@ -686,52 +689,39 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
return false; return false;
} }
if (xdr->mode == JSXDR_DECODE && state->filename) { if (scriptBits & (1 << OwnFilename)) {
if (!state->filenameSaved) { char *filename;
const char *filename = state->filename; if (xdr->mode == JSXDR_ENCODE)
filename = SaveScriptFilename(xdr->cx, filename); filename = const_cast<char *>(script->filename);
xdr->cx->free_((void *) state->filename); if (!JS_XDRCString(xdr, &filename))
state->filename = filename; return false;
state->filenameSaved = true; if (xdr->mode == JSXDR_DECODE) {
if (!filename) script->filename = SaveScriptFilename(xdr->cx, filename);
Foreground::free_(filename);
if (!script->filename)
return false; return false;
if (!xdr->sharedFilename)
xdr->sharedFilename = script->filename;
} }
script->filename = state->filename; } else if (scriptBits & (1 << SharedFilename)) {
JS_ASSERT(xdr->sharedFilename);
if (xdr->mode == JSXDR_DECODE)
script->filename = xdr->sharedFilename;
} }
JS_ASSERT_IF(xdr->mode == JSXDR_ENCODE, state->filename == script->filename); if (xdr->mode == JSXDR_DECODE) {
JS_ASSERT(!script->principals);
JS_ASSERT(!script->originPrincipals);
callbacks = JS_GetSecurityCallbacks(cx); /* The origin principals must be normalized at this point. */
if (xdr->mode == JSXDR_ENCODE) JS_ASSERT_IF(script->principals, script->originPrincipals);
encodeable = script->principals && callbacks && callbacks->principalsTranscoder; if (xdr->principals) {
script->principals = xdr->principals;
if (!JS_XDRUint32(xdr, &encodeable)) JSPRINCIPALS_HOLD(cx, xdr->principals);
return false;
if (encodeable) {
if (!callbacks || !callbacks->principalsTranscoder) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_CANT_DECODE_PRINCIPALS);
return false;
} }
if (xdr->originPrincipals) {
if (!callbacks->principalsTranscoder(xdr, &script->principals)) script->originPrincipals = xdr->originPrincipals;
return false; JSPRINCIPALS_HOLD(cx, xdr->originPrincipals);
if (xdr->mode == JSXDR_ENCODE)
sameOriginPrincipals = script->principals == script->originPrincipals;
if (!JS_XDRUint32(xdr, &sameOriginPrincipals))
return false;
if (sameOriginPrincipals) {
if (xdr->mode == JSXDR_DECODE) {
script->originPrincipals = script->principals;
JSPRINCIPALS_HOLD(cx, script->originPrincipals);
}
} else {
if (!callbacks->principalsTranscoder(xdr, &script->originPrincipals))
return false;
} }
} }
@ -1717,29 +1707,8 @@ CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, uintN *linenop
*origin = script->originPrincipals; *origin = script->originPrincipals;
} }
class DisablePrincipalsTranscoding {
JSSecurityCallbacks *callbacks;
JSPrincipalsTranscoder temp;
public:
DisablePrincipalsTranscoding(JSContext *cx)
: callbacks(JS_GetRuntimeSecurityCallbacks(cx->runtime)),
temp(NULL)
{
if (callbacks) {
temp = callbacks->principalsTranscoder;
callbacks->principalsTranscoder = NULL;
}
}
~DisablePrincipalsTranscoding() {
if (callbacks)
callbacks->principalsTranscoder = temp;
}
};
class AutoJSXDRState { class AutoJSXDRState {
public: public:
AutoJSXDRState(JSXDRState *x AutoJSXDRState(JSXDRState *x
JS_GUARD_OBJECT_NOTIFIER_PARAM) JS_GUARD_OBJECT_NOTIFIER_PARAM)
: xdr(x) : xdr(x)
@ -1756,7 +1725,12 @@ public:
return xdr; return xdr;
} }
private: JSXDRState* operator->() const
{
return xdr;
}
private:
JSXDRState *const xdr; JSXDRState *const xdr;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER JS_DECL_USE_GUARD_OBJECT_NOTIFIER
}; };
@ -1766,18 +1740,11 @@ CloneScript(JSContext *cx, JSScript *script)
{ {
JS_ASSERT(cx->compartment != script->compartment()); JS_ASSERT(cx->compartment != script->compartment());
// serialize script /* Serialize script. */
AutoJSXDRState w(JS_XDRNewMem(cx, JSXDR_ENCODE)); AutoJSXDRState w(JS_XDRNewMem(cx, JSXDR_ENCODE));
if (!w) if (!w)
return NULL; return NULL;
// we don't want gecko to transcribe our principals for us
DisablePrincipalsTranscoding disable(cx);
XDRScriptState wstate(w);
#ifdef DEBUG
wstate.filename = script->filename;
#endif
if (!XDRScript(w, &script)) if (!XDRScript(w, &script))
return NULL; return NULL;
@ -1786,35 +1753,25 @@ CloneScript(JSContext *cx, JSScript *script)
if (!p) if (!p)
return NULL; return NULL;
// de-serialize script /* De-serialize script. */
AutoJSXDRState r(JS_XDRNewMem(cx, JSXDR_DECODE)); AutoJSXDRState r(JS_XDRNewMem(cx, JSXDR_DECODE));
if (!r) if (!r)
return NULL; return NULL;
// Hand p off from w to r. Don't want them to share the data /*
// mem, lest they both try to free it in JS_XDRDestroy * Hand p off from w to r. Don't want them to share the data mem, lest
* they both try to free it in JS_XDRDestroy.
*/
JS_XDRMemSetData(r, p, nbytes); JS_XDRMemSetData(r, p, nbytes);
JS_XDRMemSetData(w, NULL, 0); JS_XDRMemSetData(w, NULL, 0);
XDRScriptState rstate(r); r->principals = cx->compartment->principals;
rstate.filename = script->filename; r->originPrincipals = JSScript::normalizeOriginPrincipals(cx->compartment->principals,
rstate.filenameSaved = true; script->originPrincipals);
JSScript *newScript = NULL; JSScript *newScript = NULL;
if (!XDRScript(r, &newScript)) if (!XDRScript(r, &newScript))
return NULL; return NULL;
// set the proper principals for the script's new compartment
// the originPrincipals are not related to compartment, so just copy
newScript->principals = newScript->compartment()->principals;
newScript->originPrincipals = script->originPrincipals;
if (!newScript->originPrincipals)
newScript->originPrincipals = newScript->principals;
if (newScript->principals) {
JSPRINCIPALS_HOLD(cx, newScript->principals);
JSPRINCIPALS_HOLD(cx, newScript->originPrincipals);
}
return newScript; return newScript;
} }

View File

@ -672,7 +672,7 @@ struct JSScript : public js::gc::Cell {
} }
/* /*
* computedSizeOfData() is the in-use size of all the data sections. * computedSizeOfData() is the in-use size of all the data sections.
* sizeOfData() is the size of the block allocated to hold all the data sections * sizeOfData() is the size of the block allocated to hold all the data sections
* (which can be larger than the in-use size). * (which can be larger than the in-use size).
*/ */
@ -829,6 +829,11 @@ struct JSScript : public js::gc::Cell {
static inline void writeBarrierPost(JSScript *script, void *addr); static inline void writeBarrierPost(JSScript *script, void *addr);
static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; } static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
static JSPrincipals *normalizeOriginPrincipals(JSPrincipals *principals,
JSPrincipals *originPrincipals) {
return originPrincipals ? originPrincipals : principals;
}
}; };
/* If this fails, padding_ can be removed. */ /* If this fails, padding_ can be removed. */
@ -932,7 +937,7 @@ extern JSScript *
CloneScript(JSContext *cx, JSScript *script); CloneScript(JSContext *cx, JSScript *script);
/* /*
* NB: after a successful JSXDR_DECODE, js_XDRScript callers must do any * NB: after a successful JSXDR_DECODE, XDRScript callers must do any
* required subsequent set-up of owning function or script object and then call * required subsequent set-up of owning function or script object and then call
* js_CallNewScriptHook. * js_CallNewScriptHook.
*/ */

View File

@ -237,7 +237,9 @@ JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx)
xdr->mode = mode; xdr->mode = mode;
xdr->cx = cx; xdr->cx = cx;
xdr->userdata = NULL; xdr->userdata = NULL;
xdr->state = NULL; xdr->sharedFilename = NULL;
xdr->principals = NULL;
xdr->originPrincipals = NULL;
} }
JS_PUBLIC_API(JSXDRState *) JS_PUBLIC_API(JSXDRState *)
@ -394,19 +396,6 @@ JS_XDRCString(JSXDRState *xdr, char **sp)
return JS_TRUE; return JS_TRUE;
} }
JS_PUBLIC_API(JSBool)
JS_XDRCStringOrNull(JSXDRState *xdr, char **sp)
{
uint32_t null = (*sp == NULL);
if (!JS_XDRUint32(xdr, &null))
return JS_FALSE;
if (null) {
*sp = NULL;
return JS_TRUE;
}
return JS_XDRCString(xdr, sp);
}
static JSBool static JSBool
XDRChars(JSXDRState *xdr, jschar *chars, uint32_t nchars) XDRChars(JSXDRState *xdr, jschar *chars, uint32_t nchars)
{ {
@ -543,44 +532,100 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp)
return JS_TRUE; return JS_TRUE;
} }
XDRScriptState::XDRScriptState(JSXDRState *x) static bool
: xdr(x) XDRPrincipals(JSXDRState *xdr)
, filename(NULL)
, filenameSaved(false)
{ {
JS_ASSERT(!xdr->state); const uint8_t HAS_PRINCIPALS = 1;
const uint8_t HAS_ORIGIN = 2;
xdr->state = this; 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)) {
JSSecurityCallbacks *scb = JS_GetSecurityCallbacks(xdr->cx);
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;
JSPRINCIPALS_HOLD(xdr->cx, xdr->principals);
}
}
return true;
} }
XDRScriptState::~XDRScriptState() namespace {
{
xdr->state = NULL; struct AutoDropXDRPrincipals {
if (xdr->mode == JSXDR_DECODE && filename && !filenameSaved) JSXDRState *const xdr;
xdr->cx->free_((void *)filename);
} AutoDropXDRPrincipals(JSXDRState *xdr)
: xdr(xdr) { }
~AutoDropXDRPrincipals() {
if (xdr->mode == JSXDR_DECODE) {
if (xdr->principals)
JSPRINCIPALS_DROP(xdr->cx, xdr->principals);
if (xdr->originPrincipals)
JSPRINCIPALS_DROP(xdr->cx, xdr->originPrincipals);
}
xdr->principals = NULL;
xdr->originPrincipals = NULL;
}
};
} /* namespace anonymous */
JS_PUBLIC_API(JSBool) JS_PUBLIC_API(JSBool)
JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
{ {
XDRScriptState fstate(xdr); AutoDropXDRPrincipals drop(xdr);
if (xdr->mode == JSXDR_ENCODE) { if (xdr->mode == JSXDR_ENCODE) {
JSFunction* fun = (*objp)->toFunction(); JSScript *script = (*objp)->toFunction()->script();
fstate.filename = fun->script()->filename; xdr->principals = script->principals;
xdr->originPrincipals = script->originPrincipals;
} }
if (!JS_XDRCStringOrNull(xdr, (char **) &fstate.filename)) return XDRPrincipals(xdr) && XDRFunctionObject(xdr, objp);
return false;
return XDRFunctionObject(xdr, objp);
} }
JS_PUBLIC_API(JSBool) JS_PUBLIC_API(JSBool)
JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
{ {
JS_ASSERT(!xdr->state);
JSScript *script; JSScript *script;
uint32_t magic; uint32_t magic;
uint32_t bytecodeVer; uint32_t bytecodeVer;
@ -605,17 +650,16 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
return false; return false;
} }
XDRScriptState state(xdr); {
if (!xdr->state) AutoDropXDRPrincipals drop(xdr);
return false; if (xdr->mode == JSXDR_ENCODE) {
xdr->principals = script->principals;
if (xdr->mode == JSXDR_ENCODE) xdr->originPrincipals = script->originPrincipals;
state.filename = script->filename; }
if (!JS_XDRCStringOrNull(xdr, (char **) &state.filename))
return false; if (!XDRPrincipals(xdr) || !XDRScript(xdr, &script))
return false;
if (!XDRScript(xdr, &script)) }
return false;
if (xdr->mode == JSXDR_DECODE) { if (xdr->mode == JSXDR_DECODE) {
JS_ASSERT(!script->compileAndGo); JS_ASSERT(!script->compileAndGo);

View File

@ -105,28 +105,14 @@ typedef struct JSXDROps {
void (*finalize)(JSXDRState *); void (*finalize)(JSXDRState *);
} JSXDROps; } JSXDROps;
struct JSXDRState;
namespace js {
class XDRScriptState {
public:
XDRScriptState(JSXDRState *x);
~XDRScriptState();
JSXDRState *xdr;
const char *filename;
bool filenameSaved;
};
} /* namespace JS */
struct JSXDRState { struct JSXDRState {
JSXDRMode mode; JSXDRMode mode;
JSXDROps *ops; JSXDROps *ops;
JSContext *cx; JSContext *cx;
void *userdata; void *userdata;
js::XDRScriptState *state; const char *sharedFilename;
JSPrincipals *principals;
JSPrincipals *originPrincipals;
}; };
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)
@ -165,9 +151,6 @@ JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32_t len);
extern JS_PUBLIC_API(JSBool) extern JS_PUBLIC_API(JSBool)
JS_XDRCString(JSXDRState *xdr, char **sp); JS_XDRCString(JSXDRState *xdr, char **sp);
extern JS_PUBLIC_API(JSBool)
JS_XDRCStringOrNull(JSXDRState *xdr, char **sp);
extern JS_PUBLIC_API(JSBool) extern JS_PUBLIC_API(JSBool)
JS_XDRString(JSXDRState *xdr, JSString **strp); JS_XDRString(JSXDRState *xdr, JSString **strp);
@ -209,7 +192,9 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp);
* and saved versions. If deserialization fails, the data should be * and saved versions. If deserialization fails, the data should be
* invalidated if possible. * invalidated if possible.
*/ */
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 107) #define JSXDR_BYTECODE_VERSION (0xb973c0de - 108)
JS_END_EXTERN_C
/* /*
* Library-private functions. * Library-private functions.
@ -217,6 +202,14 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp);
extern JSBool extern JSBool
js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
JS_END_EXTERN_C /*
* 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 principal outlive the XDR instance or are explicitly set to NULL
* before they release by the caller.
*/
extern void
js_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals);
#endif /* ! jsxdrapi_h___ */ #endif /* ! jsxdrapi_h___ */