diff --git a/browser/themes/pinstripe/browser/browser.css b/browser/themes/pinstripe/browser/browser.css index 6ff7ddf1d48a..554499062b11 100644 --- a/browser/themes/pinstripe/browser/browser.css +++ b/browser/themes/pinstripe/browser/browser.css @@ -811,9 +811,22 @@ toolbar[mode="icons"] #zoom-in-button { background-clip: padding-box; } -#urlbar:-moz-window-inactive, -.searchbar-textbox:-moz-window-inactive { - border-color: @toolbarbuttonInactiveBorderColor@; +@media (-moz-mac-lion-theme) { + #urlbar, + .searchbar-textbox { + background-image: -moz-linear-gradient(hsl(0,0%,97%), hsl(0,0%,100%)); + border-color: hsla(0,0%,0%,.35) hsla(0,0%,0%,.25) hsla(0,0%,0%,.15); + box-shadow: 0 1px 0 hsla(0,0%,100%,.2), + inset 0 0 1px hsla(0,0%,0%,.05), + inset 0 1px 2px hsla(0,0%,0%,.1); + } +} + +@media not all and (-moz-mac-lion-theme) { + #urlbar:-moz-window-inactive, + .searchbar-textbox:-moz-window-inactive { + border-color: @toolbarbuttonInactiveBorderColor@; + } } #urlbar[focused="true"], diff --git a/content/base/test/test_bug482935.html b/content/base/test/test_bug482935.html index 525add2d723e..1bfa5f4b43a7 100644 --- a/content/base/test/test_bug482935.html +++ b/content/base/test/test_bug482935.html @@ -38,51 +38,6 @@ function testCancelInPhase4() { xhr2.addEventListener("load", function() { is(xhr2.responseText, "0", "Received fresh value for second request"); - testCancelBeforePhase4(); - }, false); - - xhr2.open("GET", url); - xhr2.setRequestHeader("X-Request", "1", false); - - try { xhr2.send(); } - catch(e) { - is(xhr2.status, "200", "Exception!"); - } - }, 0); - }, false); - - xhr.abort(); - } - }, false); - - xhr.open("GET", url, true); - xhr.setRequestHeader("X-Request", "0", false); - try { xhr.send(); } - catch(e) { - is("Nothing", "Exception", "Boom: " + e); - } -} - -// Tests that response is NOT cached if the request is cancelled -// before it has reached state 4 -function testCancelBeforePhase4() { - - clearCache(); - - // First request - should be loaded from server - var xhr = new XMLHttpRequest(); - xhr.addEventListener("readystatechange", function(e) { - if (xhr.readyState == 3) { - xhr.addEventListener("abort", function() { - setTimeout(function() { - // This request was cancelled, so the responseText should be empty string - is(xhr.responseText, "", "Expected empty response to cancelled request"); - - // Second request - should be found in cache - var xhr2 = new XMLHttpRequest(); - - xhr2.addEventListener("load", function() { - is(xhr2.responseText, "1", "Received cached value for second request"); SimpleTest.finish(); }, false); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 94f0342d2612..909145ef494a 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -48,6 +48,7 @@ #include "AccessCheck.h" #include "xpcprivate.h" +#include "XPCWrapper.h" #include "nscore.h" #include "nsDOMClassInfo.h" @@ -516,7 +517,6 @@ static const char kDOMStringBundleURL[] = #define WINDOW_SCRIPTABLE_FLAGS \ (nsIXPCScriptable::WANT_GETPROPERTY | \ - nsIXPCScriptable::WANT_SETPROPERTY | \ nsIXPCScriptable::WANT_PRECREATE | \ nsIXPCScriptable::WANT_FINALIZE | \ nsIXPCScriptable::WANT_EQUALITY | \ @@ -5269,39 +5269,6 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, return NS_OK; } -NS_IMETHODIMP -nsWindowSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, PRBool *_retval) -{ - if (id == sLocation_id) { - JSAutoRequest ar(cx); - - JSString *val = ::JS_ValueToString(cx, *vp); - NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); - - nsCOMPtr window = do_QueryWrappedNative(wrapper); - NS_ENSURE_TRUE(window, NS_ERROR_UNEXPECTED); - - nsCOMPtr location; - nsresult rv = window->GetLocation(getter_AddRefs(location)); - NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && location, rv); - - nsCOMPtr holder; - rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, - vp, getter_AddRefs(holder)); - NS_ENSURE_SUCCESS(rv, rv); - - nsDependentJSString depStr; - NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); - - rv = location->SetHref(depStr); - - return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; - } - - return NS_OK; -} - NS_IMETHODIMP nsWindowSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRBool *_retval) @@ -6392,6 +6359,68 @@ static JSNewResolveOp sOtherResolveFuncs[] = { mozilla::dom::workers::ResolveWorkerClasses }; +template +static nsresult +LocationSetterGuts(JSContext *cx, JSObject *obj, jsval *vp) +{ + // This function duplicates some of the logic in XPC_WN_HelperSetProperty + XPCWrappedNative *wrapper = + XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); + + // The error checks duplicate code in THROW_AND_RETURN_IF_BAD_WRAPPER + NS_ENSURE_TRUE(wrapper, NS_ERROR_XPC_BAD_OP_ON_WN_PROTO); + NS_ENSURE_TRUE(wrapper->IsValid(), NS_ERROR_XPC_HAS_BEEN_SHUTDOWN); + + nsCOMPtr xpcomObj = do_QueryWrappedNative(wrapper); + NS_ENSURE_TRUE(xpcomObj, NS_ERROR_UNEXPECTED); + + nsCOMPtr location; + nsresult rv = xpcomObj->GetLocation(getter_AddRefs(location)); + NS_ENSURE_SUCCESS(rv, rv); + + JSString *val = ::JS_ValueToString(cx, *vp); + NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); + + nsDependentJSString depStr; + NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); + + rv = location->SetHref(depStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr holder; + return WrapNative(cx, JS_GetGlobalForScopeChain(cx), location, + &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp, + getter_AddRefs(holder)); +} + +template +static JSBool +LocationSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, + jsval *vp) +{ + nsresult rv = LocationSetterGuts(cx, obj, vp); + if (NS_FAILED(rv)) { + if (!::JS_IsExceptionPending(cx)) { + nsDOMClassInfo::ThrowJSException(cx, rv); + } + return JS_FALSE; + } + + return JS_TRUE; +} + +static JSBool +LocationSetterUnwrapper(JSContext *cx, JSObject *obj, jsid id, JSBool strict, + jsval *vp) +{ + JSObject *wrapped = XPCWrapper::UnsafeUnwrapSecurityWrapper(obj); + if (wrapped) { + obj = wrapped; + } + + return LocationSetter(cx, obj, id, strict, vp); +} + NS_IMETHODIMP nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, PRUint32 flags, @@ -6544,7 +6573,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); JSBool ok = JS_WrapValue(cx, &v) && - JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, + JS_DefinePropertyById(cx, obj, id, v, nsnull, + LocationSetterUnwrapper, JSPROP_PERMANENT | JSPROP_ENUMERATE); if (!ok) { @@ -8177,7 +8207,8 @@ nsDocumentSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSAutoRequest ar(cx); - JSBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, + JSBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, + LocationSetter, JSPROP_PERMANENT | JSPROP_ENUMERATE); if (!ok) { @@ -8222,34 +8253,6 @@ NS_IMETHODIMP nsDocumentSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, PRBool *_retval) { - if (id == sLocation_id) { - nsCOMPtr doc = do_QueryWrappedNative(wrapper); - NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); - - nsCOMPtr location; - nsresult rv = doc->GetLocation(getter_AddRefs(location)); - NS_ENSURE_SUCCESS(rv, rv); - - if (location) { - JSAutoRequest ar(cx); - - JSString *val = ::JS_ValueToString(cx, *vp); - NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); - - nsDependentJSString depStr; - NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); - - rv = location->SetHref(depStr); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr holder; - rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location, - &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp, - getter_AddRefs(holder)); - return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; - } - } - if (id == sDocumentURIObject_id && IsPrivilegedScript()) { // We don't want privileged script that can read this property to set it, // but _do_ want to allow everyone else to set a value they can then read. diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 7f0e94bba1a8..5efb38084cb4 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -407,8 +407,6 @@ public: #endif NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, PRBool *_retval); - NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, PRBool *_retval); NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRBool *_retval); NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, diff --git a/dom/plugins/base/nptypes.h b/dom/plugins/base/nptypes.h index abcc96be0d55..7cf89c2ddbb3 100644 --- a/dom/plugins/base/nptypes.h +++ b/dom/plugins/base/nptypes.h @@ -55,7 +55,7 @@ typedef unsigned int uint32_t; typedef long long int64_t; typedef unsigned long long uint64_t; -#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX) +#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(HPUX) /* * AIX and SunOS ship a inttypes.h header that defines [u]int32_t, * but not bool for C. diff --git a/dom/tests/mochitest/dom-level0/Makefile.in b/dom/tests/mochitest/dom-level0/Makefile.in index 55ace1c5d62f..a08896bb4a8b 100644 --- a/dom/tests/mochitest/dom-level0/Makefile.in +++ b/dom/tests/mochitest/dom-level0/Makefile.in @@ -55,6 +55,7 @@ _TEST_FILES = \ test_location.html \ test_innerWidthHeight_script.html \ innerWidthHeight_script.html \ + test_location_setters.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/dom/tests/mochitest/dom-level0/test_location_setters.html b/dom/tests/mochitest/dom-level0/test_location_setters.html new file mode 100644 index 000000000000..eb159c7ff2d9 --- /dev/null +++ b/dom/tests/mochitest/dom-level0/test_location_setters.html @@ -0,0 +1,78 @@ + + + + + Test for Bug 639720 + + + + +Mozilla Bug 639720 +

+ +

+ +
+
+
+ + diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp index 689a7e586179..fa2b1189f58a 100644 --- a/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/editor/libeditor/html/nsHTMLEditRules.cpp @@ -4047,11 +4047,26 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool * NS_ENSURE_SUCCESS(res, res); continue; } + // is it a block with a 'margin' property? + if (useCSS && IsBlockNode(curNode)) + { + nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode); + nsAutoString value; + mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, marginProperty, value); + float f; + nsCOMPtr unit; + mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit)); + if (f > 0) + { + RelativeChangeIndentationOfElementNode(curNode, -1); + continue; + } + } // is it a list item? if (nsHTMLEditUtils::IsListItem(curNode)) { // if it is a list item, that means we are not outdenting whole list. - // So we need to finish up dealng with any curBlockQuote, and then + // So we need to finish up dealing with any curBlockQuote, and then // pop this list item. if (curBlockQuote) { @@ -4123,7 +4138,7 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool * float f; nsCOMPtr unit; mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit)); - if (f > 0) + if (f > 0 && !(nsHTMLEditUtils::IsList(curParent) && nsHTMLEditUtils::IsList(curNode))) { curBlockQuote = n; firstBQChild = curNode; diff --git a/editor/libeditor/html/tests/test_bug290026.html b/editor/libeditor/html/tests/test_bug290026.html index a8a9be3e9bd1..b3ed867ec544 100644 --- a/editor/libeditor/html/tests/test_bug290026.html +++ b/editor/libeditor/html/tests/test_bug290026.html @@ -40,7 +40,7 @@ addLoadEvent(function() { var twoindent = '

  • Item 1
  • Item 2

'; is(editor.innerHTML, twoindent, "a twice indented bulleted list"); document.execCommand("outdent", false, false); - todo_is(editor.innerHTML, oneindent, "outdenting a twice indented bulleted list"); + is(editor.innerHTML, oneindent, "outdenting a twice indented bulleted list"); // done SimpleTest.finish(); diff --git a/editor/libeditor/html/tests/test_bug291780.html b/editor/libeditor/html/tests/test_bug291780.html index b551b380196a..93f63af61ecf 100644 --- a/editor/libeditor/html/tests/test_bug291780.html +++ b/editor/libeditor/html/tests/test_bug291780.html @@ -38,7 +38,7 @@ addLoadEvent(function() { var expected = '
  • Item 1
    • Item 2
    • Item 3
  • Item 4
