Make functions per compartment, and deep copy instead of clone them if needed (584789, r=mrbkap).

This commit is contained in:
Andreas Gal 2010-09-29 19:42:22 -07:00
Родитель 398734ce25
Коммит 64d575a614
7 изменённых файлов: 181 добавлений и 11 удалений

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

@ -50,6 +50,7 @@ CPPSRCS = \
tests.cpp \
selfTest.cpp \
testClassGetter.cpp \
testCloneScript.cpp \
testConservativeGC.cpp \
testContexts.cpp \
testDebugger.cpp \

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

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99:
*
* Test script cloning.
*/
#include "tests.h"
#include "jsapi.h"
BEGIN_TEST(test_cloneScript)
{
JSObject *A, *B;
CHECK(A = createGlobal());
CHECK(B = createGlobal());
const char *source =
"var i = 0;\n"
"var sum = 0;\n"
"while (i < 10) {\n"
" sum += i;\n"
" ++i;\n"
"}\n"
"(sum);\n";
JSObject *obj;
// compile for A
{
JSAutoEnterCompartment a;
if (!a.enter(cx, A))
return false;
JSFunction *fun;
CHECK(fun = JS_CompileFunction(cx, A, "f", 0, NULL, source, strlen(source), __FILE__, 1));
CHECK(obj = JS_GetFunctionObject(fun));
}
// clone into B
{
JSAutoEnterCompartment b;
if (!b.enter(cx, B))
return false;
CHECK(JS_CloneFunctionObject(cx, obj, B));
}
return true;
}
END_TEST(test_cloneScript)

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

@ -4516,6 +4516,7 @@ JS_NewScriptObject(JSContext *cx, JSScript *script)
* described in the comment for JSScript::u.object.
*/
JS_ASSERT(script->u.object);
JS_ASSERT(script != JSScript::emptyScript());
return script->u.object;
}

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

@ -567,8 +567,11 @@ class CompartmentChecker
}
void check(JSScript *script) {
if (script && script->u.object)
check(script->u.object);
if (script && script != JSScript::emptyScript()) {
check(script->compartment);
if (script->u.object)
check(script->u.object);
}
}
void check(JSString *) { /* nothing for now */ }

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

@ -2935,14 +2935,47 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
JS_ASSERT(parent);
JS_ASSERT(proto);
/*
* The cloned function object does not need the extra JSFunction members
* beyond JSObject as it points to fun via the private slot.
*/
JSObject *clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent);
if (!clone)
return NULL;
clone->setPrivate(fun);
JSObject *clone;
if (cx->compartment == fun->compartment()) {
/*
* The cloned function object does not need the extra JSFunction members
* beyond JSObject as it points to fun via the private slot.
*/
clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent);
if (!clone)
return NULL;
clone->setPrivate(fun);
} else {
/*
* Across compartments we have to deep copy JSFunction and clone the
* script (for interpreted functions).
*/
clone = NewFunction(cx, parent);
if (!clone)
return NULL;
JSFunction *cfun = (JSFunction *) clone;
cfun->nargs = fun->nargs;
cfun->flags = fun->flags;
cfun->u = fun->getFunctionPrivate()->u;
cfun->atom = fun->atom;
clone->setPrivate(cfun);
if (cfun->isInterpreted()) {
JSScript *script = cfun->u.i.script;
JS_ASSERT(script);
JS_ASSERT(script->compartment == fun->compartment());
JS_ASSERT(script->compartment != cx->compartment);
cfun->u.i.script = js_CloneScript(cx, script);
if (!cfun->u.i.script)
return NULL;
if (cfun->u.i.script != JSScript::emptyScript()) {
#ifdef CHECK_SCRIPT_OWNER
cfun->u.i.script->owner = NULL;
#endif
js_CallNewScriptHook(cx, cfun->u.i.script, cfun);
}
}
}
return clone;
}

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

@ -86,7 +86,7 @@ static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
false, /* debugMode */
#endif
const_cast<jsbytecode*>(emptyScriptCode),
{0, NULL}, NULL, 0, 0, 0,
{0, NULL}, NULL, NULL, 0, 0, 0,
0, /* nClosedArgs */
0, /* nClosedVars */
NULL, {NULL},
@ -1065,6 +1065,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
nsrcnotes * sizeof(jssrcnote) ==
(uint8 *)script + size);
script->compartment = cx->compartment;
#ifdef CHECK_SCRIPT_OWNER
script->owner = cx->thread;
#endif
@ -1641,6 +1642,82 @@ js_GetScriptLineExtent(JSScript *script)
return 1 + lineno - script->lineno;
}
class DisablePrincipalsTranscoding {
JSSecurityCallbacks *callbacks;
JSPrincipalsTranscoder temp;
public:
DisablePrincipalsTranscoding(JSContext *cx) {
callbacks = JS_GetRuntimeSecurityCallbacks(cx->runtime);
if (callbacks) {
temp = callbacks->principalsTranscoder;
callbacks->principalsTranscoder = NULL;
}
}
~DisablePrincipalsTranscoding() {
if (callbacks)
callbacks->principalsTranscoder = temp;
}
};
JSScript *
js_CloneScript(JSContext *cx, JSScript *script)
{
// don't clone the empty script
if (script == JSScript::emptyScript())
return script;
JS_ASSERT(cx->compartment != script->compartment);
JS_ASSERT(script->compartment);
// serialize script
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
if (!w)
return NULL;
// we don't want gecko to transcribe our principals for us
DisablePrincipalsTranscoding disable(cx);
if (!JS_XDRScript(w, &script)) {
JS_XDRDestroy(w);
return NULL;
}
uint32 nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
if (!p) {
JS_XDRDestroy(w);
return NULL;
}
// de-serialize script
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
if (!r) {
JS_XDRDestroy(w);
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
JS_XDRMemSetData(r, p, nbytes);
JS_XDRMemSetData(w, NULL, 0);
// We can't use the public API because it makes a script object.
if (!js_XDRScript(r, &script, true, NULL))
return NULL;
JS_XDRDestroy(r);
JS_XDRDestroy(w);
// set the proper principals for the script
script->principals = script->compartment->principals;
if (script->principals)
JSPRINCIPALS_HOLD(cx, script->principals);
return script;
}
#ifdef JS_METHODJIT
bool
JSScript::isValidJitCode(void *jcode)

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

@ -250,6 +250,7 @@ struct JSScript {
jsbytecode *main; /* main entry point, after predef'ing prolog */
JSAtomMap atomMap; /* maps immediate index to literal struct */
JSCompartment *compartment; /* compartment the script was compiled for */
const char *filename; /* source filename or null */
uint32 lineno; /* base line number of script */
uint16 nslots; /* vars plus maximum stack depth */
@ -277,6 +278,7 @@ struct JSScript {
JSObject *object;
JSScript *nextToGC; /* next to GC in rt->scriptsToGC list */
} u;
#ifdef CHECK_SCRIPT_OWNER
JSThread *owner; /* for thread-safe life-cycle assertions */
#endif
@ -548,6 +550,9 @@ js_GetOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
return op;
}
extern JSScript *
js_CloneScript(JSContext *cx, JSScript *script);
/*
* If magic is non-null, js_XDRScript succeeds on magic number mismatch but
* returns false in *magic; it reflects a match via a true *magic out param.