Bug 897655 - Use off thread parsing when loading scripts from XUL documents, r=billm,bz,luke.

This commit is contained in:
Brian Hackett 2013-08-15 14:14:43 -07:00
parent cd03bdb26a
commit dbe559cdac
34 changed files with 658 additions and 245 deletions

View File

@ -2548,13 +2548,56 @@ nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
return rv;
}
class NotifyOffThreadScriptCompletedRunnable : public nsRunnable
{
nsRefPtr<nsIOffThreadScriptReceiver> mReceiver;
// Note: there is no need to root the script, it is protected against GC
// until FinishOffThreadScript is called on it.
JSScript *mScript;
public:
NotifyOffThreadScriptCompletedRunnable(already_AddRefed<nsIOffThreadScriptReceiver> aReceiver,
JSScript *aScript)
: mReceiver(aReceiver), mScript(aScript)
{}
NS_DECL_NSIRUNNABLE
};
NS_IMETHODIMP
NotifyOffThreadScriptCompletedRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
// Note: this unroots mScript so that it is available to be collected by the
// JS GC. The receiver needs to root the script before performing a call that
// could GC.
JS::FinishOffThreadScript(nsJSRuntime::sRuntime, mScript);
return mReceiver->OnScriptCompileComplete(mScript, mScript ? NS_OK : NS_ERROR_FAILURE);
}
static void
OffThreadScriptReceiverCallback(JSScript *script, void *ptr)
{
// Be careful not to adjust the refcount on the receiver, as this callback
// may be invoked off the main thread.
nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(ptr);
nsRefPtr<NotifyOffThreadScriptCompletedRunnable> notify =
new NotifyOffThreadScriptCompletedRunnable(
already_AddRefed<nsIOffThreadScriptReceiver>(aReceiver), script);
NS_DispatchToMainThread(notify);
}
nsresult
nsXULPrototypeScript::Compile(const PRUnichar* aText,
int32_t aTextLength,
nsIURI* aURI,
uint32_t aLineNo,
nsIDocument* aDocument,
nsIScriptGlobalObject* aGlobal)
nsIScriptGlobalObject* aGlobal,
nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
{
// We'll compile the script using the prototype document's special
// script object as the parent. This ensures that we won't end up
@ -2604,11 +2647,23 @@ nsXULPrototypeScript::Compile(const PRUnichar* aText,
: JS::CompileOptions::SAVE_SOURCE);
JS::RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
xpc_UnmarkGrayObject(scope);
JSScript* script = JS::Compile(cx, scope, options,
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options)) {
if (!JS::CompileOffThread(cx, scope, options,
static_cast<const jschar*>(aText), aTextLength,
OffThreadScriptReceiverCallback,
static_cast<void*>(aOffThreadReceiver))) {
return NS_ERROR_OUT_OF_MEMORY;
}
// This reference will be consumed by the NotifyOffThreadScriptCompletedRunnable.
NS_ADDREF(aOffThreadReceiver);
} else {
JSScript* script = JS::Compile(cx, scope, options,
static_cast<const jschar*>(aText), aTextLength);
if (!script)
return NS_ERROR_OUT_OF_MEMORY;
Set(script);
if (!script)
return NS_ERROR_OUT_OF_MEMORY;
Set(script);
}
return NS_OK;
}

View File

@ -231,7 +231,8 @@ public:
nsresult Compile(const PRUnichar* aText, int32_t aTextLength,
nsIURI* aURI, uint32_t aLineNo,
nsIDocument* aDocument,
nsIScriptGlobalObject* aGlobal);
nsIScriptGlobalObject* aGlobal,
nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr);
void UnlinkJSObjects();

View File