'; is(editor.innerHTML, expected, "indenting part of an already indented bulleted list"); document.execCommand("outdent", false, false); - todo_is(editor.innerHTML, original, "outdenting the partially indented part of an already indented bulleted list"); + is(editor.innerHTML, original, "outdenting the partially indented part of an already indented bulleted list"); // done SimpleTest.finish(); diff --git a/extensions/spellcheck/locales/en-US/hunspell/en-US.aff b/extensions/spellcheck/locales/en-US/hunspell/en-US.aff index 2ddd98543718..33deb299d496 100644 --- a/extensions/spellcheck/locales/en-US/hunspell/en-US.aff +++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.aff @@ -110,13 +110,14 @@ SFX B e able [^aeiou]e SFX L Y 1 SFX L 0 ment . -REP 88 +REP 89 REP a ei REP ei a REP a ey REP ey a REP ai ie REP ie ai +REP alot a_lot REP are air REP are ear REP are eir diff --git a/gfx/thebes/gfxFT2Fonts.cpp b/gfx/thebes/gfxFT2Fonts.cpp index 773bbff2a680..f5e19f4322b5 100644 --- a/gfx/thebes/gfxFT2Fonts.cpp +++ b/gfx/thebes/gfxFT2Fonts.cpp @@ -697,10 +697,11 @@ gfxFT2Font::InitTextRun(gfxContext *aContext, } if (!ok) { - aTextRun->AdjustAdvancesForSyntheticBold(aRunStart, aRunLength); AddRange(aTextRun, aString, aRunStart, aRunLength); } + aTextRun->AdjustAdvancesForSyntheticBold(aContext, aRunStart, aRunLength); + return PR_TRUE; } @@ -809,9 +810,7 @@ gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont, : gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle) { NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack."); - if (aNeedsBold) { - mSyntheticBoldOffset = 1.0; - } + mApplySyntheticBold = aNeedsBold; mCharGlyphCache.Init(64); } diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index d677da77405f..33cc8ff15897 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -1035,10 +1035,10 @@ gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, PRBool aOtherIsOnLeft gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle, AntialiasOption anAAOption) : mFontEntry(aFontEntry), mIsValid(PR_TRUE), + mApplySyntheticBold(PR_FALSE), mStyle(*aFontStyle), mAdjustedSize(0.0), mFUnitsConvFactor(0.0f), - mSyntheticBoldOffset(0), mAntialiasOption(anAAOption), mPlatformShaper(nsnull), mHarfBuzzShaper(nsnull) @@ -1115,6 +1115,33 @@ struct GlyphBuffer { #undef GLYPH_BUFFER_SIZE }; +// Bug 674909. When synthetic bolding text by drawing twice, need to +// render using a pixel offset in device pixels, otherwise text +// doesn't appear bolded, it appears as if a bad text shadow exists +// when a non-identity transform exists. Use an offset factor so that +// the second draw occurs at a constant offset in device pixels. + +static double +CalcXScale(gfxContext *aContext) +{ + // determine magnitude of a 1px x offset in device space + gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0)); + if (t.width == 1.0 && t.height == 0.0) { + // short-circuit the most common case to avoid sqrt() and division + return 1.0; + } + + double m = sqrt(t.width * t.width + t.height * t.height); + + NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding"); + if (m == 0.0) { + return 0.0; // effectively disables offset + } + + // scale factor so that offsets are 1px in device pixels + return 1.0 / m; +} + void gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd, gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aPt, @@ -1128,9 +1155,18 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd, const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit); PRBool isRTL = aTextRun->IsRightToLeft(); double direction = aTextRun->GetDirection(); - // double-strike in direction of run - double synBoldDevUnitOffsetAppUnits = - direction * (double) mSyntheticBoldOffset * appUnitsPerDevUnit; + + // synthetic-bold strikes are each offset one device pixel in run direction + // (these values are only needed if IsSyntheticBold() is true) + double synBoldOnePixelOffset; + PRInt32 strikes; + if (IsSyntheticBold()) { + double xscale = CalcXScale(aContext); + synBoldOnePixelOffset = direction * xscale; + // use as many strikes as needed for the the increased advance + strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale); + } + PRUint32 i; // Current position in appunits double x = aPt->x; @@ -1169,15 +1205,21 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd, glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit); - // synthetic bolding by drawing with a one-pixel offset - if (mSyntheticBoldOffset) { - cairo_glyph_t *doubleglyph; - doubleglyph = glyphs.AppendGlyph(); - doubleglyph->index = glyph->index; - doubleglyph->x = - ToDeviceUnits(glyphX + synBoldDevUnitOffsetAppUnits, - devUnitsPerAppUnit); - doubleglyph->y = glyph->y; + // synthetic bolding by multi-striking with 1-pixel offsets + // at least once, more if there's room (large font sizes) + if (IsSyntheticBold()) { + double strikeOffset = synBoldOnePixelOffset; + PRInt32 strikeCount = strikes; + do { + cairo_glyph_t *doubleglyph; + doubleglyph = glyphs.AppendGlyph(); + doubleglyph->index = glyph->index; + doubleglyph->x = + ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit, + devUnitsPerAppUnit); + doubleglyph->y = glyph->y; + strikeOffset += synBoldOnePixelOffset; + } while (--strikeCount > 0); } glyphs.Flush(cr, aDrawToPath, isRTL); @@ -1216,15 +1258,20 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd, glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); - // synthetic bolding by drawing with a one-pixel offset - if (mSyntheticBoldOffset) { - cairo_glyph_t *doubleglyph; - doubleglyph = glyphs.AppendGlyph(); - doubleglyph->index = glyph->index; - doubleglyph->x = - ToDeviceUnits(glyphX + synBoldDevUnitOffsetAppUnits, - devUnitsPerAppUnit); - doubleglyph->y = glyph->y; + if (IsSyntheticBold()) { + double strikeOffset = synBoldOnePixelOffset; + PRInt32 strikeCount = strikes; + do { + cairo_glyph_t *doubleglyph; + doubleglyph = glyphs.AppendGlyph(); + doubleglyph->index = glyph->index; + doubleglyph->x = + ToDeviceUnits(glyphX + strikeOffset * + appUnitsPerDevUnit, + devUnitsPerAppUnit); + doubleglyph->y = glyph->y; + strikeOffset += synBoldOnePixelOffset; + } while (--strikeCount > 0); } glyphs.Flush(cr, aDrawToPath, isRTL); @@ -3492,7 +3539,9 @@ struct BufferAlphaColor { }; void -gfxTextRun::AdjustAdvancesForSyntheticBold(PRUint32 aStart, PRUint32 aLength) +gfxTextRun::AdjustAdvancesForSyntheticBold(gfxContext *aContext, + PRUint32 aStart, + PRUint32 aLength) { const PRUint32 appUnitsPerDevUnit = GetAppUnitsPerDevUnit(); PRBool isRTL = IsRightToLeft(); @@ -3501,7 +3550,9 @@ gfxTextRun::AdjustAdvancesForSyntheticBold(PRUint32 aStart, PRUint32 aLength) while (iter.NextRun()) { gfxFont *font = iter.GetGlyphRun()->mFont; if (font->IsSyntheticBold()) { - PRUint32 synAppUnitOffset = font->GetSyntheticBoldOffset() * appUnitsPerDevUnit; + PRUint32 synAppUnitOffset = + font->GetSyntheticBoldOffset() * + appUnitsPerDevUnit * CalcXScale(aContext); PRUint32 start = iter.GetStringStart(); PRUint32 end = iter.GetStringEnd(); PRUint32 i; diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 3a8b1e19fdfb..4cd5574d17c5 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1193,8 +1193,12 @@ public: // This is called by the default Draw() implementation above. virtual PRBool SetupCairoFont(gfxContext *aContext) = 0; - PRBool IsSyntheticBold() { return mSyntheticBoldOffset != 0; } - PRUint32 GetSyntheticBoldOffset() { return mSyntheticBoldOffset; } + PRBool IsSyntheticBold() { return mApplySyntheticBold; } + + // Amount by which synthetic bold "fattens" the glyphs: 1/16 of the em-size + gfxFloat GetSyntheticBoldOffset() { + return GetAdjustedSize() * (1.0 / 16.0); + } gfxFontEntry *GetFontEntry() { return mFontEntry.get(); } PRBool HasCharacter(PRUint32 ch) { @@ -1224,6 +1228,11 @@ protected: nsRefPtr mFontEntry; PRPackedBool mIsValid; + + // use synthetic bolding for environments where this is not supported + // by the platform + PRPackedBool mApplySyntheticBold; + nsExpirationState mExpirationState; gfxFontStyle mStyle; nsAutoTArray mGlyphExtentsArray; @@ -1232,9 +1241,6 @@ protected: float mFUnitsConvFactor; // conversion factor from font units to dev units - // synthetic bolding for environments where this is not supported by the platform - PRUint32 mSyntheticBoldOffset; // number of devunit pixels to offset double-strike, 0 ==> no bolding - // the AA setting requested for this font - may affect glyph bounds AntialiasOption mAntialiasOption; @@ -2043,7 +2049,9 @@ public: #endif // post-process glyph advances to deal with synthetic bolding - void AdjustAdvancesForSyntheticBold(PRUint32 aStart, PRUint32 aLength); + void AdjustAdvancesForSyntheticBold(gfxContext *aContext, + PRUint32 aStart, + PRUint32 aLength); protected: /** diff --git a/gfx/thebes/gfxMacFont.cpp b/gfx/thebes/gfxMacFont.cpp index 14a878bcd64d..e1e8d62e6b0a 100644 --- a/gfx/thebes/gfxMacFont.cpp +++ b/gfx/thebes/gfxMacFont.cpp @@ -57,9 +57,7 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyl mFontFace(nsnull), mScaledFont(nsnull) { - if (aNeedsBold) { - mSyntheticBoldOffset = 1; // devunit offset when double-striking text to fake boldness - } + mApplySyntheticBold = aNeedsBold; mCGFont = aFontEntry->GetFontRef(); if (!mCGFont) { @@ -167,7 +165,7 @@ gfxMacFont::InitTextRun(gfxContext *aContext, aRunStart, aRunLength, aRunScript, static_cast(GetFontEntry())->RequiresAATLayout()); - aTextRun->AdjustAdvancesForSyntheticBold(aRunStart, aRunLength); + aTextRun->AdjustAdvancesForSyntheticBold(aContext, aRunStart, aRunLength); return ok; } @@ -320,8 +318,10 @@ gfxMacFont::InitMetrics() mMetrics.aveCharWidth = mMetrics.maxAdvance; } } - mMetrics.aveCharWidth += mSyntheticBoldOffset; - mMetrics.maxAdvance += mSyntheticBoldOffset; + if (IsSyntheticBold()) { + mMetrics.aveCharWidth += GetSyntheticBoldOffset(); + mMetrics.maxAdvance += GetSyntheticBoldOffset(); + } mMetrics.spaceWidth = GetCharWidth(cmap, ' ', &glyphID, cgConvFactor); if (glyphID == 0) { diff --git a/intl/uconv/src/nsUTF8ConverterService.cpp b/intl/uconv/src/nsUTF8ConverterService.cpp index ab85dd6a8d7e..34d422864f0d 100644 --- a/intl/uconv/src/nsUTF8ConverterService.cpp +++ b/intl/uconv/src/nsUTF8ConverterService.cpp @@ -76,7 +76,7 @@ ToUTF8(const nsACString &aString, const char *aCharset, nsACString &aResult) rv = unicodeDecoder->Convert(inStr.get(), &srcLen, ustr, &dstLen); if (NS_SUCCEEDED(rv)){ - // Tru64 Cxx and IRIX MIPSpro 7.3 need an explicit get() + // Tru64 Cxx needs an explicit get() CopyUTF16toUTF8(Substring(ustr.get(), ustr + dstLen), aResult); } return rv; diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3ac30aabdc5e..95bc34c3e42e 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1068,7 +1068,7 @@ JSObject::makeDenseArraySlow(JSContext *cx) js::Shape *oldMap = lastProp; /* Create a native scope. */ - js::gc::FinalizeKind kind = js::gc::FinalizeKind(arenaHeader()->getThingKind()); + gc::AllocKind kind = getAllocKind(); if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getProto()->getNewType(cx), kind)) return false; @@ -3256,7 +3256,7 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto) { JS_ASSERT_IF(proto, proto->isArray()); - gc::FinalizeKind kind = GuessObjectGCKind(length, true); + gc::AllocKind kind = GuessObjectGCKind(length, true); JSObject *obj = detail::NewObject(cx, &js_ArrayClass, proto, NULL, kind); if (!obj) return NULL; diff --git a/js/src/jscell.h b/js/src/jscell.h index d380a76dac72..d5d0595f067e 100644 --- a/js/src/jscell.h +++ b/js/src/jscell.h @@ -50,6 +50,37 @@ namespace gc { struct ArenaHeader; struct Chunk; +/* The GC allocation kinds. */ +enum AllocKind { + FINALIZE_OBJECT0, + FINALIZE_OBJECT0_BACKGROUND, + FINALIZE_OBJECT2, + FINALIZE_OBJECT2_BACKGROUND, + FINALIZE_OBJECT4, + FINALIZE_OBJECT4_BACKGROUND, + FINALIZE_OBJECT8, + FINALIZE_OBJECT8_BACKGROUND, + FINALIZE_OBJECT12, + FINALIZE_OBJECT12_BACKGROUND, + FINALIZE_OBJECT16, + FINALIZE_OBJECT16_BACKGROUND, + FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, + FINALIZE_FUNCTION, + FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION, + FINALIZE_SCRIPT, + FINALIZE_SHAPE, + FINALIZE_TYPE_OBJECT, +#if JS_HAS_XML_SUPPORT + FINALIZE_XML, +#endif + FINALIZE_SHORT_STRING, + FINALIZE_STRING, + FINALIZE_EXTERNAL_STRING, + FINALIZE_LAST = FINALIZE_EXTERNAL_STRING +}; + +const size_t FINALIZE_LIMIT = FINALIZE_LAST + 1; + /* * Live objects are marked black. How many other additional colors are available * depends on the size of the GCThing. @@ -67,6 +98,7 @@ struct Cell { inline uintptr_t address() const; inline ArenaHeader *arenaHeader() const; inline Chunk *chunk() const; + inline AllocKind getAllocKind() const; JS_ALWAYS_INLINE bool isMarked(uint32 color = BLACK) const; JS_ALWAYS_INLINE bool markIfUnmarked(uint32 color = BLACK) const; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index f55286cfe420..2f30ccfc6400 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -427,7 +427,7 @@ struct JSRuntime { int64 gcNextFullGCTime; int64 gcJitReleaseTime; JSGCMode gcMode; - volatile bool gcIsNeeded; + volatile jsuword gcIsNeeded; js::WeakMapBase *gcWeakMapList; /* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */ @@ -1794,17 +1794,33 @@ class AutoXMLRooter : private AutoGCRooter { class AutoLockGC { public: - explicit AutoLockGC(JSRuntime *rt + explicit AutoLockGC(JSRuntime *rt = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM) - : rt(rt) + : runtime(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; + if (rt) + JS_LOCK_GC(rt); + } + + bool locked() const { + return !!runtime; + } + + void lock(JSRuntime *rt) { + JS_ASSERT(rt); + JS_ASSERT(!runtime); + runtime = rt; JS_LOCK_GC(rt); } - ~AutoLockGC() { JS_UNLOCK_GC(rt); } + + ~AutoLockGC() { + if (runtime) + JS_UNLOCK_GC(runtime); + } private: - JSRuntime *rt; + JSRuntime *runtime; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 73f9c775121f..187f6d7f535c 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -129,9 +129,6 @@ JSCompartment::~JSCompartment() bool JSCompartment::init(JSContext *cx) { - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) - arenas[i].init(); - activeAnalysis = activeInference = false; types.init(cx); @@ -140,7 +137,6 @@ JSCompartment::init(JSContext *cx) JS_InitArenaPool(&pool, "analysis", 4096 - ARENA_HEADER_SIZE_HACK, 8); - freeLists.init(); if (!crossCompartmentWrappers.init()) return false; @@ -188,16 +184,6 @@ JSCompartment::getMjitCodeStats(size_t& method, size_t& regexp, size_t& unused) } #endif -bool -JSCompartment::arenaListsAreEmpty() -{ - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { - if (!arenas[i].isEmpty()) - return false; - } - return true; -} - static bool IsCrossCompartmentWrapper(JSObject *wrapper) { @@ -505,10 +491,10 @@ JSCompartment::markTypes(JSTracer *trc) MarkScript(trc, script, "mark_types_script"); } - for (unsigned thingKind = FINALIZE_OBJECT0; + for (size_t thingKind = FINALIZE_OBJECT0; thingKind <= FINALIZE_FUNCTION_AND_OBJECT_LAST; thingKind++) { - for (CellIterUnderGC i(this, FinalizeKind(thingKind)); !i.done(); i.next()) { + for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) { JSObject *object = i.get(); if (!object->isNewborn() && object->hasSingletonType()) MarkObject(trc, *object, "mark_types_singleton"); @@ -652,7 +638,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) void JSCompartment::purge(JSContext *cx) { - freeLists.purge(); + arenas.purge(); dtoaCache.purge(); /* diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 0252d8f404f8..ce0b87b111c8 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -394,8 +394,7 @@ struct JS_FRIEND_API(JSCompartment) { JSRuntime *rt; JSPrincipals *principals; - js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT]; - js::gc::FreeLists freeLists; + js::gc::ArenaLists arenas; uint32 gcBytes; uint32 gcTriggerBytes; @@ -535,12 +534,6 @@ struct JS_FRIEND_API(JSCompartment) { void markTypes(JSTracer *trc); void sweep(JSContext *cx, uint32 releaseInterval); void purge(JSContext *cx); - void finishArenaLists(); - void finalizeObjectArenaLists(JSContext *cx); - void finalizeStringArenaLists(JSContext *cx); - void finalizeShapeArenaLists(JSContext *cx); - void finalizeScriptArenaLists(JSContext *cx); - bool arenaListsAreEmpty(); void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind); void reduceGCTriggerBytes(uint32 amount); diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 765746429d27..21d5385fbb52 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -4891,7 +4891,7 @@ JSParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp) case TOK_RC: { JS_ASSERT((pn_op == JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST)); - gc::FinalizeKind kind = GuessObjectGCKind(pn_count, false); + gc::AllocKind kind = GuessObjectGCKind(pn_count, false); JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) return false; @@ -7084,7 +7084,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) */ JSObject *obj = NULL; if (!cg->hasSharps() && cg->compileAndGo()) { - gc::FinalizeKind kind = GuessObjectGCKind(pn->pn_count, false); + gc::AllocKind kind = GuessObjectGCKind(pn->pn_count, false); obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) return JS_FALSE; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a2b73c55accd..7d852a502dba 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -773,7 +773,7 @@ NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *c Bindings &bindings = script->bindings; size_t argsVars = bindings.countArgsAndVars(); size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars; - gc::FinalizeKind kind = gc::GetGCObjectKind(slots); + gc::AllocKind kind = gc::GetGCObjectKind(slots); JSObject *callobj = js_NewGCObject(cx, kind); if (!callobj) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 8c808c392d5c..90f0c1f7ff0c 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -110,7 +110,7 @@ namespace js { namespace gc { /* This array should be const, but that doesn't link right under GCC. */ -FinalizeKind slotsToThingKind[] = { +AllocKind slotsToThingKind[] = { /* 0 */ FINALIZE_OBJECT0, FINALIZE_OBJECT2, FINALIZE_OBJECT2, FINALIZE_OBJECT4, /* 4 */ FINALIZE_OBJECT4, FINALIZE_OBJECT8, FINALIZE_OBJECT8, FINALIZE_OBJECT8, /* 8 */ FINALIZE_OBJECT8, FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12, @@ -120,7 +120,7 @@ FinalizeKind slotsToThingKind[] = { JS_STATIC_ASSERT(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT); -const uint8 GCThingSizeMap[] = { +const uint32 Arena::ThingSizes[] = { sizeof(JSObject), /* FINALIZE_OBJECT0 */ sizeof(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */ sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2 */ @@ -145,7 +145,34 @@ const uint8 GCThingSizeMap[] = { sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ }; -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GCThingSizeMap) == FINALIZE_LIMIT); +#define OFFSET(type) uint32(sizeof(ArenaHeader) + (ArenaSize - sizeof(ArenaHeader)) % sizeof(type)) + +const uint32 Arena::FirstThingOffsets[] = { + OFFSET(JSObject), /* FINALIZE_OBJECT0 */ + OFFSET(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */ + OFFSET(JSObject_Slots2), /* FINALIZE_OBJECT2 */ + OFFSET(JSObject_Slots2), /* FINALIZE_OBJECT2_BACKGROUND */ + OFFSET(JSObject_Slots4), /* FINALIZE_OBJECT4 */ + OFFSET(JSObject_Slots4), /* FINALIZE_OBJECT4_BACKGROUND */ + OFFSET(JSObject_Slots8), /* FINALIZE_OBJECT8 */ + OFFSET(JSObject_Slots8), /* FINALIZE_OBJECT8_BACKGROUND */ + OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12 */ + OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */ + OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16 */ + OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */ + OFFSET(JSFunction), /* FINALIZE_FUNCTION */ + OFFSET(JSScript), /* FINALIZE_SCRIPT */ + OFFSET(Shape), /* FINALIZE_SHAPE */ + OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */ +#if JS_HAS_XML_SUPPORT + OFFSET(JSXML), /* FINALIZE_XML */ +#endif + OFFSET(JSShortString), /* FINALIZE_SHORT_STRING */ + OFFSET(JSString), /* FINALIZE_STRING */ + OFFSET(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ +}; + +#undef OFFSET #ifdef DEBUG void @@ -168,7 +195,7 @@ ArenaHeader::checkSynchronizedWithFreeList() const FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); if (firstSpan.isEmpty()) return; - FreeSpan *list = &compartment->freeLists.lists[getThingKind()]; + const FreeSpan *list = compartment->arenas.getFreeList(getAllocKind()); if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress()) return; @@ -180,18 +207,28 @@ ArenaHeader::checkSynchronizedWithFreeList() const } #endif +/* static */ void +Arena::staticAsserts() +{ + JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize); + JS_STATIC_ASSERT(JS_ARRAY_LENGTH(ThingSizes) == FINALIZE_LIMIT); + JS_STATIC_ASSERT(JS_ARRAY_LENGTH(FirstThingOffsets) == FINALIZE_LIMIT); +} + template inline bool -Arena::finalize(JSContext *cx) +Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize) { /* Enforce requirements on size of T. */ - JS_STATIC_ASSERT(sizeof(T) % Cell::CellSize == 0); - JS_STATIC_ASSERT(sizeof(T) <= 255); + JS_ASSERT(thingSize % Cell::CellSize == 0); + JS_ASSERT(thingSize <= 255); JS_ASSERT(aheader.allocated()); + JS_ASSERT(thingKind == aheader.getAllocKind()); + JS_ASSERT(thingSize == aheader.getThingSize()); JS_ASSERT(!aheader.getMarkingDelay()->link); - uintptr_t thing = thingsStart(sizeof(T)); + uintptr_t thing = thingsStart(thingKind); uintptr_t lastByte = thingsEnd() - 1; FreeSpan nextFree(aheader.getFirstFreeSpan()); @@ -204,13 +241,13 @@ Arena::finalize(JSContext *cx) #ifdef DEBUG size_t nmarked = 0; #endif - for (;; thing += sizeof(T)) { + for (;; thing += thingSize) { JS_ASSERT(thing <= lastByte + 1); if (thing == nextFree.first) { JS_ASSERT(nextFree.last <= lastByte); if (nextFree.last == lastByte) break; - JS_ASSERT(Arena::isAligned(nextFree.last, sizeof(T))); + JS_ASSERT(Arena::isAligned(nextFree.last, thingSize)); if (!newFreeSpanStart) newFreeSpanStart = thing; thing = nextFree.last; @@ -224,90 +261,134 @@ Arena::finalize(JSContext *cx) nmarked++; #endif if (newFreeSpanStart) { - JS_ASSERT(thing >= thingsStart(sizeof(T)) + sizeof(T)); + JS_ASSERT(thing >= thingsStart(thingKind) + thingSize); newListTail->first = newFreeSpanStart; - newListTail->last = thing - sizeof(T); - newListTail = newListTail->nextSpanUnchecked(sizeof(T)); + newListTail->last = thing - thingSize; + newListTail = newListTail->nextSpanUnchecked(thingSize); newFreeSpanStart = 0; } } else { if (!newFreeSpanStart) newFreeSpanStart = thing; t->finalize(cx); - JS_POISON(t, JS_FREE_PATTERN, sizeof(T)); + JS_POISON(t, JS_FREE_PATTERN, thingSize); } } } if (allClear) { JS_ASSERT(newListTail == &newListHead); - JS_ASSERT(newFreeSpanStart == thingsStart(sizeof(T))); + JS_ASSERT(newFreeSpanStart == thingsStart(thingKind)); return true; } newListTail->first = newFreeSpanStart ? newFreeSpanStart : nextFree.first; - JS_ASSERT(Arena::isAligned(newListTail->first, sizeof(T))); + JS_ASSERT(Arena::isAligned(newListTail->first, thingSize)); newListTail->last = lastByte; #ifdef DEBUG size_t nfree = 0; for (const FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) { span->checkSpan(); - JS_ASSERT(Arena::isAligned(span->first, sizeof(T))); - JS_ASSERT(Arena::isAligned(span->last, sizeof(T))); - nfree += (span->last - span->first) / sizeof(T) + 1; - JS_ASSERT(nfree + nmarked <= thingsPerArena(sizeof(T))); + JS_ASSERT(Arena::isAligned(span->first, thingSize)); + JS_ASSERT(Arena::isAligned(span->last, thingSize)); + nfree += (span->last - span->first) / thingSize + 1; + JS_ASSERT(nfree + nmarked <= thingsPerArena(thingSize)); } - nfree += (newListTail->last + 1 - newListTail->first) / sizeof(T); - JS_ASSERT(nfree + nmarked == thingsPerArena(sizeof(T))); + nfree += (newListTail->last + 1 - newListTail->first) / thingSize; + JS_ASSERT(nfree + nmarked == thingsPerArena(thingSize)); #endif aheader.setFirstFreeSpan(&newListHead); return false; } -/* - * Finalize arenas from the list. On return listHeadp points to the list of - * non-empty arenas. - */ template -static void -FinalizeArenas(JSContext *cx, ArenaHeader **listHeadp) +inline void +FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind) { - ArenaHeader **ap = listHeadp; + /* + * Release empty arenas and move non-full arenas with some free things into + * a separated list that we append to al after the loop to ensure that any + * arena before al->cursor is full. + */ + JS_ASSERT_IF(!al->head, al->cursor == &al->head); + ArenaLists::ArenaList available; + ArenaHeader **ap = &al->head; + size_t thingSize = Arena::thingSize(thingKind); while (ArenaHeader *aheader = *ap) { - bool allClear = aheader->getArena()->finalize(cx); + bool allClear = aheader->getArena()->finalize(cx, thingKind, thingSize); if (allClear) { *ap = aheader->next; aheader->chunk()->releaseArena(aheader); + } else if (aheader->hasFreeThings()) { + *ap = aheader->next; + *available.cursor = aheader; + available.cursor = &aheader->next; } else { ap = &aheader->next; } } + + /* Terminate the available list and append it to al. */ + *available.cursor = NULL; + *ap = available.head; + al->cursor = ap; + JS_ASSERT_IF(!al->head, al->cursor == &al->head); } -#ifdef DEBUG -bool -checkArenaListAllUnmarked(JSCompartment *comp) +/* + * Finalize the list. On return al->cursor points to the first non-empty arena + * after the al->head. + */ +static void +FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind) { - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { - if (comp->arenas[i].markedThingsInArenaList()) - return false; - } - return true; -} + switch(thingKind) { + case FINALIZE_OBJECT0: + case FINALIZE_OBJECT0_BACKGROUND: + case FINALIZE_OBJECT2: + case FINALIZE_OBJECT2_BACKGROUND: + case FINALIZE_OBJECT4: + case FINALIZE_OBJECT4_BACKGROUND: + case FINALIZE_OBJECT8: + case FINALIZE_OBJECT8_BACKGROUND: + case FINALIZE_OBJECT12: + case FINALIZE_OBJECT12_BACKGROUND: + case FINALIZE_OBJECT16: + case FINALIZE_OBJECT16_BACKGROUND: + case FINALIZE_FUNCTION: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_SCRIPT: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_SHAPE: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_TYPE_OBJECT: + FinalizeTypedArenas(cx, al, thingKind); + break; +#if JS_HAS_XML_SUPPORT + case FINALIZE_XML: + FinalizeTypedArenas(cx, al, thingKind); + break; #endif + case FINALIZE_STRING: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_SHORT_STRING: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_EXTERNAL_STRING: + FinalizeTypedArenas(cx, al, thingKind); + break; + } +} } /* namespace gc */ } /* namespace js */ -void -JSCompartment::finishArenaLists() -{ - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) - arenas[i].releaseAll(i); -} - void Chunk::init(JSRuntime *rt) { @@ -376,15 +457,14 @@ Chunk::removeFromAvailableList() info.next = NULL; } -template ArenaHeader * -Chunk::allocateArena(JSContext *cx, unsigned thingKind) +Chunk::allocateArena(JSContext *cx, AllocKind thingKind) { JSCompartment *comp = cx->compartment; JS_ASSERT(hasAvailableArenas()); ArenaHeader *aheader = info.emptyArenaListHead; info.emptyArenaListHead = aheader->next; - aheader->init(comp, thingKind, thingSize); + aheader->init(comp, thingKind); --info.numFree; if (!hasAvailableArenas()) @@ -406,9 +486,9 @@ Chunk::releaseArena(ArenaHeader *aheader) JS_ASSERT(aheader->allocated()); JSRuntime *rt = info.runtime; #ifdef JS_THREADSAFE - Maybe maybeLock; + AutoLockGC maybeLock; if (rt->gcHelperThread.sweeping) - maybeLock.construct(info.runtime); + maybeLock.lock(info.runtime); #endif JSCompartment *comp = aheader->compartment; @@ -472,7 +552,8 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p) rt->gcChunkAllocator->free_(p); } -inline Chunk * +/* The caller must hold the GC lock. */ +static Chunk * PickChunk(JSContext *cx) { JSCompartment *comp = cx->compartment; @@ -651,45 +732,6 @@ InFreeList(ArenaHeader *aheader, uintptr_t addr) } } -template -inline ConservativeGCTest -MarkArenaPtrConservatively(JSTracer *trc, ArenaHeader *aheader, uintptr_t addr) -{ - JS_ASSERT(aheader->allocated()); - JS_ASSERT(sizeof(T) == aheader->getThingSize()); - - uintptr_t offset = addr & ArenaMask; - uintptr_t minOffset = Arena::thingsStartOffset(sizeof(T)); - if (offset < minOffset) - return CGCT_NOTARENA; - - /* addr can point inside the thing so we must align the address. */ - uintptr_t shift = (offset - minOffset) % sizeof(T); - addr -= shift; - - /* - * Check if the thing is free. We must use the list of free spans as at - * this point we no longer have the mark bits from the previous GC run and - * we must account for newly allocated things. - */ - if (InFreeList(aheader, addr)) - return CGCT_NOTLIVE; - - T *thing = reinterpret_cast(addr); - MarkRoot(trc, thing, "machine stack"); - -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - if (IS_GC_MARKING_TRACER(trc)) { - GCMarker *marker = static_cast(trc); - if (marker->conservativeDumpFileName) - marker->conservativeRoots.append(thing); - if (shift) - marker->conservativeStats.unaligned++; - } -#endif - return CGCT_VALID; -} - /* * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets * thingKind accordingly. Otherwise returns the reason for rejection. @@ -737,66 +779,44 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) if (!aheader->allocated()) return CGCT_FREEARENA; - ConservativeGCTest test; - unsigned thingKind = aheader->getThingKind(); + AllocKind thingKind = aheader->getAllocKind(); + uintptr_t offset = addr & ArenaMask; + uintptr_t minOffset = Arena::firstThingOffset(thingKind); + if (offset < minOffset) + return CGCT_NOTARENA; - switch (thingKind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_EXTERNAL_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SHORT_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_FUNCTION: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SCRIPT: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SHAPE: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_TYPE_OBJECT: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; + /* addr can point inside the thing so we must align the address. */ + uintptr_t shift = (offset - minOffset) % Arena::thingSize(thingKind); + addr -= shift; + + /* + * Check if the thing is free. We must use the list of free spans as at + * this point we no longer have the mark bits from the previous GC run and + * we must account for newly allocated things. + */ + if (InFreeList(aheader, addr)) + return CGCT_NOTLIVE; + + void *thing = reinterpret_cast(addr); + +#ifdef DEBUG + const char pattern[] = "machine_stack %lx"; + char nameBuf[sizeof(pattern) - 3 + sizeof(addr) * 2]; + JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) addr); + JS_SET_TRACING_NAME(trc, nameBuf); #endif - default: - test = CGCT_WRONGTAG; - JS_NOT_REACHED("wrong tag"); - } + MarkKind(trc, thing, MapAllocToTraceKind(thingKind)); - return test; +#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS + if (IS_GC_MARKING_TRACER(trc)) { + GCMarker *marker = static_cast(trc); + if (marker->conservativeDumpFileName) + marker->conservativeRoots.append(thing); + if (shift) + marker->conservativeStats.unaligned++; + } +#endif + return CGCT_VALID; } static void @@ -926,11 +946,8 @@ void js_FinishGC(JSRuntime *rt) { /* Delete all remaining Compartments. */ - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) { - JSCompartment *comp = *c; - comp->finishArenaLists(); - Foreground::delete_(comp); - } + for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) + Foreground::delete_(*c); rt->compartments.clear(); rt->atomsCompartment = NULL; @@ -1137,227 +1154,263 @@ JSCompartment::reduceGCTriggerBytes(uint32 amount) { namespace js { namespace gc { -inline ArenaHeader * -ArenaList::searchForFreeArena() +inline void * +ArenaLists::allocateFromArena(JSContext *cx, AllocKind thingKind) { - while (ArenaHeader *aheader = *cursor) { - cursor = &aheader->next; - if (aheader->hasFreeThings()) - return aheader; - } - return NULL; -} + Chunk *chunk = NULL; -template -inline ArenaHeader * -ArenaList::getArenaWithFreeList(JSContext *cx, unsigned thingKind) -{ - Chunk *chunk; + ArenaList *al = &arenaLists[thingKind]; + AutoLockGC maybeLock; #ifdef JS_THREADSAFE - /* - * We cannot search the arena list for free things while the - * background finalization runs and can modify head or cursor at any - * moment. - */ - if (backgroundFinalizeState == BFS_DONE) { - check_arena_list: - if (ArenaHeader *aheader = searchForFreeArena()) - return aheader; - } - - AutoLockGC lock(cx->runtime); - - for (;;) { - if (backgroundFinalizeState == BFS_JUST_FINISHED) { - /* - * Before we took the GC lock or while waiting for the background - * finalization to finish the latter added new arenas to the list. - * Check the list again for free things outside the GC lock. - */ - JS_ASSERT(*cursor); - backgroundFinalizeState = BFS_DONE; - goto check_arena_list; - } - - JS_ASSERT(!*cursor); - chunk = PickChunk(cx); - if (chunk || backgroundFinalizeState == BFS_DONE) - break; - + volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind]; + if (*bfs != BFS_DONE) { /* - * If the background finalization still runs, wait for it to - * finish and retry to check if it populated the arena list or - * added new empty arenas. + * We cannot search the arena list for free things while the + * background finalization runs and can modify head or cursor at any + * moment. So we always allocate a new arena in that case. */ - JS_ASSERT(backgroundFinalizeState == BFS_RUN); - cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime, false); - JS_ASSERT(backgroundFinalizeState == BFS_JUST_FINISHED || - backgroundFinalizeState == BFS_DONE); + maybeLock.lock(cx->runtime); + for (;;) { + if (*bfs == BFS_DONE) + break; + + if (*bfs == BFS_JUST_FINISHED) { + /* + * Before we took the GC lock or while waiting for the + * background finalization to finish the latter added new + * arenas to the list. + */ + *bfs = BFS_DONE; + break; + } + + JS_ASSERT(!*al->cursor); + chunk = PickChunk(cx); + if (chunk) + break; + + /* + * If the background finalization still runs, wait for it to + * finish and retry to check if it populated the arena list or + * added new empty arenas. + */ + JS_ASSERT(*bfs == BFS_RUN); + cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime, false); + JS_ASSERT(*bfs == BFS_JUST_FINISHED || *bfs == BFS_DONE); + } } - -#else /* !JS_THREADSAFE */ - - if (ArenaHeader *aheader = searchForFreeArena()) - return aheader; - chunk = PickChunk(cx); - -#endif /* !JS_THREADSAFE */ +#endif /* JS_THREADSAFE */ if (!chunk) { - GCREASON(CHUNK); - TriggerGC(cx->runtime); - return NULL; + if (ArenaHeader *aheader = *al->cursor) { + JS_ASSERT(aheader->hasFreeThings()); + + /* + * The empty arenas are returned to the chunk and should not present on + * the list. + */ + JS_ASSERT(!aheader->isEmpty()); + al->cursor = &aheader->next; + + /* + * Move the free span stored in the arena to the free list and + * allocate from it. + */ + freeLists[thingKind] = aheader->getFirstFreeSpan(); + aheader->setAsFullyUsed(); + return freeLists[thingKind].infallibleAllocate(Arena::thingSize(thingKind)); + } + + /* Make sure we hold the GC lock before we call PickChunk. */ + if (!maybeLock.locked()) + maybeLock.lock(cx->runtime); + chunk = PickChunk(cx); + if (!chunk) + return NULL; } /* - * While we still hold the GC lock get the arena from the chunk and add it - * to the head of the list before the cursor to prevent checking the arena - * for the free things. + * While we still hold the GC lock get an arena from some chunk, mark it + * as full as its single free span is moved to the free lits, and insert + * it to the list as a fully allocated arena. + * + * We add the arena before the the head, not after the tail pointed by the + * cursor, so after the GC the most recently added arena will be used first + * for allocations improving cache locality. */ - ArenaHeader *aheader = chunk->allocateArena(cx, thingKind); - aheader->next = head; - if (cursor == &head) - cursor = &aheader->next; - head = aheader; - return aheader; + JS_ASSERT(!*al->cursor); + ArenaHeader *aheader = chunk->allocateArena(cx, thingKind); + aheader->next = al->head; + if (!al->head) { + JS_ASSERT(al->cursor == &al->head); + al->cursor = &aheader->next; + } + al->head = aheader; + + /* See comments before allocateFromNewArena about this assert. */ + JS_ASSERT(!aheader->hasFreeThings()); + uintptr_t arenaAddr = aheader->arenaAddress(); + return freeLists[thingKind].allocateFromNewArena(arenaAddr, + Arena::firstThingOffset(thingKind), + Arena::thingSize(thingKind)); } -template void -ArenaList::finalizeNow(JSContext *cx) +ArenaLists::finalizeNow(JSContext *cx, AllocKind thingKind) { #ifdef JS_THREADSAFE - JS_ASSERT(backgroundFinalizeState == BFS_DONE); + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE); #endif - FinalizeArenas(cx, &head); - cursor = &head; + FinalizeArenas(cx, &arenaLists[thingKind], thingKind); } -#ifdef JS_THREADSAFE -template inline void -ArenaList::finalizeLater(JSContext *cx) +ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind) { - JS_ASSERT_IF(head, - head->getThingKind() == FINALIZE_OBJECT0_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT2_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT4_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT8_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT12_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT16_BACKGROUND || - head->getThingKind() == FINALIZE_FUNCTION || - head->getThingKind() == FINALIZE_SHORT_STRING || - head->getThingKind() == FINALIZE_STRING); + JS_ASSERT(thingKind == FINALIZE_OBJECT0_BACKGROUND || + thingKind == FINALIZE_OBJECT2_BACKGROUND || + thingKind == FINALIZE_OBJECT4_BACKGROUND || + thingKind == FINALIZE_OBJECT8_BACKGROUND || + thingKind == FINALIZE_OBJECT12_BACKGROUND || + thingKind == FINALIZE_OBJECT16_BACKGROUND || + thingKind == FINALIZE_FUNCTION || + thingKind == FINALIZE_SHORT_STRING || + thingKind == FINALIZE_STRING); + +#ifdef JS_THREADSAFE JS_ASSERT(!cx->runtime->gcHelperThread.sweeping); + ArenaList *al = &arenaLists[thingKind]; + if (!al->head) { + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE); + JS_ASSERT(al->cursor == &al->head); + return; + } + /* * The state can be just-finished if we have not allocated any GC things * from the arena list after the previous background finalization. */ - JS_ASSERT(backgroundFinalizeState == BFS_DONE || - backgroundFinalizeState == BFS_JUST_FINISHED); + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE || + backgroundFinalizeState[thingKind] == BFS_JUST_FINISHED); - if (head && cx->gcBackgroundFree) { + if (cx->gcBackgroundFree) { /* * To ensure the finalization order even during the background GC we * must use infallibleAppend so arenas scheduled for background * finalization would not be finalized now if the append fails. */ - cx->gcBackgroundFree->finalizeVector.infallibleAppend(head); - head = NULL; - cursor = &head; - backgroundFinalizeState = BFS_RUN; + cx->gcBackgroundFree->finalizeVector.infallibleAppend(al->head); + al->clear(); + backgroundFinalizeState[thingKind] = BFS_RUN; } else { - JS_ASSERT_IF(!head, cursor == &head); - backgroundFinalizeState = BFS_DONE; - finalizeNow(cx); + FinalizeArenas(cx, al, thingKind); + backgroundFinalizeState[thingKind] = BFS_DONE; } + +#else /* !JS_THREADSAFE */ + + finalizeNow(cx, thingKind); + +#endif } /*static*/ void -ArenaList::backgroundFinalize(JSContext *cx, ArenaHeader *listHead) +ArenaLists::backgroundFinalize(JSContext *cx, ArenaHeader *listHead) { JS_ASSERT(listHead); - unsigned thingKind = listHead->getThingKind(); + AllocKind thingKind = listHead->getAllocKind(); JSCompartment *comp = listHead->compartment; - ArenaList *al = &comp->arenas[thingKind]; - - switch (thingKind) { - default: - JS_NOT_REACHED("wrong kind"); - break; - case FINALIZE_OBJECT0_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT2_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT4_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT8_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT12_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT16_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_FUNCTION: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_STRING: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_SHORT_STRING: - FinalizeArenas(cx, &listHead); - break; - } + ArenaList finalized; + finalized.head = listHead; + FinalizeArenas(cx, &finalized, thingKind); /* * After we finish the finalization al->cursor must point to the end of * the head list as we emptied the list before the background finalization * and the allocation adds new arenas before the cursor. */ + ArenaLists *lists = &comp->arenas; + ArenaList *al = &lists->arenaLists[thingKind]; + AutoLockGC lock(cx->runtime); - JS_ASSERT(al->backgroundFinalizeState == BFS_RUN); + JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN); JS_ASSERT(!*al->cursor); - if (listHead) { - *al->cursor = listHead; - al->backgroundFinalizeState = BFS_JUST_FINISHED; + + /* + * We must set the state to BFS_JUST_FINISHED if we touch arenaList list, + * even if we add to the list only fully allocated arenas without any free + * things. It ensures that the allocation thread takes the GC lock and all + * writes to the free list elements are propagated. As we always take the + * GC lock when allocating new arenas from the chunks we can set the state + * to BFS_DONE if we have released all finalized arenas back to their + * chunks. + */ + if (finalized.head) { + *al->cursor = finalized.head; + if (finalized.cursor != &finalized.head) + al->cursor = finalized.cursor; + lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED; } else { - al->backgroundFinalizeState = BFS_DONE; + lists->backgroundFinalizeState[thingKind] = BFS_DONE; } } -#endif /* JS_THREADSAFE */ - -#ifdef DEBUG -bool -CheckAllocation(JSContext *cx) +void +ArenaLists::finalizeObjects(JSContext *cx) { + finalizeNow(cx, FINALIZE_OBJECT0); + finalizeNow(cx, FINALIZE_OBJECT2); + finalizeNow(cx, FINALIZE_OBJECT4); + finalizeNow(cx, FINALIZE_OBJECT8); + finalizeNow(cx, FINALIZE_OBJECT12); + finalizeNow(cx, FINALIZE_OBJECT16); + #ifdef JS_THREADSAFE - JS_ASSERT(cx->thread()); -#endif - JS_ASSERT(!cx->runtime->gcRunning); - return true; -} + finalizeLater(cx, FINALIZE_OBJECT0_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT2_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT4_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT8_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT12_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT16_BACKGROUND); #endif -inline bool -NeedLastDitchGC(JSContext *cx) + /* + * We must finalize Function instances after finalizing any other objects + * even if we use the background finalization for the latter. See comments + * in JSObject::finalizeUpvarsIfFlatClosure. + */ + finalizeLater(cx, FINALIZE_FUNCTION); + +#if JS_HAS_XML_SUPPORT + finalizeNow(cx, FINALIZE_XML); +#endif +} + +void +ArenaLists::finalizeStrings(JSContext *cx) { - JSRuntime *rt = cx->runtime; - return rt->gcIsNeeded; + finalizeLater(cx, FINALIZE_SHORT_STRING); + finalizeLater(cx, FINALIZE_STRING); + + finalizeNow(cx, FINALIZE_EXTERNAL_STRING); } -/* - * Return false only if the GC run but could not bring its memory usage under - * JSRuntime::gcMaxBytes. - */ -static bool +void +ArenaLists::finalizeShapes(JSContext *cx) +{ + finalizeNow(cx, FINALIZE_SHAPE); + finalizeNow(cx, FINALIZE_TYPE_OBJECT); +} + +void +ArenaLists::finalizeScripts(JSContext *cx) +{ + finalizeNow(cx, FINALIZE_SCRIPT); +} + +static void RunLastDitchGC(JSContext *cx) { JSRuntime *rt = cx->runtime; @@ -1375,37 +1428,35 @@ RunLastDitchGC(JSContext *cx) if (rt->gcBytes >= rt->gcMaxBytes) cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime); #endif - - return rt->gcBytes < rt->gcMaxBytes; } -static inline bool +inline bool IsGCAllowed(JSContext *cx) { return !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; } -template -inline void * -RefillTypedFreeList(JSContext *cx, unsigned thingKind) +/* static */ void * +ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { - JS_ASSERT(!cx->runtime->gcRunning); + JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty()); /* * For compatibility with older code we tolerate calling the allocator * during the GC in optimized builds. */ - if (cx->runtime->gcRunning) + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + if (rt->gcRunning) return NULL; - JSCompartment *compartment = cx->compartment; - JS_ASSERT(compartment->freeLists.lists[thingKind].isEmpty()); - - bool canGC = IsGCAllowed(cx); - bool runGC = canGC && JS_UNLIKELY(NeedLastDitchGC(cx)); + bool runGC = !!rt->gcIsNeeded; for (;;) { - if (runGC) { - if (!RunLastDitchGC(cx)) + if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) { + RunLastDitchGC(cx); + + /* Report OOM of the GC failed to free enough memory. */ + if (rt->gcBytes > rt->gcMaxBytes) break; /* @@ -1413,22 +1464,24 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind) * things and populate the free list. If that happens, just * return that list head. */ - if (void *thing = compartment->freeLists.getNext(thingKind, sizeof(T))) + size_t thingSize = Arena::thingSize(thingKind); + if (void *thing = cx->compartment->arenas.allocateFromFreeList(thingKind, thingSize)) return thing; } - ArenaHeader *aheader = - compartment->arenas[thingKind].getArenaWithFreeList(cx, thingKind); - if (aheader) { - JS_ASSERT(sizeof(T) == aheader->getThingSize()); - return compartment->freeLists.populate(aheader, thingKind, sizeof(T)); - } + void *thing = cx->compartment->arenas.allocateFromArena(cx, thingKind); + if (JS_LIKELY(!!thing)) + return thing; /* - * We failed to allocate any arena. Run the GC if we can unless we - * have done it already. + * We failed to allocate. Run the GC if we can unless we have done it + * already. Otherwise report OOM but first schedule a new GC soon. */ - if (!canGC || runGC) + if (runGC || !IsGCAllowed(cx)) { + AutoLockGC lock(rt); + GCREASON(REFILL); + TriggerGC(rt); break; + } runGC = true; } @@ -1436,52 +1489,6 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind) return NULL; } -void * -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) -{ - switch (thingKind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_EXTERNAL_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SHORT_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_FUNCTION: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SCRIPT: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SHAPE: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_TYPE_OBJECT: - return RefillTypedFreeList(cx, thingKind); -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - return RefillTypedFreeList(cx, thingKind); -#endif - default: - JS_NOT_REACHED("bad finalize kind"); - return 0; - } -} - } /* namespace gc */ } /* namespace js */ @@ -1583,11 +1590,12 @@ GCMarker::delayMarkingChildren(const void *thing) static void MarkDelayedChildren(JSTracer *trc, ArenaHeader *aheader) { - JSGCTraceKind traceKind = GetFinalizableTraceKind(aheader->getThingKind()); + AllocKind thingKind = aheader->getAllocKind(); + JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); size_t thingSize = aheader->getThingSize(); Arena *a = aheader->getArena(); uintptr_t end = a->thingsEnd(); - for (uintptr_t thing = a->thingsStart(thingSize); thing != end; thing += thingSize) { + for (uintptr_t thing = a->thingsStart(thingKind); thing != end; thing += thingSize) { Cell *t = reinterpret_cast(thing); if (t->isMarked()) JS_TraceChildren(trc, t, traceKind); @@ -1973,67 +1981,6 @@ MaybeGC(JSContext *cx) } /* namespace js */ -void -JSCompartment::finalizeObjectArenaLists(JSContext *cx) -{ - arenas[FINALIZE_OBJECT0]. finalizeNow(cx); - arenas[FINALIZE_OBJECT2]. finalizeNow(cx); - arenas[FINALIZE_OBJECT4]. finalizeNow(cx); - arenas[FINALIZE_OBJECT8]. finalizeNow(cx); - arenas[FINALIZE_OBJECT12].finalizeNow(cx); - arenas[FINALIZE_OBJECT16].finalizeNow(cx); - -#ifdef JS_THREADSAFE - arenas[FINALIZE_OBJECT0_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT2_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT4_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT8_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT12_BACKGROUND].finalizeLater(cx); - arenas[FINALIZE_OBJECT16_BACKGROUND].finalizeLater(cx); -#endif - - /* - * We must finalize Function instances after finalizing any other objects - * even if we use the background finalization for the latter. See comments - * in JSObject::finalizeUpvarsIfFlatClosure. - */ -#ifdef JS_THREADSAFE - arenas[FINALIZE_FUNCTION].finalizeLater(cx); -#else - arenas[FINALIZE_FUNCTION].finalizeNow(cx); -#endif - -#if JS_HAS_XML_SUPPORT - arenas[FINALIZE_XML].finalizeNow(cx); -#endif -} - -void -JSCompartment::finalizeStringArenaLists(JSContext *cx) -{ -#ifdef JS_THREADSAFE - arenas[FINALIZE_SHORT_STRING].finalizeLater(cx); - arenas[FINALIZE_STRING].finalizeLater(cx); -#else - arenas[FINALIZE_SHORT_STRING].finalizeNow(cx); - arenas[FINALIZE_STRING].finalizeNow(cx); -#endif - arenas[FINALIZE_EXTERNAL_STRING].finalizeNow(cx); -} - -void -JSCompartment::finalizeShapeArenaLists(JSContext *cx) -{ - arenas[FINALIZE_TYPE_OBJECT].finalizeNow(cx); - arenas[FINALIZE_SHAPE].finalizeNow(cx); -} - -void -JSCompartment::finalizeScriptArenaLists(JSContext *cx) -{ - arenas[FINALIZE_SCRIPT].finalizeNow(cx); -} - #ifdef JS_THREADSAFE namespace js { @@ -2125,9 +2072,9 @@ GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind) void GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked) { - Maybe lock; + AutoLockGC maybeLock; if (gcUnlocked) - lock.construct(rt); + maybeLock.lock(rt); while (sweeping) PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT); } @@ -2158,10 +2105,10 @@ GCHelperThread::doSweep() /* * We must finalize in the insert order, see comments in - * finalizeObjectArenaLists. + * finalizeObjects. */ for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) - ArenaList::backgroundFinalize(cx, *i); + ArenaLists::backgroundFinalize(cx, *i); finalizeVector.resize(0); ExpireGCChunks(cx->runtime, lastGCKind); cx = NULL; @@ -2232,9 +2179,9 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) JSCompartment *compartment = *read++; if (!compartment->hold && - (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) + (compartment->arenas.arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) { - compartment->freeLists.checkEmpty(); + compartment->arenas.checkEmptyFreeLists(); if (callback) JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY)); if (compartment->principals) @@ -2334,7 +2281,8 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM /* Make sure that we didn't mark an object in another compartment */ if (comp) { for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - JS_ASSERT_IF(*c != comp && *c != rt->atomsCompartment, checkArenaListAllUnmarked(*c)); + JS_ASSERT_IF(*c != comp && *c != rt->atomsCompartment, + (*c)->arenas.checkArenaListAllUnmarked()); } #endif @@ -2372,13 +2320,13 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM if (comp) { Probes::GCStartSweepPhase(comp); comp->sweep(cx, 0); - comp->finalizeObjectArenaLists(cx); + comp->arenas.finalizeObjects(cx); GCTIMESTAMP(sweepObjectEnd); - comp->finalizeStringArenaLists(cx); + comp->arenas.finalizeStrings(cx); GCTIMESTAMP(sweepStringEnd); - comp->finalizeScriptArenaLists(cx); + comp->arenas.finalizeScripts(cx); GCTIMESTAMP(sweepScriptEnd); - comp->finalizeShapeArenaLists(cx); + comp->arenas.finalizeShapes(cx); GCTIMESTAMP(sweepShapeEnd); Probes::GCEndSweepPhase(comp); } else { @@ -2393,24 +2341,24 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM SweepCrossCompartmentWrappers(cx); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) { Probes::GCStartSweepPhase(*c); - (*c)->finalizeObjectArenaLists(cx); + (*c)->arenas.finalizeObjects(cx); } GCTIMESTAMP(sweepObjectEnd); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - (*c)->finalizeStringArenaLists(cx); + (*c)->arenas.finalizeStrings(cx); GCTIMESTAMP(sweepStringEnd); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) { - (*c)->finalizeScriptArenaLists(cx); + (*c)->arenas.finalizeScripts(cx); } GCTIMESTAMP(sweepScriptEnd); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) { - (*c)->finalizeShapeArenaLists(cx); + (*c)->arenas.finalizeShapes(cx); Probes::GCEndSweepPhase(*c); } @@ -2808,12 +2756,12 @@ class AutoCopyFreeListToArenas { AutoCopyFreeListToArenas(JSRuntime *rt) : rt(rt) { for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->freeLists.copyToArenas(); + (*c)->arenas.copyFreeListsToArenas(); } ~AutoCopyFreeListToArenas() { for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->freeLists.clearInArenas(); + (*c)->arenas.clearFreeListsInArenas(); } }; @@ -2904,19 +2852,18 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data, JSCompartment *compartment = *c; (*compartmentCallback)(cx, data, compartment); - for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) { - JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind); - size_t thingSize = GCThingSizeMap[thingKind]; + for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) { + JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind)); + size_t thingSize = Arena::thingSize(AllocKind(thingKind)); IterateArenaCallbackOp arenaOp(cx, data, arenaCallback, traceKind, thingSize); IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize); - - ForEachArenaAndCell(compartment, (FinalizeKind) thingKind, arenaOp, cellOp); + ForEachArenaAndCell(compartment, AllocKind(thingKind), arenaOp, cellOp); } } } void -IterateCells(JSContext *cx, JSCompartment *compartment, FinalizeKind thingKind, +IterateCells(JSContext *cx, JSCompartment *compartment, AllocKind thingKind, void *data, IterateCellCallback cellCallback) { /* :XXX: Any way to common this preamble with IterateCompartmentsArenasCells? */ @@ -2936,8 +2883,8 @@ IterateCells(JSContext *cx, JSCompartment *compartment, FinalizeKind thingKind, AutoCopyFreeListToArenas copy(rt); - JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind); - size_t thingSize = GCThingSizeMap[thingKind]; + JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); + size_t thingSize = Arena::thingSize(thingKind); if (compartment) { for (CellIterUnderGC i(compartment, thingKind); !i.done(); i.next()) diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 6b12dda1fc04..37727111e2f2 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -80,43 +80,12 @@ namespace gc { struct Arena; struct MarkingDelay; -/* The kind of GC thing with a finalizer. */ -enum FinalizeKind { - FINALIZE_OBJECT0, - FINALIZE_OBJECT0_BACKGROUND, - FINALIZE_OBJECT2, - FINALIZE_OBJECT2_BACKGROUND, - FINALIZE_OBJECT4, - FINALIZE_OBJECT4_BACKGROUND, - FINALIZE_OBJECT8, - FINALIZE_OBJECT8_BACKGROUND, - FINALIZE_OBJECT12, - FINALIZE_OBJECT12_BACKGROUND, - FINALIZE_OBJECT16, - FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_FUNCTION, - FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION, - FINALIZE_SCRIPT, - FINALIZE_SHAPE, - FINALIZE_TYPE_OBJECT, -#if JS_HAS_XML_SUPPORT - FINALIZE_XML, -#endif - FINALIZE_SHORT_STRING, - FINALIZE_STRING, - FINALIZE_EXTERNAL_STRING, - FINALIZE_LIMIT -}; - /* * This must be an upper bound, but we do not need the least upper bound, so * we just exclude non-background objects. */ const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - (FINALIZE_OBJECT_LAST + 1) / 2; -extern JS_FRIEND_DATA(const uint8) GCThingSizeMap[]; - const size_t ArenaShift = 12; const size_t ArenaSize = size_t(1) << ArenaShift; const size_t ArenaMask = ArenaSize - 1; @@ -174,7 +143,7 @@ struct FreeSpan { * To minimize the size of the arena header the first span is encoded * there as offsets from the arena start. */ - static size_t encodeOffsets(size_t firstOffset, size_t lastOffset = ArenaSize - 1) { + static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) { /* Check that we can pack the offsets into uint16. */ JS_STATIC_ASSERT(ArenaShift < 16); JS_ASSERT(firstOffset <= ArenaSize); @@ -183,7 +152,11 @@ struct FreeSpan { return firstOffset | (lastOffset << 16); } - static const size_t EmptyOffsets = ArenaSize | ((ArenaSize - 1) << 16); + /* + * Encoded offsets for a full arena when its first span is the last one + * and empty. + */ + static const size_t FullArenaOffsets = ArenaSize | ((ArenaSize - 1) << 16); static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) { JS_ASSERT(!(arenaAddr & ArenaMask)); @@ -287,6 +260,37 @@ struct FreeSpan { return reinterpret_cast(thing); } + /* A version of allocate when we know that the span is not empty. */ + JS_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { + JS_ASSERT(thingSize % Cell::CellSize == 0); + checkSpan(); + uintptr_t thing = first; + if (thing < last) { + first = thing + thingSize; + } else { + JS_ASSERT(thing == last); + *this = *reinterpret_cast(thing); + } + checkSpan(); + return reinterpret_cast(thing); + } + + /* + * Allocate from a newly allocated arena. We do not move the free list + * from the arena. Rather we set the arena up as fully used during the + * initialization so to allocate we simply return the first thing in the + * arena and set the free list to point to the second. + */ + JS_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, + size_t thingSize) { + JS_ASSERT(!(arenaAddr & ArenaMask)); + uintptr_t thing = arenaAddr | firstThingOffset; + first = thing + thingSize; + last = arenaAddr | ArenaMask; + checkSpan(); + return reinterpret_cast(thing); + } + void checkSpan() const { #ifdef DEBUG /* We do not allow spans at the end of the address space. */ @@ -365,13 +369,13 @@ struct ArenaHeader { size_t firstFreeSpanOffsets; /* - * One of FinalizeKind constants or FINALIZE_LIMIT when the arena does not + * One of AllocKind constants or FINALIZE_LIMIT when the arena does not * contain any GC things and is on the list of empty arenas in the GC - * chunk. The later allows to quickly check if the arena is allocated + * chunk. The latter allows to quickly check if the arena is allocated * during the conservative GC scanning without searching the arena in the * list. */ - unsigned thingKind; + unsigned allocKind; friend struct FreeLists; @@ -380,14 +384,15 @@ struct ArenaHeader { inline Chunk *chunk() const; void setAsNotAllocated() { - thingKind = FINALIZE_LIMIT; + allocKind = FINALIZE_LIMIT; } bool allocated() const { - return thingKind < FINALIZE_LIMIT; + JS_ASSERT(allocKind <= FINALIZE_LIMIT); + return allocKind < FINALIZE_LIMIT; } - inline void init(JSCompartment *comp, unsigned thingKind, size_t thingSize); + inline void init(JSCompartment *comp, AllocKind kind); uintptr_t arenaAddress() const { return address(); @@ -397,17 +402,21 @@ struct ArenaHeader { return reinterpret_cast(arenaAddress()); } - unsigned getThingKind() const { + AllocKind getAllocKind() const { JS_ASSERT(allocated()); - return thingKind; + return AllocKind(allocKind); } + inline size_t getThingSize() const; + bool hasFreeThings() const { - return firstFreeSpanOffsets != FreeSpan::EmptyOffsets; + return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets; } + inline bool isEmpty() const; + void setAsFullyUsed() { - firstFreeSpanOffsets = FreeSpan::EmptyOffsets; + firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; } FreeSpan getFirstFreeSpan() const { @@ -424,10 +433,6 @@ struct ArenaHeader { inline MarkingDelay *getMarkingDelay() const; - size_t getThingSize() const { - return GCThingSizeMap[getThingKind()]; - } - #ifdef DEBUG void checkSynchronizedWithFreeList() const; #endif @@ -446,13 +451,24 @@ struct Arena { * +-------------+-----+----+----+-----+----+ * * <----------------------------------------> = ArenaSize bytes - * <-------------------> = thingsStartOffset + * <-------------------> = first thing offset */ ArenaHeader aheader; uint8_t data[ArenaSize - sizeof(ArenaHeader)]; - static void staticAsserts() { - JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize); + private: + static JS_FRIEND_DATA(const uint32) ThingSizes[]; + static JS_FRIEND_DATA(const uint32) FirstThingOffsets[]; + + public: + static void staticAsserts(); + + static size_t thingSize(AllocKind kind) { + return ThingSizes[kind]; + } + + static size_t firstThingOffset(AllocKind kind) { + return FirstThingOffsets[kind]; } static size_t thingsPerArena(size_t thingSize) { @@ -461,9 +477,6 @@ struct Arena { /* We should be able to fit FreeSpan in any GC thing. */ JS_ASSERT(thingSize >= sizeof(FreeSpan)); - /* GCThingSizeMap assumes that any thing fits uint8. */ - JS_ASSERT(thingSize < 256); - return (ArenaSize - sizeof(ArenaHeader)) / thingSize; } @@ -471,10 +484,6 @@ struct Arena { return thingsPerArena(thingSize) * thingSize; } - static size_t thingsStartOffset(size_t thingSize) { - return ArenaSize - thingsSpan(thingSize); - } - static bool isAligned(uintptr_t thing, size_t thingSize) { /* Things ends at the arena end. */ uintptr_t tailOffset = (ArenaSize - thing) & ArenaMask; @@ -485,8 +494,8 @@ struct Arena { return aheader.address(); } - uintptr_t thingsStart(size_t thingSize) { - return address() | thingsStartOffset(thingSize); + uintptr_t thingsStart(AllocKind thingKind) { + return address() | firstThingOffset(thingKind); } uintptr_t thingsEnd() { @@ -494,7 +503,7 @@ struct Arena { } template - bool finalize(JSContext *cx); + bool finalize(JSContext *cx, AllocKind thingKind, size_t thingSize); }; /* @@ -641,8 +650,7 @@ struct Chunk { inline void addToAvailableList(JSCompartment *compartment); inline void removeFromAvailableList(); - template - ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind); + ArenaHeader *allocateArena(JSContext *cx, AllocKind kind); void releaseArena(ArenaHeader *aheader); }; @@ -676,6 +684,12 @@ Cell::chunk() const return reinterpret_cast(addr); } +AllocKind +Cell::getAllocKind() const +{ + return arenaHeader()->getAllocKind(); +} + #ifdef DEBUG inline bool Cell::isAligned() const @@ -685,13 +699,15 @@ Cell::isAligned() const #endif inline void -ArenaHeader::init(JSCompartment *comp, unsigned kind, size_t thingSize) +ArenaHeader::init(JSCompartment *comp, AllocKind kind) { JS_ASSERT(!allocated()); JS_ASSERT(!getMarkingDelay()->link); compartment = comp; - thingKind = kind; - firstFreeSpanOffsets = FreeSpan::encodeOffsets(Arena::thingsStartOffset(thingSize)); + allocKind = kind; + + /* See comments in FreeSpan::allocateFromNewArena. */ + firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; } inline uintptr_t @@ -709,6 +725,22 @@ ArenaHeader::chunk() const return Chunk::fromAddress(address()); } +inline bool +ArenaHeader::isEmpty() const +{ + /* Arena is empty if its first span covers the whole arena. */ + JS_ASSERT(allocated()); + size_t firstThingOffset = Arena::firstThingOffset(getAllocKind()); + return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, ArenaMask); +} + +inline size_t +ArenaHeader::getThingSize() const +{ + JS_ASSERT(allocated()); + return Arena::thingSize(getAllocKind()); +} + JS_ALWAYS_INLINE void ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32 color, uintptr_t **wordp, uintptr_t *maskp) @@ -779,7 +811,7 @@ const float GC_HEAP_GROWTH_FACTOR = 3.0f; static const int64 GC_IDLE_FULL_SPAN = 20 * 1000 * 1000; static inline JSGCTraceKind -GetFinalizableTraceKind(size_t thingKind) +MapAllocToTraceKind(AllocKind thingKind) { static const JSGCTraceKind map[FINALIZE_LIMIT] = { JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ @@ -805,8 +837,6 @@ GetFinalizableTraceKind(size_t thingKind) JSTRACE_STRING, /* FINALIZE_STRING */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ }; - - JS_ASSERT(thingKind < FINALIZE_LIMIT); return map[thingKind]; } @@ -819,11 +849,46 @@ GetGCThingRuntime(void *thing) return reinterpret_cast(thing)->chunk()->info.runtime; } -/* The arenas in a list have uniform kind. */ -class ArenaList { +struct ArenaLists { + + /* + * ArenaList::head points to the start of the list. Normally cursor points + * to the first arena in the list with some free things and all arenas + * before cursor are fully allocated. However, as the arena currently being + * allocated from is considered full while its list of free spans is moved + * into the freeList, during the GC or cell enumeration, when an + * unallocated freeList is moved back to the arena, we can see an arena + * with some free cells before the cursor. The cursor is an indirect + * pointer to allow for efficient list insertion at the cursor point and + * other list manipulations. + */ + struct ArenaList { + ArenaHeader *head; + ArenaHeader **cursor; + + ArenaList() { + clear(); + } + + void clear() { + head = NULL; + cursor = &head; + } + }; + private: - ArenaHeader *head; /* list start */ - ArenaHeader **cursor; /* arena with free things */ + /* + * For each arena kind its free list is represented as the first span with + * free things. Initially all the spans are initialized as empty. After we + * find a new arena with available things we move its first free span into + * the list and set the arena as fully allocated. way we do not need to + * update the arena header after the initial allocation. When starting the + * GC we only move the head of the of the list of spans back to the arena + * only for the arena that was not fully allocated. + */ + FreeSpan freeLists[FINALIZE_LIMIT]; + + ArenaList arenaLists[FINALIZE_LIMIT]; #ifdef JS_THREADSAFE /* @@ -848,116 +913,95 @@ class ArenaList { BFS_JUST_FINISHED }; - volatile BackgroundFinalizeState backgroundFinalizeState; + volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT]; #endif public: - void init() { - head = NULL; - cursor = &head; + ArenaLists() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + freeLists[i].initAsEmpty(); #ifdef JS_THREADSAFE - backgroundFinalizeState = BFS_DONE; + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + backgroundFinalizeState[i] = BFS_DONE; #endif } - ArenaHeader *getHead() { return head; } - - inline ArenaHeader *searchForFreeArena(); - - template - inline ArenaHeader *getArenaWithFreeList(JSContext *cx, unsigned thingKind); - - template - void finalizeNow(JSContext *cx); - + ~ArenaLists() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { #ifdef JS_THREADSAFE - template - inline void finalizeLater(JSContext *cx); - - static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead); - - bool willBeFinalizedLater() const { - return backgroundFinalizeState == BFS_RUN; - } - - bool doneBackgroundFinalize() const { - return backgroundFinalizeState == BFS_DONE; - } + /* + * We can only call this during the shutdown after the last GC when + * the background finalization is disabled. + */ + JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE); #endif + ArenaHeader **headp = &arenaLists[i].head; + while (ArenaHeader *aheader = *headp) { + *headp = aheader->next; + aheader->chunk()->releaseArena(aheader); + } + } + } + + const FreeSpan *getFreeList(AllocKind thingKind) const { + return &freeLists[thingKind]; + } + + ArenaHeader *getFirstArena(AllocKind thingKind) const { + return arenaLists[thingKind].head; + } + + bool arenaListsAreEmpty() const { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { +#ifdef JS_THREADSAFE + /* + * The arena cannot be empty if the background finalization is not yet + * done. + */ + if (backgroundFinalizeState[i] != BFS_DONE) + return false; +#endif + if (arenaLists[i].head) + return false; + } + return true; + } #ifdef DEBUG - bool markedThingsInArenaList() { + bool checkArenaListAllUnmarked() const { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { # ifdef JS_THREADSAFE - /* The background finalization must have stopped at this point. */ - JS_ASSERT(backgroundFinalizeState == BFS_DONE || - backgroundFinalizeState == BFS_JUST_FINISHED); + /* The background finalization must have stopped at this point. */ + JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE || + backgroundFinalizeState[i] == BFS_JUST_FINISHED); # endif - for (ArenaHeader *aheader = head; aheader; aheader = aheader->next) { - if (!aheader->chunk()->bitmap.noBitsSet(aheader)) - return true; + for (ArenaHeader *aheader = arenaLists[i].head; aheader; aheader = aheader->next) { + if (!aheader->chunk()->bitmap.noBitsSet(aheader)) + return false; + } } - return false; + return true; } -#endif /* DEBUG */ - - void releaseAll(unsigned thingKind) { -# ifdef JS_THREADSAFE - /* - * We can only call this during the shutdown after the last GC when - * the background finalization is disabled. - */ - JS_ASSERT(backgroundFinalizeState == BFS_DONE); -# endif - while (ArenaHeader *aheader = head) { - head = aheader->next; - aheader->chunk()->releaseArena(aheader); - } - cursor = &head; - } - - bool isEmpty() const { -#ifdef JS_THREADSAFE - /* - * The arena cannot be empty if the background finalization is not yet - * done. - */ - if (backgroundFinalizeState != BFS_DONE) - return false; #endif - return !head; - } -}; -struct FreeLists { - /* - * For each arena kind its free list is represented as the first span with - * free things. Initially all the spans are zeroed to be treated as empty - * spans by the allocation code. After we find a new arena with available - * things we copy its first free span into the list and set the arena as - * if it has no free things. This way we do not need to update the arena - * header after the initial allocation. When starting the GC We only move - * the head of the of the list of spans back to the arena only for the - * arena that was not fully allocated. - */ - FreeSpan lists[FINALIZE_LIMIT]; - - void init() { - for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i) - lists[i].initAsEmpty(); +#ifdef JS_THREADSAFE + bool doneBackgroundFinalize(AllocKind kind) const { + return backgroundFinalizeState[kind] == BFS_DONE; } +#endif /* * Return the free list back to the arena so the GC finalization will not * run the finalizers over unitialized bytes from free things. */ void purge() { - for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) { - FreeSpan *list = &lists[i]; - if (!list->isEmpty()) { - ArenaHeader *aheader = list->arenaHeader(); + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { + FreeSpan *headSpan = &freeLists[i]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(list); - list->initAsEmpty(); + aheader->setFirstFreeSpan(headSpan); + headSpan->initAsEmpty(); } } } @@ -967,17 +1011,17 @@ struct FreeLists { * the proper value in ArenaHeader::freeList when accessing the latter * outside the GC. */ - void copyToArenas() { - for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) - copyToArena(FinalizeKind(i)); + void copyFreeListsToArenas() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + copyFreeListToArena(AllocKind(i)); } - void copyToArena(FinalizeKind thingKind) { - FreeSpan *list = &lists[thingKind]; - if (!list->isEmpty()) { - ArenaHeader *aheader = list->arenaHeader(); + void copyFreeListToArena(AllocKind thingKind) { + FreeSpan *headSpan = &freeLists[thingKind]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(list); + aheader->setFirstFreeSpan(headSpan); } } @@ -985,17 +1029,17 @@ struct FreeLists { * Clear the free lists in arenas that were temporarily set there using * copyToArenas. */ - void clearInArenas() { - for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) - clearInArena(FinalizeKind(i)); + void clearFreeListsInArenas() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + clearFreeListInArena(AllocKind(i)); } - void clearInArena(FinalizeKind thingKind) { - FreeSpan *list = &lists[thingKind]; - if (!list->isEmpty()) { - ArenaHeader *aheader = list->arenaHeader(); - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list)); + void clearFreeListInArena(AllocKind kind) { + FreeSpan *headSpan = &freeLists[kind]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); + JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); aheader->setAsFullyUsed(); } } @@ -1004,45 +1048,54 @@ struct FreeLists { * Check that the free list is either empty or were synchronized with the * arena using copyToArena(). */ - bool isSynchronizedWithArena(FinalizeKind thingKind) { - FreeSpan *list = &lists[thingKind]; - if (list->isEmpty()) + bool isSynchronizedFreeList(AllocKind kind) { + FreeSpan *headSpan = &freeLists[kind]; + if (headSpan->isEmpty()) return true; - ArenaHeader *aheader = list->arenaHeader(); + ArenaHeader *aheader = headSpan->arenaHeader(); if (aheader->hasFreeThings()) { /* * If the arena has a free list, it must be the same as one in * lists. - */ - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list)); + */ + JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); return true; } return false; } - JS_ALWAYS_INLINE void *getNext(unsigned thingKind, size_t thingSize) { - return lists[thingKind].allocate(thingSize); + JS_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) { + return freeLists[thingKind].allocate(thingSize); } - void *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) { - FreeSpan *list = &lists[thingKind]; - *list = aheader->getFirstFreeSpan(); - aheader->setAsFullyUsed(); - void *t = list->allocate(thingSize); - JS_ASSERT(t); - return t; - } + static void *refillFreeList(JSContext *cx, AllocKind thingKind); - void checkEmpty() { + void checkEmptyFreeLists() { #ifdef DEBUG - for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i) - JS_ASSERT(lists[i].isEmpty()); + for (size_t i = 0; i != JS_ARRAY_LENGTH(freeLists); ++i) + JS_ASSERT(freeLists[i].isEmpty()); #endif } -}; -extern void * -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); + void checkEmptyFreeList(AllocKind kind) { + JS_ASSERT(freeLists[kind].isEmpty()); + } + + void finalizeObjects(JSContext *cx); + void finalizeStrings(JSContext *cx); + void finalizeShapes(JSContext *cx); + void finalizeScripts(JSContext *cx); + +#ifdef JS_THREADSAFE + static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead); + + private: + inline void finalizeNow(JSContext *cx, AllocKind thingKind); + inline void finalizeLater(JSContext *cx, AllocKind thingKind); + + inline void *allocateFromArena(JSContext *cx, AllocKind thingKind); +#endif +}; /* * Initial allocation size for data structures holding chunks is set to hold @@ -1254,7 +1307,7 @@ class GCHelperThread { Vector finalizeVector; - friend class js::gc::ArenaList; + friend struct js::gc::ArenaLists; JS_FRIEND_API(void) replenishAndFreeLater(void *ptr); @@ -1520,7 +1573,7 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data, * the given compartment or for all compartments if it is null. */ extern JS_FRIEND_API(void) -IterateCells(JSContext *cx, JSCompartment *compartment, gc::FinalizeKind thingKind, +IterateCells(JSContext *cx, JSCompartment *compartment, gc::AllocKind thingKind, void *data, IterateCellCallback cellCallback); } /* namespace js */ diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index a215a4835f4c..ff64de8fc32f 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -119,17 +119,17 @@ GetGCThingTraceKind(const void *thing) if (JSAtom::isStatic(thing)) return JSTRACE_STRING; const Cell *cell = reinterpret_cast(thing); - return GetFinalizableTraceKind(cell->arenaHeader()->getThingKind()); + return MapAllocToTraceKind(cell->getAllocKind()); } /* Capacity for slotsToThingKind */ const size_t SLOTS_TO_THING_KIND_LIMIT = 17; /* Get the best kind to use when making an object with the given slot count. */ -static inline FinalizeKind +static inline AllocKind GetGCObjectKind(size_t numSlots, bool isArray = false) { - extern FinalizeKind slotsToThingKind[]; + extern AllocKind slotsToThingKind[]; if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) { /* @@ -144,37 +144,36 @@ GetGCObjectKind(size_t numSlots, bool isArray = false) } static inline bool -IsBackgroundFinalizeKind(FinalizeKind kind) +IsBackgroundAllocKind(AllocKind kind) { JS_ASSERT(kind <= FINALIZE_OBJECT_LAST); return kind % 2 == 1; } -static inline FinalizeKind -GetBackgroundFinalizeKind(FinalizeKind kind) +static inline AllocKind +GetBackgroundAllocKind(AllocKind kind) { - JS_ASSERT(!IsBackgroundFinalizeKind(kind)); - return (FinalizeKind) (kind + 1); + JS_ASSERT(!IsBackgroundAllocKind(kind)); + return (AllocKind) (kind + 1); } +/* + * Try to get the next larger size for an object, keeping BACKGROUND + * consistent. + */ static inline bool -CanBumpFinalizeKind(FinalizeKind kind) +TryIncrementAllocKind(AllocKind *kindp) { - JS_ASSERT(kind <= FINALIZE_OBJECT_LAST); - return (kind + 2) <= FINALIZE_OBJECT_LAST; -} - -/* Get the next larger size for an object, keeping BACKGROUND consistent. */ -static inline FinalizeKind -BumpFinalizeKind(FinalizeKind kind) -{ - JS_ASSERT(CanBumpFinalizeKind(kind)); - return (FinalizeKind) (kind + 2); + size_t next = size_t(*kindp) + 2; + if (next > size_t(FINALIZE_OBJECT_LAST)) + return false; + *kindp = AllocKind(next); + return true; } /* Get the number of fixed slots and initial capacity associated with a kind. */ static inline size_t -GetGCKindSlots(FinalizeKind thingKind) +GetGCKindSlots(AllocKind thingKind) { /* Using a switch in hopes that thingKind will usually be a compile-time constant. */ switch (thingKind) { @@ -229,11 +228,11 @@ GCPoke(JSContext *cx, Value oldval) */ template void -ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind, +ForEachArenaAndCell(JSCompartment *compartment, AllocKind thingKind, ArenaOp arenaOp, CellOp cellOp) { - size_t thingSize = GCThingSizeMap[thingKind]; - ArenaHeader *aheader = compartment->arenas[thingKind].getHead(); + size_t thingSize = Arena::thingSize(thingKind); + ArenaHeader *aheader = compartment->arenas.getFirstArena(thingKind); for (; aheader; aheader = aheader->next) { Arena *arena = aheader->getArena(); @@ -241,7 +240,7 @@ ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind, FreeSpan firstSpan(aheader->getFirstFreeSpan()); const FreeSpan *span = &firstSpan; - for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) { + for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) { JS_ASSERT(thing <= arena->thingsEnd()); if (thing == span->first) { if (!span->hasNext()) @@ -258,6 +257,7 @@ ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind, class CellIterImpl { + size_t firstThingOffset; size_t thingSize; ArenaHeader *aheader; FreeSpan firstSpan; @@ -269,9 +269,10 @@ class CellIterImpl CellIterImpl() { } - void init(JSCompartment *comp, FinalizeKind thingKind) { - thingSize = GCThingSizeMap[thingKind]; - aheader = comp->arenas[thingKind].getHead(); + void init(JSCompartment *comp, AllocKind kind) { + firstThingOffset = Arena::firstThingOffset(kind); + thingSize = Arena::thingSize(kind); + aheader = comp->arenas.getFirstArena(kind); firstSpan.initAsEmpty(); span = &firstSpan; thing = span->first; @@ -308,7 +309,7 @@ class CellIterImpl } firstSpan = aheader->getFirstFreeSpan(); span = &firstSpan; - thing = aheader->getArena()->thingsStart(thingSize); + thing = aheader->arenaAddress() | firstThingOffset; aheader = aheader->next; } cell = reinterpret_cast(thing); @@ -319,10 +320,10 @@ class CellIterImpl class CellIterUnderGC : public CellIterImpl { public: - CellIterUnderGC(JSCompartment *comp, FinalizeKind thingKind) { + CellIterUnderGC(JSCompartment *comp, AllocKind kind) { JS_ASSERT(comp->rt->gcRunning); - JS_ASSERT(comp->freeLists.lists[thingKind].isEmpty()); - init(comp, thingKind); + comp->arenas.checkEmptyFreeList(kind); + init(comp, kind); } }; @@ -333,29 +334,29 @@ class CellIterUnderGC : public CellIterImpl { */ class CellIter: public CellIterImpl { - FreeLists *lists; - FinalizeKind thingKind; + ArenaLists *lists; + AllocKind kind; #ifdef DEBUG size_t *counter; #endif public: - CellIter(JSContext *cx, JSCompartment *comp, FinalizeKind thingKind) - : lists(&comp->freeLists), - thingKind(thingKind) { + CellIter(JSContext *cx, JSCompartment *comp, AllocKind kind) + : lists(&comp->arenas), + kind(kind) { #ifdef JS_THREADSAFE - JS_ASSERT(comp->arenas[thingKind].doneBackgroundFinalize()); + JS_ASSERT(comp->arenas.doneBackgroundFinalize(kind)); #endif - if (lists->isSynchronizedWithArena(thingKind)) { + if (lists->isSynchronizedFreeList(kind)) { lists = NULL; } else { JS_ASSERT(!comp->rt->gcRunning); - lists->copyToArena(thingKind); + lists->copyFreeListToArena(kind); } #ifdef DEBUG counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck; ++*counter; #endif - init(comp, thingKind); + init(comp, kind); } ~CellIter() { @@ -364,7 +365,7 @@ class CellIter: public CellIterImpl --*counter; #endif if (lists) - lists->clearInArena(thingKind); + lists->clearFreeListInArena(kind); } }; @@ -385,14 +386,12 @@ inline void EmptyCellOp(Cell *t) {} template inline T * -NewGCThing(JSContext *cx, unsigned thingKind, size_t thingSize) +NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) { - JS_ASSERT(thingKind < js::gc::FINALIZE_LIMIT); - JS_ASSERT(thingSize == js::gc::GCThingSizeMap[thingKind]); + JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind)); #ifdef JS_THREADSAFE JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment), - (thingKind == js::gc::FINALIZE_STRING) || - (thingKind == js::gc::FINALIZE_SHORT_STRING)); + kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING); #endif JS_ASSERT(!cx->runtime->gcRunning); JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck); @@ -402,15 +401,15 @@ NewGCThing(JSContext *cx, unsigned thingKind, size_t thingSize) js::gc::RunDebugGC(cx); #endif - void *t = cx->compartment->freeLists.getNext(thingKind, thingSize); - return static_cast(t ? t : js::gc::RefillFinalizableFreeList(cx, thingKind)); + void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize); + return static_cast(t ? t : js::gc::ArenaLists::refillFreeList(cx, kind)); } inline JSObject * -js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind) +js_NewGCObject(JSContext *cx, js::gc::AllocKind kind) { JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); - JSObject *obj = NewGCThing(cx, kind, js::gc::GCThingSizeMap[kind]); + JSObject *obj = NewGCThing(cx, kind, js::gc::Arena::thingSize(kind)); if (obj) obj->earlyInit(js::gc::GetGCKindSlots(kind)); return obj; diff --git a/js/src/jsgcstats.cpp b/js/src/jsgcstats.cpp index 4ba1744bf54c..dd73c50b42f0 100644 --- a/js/src/jsgcstats.cpp +++ b/js/src/jsgcstats.cpp @@ -71,7 +71,6 @@ ConservativeGCStats::dump(FILE *fp) fprintf(fp, " not withing a chunk: %lu\n", ULSTAT(counter[CGCT_NOTCHUNK])); fprintf(fp, " not within arena range: %lu\n", ULSTAT(counter[CGCT_NOTARENA])); fprintf(fp, " points to free arena: %lu\n", ULSTAT(counter[CGCT_FREEARENA])); - fprintf(fp, " excluded, wrong tag: %lu\n", ULSTAT(counter[CGCT_WRONGTAG])); fprintf(fp, " excluded, not live: %lu\n", ULSTAT(counter[CGCT_NOTLIVE])); fprintf(fp, " valid GC things: %lu\n", ULSTAT(counter[CGCT_VALID])); fprintf(fp, " valid but not aligned: %lu\n", ULSTAT(unaligned)); @@ -204,7 +203,7 @@ GCMarker::dumpConservativeRoots() volatile GCTimer::JSGCReason gcReason = GCTimer::NOREASON; const char *gcReasons[] = {" API", "Maybe", "LastC", "DestC", "Compa", "LastD", - "Malloc", "Alloc", "Chunk", "Shape", " None"}; + "Malloc", "Refill", "Chunk", "Shape", " None"}; jsrefcount newChunkCount = 0; jsrefcount destroyChunkCount = 0; diff --git a/js/src/jsgcstats.h b/js/src/jsgcstats.h index 4925006c9a70..1ed5726ce919 100644 --- a/js/src/jsgcstats.h +++ b/js/src/jsgcstats.h @@ -99,7 +99,6 @@ enum ConservativeGCTest CGCT_NOTARENA, /* not within arena range in a chunk */ CGCT_NOTCHUNK, /* not within a valid chunk */ CGCT_FREEARENA, /* within arena containing only free things */ - CGCT_WRONGTAG, /* tagged pointer but wrong type */ CGCT_NOTLIVE, /* gcthing is not allocated */ CGCT_END }; @@ -162,7 +161,7 @@ struct GCTimer LASTDITCH, TOOMUCHMALLOC, ALLOCTRIGGER, - CHUNK, + REFILL, SHAPE, NOREASON }; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index d45a70a96a34..7d35c4473939 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2042,9 +2042,9 @@ TypeCompartment::nukeTypes(JSContext *cx) */ #ifdef JS_THREADSAFE - Maybe maybeLock; + AutoLockGC maybeLock; if (!cx->runtime->gcMarkAndSweep) - maybeLock.construct(cx->runtime); + maybeLock.lock(cx->runtime); #endif inferenceEnabled = false; @@ -4411,7 +4411,7 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) return; } - gc::FinalizeKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); + gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); /* We should not have overflowed the maximum number of fixed slots for an object. */ JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan()); @@ -4441,7 +4441,7 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) } type->newScript->script = script; - type->newScript->finalizeKind = unsigned(kind); + type->newScript->allocKind = kind; type->newScript->shape = baseobj->lastProperty(); type->newScript->initializerList = (TypeNewScript::Initializer *) @@ -5480,7 +5480,7 @@ TypeCompartment::sweep(JSContext *cx) const AllocationSiteKey &key = e.front().key; TypeObject *object = e.front().value; - if (key.script->isAboutToBeFinalized(cx) || !object->isMarked()) + if (IsAboutToBeFinalized(cx, key.script) || !object->isMarked()) e.removeFront(); } } @@ -5520,7 +5520,7 @@ TypeScript::Sweep(JSContext *cx, JSScript *script) unsigned num = NumTypeSets(script); TypeSet *typeArray = script->types->typeArray(); - if (script->isAboutToBeFinalized(cx)) { + if (IsAboutToBeFinalized(cx, script)) { /* Release all memory associated with the persistent type sets. */ for (unsigned i = 0; i < num; i++) typeArray[i].clearObjects(); diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 99ed6309ea95..90e3506d078a 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -646,8 +646,8 @@ struct TypeNewScript { JSScript *script; - /* Finalize kind to use for newly constructed objects. */ - /* gc::FinalizeKind */ unsigned finalizeKind; + /* Allocation kind to use for newly constructed objects. */ + gc::AllocKind allocKind; /* * Shape to use for newly constructed objects. Reflects all definite @@ -806,8 +806,7 @@ struct TypeObject : gc::Cell * used as the scope of a new object whose prototype is |proto|. */ inline bool canProvideEmptyShape(js::Class *clasp); - inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, - /* gc::FinalizeKind */ unsigned kind); + inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, gc::AllocKind kind); /* * Get or create a property of this object. Only call this for properties which diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index f7b1239a9ad9..7f3b30d04acc 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -1218,14 +1218,6 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key) } } /* namespace js::types */ -inline bool -JSScript::isAboutToBeFinalized(JSContext *cx) -{ - return isCachedEval || - (u.object && IsAboutToBeFinalized(cx, u.object)) || - (hasFunction && IsAboutToBeFinalized(cx, function())); -} - inline bool JSScript::ensureHasTypes(JSContext *cx) { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 1a8e04a1b5bf..b4187a0d265f 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -5238,7 +5238,7 @@ BEGIN_CASE(JSOP_NEWINIT) if (i == JSProto_Array) { obj = NewDenseEmptyArray(cx); } else { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); + gc::AllocKind kind = GuessObjectGCKind(0, false); obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 54ba25276b8a..6a28e0c3be75 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2913,7 +2913,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) if (!obj) { /* Make an object whether this was called with 'new' or not. */ JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined()); - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) return JS_FALSE; @@ -2928,7 +2928,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) JSObject * js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent, - gc::FinalizeKind kind, const Shape *shape) + gc::AllocKind kind, const Shape *shape) { JSObject *res = NewObjectWithType(cx, type, parent, kind); if (!res) @@ -2979,7 +2979,7 @@ js_CreateThis(JSContext *cx, JSObject *callee) JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL; JSObject *parent = callee->getParent(); - gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp); + gc::AllocKind kind = NewObjectGCKind(cx, newclasp); JSObject *obj = NewObject(cx, newclasp, proto, parent, kind); if (obj) obj->syncSpecialEquality(); @@ -2995,14 +2995,14 @@ CreateThisForFunctionWithType(JSContext *cx, types::TypeObject *type, JSObject * * which reflects any properties that will definitely be added to the * object before it is read from. */ - gc::FinalizeKind kind = gc::FinalizeKind(type->newScript->finalizeKind); + gc::AllocKind kind = type->newScript->allocKind; JSObject *res = NewObjectWithType(cx, type, parent, kind); if (res) res->setMap((Shape *) type->newScript->shape); return res; } - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); return NewObjectWithType(cx, type, parent, kind); } @@ -3018,7 +3018,7 @@ js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *pro return NULL; res = CreateThisForFunctionWithType(cx, type, callee->getParent()); } else { - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); res = NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent(), kind); } @@ -3077,7 +3077,7 @@ JSObject* FASTCALL js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj) { if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); + gc::AllocKind kind = GuessObjectGCKind(0, false); return NewObjectWithClassProto(cx, &js_ObjectClass, proto, kind); } @@ -3129,7 +3129,7 @@ js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot) return NULL; } - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); return NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, kind); } JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, OBJECT, UINTN, 0, @@ -3405,7 +3405,7 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp) JS_ASSERT(proto->isStaticBlock()); size_t count = OBJ_BLOCK_COUNT(cx, proto); - gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1); + gc::AllocKind kind = gc::GetGCObjectKind(count + 1); TypeObject *type = proto->getNewType(cx); if (!type) @@ -3615,9 +3615,7 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) return NULL; } } - JSObject *clone = NewObject(cx, getClass(), - proto, parent, - gc::FinalizeKind(finalizeKind())); + JSObject *clone = NewObject(cx, getClass(), proto, parent, getAllocKind()); if (!clone) return NULL; if (isNative()) { @@ -4364,16 +4362,15 @@ JSObject::allocSlots(JSContext *cx, size_t newcap) * objects are constructed. */ if (!hasLazyType() && type()->newScript) { - gc::FinalizeKind kind = gc::FinalizeKind(type()->newScript->finalizeKind); + gc::AllocKind kind = type()->newScript->allocKind; unsigned newScriptSlots = gc::GetGCKindSlots(kind); - if (newScriptSlots == numFixedSlots() && gc::CanBumpFinalizeKind(kind)) { - kind = gc::BumpFinalizeKind(kind); + if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) { JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind, type()->newScript->shape); if (!obj) return false; - type()->newScript->finalizeKind = kind; + type()->newScript->allocKind = kind; type()->newScript->shape = obj->lastProperty(); type()->markStateChange(cx); } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 540bf04c388d..51ab6da89468 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -664,8 +664,6 @@ struct JSObject : js::gc::Cell { inline bool hasPropertyTable() const; - /* gc::FinalizeKind */ unsigned finalizeKind() const; - uint32 numSlots() const { return uint32(capacity); } inline size_t structSize() const; @@ -1279,7 +1277,7 @@ struct JSObject : js::gc::Cell { js::types::TypeObject *type, JSObject *parent, void *priv, - /* gc::FinalizeKind */ unsigned kind); + js::gc::AllocKind kind); inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 8fcc22281d1a..029778b79424 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -402,12 +402,6 @@ JSObject::setPrimitiveThis(const js::Value &pthis) setFixedSlot(JSSLOT_PRIMITIVE_THIS, pthis); } -inline /* gc::FinalizeKind */ unsigned -JSObject::finalizeKind() const -{ - return js::gc::FinalizeKind(arenaHeader()->getThingKind()); -} - inline bool JSObject::hasSlotsArray() const { @@ -964,7 +958,7 @@ JSObject::initSharingEmptyShape(JSContext *cx, js::types::TypeObject *type, JSObject *parent, void *privateValue, - /* js::gc::FinalizeKind */ unsigned kind) + js::gc::AllocKind kind) { init(cx, aclasp, type, parent, privateValue, false); @@ -1245,7 +1239,7 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri static inline bool InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, js::types::TypeObject *type, - gc::FinalizeKind kind) + gc::AllocKind kind) { JS_ASSERT(clasp->isNative()); @@ -1273,7 +1267,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, js::types::Ty } static inline bool -CanBeFinalizedInBackground(gc::FinalizeKind kind, Class *clasp) +CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp) { #ifdef JS_THREADSAFE JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST); @@ -1281,10 +1275,10 @@ CanBeFinalizedInBackground(gc::FinalizeKind kind, Class *clasp) * a different thread, we change the finalize kind. For example, * FINALIZE_OBJECT0 calls the finalizer on the main thread, * FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread. - * IsBackgroundFinalizeKind is called to prevent recursively incrementing + * IsBackgroundAllocKind is called to prevent recursively incrementing * the finalize kind; kind may already be a background finalize kind. */ - if (!gc::IsBackgroundFinalizeKind(kind) && + if (!gc::IsBackgroundAllocKind(kind) && (!clasp->finalize || clasp->flags & JSCLASS_CONCURRENT_FINALIZER)) { return true; } @@ -1300,7 +1294,7 @@ CanBeFinalizedInBackground(gc::FinalizeKind kind, Class *clasp) */ static inline JSObject * NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, - JSObject *parent, gc::FinalizeKind kind) + JSObject *parent, gc::AllocKind kind) { JS_ASSERT(proto); JS_ASSERT(parent); @@ -1316,7 +1310,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, */ if (CanBeFinalizedInBackground(kind, clasp)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); @@ -1343,7 +1337,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, static inline JSObject * NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return NewNativeClassInstance(cx, clasp, proto, parent, kind); } @@ -1358,7 +1352,7 @@ FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject * right default proto and parent for clasp in cx. */ static inline JSObject * -NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) +NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::AllocKind kind) { VOUCH_DOES_NOT_REQUIRE_STACK(); @@ -1392,7 +1386,7 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) static inline JSObject * NewBuiltinClassInstance(JSContext *cx, Class *clasp) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return NewBuiltinClassInstance(cx, clasp, kind); } @@ -1457,7 +1451,7 @@ namespace detail template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) + gc::AllocKind kind) { /* Bootstrap the ur-object, and make it the default prototype object. */ if (withProto == WithProto::Class && !proto) { @@ -1478,7 +1472,7 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, */ if (!isFunction && CanBeFinalizedInBackground(kind, clasp)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx, kind); if (!obj) @@ -1530,7 +1524,7 @@ NewFunction(JSContext *cx, JSObject *parent) template static JS_ALWAYS_INLINE JSObject * NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) + gc::AllocKind kind) { return detail::NewObject(cx, clasp, proto, parent, kind); } @@ -1539,14 +1533,14 @@ template static JS_ALWAYS_INLINE JSObject * NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return detail::NewObject(cx, clasp, proto, parent, kind); } template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) + gc::AllocKind kind) { if (clasp == &js_FunctionClass) return detail::NewObject(cx, clasp, proto, parent, kind); @@ -1557,7 +1551,7 @@ template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return NewObject(cx, clasp, proto, parent, kind); } @@ -1566,12 +1560,12 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) * avoid losing creation site information for objects made by scripted 'new'. */ static JS_ALWAYS_INLINE JSObject * -NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::FinalizeKind kind) +NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind) { JS_ASSERT(type == type->proto->newType); if (CanBeFinalizedInBackground(kind, &js_ObjectClass)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); if (!obj) @@ -1597,14 +1591,14 @@ out: extern JSObject * NewReshapedObject(JSContext *cx, js::types::TypeObject *type, JSObject *parent, - gc::FinalizeKind kind, const Shape *shape); + gc::AllocKind kind, const Shape *shape); /* * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of * the object, zero if the final size is unknown. This should only be used for * objects that do not require any fixed slots. */ -static inline gc::FinalizeKind +static inline gc::AllocKind GuessObjectGCKind(size_t numSlots, bool isArray) { if (numSlots) @@ -1616,7 +1610,7 @@ GuessObjectGCKind(size_t numSlots, bool isArray) * Get the GC kind to use for scripted 'new' on the given class. * FIXME bug 547327: estimate the size from the allocation site. */ -static inline gc::FinalizeKind +static inline gc::AllocKind NewObjectGCKind(JSContext *cx, js::Class *clasp) { if (clasp == &js_ArrayClass || clasp == &js_SlowArrayClass) @@ -1628,17 +1622,16 @@ NewObjectGCKind(JSContext *cx, js::Class *clasp) static JS_ALWAYS_INLINE JSObject* NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto, - /*gc::FinalizeKind*/ unsigned _kind) + gc::AllocKind kind) { JS_ASSERT(clasp->isNative()); - gc::FinalizeKind kind = gc::FinalizeKind(_kind); types::TypeObject *type = proto->getNewType(cx); if (!type) return NULL; if (CanBeFinalizedInBackground(kind, clasp)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); if (!obj) @@ -1656,8 +1649,7 @@ CopyInitializerObject(JSContext *cx, JSObject *baseobj, types::TypeObject *type) JS_ASSERT(baseobj->getClass() == &js_ObjectClass); JS_ASSERT(!baseobj->inDictionaryMode()); - gc::FinalizeKind kind = gc::FinalizeKind(baseobj->finalizeKind()); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, baseobj->getAllocKind()); if (!obj || !obj->ensureSlots(cx, baseobj->numSlots())) return NULL; diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 44f157bdd270..70b1cd0ebe61 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1421,7 +1421,7 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) * Make a blank object from the recipe fix provided to us. This must have * number of fixed slots as the proxy so that we can swap their contents. */ - gc::FinalizeKind kind = gc::FinalizeKind(proxy->arenaHeader()->getThingKind()); + gc::AllocKind kind = proxy->getAllocKind(); JSObject *newborn = NewNonFunction(cx, clasp, proto, parent, kind); if (!newborn) return false; diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index ea13c6943a02..6740e358e7c0 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -68,7 +68,7 @@ js::Shape::freeTable(JSContext *cx) inline js::EmptyShape * js::types::TypeObject::getEmptyShape(JSContext *cx, js::Class *aclasp, - /* gc::FinalizeKind */ unsigned kind) + gc::AllocKind kind) { JS_ASSERT(!singleton); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index edb1cf5ccfa7..6a2c6674d6cf 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -629,8 +629,6 @@ struct JSScript : public js::gc::Cell { inline bool hasAnalysis(); inline js::analyze::ScriptAnalysis *analysis(); - inline bool isAboutToBeFinalized(JSContext *cx); - private: bool makeTypes(JSContext *cx); bool makeAnalysis(JSContext *cx); diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 5c4ad7c3dcfe..dce486714ae9 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -1236,10 +1236,10 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist */ Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject) { - unsigned thingKind = templateObject->arenaHeader()->getThingKind(); + gc::AllocKind allocKind = templateObject->getAllocKind(); - JS_ASSERT(thingKind >= gc::FINALIZE_OBJECT0 && thingKind <= gc::FINALIZE_OBJECT_LAST); - size_t thingSize = gc::GCThingSizeMap[thingKind]; + JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); + size_t thingSize = gc::Arena::thingSize(allocKind); JS_ASSERT(cx->typeInferenceEnabled()); JS_ASSERT(!templateObject->hasSlotsArray()); @@ -1253,7 +1253,8 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist * Inline FreeSpan::allocate. Only the case where the current freelist * span is not empty is handled. */ - gc::FreeSpan *list = &cx->compartment->freeLists.lists[thingKind]; + gc::FreeSpan *list = const_cast + (cx->compartment->arenas.getFreeList(allocKind)); loadPtr(&list->first, result); Jump jump = branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 752680127f99..31d46143809b 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1350,7 +1350,7 @@ stubs::NewInitObject(VMFrame &f, JSObject *baseobj) TypeObject *type = (TypeObject *) f.scratch; if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); + gc::AllocKind kind = GuessObjectGCKind(0, false); JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) THROW(); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 687e4c21bb32..e74b8e9348a4 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5109,7 +5109,7 @@ env_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) ToString valstr(cx, *vp, JS_TRUE); if (valstr.threw()) return JS_FALSE; -#if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX +#if defined XP_WIN || defined HPUX || defined OSF1 { char *waste = JS_smprintf("%s=%s", idstr.getBytes(), valstr.getBytes()); if (!waste) { diff --git a/js/src/vm/String-inl.h b/js/src/vm/String-inl.h index 5feff3947567..827e18ec29d8 100644 --- a/js/src/vm/String-inl.h +++ b/js/src/vm/String-inl.h @@ -416,10 +416,10 @@ inline void JSAtom::finalize(JSRuntime *rt) { JS_ASSERT(isAtom()); - if (arenaHeader()->getThingKind() == js::gc::FINALIZE_STRING) + if (getAllocKind() == js::gc::FINALIZE_STRING) asFlat().finalize(rt); else - JS_ASSERT(arenaHeader()->getThingKind() == js::gc::FINALIZE_SHORT_STRING); + JS_ASSERT(getAllocKind() == js::gc::FINALIZE_SHORT_STRING); } inline void diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 0974c65e64b6..daad19597dd3 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=4 sw=4 et tw=79 ft=cpp: * * ***** BEGIN LICENSE BLOCK ***** @@ -49,7 +49,7 @@ using namespace js; bool JSString::isShort() const { - bool is_short = arenaHeader()->getThingKind() == gc::FINALIZE_SHORT_STRING; + bool is_short = (getAllocKind() == gc::FINALIZE_SHORT_STRING); JS_ASSERT_IF(is_short, isFlat()); return is_short; } @@ -69,7 +69,7 @@ JSString::isInline() const bool JSString::isExternal() const { - bool is_external = arenaHeader()->getThingKind() == gc::FINALIZE_EXTERNAL_STRING; + bool is_external = (getAllocKind() == gc::FINALIZE_EXTERNAL_STRING); JS_ASSERT_IF(is_external, isFixed()); return is_external; } diff --git a/js/src/xpconnect/shell/xpcshell.cpp b/js/src/xpconnect/shell/xpcshell.cpp index aa7034f828bc..ec26d2d6f12b 100644 --- a/js/src/xpconnect/shell/xpcshell.cpp +++ b/js/src/xpconnect/shell/xpcshell.cpp @@ -879,8 +879,7 @@ env_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) JSAutoByteString value(cx, valstr); if (!value) return JS_FALSE; -#if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX \ - || defined SCO +#if defined XP_WIN || defined HPUX || defined OSF1 || defined SCO { char *waste = JS_smprintf("%s=%s", name.ptr(), value.ptr()); if (!waste) { diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 8ba371b73e84..02cfac7dd8f0 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1324,13 +1324,14 @@ ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena, IterateData *data = static_cast(vdata); data->currCompartmentStats->gcHeapArenaHeaders += sizeof(js::gc::ArenaHeader); + size_t allocationSpace = arena->thingsSpan(thingSize); data->currCompartmentStats->gcHeapArenaPadding += - arena->thingsStartOffset(thingSize) - sizeof(js::gc::ArenaHeader); + js::gc::ArenaSize - allocationSpace - sizeof(js::gc::ArenaHeader); // We don't call the callback on unused things. So we compute the // unused space like this: arenaUnused = maxArenaUnused - arenaUsed. // We do this by setting arenaUnused to maxArenaUnused here, and then // subtracting thingSize for every used cell, in CellCallback(). - data->currCompartmentStats->gcHeapArenaUnused += arena->thingsSpan(thingSize); + data->currCompartmentStats->gcHeapArenaUnused += allocationSpace; } void diff --git a/layout/reftests/text/reftest.list b/layout/reftests/text/reftest.list index fe59e9be52f2..f436df92ee41 100644 --- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -49,7 +49,7 @@ skip-if(!(d2d||cocoaWidget)) random-if(d2d) != subpixel-glyphs-x-2a.html subpixe == subpixel-glyphs-y-1a.html subpixel-glyphs-y-1b.html == subpixel-lineheight-1a.html subpixel-lineheight-1b.html == swash-1.html swash-1-ref.html -fails-if(Android) HTTP(..) != synthetic-bold-metrics-01.html synthetic-bold-metrics-01-notref.html +HTTP(..) != synthetic-bold-metrics-01.html synthetic-bold-metrics-01-notref.html == variation-selector-unsupported-1.html variation-selector-unsupported-1-ref.html == white-space-1a.html white-space-1-ref.html == white-space-1b.html white-space-1-ref.html diff --git a/mobile/app/mobile.js b/mobile/app/mobile.js index 428fa582c3e7..39495aed9354 100644 --- a/mobile/app/mobile.js +++ b/mobile/app/mobile.js @@ -217,6 +217,7 @@ pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCAL /* preference for the locale picker */ pref("extensions.getLocales.get.url", ""); +pref("extensions.compatability.locales.buildid", "0"); /* blocklist preferences */ pref("extensions.blocklist.enabled", true); diff --git a/mobile/chrome/content/AppMenu.js b/mobile/chrome/content/AppMenu.js index 754281934d90..78133b7707f2 100644 --- a/mobile/chrome/content/AppMenu.js +++ b/mobile/chrome/content/AppMenu.js @@ -50,7 +50,7 @@ var AppMenu = { item.onclick = function() { child.click(); } let label = document.createElement("label"); - label.setAttribute("value", child.label); + label.textContent = child.label; item.appendChild(label); if (item.classList.contains("appmenu-pageaction")) @@ -58,14 +58,8 @@ var AppMenu = { else listbox.appendChild(item); } - - this.popup.top = menuButton.getBoundingClientRect().bottom; - - let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry); - this.popup.setAttribute(chromeReg.isLocaleRTL("global") ? "left" : "right", this.offset); - this.popup.hidden = false; - this.popup.anchorTo(menuButton); + this.popup.anchorTo(menuButton, "after_end"); BrowserUI.lockToolbar(); BrowserUI.pushPopup(this, [this.popup, menuButton]); diff --git a/mobile/chrome/content/BookmarkPopup.js b/mobile/chrome/content/BookmarkPopup.js index d49f67723ecc..c552dde4e27b 100644 --- a/mobile/chrome/content/BookmarkPopup.js +++ b/mobile/chrome/content/BookmarkPopup.js @@ -19,20 +19,18 @@ var BookmarkPopup = { show : function show() { // Set the box position. let button = document.getElementById("tool-star"); + let anchorPosition = ""; if (getComputedStyle(button).visibility == "visible") { let [tabsSidebar, controlsSidebar] = [Elements.tabs.getBoundingClientRect(), Elements.controls.getBoundingClientRect()]; this.box.setAttribute(tabsSidebar.left < controlsSidebar.left ? "right" : "left", controlsSidebar.width - this.box.offset); this.box.top = button.getBoundingClientRect().top - this.box.offset; } else { button = document.getElementById("tool-star2"); - this.box.top = button.getBoundingClientRect().bottom - this.box.offset; - - let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry); - this.box.setAttribute(chromeReg.isLocaleRTL("global") ? "left" : "right", this.box.offset); + anchorPosition = "after_start"; } this.box.hidden = false; - this.box.anchorTo(button); + this.box.anchorTo(button, anchorPosition); // include the star button here, so that click-to-dismiss works as expected BrowserUI.pushPopup(this, [this.box, button]); diff --git a/mobile/chrome/content/bindings.xml b/mobile/chrome/content/bindings.xml index b61002e2f5a7..2e043207b9c7 100644 --- a/mobile/chrome/content/bindings.xml +++ b/mobile/chrome/content/bindings.xml @@ -707,7 +707,6 @@ - diff --git a/mobile/chrome/content/bindings/arrowbox.xml b/mobile/chrome/content/bindings/arrowbox.xml index 17ddaca2704e..9d8962680663 100644 --- a/mobile/chrome/content/bindings/arrowbox.xml +++ b/mobile/chrome/content/bindings/arrowbox.xml @@ -149,6 +149,7 @@ null + = Math.round(anchorRect.right - offset)) ? 1 : 0; - let vertPos = (Math.round(popupRect.bottom) <= Math.round(anchorRect.top + offset)) ? -1 : - (Math.round(popupRect.top) >= Math.round(anchorRect.bottom - offset)) ? 1 : 0; + if (aPosition) { + let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry); + let isRtl = chromeReg.isLocaleRTL("global"); + let left = 0; + let top = 0; + + switch (aPosition) { + case "before_start": + left = isRtl ? anchorRect.right - popupRect.width : anchorRect.left; + top = anchorRect.top + offset - popupRect.height; + vertPos = -1; + break; + case "before_end": + left = isRtl ? anchorRect.left : anchorRect.right - popupRect.width; + top = anchorRect.top + offset - popupRect.height; + vertPos = -1; + break; + case "after_start": + left = isRtl ? anchorRect.right - popupRect.width : anchorRect.left; + top = anchorRect.bottom - offset; + vertPos = 1; + break; + case "after_end": + left = isRtl ? anchorRect.left : anchorRect.right - popupRect.width; + top = anchorRect.bottom - offset; + vertPos = 1; + break; + case "start_before": + left = isRtl ? anchorRect.right : anchorRect.left - popupRect.width - offset; + top = anchorRect.top; + horizPos = -1; + break; + case "start_after": + left = isRtl ? anchorRect.right : anchorRect.left - popupRect.width - offset; + top = anchorRect.bottom - popupRect.height; + horizPos = -1; + break; + case "end_before": + left = isRtl ? anchorRect.left - popupRect.width - offset : anchorRect.right; + top = anchorRect.top; + horizPos = 1; + break; + case "end_after": + left = isRtl ? anchorRect.left - popupRect.width - offset : anchorRect.right; + top = anchorRect.bottom - popupRect.height; + horizPos = 1; + break; + case "overlap": + left = isRtl ? anchorRect.right - popupRect.width + offset : anchorRect.left + offset ; + top = anchorRect.top + offset ; + break; + } + if (top == 0) top = 1; + if (left == 0) left = 1; + + if (left + popupRect.width > window.innerWidth) + left = window.innerWidth - popupRect.width; + else if (left < 0) + left = 1; + + popupRect.left = left; + this.setAttribute("left", left); + popupRect.top = top; + this.setAttribute("top", top); + } else { + horizPos = (Math.round(popupRect.right) <= Math.round(anchorRect.left + offset)) ? -1 : + (Math.round(popupRect.left) >= Math.round(anchorRect.right - offset)) ? 1 : 0; + vertPos = (Math.round(popupRect.bottom) <= Math.round(anchorRect.top + offset)) ? -1 : + (Math.round(popupRect.top) >= Math.round(anchorRect.bottom - offset)) ? 1 : 0; + } this._updateArrow(popupRect, anchorRect, horizPos, vertPos); ]]> diff --git a/mobile/chrome/content/common-ui.js b/mobile/chrome/content/common-ui.js index ee169f3a8381..eb49e0703b75 100644 --- a/mobile/chrome/content/common-ui.js +++ b/mobile/chrome/content/common-ui.js @@ -87,23 +87,12 @@ var BrowserSearch = { popup.hidden = false; popup.top = BrowserUI.toolbarH - popup.offset; let searchButton = document.getElementById("tool-search"); - if (Util.isTablet()) { - let width = list.getBoundingClientRect().width; - let searchButtonRect = searchButton.getBoundingClientRect(); - let left = searchButtonRect.left; - if (Util.localeDir > 0) { - if (left + width > window.innerWidth) - left = window.innerWidth - width; - } else { - left = searchButtonRect.right - width; - if (left < 0) - left = 0; - } - popup.left = left; - } else if (popup.hasAttribute("left")) { + let anchorPosition = ""; + if (Util.isTablet()) + anchorPosition = "after_start"; + else if (popup.hasAttribute("left")) popup.removeAttribute("left"); - } - popup.anchorTo(searchButton); + popup.anchorTo(searchButton, anchorPosition); document.getElementById("urlbar-icons").setAttribute("open", "true"); BrowserUI.pushPopup(this, [popup, this._button]); diff --git a/mobile/chrome/content/extensions.js b/mobile/chrome/content/extensions.js index de5abc51f1d7..2f3165a0c334 100644 --- a/mobile/chrome/content/extensions.js +++ b/mobile/chrome/content/extensions.js @@ -430,6 +430,10 @@ var ExtensionsView = { } else if (aItem.getAttribute("type") == "theme") { aItem.addon.userDisabled = true; aItem.setAttribute("isDisabled", true); + } else if (aItem.getAttribute("type") == "locale") { + Services.prefs.clearUserPref("general.useragent.locale"); + aItem.addon.userDisabled = true; + aItem.setAttribute("isDisabled", true); } else { aItem.addon.userDisabled = true; opType = this._getOpTypeForOperations(aItem.addon.pendingOperations); diff --git a/mobile/chrome/content/localePicker.js b/mobile/chrome/content/localePicker.js index 7f83ea322050..58e36b08d83f 100644 --- a/mobile/chrome/content/localePicker.js +++ b/mobile/chrome/content/localePicker.js @@ -8,15 +8,16 @@ Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource:///modules/LocaleRepository.jsm"); let stringPrefs = [ - { selector: "#continue-in-button", pref: "continueIn", data: ["CURRENT_LANGUAGE"] }, + { selector: "#continue-in-button", pref: "continueIn", data: ["CURRENT_LOCALE"] }, { selector: "#change-language", pref: "choose", data: null }, { selector: "#picker-title", pref: "chooseLanguage", data: null }, { selector: "#continue-button", pref: "continue", data: null }, { selector: "#cancel-button", pref: "cancel", data: null }, - { selector: "#intalling-message", pref: "installing", data: ["CURRENT_LANGUAGE"] }, + { selector: "#intalling-message", pref: "installing", data: ["CURRENT_LOCALE"] }, { selector: "#cancel-install-button", pref: "cancel", data: null }, { selector: "#installing-error", pref: "installerror", data: null }, - { selector: "#install-continue", pref: "continue", data: null } + { selector: "#install-continue", pref: "continue", data: null }, + { selector: "#loading-label", pref: "loading", data: null } ]; let LocaleUI = { @@ -52,6 +53,26 @@ let LocaleUI = { return this._deck = document.getElementById("language-deck"); }, + _availableLocales: null, + get availableLocales() { + if (!this._availableLocales) { + let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry); + chrome.QueryInterface(Ci.nsIToolkitChromeRegistry); + let strings = Services.strings.createBundle("chrome://browser/content/languages.properties"); + let availableLocales = chrome.getLocalesForPackage("browser"); + + this._availableLocales = []; + while (availableLocales.hasMore()) { + let locale = availableLocales.getNext(); + let label = locale; + try { label = strings.GetStringFromName(locale); } + catch (e) { } + this._availableLocales.push({ addon: { id: locale, name: label, targetLocale: locale }}); + } + } + return this._availableLocales; + }, + _currentInstall: null, // used to cancel an install get selectedPanel() { @@ -75,7 +96,7 @@ let LocaleUI = { description.appendChild(document.createTextNode(aText)); description.setAttribute('flex', 1); item.appendChild(description); - item.setAttribute("locale", getTargetLanguage(aLocale.addon)); + item.setAttribute("locale", getTargetLocale(aLocale.addon)); if (aLocale) { item.locale = aLocale.addon; @@ -94,12 +115,12 @@ let LocaleUI = { let bestMatch = NO_MATCH; for each (let locale in aLocales) { - let targetLang = getTargetLanguage(locale.addon); - if (document.querySelector('[locale="' + targetLang + '"]')) + let targetLocale = getTargetLocale(locale.addon); + if (document.querySelector('[locale="' + targetLocale + '"]')) continue; - let item = this._createItem(targetLang, locale.addon.name, locale); - let match = localesMatch(targetLang, this.language); + let item = this._createItem(targetLocale, locale.addon.name, locale); + let match = localesMatch(targetLocale, this.locale); if (match > bestMatch) { bestMatch = match; selectedItem = item; @@ -126,27 +147,27 @@ let LocaleUI = { closePicker: function() { if (this._currentInstall) { Services.prefs.setBoolPref("intl.locale.matchOS", false); - Services.prefs.setCharPref("general.useragent.locale", getTargetLanguage(this._currentInstall)); + Services.prefs.setCharPref("general.useragent.locale", getTargetLocale(this._currentInstall)); } this.selectedPanel = this._mainPage; }, - _language: "", + _locale: "", - set language(aVal) { - if (aVal == this._language) + set locale(aVal) { + if (aVal == this._locale) return; Services.prefs.setBoolPref("intl.locale.matchOS", false); Services.prefs.setCharPref("general.useragent.locale", aVal); - this._language = aVal; + this._locale = aVal; this.strings = null; this.updateStrings(); }, - get language() { - return this._language; + get locale() { + return this._locale; }, set installStatus(aVal) { @@ -158,13 +179,13 @@ let LocaleUI = { this.selectedPanel = this._pickerPage; }, - selectLanguage: function(aEvent) { + selectLocale: function(aEvent) { let locale = this.list.selectedItem.locale; if (locale.install) { LocaleUI.strings = new FakeStringBundle(locale); this.updateStrings(); } else { - this.language = getTargetLanguage(locale); + this.locale = getTargetLocale(locale); if (this._currentInstall) this._currentInstall = null; } @@ -187,12 +208,14 @@ let LocaleUI = { if (this._currentInstall) this._currentInstall = null; // restore the last known "good" locale - this.language = this.defaultLanguage; + this.locale = this.defaultLocale; this.updateStrings(); this.closePicker(); }, closeWindow : function() { + var buildID = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).platformBuildID; + Services.prefs.setCharPref("extensions.compatability.locales.buildid", buildID); // Trying to close this window and open a new one results in a corrupt UI. if (LocaleUI._currentInstall) { // a new locale was installed, restart the browser @@ -202,7 +225,6 @@ let LocaleUI = { if (cancelQuit.data == false) { let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup); appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eForceQuit); - Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", false); } } else { // selected locale is already installed, just open the window @@ -223,7 +245,7 @@ let LocaleUI = { catch(ex) { } LocaleUI._currentInstall = null; - this.language = this.defaultLanguage; + this.locale = this.defaultLocale; } }, @@ -241,10 +263,10 @@ let LocaleUI = { } } -// Gets the target language for a locale addon +// Gets the target locale for an addon // For now this returns the targetLocale, although if and addon doesn't // specify a targetLocale we could attempt to guess the locale from the addon's name -function getTargetLanguage(aAddon) { +function getTargetLocale(aAddon) { return aAddon.targetLocale; } @@ -256,7 +278,7 @@ function getString(aStringName, aDataSet, aAddon) { if (aDataSet) { let databundle = aDataSet.map(function(aData) { switch (aData) { - case "CURRENT_LANGUAGE" : + case "CURRENT_LOCALE" : if (aAddon) return aAddon.name; try { return LocaleUI.strings.GetStringFromName("name"); } @@ -289,8 +311,8 @@ let installListener = { }, onInstallStarted: function(install) { }, onInstallEnded: function(install, addon) { - LocaleUI.updateStrings(); - LocaleUI.closePicker(); + LocaleUI.locale = getTargetLocale(LocaleUI._currentInstall); + LocaleUI.closeWindow(); }, onInstallCancelled: function(install) { LocaleUI.cancelInstall(); @@ -322,25 +344,68 @@ function start() { // if we have gotten this far, we can assume that we don't have anything matching the system // locale and we should show the locale picker + LocaleUI._mainPage.setAttribute("mode", "loading"); let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry); chrome.QueryInterface(Ci.nsIToolkitChromeRegistry); - let availableLocales = chrome.getLocalesForPackage("browser"); - let strings = Services.strings.createBundle("chrome://browser/content/languages.properties"); - LocaleUI.availableLocales = []; - while (availableLocales.hasMore()) { - let locale = availableLocales.getNext(); - - let label = locale; - try { label = strings.GetStringFromName(locale); } - catch (e) { } - LocaleUI.availableLocales.push({addon: { id: locale, name: label, targetLocale: locale }}); - } - - LocaleUI._language = chrome.getSelectedLocale("browser"); + LocaleUI._locale = chrome.getSelectedLocale("browser"); LocaleUI.updateStrings(); + // if we haven't gotten the list of available locales from AMO within 5 seconds, we give up + // users can try downloading the list again by selecting "Choose another locale" + let timeout = setTimeout(function() { + LocaleUI._mainPage.removeAttribute("mode"); + timeout = null; + }, 5000); + + // Look on AMO for something that matches the system locale + LocaleRepository.getLocales(function lp_initalDownload(aLocales) { + if (!LocaleUI._mainPage.hasAttribute("mode")) return; + + clearTimeout(timeout); + LocaleUI._mainPage.removeAttribute("mode"); + + let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci.nsILocaleService); + let currentLocale = localeService.getSystemLocale().getCategory("NSILOCALE_CTYPE"); + if (Services.prefs.prefHasUserValue("general.useragent.locale")) { + currentLocale = Services.prefs.getCharPref("general.useragent.locale"); + } + + let match = NO_MATCH; + let matchingLocale = null; + + for each (let locale in aLocales) { + let targetLocale = getTargetLocale(locale.addon); + let newMatch = localesMatch(targetLocale, currentLocale); + if (newMatch > match) { + matchingLocale = locale; + match = newMatch; + } + } + + if (matchingLocale) { + // if we found something, try to install it automatically + AddonManager.getAddonByID(matchingLocale.addon.id, function (aAddon) { + // if this locale is already installed, but is user disabled, + // bail out of here. + if (aAddon && aAddon.userDisabled) { + Services.prefs.clearUserPref("general.useragent.locale"); + LocaleUI.closeWindow(); + return; + } + // if we found something, try to install it automatically + LocaleUI.strings = new FakeStringBundle(matchingLocale.addon); + LocaleUI.updateStrings(); + LocaleUI._currentInstall = matchingLocale.addon; + + LocaleUI.selectedPanel = LocaleUI._installerPage; + matchingLocale.addon.install.addListener(installListener); + matchingLocale.addon.install.install(); + }); + } + }); + // update the page strings and show the correct page - LocaleUI.defaultLanguage = LocaleUI._language; + LocaleUI.defaultLocale = LocaleUI._locale; window.addEventListener("resize", resizeHandler, false); } diff --git a/mobile/chrome/content/localePicker.xul b/mobile/chrome/content/localePicker.xul index e0da34a88183..0a64d0b7e443 100644 --- a/mobile/chrome/content/localePicker.xul +++ b/mobile/chrome/content/localePicker.xul @@ -15,12 +15,13 @@