mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-11 01:57:00 +00:00
Bug 897655 - Use off thread parsing when loading scripts from XUL documents, r=billm,bz,luke.
This commit is contained in:
parent
55c9d6f3f5
commit
cbc8048e52
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,9 @@ Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (needs && runtimeFromMainThread()->isAtomsZone(this))
|
||||
JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent());
|
||||
|
||||
needsBarrier_ = needs;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -208,6 +208,9 @@ typedef bool JSCallOnceType;
|
||||
typedef bool (*JSInitCallback)(void);
|
||||
|
||||
namespace JS {
|
||||
|
||||
typedef void (*OffThreadCompileCallback)(JSScript *script, void *callbackData);
|
||||
|
||||
namespace shadow {
|
||||
|
||||
struct Runtime
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user