@ -354,9 +354,9 @@ NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument)
// QueryInterface implementation for XULDocument
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument)
NS_INTERFACE_TABLE_INHERITED4(XULDocument, nsIXULDocument,
NS_INTERFACE_TABLE_INHERITED5(XULDocument, nsIXULDocument,
nsIDOMXULDocument, nsIStreamLoaderObserver,
nsICSSLoaderObserver)
nsICSSLoaderObserver, nsIOffThreadScriptReceiver)
NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument)
@ -3465,7 +3465,6 @@ XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
return NS_OK;
}
NS_IMETHODIMP
XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
nsISupports* context,
@ -3505,17 +3504,6 @@ XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
return NS_OK;
}
// Clear mCurrentScriptProto now, but save it first for use below in
// the compile/execute code, and in the while loop that resumes walks
// of other documents that raced to load this script
nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
mCurrentScriptProto = nullptr;
// Clear the prototype's loading flag before executing the script or
// resuming document walks, in case any of those control flows starts a
// new script load.
scriptProto->mSrcLoading = false;
if (NS_SUCCEEDED(aStatus)) {
// If the including XUL document is a FastLoad document, and we're
// compiling an out-of-line script (one with src=...), then we must
@ -3524,78 +3512,123 @@ XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
// nsXULContentSink.cpp) would have already deserialized a non-null
// script->mScriptObject, causing control flow at the top of LoadScript
// not to reach here.
nsCOMPtr<nsIURI> uri = scriptProto->mSrcURI;
nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
// XXX should also check nsIHttpChannel::requestSucceeded
nsString stringStr;
MOZ_ASSERT(!mOffThreadCompiling && mOffThreadCompileString.Length() == 0,
"XULDocument can't load multiple scripts at once");
rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen,
EmptyString(), this, stringStr);
EmptyString(), this, mOffThreadCompileString);
if (NS_SUCCEEDED(rv)) {
rv = scriptProto->Compile(stringStr.get(), stringStr.Length(),
uri, 1, this,
mCurrentPrototype->GetScriptGlobalObject());
rv = mCurrentScriptProto->Compile(mOffThreadCompileString.get(),
mOffThreadCompileString.Length(),
uri, 1, this,
mCurrentPrototype->GetScriptGlobalObject(),
this);
if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->GetScriptObject()) {
// We will be notified via OnOffThreadCompileComplete when the
// compile finishes. Keep the contents of the compiled script
// alive until the compilation finishes.
mOffThreadCompiling = true;
BlockOnload();
return NS_OK;
}
mOffThreadCompileString.Truncate();
}
}
aStatus = rv;
if (NS_SUCCEEDED(rv)) {
rv = ExecuteScript(scriptProto);
return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
}
// If the XUL cache is enabled, save the script object there in
// case different XUL documents source the same script.
//
// But don't save the script in the cache unless the master XUL
// document URL is a chrome: URL. It is valid for a URL such as
// about:config to translate into a master document URL, whose
// prototype document nodes -- including prototype scripts that
// hold GC roots protecting their mJSObject pointers -- are not
// cached in the XUL prototype cache. See StartDocumentLoad,
// the fillXULCache logic.
//
// A document such as about:config is free to load a script via
// a URL such as chrome://global/content/config.js, and we must
// not cache that script object without a prototype cache entry
// containing a companion nsXULPrototypeScript node that owns a
// GC root protecting the script object. Otherwise, the script
// cache entry will dangle once the uncached prototype document
// is released when its owning XULDocument is unloaded.
//
// (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
// the true crime story.)
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
NS_IMETHODIMP
XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus)
{
// Allow load events to be fired once off thread compilation finishes.
if (mOffThreadCompiling) {
mOffThreadCompiling = false;
UnblockOnload(false);
}
// After compilation finishes the script's characters are no longer needed.
mOffThreadCompileString.Truncate();
// When compiling off thread the script will not have been attached to the
// script proto yet.
if (aScript && !mCurrentScriptProto->GetScriptObject())
mCurrentScriptProto->Set(aScript);
// Clear mCurrentScriptProto now, but save it first for use below in
// the execute code, and in the while loop that resumes walks of other
// documents that raced to load this script.
nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
mCurrentScriptProto = nullptr;
// Clear the prototype's loading flag before executing the script or
// resuming document walks, in case any of those control flows starts a
// new script load.
scriptProto->mSrcLoading = false;
nsresult rv = aStatus;
if (NS_SUCCEEDED(rv)) {
rv = ExecuteScript(scriptProto);
// If the XUL cache is enabled, save the script object there in
// case different XUL documents source the same script.
//
// But don't save the script in the cache unless the master XUL
// document URL is a chrome: URL. It is valid for a URL such as
// about:config to translate into a master document URL, whose
// prototype document nodes -- including prototype scripts that
// hold GC roots protecting their mJSObject pointers -- are not
// cached in the XUL prototype cache. See StartDocumentLoad,
// the fillXULCache logic.
//
// A document such as about:config is free to load a script via
// a URL such as chrome://global/content/config.js, and we must
// not cache that script object without a prototype cache entry
// containing a companion nsXULPrototypeScript node that owns a
// GC root protecting the script object. Otherwise, the script
// cache entry will dangle once the uncached prototype document
// is released when its owning XULDocument is unloaded.
//
// (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
// the true crime story.)
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
if (useXULCache && IsChromeURI(mDocumentURI)) {
nsXULPrototypeCache::GetInstance()->PutScript(
scriptProto->mSrcURI,
scriptProto->GetScriptObject());
}
if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->GetScriptObject()) {
nsXULPrototypeCache::GetInstance()->PutScript(
scriptProto->mSrcURI,
scriptProto->GetScriptObject());
}
if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
// If we are loading an overlay script, try to serialize
// it to the FastLoad file here. Master scripts will be
// serialized when the master prototype document gets
// written, at the bottom of ResumeWalk. That way, master
// out-of-line scripts are serialized in the same order that
// they'll be read, in the FastLoad file, which reduces the
// number of seeks that dump the underlying stream's buffer.
//
// Ignore the return value, as we don't need to propagate
// a failure to write to the FastLoad file, because this
// method aborts that whole process on error.
nsIScriptGlobalObject* global =
mCurrentPrototype->GetScriptGlobalObject();
if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
// If we are loading an overlay script, try to serialize
// it to the FastLoad file here. Master scripts will be
// serialized when the master prototype document gets
// written, at the bottom of ResumeWalk. That way, master
// out-of-line scripts are serialized in the same order that
// they'll be read, in the FastLoad file, which reduces the
// number of seeks that dump the underlying stream's buffer.
//
// Ignore the return value, as we don't need to propagate
// a failure to write to the FastLoad file, because this
// method aborts that whole process on error.
nsIScriptGlobalObject* global =
mCurrentPrototype->GetScriptGlobalObject();
NS_ASSERTION(global != nullptr, "master prototype w/o global?!");
if (global) {
nsIScriptContext *scriptContext = \
global->GetScriptContext();
NS_ASSERTION(scriptContext != nullptr,
"Failed to get script context for language");
if (scriptContext)
scriptProto->SerializeOutOfLine(nullptr, global);
}
NS_ASSERTION(global != nullptr, "master prototype w/o global?!");
if (global) {
nsIScriptContext *scriptContext =
global->GetScriptContext();
NS_ASSERTION(scriptContext != nullptr,
"Failed to get script context for language");
if (scriptContext)
scriptProto->SerializeOutOfLine(nullptr, global);
}
}
// ignore any evaluation errors
}
@ -3628,7 +3661,6 @@ XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
return rv;
}
nsresult
XULDocument::ExecuteScript(nsIScriptContext * aContext,
JS::Handle<JSScript*> aScriptObject)

View File

@ -88,7 +88,8 @@ class XULDocument MOZ_FINAL : public XMLDocument,
public nsIXULDocument,
public nsIDOMXULDocument,
public nsIStreamLoaderObserver,
public nsICSSLoaderObserver
public nsICSSLoaderObserver,
public nsIOffThreadScriptReceiver
{
public:
XULDocument();
@ -173,6 +174,8 @@ public:
virtual void ResetDocumentLWTheme() MOZ_OVERRIDE { mDocLWTheme = Doc_Theme_Uninitialized; }
NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) MOZ_OVERRIDE;
static bool
MatchAttribute(nsIContent* aContent,
int32_t aNameSpaceID,
@ -439,6 +442,18 @@ protected:
*/
nsXULPrototypeScript* mCurrentScriptProto;
/**
* Whether the current transcluded script is being compiled off thread.
* The load event is blocked while this is in progress.
*/
bool mOffThreadCompiling;
/**
* If the current transcluded script is being compiled off thread, the
* source for that script.
*/
nsString mOffThreadCompileString;
/**
* Check if a XUL template builder has already been hooked up.
*/

View File

@ -204,6 +204,8 @@ nsresult
nsXULPrototypeCache::PutScript(nsIURI* aURI,
JS::Handle<JSScript*> aScriptObject)
{
MOZ_ASSERT(aScriptObject, "Need a non-NULL script");
#ifdef DEBUG
if (mScriptTable.Get(aURI)) {
nsAutoCString scriptName;

View File

@ -35,6 +35,8 @@ class nsIURI;
know what language we have is a little silly... */
#define SCRIPTVERSION_DEFAULT JSVERSION_DEFAULT
class nsIOffThreadScriptReceiver;
/**
* It is used by the application to initialize a runtime and run scripts.
* A script runtime would implement this interface.
@ -176,5 +178,24 @@ public:
NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptContext, NS_ISCRIPTCONTEXT_IID)
#define NS_IOFFTHREADSCRIPTRECEIVER_IID \
{0x3a980010, 0x878d, 0x46a9, \
{0x93, 0xad, 0xbc, 0xfd, 0xd3, 0x8e, 0xa0, 0xc2}}
class nsIOffThreadScriptReceiver : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IOFFTHREADSCRIPTRECEIVER_IID)
/**
* Notify this object that a previous CompileScript call specifying this as
* aOffThreadReceiver has completed. The script being passed in must be
* rooted before any call which could trigger GC.
*/
NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIOffThreadScriptReceiver, NS_IOFFTHREADSCRIPTRECEIVER_IID)
#endif // nsIScriptContext_h__

View File

@ -13,90 +13,11 @@ include $(DEPTH)/config/autoconf.mk
MOCHITEST_FILES = \
offlineTests.js \
test_badManifestMagic.html \
test_bypass.html \
test_missingFile.html \
test_noManifest.html \
test_simpleManifest.html \
test_identicalManifest.html \
test_changingManifest.html \
test_refetchManifest.html \
test_offlineIFrame.html \
test_bug445544.html \
test_bug460353.html \
test_bug474696.html \
test_bug544462.html \
test_bug744719.html \
744719.cacheManifest \
744719.cacheManifest^headers^ \
test_bug765203.html \
unknownSection.cacheManifest \
unknownSection.cacheManifest^headers^ \
test_bug744719-cancel.html \
744719-cancel.cacheManifest \
744719-cancel.cacheManifest^headers^ \
subresource744719.html \
test_foreign.html \
test_fallback.html \
test_overlap.html \
test_redirectManifest.html \
test_redirectUpdateItem.html \
overlap.cacheManifest \
overlap.cacheManifest^headers^ \
test_updatingManifest.html \
test_updateCheck.html \
445544_part1.html \
445544_part2.html \
445544.cacheManifest \
445544.cacheManifest^headers^ \
460353_iframe_nomanifest.html \
460353_iframe_ownmanifest.html \
460353_iframe_samemanifest.html \
test_obsolete.html \
obsolete.html \
obsoletingManifest.sjs \
badManifestMagic.cacheManifest \
badManifestMagic.cacheManifest^headers^ \
bypass.cacheManifest \
bypass.cacheManifest^headers^ \
bypass.html \
dynamicRedirect.sjs \
explicitRedirect.sjs \
fallback.html \
fallback2.html \
fallbackTop.html \
fallback.cacheManifest \
fallback.cacheManifest^headers^ \
foreign1.cacheManifest \
foreign1.cacheManifest^headers^ \
foreign2.cacheManifest \
foreign2.cacheManifest^headers^ \
foreign2.html \
notonwhitelist.html \
onwhitelist.html \
onwhitelist.html^headers^ \
updatingIframe.sjs \
updatingImplicit.html \
manifestRedirect.sjs \
missingFile.cacheManifest \
missingFile.cacheManifest^headers^ \
redirects.sjs \
simpleManifest.cacheManifest \
simpleManifest.cacheManifest^headers^ \
wildcardManifest.cacheManifest \
wildcardManifest.cacheManifest^headers^ \
updatingManifest.sjs \
changing1Sec.sjs \
changing1Hour.sjs \
changingManifest.sjs \
offlineChild.html \
test_xhtmlManifest.xhtml \
test_missingManifest.html \
missing.html \
jupiter.jpg \
test_cancelOfflineCache.html \
test_lowDeviceStorage.html \
test_lowDeviceStorageDuringUpdate.html \
$(NULL)
# test_offlineMode.html disabled due to bug 656943

View File

@ -194,7 +194,8 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
return NULL;
// Saving source is not yet supported when parsing off thread.
JS_ASSERT_IF(!cx->isJSContext(), !extraSct && options.sourcePolicy == CompileOptions::NO_SOURCE);
JS_ASSERT_IF(!cx->isJSContext(),
!extraSct && options.sourcePolicy != CompileOptions::SAVE_SOURCE);
SourceCompressionToken *sct = extraSct;
Maybe<SourceCompressionToken> mysct;

View File

@ -613,7 +613,7 @@ Fold(ExclusiveContext *cx, ParseNode **pnp,
return true;
RootedString left(cx, pn1->pn_atom);
RootedString right(cx, pn2->pn_atom);
RootedString str(cx, ConcatStrings<CanGC>(cx->asJSContext(), left, right));
RootedString str(cx, ConcatStrings<CanGC>(cx, left, right));
if (!str)
return false;
pn->pn_atom = AtomizeString<CanGC>(cx, str);

View File

@ -1232,7 +1232,8 @@ Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
pc = pc->parent;
RootedObject parent(context);
parent = pc->sc->isFunctionBox() ? NULL : pc->sc->asGlobalSharedContext()->scopeChain();
if (!pc->sc->isFunctionBox() && options().compileAndGo)
parent = pc->sc->asGlobalSharedContext()->scopeChain();
RootedFunction fun(context);
JSFunction::Flags flags = (kind == Expression)
@ -1242,17 +1243,10 @@ Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
: JSFunction::INTERPRETED;
fun = NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom,
JSFunction::FinalizeKind, MaybeSingletonObject);
if (!fun)
return NULL;
if (options().selfHostingMode)
fun->setIsSelfHostedBuiltin();
if (fun && !options().compileAndGo) {
if (!context->shouldBeJSContext())
return NULL;
if (!JSObject::clearParent(context->asJSContext(), fun))
return NULL;
if (!JSObject::clearType(context->asJSContext(), fun))
return NULL;
fun->setEnvironment(NULL);
}
return fun;
}

View File

@ -73,6 +73,9 @@ Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
}
#endif
if (needs && runtimeFromMainThread()->isAtomsZone(this))
JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent());
needsBarrier_ = needs;
}

View File

@ -387,6 +387,9 @@ template <class> struct MatchContext { };
template <> struct MatchContext<JSContext *> {
static const ExecutionMode execMode = SequentialExecution;
};
template <> struct MatchContext<ExclusiveContext *> {
static const ExecutionMode execMode = SequentialExecution;
};
template <> struct MatchContext<ForkJoinSlice *> {
static const ExecutionMode execMode = ParallelExecution;
};

View File

@ -4841,6 +4841,69 @@ JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options, const char
return script;
}
JS_PUBLIC_API(bool)
JS::CanCompileOffThread(JSContext *cx, const CompileOptions &options)
{
#if defined(JS_THREADSAFE) && defined(JS_ION)
if (!cx->runtime()->useHelperThreads() || !cx->runtime()->helperThreadCount())
return false;
// Off thread compilation can't occur during incremental collections on the
// atoms compartment, to avoid triggering barriers. Outside the atoms
// compartment, the compilation will use a new zone which doesn't require
// barriers itself.
if (cx->runtime()->atomsZoneNeedsBarrier())
return false;
// Blacklist filenames which cause mysterious assertion failures in
// graphics code on OS X. These seem to tickle some preexisting race
// condition unrelated to off thread compilation. See bug 897655.
static const char *blacklist[] = {
#ifdef XP_MACOSX
"chrome://browser/content/places/editBookmarkOverlay.js",
"chrome://browser/content/nsContextMenu.js",
"chrome://browser/content/newtab/newTab.js",
"chrome://browser/content/places/browserPlacesViews.js",
#endif
NULL
};
const char *filename = options.filename;
for (const char **ptest = blacklist; *ptest; ptest++) {
if (!strcmp(*ptest, filename))
return false;
}
return true;
#else
return false;
#endif
}
JS_PUBLIC_API(bool)
JS::CompileOffThread(JSContext *cx, Handle<JSObject*> obj, CompileOptions options,
const jschar *chars, size_t length,
OffThreadCompileCallback callback, void *callbackData)
{
#if defined(JS_THREADSAFE) && defined(JS_ION)
JS_ASSERT(CanCompileOffThread(cx, options));
return StartOffThreadParseScript(cx, options, chars, length, obj, callback, callbackData);
#else
MOZ_ASSUME_UNREACHABLE("Off thread compilation is only available with JS_ION");
#endif
}
JS_PUBLIC_API(void)
JS::FinishOffThreadScript(JSRuntime *rt, JSScript *script)
{
#if defined(JS_THREADSAFE) && defined(JS_ION)
JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
rt->workerThreadState->finishParseTaskForScript(script);
#else
MOZ_ASSUME_UNREACHABLE("Off thread compilation is only available with JS_ION");
#endif
}
JS_PUBLIC_API(JSScript *)
JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *objArg, JSPrincipals *principals,
const jschar *chars, size_t length,

View File

@ -4119,6 +4119,33 @@ Compile(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options, FILE *
extern JS_PUBLIC_API(JSScript *)
Compile(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options, const char *filename);
extern JS_PUBLIC_API(bool)
CanCompileOffThread(JSContext *cx, const CompileOptions &options);
/*
* Off thread compilation control flow.
*
* After successfully triggering an off thread compile of a script, the
* callback will eventually be invoked with the specified data and the result
* script or NULL. The callback will be invoked while off the main thread, so
* must ensure that its operations are thread safe. Afterwards,
* FinishOffThreadScript must be invoked on the main thread to make the script
* usable (correct compartment/zone); this method must be invoked even if the
* off thread compilation produced a NULL script.
*
* The characters passed in to CompileOffThread must remain live until the
* callback is invoked, and the resulting script will be rooted until the call
* to FinishOffThreadScript.
*/
extern JS_PUBLIC_API(bool)
CompileOffThread(JSContext *cx, Handle<JSObject*> obj, CompileOptions options,
const jschar *chars, size_t length,
OffThreadCompileCallback callback, void *callbackData);
extern JS_PUBLIC_API(void)
FinishOffThreadScript(JSRuntime *rt, JSScript *script);
extern JS_PUBLIC_API(JSFunction *)
CompileFunction(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options,
const char *name, unsigned nargs, const char **argnames,

View File

@ -350,16 +350,9 @@ js::AtomizeString(ExclusiveContext *cx, JSString *str,
return &atom;
}
const jschar *chars;
if (str->isLinear()) {
chars = str->asLinear().chars();
} else {
if (!cx->shouldBeJSContext())
return NULL;
chars = str->getChars(cx->asJSContext());
if (!chars)
return NULL;
}
const jschar *chars = str->getChars(cx);
if (!chars)
return NULL;
if (JSAtom *atom = AtomizeAndCopyChars<NoGC>(cx, chars, str->length(), ib))
return atom;

View File

@ -277,7 +277,6 @@ struct WorkerThread;
class ExclusiveContext : public ThreadSafeContext
{
friend class gc::ArenaLists;
friend class CompartmentChecker;
friend class AutoCompartment;
friend class AutoLockForExclusiveAccess;
friend struct StackBaseShape;

View File

@ -28,7 +28,7 @@ class CompartmentChecker
public:
explicit CompartmentChecker(ExclusiveContext *cx)
: compartment(cx->compartment_)
: compartment(cx->compartment())
{}
/*
@ -47,14 +47,14 @@ class CompartmentChecker
/* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */
static void check(JSCompartment *c1, JSCompartment *c2) {
JS_ASSERT(!c1->runtimeFromMainThread()->isAtomsCompartment(c1));
JS_ASSERT(!c2->runtimeFromMainThread()->isAtomsCompartment(c2));
JS_ASSERT(!c1->runtimeFromAnyThread()->isAtomsCompartment(c1));
JS_ASSERT(!c2->runtimeFromAnyThread()->isAtomsCompartment(c2));
if (c1 != c2)
fail(c1, c2);
}
void check(JSCompartment *c) {
if (c && !compartment->runtimeFromMainThread()->isAtomsCompartment(c)) {
if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) {
if (!compartment)
compartment = c;
else if (c != compartment)

View File

@ -617,6 +617,34 @@ JSCompartment::purge()
dtoaCache.purge();
}
void
JSCompartment::clearTables()
{
global_ = NULL;
regExps.clearTables();
// No scripts should have run in this compartment. This is used when
// merging a compartment that has been used off thread into another
// compartment and zone.
JS_ASSERT(crossCompartmentWrappers.empty());
JS_ASSERT_IF(callsiteClones.initialized(), callsiteClones.empty());
JS_ASSERT(!ionCompartment_);
JS_ASSERT(!debugScopes);
JS_ASSERT(!gcWeakMapList);
JS_ASSERT(!analysisLifoAlloc.used());
JS_ASSERT(enumerators->next() == enumerators);
if (baseShapes.initialized())
baseShapes.clear();
if (initialShapes.initialized())
initialShapes.clear();
if (newTypeObjects.initialized())
newTypeObjects.clear();
if (lazyTypeObjects.initialized())
lazyTypeObjects.clear();
}
bool
JSCompartment::hasScriptsOnStack()
{

View File

@ -310,6 +310,7 @@ struct JSCompartment
void sweep(js::FreeOp *fop, bool releaseTypes);
void sweepCrossCompartmentWrappers();
void purge();
void clearTables();
void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
@ -400,6 +401,12 @@ JSRuntime::isAtomsZone(JS::Zone *zone)
return zone == atomsCompartment_->zone();
}
inline bool
JSRuntime::atomsZoneNeedsBarrier()
{
return atomsCompartment_->zone()->needsBarrier();
}
// For use when changing the debug mode flag on one or more compartments.
// Do not run scripts in any compartment that is scheduled for GC using this
// object. See comment in updateForDebugMode.

View File

@ -1079,6 +1079,12 @@ js::AddObjectRoot(JSContext *cx, JSObject **rp, const char *name)
return AddRoot(cx, rp, name, JS_GC_ROOT_OBJECT_PTR);
}
extern bool
js::AddObjectRoot(JSRuntime *rt, JSObject **rp, const char *name)
{
return AddRoot(rt, rp, name, JS_GC_ROOT_OBJECT_PTR);
}
extern bool
js::AddScriptRoot(JSContext *cx, JSScript **rp, const char *name)
{
@ -1218,7 +1224,7 @@ ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind)
* background finalization runs and can modify head or cursor at any
* moment. So we always allocate a new arena in that case.
*/
maybeLock.lock(zone->runtimeFromMainThread());
maybeLock.lock(zone->runtimeFromAnyThread());
if (*bfs == BFS_RUN) {
JS_ASSERT(!*al->cursor);
chunk = PickChunk(zone);
@ -4768,6 +4774,50 @@ js::NewCompartment(JSContext *cx, Zone *zone, JSPrincipals *principals,
return compartment.forget();
}
void
gc::MergeCompartments(JSCompartment *source, JSCompartment *target)
{
JSRuntime *rt = source->runtimeFromMainThread();
AutoPrepareForTracing prepare(rt);
// Cleanup tables and other state in the source compartment that will be
// meaningless after merging into the target compartment.
source->clearTables();
// Fixup compartment pointers in source to refer to target.
for (CellIter iter(source->zone(), FINALIZE_SCRIPT); !iter.done(); iter.next()) {
JSScript *script = iter.get<JSScript>();
JS_ASSERT(script->compartment() == source);
script->compartment_ = target;
}
for (CellIter iter(source->zone(), FINALIZE_BASE_SHAPE); !iter.done(); iter.next()) {
BaseShape *base = iter.get<BaseShape>();
JS_ASSERT(base->compartment() == source);
base->compartment_ = target;
}
// Fixup zone pointers in source's zone to refer to target's zone.
for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
for (ArenaIter aiter(source->zone(), AllocKind(thingKind)); !aiter.done(); aiter.next()) {
ArenaHeader *aheader = aiter.get();
aheader->zone = target->zone();
}
}
// The source should be the only compartment in its zone.
for (CompartmentsInZoneIter c(source->zone()); !c.done(); c.next())
JS_ASSERT(c.get() == source);
// Merge the allocator in source's zone into target's zone.
target->zone()->allocator.arenas.adoptArenas(rt, &source->zone()->allocator.arenas);
target->zone()->gcBytes += source->zone()->gcBytes;
source->zone()->gcBytes = 0;
}
void
gc::RunDebugGC(JSContext *cx)
{
@ -5042,6 +5092,7 @@ ArenaLists::adoptArenas(JSRuntime *rt, ArenaLists *fromArenaLists)
toList->insert(fromHeader);
}
fromList->cursor = &fromList->head;
}
}

View File

@ -409,7 +409,7 @@ class ArenaLists
/* For each arena kind, a list of arenas remaining to be swept. */
ArenaHeader *arenaListsToSweep[FINALIZE_LIMIT];
/* Shape areneas to be swept in the foreground. */
/* Shape arenas to be swept in the foreground. */
ArenaHeader *gcShapeArenasToSweep;
public:
@ -662,6 +662,9 @@ AddStringRoot(JSContext *cx, JSString **rp, const char *name);
extern bool
AddObjectRoot(JSContext *cx, JSObject **rp, const char *name);
extern bool
AddObjectRoot(JSRuntime *rt, JSObject **rp, const char *name);
extern bool
AddScriptRoot(JSContext *cx, JSScript **rp, const char *name);
@ -1335,6 +1338,13 @@ SetFullCompartmentChecks(JSContext *cx, bool enabled);
void
FinishBackgroundFinalize(JSRuntime *rt);
/*
* Merge all contents of source into target. This can only be used if source is
* the only compartment in its zone.
*/
void
MergeCompartments(JSCompartment *source, JSCompartment *target);
const int ZealPokeValue = 1;
const int ZealAllocValue = 2;
const int ZealFrameGCValue = 3;

View File

@ -5105,6 +5105,21 @@ js::IsDelegate(JSContext *cx, HandleObject obj, const js::Value &v, bool *result
}
}
JSObject *
js::GetClassPrototypePure(GlobalObject *global, JSProtoKey protoKey)
{
JS_ASSERT(JSProto_Null <= protoKey);
JS_ASSERT(protoKey < JSProto_LIMIT);
if (protoKey != JSProto_Null) {
const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
if (v.isObject())
return &v.toObject();
}
return NULL;
}
/*
* The first part of this function has been hand-expanded and optimized into
* NewBuiltinClassInstance in jsobjinlines.h.
@ -5113,15 +5128,9 @@ bool
js_GetClassPrototype(ExclusiveContext *cx, JSProtoKey protoKey,
MutableHandleObject protop, Class *clasp)
{
JS_ASSERT(JSProto_Null <= protoKey);
JS_ASSERT(protoKey < JSProto_LIMIT);
if (protoKey != JSProto_Null) {
const Value &v = cx->global()->getReservedSlot(JSProto_LIMIT + protoKey);
if (v.isObject()) {
protop.set(&v.toObject());
return true;
}
if (JSObject *proto = GetClassPrototypePure(cx->global(), protoKey)) {
protop.set(proto);
return true;
}
RootedValue v(cx);

View File

@ -1440,6 +1440,9 @@ js_GetClassPrototype(js::ExclusiveContext *cx, JSProtoKey protoKey, js::MutableH
namespace js {
JSObject *
GetClassPrototypePure(GlobalObject *global, JSProtoKey protoKey);
extern bool
SetClassAndProto(JSContext *cx, HandleObject obj,
Class *clasp, Handle<TaggedProto> proto, bool checkForCycles);

View File

@ -208,6 +208,9 @@ typedef bool JSCallOnceType;
typedef bool (*JSInitCallback)(void);
namespace JS {
typedef void (*OffThreadCompileCallback)(JSScript *script, void *callbackData);
namespace shadow {
struct Runtime

View File

@ -17,6 +17,7 @@
#include "jscntxtinlines.h"
#include "jscompartmentinlines.h"
#include "jsobjinlines.h"
using namespace js;
@ -165,23 +166,32 @@ static JSClass workerGlobalClass = {
JS_ConvertStub, NULL
};
ParseTask::ParseTask(JSRuntime *rt, ExclusiveContext *cx, const CompileOptions &options,
const jschar *chars, size_t length)
: runtime(rt), cx(cx), options(options), chars(chars), length(length),
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), script(NULL)
ParseTask::ParseTask(Zone *zone, ExclusiveContext *cx, const CompileOptions &options,
const jschar *chars, size_t length, JSObject *scopeChain,
JS::OffThreadCompileCallback callback, void *callbackData)
: zone(zone), cx(cx), options(options), chars(chars), length(length),
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(scopeChain),
callback(callback), callbackData(callbackData), script(NULL)
{
JSRuntime *rt = zone->runtimeFromMainThread();
if (options.principals())
JS_HoldPrincipals(options.principals());
if (options.originPrincipals())
JS_HoldPrincipals(options.originPrincipals());
if (!AddObjectRoot(rt, &this->scopeChain, "ParseTask::scopeChain"))
MOZ_CRASH();
}
ParseTask::~ParseTask()
{
JSRuntime *rt = zone->runtimeFromMainThread();
if (options.principals())
JS_DropPrincipals(runtime, options.principals());
JS_DropPrincipals(rt, options.principals());
if (options.originPrincipals())
JS_DropPrincipals(runtime, options.originPrincipals());
JS_DropPrincipals(rt, options.originPrincipals());
JS_RemoveObjectRootRT(rt, &scopeChain);
// ParseTask takes over ownership of its input exclusive context.
js_delete(cx);
@ -189,8 +199,13 @@ ParseTask::~ParseTask()
bool
js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
const jschar *chars, size_t length)
const jschar *chars, size_t length, HandleObject scopeChain,
JS::OffThreadCompileCallback callback, void *callbackData)
{
// Suppress GC so that calls below do not trigger a new incremental GC
// which could require barriers on the atoms compartment.
gc::AutoSuppressGC suppress(cx);
frontend::MaybeCallSourceHandler(cx, options, chars, length);
JSRuntime *rt = cx->runtime();
@ -205,16 +220,27 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
if (!global)
return false;
// For now, type inference is always disabled in exclusive zones.
// This restriction would be fairly easy to lift.
// For now, type inference is always disabled in exclusive zones, as type
// inference data is not merged between zones when finishing the off thread
// parse. This restriction would be fairly easy to lift.
JS_ASSERT(!cx->typeInferenceEnabled());
global->zone()->types.inferenceEnabled = false;
JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals);
RootedObject obj(cx);
// Initialize all classes needed for parsing while we are still on the main
// thread.
// thread. Do this for both the target and the new global so that prototype
// pointers can be changed infallibly after parsing finishes.
if (!js_GetClassObject(cx, cx->global(), JSProto_Function, &obj) ||
!js_GetClassObject(cx, cx->global(), JSProto_Array, &obj) ||
!js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj))
{
return false;
}
{
AutoCompartment ac(cx, global);
RootedObject obj(cx);
if (!js_GetClassObject(cx, global, JSProto_Function, &obj) ||
!js_GetClassObject(cx, global, JSProto_Array, &obj) ||
!js_GetClassObject(cx, global, JSProto_RegExp, &obj))
@ -223,7 +249,7 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
}
}
global->zone()->usedByExclusiveThread = true;
cx->runtime()->setUsedByExclusiveThread(global->zone());
ScopedJSDeletePtr<ExclusiveContext> workercx(
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData *) NULL,
@ -234,7 +260,8 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
workercx->enterCompartment(global->compartment());
ScopedJSDeletePtr<ParseTask> task(
cx->new_<ParseTask>(cx->runtime(), workercx.get(), options, chars, length));
cx->new_<ParseTask>(global->zone(), workercx.get(), options, chars, length,
scopeChain, callback, callbackData));
if (!task)
return false;
@ -430,6 +457,82 @@ WorkerThreadState::canStartIonCompile()
return true;
}
bool
WorkerThreadState::canStartParseTask()
{
// Don't allow simultaneous off thread parses, to reduce contention on the
// atoms table. Note that asm.js compilation depends on this to avoid
// stalling the worker thread, as off thread parse tasks can trigger and
// block on other off thread asm.js compilation tasks.
JS_ASSERT(isLocked());
if (parseWorklist.empty())
return false;
for (size_t i = 0; i < numThreads; i++) {
if (threads[i].parseTask)
return false;
}
return true;
}
void
WorkerThreadState::finishParseTaskForScript(JSScript *script)
{
JSRuntime *rt = script->compartment()->runtimeFromMainThread();
ParseTask *parseTask = NULL;
{
AutoLockWorkerThreadState lock(rt);
for (size_t i = 0; i < parseFinishedList.length(); i++) {
if (parseFinishedList[i]->script == script) {
parseTask = parseFinishedList[i];
parseFinishedList[i] = parseFinishedList.back();
parseFinishedList.popBack();
break;
}
}
}
JS_ASSERT(parseTask);
// Mark the zone as no longer in use by an ExclusiveContext, and available
// to be collected by the GC.
rt->clearUsedByExclusiveThread(parseTask->zone);
if (!script) {
// Parsing failed and there is nothing to finish, but there still may
// be lingering ParseTask instances holding roots which need to be
// cleaned up. The ParseTask which we picked might not be the right
// one but this is ok as finish calls will be 1:1 with calls that
// create a ParseTask.
js_delete(parseTask);
return;
}
// Point the prototypes of any objects in the script's compartment to refer
// to the corresponding prototype in the new compartment. This will briefly
// create cross compartment pointers, which will be fixed by the
// MergeCompartments call below.
for (gc::CellIter iter(parseTask->zone, gc::FINALIZE_TYPE_OBJECT); !iter.done(); iter.next()) {
types::TypeObject *object = iter.get<types::TypeObject>();
JSObject *proto = object->proto;
if (!proto)
continue;
JSProtoKey key = js_IdentifyClassPrototype(proto);
if (key == JSProto_Null)
continue;
JSObject *newProto = GetClassPrototypePure(&parseTask->scopeChain->global(), key);
JS_ASSERT(newProto);
object->proto = newProto;
}
// Move the parsed script and all its contents into the desired compartment.
gc::MergeCompartments(parseTask->script->compartment(), parseTask->scopeChain->compartment());
js_delete(parseTask);
}
void
WorkerThread::destroy()
{
@ -548,7 +651,7 @@ void
WorkerThread::handleParseWorkload(WorkerThreadState &state)
{
JS_ASSERT(state.isLocked());
JS_ASSERT(!state.parseWorklist.empty());
JS_ASSERT(state.canStartParseTask());
JS_ASSERT(idle());
parseTask = state.parseWorklist.popCopy();
@ -562,7 +665,13 @@ WorkerThread::handleParseWorkload(WorkerThreadState &state)
parseTask->chars, parseTask->length);
}
// The callback is invoked while we are still off the main thread.
parseTask->callback(parseTask->script, parseTask->callbackData);
// FinishOffThreadScript will need to be called on the script to
// migrate it into the correct compartment.
state.parseFinishedList.append(parseTask);
parseTask = NULL;
// Notify the main thread in case it is waiting for the parse/emit to finish.
@ -583,7 +692,7 @@ WorkerThread::threadLoop()
// Block until a task is available.
while (!state.canStartIonCompile() &&
!state.canStartAsmJSCompile() &&
state.parseWorklist.empty())
!state.canStartParseTask())
{
if (state.shouldPause)
pause();
@ -597,7 +706,7 @@ WorkerThread::threadLoop()
handleAsmJSWorkload(state);
else if (state.canStartIonCompile())
handleIonWorkload(state);
else if (!state.parseWorklist.empty())
else if (state.canStartParseTask())
handleParseWorkload(state);
}
}
@ -695,7 +804,8 @@ js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
bool
js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
const jschar *chars, size_t length)
const jschar *chars, size_t length, HandleObject scopeChain,
JS::OffThreadCompileCallback callback, void *callbackData)
{
MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
}

View File

@ -89,6 +89,7 @@ class WorkerThreadState
bool canStartAsmJSCompile();
bool canStartIonCompile();
bool canStartParseTask();
uint32_t harvestFailedAsmJSJobs() {
JS_ASSERT(isLocked());
@ -114,6 +115,8 @@ class WorkerThreadState
return asmJSFailedFunction;
}
void finishParseTaskForScript(JSScript *script);
private:
/*
@ -224,7 +227,8 @@ CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
*/
bool
StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
const jschar *chars, size_t length);
const jschar *chars, size_t length, HandleObject scopeChain,
JS::OffThreadCompileCallback callback, void *callbackData);
/* Block until in progress and pending off thread parse jobs have finished. */
void
@ -332,17 +336,31 @@ struct AsmJSParallelTask
struct ParseTask
{
JSRuntime *runtime;
Zone *zone;
ExclusiveContext *cx;
CompileOptions options;
const jschar *chars;
size_t length;
LifoAlloc alloc;
// Rooted pointer to the scope in the target compartment which the
// resulting script will be merged into. This is not safe to use off the
// main thread.
JSObject *scopeChain;
// Callback invoked off the main thread when the parse finishes.
JS::OffThreadCompileCallback callback;
void *callbackData;
// Holds the final script between the invocation of the callback and the
// point where FinishOffThreadScript is called, which will destroy the
// ParseTask.
JSScript *script;
ParseTask(JSRuntime *rt, ExclusiveContext *cx, const CompileOptions &options,
const jschar *chars, size_t length);
ParseTask(Zone *zone, ExclusiveContext *cx, const CompileOptions &options,
const jschar *chars, size_t length, JSObject *scopeChain,
JS::OffThreadCompileCallback callback, void *callbackData);
~ParseTask();
};

View File

@ -3235,6 +3235,13 @@ SyntaxParse(JSContext *cx, unsigned argc, jsval *vp)
#ifdef JS_THREADSAFE
static void
OffThreadCompileScriptCallback(JSScript *script, void *callbackData)
{
// This callback is invoked off the main thread and there isn't a good way
// to pass the script on to the main thread. Just let the script leak.
}
static bool
OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp)
{
@ -3271,8 +3278,11 @@ OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp)
if (!JS_AddStringRoot(cx, permanentRoot))
return false;
if (!StartOffThreadParseScript(cx, options, chars, length))
if (!StartOffThreadParseScript(cx, options, chars, length, cx->global(),
OffThreadCompileScriptCallback, NULL))
{
return false;
}
args.rval().setUndefined();
return true;

View File

@ -681,6 +681,13 @@ RegExpCompartment::sweep(JSRuntime *rt)
}
}
void
RegExpCompartment::clearTables()
{
JS_ASSERT(inUse_.empty());
map_.clear();
}
bool
RegExpCompartment::get(ExclusiveContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g)
{

View File

@ -319,6 +319,7 @@ class RegExpCompartment
bool init(JSContext *cx);
void sweep(JSRuntime *rt);
void clearTables();
bool get(ExclusiveContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g);

View File

@ -713,6 +713,22 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
#ifdef JS_THREADSAFE
void
JSRuntime::setUsedByExclusiveThread(Zone *zone)
{
JS_ASSERT(!zone->usedByExclusiveThread);
zone->usedByExclusiveThread = true;
numExclusiveThreads++;
}
void
JSRuntime::clearUsedByExclusiveThread(Zone *zone)
{
JS_ASSERT(zone->usedByExclusiveThread);
zone->usedByExclusiveThread = false;
numExclusiveThreads--;
}
bool
js::CurrentThreadCanAccessRuntime(JSRuntime *rt)
{

View File

@ -774,6 +774,9 @@ struct JSRuntime : public JS::shadow::Runtime,
friend class js::AutoPauseWorkersForGC;
public:
void setUsedByExclusiveThread(JS::Zone *zone);
void clearUsedByExclusiveThread(JS::Zone *zone);
#endif // JS_THREADSAFE
bool currentThreadHasExclusiveAccess() {
@ -1298,7 +1301,7 @@ struct JSRuntime : public JS::shadow::Runtime,
js::GCHelperThread gcHelperThread;
#ifdef XP_MACOSX
#if defined(XP_MACOSX) && defined(JS_ION)
js::AsmJSMachExceptionHandler asmJSMachExceptionHandler;
#endif
@ -1403,6 +1406,8 @@ struct JSRuntime : public JS::shadow::Runtime,
// The atoms compartment is the only one in its zone.
inline bool isAtomsZone(JS::Zone *zone);
inline bool atomsZoneNeedsBarrier();
union {
/*
* Cached pointers to various interned property names, initialized in

View File

@ -230,12 +230,17 @@ class Shape;
class UnownedBaseShape;
struct StackBaseShape;
namespace gc {
void MergeCompartments(JSCompartment *source, JSCompartment *target);
}
class BaseShape : public js::gc::Cell
{
public:
friend class Shape;
friend struct StackBaseShape;
friend struct StackShape;
friend void gc::MergeCompartments(JSCompartment *source, JSCompartment *target);
enum Flag {
/* Owned by the referring shape. */

View File

@ -251,7 +251,7 @@ JSRope::getCharsNonDestructiveInternal(ThreadSafeContext *cx, ScopedJSFreePtr<js
template<JSRope::UsingBarrier b>
JSFlatString *
JSRope::flattenInternal(JSContext *maybecx)
JSRope::flattenInternal(ExclusiveContext *maybecx)
{
/*
* Perform a depth-first dag traversal, splatting each node's characters
@ -393,7 +393,7 @@ JSRope::flattenInternal(JSContext *maybecx)
}
JSFlatString *
JSRope::flatten(JSContext *maybecx)
JSRope::flatten(ExclusiveContext *maybecx)
{
#if JSGC_INCREMENTAL
if (zone()->needsBarrier())
@ -471,7 +471,7 @@ JSDependentString::getCharsZNonDestructive(ThreadSafeContext *cx, ScopedJSFreePt
}
JSFlatString *
JSDependentString::undepend(JSContext *cx)
JSDependentString::undepend(ExclusiveContext *cx)
{
JS_ASSERT(JSString::isDependent());
@ -502,7 +502,7 @@ JSDependentString::undepend(JSContext *cx)
}
JSStableString *
JSInlineString::uninline(JSContext *maybecx)
JSInlineString::uninline(ExclusiveContext *maybecx)
{
JS_ASSERT(isInline());
size_t n = length();
@ -571,8 +571,8 @@ ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx)
if (chars_)
return true;
if (cx->isJSContext()) {
JSLinearString *linear = str_->ensureLinear(cx->asJSContext());
if (cx->isExclusiveContext()) {
JSLinearString *linear = str_->ensureLinear(cx->asExclusiveContext());
if (!linear)
return false;
chars_ = linear->chars();

View File

@ -266,9 +266,9 @@ class JSString : public js::gc::Cell
* getCharsZ additionally ensures the array is null terminated.
*/
inline const jschar *getChars(JSContext *cx);
inline const jschar *getCharsZ(JSContext *cx);
inline bool getChar(JSContext *cx, size_t index, jschar *code);
inline const jschar *getChars(js::ExclusiveContext *cx);
inline const jschar *getCharsZ(js::ExclusiveContext *cx);
inline bool getChar(js::ExclusiveContext *cx, size_t index, jschar *code);
/*
* Returns chars() if the string is already linear or flat. Otherwise
@ -289,11 +289,11 @@ class JSString : public js::gc::Cell
/* Fallible conversions to more-derived string types. */
inline JSLinearString *ensureLinear(JSContext *cx);
inline JSFlatString *ensureFlat(JSContext *cx);
inline JSStableString *ensureStable(JSContext *cx);
inline JSLinearString *ensureLinear(js::ExclusiveContext *cx);
inline JSFlatString *ensureFlat(js::ExclusiveContext *cx);
inline JSStableString *ensureStable(js::ExclusiveContext *cx);
static bool ensureLinear(JSContext *cx, JSString *str) {
static bool ensureLinear(js::ExclusiveContext *cx, JSString *str) {
return str->ensureLinear(cx) != NULL;
}
@ -465,10 +465,10 @@ class JSRope : public JSString
enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
template<UsingBarrier b>
JSFlatString *flattenInternal(JSContext *cx);
JSFlatString *flattenInternal(js::ExclusiveContext *cx);
friend class JSString;
JSFlatString *flatten(JSContext *cx);
JSFlatString *flatten(js::ExclusiveContext *cx);
void init(js::ThreadSafeContext *cx, JSString *left, JSString *right, size_t length);
@ -531,7 +531,7 @@ class JSDependentString : public JSLinearString
js::ScopedJSFreePtr<jschar> &out) const;
friend class JSString;
JSFlatString *undepend(JSContext *cx);
JSFlatString *undepend(js::ExclusiveContext *cx);
void init(js::ThreadSafeContext *cx, JSLinearString *base, const jschar *chars,
size_t length);
@ -703,7 +703,7 @@ class JSInlineString : public JSFlatString
inline jschar *init(size_t length);
JSStableString *uninline(JSContext *cx);
JSStableString *uninline(js::ExclusiveContext *cx);
inline void resetLength(size_t length);
@ -1009,7 +1009,7 @@ class AutoNameVector : public AutoVectorRooter<PropertyName *>
/* Avoid requiring vm/String-inl.h just to call getChars. */
JS_ALWAYS_INLINE const jschar *
JSString::getChars(JSContext *cx)
JSString::getChars(js::ExclusiveContext *cx)
{
if (JSLinearString *str = ensureLinear(cx))
return str->chars();
@ -1017,7 +1017,7 @@ JSString::getChars(JSContext *cx)
}
JS_ALWAYS_INLINE bool
JSString::getChar(JSContext *cx, size_t index, jschar *code)
JSString::getChar(js::ExclusiveContext *cx, size_t index, jschar *code)
{
JS_ASSERT(index < length());
@ -1051,7 +1051,7 @@ JSString::getChar(JSContext *cx, size_t index, jschar *code)
}
JS_ALWAYS_INLINE const jschar *
JSString::getCharsZ(JSContext *cx)
JSString::getCharsZ(js::ExclusiveContext *cx)
{
if (JSFlatString *str = ensureFlat(cx))
return str->chars();
@ -1095,7 +1095,7 @@ JSString::getCharsZNonDestructive(js::ThreadSafeContext *cx,
}
JS_ALWAYS_INLINE JSLinearString *
JSString::ensureLinear(JSContext *cx)
JSString::ensureLinear(js::ExclusiveContext *cx)
{
return isLinear()
? &asLinear()
@ -1103,7 +1103,7 @@ JSString::ensureLinear(JSContext *cx)
}
JS_ALWAYS_INLINE JSFlatString *
JSString::ensureFlat(JSContext *cx)
JSString::ensureFlat(js::ExclusiveContext *cx)
{
return isFlat()
? &asFlat()
@ -1113,7 +1113,7 @@ JSString::ensureFlat(JSContext *cx)
}
JS_ALWAYS_INLINE JSStableString *
JSString::ensureStable(JSContext *maybecx)
JSString::ensureStable(js::ExclusiveContext *maybecx)
{
if (isRope()) {
JSFlatString *flat = asRope().flatten(maybecx);