diff --git a/accessible/public/nsIAccessible.idl b/accessible/public/nsIAccessible.idl index 8af90ba6266a..bef69df27772 100644 --- a/accessible/public/nsIAccessible.idl +++ b/accessible/public/nsIAccessible.idl @@ -56,7 +56,7 @@ interface nsIAccessibleRelation; * Mozilla creates the implementations of nsIAccessible on demand. * See http://www.mozilla.org/projects/ui/accessibility for more information. */ -[scriptable, uuid(c7ac764a-b4c5-4479-9fb7-06e3c9f3db34)] +[scriptable, uuid(3126544c-826c-4694-a2ed-67bfe56a1f37)] interface nsIAccessible : nsISupports { /** @@ -221,26 +221,6 @@ interface nsIAccessible : nsISupports */ nsIAccessible getChildAt(in long aChildIndex); - /** - * Accessible node geometrically to the right of this one - */ - nsIAccessible getAccessibleToRight(); - - /** - * Accessible node geometrically to the left of this one - */ - nsIAccessible getAccessibleToLeft(); - - /** - * Accessible node geometrically above this one - */ - nsIAccessible getAccessibleAbove(); - - /** - * Accessible node geometrically below this one - */ - nsIAccessible getAccessibleBelow(); - /** * Return accessible relation by the given relation type (see. * constants defined in nsIAccessibleRelation). diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index 414c70d11cc1..8e0ecfad01bc 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -1974,30 +1974,6 @@ NS_IMETHODIMP nsAccessible::GetHelp(nsAString& _retval) return NS_ERROR_NOT_IMPLEMENTED; } -/* nsIAccessible getAccessibleToRight(); */ -NS_IMETHODIMP nsAccessible::GetAccessibleToRight(nsIAccessible **_retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -/* nsIAccessible getAccessibleToLeft(); */ -NS_IMETHODIMP nsAccessible::GetAccessibleToLeft(nsIAccessible **_retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -/* nsIAccessible getAccessibleAbove(); */ -NS_IMETHODIMP nsAccessible::GetAccessibleAbove(nsIAccessible **_retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -/* nsIAccessible getAccessibleBelow(); */ -NS_IMETHODIMP nsAccessible::GetAccessibleBelow(nsIAccessible **_retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - nsIContent* nsAccessible::GetAtomicRegion() const { diff --git a/accessible/src/msaa/nsAccessibleWrap.cpp b/accessible/src/msaa/nsAccessibleWrap.cpp index b6eac300fc1c..c506b3839e0b 100644 --- a/accessible/src/msaa/nsAccessibleWrap.cpp +++ b/accessible/src/msaa/nsAccessibleWrap.cpp @@ -810,42 +810,35 @@ __try { if (!pvarEndUpAt) return E_INVALIDARG; - nsAccessible *xpAccessibleStart = GetXPAccessibleFor(varStart); - if (!xpAccessibleStart || IsDefunct()) + nsAccessible* accessible = GetXPAccessibleFor(varStart); + if (!accessible || accessible->IsDefunct()) return E_FAIL; VariantInit(pvarEndUpAt); - nsCOMPtr xpAccessibleResult; + nsAccessible* navAccessible = nsnull; PRUint32 xpRelation = 0; switch(navDir) { - case NAVDIR_DOWN: - xpAccessibleStart->GetAccessibleBelow(getter_AddRefs(xpAccessibleResult)); - break; case NAVDIR_FIRSTCHILD: - if (!nsAccUtils::MustPrune(xpAccessibleStart)) - xpAccessibleStart->GetFirstChild(getter_AddRefs(xpAccessibleResult)); + if (!nsAccUtils::MustPrune(accessible)) + navAccessible = accessible->FirstChild(); break; case NAVDIR_LASTCHILD: - if (!nsAccUtils::MustPrune(xpAccessibleStart)) - xpAccessibleStart->GetLastChild(getter_AddRefs(xpAccessibleResult)); - break; - case NAVDIR_LEFT: - xpAccessibleStart->GetAccessibleToLeft(getter_AddRefs(xpAccessibleResult)); + if (!nsAccUtils::MustPrune(accessible)) + navAccessible = accessible->LastChild(); break; case NAVDIR_NEXT: - xpAccessibleStart->GetNextSibling(getter_AddRefs(xpAccessibleResult)); + navAccessible = accessible->NextSibling(); break; case NAVDIR_PREVIOUS: - xpAccessibleStart->GetPreviousSibling(getter_AddRefs(xpAccessibleResult)); + navAccessible = accessible->PrevSibling(); break; + case NAVDIR_DOWN: + case NAVDIR_LEFT: case NAVDIR_RIGHT: - xpAccessibleStart->GetAccessibleToRight(getter_AddRefs(xpAccessibleResult)); - break; case NAVDIR_UP: - xpAccessibleStart->GetAccessibleAbove(getter_AddRefs(xpAccessibleResult)); - break; + return E_NOTIMPL; // MSAA relationship extensions to accNavigate case NAVRELATION_CONTROLLED_BY: @@ -896,17 +889,20 @@ __try { case NAVRELATION_DESCRIPTION_FOR: xpRelation = nsIAccessibleRelation::RELATION_DESCRIPTION_FOR; break; + + default: + return E_INVALIDARG; } pvarEndUpAt->vt = VT_EMPTY; if (xpRelation) { Relation rel = RelationByType(xpRelation); - xpAccessibleResult = rel.Next(); + navAccessible = rel.Next(); } - if (xpAccessibleResult) { - pvarEndUpAt->pdispVal = NativeAccessible(xpAccessibleResult); + if (navAccessible) { + pvarEndUpAt->pdispVal = NativeAccessible(navAccessible); pvarEndUpAt->vt = VT_DISPATCH; return S_OK; } diff --git a/aclocal.m4 b/aclocal.m4 index efaca9b457d3..2e6734a9440a 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -16,6 +16,7 @@ builtin(include, build/autoconf/mozheader.m4)dnl builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl +builtin(include, build/autoconf/frameptr.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 368345232048..a90760bcfde5 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -4039,11 +4039,14 @@ this.style.MozUserFocus = ''; - + diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js index 6a67311bf011..5c15e6e0ccee 100644 --- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -486,6 +486,11 @@ SessionStoreService.prototype = { this._forEachBrowserWindow(function(aWindow) { this._collectWindowData(aWindow); }); + // we must cache this because _getMostRecentBrowserWindow will always + // return null by the time quit-application occurs + var activeWindow = this._getMostRecentBrowserWindow(); + if (activeWindow) + this.activeWindowSSiCache = activeWindow.__SSi || ""; this._dirtyWindows = []; break; case "quit-application-granted": @@ -1512,6 +1517,13 @@ SessionStoreService.prototype = { let lastWindow = this._getMostRecentBrowserWindow(); let canUseLastWindow = lastWindow && !lastWindow.__SS_lastSessionWindowID; + let lastSessionFocusedWindow = null; + this.windowToFocus = lastWindow; + + // move the last focused window to the start of the array so that we + // minimize window movement (see bug 669272) + lastSessionState.windows.unshift( + lastSessionState.windows.splice(lastSessionState.selectedWindow - 1, 1)[0]); // Restore into windows or open new ones as needed. for (let i = 0; i < lastSessionState.windows.length; i++) { @@ -1549,9 +1561,18 @@ SessionStoreService.prototype = { // weirdness but we will still merge other extData. // Bug 588217 should make this go away by merging the group data. this.restoreWindow(windowToUse, { windows: [winState] }, canOverwriteTabs, true); + if (i == 0) + lastSessionFocusedWindow = windowToUse; + + // if we overwrote the tabs for our last focused window, we should + // give focus to the window that had it in the previous session + if (canOverwriteTabs && windowToUse == lastWindow) + this.windowToFocus = lastSessionFocusedWindow; } else { - this._openWindowWithState({ windows: [winState] }); + let win = this._openWindowWithState({ windows: [winState] }); + if (i == 0) + lastSessionFocusedWindow = win; } } @@ -2544,8 +2565,12 @@ SessionStoreService.prototype = { this._closedWindows = root._closedWindows; var winData; - if (!aState.selectedWindow) { - aState.selectedWindow = 0; + if (!root.selectedWindow) { + root.selectedWindow = 0; + } else { + // put the selected window at the beginning of the array to ensure that + // it gets restored first + root.windows.unshift(root.windows.splice(root.selectedWindow - 1, 1)[0]); } // open new windows for all further window entries of a multi-window session // (unless they don't contain any tab data) @@ -2553,9 +2578,6 @@ SessionStoreService.prototype = { winData = root.windows[w]; if (winData && winData.tabs && winData.tabs[0]) { var window = this._openWindowWithState({ windows: [winData] }); - if (w == aState.selectedWindow - 1) { - this.windowToFocus = window; - } } } winData = root.windows[0]; diff --git a/browser/components/wintaskbar/WindowsJumpLists.jsm b/browser/components/wintaskbar/WindowsJumpLists.jsm index 4d33f83f3888..91e962221d5d 100644 --- a/browser/components/wintaskbar/WindowsJumpLists.jsm +++ b/browser/components/wintaskbar/WindowsJumpLists.jsm @@ -22,6 +22,7 @@ * Contributor(s): * Jim Mathies (Original author) * Marco Bonardo + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -331,7 +332,7 @@ var WinTaskbarJumpList = if ((this._shuttingDown && !task.close) || (!this._shuttingDown && !task.open)) return; var item = this._getHandlerAppItem(task.title, task.description, - task.args, task.iconIndex); + task.args, task.iconIndex, null); items.appendElement(item, false); }, this); @@ -374,7 +375,9 @@ var WinTaskbarJumpList = } let title = aResult.title || aResult.uri; - let shortcut = this._getHandlerAppItem(title, title, aResult.uri, 1); + let faviconPageUri = Services.io.newURI(aResult.uri, null, null); + let shortcut = this._getHandlerAppItem(title, title, aResult.uri, 1, + faviconPageUri); items.appendElement(shortcut, false); this._frequentHashList.push(aResult.uri); }, @@ -417,7 +420,9 @@ var WinTaskbarJumpList = } let title = aResult.title || aResult.uri; - let shortcut = this._getHandlerAppItem(title, title, aResult.uri, 1); + let faviconPageUri = Services.io.newURI(aResult.uri, null, null); + let shortcut = this._getHandlerAppItem(title, title, aResult.uri, 1, + faviconPageUri); items.appendElement(shortcut, false); count++; }, @@ -433,7 +438,9 @@ var WinTaskbarJumpList = * Jump list item creation helpers */ - _getHandlerAppItem: function WTBJL__getHandlerAppItem(name, description, args, icon) { + _getHandlerAppItem: function WTBJL__getHandlerAppItem(name, description, + args, iconIndex, + faviconPageUri) { var file = Services.dirsvc.get("XCurProcD", Ci.nsILocalFile); // XXX where can we grab this from in the build? Do we need to? @@ -451,7 +458,8 @@ var WinTaskbarJumpList = var item = Cc["@mozilla.org/windows-jumplistshortcut;1"]. createInstance(Ci.nsIJumpListShortcut); item.app = handlerApp; - item.iconIndex = icon; + item.iconIndex = iconIndex; + item.faviconPageUri = faviconPageUri; return item; }, diff --git a/browser/devtools/styleinspector/StyleInspector.jsm b/browser/devtools/styleinspector/StyleInspector.jsm index 19798ab15afb..9ca4b6c4fefa 100644 --- a/browser/devtools/styleinspector/StyleInspector.jsm +++ b/browser/devtools/styleinspector/StyleInspector.jsm @@ -55,7 +55,13 @@ var StyleInspector = { return Services.prefs.getBoolPref("devtools.styleinspector.enabled"); }, - createPanel: function SI_createPanel() + /** + * Factory method to create the actual style panel + * @param {Boolean} aPreserveOnHide Prevents destroy from being called + * onpopuphide. USE WITH CAUTION: When this value is set to true then you are + * responsible to manually call destroy from outside the style inspector. + */ + createPanel: function SI_createPanel(aPreserveOnHide) { let win = Services.wm.getMostRecentWindow("navigator:browser"); let popupSet = win.document.getElementById("mainPopupSet"); @@ -98,7 +104,10 @@ var StyleInspector = { hbox.appendChild(resizer); popupSet.appendChild(panel); - panel.addEventListener("popupshown", function SI_popup_shown() { + /** + * Initialize the popup when it is first shown + */ + function SI_popupShown() { if (!this.cssHtmlTree) { this.cssLogic = new CssLogic(); this.cssHtmlTree = new CssHtmlTree(iframe, this.cssLogic, this); @@ -107,12 +116,23 @@ var StyleInspector = { this.cssLogic.highlight(this.selectedNode); this.cssHtmlTree.highlight(this.selectedNode); Services.obs.notifyObservers(null, "StyleInspector-opened", null); - }, false); + } + + /** + * Hide the popup and conditionally destroy it + */ + function SI_popupHidden() { + if (panel.preserveOnHide) { + Services.obs.notifyObservers(null, "StyleInspector-closed", null); + } else { + panel.destroy(); + } + } + + panel.addEventListener("popupshown", SI_popupShown); + panel.addEventListener("popuphidden", SI_popupHidden); + panel.preserveOnHide = !!aPreserveOnHide; - panel.addEventListener("popuphidden", function SI_popup_hidden() { - Services.obs.notifyObservers(null, "StyleInspector-closed", null); - }, false); - /** * Check if the style inspector is open */ @@ -138,6 +158,19 @@ var StyleInspector = { } }; + /** + * Destroy the style panel, remove listeners etc. + */ + panel.destroy = function SI_destroy() + { + this.cssLogic = null; + this.cssHtmlTree = null; + this.removeEventListener("popupshown", SI_popupShown); + this.removeEventListener("popuphidden", SI_popupHidden); + this.parentNode.removeChild(this); + Services.obs.notifyObservers(null, "StyleInspector-closed", null); + }; + /** * Is the Style Inspector initialized? * @returns {Boolean} true or false diff --git a/browser/devtools/webconsole/HUDService.jsm b/browser/devtools/webconsole/HUDService.jsm index d0b852b28492..e71468955e87 100644 --- a/browser/devtools/webconsole/HUDService.jsm +++ b/browser/devtools/webconsole/HUDService.jsm @@ -1786,7 +1786,6 @@ HUD_SERVICE.prototype = panels = popupset.querySelectorAll("panel[hudToolId=" + aHUDId + "]"); for (let i = 0; i < panels.length; i++) { panels[i].hidePopup(); - popupset.removeChild(panels[i]); } let id = ConsoleUtils.supString(aHUDId); diff --git a/browser/locales/en-US/searchplugins/list.txt b/browser/locales/en-US/searchplugins/list.txt index 7c7d9ee7a1ef..2a1141a984f7 100644 --- a/browser/locales/en-US/searchplugins/list.txt +++ b/browser/locales/en-US/searchplugins/list.txt @@ -2,5 +2,6 @@ amazondotcom bing eBay google +twitter wikipedia yahoo diff --git a/browser/locales/en-US/searchplugins/twitter.xml b/browser/locales/en-US/searchplugins/twitter.xml new file mode 100644 index 000000000000..9f1850a59c7b --- /dev/null +++ b/browser/locales/en-US/searchplugins/twitter.xml @@ -0,0 +1,11 @@ + +Twitter +Realtime Twitter Search +UTF-8 +data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A/v7+D/7+/j/+/v5g/v7+YP7+/mD+/v5I/v7+KP///wD///8A////AP///wD///8A////AP///wD+/v4H/v7+UPbv4pHgx47B1K9Y3tWwWN7Ur1je3sKCx+rbuKj+/v5n/v7+GP///wD///8A////AP///wD+/v4Y+fbweM2ycMe2iB7/vI0f/8STIf/KlyL/zJki/8yZIv/LmCL/0ahK5/Hp1JH+/v4Y////AP///wD///8A7OTTaquHN+CujkXPs5ZTv6N6G/+2iB7/xpUh/8yZIv/MmSL/zJki/8yZIv/Kmy738OjUi////wD///8A////AMKtfY7w6+Ef////AP///wD///8A3sqbp8iWIf/MmSL/zJki/8yZIv/MmSL/y5gi/8mePO7+/v4w////AP///wD///8A////AP///wD+/v4H/v7+V9CtWN3KmCL/zJki/8yZIv/MmSL/zJki/8yZIv/JlyH/5tSqp/7+/mD+/v4/////AP///wD///8A+PXvJtGyZdXNnS/3y5gi/8qYIv/LmCL/zJki/8yZIv/MmSL/y5gi/82iPO7LqVfe0byMmf///wD///8A/v7+D/Do1JHKmy73ypci/8KSIP+/jyD/xpQh/8uYIv/MmSL/zJki/8qYIv+/jyD/rIEd/9nKqH7///8A////APPu4TzAlSz3wZEg/7mLH/+sgR3/uZdGz7mLH//JlyH/zJki/8yZIv/GlSH/to0r9eXbxD/Vx6dg////AP7+/h/p38WhtIsq9al/HP+kfyjuybaKgf///wCzjzjlwJAg/8qYIv/JlyH/u4wf/8CkYrn///8A////AP///wDj2sRMnHUa/7meYa7Vx6dg////AP///wD///8A2MmnYK6DHf++jiD/vo4g/62CHf/k2sQ/////AP///wD///8A8OvhH/f07w////8A////AP///wD///8A////AP///wC/p3Cfpnwc/66GKvPg1LZ8////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANXHp2DJtoqByLWKgf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AADgPwAAwA8AAIAHAAB4BwAA+AMAAPAAAADgAQAA4AMAAMEDAADPhwAA/48AAP/nAAD//wAA//8AAA== +https://twitter.com/search/ + + + + + 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/build/autoconf/frameptr.m4 b/build/autoconf/frameptr.m4 new file mode 100644 index 000000000000..77a6d71aedc8 --- /dev/null +++ b/build/autoconf/frameptr.m4 @@ -0,0 +1,25 @@ +dnl Set MOZ_FRAMEPTR_FLAGS to the flags that should be used for enabling or +dnl disabling frame pointers in this architecture based on the configure +dnl options + +AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [ + if test "$GNU_CC"; then + MOZ_ENABLE_FRAME_PTR="-fno-omit-frame-pointer" + MOZ_DISABLE_FRAME_PTR="-fomit-frame-pointer" + else + case "$target" in + *-mingw*) + MOZ_ENABLE_FRAME_PTR="-Oy-" + MOZ_DISABLE_FRAME_PTR="-Oy" + ;; + esac + fi + + # if we are debugging or profiling, we want a frame pointer. + if test -z "$MOZ_OPTIMIZE" -o \ + -n "$MOZ_PROFILING" -o -n "$MOZ_DEBUG"; then + MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR" + else + MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR" + fi +]) diff --git a/build/autoconf/mozconfig-find b/build/autoconf/mozconfig-find index fe2ec9ac9e51..1895292f7783 100755 --- a/build/autoconf/mozconfig-find +++ b/build/autoconf/mozconfig-find @@ -40,8 +40,10 @@ # mozconfigfind - Loads options from .mozconfig onto configure's # command-line. The .mozconfig file is searched for in the # order: -# if $MOZCONFIG is set, use that. -# Otherwise, use $TOPSRCDIR/.mozconfig +# If $MOZCONFIG is set, use that. +# If one of $TOPSRCDIR/.mozconfig or $TOPSRCDIR/mozconfig exists, use it. +# If both exist, or if various legacy locations contain a mozconfig, error. +# Otherwise, use the default build options. # topsrcdir=$1 @@ -76,8 +78,14 @@ if [ -n "$MOZ_MYCONFIG" ]; then exit 1 fi +if [ -z "$MOZCONFIG" ] && [ -f "$topsrcdir/.mozconfig" ] && [ -f "$topsrcdir/mozconfig" ]; then + echo "Both \$topsrcdir/.mozconfig and \$topsrcdir/mozconfig are supported, but you must choose only one. Please remove the other." + exit 1 +fi + for _config in "$MOZCONFIG" \ - "$topsrcdir/.mozconfig" + "$topsrcdir/.mozconfig" \ + "$topsrcdir/mozconfig" do if test -f "$_config"; then echo `abspath $_config` @@ -88,8 +96,7 @@ done # We used to support a number of other implicit .mozconfig locations. We now # detect if we were about to use any of these locations and issue an error if we # find any. -for _config in "$topsrcdir/mozconfig" \ - "$topsrcdir/mozconfig.sh" \ +for _config in "$topsrcdir/mozconfig.sh" \ "$topsrcdir/myconfig.sh" \ "$HOME/.mozconfig" \ "$HOME/.mozconfig.sh" \ diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 4c0e084801e6..461545a8b206 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -2423,7 +2423,7 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj // avoid wasting time checking properties of their classes etc in // the loop. - if (jsClass == &js_FunctionClass) { + if (jsClass == &js::FunctionClass) { aObj = aObj->getParent(); if (!aObj) @@ -2431,7 +2431,7 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj jsClass = aObj->getClass(); - if (jsClass == &js_CallClass) { + if (jsClass == &js::CallClass) { aObj = aObj->getParent(); if (!aObj) diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in index e86645621c42..dd172026f76e 100644 --- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -302,6 +302,7 @@ ACDEFINES = @MOZ_DEFINES@ WARNINGS_AS_ERRORS = @WARNINGS_AS_ERRORS@ MOZ_OPTIMIZE = @MOZ_OPTIMIZE@ +MOZ_FRAMEPTR_FLAGS = @MOZ_FRAMEPTR_FLAGS@ MOZ_OPTIMIZE_FLAGS = @MOZ_OPTIMIZE_FLAGS@ MOZ_PGO_OPTIMIZE_FLAGS = @MOZ_PGO_OPTIMIZE_FLAGS@ MOZ_OPTIMIZE_LDFLAGS = @MOZ_OPTIMIZE_LDFLAGS@ diff --git a/config/config.mk b/config/config.mk index 162cdaec2a7d..8d714411492e 100644 --- a/config/config.mk +++ b/config/config.mk @@ -456,6 +456,9 @@ endif # MOZ_OPTIMIZE == 1 endif # MOZ_OPTIMIZE endif # CROSS_COMPILE +CFLAGS += $(MOZ_FRAMEPTR_FLAGS) +CXXFLAGS += $(MOZ_FRAMEPTR_FLAGS) + # Check for FAIL_ON_WARNINGS & FAIL_ON_WARNINGS_DEBUG (Shorthand for Makefiles # to request that we use the 'warnings as errors' compile flags) diff --git a/config/optimizejars.py b/config/optimizejars.py index cbd0e7a0a08a..84ab50d4d868 100644 --- a/config/optimizejars.py +++ b/config/optimizejars.py @@ -303,6 +303,7 @@ def optimizejar(jar, outjar, inlog = None): print("WARNING: Found %d duplicate files taking %d bytes"%(dups_found, dupe_bytes)) dirend.cdir_size = len(cdir_data) + dirend.disk_entries = dirend.cdir_entries dirend_data = dirend.pack() assert_true(size_of(cdir_end) == len(dirend_data), "Failed to serialize directory end correctly. Serialized size;%d, expected:%d"%(len(dirend_data), size_of(cdir_end))); diff --git a/configure.in b/configure.in index ae0d5124a325..9cc51a4b3be6 100644 --- a/configure.in +++ b/configure.in @@ -1990,12 +1990,7 @@ case "$target" in *-darwin*) MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_OPTIMIZE_FLAGS="-O3 -fomit-frame-pointer" - else - MOZ_OPTIMIZE_FLAGS="-O3 -fno-omit-frame-pointer" - fi + MOZ_OPTIMIZE_FLAGS="-O3" _PEDANTIC= CFLAGS="$CFLAGS -fno-common" CXXFLAGS="$CXXFLAGS -fno-common" @@ -2107,12 +2102,7 @@ ia64*-hpux*) TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"' MOZ_GFX_OPTIMIZE_MOBILE=1 - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions -fomit-frame-pointer" - else - MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions -fno-omit-frame-pointer" - fi + MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions" ;; *-*linux*) @@ -2130,14 +2120,8 @@ ia64*-hpux*) # -Os is broken on gcc 4.1.x 4.2.x, 4.5.x we need to tweak it to get good results. MOZ_OPTIMIZE_SIZE_TWEAK="-finline-limit=50" esac - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_FRAMEPTR_FLAGS="-fomit-frame-pointer" - else - MOZ_FRAMEPTR_FLAGS="-fno-omit-frame-pointer" - fi - MOZ_PGO_OPTIMIZE_FLAGS="-O3 $MOZ_FRAMEPTR_FLAGS" - MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks $MOZ_OPTIMIZE_SIZE_TWEAK $MOZ_FRAMEPTR_FLAGS" + MOZ_PGO_OPTIMIZE_FLAGS="-O3" + MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks $MOZ_OPTIMIZE_SIZE_TWEAK" MOZ_DEBUG_FLAGS="-g" fi @@ -2234,12 +2218,7 @@ ia64*-hpux*) MOZ_DEBUG_FLAGS='-Zi' MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' WARNINGS_AS_ERRORS='-WX' - # If we're building with --enable-profiling, we need -Oy-, which forces a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_OPTIMIZE_FLAGS='-O1' - else - MOZ_OPTIMIZE_FLAGS='-O1 -Oy-' - fi + MOZ_OPTIMIZE_FLAGS='-O1' MOZ_FIX_LINK_PATHS= DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib $(LIBXUL_DIST)/lib/mozalloc.lib' XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/mozalloc.lib' @@ -6764,6 +6743,8 @@ else MOZ_OPTIMIZE= fi ], MOZ_OPTIMIZE=1) +MOZ_SET_FRAMEPTR_FLAGS + if test "$COMPILE_ENVIRONMENT"; then if test -n "$MOZ_OPTIMIZE"; then AC_MSG_CHECKING([for valid optimization flags]) @@ -6782,6 +6763,7 @@ fi fi # COMPILE_ENVIRONMENT AC_SUBST(MOZ_OPTIMIZE) +AC_SUBST(MOZ_FRAMEPTR_FLAGS) AC_SUBST(MOZ_OPTIMIZE_FLAGS) AC_SUBST(MOZ_OPTIMIZE_LDFLAGS) AC_SUBST(MOZ_OPTIMIZE_SIZE_TWEAK) diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 0503e6b64a0e..1d500f1fd8fa 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -83,6 +83,7 @@ static fp_except_t oldmask = fpsetmask(~allmask); #include "nsHtml5Parser.h" #include "nsIFragmentContentSink.h" #include "nsMathUtils.h" +#include "mozilla/TimeStamp.h" struct nsNativeKeyEvent; // Don't include nsINativeKeyBindings.h here: it will force strange compilation error! @@ -189,6 +190,7 @@ class nsContentUtils { friend class nsAutoScriptBlockerSuppressNodeRemoved; typedef mozilla::dom::Element Element; + typedef mozilla::TimeDuration TimeDuration; public: static nsresult Init(); @@ -1700,6 +1702,33 @@ public: */ static PRBool IsFocusedContent(const nsIContent *aContent); + /** + * Returns PR_TRUE if the DOM full-screen API is enabled. + */ + static PRBool IsFullScreenApiEnabled(); + + /** + * Returns PR_TRUE if requests for full-screen are allowed in the current + * context. Requests are only allowed if the user initiated them (like with + * a mouse-click or key press), unless this check has been disabled by + * setting the pref "full-screen-api.allow-trusted-requests-only" to false. + */ + static PRBool IsRequestFullScreenAllowed(); + + /** + * Returns PR_TRUE if key input is restricted in DOM full-screen mode + * to non-alpha-numeric key codes only. This mirrors the + * "full-screen-api.key-input-restricted" pref. + */ + static PRBool IsFullScreenKeyInputRestricted(); + + /** + * Returns the time limit on handling user input before + * nsEventStateManager::IsHandlingUserInput() stops returning PR_TRUE. + * This enables us to detect long running user-generated event handlers. + */ + static TimeDuration HandlingUserInputTimeout(); + static void GetShiftText(nsAString& text); static void GetControlText(nsAString& text); static void GetMetaText(nsAString& text); @@ -1864,6 +1893,10 @@ private: static PRBool sIsHandlingKeyBoardEvent; static PRBool sAllowXULXBL_for_file; + static PRBool sIsFullScreenApiEnabled; + static PRBool sTrustedFullScreenOnly; + static PRBool sFullScreenKeyInputRestricted; + static PRUint32 sHandlingInputTimeout; static nsHtml5Parser* sHTMLFragmentParser; static nsIParser* sXMLFragmentParser; diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index aa8a104328b2..00a1309f2c89 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -125,10 +125,9 @@ class Element; } // namespace dom } // namespace mozilla - #define NS_IDOCUMENT_IID \ -{ 0x455e4d79, 0x756b, 0x4f73, \ - { 0x95, 0xea, 0x3f, 0xf6, 0x0c, 0x6a, 0x8c, 0xa6 } } +{ 0x170d5a75, 0xff0b, 0x4599, \ + { 0x9b, 0x68, 0x18, 0xb7, 0x42, 0xe0, 0xf9, 0xf7 } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -742,6 +741,43 @@ public: virtual void AddToNameTable(Element* aElement, nsIAtom* aName) = 0; virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) = 0; + /** + * Resets the current full-screen element to nsnull. + */ + virtual void ResetFullScreenElement() = 0; + + /** + * Returns the element which either is the full-screen element, or + * contains the full-screen element if a child of this document contains + * the fullscreen element. + */ + virtual Element* GetFullScreenElement() = 0; + + /** + * Requests that the document make aElement the full-screen element, + * and move into full-screen mode. + */ + virtual void RequestFullScreen(Element* aElement) = 0; + + /** + * Requests that the document, and all documents in its hierarchy exit + * from DOM full-screen mode. + */ + virtual void CancelFullScreen() = 0; + + /** + * Updates the full-screen status on this document, setting the full-screen + * mode to aIsFullScreen. This doesn't affect the window's full-screen mode, + * this updates the document's internal state which determines whether the + * document reports as being in full-screen mode. + */ + virtual void UpdateFullScreenStatus(PRBool aIsFullScreen) = 0; + + /** + * Returns PR_TRUE if this document is in full-screen mode. + */ + virtual PRBool IsFullScreenDoc() = 0; + //---------------------------------------------------------------------- // Document notification API's diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 276b80e64f99..676f548fe455 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -176,6 +176,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #include "nsIPluginHost.h" #include "nsICategoryManager.h" #include "nsIViewManager.h" +#include "nsEventStateManager.h" #ifdef IBMBIDI #include "nsIBidiKeyboard.h" @@ -200,6 +201,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #endif #include "nsDOMTouchEvent.h" #include "nsIScriptElement.h" +#include "prdtoa.h" #include "mozilla/Preferences.h" @@ -261,6 +263,11 @@ nsString* nsContentUtils::sAltText = nsnull; nsString* nsContentUtils::sModifierSeparator = nsnull; PRBool nsContentUtils::sInitialized = PR_FALSE; +PRBool nsContentUtils::sIsFullScreenApiEnabled = PR_FALSE; +PRBool nsContentUtils::sTrustedFullScreenOnly = PR_TRUE; +PRBool nsContentUtils::sFullScreenKeyInputRestricted = PR_TRUE; + +PRUint32 nsContentUtils::sHandlingInputTimeout = 1000; nsHtml5Parser* nsContentUtils::sHTMLFragmentParser = nsnull; nsIParser* nsContentUtils::sXMLFragmentParser = nsnull; @@ -315,6 +322,13 @@ class nsSameOriginChecker : public nsIChannelEventSink, NS_DECL_NSIINTERFACEREQUESTOR }; +/* static */ +TimeDuration +nsContentUtils::HandlingUserInputTimeout() +{ + return TimeDuration::FromMilliseconds(sHandlingInputTimeout); +} + // static nsresult nsContentUtils::Init() @@ -384,6 +398,19 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sAllowXULXBL_for_file, "dom.allow_XUL_XBL_for_file"); + Preferences::AddBoolVarCache(&sIsFullScreenApiEnabled, + "full-screen-api.enabled"); + + Preferences::AddBoolVarCache(&sTrustedFullScreenOnly, + "full-screen-api.allow-trusted-requests-only"); + + Preferences::AddBoolVarCache(&sFullScreenKeyInputRestricted, + "full-screen-api.key-input-restricted"); + + Preferences::AddUintVarCache(&sHandlingInputTimeout, + "dom.event.handling-user-input-time-limit", + 1000); + sInitialized = PR_TRUE; return NS_OK; @@ -5696,3 +5723,20 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern, return res == JS_FALSE || rval != JSVAL_NULL; } + +PRBool +nsContentUtils::IsFullScreenApiEnabled() +{ + return sIsFullScreenApiEnabled; +} + +PRBool nsContentUtils::IsRequestFullScreenAllowed() +{ + return !sTrustedFullScreenOnly || nsEventStateManager::IsHandlingUserInput(); +} + +PRBool +nsContentUtils::IsFullScreenKeyInputRestricted() +{ + return sFullScreenKeyInputRestricted; +} diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 0148ce848cfb..d3371abe2573 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -181,6 +181,7 @@ #include "nsGlobalWindow.h" #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "nsDOMNavigationTiming.h" +#include "nsEventStateManager.h" #ifdef MOZ_SMIL #include "nsSMILAnimationController.h" @@ -1544,6 +1545,7 @@ nsDOMImplementation::CreateHTMLDocument(const nsAString& aTitle, nsDocument::nsDocument(const char* aContentType) : nsIDocument() , mAnimatingImages(PR_TRUE) + , mIsFullScreen(PR_FALSE) { SetContentTypeInternal(nsDependentCString(aContentType)); @@ -1871,6 +1873,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) nsIDOMNodeList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedEncoder) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFullScreenElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStateObjectCached) // Traverse all our nsCOMArrays. @@ -1927,6 +1930,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mImageMaps) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOriginalDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedEncoder) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFullScreenElement) tmp->mParentDocument = nsnull; @@ -8465,6 +8469,221 @@ nsIDocument::SizeOf() const return size; } +// Returns the root document in a document hierarchy. +static nsIDocument* +GetRootDocument(nsIDocument* aDoc) +{ + if (!aDoc) { + return nsnull; + } + nsCOMPtr shell = aDoc->GetShell(); + if (!shell) { + return nsnull; + } + nsPresContext* ctx = shell->GetPresContext(); + if (!ctx) { + return nsnull; + } + nsRootPresContext* rpc = ctx->GetRootPresContext(); + if (!rpc) { + return nsnull; + } + return rpc->Document(); +} + +class nsDispatchFullScreenChange : public nsRunnable +{ +public: + nsDispatchFullScreenChange(nsIDocument *aDoc) + : mDoc(aDoc) + { + mTarget = aDoc->GetFullScreenElement(); + if (!mTarget) { + mTarget = aDoc; + } + } + + NS_IMETHOD Run() + { + nsContentUtils::DispatchTrustedEvent(mDoc, + mTarget, + NS_LITERAL_STRING("mozfullscreenchange"), + PR_TRUE, + PR_FALSE); + return NS_OK; + } + + nsCOMPtr mDoc; + nsCOMPtr mTarget; +}; + +void +nsDocument::UpdateFullScreenStatus(PRBool aIsFullScreen) +{ + if (mIsFullScreen != aIsFullScreen) { + nsCOMPtr event(new nsDispatchFullScreenChange(this)); + NS_DispatchToCurrentThread(event); + } + mIsFullScreen = aIsFullScreen; + if (!mIsFullScreen) { + // Full-screen is being turned off. Reset the full-screen element, to + // save us from having to traverse the document hierarchy again in + // MozCancelFullScreen(). + ResetFullScreenElement(); + } +} + +static PRBool +UpdateFullScreenStatus(nsIDocument* aDocument, void* aData) +{ + aDocument->UpdateFullScreenStatus(*static_cast(aData)); + aDocument->EnumerateSubDocuments(UpdateFullScreenStatus, aData); + return PR_TRUE; +} + +static void +UpdateFullScreenStatusInDocTree(nsIDocument* aDoc, PRBool aIsFullScreen) +{ + nsIDocument* root = GetRootDocument(aDoc); + if (root) { + UpdateFullScreenStatus(root, static_cast(&aIsFullScreen)); + } +} + +void +nsDocument::ResetFullScreenElement() +{ + if (mFullScreenElement) { + nsEventStateManager::SetFullScreenState(mFullScreenElement, PR_FALSE); + } + mFullScreenElement = nsnull; +} + +static PRBool +ResetFullScreenElement(nsIDocument* aDocument, void* aData) +{ + aDocument->ResetFullScreenElement(); + aDocument->EnumerateSubDocuments(ResetFullScreenElement, aData); + return PR_TRUE; +} + +static void +ResetFullScreenElementInDocTree(nsIDocument* aDoc) +{ + nsIDocument* root = GetRootDocument(aDoc); + if (root) { + ResetFullScreenElement(root, nsnull); + } +} + +NS_IMETHODIMP +nsDocument::MozCancelFullScreen() +{ + if (!nsContentUtils::IsRequestFullScreenAllowed()) { + return NS_OK; + } + CancelFullScreen(); + return NS_OK; +} + +void +nsDocument::CancelFullScreen() +{ + if (!nsContentUtils::IsFullScreenApiEnabled() || + !IsFullScreenDoc() || + !GetWindow()) { + return; + } + + // Disable full-screen mode in all documents in this hierarchy. + UpdateFullScreenStatusInDocTree(this, PR_FALSE); + + // Move the window out of full-screen mode. + GetWindow()->SetFullScreen(PR_FALSE); + + return; +} + +PRBool +nsDocument::IsFullScreenDoc() +{ + return nsContentUtils::IsFullScreenApiEnabled() && mIsFullScreen; +} + +void +nsDocument::RequestFullScreen(Element* aElement) +{ + if (!aElement || !nsContentUtils::IsFullScreenApiEnabled() || !GetWindow()) { + return; + } + + // Reset the full-screen elements of every document in this document + // hierarchy. + ResetFullScreenElementInDocTree(this); + + if (aElement->IsInDoc()) { + // Propagate up the document hierarchy, setting the full-screen element as + // the element's container in ancestor documents. Note we don't propagate + // down the document hierarchy, the full-screen element (or its container) + // is not visible there. + mFullScreenElement = aElement; + // Set the full-screen state on the element, so the css-pseudo class + // applies to the element. + nsEventStateManager::SetFullScreenState(mFullScreenElement, PR_TRUE); + nsIDocument* child = this; + nsIDocument* parent; + while (parent = child->GetParentDocument()) { + nsIContent* content = parent->FindContentForSubDocument(child); + nsCOMPtr element(do_QueryInterface(content)); + // Containing frames also need the css-pseudo class applied. + nsEventStateManager::SetFullScreenState(element, PR_TRUE); + static_cast(parent)->mFullScreenElement = element; + child = parent; + } + } + + // Set all documents in hierarchy to full-screen mode. + UpdateFullScreenStatusInDocTree(this, PR_TRUE); + + // Make the window full-screen. Note we must make the state changes above + // before making the window full-screen, as then the document reports as + // being in full-screen mode when the Chrome "fullscreen" event fires, + // enabling browser.js to distinguish between browser and dom full-screen + // modes. + GetWindow()->SetFullScreen(PR_TRUE); +} + +NS_IMETHODIMP +nsDocument::GetMozFullScreenElement(nsIDOMHTMLElement **aFullScreenElement) +{ + NS_ENSURE_ARG_POINTER(aFullScreenElement); + if (!nsContentUtils::IsFullScreenApiEnabled() || !IsFullScreenDoc()) { + *aFullScreenElement = nsnull; + return NS_OK; + } + nsCOMPtr e(do_QueryInterface(GetFullScreenElement())); + NS_IF_ADDREF(*aFullScreenElement = e); + return NS_OK; +} + +Element* +nsDocument::GetFullScreenElement() +{ + if (!nsContentUtils::IsFullScreenApiEnabled() || + (mFullScreenElement && !mFullScreenElement->IsInDoc())) { + return nsnull; + } + return mFullScreenElement; +} + +NS_IMETHODIMP +nsDocument::GetMozFullScreen(PRBool *aFullScreen) +{ + NS_ENSURE_ARG_POINTER(aFullScreen); + *aFullScreen = nsContentUtils::IsFullScreenApiEnabled() && IsFullScreenDoc(); + return NS_OK; +} + PRInt64 nsDocument::SizeOf() const { diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 28e5310b919b..8f5d0d83e423 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -941,6 +941,13 @@ public: virtual Element* FindImageMap(const nsAString& aNormalizedMapName); + virtual void ResetFullScreenElement(); + virtual Element* GetFullScreenElement(); + virtual void RequestFullScreen(Element* aElement); + virtual void CancelFullScreen(); + virtual void UpdateFullScreenStatus(PRBool aIsFullScreen); + virtual PRBool IsFullScreenDoc(); + protected: friend class nsNodeUtils; @@ -1078,6 +1085,9 @@ protected: // Recorded time of change to 'loading' state. mozilla::TimeStamp mLoadingTimeStamp; + // The current full-screen element of this document. + nsCOMPtr mFullScreenElement; + // True if the document has been detached from its content viewer. PRPackedBool mIsGoingAway:1; // True if the document is being destroyed. @@ -1110,6 +1120,9 @@ protected: // Whether we currently require our images to animate PRPackedBool mAnimatingImages:1; + // Whether we are currently in full-screen mode, as per the DOM API. + PRPackedBool mIsFullScreen:1; + PRUint8 mXMLDeclarationBits; PRUint8 mDefaultElementType; diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index e24ae0c02e3b..740aba8c8652 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -63,6 +63,7 @@ GK_ATOM(_empty, "") GK_ATOM(moz, "_moz") +GK_ATOM(mozallowfullscreen, "mozallowfullscreen") GK_ATOM(moztype, "_moz-type") GK_ATOM(mozdirty, "_moz_dirty") GK_ATOM(mozdonotsend, "moz-do-not-send") @@ -583,6 +584,7 @@ GK_ATOM(mouseout, "mouseout") GK_ATOM(mouseover, "mouseover") GK_ATOM(mousethrough, "mousethrough") GK_ATOM(mouseup, "mouseup") +GK_ATOM(mozfullscreenchange, "mozfullscreenchange") GK_ATOM(moz_opaque, "moz-opaque") GK_ATOM(moz_action_hint, "mozactionhint") GK_ATOM(x_moz_errormessage, "x-moz-errormessage") @@ -695,6 +697,7 @@ GK_ATOM(onMozMouseHittest, "onMozMouseHittest") GK_ATOM(onmouseup, "onmouseup") GK_ATOM(onMozAfterPaint, "onMozAfterPaint") GK_ATOM(onMozBeforePaint, "onMozBeforePaint") +GK_ATOM(onmozfullscreenchange, "onmozfullscreenchange") GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll") GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged") GK_ATOM(ononline, "ononline") 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/content/events/public/nsEventNameList.h b/content/events/public/nsEventNameList.h index 3b98283d66ff..8c5c4fb971a7 100644 --- a/content/events/public/nsEventNameList.h +++ b/content/events/public/nsEventNameList.h @@ -243,6 +243,10 @@ EVENT(mouseup, NS_MOUSE_BUTTON_UP, EventNameType_All, NS_MOUSE_EVENT) +EVENT(mozfullscreenchange, + NS_FULLSCREENCHANGE, + EventNameType_HTML, + NS_EVENT_NULL) // Not supported yet; probably never because "wheel" is a better idea. // EVENT(mousewheel) EVENT(pause, diff --git a/content/events/public/nsEventStates.h b/content/events/public/nsEventStates.h index bcca98915797..97078ce864a8 100644 --- a/content/events/public/nsEventStates.h +++ b/content/events/public/nsEventStates.h @@ -261,6 +261,9 @@ private: #define NS_EVENT_STATE_MOZ_UI_INVALID NS_DEFINE_EVENT_STATE_MACRO(32) // UI friendly version of :valid pseudo-class. #define NS_EVENT_STATE_MOZ_UI_VALID NS_DEFINE_EVENT_STATE_MACRO(33) +// Content is the full screen element, or a frame containing the +// current full-screen element. +#define NS_EVENT_STATE_FULL_SCREEN NS_DEFINE_EVENT_STATE_MACRO(34) /** * NOTE: do not go over 63 without updating nsEventStates::InternalType! @@ -268,7 +271,8 @@ private: #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \ NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \ - NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING) + NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \ + NS_EVENT_STATE_FULL_SCREEN) #define INTRINSIC_STATES (~ESM_MANAGED_STATES) diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 059ee02eeaf1..8df917f6b581 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -92,6 +92,7 @@ static const char* const sEventNames[] = { "MozAfterPaint", "MozBeforePaint", "MozBeforeResize", + "mozfullscreenchange", "MozSwipeGesture", "MozMagnifyGestureStart", "MozMagnifyGestureUpdate", @@ -1366,6 +1367,8 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) return sEventNames[eDOMEvents_devicemotion]; case NS_DEVICE_ORIENTATION: return sEventNames[eDOMEvents_deviceorientation]; + case NS_FULLSCREENCHANGE: + return sEventNames[eDOMEvents_mozfullscreenchange]; default: break; } diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index c8a3f726ba1d..d5df9854db78 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -177,6 +177,7 @@ public: eDOMEvents_afterpaint, eDOMEvents_beforepaint, eDOMEvents_beforeresize, + eDOMEvents_mozfullscreenchange, eDOMEvents_MozSwipeGesture, eDOMEvents_MozMagnifyGestureStart, eDOMEvents_MozMagnifyGestureUpdate, diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index aeb3411ab8b9..a1f84b16d818 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -175,6 +175,8 @@ static PRUint32 gPixelScrollDeltaTimeout = 0; static nscoord GetScrollableLineHeight(nsIFrame* aTargetFrame); +TimeStamp nsEventStateManager::sHandlingInputStart; + static inline PRBool IsMouseEventReal(nsEvent* aEvent) { @@ -4384,6 +4386,14 @@ static nsIContent* FindCommonAncestor(nsIContent *aNode1, nsIContent *aNode2) return nsnull; } +/* static */ +void +nsEventStateManager::SetFullScreenState(Element* aElement, + PRBool aIsFullScreen) +{ + DoStateChange(aElement, NS_EVENT_STATE_FULL_SCREEN, aIsFullScreen); +} + /* static */ inline void nsEventStateManager::DoStateChange(Element* aElement, nsEventStates aState, diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index d67832b139dd..915941638f75 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -57,6 +57,8 @@ #include "nsFocusManager.h" #include "nsIDocument.h" #include "nsEventStates.h" +#include "mozilla/TimeStamp.h" +#include "nsContentUtils.h" class nsIPresShell; class nsIDocShell; @@ -80,6 +82,10 @@ class nsEventStateManager : public nsSupportsWeakReference, { friend class nsMouseWheelTransaction; public: + + typedef mozilla::TimeStamp TimeStamp; + typedef mozilla::TimeDuration TimeDuration; + nsEventStateManager(); virtual ~nsEventStateManager(); @@ -169,16 +175,27 @@ public: static void StartHandlingUserInput() { ++sUserInputEventDepth; + if (sUserInputEventDepth == 1) { + sHandlingInputStart = TimeStamp::Now(); + } } static void StopHandlingUserInput() { --sUserInputEventDepth; + if (sUserInputEventDepth == 0) { + sHandlingInputStart = TimeStamp(); + } } static PRBool IsHandlingUserInput() { - return sUserInputEventDepth > 0; + if (sUserInputEventDepth <= 0) { + return PR_FALSE; + } + TimeDuration timeout = nsContentUtils::HandlingUserInputTimeout(); + return timeout <= TimeDuration(0) || + (TimeStamp::Now() - sHandlingInputStart) <= timeout; } /** @@ -186,7 +203,10 @@ public: * This includes timers or anything else that is initiated from user input. * However, mouse hover events are not counted as user input, nor are * page load events. If this method is called from asynchronously executed code, - * such as during layout reflows, it will return false. + * such as during layout reflows, it will return false. If more time has elapsed + * since the user input than is specified by the + * dom.event.handling-user-input-time-limit pref (default 1 second), this + * function also returns false. */ NS_IMETHOD_(PRBool) IsHandlingUserInputExternal() { return IsHandlingUserInput(); } @@ -203,6 +223,10 @@ public: // if aContent is non-null, marks the object as active. static void SetActiveManager(nsEventStateManager* aNewESM, nsIContent* aContent); + + // Sets the full-screen event state on aElement to aIsFullScreen. + static void SetFullScreenState(mozilla::dom::Element* aElement, PRBool aIsFullScreen); + protected: void UpdateCursor(nsPresContext* aPresContext, nsEvent* aEvent, nsIFrame* aTargetFrame, nsEventStatus* aStatus); /** @@ -503,6 +527,8 @@ private: PRPackedBool m_haveShutdown; + // Time at which we began handling user input. + static TimeStamp sHandlingInputStart; public: static nsresult UpdateUserActivityTimer(void); diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 86721b1150ad..a1e3ca0fb935 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -3379,6 +3379,50 @@ nsGenericHTMLElement::Focus() return fm ? fm->SetFocus(elem, 0) : NS_OK; } +nsresult nsGenericHTMLElement::MozRequestFullScreen() +{ + // Only grant full-screen requests if this is called from inside a trusted + // event handler (i.e. inside an event handler for a user initiated event). + // This stops the full-screen from being abused similar to the popups of old, + // and it also makes it harder for bad guys' script to go full-screen and + // spoof the browser chrome/window and phish logins etc. + if (!nsContentUtils::IsFullScreenApiEnabled() || + !nsContentUtils::IsRequestFullScreenAllowed()) { + return NS_OK; + } + + // Ensure that all ancestor +

+ +
+
+
+ + diff --git a/dom/tests/mochitest/general/Makefile.in b/dom/tests/mochitest/general/Makefile.in index 2fbf42742eab..12f5e9ddf552 100644 --- a/dom/tests/mochitest/general/Makefile.in +++ b/dom/tests/mochitest/general/Makefile.in @@ -68,6 +68,7 @@ _TEST_FILES = \ test_framedhistoryframes.html \ test_windowedhistoryframes.html \ test_focusrings.xul \ + file_moving_xhr.html \ $(NULL) _CHROME_FILES = \ diff --git a/dom/tests/mochitest/general/file_moving_xhr.html b/dom/tests/mochitest/general/file_moving_xhr.html new file mode 100644 index 000000000000..d6a0e5d0a3ac --- /dev/null +++ b/dom/tests/mochitest/general/file_moving_xhr.html @@ -0,0 +1,26 @@ + + + + + + + diff --git a/editor/composer/src/crashtests/crashtests.list b/editor/composer/src/crashtests/crashtests.list index 487d96b45a51..c42ade4e0f98 100644 --- a/editor/composer/src/crashtests/crashtests.list +++ b/editor/composer/src/crashtests/crashtests.list @@ -1,4 +1,4 @@ -load 351236-1.html +asserts-if(Android,2) load 351236-1.html load 407062-1.html load 419563-1.xhtml skip-if(winWidget) load 428844-1.html # bug 471185 diff --git a/editor/composer/src/nsEditorSpellCheck.cpp b/editor/composer/src/nsEditorSpellCheck.cpp index ad4bc1675ec0..8eea444ac9c1 100644 --- a/editor/composer/src/nsEditorSpellCheck.cpp +++ b/editor/composer/src/nsEditorSpellCheck.cpp @@ -54,12 +54,8 @@ #include "nsIComponentManager.h" #include "nsIContentPrefService.h" -#include "nsIObserverService.h" #include "nsServiceManagerUtils.h" #include "nsIChromeRegistry.h" -#include "nsIPrivateBrowsingService.h" -#include "nsIContentURIGrouper.h" -#include "nsNetCID.h" #include "nsString.h" #include "nsReadableUtils.h" #include "nsITextServicesFilter.h" @@ -87,16 +83,11 @@ class UpdateDictionnaryHolder { #define CPS_PREF_NAME NS_LITERAL_STRING("spellcheck.lang") -class LastDictionary : public nsIObserver, public nsSupportsWeakReference { +class LastDictionary { public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - - LastDictionary(); - /** * Store current dictionary for editor document url. Use content pref - * service. Or, if in private mode, store this information in memory. + * service. */ NS_IMETHOD StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary); @@ -115,26 +106,8 @@ public: * */ static nsresult GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI); - - PRBool mInPrivateBrowsing; - - // During private browsing, dictionaries are stored in memory - nsDataHashtable mMemoryStorage; }; -NS_IMPL_ISUPPORTS2(LastDictionary, nsIObserver, nsISupportsWeakReference) - -LastDictionary::LastDictionary(): - mInPrivateBrowsing(PR_FALSE) -{ - nsCOMPtr pbService = - do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID); - if (pbService) { - pbService->GetPrivateBrowsingEnabled(&mInPrivateBrowsing); - mMemoryStorage.Init(); - } -} - // static nsresult LastDictionary::GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI) @@ -168,21 +141,6 @@ LastDictionary::FetchLastDictionary(nsIEditor* aEditor, nsAString& aDictionary) rv = GetDocumentURI(aEditor, getter_AddRefs(docUri)); NS_ENSURE_SUCCESS(rv, rv); - if (mInPrivateBrowsing) { - nsCOMPtr hostnameGrouperService = - do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(hostnameGrouperService, NS_ERROR_NOT_AVAILABLE); - nsString group; - hostnameGrouperService->Group(docUri, group); - nsAutoString lastDict; - if (mMemoryStorage.Get(group, &lastDict)) { - aDictionary.Assign(lastDict); - } else { - aDictionary.Truncate(); - } - return NS_OK; - } - nsCOMPtr contentPrefService = do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_AVAILABLE); @@ -214,20 +172,6 @@ LastDictionary::StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDic rv = GetDocumentURI(aEditor, getter_AddRefs(docUri)); NS_ENSURE_SUCCESS(rv, rv); - if (mInPrivateBrowsing) { - nsCOMPtr hostnameGrouperService = - do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(hostnameGrouperService, NS_ERROR_NOT_AVAILABLE); - nsString group; - hostnameGrouperService->Group(docUri, group); - - if (mMemoryStorage.Put(group, nsString(aDictionary))) { - return NS_OK; - } else { - return NS_ERROR_FAILURE; - } - } - nsCOMPtr uri = do_CreateInstance(NS_VARIANT_CONTRACTID); NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY); uri->SetAsISupports(docUri); @@ -254,18 +198,6 @@ LastDictionary::ClearCurrentDictionary(nsIEditor* aEditor) rv = GetDocumentURI(aEditor, getter_AddRefs(docUri)); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr hostnameGrouperService = - do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(hostnameGrouperService, NS_ERROR_NOT_AVAILABLE); - - nsString group; - hostnameGrouperService->Group(docUri, group); - NS_ENSURE_SUCCESS(rv, rv); - - if (mMemoryStorage.IsInitialized()) { - mMemoryStorage.Remove(group); - } - nsCOMPtr uri = do_CreateInstance(NS_VARIANT_CONTRACTID); NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY); uri->SetAsISupports(docUri); @@ -277,22 +209,6 @@ LastDictionary::ClearCurrentDictionary(nsIEditor* aEditor) return contentPrefService->RemovePref(uri, CPS_PREF_NAME); } -NS_IMETHODIMP -LastDictionary::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData) -{ - if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) { - if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) { - mInPrivateBrowsing = PR_TRUE; - } else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) { - mInPrivateBrowsing = PR_FALSE; - if (mMemoryStorage.IsInitialized()) { - mMemoryStorage.Clear(); - } - } - } - return NS_OK; -} - LastDictionary* nsEditorSpellCheck::gDictionaryStore = nsnull; NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditorSpellCheck) @@ -358,14 +274,6 @@ nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, PRBool aEnableSelection if (!gDictionaryStore) { gDictionaryStore = new LastDictionary(); - if (gDictionaryStore) { - NS_ADDREF(gDictionaryStore); - nsCOMPtr observerService = - mozilla::services::GetObserverService(); - if (observerService) { - observerService->AddObserver(gDictionaryStore, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE); - } - } } @@ -729,8 +637,9 @@ nsEditorSpellCheck::UpdateCurrentDictionary() } // otherwise, get language from preferences + nsAutoString preferedDict(Preferences::GetLocalizedString("spellchecker.dictionary")); if (dictName.IsEmpty()) { - dictName.Assign(Preferences::GetLocalizedString("spellchecker.dictionary")); + dictName.Assign(preferedDict); } if (dictName.IsEmpty()) @@ -755,27 +664,50 @@ nsEditorSpellCheck::UpdateCurrentDictionary() rv = SetCurrentDictionary(dictName); if (NS_FAILED(rv)) { // required dictionary was not available. Try to get a dictionary - // matching at least language part of dictName: If required dictionary is - // "aa-bb", we try "aa", then we try any available dictionary aa-XX + // matching at least language part of dictName: + nsAutoString langCode; PRInt32 dashIdx = dictName.FindChar('-'); if (dashIdx != -1) { langCode.Assign(Substring(dictName, 0, dashIdx)); - // try to use langCode - rv = SetCurrentDictionary(langCode); } else { langCode.Assign(dictName); } + + nsDefaultStringComparator comparator; + + // try dictionary.spellchecker preference if it starts with langCode (and + // if we haven't tried it already) + if (!preferedDict.IsEmpty() && !dictName.Equals(preferedDict) && + nsStyleUtil::DashMatchCompare(preferedDict, langCode, comparator)) { + rv = SetCurrentDictionary(preferedDict); + } + + // Otherwise, try langCode (if we haven't tried it already) + if (NS_FAILED(rv)) { + if (!dictName.Equals(langCode) && !preferedDict.Equals(langCode)) { + rv = SetCurrentDictionary(langCode); + } + } + + // Otherwise, try any available dictionary aa-XX if (NS_FAILED(rv)) { // loop over avaible dictionaries; if we find one with required // language, use it nsTArray dictList; rv = mSpellChecker->GetDictionaryList(&dictList); NS_ENSURE_SUCCESS(rv, rv); - nsDefaultStringComparator comparator; PRInt32 i, count = dictList.Length(); for (i = 0; i < count; i++) { nsAutoString dictStr(dictList.ElementAt(i)); + + if (dictStr.Equals(dictName) || + dictStr.Equals(preferedDict) || + dictStr.Equals(langCode)) { + // We have already tried it + continue; + } + if (nsStyleUtil::DashMatchCompare(dictStr, langCode, comparator) && NS_SUCCEEDED(SetCurrentDictionary(dictStr))) { break; @@ -815,5 +747,5 @@ nsEditorSpellCheck::UpdateCurrentDictionary() void nsEditorSpellCheck::ShutDown() { - NS_IF_RELEASE(gDictionaryStore); + delete gDictionaryStore; } diff --git a/editor/composer/src/nsEditorSpellCheck.h b/editor/composer/src/nsEditorSpellCheck.h index f4fe764a3208..63e8fe2ab1e2 100644 --- a/editor/composer/src/nsEditorSpellCheck.h +++ b/editor/composer/src/nsEditorSpellCheck.h @@ -43,7 +43,6 @@ #include "nsIEditorSpellCheck.h" #include "nsISpellChecker.h" -#include "nsIObserver.h" #include "nsIURI.h" #include "nsWeakReference.h" #include "nsCOMPtr.h" diff --git a/editor/composer/test/test_bug434998.xul b/editor/composer/test/test_bug434998.xul index 2e9ccc929e56..3a5f50faa778 100644 --- a/editor/composer/test/test_bug434998.xul +++ b/editor/composer/test/test_bug434998.xul @@ -65,6 +65,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=434998 threw = true; } ok(!threw, "The execCommand API should work on "); + progress.removeProgressListener(progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL); SimpleTest.finish(); } } diff --git a/editor/libeditor/base/crashtests/crashtests.list b/editor/libeditor/base/crashtests/crashtests.list index 84c1cb7318ef..0619a5624f25 100644 --- a/editor/libeditor/base/crashtests/crashtests.list +++ b/editor/libeditor/base/crashtests/crashtests.list @@ -6,5 +6,5 @@ load 407256-1.html load 430624-1.html load 459613.html load 475132-1.xhtml -load 633709.xhtml -asserts(6) load 636074-1.html # Bug 439258, charged to the wrong test due to bug 635550 +asserts-if(Android,6) load 633709.xhtml +asserts-if(!Android,6) load 636074-1.html # Bug 439258, charged to the wrong test due to bug 635550 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/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 3b7eb3661968..510c007e34ea 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -10,7 +10,7 @@ #endif > + android:targetSdkVersion="5"/> diff --git a/embedding/android/GeckoApp.java b/embedding/android/GeckoApp.java index 1655b60786e0..560e00e0d1b5 100644 --- a/embedding/android/GeckoApp.java +++ b/embedding/android/GeckoApp.java @@ -467,9 +467,15 @@ abstract public class GeckoApp componentsDir.mkdir(); componentsDir.setLastModified(applicationPackage.lastModified()); + surfaceView.mSplashStatusMsg = + getResources().getString(R.string.splash_firstrun); + surfaceView.drawSplashScreen(); + + GeckoAppShell.killAnyZombies(); + ZipFile zip = new ZipFile(applicationPackage); - byte[] buf = new byte[8192]; + byte[] buf = new byte[32768]; try { if (unpackFile(zip, buf, null, "removed-files")) removeFiles(); @@ -520,8 +526,6 @@ abstract public class GeckoApp } - boolean haveKilledZombies = false; - private boolean unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry, String name) throws IOException, FileNotFoundException @@ -533,22 +537,12 @@ abstract public class GeckoApp zip.getName()); File outFile = new File(sGREDir, name); - if (outFile.exists() && - outFile.lastModified() == fileEntry.getTime() && + if (outFile.lastModified() == fileEntry.getTime() && outFile.length() == fileEntry.getSize()) return false; - surfaceView.mSplashStatusMsg = - getResources().getString(R.string.splash_firstrun); - surfaceView.drawSplashScreen(); - - if (!haveKilledZombies) { - haveKilledZombies = true; - GeckoAppShell.killAnyZombies(); - } - File dir = outFile.getParentFile(); - if (!outFile.exists()) + if (!dir.exists()) dir.mkdirs(); InputStream fileStream; diff --git a/embedding/android/GeckoSurfaceView.java b/embedding/android/GeckoSurfaceView.java index b8a49477efc3..34a64ca59406 100644 --- a/embedding/android/GeckoSurfaceView.java +++ b/embedding/android/GeckoSurfaceView.java @@ -597,9 +597,9 @@ class GeckoSurfaceView switch (event.getAction()) { case KeyEvent.ACTION_DOWN: - return onKeyDown(keyCode, event); + return processKeyDown(keyCode, event, true); case KeyEvent.ACTION_UP: - return onKeyUp(keyCode, event); + return processKeyUp(keyCode, event, true); case KeyEvent.ACTION_MULTIPLE: return onKeyMultiple(keyCode, event.getRepeatCount(), event); } @@ -608,6 +608,10 @@ class GeckoSurfaceView @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + return processKeyDown(keyCode, event, false); + } + + private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: if (event.getRepeatCount() == 0) { @@ -644,6 +648,11 @@ class GeckoSurfaceView default: break; } + + if (isPreIme && mIMEState != IME_STATE_DISABLED) + // Let active IME process pre-IME key events + return false; + // KeyListener returns true if it handled the event for us. if (mIMEState == IME_STATE_DISABLED || keyCode == KeyEvent.KEYCODE_ENTER || @@ -656,6 +665,10 @@ class GeckoSurfaceView @Override public boolean onKeyUp(int keyCode, KeyEvent event) { + return processKeyUp(keyCode, event, false); + } + + private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: if (!event.isTracking() || event.isCanceled()) @@ -664,6 +677,11 @@ class GeckoSurfaceView default: break; } + + if (isPreIme && mIMEState != IME_STATE_DISABLED) + // Let active IME process pre-IME key events + return false; + if (mIMEState == IME_STATE_DISABLED || keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DEL || 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..85b869f35f2e 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -581,12 +581,28 @@ StyleDistance(gfxFontEntry *aFontEntry, // and the given fontEntry, // considering italicness and font-stretch but not weight. - // TODO (refine CSS spec...): discuss priority of italic vs stretch; - // whether penalty for stretch mismatch should depend on actual difference in values; - // whether a sign mismatch in stretch should increase the effective distance - - return (aFontEntry->IsItalic() != anItalic ? 1 : 0) + - (aFontEntry->mStretch != aStretch ? 10 : 0); + PRInt32 distance = 0; + if (aStretch != aFontEntry->mStretch) { + // stretch values are in the range -4 .. +4 + // if aStretch is positive, we prefer more-positive values; + // if zero or negative, prefer more-negative + if (aStretch > 0) { + distance = (aFontEntry->mStretch - aStretch) * 2; + } else { + distance = (aStretch - aFontEntry->mStretch) * 2; + } + // if the computed "distance" here is negative, it means that + // aFontEntry lies in the "non-preferred" direction from aStretch, + // so we treat that as larger than any preferred-direction distance + // (max possible is 8) by adding an extra 10 to the absolute value + if (distance < 0) { + distance = -distance + 10; + } + } + if (aFontEntry->IsItalic() != anItalic) { + distance += 1; + } + return PRUint32(distance); } PRBool @@ -609,9 +625,11 @@ gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[], aFontsForWeights[wt] = fe; ++foundWeights; } else { - PRUint32 prevDistance = StyleDistance(aFontsForWeights[wt], anItalic, aStretch); + PRUint32 prevDistance = + StyleDistance(aFontsForWeights[wt], anItalic, aStretch); if (prevDistance >= distance) { - // replacing a weight we already found, so don't increment foundWeights + // replacing a weight we already found, + // so don't increment foundWeights aFontsForWeights[wt] = fe; } } @@ -1035,10 +1053,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 +1133,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 +1173,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 +1223,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 +1276,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 +3557,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 +3568,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/gfxFontconfigUtils.cpp b/gfx/thebes/gfxFontconfigUtils.cpp index 8c7353ba5c7c..78d5c2bc6481 100644 --- a/gfx/thebes/gfxFontconfigUtils.cpp +++ b/gfx/thebes/gfxFontconfigUtils.cpp @@ -177,6 +177,66 @@ gfxFontconfigUtils::FcWeightForBaseWeight(PRInt8 aBaseWeight) return aBaseWeight < 2 ? FC_WEIGHT_THIN : FC_WEIGHT_EXTRABLACK; } +/* static */ PRInt16 +gfxFontconfigUtils::GetThebesStretch(FcPattern *aPattern) +{ + int width; + if (FcPatternGetInteger(aPattern, FC_WIDTH, 0, &width) != FcResultMatch) { + return NS_FONT_STRETCH_NORMAL; + } + + if (width <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) { + return NS_FONT_STRETCH_ULTRA_CONDENSED; + } + if (width <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) { + return NS_FONT_STRETCH_EXTRA_CONDENSED; + } + if (width <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) { + return NS_FONT_STRETCH_CONDENSED; + } + if (width <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) { + return NS_FONT_STRETCH_SEMI_CONDENSED; + } + if (width <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) { + return NS_FONT_STRETCH_NORMAL; + } + if (width <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) { + return NS_FONT_STRETCH_SEMI_EXPANDED; + } + if (width <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) { + return NS_FONT_STRETCH_EXPANDED; + } + if (width <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) { + return NS_FONT_STRETCH_EXTRA_EXPANDED; + } + return NS_FONT_STRETCH_ULTRA_EXPANDED; +} + +/* static */ int +gfxFontconfigUtils::FcWidthForThebesStretch(PRInt16 aStretch) +{ + switch (aStretch) { + default: // this will catch "normal" (0) as well as out-of-range values + return FC_WIDTH_NORMAL; + case NS_FONT_STRETCH_ULTRA_CONDENSED: + return FC_WIDTH_ULTRACONDENSED; + case NS_FONT_STRETCH_EXTRA_CONDENSED: + return FC_WIDTH_EXTRACONDENSED; + case NS_FONT_STRETCH_CONDENSED: + return FC_WIDTH_CONDENSED; + case NS_FONT_STRETCH_SEMI_CONDENSED: + return FC_WIDTH_SEMICONDENSED; + case NS_FONT_STRETCH_SEMI_EXPANDED: + return FC_WIDTH_SEMIEXPANDED; + case NS_FONT_STRETCH_EXPANDED: + return FC_WIDTH_EXPANDED; + case NS_FONT_STRETCH_EXTRA_EXPANDED: + return FC_WIDTH_EXTRAEXPANDED; + case NS_FONT_STRETCH_ULTRA_EXPANDED: + return FC_WIDTH_ULTRAEXPANDED; + } +} + // This makes a guess at an FC_WEIGHT corresponding to a base weight and // offset (without any knowledge of which weights are available). @@ -244,6 +304,7 @@ gfxFontconfigUtils::NewPattern(const nsTArray& aFamilies, FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle.size); FcPatternAddInteger(pattern, FC_SLANT, GetFcSlant(aFontStyle)); FcPatternAddInteger(pattern, FC_WEIGHT, GuessFcWeight(aFontStyle)); + FcPatternAddInteger(pattern, FC_WIDTH, FcWidthForThebesStretch(aFontStyle.stretch)); if (aLang) { AddString(pattern, FC_LANG, aLang); diff --git a/gfx/thebes/gfxFontconfigUtils.h b/gfx/thebes/gfxFontconfigUtils.h index e072b4ee3d06..5f4d0f0451ea 100644 --- a/gfx/thebes/gfxFontconfigUtils.h +++ b/gfx/thebes/gfxFontconfigUtils.h @@ -149,12 +149,15 @@ public: static PRUint8 FcSlantToThebesStyle(int aFcSlant); static PRUint8 GetThebesStyle(FcPattern *aPattern); // slant static PRUint16 GetThebesWeight(FcPattern *aPattern); + static PRInt16 GetThebesStretch(FcPattern *aPattern); static int GetFcSlant(const gfxFontStyle& aFontStyle); // Returns a precise FC_WEIGHT from |aBaseWeight|, // which is a CSS absolute weight / 100. static int FcWeightForBaseWeight(PRInt8 aBaseWeight); + static int FcWidthForThebesStretch(PRInt16 aStretch); + static PRBool GetFullnameFromFamilyAndStyle(FcPattern *aFont, nsACString *aFullname); diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp index 6ce4fd91f6e1..b84390654a24 100644 --- a/gfx/thebes/gfxGDIFontList.cpp +++ b/gfx/thebes/gfxGDIFontList.cpp @@ -213,17 +213,20 @@ FontTypeToOutPrecision(PRUint8 fontType) * */ -GDIFontEntry::GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType, - PRBool aItalic, PRUint16 aWeight, gfxUserFontData *aUserFontData) : - gfxFontEntry(aFaceName), - mWindowsFamily(0), mWindowsPitch(0), - mFontType(aFontType), - mForceGDI(PR_FALSE), mUnknownCMAP(PR_FALSE), - mCharset(), mUnicodeRanges() +GDIFontEntry::GDIFontEntry(const nsAString& aFaceName, + gfxWindowsFontType aFontType, + PRBool aItalic, PRUint16 aWeight, PRInt16 aStretch, + gfxUserFontData *aUserFontData) + : gfxFontEntry(aFaceName), + mWindowsFamily(0), mWindowsPitch(0), + mFontType(aFontType), + mForceGDI(PR_FALSE), mUnknownCMAP(PR_FALSE), + mCharset(), mUnicodeRanges() { mUserFontData = aUserFontData; mItalic = aItalic; mWeight = aWeight; + mStretch = aStretch; if (IsType1()) mForceGDI = PR_TRUE; mIsUserFont = aUserFontData != nsnull; @@ -318,8 +321,8 @@ GDIFontEntry::GetFontTable(PRUint32 aTableTag, void GDIFontEntry::FillLogFont(LOGFONTW *aLogFont, PRBool aItalic, - PRUint16 aWeight, gfxFloat aSize, - PRBool aUseCleartype) + PRUint16 aWeight, gfxFloat aSize, + PRBool aUseCleartype) { memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW)); @@ -414,7 +417,7 @@ GDIFontEntry::TestCharacterMap(PRUint32 aCh) void GDIFontEntry::InitLogFont(const nsAString& aName, - gfxWindowsFontType aFontType) + gfxWindowsFontType aFontType) { #define CLIP_TURNOFF_FONTASSOCIATION 0x40 @@ -444,14 +447,15 @@ GDIFontEntry::InitLogFont(const nsAString& aName, } GDIFontEntry* -GDIFontEntry::CreateFontEntry(const nsAString& aName, gfxWindowsFontType aFontType, - PRBool aItalic, PRUint16 aWeight, - gfxUserFontData* aUserFontData) +GDIFontEntry::CreateFontEntry(const nsAString& aName, + gfxWindowsFontType aFontType, PRBool aItalic, + PRUint16 aWeight, PRInt16 aStretch, + gfxUserFontData* aUserFontData) { // jtdfix - need to set charset, unicode ranges, pitch/family - GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aItalic, aWeight, - aUserFontData); + GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aItalic, + aWeight, aStretch, aUserFontData); return fe; } @@ -500,8 +504,10 @@ GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe, } } - fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName), feType, (logFont.lfItalic == 0xFF), - (PRUint16) (logFont.lfWeight), nsnull); + fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName), + feType, (logFont.lfItalic == 0xFF), + (PRUint16) (logFont.lfWeight), 0, + nsnull); if (!fe) return 1; @@ -747,7 +753,7 @@ gfxGDIFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, gfxFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(), gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, PRUint32(aProxyEntry->mItalic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL), - w, nsnull); + w, aProxyEntry->mStretch, nsnull); if (!fe) return nsnull; @@ -966,7 +972,7 @@ gfxGDIFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName, gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, PRUint32(aProxyEntry->mItalic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL), - w, winUserFontData); + w, aProxyEntry->mStretch, winUserFontData); if (!fe) return fe; diff --git a/gfx/thebes/gfxGDIFontList.h b/gfx/thebes/gfxGDIFontList.h index 35603629b463..254bb0d7a42d 100644 --- a/gfx/thebes/gfxGDIFontList.h +++ b/gfx/thebes/gfxGDIFontList.h @@ -275,14 +275,15 @@ public: virtual PRBool TestCharacterMap(PRUint32 aCh); // create a font entry for a font with a given name - static GDIFontEntry* CreateFontEntry(const nsAString& aName, - gfxWindowsFontType aFontType, - PRBool aItalic, PRUint16 aWeight, - gfxUserFontData* aUserFontData); + static GDIFontEntry* CreateFontEntry(const nsAString& aName, + gfxWindowsFontType aFontType, + PRBool aItalic, + PRUint16 aWeight, PRInt16 aStretch, + gfxUserFontData* aUserFontData); // create a font entry for a font referenced by its fullname static GDIFontEntry* LoadLocalFont(const gfxProxyFontEntry &aProxyEntry, - const nsAString& aFullname); + const nsAString& aFullname); PRUint8 mWindowsFamily; PRUint8 mWindowsPitch; @@ -298,7 +299,8 @@ protected: friend class gfxWindowsFont; GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType, - PRBool aItalic, PRUint16 aWeight, gfxUserFontData *aUserFontData); + PRBool aItalic, PRUint16 aWeight, PRInt16 aStretch, + gfxUserFontData *aUserFontData); void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType); 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/gfx/thebes/gfxPangoFonts.cpp b/gfx/thebes/gfxPangoFonts.cpp index 6bf70f8544eb..c29ea7e25305 100644 --- a/gfx/thebes/gfxPangoFonts.cpp +++ b/gfx/thebes/gfxPangoFonts.cpp @@ -449,6 +449,14 @@ gfxUserFcFontEntry::AdjustPatternToCSS(FcPattern *aPattern) IsItalic() ? FC_SLANT_OBLIQUE : FC_SLANT_ROMAN); } + int fontWidth = -1; + FcPatternGetInteger(aPattern, FC_WIDTH, 0, &fontWidth); + int cssWidth = gfxFontconfigUtils::FcWidthForThebesStretch(mStretch); + if (cssWidth != fontWidth) { + FcPatternDel(aPattern, FC_WIDTH); + FcPatternAddInteger(aPattern, FC_WIDTH, cssWidth); + } + // Ensure that there is a fullname property (if there is a family // property) so that fontconfig rules can identify the real name of the // font, because the family property will be replaced. @@ -1250,8 +1258,9 @@ private: // and style |aStyle| properties. static const nsTArray< nsCountedRef >* FindFontPatterns(gfxUserFontSet *mUserFontSet, - const nsACString &aFamily, PRUint8 aStyle, PRUint16 aWeight, - PRBool& aFoundFamily, PRBool& aWaitForUserFont) + const nsACString &aFamily, PRUint8 aStyle, + PRUint16 aWeight, PRInt16 aStretch, + PRBool& aFoundFamily, PRBool& aWaitForUserFont) { // Convert to UTF16 NS_ConvertUTF8toUTF16 utf16Family(aFamily); @@ -1264,6 +1273,7 @@ FindFontPatterns(gfxUserFontSet *mUserFontSet, gfxFontStyle style; style.style = aStyle; style.weight = aWeight; + style.stretch = aStretch; gfxUserFcFontEntry *fontEntry = static_cast (mUserFontSet->FindFontEntry(utf16Family, style, aFoundFamily, @@ -1431,10 +1441,13 @@ gfxFcFontSet::SortPreferredFonts(PRBool &aWaitForUserFont) gfxFontconfigUtils::FcSlantToThebesStyle(requestedSlant); PRUint16 thebesWeight = gfxFontconfigUtils::GetThebesWeight(mSortPattern); + PRInt16 thebesStretch = + gfxFontconfigUtils::GetThebesStretch(mSortPattern); PRBool foundFamily, waitForUserFont; familyFonts = FindFontPatterns(mUserFontSet, cssFamily, - thebesStyle, thebesWeight, + thebesStyle, + thebesWeight, thebesStretch, foundFamily, waitForUserFont); if (waitForUserFont) { aWaitForUserFont = PR_TRUE; 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/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index ef8f2b7b4a5c..581e83869ce4 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -155,20 +155,20 @@ void GetPathToBinary(FilePath& exePath) nsCString path; greDir->GetNativePath(path); exePath = FilePath(path.get()); +#ifdef OS_MACOSX + // We need to use an App Bundle on OS X so that we can hide + // the dock icon. See Bug 557225. + exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE); +#endif } } } + if (exePath.empty()) { exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]); exePath = exePath.DirName(); } -#ifdef OS_MACOSX - // We need to use an App Bundle on OS X so that we can hide - // the dock icon. See Bug 557225 - exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE); -#endif - exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME); #endif } diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4 index 6b0a411d8ca5..144a8641298a 100644 --- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -11,5 +11,6 @@ builtin(include, build/autoconf/mozprog.m4)dnl builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl +builtin(include, build/autoconf/frameptr.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/js/src/assembler/assembler/AssemblerBuffer.h b/js/src/assembler/assembler/AssemblerBuffer.h index ee759105a412..7107e935559a 100644 --- a/js/src/assembler/assembler/AssemblerBuffer.h +++ b/js/src/assembler/assembler/AssemblerBuffer.h @@ -191,7 +191,11 @@ namespace JSC { void grow(int extraCapacity = 0) { - int newCapacity = m_capacity + m_capacity / 2 + extraCapacity; + /* + * If |extraCapacity| is zero (as it almost always is) this is an + * allocator-friendly doubling growth strategy. + */ + int newCapacity = m_capacity + m_capacity + extraCapacity; char* newBuffer; if (m_buffer == m_inlineBuffer) { diff --git a/js/src/assembler/assembler/SparcAssembler.h b/js/src/assembler/assembler/SparcAssembler.h index e44a23e52c56..4457c62f7238 100644 --- a/js/src/assembler/assembler/SparcAssembler.h +++ b/js/src/assembler/assembler/SparcAssembler.h @@ -1116,19 +1116,24 @@ namespace JSC { static void relinkCall(void* from, void* to) { js::JaegerSpew(js::JSpew_Insns, - ISPFX "##linkCall ((from=%p)) ((to=%p))\n", + ISPFX "##relinkCall ((from=%p)) ((to=%p))\n", from, to); - int disp = ((int)to - (int)from)/4; - *(uint32_t *)((int)from) &= 0x40000000; - *(uint32_t *)((int)from) |= disp & 0x3fffffff; - ExecutableAllocator::cacheFlush(from, 4); + void * where= (void *)((intptr_t)from - 20); + patchPointerInternal(where, (int)to); + ExecutableAllocator::cacheFlush(where, 8); } static void linkCall(void* code, JmpSrc where, void* to) { void *from = (void *)((intptr_t)code + where.m_offset); - relinkCall(from, to); + js::JaegerSpew(js::JSpew_Insns, + ISPFX "##linkCall ((from=%p)) ((to=%p))\n", + from, to); + int disp = ((int)to - (int)from)/4; + *(uint32_t *)((int)from) &= 0x40000000; + *(uint32_t *)((int)from) |= disp & 0x3fffffff; + ExecutableAllocator::cacheFlush(from, 4); } static void linkPointer(void* code, JmpDst where, void* value) diff --git a/js/src/build/autoconf/frameptr.m4 b/js/src/build/autoconf/frameptr.m4 new file mode 100644 index 000000000000..77a6d71aedc8 --- /dev/null +++ b/js/src/build/autoconf/frameptr.m4 @@ -0,0 +1,25 @@ +dnl Set MOZ_FRAMEPTR_FLAGS to the flags that should be used for enabling or +dnl disabling frame pointers in this architecture based on the configure +dnl options + +AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [ + if test "$GNU_CC"; then + MOZ_ENABLE_FRAME_PTR="-fno-omit-frame-pointer" + MOZ_DISABLE_FRAME_PTR="-fomit-frame-pointer" + else + case "$target" in + *-mingw*) + MOZ_ENABLE_FRAME_PTR="-Oy-" + MOZ_DISABLE_FRAME_PTR="-Oy" + ;; + esac + fi + + # if we are debugging or profiling, we want a frame pointer. + if test -z "$MOZ_OPTIMIZE" -o \ + -n "$MOZ_PROFILING" -o -n "$MOZ_DEBUG"; then + MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR" + else + MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR" + fi +]) diff --git a/js/src/config/autoconf.mk.in b/js/src/config/autoconf.mk.in index 6eb14b482455..f76e27585984 100644 --- a/js/src/config/autoconf.mk.in +++ b/js/src/config/autoconf.mk.in @@ -143,6 +143,7 @@ WARNINGS_AS_ERRORS = @WARNINGS_AS_ERRORS@ FAIL_ON_WARNINGS = @FAIL_ON_WARNINGS@ MOZ_OPTIMIZE = @MOZ_OPTIMIZE@ +MOZ_FRAMEPTR_FLAGS = @MOZ_FRAMEPTR_FLAGS@ MOZ_OPTIMIZE_FLAGS = @MOZ_OPTIMIZE_FLAGS@ MOZ_PGO_OPTIMIZE_FLAGS = @MOZ_PGO_OPTIMIZE_FLAGS@ MOZ_OPTIMIZE_LDFLAGS = @MOZ_OPTIMIZE_LDFLAGS@ diff --git a/js/src/config/config.mk b/js/src/config/config.mk index 162cdaec2a7d..8d714411492e 100644 --- a/js/src/config/config.mk +++ b/js/src/config/config.mk @@ -456,6 +456,9 @@ endif # MOZ_OPTIMIZE == 1 endif # MOZ_OPTIMIZE endif # CROSS_COMPILE +CFLAGS += $(MOZ_FRAMEPTR_FLAGS) +CXXFLAGS += $(MOZ_FRAMEPTR_FLAGS) + # Check for FAIL_ON_WARNINGS & FAIL_ON_WARNINGS_DEBUG (Shorthand for Makefiles # to request that we use the 'warnings as errors' compile flags) diff --git a/js/src/configure.in b/js/src/configure.in index 7ee0053208bf..c347a12fb901 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1857,13 +1857,7 @@ case "$target" in *-darwin*) MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_FRAMEPTR_FLAGS="-fomit-frame-pointer" - else - MOZ_FRAMEPTR_FLAGS="-fno-omit-frame-pointer" - fi - MOZ_OPTIMIZE_FLAGS="-O3 $MOZ_FRAMEPTR_FLAGS -fno-stack-protector" + MOZ_OPTIMIZE_FLAGS="-O3 -fno-stack-protector" _PEDANTIC= CFLAGS="$CFLAGS -fpascal-strings -fno-common" CXXFLAGS="$CXXFLAGS -fpascal-strings -fno-common" @@ -1968,13 +1962,7 @@ ia64*-hpux*) TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"' MOZ_GFX_OPTIMIZE_MOBILE=1 - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_FRAMEPTR_FLAGS="-fomit-frame-pointer" - else - MOZ_FRAMEPTR_FLAGS="-fno-omit-frame-pointer" - fi - MOZ_OPTIMIZE_FLAGS="-O3 -freorder-blocks -fno-reorder-functions $MOZ_FRAMEPTR_FLAGS" + MOZ_OPTIMIZE_FLAGS="-O3 -freorder-blocks -fno-reorder-functions" # The Maemo builders don't know about this flag MOZ_ARM_VFP_FLAGS="-mfpu=vfp" ;; @@ -1994,14 +1982,8 @@ ia64*-hpux*) # -Os is broken on gcc 4.1.x 4.2.x, 4.5.x we need to tweak it to get good results. MOZ_OPTIMIZE_SIZE_TWEAK="-finline-limit=50" esac - # If we're building with --enable-profiling, we need a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_FRAMEPTR_FLAGS="-fomit-frame-pointer" - else - MOZ_FRAMEPTR_FLAGS="-fno-omit-frame-pointer" - fi - MOZ_PGO_OPTIMIZE_FLAGS="-O3 $MOZ_FRAMEPTR_FLAGS" - MOZ_OPTIMIZE_FLAGS="-O3 -freorder-blocks $MOZ_OPTIMIZE_SIZE_TWEAK $MOZ_FRAMEPTR_FLAGS" + MOZ_PGO_OPTIMIZE_FLAGS="-O3" + MOZ_OPTIMIZE_FLAGS="-O3 -freorder-blocks $MOZ_OPTIMIZE_SIZE_TWEAK" MOZ_DEBUG_FLAGS="-g" fi @@ -2094,13 +2076,7 @@ ia64*-hpux*) MOZ_DEBUG_FLAGS='-Zi' MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' WARNINGS_AS_ERRORS='-WX' - # If we're building with --enable-profiling, we need -Oy-, which forces a frame pointer. - if test -z "$MOZ_PROFILING"; then - MOZ_FRAMEPTR_FLAGS= - else - MOZ_FRAMEPTR_FLAGS='-Oy-' - fi - MOZ_OPTIMIZE_FLAGS="-O2 $MOZ_FRAMEPTR_FLAGS" + MOZ_OPTIMIZE_FLAGS="-O2" MOZ_JS_LIBS='$(libdir)/mozjs.lib' MOZ_FIX_LINK_PATHS= DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib $(LIBXUL_DIST)/lib/mozalloc.lib' @@ -4200,6 +4176,8 @@ else MOZ_OPTIMIZE= fi ], MOZ_OPTIMIZE=1) +MOZ_SET_FRAMEPTR_FLAGS + if test "$COMPILE_ENVIRONMENT"; then if test -n "$MOZ_OPTIMIZE"; then AC_MSG_CHECKING([for valid optimization flags]) @@ -4218,6 +4196,7 @@ fi fi # COMPILE_ENVIRONMENT AC_SUBST(MOZ_OPTIMIZE) +AC_SUBST(MOZ_FRAMEPTR_FLAGS) AC_SUBST(MOZ_OPTIMIZE_FLAGS) AC_SUBST(MOZ_OPTIMIZE_LDFLAGS) AC_SUBST(MOZ_OPTIMIZE_SIZE_TWEAK) diff --git a/js/src/jit-test/tests/basic/bug684348.js b/js/src/jit-test/tests/basic/bug684348.js new file mode 100644 index 000000000000..58c064fc4a4b --- /dev/null +++ b/js/src/jit-test/tests/basic/bug684348.js @@ -0,0 +1,4 @@ +var x = Proxy.create({ fix: function() { return []; } }); +Object.__proto__ = x; +Object.freeze(x); +quit(); diff --git a/js/src/jit-test/tests/basic/bug684796.js b/js/src/jit-test/tests/basic/bug684796.js new file mode 100644 index 000000000000..f541abdf7fc2 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug684796.js @@ -0,0 +1,2 @@ +if (typeof mjitdatastats == "function") + mjitdatastats(); diff --git a/js/src/jit-test/tests/jaeger/bug684084.js b/js/src/jit-test/tests/jaeger/bug684084.js new file mode 100644 index 000000000000..ac2c11a38446 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/bug684084.js @@ -0,0 +1,7 @@ +// |jit-test| error: TypeError +function Integer( value, exception ) { + try { } catch ( e ) { } + new (value = this)( this.value ); + if ( Math.floor(value) != value || isNaN(value) ) { } +} +new Integer( 3, false ); diff --git a/js/src/jit-test/tests/jaeger/bug684576.js b/js/src/jit-test/tests/jaeger/bug684576.js new file mode 100644 index 000000000000..b845d2160d22 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/bug684576.js @@ -0,0 +1,10 @@ +// |jit-test| error: TypeError +function f0(p0,p1) { + var v3; + do { + p1 > v3 + v3=1.7 + } while (((p0[p1][5]==1)||(p0[p1][5]==2)||(p0[p1][5] == 3)) + 0 > p0); + + (v3(f0)); +} +f0(4105,8307); diff --git a/js/src/jit-test/tests/jaeger/closure-01.js b/js/src/jit-test/tests/jaeger/closure-01.js new file mode 100644 index 000000000000..4c82b6fd350e --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-01.js @@ -0,0 +1,18 @@ + +/* Non-reentrant call on an inner and outer closure. */ + +function foo() { + var x = 0; + function bar() { + var y = 0; + function baz() { + return ++x + ++y; + } + return baz; + } + return bar(); +} + +var a = foo(); +var b = foo(); +assertEq(a() + a() + b() + b(), 12); diff --git a/js/src/jit-test/tests/jaeger/closure-02.js b/js/src/jit-test/tests/jaeger/closure-02.js new file mode 100644 index 000000000000..902c8f5ae78e --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-02.js @@ -0,0 +1,14 @@ + +/* Non-reentrant closure used in an invoke session. */ + +var last = null; + +var a = [1,2,3,4,5,6,7,8]; +var b = a.map(function(x) { + x++; + var res = last ? last() : 0; + last = function() { return x; }; + return res; + }); + +assertEq("" + b, "0,2,3,4,5,6,7,8"); diff --git a/js/src/jit-test/tests/jaeger/closure-03.js b/js/src/jit-test/tests/jaeger/closure-03.js new file mode 100644 index 000000000000..e2d1ebe3f85c --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-03.js @@ -0,0 +1,15 @@ + +/* Recovering non-reentrant information on singletons after a GC. */ + +function foo(a) { + return function() { + gc(); + var n = 0; + for (var i = 0; i < 20; i++) + n = a++; + assertEq(n, 29); + }; +} +var a = foo(10); +var b = foo(20); +a(); diff --git a/js/src/jit-test/tests/jaeger/closure-04.js b/js/src/jit-test/tests/jaeger/closure-04.js new file mode 100644 index 000000000000..72b188f03b23 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-04.js @@ -0,0 +1,23 @@ +test(); +function test() { + var catch1, catch2, catch3, finally1, finally2, finally3; + function gen() { + yield 1; + try { + try { + try { + yield 1; + } finally { + test(); + } + } catch (e) { + finally2 = true; + } + } catch (e) { } + } + iter = gen(); + iter.next(); + iter.next(); + iter.close(); + gc(); +} diff --git a/js/src/jit-test/tests/jaeger/closure-05.js b/js/src/jit-test/tests/jaeger/closure-05.js new file mode 100644 index 000000000000..b2aeffa3650f --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-05.js @@ -0,0 +1,17 @@ +var gTestcases = new Array(); +var gTc = gTestcases.length; +function TestCase(n, d, e, a) { + gTestcases[gTc++] = this; +} +new TestCase("SECTION", "with MyObject, eval should return square of "); +test(); +function test() { + for (gTc = 0; gTc < gTestcases.length; gTc++) { + var MYOBJECT = (function isPrototypeOf(message) { + delete input; + })(); + with({}) { + gTestcases[gTc].actual = eval(""); + } + } +} diff --git a/js/src/jit-test/tests/jaeger/loops/bug684621.js b/js/src/jit-test/tests/jaeger/loops/bug684621.js new file mode 100644 index 000000000000..9ca13bc7f4c5 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/loops/bug684621.js @@ -0,0 +1,15 @@ +function runRichards() { + queue = new Packet; + Packet(queue, ID_DEVICE_A, KIND_DEVICE); + new Packet; +} +var ID_DEVICE_A = 4; +var KIND_DEVICE = 0; +Packet = function (queue) { + this.link = null + if (queue == null) return; + var peek, next = queue; + while ((peek = next.link) != null) + ID_HANDLER_B +}; +runRichards() diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index f0f237d2d8a7..09e7a838b3e3 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -342,7 +342,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) * any safe point. */ if (cx->compartment->debugMode()) - usesRval = true; + usesReturnValue_ = true; isInlineable = true; if (script->nClosedArgs || script->nClosedVars || script->nfixed >= LOCAL_LIMIT || @@ -498,7 +498,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_SETRVAL: case JSOP_POPV: - usesRval = true; + usesReturnValue_ = true; isInlineable = false; break; @@ -510,7 +510,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_QNAMEPART: case JSOP_QNAMECONST: checkAliasedName(cx, pc); - usesScope = true; + usesScopeChain_ = true; isInlineable = false; break; @@ -519,20 +519,34 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_DEFCONST: case JSOP_SETCONST: checkAliasedName(cx, pc); - /* FALLTHROUGH */ - - case JSOP_ENTERWITH: + extendsScope_ = true; isInlineable = canTrackVars = false; break; + case JSOP_EVAL: + extendsScope_ = true; + isInlineable = canTrackVars = false; + break; + + case JSOP_ENTERWITH: + addsScopeObjects_ = true; + isInlineable = canTrackVars = false; + break; + + case JSOP_ENTERBLOCK: + case JSOP_LEAVEBLOCK: + addsScopeObjects_ = true; + isInlineable = false; + break; + case JSOP_THIS: - usesThis = true; + usesThisValue_ = true; break; case JSOP_CALL: case JSOP_NEW: /* Only consider potentially inlineable calls here. */ - hasCalls = true; + hasFunctionCalls_ = true; break; case JSOP_TABLESWITCH: @@ -717,7 +731,6 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) /* Additional opcodes which can be compiled but which can't be inlined. */ case JSOP_ARGUMENTS: - case JSOP_EVAL: case JSOP_THROW: case JSOP_EXCEPTION: case JSOP_DEFLOCALFUN: @@ -729,8 +742,6 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_ARGSUB: case JSOP_ARGCNT: case JSOP_DEBUGGER: - case JSOP_ENTERBLOCK: - case JSOP_LEAVEBLOCK: case JSOP_FUNCALL: case JSOP_FUNAPPLY: isInlineable = false; @@ -905,9 +916,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) ensureVariable(lifetimes[i], startOffset - 1); } -#ifdef DEBUG found = true; -#endif break; } } diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 2b2500ab759f..5570310b3466 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -886,15 +886,18 @@ class ScriptAnalysis /* --------- Bytecode analysis --------- */ - bool usesRval; - bool usesScope; - bool usesThis; - bool hasCalls; - bool canTrackVars; - bool isInlineable; + bool usesReturnValue_:1; + bool usesScopeChain_:1; + bool usesThisValue_:1; + bool hasFunctionCalls_:1; + bool modifiesArguments_:1; + bool extendsScope_:1; + bool addsScopeObjects_:1; + bool localsAliasStack_:1; + bool isInlineable:1; + bool canTrackVars:1; + uint32 numReturnSites_; - bool modifiesArguments_; - bool localsAliasStack_; /* Offsets at which each local becomes unconditionally defined, or a value below. */ uint32 *definedLocals; @@ -928,13 +931,13 @@ class ScriptAnalysis bool inlineable(uint32 argc) { return isInlineable && argc == script->function()->nargs; } /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */ - bool usesReturnValue() const { return usesRval; } + bool usesReturnValue() const { return usesReturnValue_; } /* Whether there are NAME bytecodes which can access the frame's scope chain. */ - bool usesScopeChain() const { return usesScope; } + bool usesScopeChain() const { return usesScopeChain_; } - bool usesThisValue() const { return usesThis; } - bool hasFunctionCalls() const { return hasCalls; } + bool usesThisValue() const { return usesThisValue_; } + bool hasFunctionCalls() const { return hasFunctionCalls_; } uint32 numReturnSites() const { return numReturnSites_; } /* @@ -943,6 +946,15 @@ class ScriptAnalysis */ bool modifiesArguments() { return modifiesArguments_; } + /* + * True if the script may extend declarations in its top level scope with + * dynamic fun/var declarations or through eval. + */ + bool extendsScope() { return extendsScope_; } + + /* True if the script may add block or with objects to its scope chain. */ + bool addsScopeObjects() { return addsScopeObjects_; } + /* * True if there are any LOCAL opcodes aliasing values on the stack (above * script->nfixed). @@ -1168,6 +1180,21 @@ class ScriptAnalysis return lifetimes[slot]; } + /* + * If a NAME or similar opcode is definitely accessing a particular slot + * of a script this one is nested in, get that script/slot. + */ + struct NameAccess { + JSScript *script; + types::TypeScriptNesting *nesting; + uint32 slot; + + /* Decompose the slot above. */ + bool arg; + uint32 index; + }; + NameAccess resolveNameAccess(JSContext *cx, jsid id, bool addDependency = false); + void printSSA(JSContext *cx); void printTypes(JSContext *cx); diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index 4e57b175927e..98a251841c56 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -65,7 +65,7 @@ CPPSRCS = \ testExternalStrings.cpp \ testFuncCallback.cpp \ testFunctionProperties.cpp \ - testGCChunkAlloc.cpp \ + testGCOutOfMemory.cpp \ testGetPropertyDefault.cpp \ testIndexToString.cpp \ testIntString.cpp \ diff --git a/js/src/jsapi-tests/testConservativeGC.cpp b/js/src/jsapi-tests/testConservativeGC.cpp index a01024b978fc..0286060b157a 100644 --- a/js/src/jsapi-tests/testConservativeGC.cpp +++ b/js/src/jsapi-tests/testConservativeGC.cpp @@ -49,7 +49,7 @@ bool checkObjectFields(JSObject *savedCopy, JSObject *obj) { /* Ignore fields which are unstable across GCs. */ CHECK(savedCopy->lastProp == obj->lastProp); - CHECK(savedCopy->clasp == obj->clasp); + CHECK(savedCopy->getClass() == obj->getClass()); CHECK(savedCopy->flags == obj->flags); CHECK(savedCopy->newType == obj->newType); CHECK(savedCopy->getProto() == obj->getProto()); diff --git a/js/src/jsapi-tests/testGCChunkAlloc.cpp b/js/src/jsapi-tests/testGCChunkAlloc.cpp deleted file mode 100644 index d126c1d53b81..000000000000 --- a/js/src/jsapi-tests/testGCChunkAlloc.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: - * - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Igor Bukanov - */ - -#include "tests.h" -#include "jsgcchunk.h" -#include "jscntxt.h" - -/* We allow to allocate 2 (system/user) chunks. */ - -static const int SYSTEM = 0; -static const int USER = 1; -static const int N_POOLS = 2; - -class CustomGCChunkAllocator: public js::GCChunkAllocator { - public: - CustomGCChunkAllocator() { pool[SYSTEM] = NULL; pool[USER] = NULL; } - void *pool[N_POOLS]; - - private: - - virtual void *doAlloc() { - if (!pool[SYSTEM] && !pool[USER]) - return NULL; - void *chunk = NULL; - if (pool[SYSTEM]) { - chunk = pool[SYSTEM]; - pool[SYSTEM] = NULL; - } else { - chunk = pool[USER]; - pool[USER] = NULL; - } - return chunk; - } - - virtual void doFree(void *chunk) { - JS_ASSERT(!pool[SYSTEM] || !pool[USER]); - if (!pool[SYSTEM]) { - pool[SYSTEM] = chunk; - } else { - pool[USER] = chunk; - } - } -}; - -static CustomGCChunkAllocator customGCChunkAllocator; - -static unsigned errorCount = 0; - -static void -ErrorCounter(JSContext *cx, const char *message, JSErrorReport *report) -{ - ++errorCount; -} - -BEGIN_TEST(testGCChunkAlloc) -{ - JS_SetErrorReporter(cx, ErrorCounter); - - jsvalRoot root(cx); - - /* - * We loop until out-of-memory happens during the chunk allocation. But - * we have to disable the jit since it cannot tolerate OOM during the - * chunk allocation. - */ - JS_ToggleOptions(cx, JSOPTION_JIT); - - static const char source[] = - "var max = 0; (function() {" - " var array = [];" - " for (; ; ++max)" - " array.push({});" - "})();"; - JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1, - root.addr()); - - /* Check that we get OOM. */ - CHECK(!ok); - CHECK(!JS_IsExceptionPending(cx)); - CHECK_EQUAL(errorCount, 1); - CHECK(!customGCChunkAllocator.pool[SYSTEM]); - CHECK(!customGCChunkAllocator.pool[USER]); - JS_GC(cx); - JS_ToggleOptions(cx, JSOPTION_JIT); - EVAL("(function() {" - " var array = [];" - " for (var i = max >> 1; i != 0;) {" - " --i;" - " array.push({});" - " }" - "})();", root.addr()); - CHECK_EQUAL(errorCount, 1); - return true; -} - -virtual JSRuntime * createRuntime() { - /* - * To test failure of chunk allocation allow to use GC twice the memory - * the single chunk contains. - */ - JSRuntime *rt = JS_NewRuntime(2 * js::GC_CHUNK_SIZE); - if (!rt) - return NULL; - - customGCChunkAllocator.pool[SYSTEM] = js::AllocGCChunk(); - customGCChunkAllocator.pool[USER] = js::AllocGCChunk(); - JS_ASSERT(customGCChunkAllocator.pool[SYSTEM]); - JS_ASSERT(customGCChunkAllocator.pool[USER]); - - rt->setCustomGCChunkAllocator(&customGCChunkAllocator); - return rt; -} - -virtual void destroyRuntime() { - JS_DestroyRuntime(rt); - - /* We should get the initial chunk back at this point. */ - JS_ASSERT(customGCChunkAllocator.pool[SYSTEM]); - JS_ASSERT(customGCChunkAllocator.pool[USER]); - js::FreeGCChunk(customGCChunkAllocator.pool[SYSTEM]); - js::FreeGCChunk(customGCChunkAllocator.pool[USER]); - customGCChunkAllocator.pool[SYSTEM] = NULL; - customGCChunkAllocator.pool[USER] = NULL; -} - -END_TEST(testGCChunkAlloc) diff --git a/js/src/jsapi-tests/testGCOutOfMemory.cpp b/js/src/jsapi-tests/testGCOutOfMemory.cpp new file mode 100644 index 000000000000..f737bb4cbcdb --- /dev/null +++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Igor Bukanov + */ + +#include "tests.h" +#include "jsgcchunk.h" +#include "jscntxt.h" + +static unsigned errorCount = 0; + +static void +ErrorCounter(JSContext *cx, const char *message, JSErrorReport *report) +{ + ++errorCount; +} + +BEGIN_TEST(testGCOutOfMemory) +{ + JS_SetErrorReporter(cx, ErrorCounter); + + jsvalRoot root(cx); + + /* + * We loop until we get out-of-memory. We have to disable the jit since it + * ignores the runtime allocation limits during execution. + */ + JS_ToggleOptions(cx, JSOPTION_JIT); + + static const char source[] = + "var max = 0; (function() {" + " var array = [];" + " for (; ; ++max)" + " array.push({});" + " array = []; array.push(0);" + "})();"; + JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1, + root.addr()); + + /* Check that we get OOM. */ + CHECK(!ok); + CHECK(!JS_IsExceptionPending(cx)); + CHECK_EQUAL(errorCount, 1); + JS_GC(cx); + JS_ToggleOptions(cx, JSOPTION_JIT); + EVAL("(function() {" + " var array = [];" + " for (var i = max >> 2; i != 0;) {" + " --i;" + " array.push({});" + " }" + "})();", root.addr()); + CHECK_EQUAL(errorCount, 1); + return true; +} + +virtual JSRuntime * createRuntime() { + return JS_NewRuntime(256 * 1024); +} + +virtual void destroyRuntime() { + JS_DestroyRuntime(rt); +} + +END_TEST(testGCOutOfMemory) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8a0d245ba05f..e740e8c9d07b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -638,8 +638,7 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun) static JSBool js_NewRuntimeWasCalled = JS_FALSE; JSRuntime::JSRuntime() - : gcChunkAllocator(&defaultGCChunkAllocator), - trustedPrincipals_(NULL) + : trustedPrincipals_(NULL) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ JS_INIT_CLIST(&contextList); @@ -1543,7 +1542,7 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) return obj->asGlobal()->initStandardClasses(cx); } -#define CLASP(name) (&js_##name##Class) +#define CLASP(name) (&name##Class) #define TYPED_ARRAY_CLASP(type) (&TypedArray::fastClasses[TypedArray::type]) #define EAGER_ATOM(name) ATOM_OFFSET(name), NULL #define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL @@ -1653,7 +1652,7 @@ static JSStdName standard_class_names[] = { #endif /* Typed Arrays */ - {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js_ArrayBufferClass}, + {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &ArrayBufferClass}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int8Array), TYPED_ARRAY_CLASP(TYPE_INT8)}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8Array), TYPED_ARRAY_CLASP(TYPE_UINT8)}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int16Array), TYPED_ARRAY_CLASP(TYPE_INT16)}, @@ -2275,7 +2274,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, { JSObject *obj = (JSObject *)thing; Class *clasp = obj->getClass(); - if (clasp == &js_FunctionClass) { + if (clasp == &FunctionClass) { JSFunction *fun = obj->getFunctionPrivate(); if (!fun) { JS_snprintf(buf, bufsize, ""); @@ -3064,9 +3063,9 @@ JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent) Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ - JS_ASSERT(clasp != &js_FunctionClass); + JS_ASSERT(clasp != &FunctionClass); JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); if (proto) @@ -3093,9 +3092,9 @@ JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSO Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ - JS_ASSERT(clasp != &js_FunctionClass); + JS_ASSERT(clasp != &FunctionClass); JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); JSObject *obj = NewNonFunction(cx, clasp, proto, parent); @@ -3168,7 +3167,7 @@ JS_ConstructObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *p assertSameCompartment(cx, proto, parent); Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); } @@ -3180,7 +3179,7 @@ JS_ConstructObjectWithArguments(JSContext *cx, JSClass *jsclasp, JSObject *proto assertSameCompartment(cx, proto, parent, JSValueArray(argv, argc)); Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ return js_ConstructObject(cx, clasp, proto, parent, argc, Valueify(argv)); } @@ -3520,7 +3519,7 @@ JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *jsclasp Class *clasp = Valueify(jsclasp); if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ + clasp = &ObjectClass; /* default class is Object */ JSObject *nobj = NewObject(cx, clasp, proto, obj); if (!nobj) @@ -4215,7 +4214,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) JS_ASSERT(parent); } - if (funobj->getClass() != &js_FunctionClass) { + if (!funobj->isFunction()) { /* * We cannot clone this object, so fail (we used to return funobj, bad * idea, but we changed incompatibly to teach any abusers a lesson!). @@ -4306,7 +4305,7 @@ JS_GetFunctionArity(JSFunction *fun) JS_PUBLIC_API(JSBool) JS_ObjectIsFunction(JSContext *cx, JSObject *obj) { - return obj->getClass() == &js_FunctionClass; + return obj->isFunction(); } JS_PUBLIC_API(JSBool) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3ac30aabdc5e..58c98a40799f 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -44,7 +44,7 @@ * Array objects begin as "dense" arrays, optimized for index-only property * access over a vector of slots with high load factor. Array methods * optimize for denseness by testing that the object's class is - * &js_ArrayClass, and can then directly manipulate the slots for efficiency. + * &ArrayClass, and can then directly manipulate the slots for efficiency. * * We track these pieces of metadata for arrays in dense mode: * - The array's length property as a uint32, accessible with @@ -81,7 +81,7 @@ * known to have no hole values below its initialized length, then it is a * "packed" array and can be accessed much faster by JIT code. * - * Arrays are converted to use js_SlowArrayClass when any of these conditions + * Arrays are converted to use SlowArrayClass when any of these conditions * are met: * - there are more than MIN_SPARSE_INDEX slots total and the load factor * (COUNT / capacity) is less than 0.25 @@ -93,7 +93,7 @@ * properties in the order they were created. We could instead maintain the * scope to track property enumeration order, but still use the fast slot * access. That would have the same memory cost as just using a - * js_SlowArrayClass, but have the same performance characteristics as a dense + * SlowArrayClass, but have the same performance characteristics as a dense * array for slot accesses, at some cost in code complexity. */ #include @@ -146,13 +146,6 @@ using namespace js; using namespace js::gc; using namespace js::types; -static inline bool -ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj) -{ - return obj->getClass() == &js_SlowArrayClass || - obj->makeDenseArraySlow(cx); -} - JSBool js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { @@ -246,14 +239,11 @@ static JSBool BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, jsid *idp) { - jschar buf[10], *start; - Class *clasp; - JSAtom *atom; JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); - JS_ASSERT(index > JSID_INT_MAX); - start = JS_ARRAY_END(buf); + jschar buf[10]; + jschar *start = JS_ARRAY_END(buf); do { --start; *start = (jschar)('0' + index % 10); @@ -264,14 +254,11 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, * Skip the atomization if the class is known to store atoms corresponding * to big indexes together with elements. In such case we know that the * array does not have an element at the given index if its atom does not - * exist. Fast arrays (clasp == &js_ArrayClass) don't use atoms for - * any indexes, though it would be rare to see them have a big index - * in any case. + * exist. Dense arrays don't use atoms for any indexes, though it would be + * rare to see them have a big index in any case. */ - if (!createAtom && - ((clasp = obj->getClass()) == &js_SlowArrayClass || - obj->isArguments() || - clasp == &js_ObjectClass)) { + JSAtom *atom; + if (!createAtom && (obj->isSlowArray() || obj->isArguments() || obj->isObject())) { atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); if (!atom) { *idp = JSID_VOID; @@ -501,13 +488,13 @@ JSBool JS_FASTCALL js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i) { #ifdef DEBUG - Class *origObjClasp = obj->clasp; + Class *origObjClasp = obj->getClass(); #endif jsuint u = jsuint(i); JSBool ret = (obj->ensureDenseArrayElements(cx, u, 1) == JSObject::ED_OK); /* Partially check the CallInfo's storeAccSet is correct. */ - JS_ASSERT(obj->clasp == origObjClasp); + JS_ASSERT(obj->getClass() == origObjClasp); return ret; } /* This function and its callees do not touch any object's .clasp field. */ @@ -989,7 +976,7 @@ array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props) return true; } -Class js_ArrayClass = { +Class js::ArrayClass = { "Array", Class::NON_NATIVE | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), PropertyStub, /* addProperty */ @@ -1024,7 +1011,7 @@ Class js_ArrayClass = { } }; -Class js_SlowArrayClass = { +Class js::SlowArrayClass = { "Array", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), @@ -1068,8 +1055,8 @@ JSObject::makeDenseArraySlow(JSContext *cx) js::Shape *oldMap = lastProp; /* Create a native scope. */ - js::gc::FinalizeKind kind = js::gc::FinalizeKind(arenaHeader()->getThingKind()); - if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getProto()->getNewType(cx), kind)) + gc::AllocKind kind = getAllocKind(); + if (!InitScopeForObject(cx, this, &SlowArrayClass, getProto()->getNewType(cx), kind)) return false; backfillDenseArrayHoles(cx); @@ -1090,7 +1077,7 @@ JSObject::makeDenseArraySlow(JSContext *cx) JS_ASSERT(!denseArrayHasInlineSlots()); } capacity = numFixedSlots() + arrayCapacity; - clasp = &js_SlowArrayClass; + clasp = &SlowArrayClass; /* * Root all values in the array during conversion, as SlowArrayClass only @@ -1110,7 +1097,7 @@ JSObject::makeDenseArraySlow(JSContext *cx) setMap(oldMap); capacity = arrayCapacity; initializedLength = arrayInitialized; - clasp = &js_ArrayClass; + clasp = &ArrayClass; return false; } @@ -1133,7 +1120,7 @@ JSObject::makeDenseArraySlow(JSContext *cx) setMap(oldMap); capacity = arrayCapacity; initializedLength = arrayInitialized; - clasp = &js_ArrayClass; + clasp = &ArrayClass; return false; } @@ -1144,7 +1131,7 @@ JSObject::makeDenseArraySlow(JSContext *cx) /* * Finally, update class. If |this| is Array.prototype, then js_InitClass - * will create an emptyShape whose class is &js_SlowArrayClass, to ensure + * will create an emptyShape whose class is &SlowArrayClass, to ensure * that delegating instances can share shapes in the tree rooted at the * proto's empty shape. */ @@ -1211,7 +1198,7 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) if (!obj) return false; if (!obj->isArray()) { - ReportIncompatibleMethod(cx, vp, &js_ArrayClass); + ReportIncompatibleMethod(cx, vp, &ArrayClass); return false; } @@ -1540,7 +1527,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu return JS_TRUE; /* Finish out any remaining elements past the max array index. */ - if (obj->isDenseArray() && !ENSURE_SLOW_ARRAY(cx, obj)) + if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx)) return JS_FALSE; JS_ASSERT(start == MAX_ARRAY_INDEX + 1); @@ -1849,10 +1836,11 @@ js_MergeSort(void *src, size_t nel, size_t elsize, struct CompareArgs { JSContext *context; - InvokeSessionGuard session; + InvokeArgsGuard args; + Value fval; - CompareArgs(JSContext *cx) - : context(cx) + CompareArgs(JSContext *cx, Value fval) + : context(cx), fval(fval) {} }; @@ -1873,15 +1861,21 @@ sort_compare(void *arg, const void *a, const void *b, int *result) if (!JS_CHECK_OPERATION_LIMIT(cx)) return JS_FALSE; - InvokeSessionGuard &session = ca->session; - session[0] = *av; - session[1] = *bv; + InvokeArgsGuard &args = ca->args; + if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &args)) + return JS_FALSE; + + args.calleeHasBeenReset(); + args.calleev() = ca->fval; + args.thisv() = UndefinedValue(); + args[0] = *av; + args[1] = *bv; - if (!session.invoke(cx)) + if (!Invoke(cx, args)) return JS_FALSE; jsdouble cmp; - if (!ToNumber(cx, session.rval(), &cmp)) + if (!ToNumber(cx, args.rval(), &cmp)) return JS_FALSE; /* Clamp cmp to -1, 0, 1. */ @@ -2118,10 +2112,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) } while (++i != newlen); } } else { - CompareArgs ca(cx); - if (!ca.session.start(cx, fval, UndefinedValue(), 2)) - return false; - + CompareArgs ca(cx, fval); if (!js_MergeSort(vec, size_t(newlen), sizeof(Value), comparator_stack_cast(sort_compare), &ca, mergesort_tmp, @@ -2975,16 +2966,13 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) */ argc = 3 + REDUCE_MODE(mode); - InvokeSessionGuard session; - if (!session.start(cx, ObjectValue(*callable), thisv, argc)) - return JS_FALSE; - MUST_FLOW_THROUGH("out"); JSBool ok = JS_TRUE; JSBool cond; Value objv = ObjectValue(*obj); AutoValueRooter tvr(cx); + InvokeArgsGuard args; for (jsuint i = start; i != end; i += step) { JSBool hole; ok = JS_CHECK_OPERATION_LIMIT(cx) && @@ -2994,23 +2982,29 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) if (hole) continue; + if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args)) + return false; + /* * Push callable and 'this', then args. We must do this for every * iteration around the loop since Invoke clobbers its arguments. */ + args.calleeHasBeenReset(); + args.calleev() = ObjectValue(*callable); + args.thisv() = thisv; uintN argi = 0; if (REDUCE_MODE(mode)) - session[argi++] = *vp; - session[argi++] = tvr.value(); - session[argi++] = Int32Value(i); - session[argi] = objv; + args[argi++] = *vp; + args[argi++] = tvr.value(); + args[argi++] = Int32Value(i); + args[argi] = objv; /* Do the call. */ - ok = session.invoke(cx); + ok = Invoke(cx, args); if (!ok) break; - const Value &rval = session.rval(); + const Value &rval = args.rval(); if (mode > MAP) cond = js_ValueToBoolean(rval); @@ -3217,12 +3211,12 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) GlobalObject *global = obj->asGlobal(); - JSObject *arrayProto = global->createBlankPrototype(cx, &js_SlowArrayClass); + JSObject *arrayProto = global->createBlankPrototype(cx, &SlowArrayClass); if (!arrayProto || !AddLengthProperty(cx, arrayProto)) return NULL; arrayProto->setArrayLength(cx, 0); - JSFunction *ctor = global->createConstructor(cx, js_Array, &js_ArrayClass, + JSFunction *ctor = global->createConstructor(cx, js_Array, &ArrayClass, CLASS_ATOM(cx, Array), 1); if (!ctor) return NULL; @@ -3256,8 +3250,8 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto) { JS_ASSERT_IF(proto, proto->isArray()); - gc::FinalizeKind kind = GuessObjectGCKind(length, true); - JSObject *obj = detail::NewObject(cx, &js_ArrayClass, proto, NULL, kind); + gc::AllocKind kind = GuessObjectGCKind(length, true); + JSObject *obj = detail::NewObject(cx, &ArrayClass, proto, NULL, kind); if (!obj) return NULL; @@ -3346,7 +3340,7 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, NewDenseUnallocatedArray, CONTEXT, UINT32, JSObject * NewSlowEmptyArray(JSContext *cx) { - JSObject *obj = NewNonFunction(cx, &js_SlowArrayClass, NULL, NULL); + JSObject *obj = NewNonFunction(cx, &SlowArrayClass, NULL, NULL); if (!obj || !AddLengthProperty(cx, obj)) return NULL; diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index aaa74d258bdb..460675798b3e 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -63,7 +63,7 @@ using namespace js; using namespace js::types; -Class js_BooleanClass = { +Class js::BooleanClass = { "Boolean", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), @@ -138,7 +138,7 @@ Boolean(JSContext *cx, uintN argc, Value *vp) bool b = argc != 0 ? js_ValueToBoolean(argv[0]) : false; if (IsConstructing(vp)) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_BooleanClass); + JSObject *obj = NewBuiltinClassInstance(cx, &BooleanClass); if (!obj) return false; obj->setPrimitiveThis(BooleanValue(b)); @@ -152,7 +152,7 @@ Boolean(JSContext *cx, uintN argc, Value *vp) JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj) { - JSObject *proto = js_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, + JSObject *proto = js_InitClass(cx, obj, NULL, &BooleanClass, Boolean, 1, NULL, boolean_methods, NULL, NULL); if (!proto) return NULL; diff --git a/js/src/jsbool.h b/js/src/jsbool.h index 9da251e439d5..3677425afc11 100644 --- a/js/src/jsbool.h +++ b/js/src/jsbool.h @@ -46,14 +46,6 @@ #include "jsapi.h" #include "jsobj.h" -extern js::Class js_BooleanClass; - -inline bool -JSObject::isBoolean() const -{ - return getClass() == &js_BooleanClass; -} - extern JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj); diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index 408213bfa9f6..52ec1f055437 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -251,7 +251,7 @@ HasProperty(JSContext* cx, JSObject* obj, jsid id) if (pobj->getOps()->lookupProperty) return JS_NEITHER; Class* clasp = pobj->getClass(); - if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass) + if (clasp->resolve != JS_ResolveStub && clasp != &StringClass) return JS_NEITHER; } @@ -320,7 +320,7 @@ js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* pa if (!closure) return NULL; - if (!closure->initSharingEmptyShape(cx, &js_FunctionClass, type, parent, + if (!closure->initSharingEmptyShape(cx, &FunctionClass, type, parent, fun, gc::FINALIZE_OBJECT2)) { return NULL; } diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index 1b47a259a25b..1f26ed191ca1 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -84,8 +84,8 @@ enum { * 'i': an integer argument * 's': a JSString* argument * 'o': a JSObject* argument - * 'r': a JSObject* argument that is of class js_RegExpClass - * 'f': a JSObject* argument that is of class js_FunctionClass + * 'r': a JSObject* argument that is of class RegExpClass + * 'f': a JSObject* argument that is of class FunctionClass * 'v': a value argument: on 32-bit, a Value*, on 64-bit, a jsval */ struct JSSpecializedNative { 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/jsclone.cpp b/js/src/jsclone.cpp index 17301bf1c037..48f362bc2f8c 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -782,7 +782,7 @@ JSStructuredCloneReader::startRead(Value *vp) case SCTAG_OBJECT_OBJECT: { JSObject *obj = (tag == SCTAG_ARRAY_OBJECT) ? NewDenseEmptyArray(context()) - : NewBuiltinClassInstance(context(), &js_ObjectClass); + : NewBuiltinClassInstance(context(), &ObjectClass); if (!obj || !objs.append(ObjectValue(*obj)) || !allObjs.append(ObjectValue(*obj))) return false; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index f55286cfe420..2467a1e96f87 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. */ @@ -512,14 +512,6 @@ struct JSRuntime { volatile ptrdiff_t gcMallocBytes; public: - js::GCChunkAllocator *gcChunkAllocator; - - void setCustomGCChunkAllocator(js::GCChunkAllocator *allocator) { - JS_ASSERT(allocator); - JS_ASSERT(state == JSRTS_DOWN); - gcChunkAllocator = allocator; - } - /* * The trace operation and its data argument to trace embedding-specific * GC roots. @@ -1496,8 +1488,7 @@ class AutoGCRooter { DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */ STRING = -14, /* js::AutoStringRooter */ IDVECTOR = -15, /* js::AutoIdVector */ - OBJVECTOR = -16, /* js::AutoObjectVector */ - TYPE = -17 /* js::types::AutoTypeRooter */ + OBJVECTOR = -16 /* js::AutoObjectVector */ }; private: @@ -1794,17 +1785,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/jscntxtinlines.h b/js/src/jscntxtinlines.h index b189e15f8b60..24860dbb7d86 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -334,12 +334,10 @@ CallJSNativeConstructor(JSContext *cx, js::Native native, const CallArgs &args) * * (new Object(Object)) returns the callee. */ - extern JSBool proxy_Construct(JSContext *, uintN, Value *); - extern JSBool callable_Construct(JSContext *, uintN, Value *); - JS_ASSERT_IF(native != proxy_Construct && - native != callable_Construct && + JS_ASSERT_IF(native != FunctionProxyClass.construct && + native != CallableObjectClass.construct && native != js::CallOrConstructBoundFunction && - (!callee.isFunction() || callee.getFunctionPrivate()->u.n.clasp != &js_ObjectClass), + (!callee.isFunction() || callee.getFunctionPrivate()->u.n.clasp != &ObjectClass), !args.rval().isPrimitive() && callee != args.rval().toObject()); return true; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 73f9c775121f..fda45985aa43 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) { @@ -261,7 +247,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp) return true; /* Translate StopIteration singleton. */ - if (obj->getClass() == &js_StopIterationClass) + if (obj->isStopIteration()) return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp); /* Don't unwrap an outer window proxy. */ @@ -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"); @@ -599,9 +585,26 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) #endif - if (!activeAnalysis) { +#ifdef JS_METHODJIT + if (types.inferenceEnabled) + mjit::ClearAllFrames(this); +#endif + + if (activeAnalysis) { /* - * Clear the analysis pool, but don't releas its data yet. While + * Analysis information is in use, so don't clear the analysis pool. + * jitcode still needs to be released, if this is a shape-regenerating + * GC then shape numbers baked into the code may change. + */ + if (types.inferenceEnabled) { + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + mjit::ReleaseScriptCode(cx, script); + } + } + } else { + /* + * Clear the analysis pool, but don't release its data yet. While * sweeping types any live data will be allocated into the pool. */ JSArenaPool oldPool; @@ -613,9 +616,6 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) * enabled in the compartment. */ if (types.inferenceEnabled) { -#ifdef JS_METHODJIT - mjit::ClearAllFrames(this); -#endif for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); if (script->types) { @@ -629,6 +629,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) if (discardScripts) { script->types->destroy(); script->types = NULL; + script->typesPurged = true; } } } @@ -638,8 +639,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); - if (script->types) - script->types->analysis = NULL; + script->clearAnalysis(); } /* Reset the analysis pool, releasing all analysis and intermediate type data. */ @@ -652,7 +652,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/jscrashreport.h b/js/src/jscrashreport.h index 378cbc94104f..bdc0b0bee9d0 100644 --- a/js/src/jscrashreport.h +++ b/js/src/jscrashreport.h @@ -56,11 +56,11 @@ SnapshotErrorStack(); void SaveCrashData(uint64 tag, void *ptr, size_t size); -template +template class StackBuffer { private: JS_DECL_USE_GUARD_OBJECT_NOTIFIER - volatile char buffer[size + 4]; + volatile unsigned char buffer[size + 4]; public: StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) { @@ -71,7 +71,7 @@ class StackBuffer { for (size_t i = 0; i < size; i++) { if (data) - buffer[i + 2] = ((char *)data)[i]; + buffer[i + 2] = ((unsigned char *)data)[i]; else buffer[i + 2] = 0; } diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 988a9c4683f4..eb7bdf168765 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -503,7 +503,7 @@ date_convert(JSContext *cx, JSObject *obj, JSType hint, Value *vp) * Other Support routines and definitions */ -Class js_DateClass = { +Class js::DateClass = { js_Date_str, JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Date), @@ -1226,7 +1226,7 @@ GetUTCTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *dp) { if (!obj->isDate()) { if (vp) - ReportIncompatibleMethod(cx, vp, &js_DateClass); + ReportIncompatibleMethod(cx, vp, &DateClass); return false; } *dp = obj->getDateUTCTime().toNumber(); @@ -1408,7 +1408,7 @@ GetAndCacheLocalTime(JSContext *cx, JSObject *obj, Value *vp, jsdouble *time = N return false; if (!obj->isDate()) { if (vp) - ReportIncompatibleMethod(cx, vp, &js_DateClass); + ReportIncompatibleMethod(cx, vp, &DateClass); return false; } @@ -1701,7 +1701,7 @@ date_setTime(JSContext *cx, uintN argc, Value *vp) return false; if (!obj->isDate()) { - ReportIncompatibleMethod(cx, vp, &js_DateClass); + ReportIncompatibleMethod(cx, vp, &DateClass); return false; } @@ -2607,7 +2607,7 @@ js_InitDateClass(JSContext *cx, JSObject *obj) { /* set static LocalTZA */ LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); - JSObject *proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS, + JSObject *proto = js_InitClass(cx, obj, NULL, &DateClass, js_Date, MAXARGS, NULL, date_methods, NULL, date_static_methods); if (!proto) return NULL; @@ -2638,7 +2638,7 @@ js_InitDateClass(JSContext *cx, JSObject *obj) JS_FRIEND_API(JSObject *) js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass); + JSObject *obj = NewBuiltinClassInstance(cx, &DateClass); if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS)) return NULL; if (!SetUTCTime(cx, obj, msec_time)) diff --git a/js/src/jsdate.h b/js/src/jsdate.h index ba97ddd855c4..28a27f935039 100644 --- a/js/src/jsdate.h +++ b/js/src/jsdate.h @@ -46,14 +46,6 @@ #include "jsobj.h" -extern js::Class js_DateClass; - -inline bool -JSObject::isDate() const -{ - return getClass() == &js_DateClass; -} - #define HalfTimeDomain 8.64e15 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 765746429d27..fb3bd0a26c93 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -77,14 +77,12 @@ #include "frontend/ParseMaps-inl.h" /* Allocation chunk counts, must be powers of two in general. */ -#define BYTECODE_CHUNK 256 /* code allocation increment */ -#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */ -#define TRYNOTE_CHUNK 64 /* trynote allocation increment */ +#define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */ +#define SRCNOTE_CHUNK_LENGTH 1024 /* initial srcnote chunk length */ /* Macros to compute byte sizes from typed element counts. */ #define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) #define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote)) -#define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote)) using namespace js; using namespace js::gc; @@ -106,12 +104,8 @@ JSTreeContext::trace(JSTracer *trc) bindings.trace(trc); } -JSCodeGenerator::JSCodeGenerator(Parser *parser, - JSArenaPool *cpool, JSArenaPool *npool, - uintN lineno) +JSCodeGenerator::JSCodeGenerator(Parser *parser, uintN lineno) : JSTreeContext(parser), - codePool(cpool), notePool(npool), - codeMark(JS_ARENA_MARK(cpool)), noteMark(JS_ARENA_MARK(npool)), atomIndices(parser->context), stackDepth(0), maxStackDepth(0), ntrynotes(0), lastTryNode(NULL), @@ -135,7 +129,6 @@ JSCodeGenerator::JSCodeGenerator(Parser *parser, memset(&main, 0, sizeof main); current = &main; firstLine = prolog.currentLine = main.currentLine = lineno; - prolog.noteMask = main.noteMask = SRCNOTE_CHUNK - 1; } bool @@ -147,47 +140,51 @@ JSCodeGenerator::init(JSContext *cx, JSTreeContext::InitBehavior ib) JSCodeGenerator::~JSCodeGenerator() { - JS_ARENA_RELEASE(codePool, codeMark); - JS_ARENA_RELEASE(notePool, noteMark); - JSContext *cx = parser->context; + cx->free_(prolog.base); + cx->free_(prolog.notes); + cx->free_(main.base); + cx->free_(main.notes); + /* NB: non-null only after OOM. */ if (spanDeps) cx->free_(spanDeps); } static ptrdiff_t -EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta) +EmitCheck(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t delta) { - jsbytecode *base, *limit, *next; - ptrdiff_t offset, length; - size_t incr, size; + jsbytecode *base = CG_BASE(cg); + jsbytecode *newbase; + jsbytecode *next = CG_NEXT(cg); + jsbytecode *limit = CG_LIMIT(cg); + ptrdiff_t offset = next - base; + size_t minlength = offset + delta; - base = CG_BASE(cg); - next = CG_NEXT(cg); - limit = CG_LIMIT(cg); - offset = next - base; if (next + delta > limit) { - length = offset + delta; - length = (length <= BYTECODE_CHUNK) - ? BYTECODE_CHUNK - : JS_BIT(JS_CeilingLog2(length)); - incr = BYTECODE_SIZE(length); + size_t newlength; if (!base) { - JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr); + JS_ASSERT(!next && !limit); + newlength = BYTECODE_CHUNK_LENGTH; + if (newlength < minlength) /* make it bigger if necessary */ + newlength = RoundUpPow2(minlength); + newbase = (jsbytecode *) cx->malloc_(BYTECODE_SIZE(newlength)); } else { - size = BYTECODE_SIZE(limit - base); - incr -= size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); + JS_ASSERT(base <= next && next <= limit); + newlength = (limit - base) * 2; + if (newlength < minlength) /* make it bigger if necessary */ + newlength = RoundUpPow2(minlength); + newbase = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(newlength)); } - if (!base) { + if (!newbase) { js_ReportOutOfMemory(cx); return -1; } - CG_BASE(cg) = base; - CG_LIMIT(cg) = base + length; - CG_NEXT(cg) = base + offset; + JS_ASSERT(newlength >= size_t(offset + delta)); + CG_BASE(cg) = newbase; + CG_LIMIT(cg) = newbase + newlength; + CG_NEXT(cg) = newbase + offset; } return offset; } @@ -263,7 +260,7 @@ UpdateDecomposeLength(JSCodeGenerator *cg, uintN start) ptrdiff_t js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) { - ptrdiff_t offset = EmitCheck(cx, cg, op, 1); + ptrdiff_t offset = EmitCheck(cx, cg, 1); if (offset >= 0) { *CG_NEXT(cg)++ = (jsbytecode)op; @@ -275,7 +272,7 @@ js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) ptrdiff_t js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1) { - ptrdiff_t offset = EmitCheck(cx, cg, op, 2); + ptrdiff_t offset = EmitCheck(cx, cg, 2); if (offset >= 0) { jsbytecode *next = CG_NEXT(cg); @@ -291,7 +288,7 @@ ptrdiff_t js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, jsbytecode op2) { - ptrdiff_t offset = EmitCheck(cx, cg, op, 3); + ptrdiff_t offset = EmitCheck(cx, cg, 3); if (offset >= 0) { jsbytecode *next = CG_NEXT(cg); @@ -307,7 +304,7 @@ js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, ptrdiff_t js_Emit5(JSContext *cx, JSCodeGenerator *cg, JSOp op, uint16 op1, uint16 op2) { - ptrdiff_t offset = EmitCheck(cx, cg, op, 5); + ptrdiff_t offset = EmitCheck(cx, cg, 5); if (offset >= 0) { jsbytecode *next = CG_NEXT(cg); @@ -326,7 +323,7 @@ ptrdiff_t js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) { ptrdiff_t length = 1 + (ptrdiff_t)extra; - ptrdiff_t offset = EmitCheck(cx, cg, op, length); + ptrdiff_t offset = EmitCheck(cx, cg, length); if (offset >= 0) { jsbytecode *next = CG_NEXT(cg); @@ -842,7 +839,6 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) JSBool done; JSOp op; uint32 type; - size_t size, incr; jssrcnote *sn, *snlimit; JSSrcNoteSpec *spec; uintN i, n, noteIndex; @@ -972,10 +968,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) length = offset + growth; next = base + length; if (next > limit) { - JS_ASSERT(length > BYTECODE_CHUNK); - size = BYTECODE_SIZE(limit - base); - incr = BYTECODE_SIZE(length) - size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); + base = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(length)); if (!base) { js_ReportOutOfMemory(cx); return JS_FALSE; @@ -1057,7 +1050,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) * including it in offset's new value. */ offset = sd->before + 1; - size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN)); + size_t size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN)); if (size) { memmove(pc + 1 + JUMPX_OFFSET_LEN, oldpc + 1 + JUMP_OFFSET_LEN, @@ -1227,7 +1220,6 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) * generation of successive, independent, top-level statements. No jump * can span top-level statements, because JS lacks goto. */ - size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps))); cx->free_(cg->spanDeps); cg->spanDeps = NULL; FreeJumpTargets(cg, cg->jumpTargets); @@ -2000,7 +1992,7 @@ EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg) if (depth < 0) return false; - uintN base = JSSLOT_FREE(&js_BlockClass); + uintN base = JSSLOT_FREE(&BlockClass); for (uintN slot = base, limit = base + OBJ_BLOCK_COUNT(cx, blockObj); slot < limit; slot++) { const Value &v = blockObj->getSlot(slot); @@ -3712,8 +3704,7 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, /* Switch note's second offset is to first JSOP_CASE. */ noteCount = CG_NOTE_COUNT(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, - off - top)) { + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, off - top)) { return JS_FALSE; } noteCountDelta = CG_NOTE_COUNT(cg) - noteCount; @@ -4891,8 +4882,8 @@ 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); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = GuessObjectGCKind(pn_count, false); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); if (!obj) return false; @@ -5025,18 +5016,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) fun->kind() == JSFUN_INTERPRETED); /* Generate code for the function's body. */ - void *cg2mark = JS_ARENA_MARK(cg->codePool); - void *cg2space; - JS_ARENA_ALLOCATE_TYPE(cg2space, JSCodeGenerator, cg->codePool); - if (!cg2space) { + JSCodeGenerator *cg2 = cx->new_(cg->parser, pn->pn_pos.begin.lineno); + if (!cg2) { js_ReportOutOfMemory(cx); return JS_FALSE; } - JSCodeGenerator *cg2 = - new (cg2space) JSCodeGenerator(cg->parser, - cg->codePool, cg->notePool, - pn->pn_pos.begin.lineno); - if (!cg2->init(cx)) return JS_FALSE; @@ -5066,8 +5050,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) pn = NULL; - cg2->~JSCodeGenerator(); - JS_ARENA_RELEASE(cg->codePool, cg2mark); + cx->delete_(cg2); cg2 = NULL; if (!pn) return JS_FALSE; @@ -7084,8 +7067,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) */ JSObject *obj = NULL; if (!cg->hasSharps() && cg->compileAndGo()) { - gc::FinalizeKind kind = GuessObjectGCKind(pn->pn_count, false); - obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = GuessObjectGCKind(pn->pn_count, false); + obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); if (!obj) return JS_FALSE; } @@ -7445,31 +7428,32 @@ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { static intN AllocSrcNote(JSContext *cx, JSCodeGenerator *cg) { - intN index; - JSArenaPool *pool; - size_t size; + jssrcnote *notes = CG_NOTES(cg); + jssrcnote *newnotes; + uintN index = CG_NOTE_COUNT(cg); + uintN max = CG_NOTE_LIMIT(cg); - index = CG_NOTE_COUNT(cg); - if (((uintN)index & CG_NOTE_MASK(cg)) == 0) { - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - if (!CG_NOTES(cg)) { - /* Allocate the first note array lazily; leave noteMask alone. */ - JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size); + if (index == max) { + size_t newlength; + if (!notes) { + JS_ASSERT(!index && !max); + newlength = SRCNOTE_CHUNK_LENGTH; + newnotes = (jssrcnote *) cx->malloc_(SRCNOTE_SIZE(newlength)); } else { - /* Grow by doubling note array size; update noteMask on success. */ - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (CG_NOTES(cg)) - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; + JS_ASSERT(index <= max); + newlength = max * 2; + newnotes = (jssrcnote *) cx->realloc_(notes, SRCNOTE_SIZE(newlength)); } - if (!CG_NOTES(cg)) { + if (!newnotes) { js_ReportOutOfMemory(cx); return -1; } + CG_NOTES(cg) = newnotes; + CG_NOTE_LIMIT(cg) = newlength; } CG_NOTE_COUNT(cg) = index + 1; - return index; + return (intN)index; } intN @@ -7553,18 +7537,14 @@ js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, static JSBool GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg) { - JSArenaPool *pool; - size_t size; - - /* Grow by doubling note array size; update noteMask on success. */ - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (!CG_NOTES(cg)) { + size_t newlength = CG_NOTE_LIMIT(cg) * 2; + jssrcnote *newnotes = (jssrcnote *) cx->realloc_(CG_NOTES(cg), newlength); + if (!newnotes) { js_ReportOutOfMemory(cx); return JS_FALSE; } - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; + CG_NOTES(cg) = newnotes; + CG_NOTE_LIMIT(cg) = newlength; return JS_TRUE; } @@ -7589,7 +7569,7 @@ js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, SN_SET_DELTA(sn, newdelta); } else { index = sn - cg->main.notes; - if ((cg->main.noteCount & cg->main.noteMask) == 0) { + if (cg->main.noteCount == cg->main.noteLimit) { if (!GrowSrcNotes(cx, cg)) return NULL; sn = cg->main.notes + index; @@ -7664,11 +7644,11 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, index = sn - CG_NOTES(cg); /* - * Simultaneously test to see if the source note array must grow to - * accommodate either the first or second byte of additional storage - * required by this 3-byte offset. + * Test to see if the source note array must grow to accommodate + * either the first or second byte of additional storage required + * by this 3-byte offset. */ - if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { + if (CG_NOTE_COUNT(cg) + 1 >= CG_NOTE_LIMIT(cg)) { if (!GrowSrcNotes(cx, cg)) return JS_FALSE; sn = CG_NOTES(cg) + index; diff --git a/js/src/jsemit.h b/js/src/jsemit.h index ef91272aae88..0a576b6dc050 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -601,18 +601,13 @@ class JSGCConstList { struct JSCodeGenerator : public JSTreeContext { - JSArenaPool *codePool; /* pointer to thread code arena pool */ - JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ - void *codeMark; /* low watermark in cg->codePool */ - void *noteMark; /* low watermark in cg->notePool */ - struct { jsbytecode *base; /* base of JS bytecode vector */ jsbytecode *limit; /* one byte beyond end of bytecode */ jsbytecode *next; /* pointer to next free bytecode */ jssrcnote *notes; /* source notes, see below */ uintN noteCount; /* number of source notes so far */ - uintN noteMask; /* growth increment for notes */ + uintN noteLimit; /* limit number for source notes in notePool */ ptrdiff_t lastNoteOffset; /* code offset for last source note */ uintN currentLine; /* line number for tree-based srcnote gen */ } prolog, main, *current; @@ -665,14 +660,7 @@ struct JSCodeGenerator : public JSTreeContext uint16 traceIndex; /* index for the next JSOP_TRACE instruction */ uint16 typesetCount; /* Number of JOF_TYPESET opcodes generated */ - /* - * Initialize cg to allocate bytecode space from codePool, source note - * space from notePool, and all other arena-allocated temporaries from - * parser->context->tempPool. - */ - JSCodeGenerator(js::Parser *parser, - JSArenaPool *codePool, JSArenaPool *notePool, - uintN lineno); + JSCodeGenerator(js::Parser *parser, uintN lineno); bool init(JSContext *cx, JSTreeContext::InitBehavior ib = USED_AS_CODE_GENERATOR); JSContext *context() { @@ -761,7 +749,7 @@ struct JSCodeGenerator : public JSTreeContext #define CG_NOTES(cg) ((cg)->current->notes) #define CG_NOTE_COUNT(cg) ((cg)->current->noteCount) -#define CG_NOTE_MASK(cg) ((cg)->current->noteMask) +#define CG_NOTE_LIMIT(cg) ((cg)->current->noteLimit) #define CG_LAST_NOTE_OFFSET(cg) ((cg)->current->lastNoteOffset) #define CG_CURRENT_LINE(cg) ((cg)->current->currentLine) diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 342f8e9e370b..c3aec9f64b18 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -75,7 +75,7 @@ using namespace js; using namespace js::gc; using namespace js::types; -/* Forward declarations for js_ErrorClass's initializer. */ +/* Forward declarations for ErrorClass's initializer. */ static JSBool Exception(JSContext *cx, uintN argc, Value *vp); @@ -89,7 +89,7 @@ static JSBool exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp); -Class js_ErrorClass = { +Class js::ErrorClass = { js_Error_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Error), @@ -263,113 +263,99 @@ GetStackTraceValueBuffer(JSExnPrivate *priv) return (jsval *)(priv->stackElems + priv->stackDepth); } -static JSBool +struct SuppressErrorsGuard +{ + JSContext *cx; + JSErrorReporter prevReporter; + JSExceptionState *prevState; + + SuppressErrorsGuard(JSContext *cx) + : cx(cx), + prevReporter(JS_SetErrorReporter(cx, NULL)), + prevState(JS_SaveExceptionState(cx)) + {} + + ~SuppressErrorsGuard() + { + JS_RestoreExceptionState(cx, prevState); + JS_SetErrorReporter(cx, prevReporter); + } +}; + +struct AppendArg { + Vector &values; + AppendArg(Vector &values) : values(values) {} + bool operator()(uintN, Value *vp) { + return values.append(*vp); + } +}; + +static bool InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, JSString *filename, uintN lineno, JSErrorReport *report, intN exnType) { - JSSecurityCallbacks *callbacks; - CheckAccessOp checkAccess; - JSErrorReporter older; - JSExceptionState *state; - jsid callerid; - size_t stackDepth, valueCount, size; - JSBool overflow; - JSExnPrivate *priv; - JSStackTraceElem *elem; - jsval *values; + JS_ASSERT(exnObject->isError()); + JS_ASSERT(!exnObject->getPrivate()); - JS_ASSERT(exnObject->getClass() == &js_ErrorClass); + JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); + CheckAccessOp checkAccess = callbacks + ? Valueify(callbacks->checkObjectAccess) + : NULL; - /* - * Prepare stack trace data. - * - * Set aside any error reporter for cx and save its exception state - * so we can suppress any checkAccess failures. Such failures should stop - * the backtrace procedure, not result in a failure of this constructor. - */ - callbacks = JS_GetSecurityCallbacks(cx); - checkAccess = callbacks - ? Valueify(callbacks->checkObjectAccess) - : NULL; - older = JS_SetErrorReporter(cx, NULL); - state = JS_SaveExceptionState(cx); - - callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom); - stackDepth = 0; - valueCount = 0; - FrameRegsIter firstPass(cx); - for (; !firstPass.done(); ++firstPass) { - StackFrame *fp = firstPass.fp(); - if (fp->compartment() != cx->compartment) - break; - if (fp->isNonEvalFunctionFrame()) { - Value v = NullValue(); - if (checkAccess && - !checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) { + Vector frames(cx); + Vector values(cx); + { + SuppressErrorsGuard seg(cx); + for (FrameRegsIter i(cx); !i.done(); ++i) { + /* + * An exception object stores stack values from 'fp' which may be + * in a different compartment from 'exnObject'. Engine compartment + * invariants require such values to be wrapped. A simpler solution + * is to just cut off the backtrace at compartment boundaries. + * Also, avoid exposing values from different security principals. + */ + StackFrame *fp = i.fp(); + if (fp->compartment() != cx->compartment) break; + if (checkAccess && fp->isNonEvalFunctionFrame()) { + Value v = NullValue(); + jsid callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom); + if (!checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) + break; } - valueCount += fp->numActualArgs(); - } - ++stackDepth; - } - JS_RestoreExceptionState(cx, state); - JS_SetErrorReporter(cx, older); - size = offsetof(JSExnPrivate, stackElems); - overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); - size += stackDepth * sizeof(JSStackTraceElem); - overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); - size += valueCount * sizeof(jsval); - if (overflow) { - js_ReportAllocationOverflow(cx); - return JS_FALSE; + if (!frames.growBy(1)) + return false; + JSStackTraceElem &frame = frames.back(); + if (fp->isNonEvalFunctionFrame()) { + frame.funName = fp->fun()->atom ? fp->fun()->atom : cx->runtime->emptyString; + frame.argc = fp->numActualArgs(); + if (!fp->forEachCanonicalActualArg(AppendArg(values))) + return false; + } else { + frame.funName = NULL; + frame.argc = 0; + } + if (fp->isScriptFrame()) { + frame.filename = fp->script()->filename; + frame.ulineno = js_FramePCToLineNumber(cx, fp, i.pc()); + } else { + frame.ulineno = 0; + frame.filename = NULL; + } + } } - priv = (JSExnPrivate *)cx->malloc_(size); + + /* Do not need overflow check: the vm stack is already bigger. */ + JS_STATIC_ASSERT(sizeof(JSStackTraceElem) <= sizeof(StackFrame)); + + size_t nbytes = offsetof(JSExnPrivate, stackElems) + + frames.length() * sizeof(JSStackTraceElem) + + values.length() * sizeof(Value); + + JSExnPrivate *priv = (JSExnPrivate *)cx->malloc_(nbytes); if (!priv) - return JS_FALSE; - - /* - * We initialize errorReport with a copy of report after setting the - * private slot, to prevent GC accessing a junk value we clear the field - * here. - */ - priv->errorReport = NULL; - priv->message = message; - priv->filename = filename; - priv->lineno = lineno; - priv->stackDepth = stackDepth; - priv->exnType = exnType; - - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - - for (FrameRegsIter iter(cx); iter != firstPass; ++iter) { - StackFrame *fp = iter.fp(); - if (fp->compartment() != cx->compartment) - break; - if (!fp->isNonEvalFunctionFrame()) { - elem->funName = NULL; - elem->argc = 0; - } else { - elem->funName = fp->fun()->atom - ? fp->fun()->atom - : cx->runtime->emptyString; - elem->argc = fp->numActualArgs(); - fp->forEachCanonicalActualArg(CopyTo(Valueify(values))); - values += elem->argc; - } - elem->ulineno = 0; - elem->filename = NULL; - if (fp->isScriptFrame()) { - elem->filename = fp->script()->filename; - elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc()); - } - ++elem; - } - JS_ASSERT(priv->stackElems + stackDepth == elem); - JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); - - exnObject->setPrivate(priv); + return false; if (report) { /* @@ -380,12 +366,28 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, */ priv->errorReport = CopyErrorReport(cx, report); if (!priv->errorReport) { - /* The finalizer realeases priv since it is in the private slot. */ - return JS_FALSE; + cx->free_(priv); + return false; } + } else { + priv->errorReport = NULL; } - return JS_TRUE; + priv->message = message; + priv->filename = filename; + priv->lineno = lineno; + priv->stackDepth = frames.length(); + priv->exnType = exnType; + + JSStackTraceElem *framesDest = priv->stackElems; + Value *valuesDest = reinterpret_cast(framesDest + frames.length()); + JS_ASSERT(valuesDest == Valueify(GetStackTraceValueBuffer(priv))); + + PodCopy(framesDest, frames.begin(), frames.length()); + PodCopy(valuesDest, values.begin(), values.length()); + + exnObject->setPrivate(priv); + return true; } static inline JSExnPrivate * @@ -429,12 +431,9 @@ exn_trace(JSTracer *trc, JSObject *obj) static void exn_finalize(JSContext *cx, JSObject *obj) { - JSExnPrivate *priv; - - priv = GetExnPrivate(obj); - if (priv) { - if (priv->errorReport) - cx->free_(priv->errorReport); + if (JSExnPrivate *priv = GetExnPrivate(obj)) { + if (JSErrorReport *report = priv->errorReport) + cx->free_(report); cx->free_(priv); } } @@ -521,7 +520,7 @@ js_ErrorFromException(JSContext *cx, jsval exn) if (JSVAL_IS_PRIMITIVE(exn)) return NULL; obj = JSVAL_TO_OBJECT(exn); - if (obj->getClass() != &js_ErrorClass) + if (!obj->isError()) return NULL; priv = GetExnPrivate(obj); if (!priv) @@ -704,6 +703,8 @@ enum { static JSBool Exception(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give @@ -711,36 +712,28 @@ Exception(JSContext *cx, uintN argc, Value *vp) * NewNativeClassInstance to find the class prototype, we must get the * class prototype ourselves. */ - JSObject &callee = vp[0].toObject(); Value protov; - if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov)) - return JS_FALSE; + jsid protoAtom = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + if (!args.callee().getProperty(cx, protoAtom, &protov)) + return false; if (!protov.isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); - return JS_FALSE; + return false; } JSObject *errProto = &protov.toObject(); - JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent()); + JSObject *obj = NewNativeClassInstance(cx, &ErrorClass, errProto, errProto->getParent()); if (!obj) - return JS_FALSE; - - /* - * If it's a new object of class Exception, then null out the private - * data so that the finalizer doesn't attempt to free it. - */ - if (obj->getClass() == &js_ErrorClass) - obj->setPrivate(NULL); + return false; /* Set the 'message' property. */ - Value *argv = vp + 2; JSString *message; - if (argc != 0 && !argv[0].isUndefined()) { - message = js_ValueToString(cx, argv[0]); + if (args.argc() != 0 && !args[0].isUndefined()) { + message = js_ValueToString(cx, args[0]); if (!message) - return JS_FALSE; - argv[0].setString(message); + return false; + args[0].setString(message); } else { message = NULL; } @@ -752,16 +745,16 @@ Exception(JSContext *cx, uintN argc, Value *vp) /* Set the 'fileName' property. */ JSString *filename; - if (argc > 1) { - filename = js_ValueToString(cx, argv[1]); + if (args.argc() > 1) { + filename = js_ValueToString(cx, args[1]); if (!filename) - return JS_FALSE; - argv[1].setString(filename); + return false; + args[1].setString(filename); } else { if (!iter.done()) { filename = FilenameToString(cx, iter.fp()->script()->filename); if (!filename) - return JS_FALSE; + return false; } else { filename = cx->runtime->emptyString; } @@ -769,21 +762,19 @@ Exception(JSContext *cx, uintN argc, Value *vp) /* Set the 'lineNumber' property. */ uint32_t lineno; - if (argc > 2) { - if (!ValueToECMAUint32(cx, argv[2], &lineno)) - return JS_FALSE; + if (args.argc() > 2) { + if (!ValueToECMAUint32(cx, args[2], &lineno)) + return false; } else { lineno = iter.done() ? 0 : js_FramePCToLineNumber(cx, iter.fp(), iter.pc()); } - intN exnType = callee.getReservedSlot(JSSLOT_ERROR_EXNTYPE).toInt32(); - if (obj->getClass() == &js_ErrorClass && - !InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType)) { - return JS_FALSE; - } + intN exnType = args.callee().getReservedSlot(JSSLOT_ERROR_EXNTYPE).toInt32(); + if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType)) + return false; - vp->setObject(*obj); - return JS_TRUE; + args.rval().setObject(*obj); + return true; } /* @@ -1019,7 +1010,7 @@ InitErrorClass(JSContext *cx, GlobalObject *global, intN type, JSObject &proto) { JSProtoKey key = GetExceptionProtoKey(type); JSAtom *name = cx->runtime->atomState.classAtoms[key]; - JSObject *errorProto = global->createBlankPrototypeInheriting(cx, &js_ErrorClass, proto); + JSObject *errorProto = global->createBlankPrototypeInheriting(cx, &ErrorClass, proto); if (!errorProto) return NULL; @@ -1041,7 +1032,7 @@ InitErrorClass(JSContext *cx, GlobalObject *global, intN type, JSObject &proto) } /* Create the corresponding constructor. */ - JSFunction *ctor = global->createConstructor(cx, Exception, &js_ErrorClass, name, 1); + JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1); if (!ctor) return NULL; ctor->setReservedSlot(JSSLOT_ERROR_EXNTYPE, Int32Value(int32(type))); @@ -1180,7 +1171,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, goto out; tv[0] = OBJECT_TO_JSVAL(errProto); - errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent()); + errObject = NewNativeClassInstance(cx, &ErrorClass, errProto, errProto->getParent()); if (!errObject) { ok = JS_FALSE; goto out; @@ -1264,7 +1255,7 @@ js_ReportUncaughtException(JSContext *cx) } JSAutoByteString filename; - if (!reportp && exnObject && exnObject->getClass() == &js_ErrorClass) { + if (!reportp && exnObject && exnObject->isError()) { if (!JS_GetProperty(cx, exnObject, js_message_str, &roots[2])) return false; if (JSVAL_IS_STRING(roots[2])) { @@ -1368,7 +1359,7 @@ js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope) JSObject *proto; if (!js_GetClassPrototype(cx, scope->getGlobal(), GetExceptionProtoKey(copy->exnType), &proto)) return NULL; - JSObject *copyobj = NewNativeClassInstance(cx, &js_ErrorClass, proto, proto->getParent()); + JSObject *copyobj = NewNativeClassInstance(cx, &ErrorClass, proto, proto->getParent()); copyobj->setPrivate(copy); autoFree.p = NULL; return copyobj; diff --git a/js/src/jsexn.h b/js/src/jsexn.h index 3966331e3128..db26f3994798 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -46,14 +46,6 @@ #include "jsobj.h" -extern js::Class js_ErrorClass; - -inline bool -JSObject::isError() const -{ - return clasp == &js_ErrorClass; -} - /* * Initialize the exception constructor/prototype hierarchy. */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a2b73c55accd..4d053627e951 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -216,8 +216,8 @@ ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee) /* Can't fail from here on, so initialize everything in argsobj. */ obj->init(cx, callee.getFunctionPrivate()->inStrictMode() - ? &StrictArgumentsObject::jsClass - : &NormalArgumentsObject::jsClass, + ? &StrictArgumentsObjectClass + : &NormalArgumentsObjectClass, type, proto->getParent(), NULL, false); obj->setMap(emptyArgumentsShape); @@ -686,18 +686,16 @@ args_trace(JSTracer *trc, JSObject *obj) MaybeMarkGenerator(trc, argsobj); } -namespace js { - /* * The classes below collaborate to lazily reflect and synchronize actual * argument values, argument count, and callee function object stored in a * StackFrame with their corresponding property values in the frame's * arguments object. */ -Class NormalArgumentsObject::jsClass = { +Class js::NormalArgumentsObjectClass = { "Arguments", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | + JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), PropertyStub, /* addProperty */ args_delProperty, @@ -721,10 +719,10 @@ Class NormalArgumentsObject::jsClass = { * arguments, so it is represented by a different class while sharing some * functionality. */ -Class StrictArgumentsObject::jsClass = { +Class js::StrictArgumentsObjectClass = { "Arguments", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | + JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), PropertyStub, /* addProperty */ args_delProperty, @@ -743,13 +741,11 @@ Class StrictArgumentsObject::jsClass = { args_trace }; -} - /* * A Declarative Environment object stores its active StackFrame pointer in * its private slot, just as Call and Arguments objects do. */ -Class js_DeclEnvClass = { +Class js::DeclEnvClass = { js_Object_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), PropertyStub, /* addProperty */ @@ -773,7 +769,18 @@ 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); + + /* + * Make sure that the arguments and variables in the call object all end up + * in a contiguous range of slots. We need this to be able to embed the + * args/vars arrays in the TypeScriptNesting for the function, after the + * call object's frame has finished. + */ + if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) { + kind = gc::GetGCObjectKind(JSObject::CALL_RESERVED_SLOTS); + JS_ASSERT(gc::GetGCKindSlots(kind) == JSObject::CALL_RESERVED_SLOTS); + } JSObject *callobj = js_NewGCObject(cx, kind); if (!callobj) @@ -811,7 +818,7 @@ NewDeclEnvObject(JSContext *cx, StackFrame *fp) EmptyShape *emptyDeclEnvShape = EmptyShape::getEmptyDeclEnvShape(cx); if (!emptyDeclEnvShape) return NULL; - envobj->init(cx, &js_DeclEnvClass, &emptyTypeObject, &fp->scopeChain(), fp, false); + envobj->init(cx, &DeclEnvClass, &emptyTypeObject, &fp->scopeChain(), fp, false); envobj->setMap(emptyDeclEnvShape); return envobj; @@ -833,7 +840,7 @@ CreateFunCallObject(JSContext *cx, StackFrame *fp) * For a named function expression Call's parent points to an environment * object holding function's name. */ - if (JSAtom *lambdaName = (fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL) { + if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) { scopeChain = NewDeclEnvObject(cx, fp); if (!scopeChain) return NULL; @@ -950,13 +957,23 @@ js_PutCallObject(StackFrame *fp) callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]); } } + + /* + * Update the args and vars for the active call if this is an outer + * function in a script nesting. + */ + types::TypeScriptNesting *nesting = script->nesting(); + if (nesting && script->isOuterFunction) { + nesting->argArray = callobj.callObjArgArray(); + nesting->varArray = callobj.callObjVarArray(); + } } - /* Clear private pointers to fp, which is about to go away (js_Invoke). */ + /* Clear private pointers to fp, which is about to go away. */ if (js_IsNamedLambda(fun)) { JSObject *env = callobj.getParent(); - JS_ASSERT(env->getClass() == &js_DeclEnvClass); + JS_ASSERT(env->isDeclEnv()); JS_ASSERT(env->getPrivate() == fp); env->setPrivate(NULL); } @@ -1032,7 +1049,11 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) else obj->setCallObjArg(i, *vp); - JSScript *script = obj->getCallObjCalleeFunction()->script(); + JSFunction *fun = obj->getCallObjCalleeFunction(); + JSScript *script = fun->script(); + if (!script->ensureHasTypes(cx, fun)) + return false; + TypeScript::SetArgument(cx, script, i, *vp); return true; @@ -1099,7 +1120,11 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) else obj->setCallObjVar(i, *vp); - JSScript *script = obj->getCallObjCalleeFunction()->script(); + JSFunction *fun = obj->getCallObjCalleeFunction(); + JSScript *script = fun->script(); + if (!script->ensureHasTypes(cx, fun)) + return false; + TypeScript::SetLocal(cx, script, i, *vp); return true; @@ -1176,7 +1201,7 @@ call_trace(JSTracer *trc, JSObject *obj) MaybeMarkGenerator(trc, obj); } -JS_PUBLIC_DATA(Class) js_CallClass = { +JS_PUBLIC_DATA(Class) js::CallClass = { "Call", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) | @@ -1466,7 +1491,7 @@ ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj) JSObject *objProto; if (!js_GetClassPrototype(cx, parent, JSProto_Object, &objProto)) return NULL; - JSObject *proto = NewNativeClassInstance(cx, &js_ObjectClass, objProto, parent); + JSObject *proto = NewNativeClassInstance(cx, &ObjectClass, objProto, parent); if (!proto || !proto->setSingletonType(cx)) return NULL; @@ -1718,7 +1743,7 @@ fun_finalize(JSContext *cx, JSObject *obj) * does not bloat every instance, only those on which reserved slots are set, * and those on which ad-hoc properties are defined. */ -JS_PUBLIC_DATA(Class) js_FunctionClass = { +JS_PUBLIC_DATA(Class) js::FunctionClass = { js_Function_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) | @@ -1827,11 +1852,10 @@ fun_toSource(JSContext *cx, uintN argc, Value *vp) JSBool js_fun_call(JSContext *cx, uintN argc, Value *vp) { - LeaveTrace(cx); Value fval = vp[1]; if (!js_IsCallable(fval)) { - ReportIncompatibleMethod(cx, vp, &js_FunctionClass); + ReportIncompatibleMethod(cx, vp, &FunctionClass); return false; } @@ -1868,7 +1892,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) /* Step 1. */ Value fval = vp[1]; if (!js_IsCallable(fval)) { - ReportIncompatibleMethod(cx, vp, &js_FunctionClass); + ReportIncompatibleMethod(cx, vp, &FunctionClass); return false; } @@ -1893,8 +1917,6 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) if (!js_GetLengthProperty(cx, aobj, &length)) return false; - LeaveTrace(cx); - /* Step 6. */ if (length > StackSpace::ARGS_LENGTH_MAX) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS); @@ -2002,8 +2024,6 @@ CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp) JS_ASSERT(obj->isFunction()); JS_ASSERT(obj->isBoundFunction()); - LeaveTrace(cx); - bool constructing = IsConstructing(vp); /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */ @@ -2077,7 +2097,7 @@ fun_bind(JSContext *cx, uintN argc, Value *vp) /* Step 2. */ if (!js_IsCallable(thisv)) { - ReportIncompatibleMethod(cx, vp, &js_FunctionClass); + ReportIncompatibleMethod(cx, vp, &FunctionClass); return false; } @@ -2370,7 +2390,7 @@ ThrowTypeError(JSContext *cx, uintN argc, Value *vp) JSObject * js_InitFunctionClass(JSContext *cx, JSObject *obj) { - JSObject *proto = js_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, + JSObject *proto = js_InitClass(cx, obj, NULL, &FunctionClass, Function, 1, NULL, function_methods, NULL, NULL); if (!proto) return NULL; @@ -2393,9 +2413,8 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj) script->code[0] = JSOP_STOP; script->code[1] = SRC_NULL; fun->u.i.script = script; - fun->getType(cx)->functionScript = script; + fun->getType(cx)->interpretedFunction = fun; script->hasFunction = true; - script->where.fun = fun; script->setOwnerObject(fun); js_CallNewScriptHook(cx, script, fun); @@ -2477,7 +2496,7 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, * The cloned function object does not need the extra JSFunction members * beyond JSObject as it points to fun via the private slot. */ - clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent); + clone = NewNativeClassInstance(cx, &FunctionClass, proto, parent); if (!clone) return NULL; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index c1861cf5720f..83e806045b47 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -170,7 +170,7 @@ struct JSFunction : public JSObject_Slots2 private: /* - * js_FunctionClass reserves two slots, which are free in JSObject::fslots + * FunctionClass reserves two slots, which are free in JSObject::fslots * without requiring dslots allocation. Null closures that can be joined to * a compiler-created function object use the first one to hold a mutable * methodAtom() state variable, needed for correct foo.caller handling. @@ -255,22 +255,6 @@ struct JSFunction : public JSObject_Slots2 JS_FN(name, fastcall, nargs, flags) #endif -extern JS_PUBLIC_DATA(js::Class) js_CallClass; -extern JS_PUBLIC_DATA(js::Class) js_FunctionClass; -extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; - -inline bool -JSObject::isCall() const -{ - return getClass() == &js_CallClass; -} - -inline bool -JSObject::isFunction() const -{ - return getClass() == &js_FunctionClass; -} - inline JSFunction * JSObject::getFunctionPrivate() const { @@ -331,7 +315,7 @@ IsNativeFunction(const js::Value &v, Native native) /* * When we have an object of a builtin class, we don't quite know what its * valueOf/toString methods are, since these methods may have been overwritten - * or shadowed. However, we can still do better than js_TryMethod by + * or shadowed. However, we can still do better than the general case by * hard-coding the necessary properties for us to find the native we expect. * * TODO: a per-thread shape-based cache would be faster and simpler. diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 8c808c392d5c..3bfae14d3f61 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; @@ -454,7 +534,7 @@ Chunk::releaseArena(ArenaHeader *aheader) inline Chunk * AllocateGCChunk(JSRuntime *rt) { - Chunk *p = (Chunk *)rt->gcChunkAllocator->alloc(); + Chunk *p = static_cast(AllocGCChunk()); #ifdef MOZ_GCTIMER if (p) JS_ATOMIC_INCREMENT(&newChunkCount); @@ -469,10 +549,11 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p) #ifdef MOZ_GCTIMER JS_ATOMIC_INCREMENT(&destroyChunkCount); #endif - rt->gcChunkAllocator->free_(p); + FreeGCChunk(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,265 @@ 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 } +#ifdef JS_THREADSAFE /*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 +1430,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 +1466,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 +1491,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 +1592,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 +1983,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 +2074,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 +2107,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 +2181,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 +2283,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 +2322,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 +2343,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 +2758,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 +2854,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 +2885,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..40168a38e6e6 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); +#endif + + private: + inline void finalizeNow(JSContext *cx, AllocKind thingKind); + inline void finalizeLater(JSContext *cx, AllocKind thingKind); + + inline void *allocateFromArena(JSContext *cx, AllocKind thingKind); +}; /* * 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/jsgcchunk.cpp b/js/src/jsgcchunk.cpp index 551f6b7ebfa9..2529166fe9cb 100644 --- a/js/src/jsgcchunk.cpp +++ b/js/src/jsgcchunk.cpp @@ -272,8 +272,6 @@ UnmapPages(void *addr, size_t size) namespace js { -GCChunkAllocator defaultGCChunkAllocator; - inline void * FindChunkStart(void *p) { @@ -282,7 +280,7 @@ FindChunkStart(void *p) return reinterpret_cast(addr); } -JS_FRIEND_API(void *) +void * AllocGCChunk() { void *p; @@ -329,7 +327,7 @@ AllocGCChunk() return p; } -JS_FRIEND_API(void) +void FreeGCChunk(void *p) { JS_ASSERT(p); diff --git a/js/src/jsgcchunk.h b/js/src/jsgcchunk.h index 94d37ae2d329..df648ce9545d 100644 --- a/js/src/jsgcchunk.h +++ b/js/src/jsgcchunk.h @@ -49,44 +49,12 @@ const size_t GC_CHUNK_SHIFT = 20; const size_t GC_CHUNK_SIZE = size_t(1) << GC_CHUNK_SHIFT; const size_t GC_CHUNK_MASK = GC_CHUNK_SIZE - 1; -JS_FRIEND_API(void *) +void * AllocGCChunk(); -JS_FRIEND_API(void) +void FreeGCChunk(void *p); -class GCChunkAllocator { - public: - GCChunkAllocator() {} - - void *alloc() { - void *chunk = doAlloc(); - JS_ASSERT(!(reinterpret_cast(chunk) & GC_CHUNK_MASK)); - return chunk; - } - - void free_(void *chunk) { - JS_ASSERT(chunk); - JS_ASSERT(!(reinterpret_cast(chunk) & GC_CHUNK_MASK)); - doFree(chunk); - } - - private: - virtual void *doAlloc() { - return AllocGCChunk(); - } - - virtual void doFree(void *chunk) { - FreeGCChunk(chunk); - } - - /* No copy or assignment semantics. */ - GCChunkAllocator(const GCChunkAllocator &); - void operator=(const GCChunkAllocator &); -}; - -extern GCChunkAllocator defaultGCChunkAllocator; - } #endif /* jsgchunk_h__ */ diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index a215a4835f4c..a7d1c40ee63c 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,11 @@ class CellIterImpl CellIterImpl() { } - void init(JSCompartment *comp, FinalizeKind thingKind) { - thingSize = GCThingSizeMap[thingKind]; - aheader = comp->arenas[thingKind].getHead(); + void init(JSCompartment *comp, AllocKind kind) { + JS_ASSERT(comp->arenas.isSynchronizedFreeList(kind)); + firstThingOffset = Arena::firstThingOffset(kind); + thingSize = Arena::thingSize(kind); + aheader = comp->arenas.getFirstArena(kind); firstSpan.initAsEmpty(); span = &firstSpan; thing = span->first; @@ -308,7 +310,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 +321,9 @@ 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); + 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/jsgcmark.cpp b/js/src/jsgcmark.cpp index fd6d842e3f16..ef7acfaf0caa 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -66,7 +66,7 @@ * MarkString, etc. These functions check if an object is in the compartment * currently being GCed. If it is, they call PushMarkStack. Roots are pushed * this way as well as pointers traversed inside trace hooks (for things like - * js_IteratorClass). It it always valid to call a MarkX function instead of + * IteratorClass). It it always valid to call a MarkX function instead of * PushMarkStack, although it may be slower. * * The MarkX functions also handle non-GC object traversal. In this case, they @@ -234,8 +234,8 @@ MarkTypeObject(JSTracer *trc, types::TypeObject *type, const char *name) if (IS_GC_MARKING_TRACER(trc)) { if (type->singleton) MarkObject(trc, *type->singleton, "type_singleton"); - if (type->functionScript) - MarkScript(trc, type->functionScript, "functionScript"); + if (type->interpretedFunction) + MarkObject(trc, *type->interpretedFunction, "type_function"); } } @@ -415,7 +415,7 @@ MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind) Mark(trc, reinterpret_cast(thing)); break; case JSTRACE_TYPE_OBJECT: - Mark(trc, reinterpret_cast(thing)); + MarkTypeObject(trc, reinterpret_cast(thing), "type_stack"); break; #if JS_HAS_XML_SUPPORT case JSTRACE_XML: @@ -708,7 +708,7 @@ ScanObject(GCMarker *gcmarker, JSObject *obj) */ Class *clasp = obj->getClass(); if (clasp->trace) { - if (clasp == &js_ArrayClass) { + if (clasp == &ArrayClass) { if (obj->getDenseArrayInitializedLength() > LARGE_OBJECT_CHUNK_SIZE) { if (!gcmarker->largeStack.push(LargeMarkItem(obj))) clasp->trace(gcmarker, obj); @@ -851,14 +851,15 @@ MarkChildren(JSTracer *trc, JSScript *script) if (!script->isCachedEval && script->u.object) MarkObject(trc, *script->u.object, "object"); - if (script->hasFunction) - MarkObject(trc, *script->function(), "script_fun"); if (IS_GC_MARKING_TRACER(trc) && script->filename) js_MarkScriptFilename(script->filename); script->bindings.trace(trc); + if (script->types) + script->types->trace(trc); + #ifdef JS_METHODJIT if (script->jitNormal) script->jitNormal->trace(trc); @@ -913,7 +914,7 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) PushMarkStack(gcmarker, type->proto); if (type->newScript) { - PushMarkStack(gcmarker, type->newScript->script); + PushMarkStack(gcmarker, type->newScript->fun); PushMarkStack(gcmarker, type->newScript->shape); } @@ -953,12 +954,12 @@ MarkChildren(JSTracer *trc, types::TypeObject *type) MarkObject(trc, *type->singleton, "type_singleton"); if (type->newScript) { - MarkScript(trc, type->newScript->script, "type_new_script"); + MarkObject(trc, *type->newScript->fun, "type_new_function"); MarkShape(trc, type->newScript->shape, "type_new_shape"); } - if (type->functionScript) - MarkScript(trc, type->functionScript, "functionScript"); + if (type->interpretedFunction) + MarkObject(trc, *type->interpretedFunction, "type_function"); } #ifdef JS_HAS_XML_SUPPORT diff --git a/js/src/jsgcmark.h b/js/src/jsgcmark.h index 513bb432d479..125a24099633 100644 --- a/js/src/jsgcmark.h +++ b/js/src/jsgcmark.h @@ -50,9 +50,6 @@ namespace js { namespace gc { -template -void Mark(JSTracer *trc, T *thing); - void MarkString(JSTracer *trc, JSString *str); 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..c9503c8a47aa 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -716,21 +716,32 @@ class TypeConstraintFilterPrimitive : public TypeConstraint { public: TypeSet *target; + TypeSet::FilterKind filter; - /* Primitive types other than null and undefined are passed through. */ - bool onlyNullVoid; - - TypeConstraintFilterPrimitive(TypeSet *target, bool onlyNullVoid) - : TypeConstraint("filter"), target(target), onlyNullVoid(onlyNullVoid) + TypeConstraintFilterPrimitive(TypeSet *target, TypeSet::FilterKind filter) + : TypeConstraint("filter"), target(target), filter(filter) {} void newType(JSContext *cx, TypeSet *source, Type type) { - if (onlyNullVoid) { + switch (filter) { + case TypeSet::FILTER_ALL_PRIMITIVES: + if (type.isPrimitive()) + return; + break; + + case TypeSet::FILTER_NULL_VOID: if (type.isPrimitive(JSVAL_TYPE_NULL) || type.isPrimitive(JSVAL_TYPE_UNDEFINED)) return; - } else if (type.isPrimitive()) { - return; + break; + + case TypeSet::FILTER_VOID: + if (type.isPrimitive(JSVAL_TYPE_UNDEFINED)) + return; + break; + + default: + JS_NOT_REACHED("Bad filter"); } target->addType(cx, type); @@ -738,10 +749,9 @@ public: }; void -TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid) +TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter) { - add(cx, ArenaNew(cx->compartment->pool, - target, onlyNullVoid)); + add(cx, ArenaNew(cx->compartment->pool, target, filter)); } /* If id is a normal slotful 'own' property of an object, get its shape. */ @@ -1103,7 +1113,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) return; } - JSScript *callee = NULL; + JSFunction *callee = NULL; if (type.isSingleObject()) { JSObject *obj = type.singleObject(); @@ -1159,9 +1169,9 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) return; } - callee = obj->getFunctionPrivate()->script(); + callee = obj->getFunctionPrivate(); } else if (type.isTypeObject()) { - callee = type.typeObject()->functionScript; + callee = type.typeObject()->interpretedFunction; if (!callee) return; } else { @@ -1169,30 +1179,27 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) return; } - unsigned nargs = callee->function()->nargs; - - if (!callee->ensureHasTypes(cx)) + if (!callee->script()->ensureHasTypes(cx, callee)) return; - /* Analyze the function if we have not already done so. */ - if (!callee->ensureRanInference(cx)) { - cx->compartment->types.setPendingNukeTypes(cx); - return; - } + unsigned nargs = callee->nargs; /* Add bindings for the arguments of the call. */ for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) { TypeSet *argTypes = callsite->argumentTypes[i]; - TypeSet *types = TypeScript::ArgTypes(callee, i); + TypeSet *types = TypeScript::ArgTypes(callee->script(), i); argTypes->addSubsetBarrier(cx, script, pc, types); } /* Add void type for any formals in the callee not supplied at the call site. */ for (unsigned i = callsite->argumentCount; i < nargs; i++) { - TypeSet *types = TypeScript::ArgTypes(callee, i); + TypeSet *types = TypeScript::ArgTypes(callee->script(), i); types->addType(cx, Type::UndefinedType()); } + TypeSet *thisTypes = TypeScript::ThisTypes(callee->script()); + TypeSet *returnTypes = TypeScript::ReturnTypes(callee->script()); + if (callsite->isNew) { /* * If the script does not return a value then the pushed value is the @@ -1200,8 +1207,9 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) * the new value, which is done dynamically; we don't keep track of the * possible 'new' types for a given prototype type object. */ - TypeScript::ThisTypes(callee)->addSubset(cx, callsite->returnTypes); - TypeScript::ReturnTypes(callee)->addFilterPrimitives(cx, callsite->returnTypes, false); + thisTypes->addSubset(cx, callsite->returnTypes); + returnTypes->addFilterPrimitives(cx, callsite->returnTypes, + TypeSet::FILTER_ALL_PRIMITIVES); } else { /* * Add a binding for the return value of the call. We don't add a @@ -1211,7 +1219,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) * in the 'this' and 'callee' sets, which we want to maintain for * polymorphic JSOP_CALLPROP invocations. */ - TypeScript::ReturnTypes(callee)->addSubset(cx, callsite->returnTypes); + returnTypes->addSubset(cx, callsite->returnTypes); } } @@ -1230,27 +1238,27 @@ TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type) } /* Ignore calls to natives, these will be handled by TypeConstraintCall. */ - JSScript *callee = NULL; + JSFunction *callee = NULL; if (type.isSingleObject()) { JSObject *object = type.singleObject(); if (!object->isFunction() || !object->getFunctionPrivate()->isInterpreted()) return; - callee = object->getFunctionPrivate()->script(); + callee = object->getFunctionPrivate(); } else if (type.isTypeObject()) { TypeObject *object = type.typeObject(); - if (!object->isFunction() || !object->functionScript) + if (!object->interpretedFunction) return; - callee = object->functionScript; + callee = object->interpretedFunction; } else { /* Ignore calls to primitives, these will go through a stub. */ return; } - if (!callee->ensureHasTypes(cx)) + if (!callee->script()->ensureHasTypes(cx, callee)) return; - TypeScript::ThisTypes(callee)->addType(cx, this->type); + TypeScript::ThisTypes(callee->script())->addType(cx, this->type); } void @@ -1623,7 +1631,7 @@ types::MarkArgumentsCreated(JSContext *cx, JSScript *script) mjit::ExpandInlineFrames(cx->compartment); #endif - if (!script->ensureRanBytecode(cx)) + if (!script->ensureRanAnalysis(cx)) return; ScriptAnalysis *analysis = script->analysis(); @@ -1712,7 +1720,7 @@ public: }; static void -CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script); +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun); bool TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) @@ -1725,7 +1733,7 @@ TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) */ if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { if (object->newScript) { - CheckNewScriptProperties(cx, object, object->newScript->script); + CheckNewScriptProperties(cx, object, object->newScript->fun); } else { JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED); object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; @@ -1814,6 +1822,64 @@ TypeSet::getSingleton(JSContext *cx, bool freeze) return obj; } +static inline bool +TypeHasGlobal(Type type, JSObject *global) +{ + if (type.isUnknown() || type.isAnyObject()) + return false; + + if (type.isSingleObject()) + return type.singleObject()->getGlobal() == global; + + if (type.isTypeObject()) + return type.typeObject()->getGlobal() == global; + + JS_ASSERT(type.isPrimitive()); + return true; +} + +class TypeConstraintFreezeGlobal : public TypeConstraint +{ +public: + JSScript *script; + JSObject *global; + + TypeConstraintFreezeGlobal(JSScript *script, JSObject *global) + : TypeConstraint("freezeGlobal"), script(script), global(global) + { + JS_ASSERT(global); + } + + void newType(JSContext *cx, TypeSet *source, Type type) + { + if (!global || TypeHasGlobal(type, global)) + return; + + global = NULL; + cx->compartment->types.addPendingRecompile(cx, script); + } +}; + +bool +TypeSet::hasGlobalObject(JSContext *cx, JSObject *global) +{ + if (unknownObject()) + return false; + + unsigned count = getObjectCount(); + for (unsigned i = 0; i < count; i++) { + TypeObjectKey *object = getObject(i); + if (object && !TypeHasGlobal(Type::ObjectType(object), global)) + return false; + } + + add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript, + global), false); + + return true; +} + ///////////////////////////////////////////////////////////////////// // TypeCompartment ///////////////////////////////////////////////////////////////////// @@ -2042,9 +2108,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; @@ -2280,28 +2346,6 @@ ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, Typ code.typeBarriers = barrier; } -static void -PrintScriptTypeCallback(JSContext *cx, void *data, void *thing, - JSGCTraceKind traceKind, size_t thingSize) -{ - JS_ASSERT(!data); - JS_ASSERT(traceKind == JSTRACE_SCRIPT); - JSScript *script = static_cast(thing); - if (script->hasAnalysis() && script->analysis()->ranInference()) - script->analysis()->printTypes(cx); -} - -#ifdef DEBUG -static void -PrintObjectCallback(JSContext *cx, void *data, void *thing, - JSGCTraceKind traceKind, size_t thingSize) -{ - JS_ASSERT(traceKind == JSTRACE_OBJECT); - TypeObject *object = (TypeObject *) thing; - object->print(cx); -} -#endif - void TypeCompartment::print(JSContext *cx, bool force) { @@ -2310,15 +2354,16 @@ TypeCompartment::print(JSContext *cx, bool force) if (!force && !InferSpewActive(ISpewResult)) return; - { - AutoUnlockGC unlock(cx->runtime); - IterateCells(cx, compartment, gc::FINALIZE_SCRIPT, cx, PrintScriptTypeCallback); + for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->hasAnalysis() && script->analysis()->ranInference()) + script->analysis()->printTypes(cx); } #ifdef DEBUG - { - AutoUnlockGC unlock(cx->runtime); - IterateCells(cx, compartment, gc::FINALIZE_TYPE_OBJECT, NULL, PrintObjectCallback); + for (gc::CellIter i(cx, compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { + TypeObject *object = i.get(); + object->print(cx); } #endif @@ -2841,9 +2886,14 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags) if (singleton) { /* Make sure flags are consistent with persistent object state. */ JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS, - (flags & OBJECT_FLAG_UNINLINEABLE) && functionScript->createdArgs); - JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, functionScript->uninlineable); - JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, singleton->flags & JSObject::ITERATED); + (flags & OBJECT_FLAG_UNINLINEABLE) && + interpretedFunction->script()->createdArgs); + JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, + interpretedFunction->script()->uninlineable); + JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION, + interpretedFunction->script()->reentrantOuterFunction); + JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, + singleton->flags & JSObject::ITERATED); } this->flags |= flags; @@ -2934,7 +2984,7 @@ TypeObject::clearNewScript(JSContext *cx) for (FrameRegsIter iter(cx); !iter.done(); ++iter) { StackFrame *fp = iter.fp(); if (fp->isScriptFrame() && fp->isConstructing() && - fp->script() == newScript->script && fp->thisValue().isObject() && + fp->fun() == newScript->fun && fp->thisValue().isObject() && !fp->thisValue().toObject().hasLazyType() && fp->thisValue().toObject().type() == this) { JSObject *obj = &fp->thisValue().toObject(); @@ -3089,6 +3139,96 @@ GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc) return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object); } +/* + * Detach nesting state for script from its parent, removing it entirely if it + * has no children of its own. This happens when walking type information while + * initially resolving NAME accesses, thus will not invalidate any compiler + * dependencies. + */ +static void +DetachNestingParent(JSScript *script) +{ + TypeScriptNesting *nesting = script->nesting(); + + if (!nesting || !nesting->parent) + return; + + /* Remove from parent's list of children. */ + JSScript **pscript = &nesting->parent->nesting()->children; + while ((*pscript)->nesting() != nesting) + pscript = &(*pscript)->nesting()->next; + *pscript = nesting->next; + + nesting->parent = NULL; + + /* If this nesting can have no children of its own, destroy it. */ + if (!script->isOuterFunction) + script->clearNesting(); +} + +ScriptAnalysis::NameAccess +ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + + NameAccess access; + PodZero(&access); + + if (!JSID_IS_ATOM(id)) + return access; + JSAtom *atom = JSID_TO_ATOM(id); + + JSScript *script = this->script; + while (script->hasFunction && script->nesting()) { + if (!script->ensureRanInference(cx)) + return access; + + /* + * Don't resolve names in scripts which use 'let' or 'with'. New names + * bound here can mask variables of the script itself. + * + * Also, don't resolve names in scripts which are generators. Frame + * balancing works differently for generators and we do not maintain + * active frame counts for such scripts. + */ + if (script->analysis()->addsScopeObjects() || + js_GetOpcode(cx, script, script->code) == JSOP_GENERATOR) { + return access; + } + + /* Check if the script definitely binds the identifier. */ + uintN index; + BindingKind kind = script->bindings.lookup(cx, atom, &index); + if (kind == ARGUMENT || kind == VARIABLE) { + TypeObject *obj = script->function()->getType(cx); + + if (addDependency) { + /* + * Record the dependency which compiled code has on the outer + * function being non-reentrant. + */ + if (TypeSet::HasObjectFlags(cx, obj, OBJECT_FLAG_REENTRANT_FUNCTION)) + return access; + } + + access.script = script; + access.nesting = script->nesting(); + access.slot = (kind == ARGUMENT) ? ArgSlot(index) : LocalSlot(script, index); + access.arg = (kind == ARGUMENT); + access.index = index; + return access; + } else if (kind != NONE) { + return access; + } + + if (!script->nesting()->parent) + return access; + script = script->nesting()->parent; + } + + return access; +} + /* Analyze type information for a single bytecode. */ bool ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, @@ -3321,7 +3461,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, else id = GetAtomId(cx, script, pc, 0); - TypeSet *seen = script->analysis()->bytecodeTypes(pc); + TypeSet *seen = bytecodeTypes(pc); seen->addSubset(cx, &pushed[0]); /* @@ -3351,13 +3491,25 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, case JSOP_NAME: case JSOP_CALLNAME: { - /* - * The first value pushed by NAME/CALLNAME must always be added to the - * bytecode types, we don't model these opcodes with inference. - */ - TypeSet *seen = script->analysis()->bytecodeTypes(pc); - addTypeBarrier(cx, pc, seen, Type::UnknownType()); + TypeSet *seen = bytecodeTypes(pc); seen->addSubset(cx, &pushed[0]); + + /* + * Try to resolve this name by walking the function's scope nesting. + * If we succeed but the accessed script has had its TypeScript purged + * in the past, we still must use a type barrier: the name access can + * be on a call object which predated the purge, and whose types might + * not be reflected in the reconstructed information. + */ + jsid id = GetAtomId(cx, script, pc, 0); + NameAccess access = resolveNameAccess(cx, id); + if (access.script && !access.script->typesPurged) { + TypeSet *types = TypeScript::SlotTypes(access.script, access.slot); + types->addSubsetBarrier(cx, script, pc, seen); + } else { + addTypeBarrier(cx, pc, seen, Type::UnknownType()); + } + if (op == JSOP_CALLNAME) { pushed[1].addType(cx, Type::UnknownType()); pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType()); @@ -3377,7 +3529,19 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, break; } - case JSOP_SETNAME: + case JSOP_SETNAME: { + jsid id = GetAtomId(cx, script, pc, 0); + NameAccess access = resolveNameAccess(cx, id); + if (access.script) { + TypeSet *types = TypeScript::SlotTypes(access.script, access.slot); + poppedTypes(pc, 0)->addSubset(cx, types); + } else { + cx->compartment->types.monitorBytecode(cx, script, offset); + } + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + } + case JSOP_SETCONST: cx->compartment->types.monitorBytecode(cx, script, offset); poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); @@ -3496,7 +3660,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, seen->addSubset(cx, &pushed[0]); if (op == JSOP_CALLPROP) - poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], true); + poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID); if (CheckNextTest(pc)) pushed[0].addType(cx, Type::UndefinedType()); break; @@ -3517,7 +3681,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, seen->addSubset(cx, &pushed[0]); if (op == JSOP_CALLELEM) - poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], true); + poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID); if (CheckNextTest(pc)) pushed[0].addType(cx, Type::UndefinedType()); break; @@ -3915,6 +4079,46 @@ ScriptAnalysis::analyzeTypes(JSContext *cx) for (unsigned i = 0; i < script->nfixed; i++) TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType()); + TypeScriptNesting *nesting = script->hasFunction ? script->nesting() : NULL; + if (nesting && nesting->parent) { + /* + * Check whether NAME accesses can be resolved in parent scopes, and + * detach from the parent if so. Even if outdated activations of this + * function are live when the parent is called again, we do not need to + * consider this reentrance as no state in the parent will be used. + */ + if (!nesting->parent->ensureRanInference(cx)) + return; + + bool detached = false; + + /* Don't track for leaf scripts which have no free variables. */ + if (!usesScopeChain() && !script->isOuterFunction) { + DetachNestingParent(script); + detached = true; + } + + /* + * If the names bound by the script are extensible (DEFFUN, EVAL, ...), + * don't resolve NAME accesses into the parent. + */ + if (!detached && extendsScope()) { + DetachNestingParent(script); + detached = true; + } + + /* + * Don't track for parents which add call objects or are generators, + * don't resolve NAME accesses into the parent. + */ + if (!detached && + (nesting->parent->analysis()->addsScopeObjects() || + js_GetOpcode(cx, nesting->parent, nesting->parent->code) == JSOP_GENERATOR)) { + DetachNestingParent(script); + detached = true; + } + } + TypeInferenceState state(cx); unsigned offset = 0; @@ -4123,7 +4327,7 @@ public: }; static bool -AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JSObject **pbaseobj, +AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSObject **pbaseobj, Vector *initializerList) { /* @@ -4144,14 +4348,18 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS return false; } - if (script->hasClearedGlobal()) - return false; + JSScript *script = fun->script(); + JS_ASSERT(!script->isInnerFunction); - if (!script->ensureRanInference(cx)) { + if (!script->ensureRanAnalysis(cx, fun) || !script->ensureRanInference(cx)) { *pbaseobj = NULL; cx->compartment->types.setPendingNukeTypes(cx); return false; } + + if (script->hasClearedGlobal()) + return false; + ScriptAnalysis *analysis = script->analysis(); /* @@ -4329,7 +4537,8 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS return false; } - JSScript *functionScript = scriptObj->getFunctionPrivate()->script(); + JSFunction *function = scriptObj->getFunctionPrivate(); + JS_ASSERT(!function->script()->isInnerFunction); /* * Generate constraints to clear definite properties from the type @@ -4347,7 +4556,7 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS return false; } - if (!AnalyzeNewScriptProperties(cx, type, functionScript, + if (!AnalyzeNewScriptProperties(cx, type, function, pbaseobj, initializerList)) { return false; } @@ -4379,13 +4588,13 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS * newScript on the type after they were cleared by a GC. */ static void -CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) { - if (type->unknownProperties()) + if (type->unknownProperties() || fun->script()->isInnerFunction) return; /* Strawman object to add properties to and watch for duplicates. */ - JSObject *baseobj = NewBuiltinClassInstance(cx, &js_ObjectClass, gc::FINALIZE_OBJECT16); + JSObject *baseobj = NewBuiltinClassInstance(cx, &ObjectClass, gc::FINALIZE_OBJECT16); if (!baseobj) { if (type->newScript) type->clearNewScript(cx); @@ -4393,7 +4602,7 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) } Vector initializerList(cx); - AnalyzeNewScriptProperties(cx, type, script, &baseobj, &initializerList); + AnalyzeNewScriptProperties(cx, type, fun, &baseobj, &initializerList); if (!baseobj || baseobj->slotSpan() == 0 || !!(type->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)) { if (type->newScript) type->clearNewScript(cx); @@ -4411,7 +4620,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()); @@ -4440,8 +4649,8 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) return; } - type->newScript->script = script; - type->newScript->finalizeKind = unsigned(kind); + type->newScript->fun = fun; + type->newScript->allocKind = kind; type->newScript->shape = baseobj->lastProperty(); type->newScript->initializerList = (TypeNewScript::Initializer *) @@ -4690,6 +4899,14 @@ IsAboutToBeFinalized(JSContext *cx, TypeObjectKey *key) return !reinterpret_cast((jsuword) key & ~1)->isMarked(); } +inline bool +ScriptIsAboutToBeFinalized(JSContext *cx, JSScript *script, JSFunction *fun) +{ + return script->isCachedEval || + (script->u.object && IsAboutToBeFinalized(cx, script->u.object)) || + (fun && IsAboutToBeFinalized(cx, fun)); +} + void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type) { @@ -4700,7 +4917,7 @@ TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type) /* Directly update associated type sets for applicable bytecodes. */ if (js_CodeSpec[*pc].format & JOF_TYPESET) { - if (!script->ensureRanBytecode(cx)) { + if (!script->ensureRanAnalysis(cx)) { cx->compartment->types.setPendingNukeTypes(cx); return; } @@ -4808,7 +5025,7 @@ TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Val AutoEnterTypeInference enter(cx); - if (!script->ensureRanBytecode(cx)) { + if (!script->ensureRanAnalysis(cx)) { cx->compartment->types.setPendingNukeTypes(cx); return; } @@ -4823,6 +5040,239 @@ TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Val types->addType(cx, type); } +bool +TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope) +{ + JS_ASSERT(script->types && !script->types->hasScope()); + + JSFunction *fun = script->types->function; + + JS_ASSERT(script->hasFunction == (fun != NULL)); + JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction); + JS_ASSERT_IF(!scope, fun && !script->isInnerFunction); + + /* + * The scope object must be the initial one for the script, before any call + * object has been created in the heavyweight case. + */ + JS_ASSERT_IF(scope && scope->isCall() && !scope->callIsForEval(), + scope->getCallObjCalleeFunction() != fun); + + if (!script->compileAndGo) { + script->types->global = NULL; + return true; + } + + JS_ASSERT_IF(fun && scope, fun->getGlobal() == scope->getGlobal()); + script->types->global = fun ? fun->getGlobal() : scope->getGlobal(); + + if (!cx->typeInferenceEnabled()) + return true; + + if (!script->isInnerFunction || fun->isNullClosure()) { + /* + * Outermost functions need nesting information if there are inner + * functions directly nested in them. + */ + if (script->isOuterFunction) { + script->types->nesting = cx->new_(); + if (!script->types->nesting) + return false; + } + return true; + } + + /* + * Walk the scope chain to the next call object, which will be the function + * the script is nested inside. + */ + while (!scope->isCall()) + scope = scope->getParent(); + + /* The isInnerFunction test ensures there is no intervening strict eval call object. */ + JS_ASSERT(!scope->callIsForEval()); + + /* Don't track non-heavyweight parents, NAME ops won't reach into them. */ + JSFunction *parentFun = scope->getCallObjCalleeFunction(); + if (!parentFun || !parentFun->isHeavyweight()) + return true; + JSScript *parent = parentFun->script(); + JS_ASSERT(parent->isOuterFunction); + + /* + * We only need the nesting in the child if it has NAME accesses going + * into the parent. We won't know for sure whether this is the case until + * analyzing the script's types, which we don't want to do yet. The nesting + * info we make here may get pruned if/when we eventually do such analysis. + */ + + /* + * Scopes are set when scripts first execute, and the parent script must + * have executed first. It is still possible for the parent script to not + * have a scope, however, as we occasionally purge all TypeScripts from the + * compartment and there may be inner function objects parented to an + * activation of the outer function sticking around. In such cases, treat + * the parent's call object as the most recent one, so that it is not + * marked as reentrant. + */ + if (!parent->ensureHasTypes(cx, parentFun)) + return false; + if (!parent->types->hasScope()) { + if (!SetScope(cx, parent, scope->getParent())) + return false; + parent->nesting()->activeCall = scope; + parent->nesting()->argArray = scope->callObjArgArray(); + parent->nesting()->varArray = scope->callObjVarArray(); + } + + JS_ASSERT(!script->types->nesting); + + /* Construct and link nesting information for the two functions. */ + + script->types->nesting = cx->new_(); + if (!script->types->nesting) + return false; + + script->nesting()->parent = parent; + script->nesting()->next = parent->nesting()->children; + parent->nesting()->children = script; + + return true; +} + +TypeScriptNesting::~TypeScriptNesting() +{ + /* + * Unlink from any parent/child. Nesting info on a script does not keep + * either the parent or children live during GC. + */ + + if (parent) { + JSScript **pscript = &parent->nesting()->children; + while ((*pscript)->nesting() != this) + pscript = &(*pscript)->nesting()->next; + *pscript = next; + } + + while (children) { + TypeScriptNesting *child = children->nesting(); + children = child->next; + child->parent = NULL; + child->next = NULL; + } +} + +bool +ClearActiveNesting(JSScript *start) +{ + /* + * Clear active call information for script and any outer functions + * inner to it. Return false if an inner function has frames on the stack. + */ + + /* Traverse children, then parent, avoiding recursion. */ + JSScript *script = start; + bool traverseChildren = true; + while (true) { + TypeScriptNesting *nesting = script->nesting(); + if (nesting->children && traverseChildren) { + script = nesting->children; + continue; + } + if (nesting->activeFrames) + return false; + if (script->isOuterFunction) { + nesting->activeCall = NULL; + nesting->argArray = NULL; + nesting->varArray = NULL; + } + if (script == start) + break; + if (nesting->next) { + script = nesting->next; + traverseChildren = true; + } else { + script = nesting->parent; + traverseChildren = false; + } + } + + return true; +} + +/* + * For the specified scope and script with an outer function, check if the + * scope represents a reentrant activation on an inner function of the parent + * or any of its transitive parents. + */ +static void +CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script) +{ + restart: + JSScript *parent = script->nesting()->parent; + JS_ASSERT(parent); + + while (!scope->isCall() || scope->getCallObjCalleeFunction()->script() != parent) + scope = scope->getParent(); + + if (scope != parent->nesting()->activeCall) { + parent->reentrantOuterFunction = true; + MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION); + + /* + * Continue checking parents to see if this is reentrant for them too. + * We don't need to check this in for non-reentrant calls on the outer + * function: when we entered any outer function to the immediate parent + * we cleared the active call for its transitive children, so a + * non-reentrant call on a child is also a non-reentrant call on the + * parent. + */ + if (parent->nesting()->parent) { + scope = scope->getParent(); + script = parent; + goto restart; + } + } +} + +void +NestingPrologue(JSContext *cx, StackFrame *fp) +{ + JSScript *script = fp->fun()->script(); + TypeScriptNesting *nesting = script->nesting(); + + if (nesting->parent) + CheckNestingParent(cx, &fp->scopeChain(), script); + + if (script->isOuterFunction) { + /* + * Check the stack has no frames for this activation, any of its inner + * functions or any of their transitive inner functions. + */ + if (!ClearActiveNesting(script)) { + script->reentrantOuterFunction = true; + MarkTypeObjectFlags(cx, fp->fun(), OBJECT_FLAG_REENTRANT_FUNCTION); + } + + nesting->activeCall = &fp->callObj(); + nesting->argArray = fp->formalArgs(); + nesting->varArray = fp->slots(); + } + + /* Maintain stack frame count for the function. */ + nesting->activeFrames++; +} + +void +NestingEpilogue(StackFrame *fp) +{ + JSScript *script = fp->fun()->script(); + TypeScriptNesting *nesting = script->nesting(); + + JS_ASSERT(nesting->activeFrames != 0); + nesting->activeFrames--; +} + } } /* namespace js::types */ ///////////////////////////////////////////////////////////////////// @@ -4906,24 +5356,32 @@ IgnorePushed(const jsbytecode *pc, unsigned index) } bool -JSScript::makeTypes(JSContext *cx) +JSScript::makeTypes(JSContext *cx, JSFunction *fun) { JS_ASSERT(!types); + JS_ASSERT(hasFunction == (fun != NULL)); if (!cx->typeInferenceEnabled()) { types = (TypeScript *) cx->calloc_(sizeof(TypeScript)); - return types != NULL; + if (!types) + return false; + new(types) TypeScript(fun); + return true; } AutoEnterTypeInference enter(cx); - unsigned count = TypeScript::NumTypeSets(this); + /* Open code for NumTypeSets since the types are not filled in yet. */ + unsigned count = 2 + (fun ? fun->nargs : 0) + nfixed + nTypeSets; + types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(TypeSet) * count)); if (!types) { cx->compartment->types.setPendingNukeTypes(cx); return false; } + new(types) TypeScript(fun); + #ifdef DEBUG TypeSet *typeArray = types->typeArray(); for (unsigned i = 0; i < nTypeSets; i++) @@ -4982,7 +5440,8 @@ bool JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton) { hasFunction = true; - where.fun = fun; + if (fun->isHeavyweight()) + isHeavyweightFunction = true; if (!cx->typeInferenceEnabled()) return true; @@ -4997,7 +5456,7 @@ JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton) return false; fun->setType(type); - type->functionScript = this; + type->interpretedFunction = fun; } return true; @@ -5130,11 +5589,14 @@ JSObject::makeLazyType(JSContext *cx) type->singleton = this; if (isFunction() && getFunctionPrivate() && getFunctionPrivate()->isInterpreted()) { - type->functionScript = getFunctionPrivate()->script(); - if (type->functionScript->uninlineable) - type->flags |= OBJECT_FLAG_UNINLINEABLE; - if (type->functionScript->createdArgs) + type->interpretedFunction = getFunctionPrivate(); + JSScript *script = type->interpretedFunction->script(); + if (script->createdArgs) type->flags |= OBJECT_FLAG_CREATED_ARGUMENTS; + if (script->uninlineable) + type->flags |= OBJECT_FLAG_UNINLINEABLE; + if (script->reentrantOuterFunction) + type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION; } if (flags & ITERATED) @@ -5168,7 +5630,7 @@ JSObject::makeLazyType(JSContext *cx) } void -JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool unknown) +JSObject::makeNewType(JSContext *cx, JSFunction *fun, bool unknown) { JS_ASSERT(!newType); @@ -5193,8 +5655,8 @@ JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool unknown) if (hasSpecialEquality()) type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY; - if (newScript) - CheckNewScriptProperties(cx, type, newScript); + if (fun) + CheckNewScriptProperties(cx, type, fun); #if JS_HAS_XML_SUPPORT /* Special case for XML object equality, see makeLazyType(). */ @@ -5480,7 +5942,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,15 +5982,9 @@ TypeScript::Sweep(JSContext *cx, JSScript *script) unsigned num = NumTypeSets(script); TypeSet *typeArray = script->types->typeArray(); - if (script->isAboutToBeFinalized(cx)) { - /* Release all memory associated with the persistent type sets. */ - for (unsigned i = 0; i < num; i++) - typeArray[i].clearObjects(); - } else { - /* Remove constraints and references to dead objects from the persistent type sets. */ - for (unsigned i = 0; i < num; i++) - typeArray[i].sweep(cx, compartment); - } + /* Remove constraints and references to dead objects from the persistent type sets. */ + for (unsigned i = 0; i < num; i++) + typeArray[i].sweep(cx, compartment); TypeResult **presult = &script->types->dynamicList; while (*presult) { @@ -5544,6 +6000,15 @@ TypeScript::Sweep(JSContext *cx, JSScript *script) } } + /* + * If the script has nesting state with a most recent activation, we do not + * need either to mark the call object or clear it if not live. Even with + * a dead pointer in the nesting, we can't get a spurious match while + * testing for reentrancy: if previous activations are still live, they + * cannot alias the most recent one, and future activations will overwrite + * activeCall on creation. + */ + /* * Method JIT code depends on the type inference data which is about to * be purged, so purge the jitcode as well. @@ -5562,6 +6027,9 @@ TypeScript::destroy() dynamicList = next; } + if (nesting) + Foreground::delete_(nesting); + Foreground::free_(this); } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 99ed6309ea95..4889a15f47e9 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -55,6 +55,7 @@ namespace js { namespace analyze { class ScriptAnalysis; } + struct GlobalObject; } namespace js { @@ -311,37 +312,40 @@ enum { OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT, /* - * Whether any objects this represents are not dense arrays. This also - * includes dense arrays whose length property does not fit in an int32. + * Some objects are not dense arrays, or are dense arrays whose length + * property does not fit in an int32. */ - OBJECT_FLAG_NON_DENSE_ARRAY = 0x010000, + OBJECT_FLAG_NON_DENSE_ARRAY = 0x0010000, /* Whether any objects this represents are not packed arrays. */ - OBJECT_FLAG_NON_PACKED_ARRAY = 0x020000, + OBJECT_FLAG_NON_PACKED_ARRAY = 0x0020000, /* Whether any objects this represents are not typed arrays. */ - OBJECT_FLAG_NON_TYPED_ARRAY = 0x040000, + OBJECT_FLAG_NON_TYPED_ARRAY = 0x0040000, /* Whether any represented script has had arguments objects created. */ - OBJECT_FLAG_CREATED_ARGUMENTS = 0x080000, + OBJECT_FLAG_CREATED_ARGUMENTS = 0x0080000, /* Whether any represented script is considered uninlineable. */ - OBJECT_FLAG_UNINLINEABLE = 0x100000, + OBJECT_FLAG_UNINLINEABLE = 0x0100000, /* Whether any objects have an equality hook. */ - OBJECT_FLAG_SPECIAL_EQUALITY = 0x200000, + OBJECT_FLAG_SPECIAL_EQUALITY = 0x0200000, /* Whether any objects have been iterated over. */ - OBJECT_FLAG_ITERATED = 0x400000, + OBJECT_FLAG_ITERATED = 0x0400000, + + /* Outer function which has been marked reentrant. */ + OBJECT_FLAG_REENTRANT_FUNCTION = 0x0800000, /* Flags which indicate dynamic properties of represented objects. */ - OBJECT_FLAG_DYNAMIC_MASK = 0x7f0000, + OBJECT_FLAG_DYNAMIC_MASK = 0x0ff0000, /* * Whether all properties of this object are considered unknown. * If set, all flags in DYNAMIC_MASK will also be set. */ - OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x800000, + OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x1000000, /* Mask for objects created with unknown properties. */ OBJECT_FLAG_UNKNOWN_MASK = @@ -429,6 +433,12 @@ class TypeSet bool hasPropagatedProperty() { return !!(flags & TYPE_FLAG_PROPAGATED_PROPERTY); } void setPropagatedProperty() { flags |= TYPE_FLAG_PROPAGATED_PROPERTY; } + enum FilterKind { + FILTER_ALL_PRIMITIVES, + FILTER_NULL_VOID, + FILTER_VOID + }; + /* Add specific kinds of constraints to this set. */ inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true); void addSubset(JSContext *cx, TypeSet *target); @@ -443,7 +453,7 @@ class TypeSet void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL); void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target); void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type); - void addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid); + void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter); void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target); void addLazyArguments(JSContext *cx, TypeSet *target); @@ -500,6 +510,9 @@ class TypeSet /* Get the single value which can appear in this type set, otherwise NULL. */ JSObject *getSingleton(JSContext *cx, bool freeze = true); + /* Whether all objects in this set are parented to a particular global. */ + bool hasGlobalObject(JSContext *cx, JSObject *global); + inline void clearObjects(); private: @@ -644,10 +657,10 @@ struct Property */ struct TypeNewScript { - JSScript *script; + JSFunction *fun; - /* 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 @@ -776,8 +789,8 @@ struct TypeObject : gc::Cell */ Property **propertySet; - /* If this is an interpreted function, the corresponding script. */ - JSScript *functionScript; + /* If this is an interpreted function, the function object. */ + JSFunction *interpretedFunction; inline TypeObject(JSObject *proto, bool isFunction, bool unknown); @@ -806,8 +819,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 @@ -826,6 +838,12 @@ struct TypeObject : gc::Cell /* Set flags on this object which are implied by the specified key. */ inline void setFlagsFromKey(JSContext *cx, JSProtoKey kind); + /* + * Get the global object which all objects of this type are parented to, + * or NULL if there is none known. + */ + inline JSObject *getGlobal(); + /* Helpers */ bool addProperty(JSContext *cx, jsid id, Property **pprop); @@ -903,19 +921,131 @@ struct TypeCallsite bool isNew, unsigned argumentCount); }; -/* Persistent type information for a script, retained across GCs. */ -struct TypeScript +/* + * Information attached to outer and inner function scripts nested in one + * another for tracking the reentrance state for outer functions. This state is + * used to generate fast accesses to the args and vars of the outer function. + * + * A function is non-reentrant if, at any point in time, only the most recent + * activation (i.e. call object) is live. An activation is live if either the + * activation is on the stack, or a transitive inner function parented to the + * activation is on the stack. + * + * Because inner functions can be (and, quite often, are) stored in object + * properties and it is difficult to build a fast and robust escape analysis + * to cope with such flow, we detect reentrance dynamically. For the outer + * function, we keep track of the call object for the most recent activation, + * and the number of frames for the function and its inner functions which are + * on the stack. + * + * If the outer function is called while frames associated with a previous + * activation are on the stack, the outer function is reentrant. If an inner + * function is called whose scope does not match the most recent activation, + * the outer function is reentrant. + * + * The situation gets trickier when there are several levels of nesting. + * + * function foo() { + * var a; + * function bar() { + * var b; + * function baz() { return a + b; } + * } + * } + * + * At calls to 'baz', we don't want to do the scope check for the activations + * of both 'foo' and 'bar', but rather 'bar' only. For this to work, a call to + * 'baz' which is a reentrant call on 'foo' must also be a reentrant call on + * 'bar'. When 'foo' is called, we clear the most recent call object for 'bar'. + */ +struct TypeScriptNesting { + /* + * If this is an inner function, the outer function. If non-NULL, this will + * be the immediate nested parent of the script (even if that parent has + * been marked reentrant). May be NULL even if the script has a nested + * parent, if NAME accesses cannot be tracked into the parent (either the + * script extends its scope with eval() etc., or the parent can make new + * scope chain objects with 'let' or 'with'). + */ + JSScript *parent; + + /* If this is an outer function, list of inner functions. */ + JSScript *children; + + /* Link for children list of parent. */ + JSScript *next; + + /* If this is an outer function, the most recent activation. */ + JSObject *activeCall; + + /* + * If this is an outer function, pointers to the most recent activation's + * arguments and variables arrays. These could be referring either to stack + * values in activeCall's frame (if it has not finished yet) or to the + * internal slots of activeCall (if the frame has finished). Pointers to + * these fields can be embedded directly in JIT code (though remember to + * use 'addDependency == true' when calling resolveNameAccess). + */ + Value *argArray; + Value *varArray; + + /* Number of frames for this function on the stack. */ + uint32 activeFrames; + + TypeScriptNesting() { PodZero(this); } + ~TypeScriptNesting(); +}; + +/* Construct nesting information for script wrt its parent. */ +bool CheckScriptNesting(JSContext *cx, JSScript *script); + +/* Track nesting state when calling or finishing an outer/inner function. */ +void NestingPrologue(JSContext *cx, StackFrame *fp); +void NestingEpilogue(StackFrame *fp); + +/* Persistent type information for a script, retained across GCs. */ +class TypeScript +{ + friend struct ::JSScript; + /* Analysis information for the script, cleared on each GC. */ analyze::ScriptAnalysis *analysis; + /* Function for the script, if it has one. */ + JSFunction *function; + + /* + * Information about the scope in which a script executes. This information + * is not set until the script has executed at least once and SetScope + * called, before that 'global' will be poisoned per GLOBAL_MISSING_SCOPE. + */ + static const size_t GLOBAL_MISSING_SCOPE = 0x1; + + /* Global object for the script, if compileAndGo. */ + js::GlobalObject *global; + + /* Nesting state for outer or inner function scripts. */ + TypeScriptNesting *nesting; + + public: + + /* Dynamic types generated at points within this script. */ + TypeResult *dynamicList; + + TypeScript(JSFunction *fun) { + this->function = fun; + this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE; + } + + bool hasScope() { return size_t(global) != GLOBAL_MISSING_SCOPE; } + /* Array of type type sets for variables and JOF_TYPESET ops. */ TypeSet *typeArray() { return (TypeSet *) (jsuword(this) + sizeof(TypeScript)); } static inline unsigned NumTypeSets(JSScript *script); - /* Dynamic types generated at points within this script. */ - TypeResult *dynamicList; + static bool SetScope(JSContext *cx, JSScript *script, JSObject *scope); static inline TypeSet *ReturnTypes(JSScript *script); static inline TypeSet *ThisTypes(JSScript *script); @@ -967,6 +1097,7 @@ struct TypeScript static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value); static void Sweep(JSContext *cx, JSScript *script); + inline void trace(JSTracer *trc); void destroy(); }; diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index f7b1239a9ad9..8253f6ebcacd 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -42,6 +42,7 @@ #include "jsarray.h" #include "jsanalyze.h" #include "jscompartment.h" +#include "jsgcmark.h" #include "jsinfer.h" #include "jsprf.h" #include "vm/GlobalObject.h" @@ -318,10 +319,16 @@ TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing) extern void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args, bool constructing); - if (cx->typeInferenceEnabled()) { - JSObject *callee = &args.callee(); - if (callee->isFunction() && callee->getFunctionPrivate()->isInterpreted()) - TypeMonitorCallSlow(cx, callee, args, constructing); + JSObject *callee = &args.callee(); + if (callee->isFunction()) { + JSFunction *fun = callee->getFunctionPrivate(); + if (fun->isInterpreted()) { + JSScript *script = fun->script(); + if (!script->ensureRanAnalysis(cx, fun, callee->getParent())) + return; + if (cx->typeInferenceEnabled()) + TypeMonitorCallSlow(cx, callee, args, constructing); + } } } @@ -508,6 +515,7 @@ TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key) struct AllocationSiteKey { JSScript *script; + uint32 offset : 24; JSProtoKey kind : 8; @@ -601,8 +609,9 @@ TypeScript::MonitorAssign(JSContext *cx, JSScript *script, jsbytecode *pc, /* static */ inline void TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) { - if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx)) + if (!cx->typeInferenceEnabled()) return; + JS_ASSERT(script->types); /* Analyze the script regardless if -a was used. */ bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS); @@ -614,7 +623,7 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) script->id(), TypeString(type)); ThisTypes(script)->addType(cx, type); - if (analyze) + if (analyze && script->types->hasScope()) script->ensureRanInference(cx); } } @@ -629,8 +638,10 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value) /* static */ inline void TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type) { - if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx)) + if (!cx->typeInferenceEnabled()) return; + JS_ASSERT(script->types); + if (!LocalTypes(script, local)->hasType(type)) { AutoEnterTypeInference enter(cx); @@ -652,8 +663,10 @@ TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, const js:: /* static */ inline void TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type) { - if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx)) + if (!cx->typeInferenceEnabled()) return; + JS_ASSERT(script->types); + if (!ArgTypes(script, arg)->hasType(type)) { AutoEnterTypeInference enter(cx); @@ -672,6 +685,17 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js: } } +void +TypeScript::trace(JSTracer *trc) +{ + if (function) + gc::MarkObject(trc, *function, "script_fun"); + if (hasScope() && global) + gc::MarkObject(trc, *global, "script_global"); + + /* Note: nesting does not keep anything alive. */ +} + ///////////////////////////////////////////////////////////////////// // TypeCompartment ///////////////////////////////////////////////////////////////////// @@ -1085,6 +1109,7 @@ inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown) PodZero(this); this->proto = proto; + if (function) flags |= OBJECT_FLAG_FUNCTION; if (unknown) @@ -1216,31 +1241,33 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key) setFlags(cx, flags); } +inline JSObject * +TypeObject::getGlobal() +{ + if (singleton) + return singleton->getGlobal(); + if (interpretedFunction && interpretedFunction->script()->compileAndGo) + return interpretedFunction->getGlobal(); + return NULL; +} + } } /* namespace js::types */ inline bool -JSScript::isAboutToBeFinalized(JSContext *cx) +JSScript::ensureHasTypes(JSContext *cx, JSFunction *fun) { - return isCachedEval || - (u.object && IsAboutToBeFinalized(cx, u.object)) || - (hasFunction && IsAboutToBeFinalized(cx, function())); + return types || makeTypes(cx, fun); } inline bool -JSScript::ensureHasTypes(JSContext *cx) +JSScript::ensureRanAnalysis(JSContext *cx, JSFunction *fun, JSObject *scope) { - return types || makeTypes(cx); -} - -inline bool -JSScript::ensureRanBytecode(JSContext *cx) -{ - if (!ensureHasTypes(cx)) + if (!ensureHasTypes(cx, fun)) + return false; + if (!types->hasScope() && !js::types::TypeScript::SetScope(cx, this, scope)) + return false; + if (!hasAnalysis() && !makeAnalysis(cx)) return false; - if (!hasAnalysis()) { - if (!makeAnalysis(cx)) - return false; - } JS_ASSERT(analysis()->ranBytecode()); return true; } @@ -1248,7 +1275,7 @@ JSScript::ensureRanBytecode(JSContext *cx) inline bool JSScript::ensureRanInference(JSContext *cx) { - if (!ensureRanBytecode(cx)) + if (!ensureRanAnalysis(cx)) return false; if (!analysis()->ranInference()) { js::types::AutoEnterTypeInference enter(cx); @@ -1270,6 +1297,13 @@ JSScript::analysis() return types->analysis; } +inline void +JSScript::clearAnalysis() +{ + if (types) + types->analysis = NULL; +} + inline void js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32 which, js::types::Type type) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 1a8e04a1b5bf..c2a1dea04ef1 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -300,7 +300,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) * to, but not including, that prototype. */ limitClone = &fp->scopeChain(); - while (limitClone->getClass() == &js_WithClass) + while (limitClone->isWith()) limitClone = limitClone->getParent(); JS_ASSERT(limitClone); @@ -402,10 +402,8 @@ CallThisObjectHook(JSContext *cx, JSObject *obj, Value *argv) return thisp; } -namespace js { - void -ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) +js::ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) { Value &thisv = vp[1]; @@ -413,11 +411,11 @@ ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) if (thisv.isObject()) { JS_ASSERT(thisv.toObject().getClass() != clasp); } else if (thisv.isString()) { - JS_ASSERT(clasp != &js_StringClass); + JS_ASSERT(clasp != &StringClass); } else if (thisv.isNumber()) { - JS_ASSERT(clasp != &js_NumberClass); + JS_ASSERT(clasp != &NumberClass); } else if (thisv.isBoolean()) { - JS_ASSERT(clasp != &js_BooleanClass); + JS_ASSERT(clasp != &BooleanClass); } else { JS_ASSERT(thisv.isUndefined() || thisv.isNull()); } @@ -448,7 +446,7 @@ ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) * The alert should display "true". */ bool -BoxNonStrictThis(JSContext *cx, const CallReceiver &call) +js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call) { /* * Check for SynthesizeFrame poisoning and fast constructors which @@ -476,8 +474,6 @@ BoxNonStrictThis(JSContext *cx, const CallReceiver &call) return true; } -} - #if JS_HAS_NO_SUCH_METHOD const uint32 JSSLOT_FOUND_FUNCTION = 0; @@ -524,8 +520,8 @@ Class js_NoSuchMethodClass = { * call by name, and args is an Array containing this invocation's actual * parameters. */ -JSBool -js_OnUnknownMethod(JSContext *cx, Value *vp) +bool +js::OnUnknownMethod(JSContext *cx, Value *vp) { JS_ASSERT(!vp[1].isPrimitive()); @@ -586,10 +582,8 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp) #endif /* JS_HAS_NO_SUCH_METHOD */ -namespace js { - JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, StackFrame *fp) +js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp) { JS_ASSERT(script); JS_ASSERT(fp == cx->fp()); @@ -626,11 +620,9 @@ RunScript(JSContext *cx, JSScript *script, StackFrame *fp) * required arguments, allocate declared local variables, and pop everything * when done. Then push the return value. */ -JS_REQUIRES_STACK bool -InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct) +bool +js::InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct) { - /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */ - CallArgs args = argsRef; JS_ASSERT(args.argc() <= StackSpace::ARGS_LENGTH_MAX); @@ -648,7 +640,7 @@ InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct) Class *clasp = callee.getClass(); /* Invoke non-functions. */ - if (JS_UNLIKELY(clasp != &js_FunctionClass)) { + if (JS_UNLIKELY(clasp != &FunctionClass)) { #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(clasp == &js_NoSuchMethodClass)) return NoSuchMethod(cx, args.argc(), args.base()); @@ -667,22 +659,6 @@ InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct) if (fun->isNative()) return CallJSNative(cx, fun->u.n.native, args); - /* Handle the empty-script special case. */ - JSScript *script = fun->script(); - if (JS_UNLIKELY(script->isEmpty())) { - if (construct) { - bool newType = cx->typeInferenceEnabled() && cx->fp()->isScriptFrame() && - UseNewType(cx, cx->fp()->script(), cx->regs().pc); - JSObject *obj = js_CreateThisForFunction(cx, &callee, newType); - if (!obj) - return false; - args.rval().setObject(*obj); - } else { - args.rval().setUndefined(); - } - return true; - } - TypeMonitorCall(cx, args, construct); /* Get pointer to new frame/slots, prepare arguments. */ @@ -692,14 +668,14 @@ InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct) /* Now that the new frame is rooted, maybe create a call object. */ StackFrame *fp = ifg.fp(); - if (fun->isHeavyweight() && !CreateFunCallObject(cx, fp)) + if (!fp->functionPrologue(cx)) return false; /* Run function until JSOP_STOP, JSOP_RETURN or error. */ JSBool ok; { AutoPreserveEnumerators preserve(cx); - ok = RunScript(cx, script, fp); + ok = RunScript(cx, fun->script(), fp); } args.rval() = fp->returnValue(); @@ -733,13 +709,15 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this if (!calleev.isObject()) break; JSObject &callee = calleev.toObject(); - if (callee.getClass() != &js_FunctionClass) + if (callee.getClass() != &FunctionClass) break; JSFunction *fun = callee.getFunctionPrivate(); if (fun->isNative()) break; script_ = fun->script(); - if (fun->isHeavyweight() || script_->isEmpty()) + if (!script_->ensureRanAnalysis(cx, fun, callee.getParent())) + return false; + if (FunctionNeedsPrologue(cx, fun) || script_->isEmpty()) break; /* @@ -806,11 +784,9 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this } bool -Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv, - Value *rval) +js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv, + Value *rval) { - LeaveTrace(cx); - InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, argc, &args)) return false; @@ -839,10 +815,8 @@ Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value * } bool -InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval) +js::InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval) { - LeaveTrace(cx); - InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, argc, &args)) return false; @@ -859,8 +833,8 @@ InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Val } bool -InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv, - Value *rval) +js::InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv, + Value *rval) { LeaveTrace(cx); @@ -902,8 +876,8 @@ InitSharpSlots(JSContext *cx, StackFrame *fp) #endif bool -ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv, - ExecuteType type, StackFrame *evalInFrame, Value *result) +js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv, + ExecuteType type, StackFrame *evalInFrame, Value *result) { JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG); @@ -931,6 +905,9 @@ ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value Probes::startExecution(cx, script); + if (!script->ensureRanAnalysis(cx, NULL, &scopeChain)) + return false; + TypeScript::SetThis(cx, script, fp->thisValue()); AutoPreserveEnumerators preserve(cx); @@ -938,13 +915,16 @@ ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value if (result && ok) *result = fp->returnValue(); + if (fp->isStrictEvalFrame()) + js_PutCallObject(fp); + Probes::stopExecution(cx, script); return !!ok; } bool -Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval) +js::Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval) { /* The scope chain could be anything, so innerize just in case. */ JSObject *scopeChain = &scopeChainArg; @@ -974,7 +954,7 @@ Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval) } bool -CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs) +js::CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs) { JSObject *obj2; JSProperty *prop; @@ -1045,7 +1025,7 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs) } JSBool -HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) +js::HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) { Class *clasp = obj->getClass(); if (clasp->hasInstance) @@ -1056,7 +1036,7 @@ HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) } bool -LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *result) +js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *result) { #if JS_HAS_XML_SUPPORT if (JS_UNLIKELY(lval.isObject() && lval.toObject().isXML()) || @@ -1127,7 +1107,7 @@ LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *result } bool -StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal) +js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal) { Value lval = lref, rval = rref; if (SameType(lval, rval)) { @@ -1179,7 +1159,7 @@ IsNaN(const Value &v) } bool -SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same) +js::SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same) { if (IsNegativeZero(v1)) { *same = IsNegativeZero(v2); @@ -1197,7 +1177,7 @@ SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same) } JSType -TypeOfValue(JSContext *cx, const Value &vref) +js::TypeOfValue(JSContext *cx, const Value &vref) { Value v = vref; if (v.isNumber()) @@ -1214,24 +1194,24 @@ TypeOfValue(JSContext *cx, const Value &vref) return JSTYPE_BOOLEAN; } -JS_REQUIRES_STACK bool -InvokeConstructorKernel(JSContext *cx, const CallArgs &argsRef) +bool +js::InvokeConstructorKernel(JSContext *cx, const CallArgs &argsRef) { - JS_ASSERT(!js_FunctionClass.construct); + JS_ASSERT(!FunctionClass.construct); CallArgs args = argsRef; if (args.calleev().isObject()) { JSObject *callee = &args.callee(); Class *clasp = callee->getClass(); - if (clasp == &js_FunctionClass) { + if (clasp == &FunctionClass) { JSFunction *fun = callee->getFunctionPrivate(); if (fun->isConstructor()) { args.thisv().setMagicWithObjectOrNullPayload(NULL); - Probes::calloutBegin(cx, fun); - bool ok = CallJSNativeConstructor(cx, fun->u.n.native, args); - Probes::calloutEnd(cx, fun); - return ok; + Probes::calloutBegin(cx, fun); + bool ok = CallJSNativeConstructor(cx, fun->u.n.native, args); + Probes::calloutEnd(cx, fun); + return ok; } if (!fun->isInterpretedConstructor()) @@ -1255,11 +1235,9 @@ error: } bool -InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval, - uintN argc, Value *argv, Value *rval) +js::InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval, + uintN argc, Value *argv, Value *rval) { - LeaveTrace(cx); - InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, argc, &args)) return JS_FALSE; @@ -1273,7 +1251,7 @@ InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fv Class *clasp = callee.getClass(); JSFunction *fun; bool ok; - if (clasp == &js_FunctionClass && (fun = callee.getFunctionPrivate())->isConstructor()) { + if (clasp == &FunctionClass && (fun = callee.getFunctionPrivate())->isConstructor()) { args.thisv().setMagicWithObjectOrNullPayload(thisobj); Probes::calloutBegin(cx, fun); ok = CallJSNativeConstructor(cx, fun->u.n.native, args); @@ -1291,7 +1269,7 @@ InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fv } bool -ValueToId(JSContext *cx, const Value &v, jsid *idp) +js::ValueToId(JSContext *cx, const Value &v, jsid *idp) { int32_t i; if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) { @@ -1312,14 +1290,12 @@ ValueToId(JSContext *cx, const Value &v, jsid *idp) return js_ValueToStringId(cx, v, idp); } -} /* namespace js */ - /* * Enter the new with scope using an object at sp[-1] and associate the depth * of the with block with sp + stackIndex. */ -JS_REQUIRES_STACK JSBool -js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) +static bool +EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) { StackFrame *fp = cx->fp(); Value *sp = cx->regs().sp; @@ -1353,55 +1329,47 @@ js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) return JS_TRUE; } -JS_REQUIRES_STACK void -js_LeaveWith(JSContext *cx) +static void +LeaveWith(JSContext *cx) { JSObject *withobj; withobj = &cx->fp()->scopeChain(); - JS_ASSERT(withobj->getClass() == &js_WithClass); + JS_ASSERT(withobj->getClass() == &WithClass); JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp())); JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0); withobj->setPrivate(NULL); cx->fp()->setScopeChainNoCallObj(*withobj->getParent()); } -JS_REQUIRES_STACK Class * -js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth) +bool +js::IsActiveWithOrBlock(JSContext *cx, JSObject &obj, int stackDepth) { - Class *clasp; - - clasp = obj->getClass(); - if ((clasp == &js_WithClass || clasp == &js_BlockClass) && - obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) && - OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) { - return clasp; - } - return NULL; + return (obj.isWith() || obj.isBlock()) && + obj.getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) && + OBJ_BLOCK_DEPTH(cx, &obj) >= stackDepth; } /* * Unwind block and scope chains to match the given depth. The function sets * fp->sp on return to stackDepth. */ -JS_REQUIRES_STACK JSBool -js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind) +bool +js::UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind) { - Class *clasp; - JS_ASSERT(stackDepth >= 0); JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp); StackFrame *fp = cx->fp(); for (;;) { - clasp = js_IsActiveWithOrBlock(cx, &fp->scopeChain(), stackDepth); - if (!clasp) + JSObject &scopeChain = fp->scopeChain(); + if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth)) break; - if (clasp == &js_BlockClass) { + if (scopeChain.isBlock()) { /* Don't fail until after we've updated all stacks. */ normalUnwind &= js_PutBlockObject(cx, normalUnwind); } else { - js_LeaveWith(cx); + LeaveWith(cx); } } @@ -1409,8 +1377,14 @@ js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind) return normalUnwind; } -JSBool -js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2) +/* + * Find the results of incrementing or decrementing *vp. For pre-increments, + * both *vp and *vp2 will contain the result on return. For post-increments, + * vp will contain the original value converted to a number and vp2 will get + * the result. Both vp and vp2 must be roots. + */ +static bool +DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2) { if (cs->format & JOF_POST) { double d; @@ -1685,7 +1659,7 @@ JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH); static inline bool IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval) { - if (iterobj->getClass() == &js_IteratorClass) { + if (iterobj->isIterator()) { NativeIterator *ni = iterobj->getNativeIterator(); if (ni->isKeyIter()) { *cond = (ni->props_cursor < ni->props_end); @@ -1701,7 +1675,7 @@ IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval) static inline bool IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) { - if (iterobj->getClass() == &js_IteratorClass) { + if (iterobj->isIterator()) { NativeIterator *ni = iterobj->getNativeIterator(); if (ni->isKeyIter()) { JS_ASSERT(ni->props_cursor < ni->props_end); @@ -1733,10 +1707,8 @@ TypeCheckNextBytecode(JSContext *cx, JSScript *script, unsigned n, const FrameRe #endif } -namespace js { - -JS_REQUIRES_STACK JS_NEVER_INLINE bool -Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) +JS_NEVER_INLINE bool +js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) { #ifdef MOZ_TRACEVIS TraceVisStateObj tvso(cx, S_INTERP); @@ -2371,15 +2343,12 @@ BEGIN_CASE(JSOP_POPN) OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj) <= (size_t) (regs.sp - regs.fp()->base())); for (obj = ®s.fp()->scopeChain(); obj; obj = obj->getParent()) { - Class *clasp = obj->getClass(); - if (clasp != &js_BlockClass && clasp != &js_WithClass) + if (!obj->isBlock() || !obj->isWith()) continue; if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp())) break; JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) - + ((clasp == &js_BlockClass) - ? OBJ_BLOCK_COUNT(cx, obj) - : 1) + + (obj->isBlock() ? OBJ_BLOCK_COUNT(cx, obj) : 1) <= regs.sp); } #endif @@ -2392,7 +2361,7 @@ BEGIN_CASE(JSOP_POPV) END_CASE(JSOP_POPV) BEGIN_CASE(JSOP_ENTERWITH) - if (!js_EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH)) + if (!EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH)) goto error; /* @@ -2410,7 +2379,7 @@ END_CASE(JSOP_ENTERWITH) BEGIN_CASE(JSOP_LEAVEWITH) JS_ASSERT(regs.sp[-1].toObject() == regs.fp()->scopeChain()); regs.sp--; - js_LeaveWith(cx); + LeaveWith(cx); END_CASE(JSOP_LEAVEWITH) BEGIN_CASE(JSOP_RETURN) @@ -2451,7 +2420,7 @@ BEGIN_CASE(JSOP_STOP) inline_return: { JS_ASSERT(!regs.fp()->hasImacropc()); - JS_ASSERT(!js_IsActiveWithOrBlock(cx, ®s.fp()->scopeChain(), 0)); + JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0)); interpReturnOK = ScriptEpilogue(cx, regs.fp(), interpReturnOK); /* The JIT inlines ScriptEpilogue. */ @@ -3531,7 +3500,7 @@ do_incop: } else { /* We need an extra root for the result. */ PUSH_NULL(); - if (!js_DoIncDec(cx, cs, ®s.sp[-2], ®s.sp[-1])) + if (!DoIncDec(cx, cs, ®s.sp[-2], ®s.sp[-1])) goto error; { @@ -3603,7 +3572,7 @@ BEGIN_CASE(JSOP_LOCALINC) PUSH_INT32(tmp + incr2); } else { PUSH_COPY(*vp); - if (!js_DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp)) + if (!DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp)) goto error; TypeScript::MonitorOverflow(cx, script, regs.pc); } @@ -3809,7 +3778,7 @@ BEGIN_CASE(JSOP_CALLPROP) if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) { LOAD_ATOM(0, atom); regs.sp[-2].setString(atom); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) + if (!OnUnknownMethod(cx, regs.sp - 2)) goto error; } #endif @@ -4085,10 +4054,10 @@ BEGIN_CASE(JSOP_CALLELEM) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) { - /* For js_OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */ + /* For OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */ regs.sp[-2] = regs.sp[-1]; regs.sp[-1].setObject(*thisObj); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) + if (!OnUnknownMethod(cx, regs.sp - 2)) goto error; } else #endif @@ -4207,15 +4176,16 @@ BEGIN_CASE(JSOP_FUNAPPLY) RESTORE_INTERP_VARS(); - /* Only create call object after frame is rooted. */ - if (fun->isHeavyweight() && !CreateFunCallObject(cx, regs.fp())) + if (!regs.fp()->functionPrologue(cx)) goto error; RESET_USE_METHODJIT(); TRACE_0(EnterFrame); + bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc); + #ifdef JS_METHODJIT - { + if (!newType) { /* Try to ensure methods are method JIT'd. */ mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL) ? mjit::CompileRequest_Interpreter @@ -4233,7 +4203,6 @@ BEGIN_CASE(JSOP_FUNAPPLY) } #endif - bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc); if (!ScriptPrologue(cx, regs.fp(), newType)) goto error; @@ -4324,7 +4293,7 @@ BEGIN_CASE(JSOP_CALLNAME) } else { shape = (Shape *)prop; JSObject *normalized = obj; - if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter()) + if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter()) normalized = js_UnwrapWithObject(cx, normalized); NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval); } @@ -5022,7 +4991,7 @@ BEGIN_CASE(JSOP_LAMBDA) const Value &lref = regs.sp[-1]; JS_ASSERT(lref.isObject()); JSObject *obj2 = &lref.toObject(); - JS_ASSERT(obj2->getClass() == &js_ObjectClass); + JS_ASSERT(obj2->isObject()); #endif fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); @@ -5238,8 +5207,8 @@ BEGIN_CASE(JSOP_NEWINIT) if (i == JSProto_Array) { obj = NewDenseEmptyArray(cx); } else { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); - obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = GuessObjectGCKind(0, false); + obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); } if (!obj) @@ -5825,7 +5794,7 @@ BEGIN_CASE(JSOP_ENDFILTER) bool cond = !regs.sp[-1].isMagic(); if (cond) { /* Exit the "with" block left from the previous iteration. */ - js_LeaveWith(cx); + LeaveWith(cx); } if (!js_StepXMLListFilter(cx, cond)) goto error; @@ -5835,7 +5804,7 @@ BEGIN_CASE(JSOP_ENDFILTER) * temporaries. */ JS_ASSERT(IsXML(regs.sp[-1])); - if (!js_EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH)) + if (!EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH)) goto error; regs.sp--; len = GET_JUMP_OFFSET(regs.pc); @@ -5962,10 +5931,9 @@ BEGIN_CASE(JSOP_ENTERBLOCK) * static scope. */ JSObject *obj2 = ®s.fp()->scopeChain(); - Class *clasp; - while ((clasp = obj2->getClass()) == &js_WithClass) + while (obj2->isWith()) obj2 = obj2->getParent(); - if (clasp == &js_BlockClass && + if (obj2->isBlock() && obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) { JSObject *youngestProto = obj2->getProto(); JS_ASSERT(youngestProto->isStaticBlock()); @@ -6230,7 +6198,7 @@ END_CASE(JSOP_ARRAYPUSH) */ regs.pc = (script)->main() + tn->start + tn->length; - JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE); + JSBool ok = UnwindScope(cx, tn->stackDepth, JS_TRUE); JS_ASSERT(regs.sp == regs.fp()->base() + tn->stackDepth); if (!ok) { /* @@ -6300,12 +6268,12 @@ END_CASE(JSOP_ARRAYPUSH) forced_return: /* * Unwind the scope making sure that interpReturnOK stays false even when - * js_UnwindScope returns true. + * UnwindScope returns true. * * When a trap handler returns JSTRAP_RETURN, we jump here with * interpReturnOK set to true bypassing any finally blocks. */ - interpReturnOK &= js_UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending()); + interpReturnOK &= UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending()); JS_ASSERT(regs.sp == regs.fp()->base()); if (entryFrame != regs.fp()) @@ -6339,7 +6307,7 @@ END_CASE(JSOP_ARRAYPUSH) #endif JS_ASSERT_IF(!regs.fp()->isGeneratorFrame(), - !js_IsActiveWithOrBlock(cx, ®s.fp()->scopeChain(), 0)); + !IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0)); #ifdef JS_METHODJIT /* @@ -6359,5 +6327,3 @@ END_CASE(JSOP_ARRAYPUSH) } goto error; } - -} /* namespace js */ diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 35506c2d19f1..f4e013db1b25 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -187,32 +187,6 @@ extern bool InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv, Value *rval); -/* - * Natives like sort/forEach/replace call Invoke repeatedly with the same - * callee, this, and number of arguments. To optimize this, such natives can - * start an "invoke session" to factor out much of the dynamic setup logic - * required by a normal Invoke. Usage is: - * - * InvokeSessionGuard session(cx); - * if (!session.start(cx, callee, thisp, argc, &session)) - * ... - * - * while (...) { - * // write actual args (not callee, this) - * session[0] = ... - * ... - * session[argc - 1] = ... - * - * if (!session.invoke(cx, session)) - * ... - * - * ... = session.rval(); - * } - * - * // session ended by ~InvokeSessionGuard - */ -class InvokeSessionGuard; - /* * InvokeConstructor* implement a function call from a constructor context * (e.g. 'new') handling the the creation of the new 'this' object. @@ -360,34 +334,19 @@ class InterpreterFrames { const InterruptEnablerBase &enabler; }; -} /* namespace js */ - -extern JS_REQUIRES_STACK JSBool -js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen); - -extern JS_REQUIRES_STACK void -js_LeaveWith(JSContext *cx); - -/* - * Find the results of incrementing or decrementing *vp. For pre-increments, - * both *vp and *vp2 will contain the result on return. For post-increments, - * vp will contain the original value converted to a number and vp2 will get - * the result. Both vp and vp2 must be roots. - */ -extern JSBool -js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, js::Value *vp, js::Value *vp2); - /* * Unwind block and scope chains to match the given depth. The function sets * fp->sp on return to stackDepth. */ -extern JS_REQUIRES_STACK JSBool -js_UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind); +extern bool +UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind); -extern JSBool -js_OnUnknownMethod(JSContext *cx, js::Value *vp); +extern bool +OnUnknownMethod(JSContext *cx, js::Value *vp); -extern JS_REQUIRES_STACK js::Class * -js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth); +extern bool +IsActiveWithOrBlock(JSContext *cx, JSObject &obj, int stackDepth); + +} /* namespace js */ #endif /* jsinterp_h___ */ diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 2b2d507d9ceb..823bd6c326e7 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -149,8 +149,18 @@ InvokeSessionGuard::invoke(JSContext *cx) return Invoke(cx, args_); #endif - /* Clear any garbage left from the last Invoke. */ StackFrame *fp = ifg_.fp(); + + /* + * Clear any activation objects on the frame. Normally the frame should not + * have any, but since we leave it on the stack between calls to invoke() + * the debugger can start operating on it. See markFunctionEpilogueDone() + * calls below. :XXX: this is pretty gross, and slows us down. Can the + * debugger be prevented from observing this frame? + */ + fp->functionEpilogue(/* activationOnly = */ true); + fp->markFunctionEpilogueDone(/* activationOnly = */ true); + fp->resetCallFrame(script_); JSBool ok; @@ -164,11 +174,20 @@ InvokeSessionGuard::invoke(JSContext *cx) #else cx->regs().pc = script_->code; ok = Interpret(cx, cx->fp()); + + /* Interpret does not perform the entry frame's epilogue, unlike EnterMethodJIT. */ + cx->fp()->functionEpilogue(); #endif Probes::exitJSFun(cx, fp->fun(), script_); args_.setInactive(); } + /* + * Clear activation object flags, for the functionEpilogue() call in the + * next invoke(). + */ + fp->markFunctionEpilogueDone(/* activationOnly = */ true); + /* Don't clobber callee with rval; rval gets read from fp->rval. */ return ok; } @@ -182,7 +201,7 @@ class PrimitiveBehavior { public: static inline bool isType(const Value &v) { return v.isString(); } static inline JSString *extract(const Value &v) { return v.toString(); } - static inline Class *getClass() { return &js_StringClass; } + static inline Class *getClass() { return &StringClass; } }; template<> @@ -190,7 +209,7 @@ class PrimitiveBehavior { public: static inline bool isType(const Value &v) { return v.isBoolean(); } static inline bool extract(const Value &v) { return v.toBoolean(); } - static inline Class *getClass() { return &js_BooleanClass; } + static inline Class *getClass() { return &BooleanClass; } }; template<> @@ -198,7 +217,7 @@ class PrimitiveBehavior { public: static inline bool isType(const Value &v) { return v.isNumber(); } static inline double extract(const Value &v) { return v.toNumber(); } - static inline Class *getClass() { return &js_NumberClass; } + static inline Class *getClass() { return &NumberClass; } }; } // namespace detail @@ -331,6 +350,20 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex) return pobj; } +inline bool +FunctionNeedsPrologue(JSContext *cx, JSFunction *fun) +{ + /* Heavyweight functions need call objects created. */ + if (fun->isHeavyweight()) + return true; + + /* Outer and inner functions need to preserve nesting invariants. */ + if (cx->typeInferenceEnabled() && fun->script()->nesting()) + return true; + + return false; +} + inline bool ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType) { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index b0293da57187..edf2a5fcdf35 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -90,7 +90,7 @@ static void iterator_finalize(JSContext *cx, JSObject *obj); static void iterator_trace(JSTracer *trc, JSObject *obj); static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly); -Class js_IteratorClass = { +Class js::IteratorClass = { "Iterator", JSCLASS_HAS_PRIVATE | JSCLASS_CONCURRENT_FINALIZER | @@ -130,7 +130,7 @@ NativeIterator::mark(JSTracer *trc) static void iterator_finalize(JSContext *cx, JSObject *obj) { - JS_ASSERT(obj->getClass() == &js_IteratorClass); + JS_ASSERT(obj->isIterator()); NativeIterator *ni = obj->getNativeIterator(); if (ni) { @@ -418,12 +418,12 @@ NewIteratorObject(JSContext *cx, uintN flags) EmptyShape *emptyEnumeratorShape = EmptyShape::getEmptyEnumeratorShape(cx); if (!emptyEnumeratorShape) return NULL; - obj->init(cx, &js_IteratorClass, &types::emptyTypeObject, NULL, NULL, false); + obj->init(cx, &IteratorClass, &types::emptyTypeObject, NULL, NULL, false); obj->setMap(emptyEnumeratorShape); return obj; } - return NewBuiltinClassInstance(cx, &js_IteratorClass); + return NewBuiltinClassInstance(cx, &IteratorClass); } NativeIterator * @@ -571,7 +571,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) if (obj) { /* Enumerate Iterator.prototype directly. */ JSIteratorOp op = obj->getClass()->ext.iteratorObject; - if (op && (obj->getClass() != &js_IteratorClass || obj->getNativeIterator())) { + if (op && (obj->getClass() != &IteratorClass || obj->getNativeIterator())) { JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH)); if (!iterobj) return false; @@ -718,8 +718,8 @@ iterator_next(JSContext *cx, uintN argc, Value *vp) JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return false; - if (obj->getClass() != &js_IteratorClass) { - ReportIncompatibleMethod(cx, vp, &js_IteratorClass); + if (!obj->isIterator()) { + ReportIncompatibleMethod(cx, vp, &IteratorClass); return false; } @@ -792,8 +792,7 @@ js_CloseIterator(JSContext *cx, JSObject *obj) { cx->iterValue.setMagic(JS_NO_ITER_VALUE); - Class *clasp = obj->getClass(); - if (clasp == &js_IteratorClass) { + if (obj->isIterator()) { /* Remove enumerators from the active list, which is a stack. */ NativeIterator *ni = obj->getNativeIterator(); @@ -812,7 +811,7 @@ js_CloseIterator(JSContext *cx, JSObject *obj) } } #if JS_HAS_GENERATORS - else if (clasp == &js_GeneratorClass) { + else if (obj->isGenerator()) { return CloseGenerator(cx, obj); } #endif @@ -943,7 +942,7 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) { /* Fast path for native iterators */ NativeIterator *ni = NULL; - if (iterobj->getClass() == &js_IteratorClass) { + if (iterobj->isIterator()) { /* Key iterators are handled by fast-paths. */ ni = iterobj->getNativeIterator(); if (ni) { @@ -971,7 +970,7 @@ js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) return false; if (!Invoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) { /* Check for StopIteration. */ - if (!cx->isExceptionPending() || !js_ValueIsStopIteration(cx->getPendingException())) + if (!cx->isExceptionPending() || !IsStopIteration(cx->getPendingException())) return false; cx->clearPendingException(); @@ -1000,7 +999,7 @@ JSBool js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) { /* Fast path for native iterators */ - if (iterobj->getClass() == &js_IteratorClass) { + if (iterobj->isIterator()) { /* * Implement next directly as all the methods of the native iterator are * read-only and permanent. @@ -1039,11 +1038,11 @@ js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) static JSBool stopiter_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) { - *bp = js_ValueIsStopIteration(*v); + *bp = IsStopIteration(*v); return JS_TRUE; } -Class js_StopIterationClass = { +Class js::StopIterationClass = { js_StopIteration_str, JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) | JSCLASS_FREEZE_PROTO, @@ -1111,7 +1110,7 @@ generator_trace(JSTracer *trc, JSObject *obj) MarkStackRangeConservatively(trc, fp->slots(), gen->regs.sp); } -Class js_GeneratorClass = { +Class js::GeneratorClass = { js_Generator_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) | JSCLASS_IS_ANONYMOUS, @@ -1150,7 +1149,7 @@ Class js_GeneratorClass = { JS_REQUIRES_STACK JSObject * js_NewGenerator(JSContext *cx) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass); + JSObject *obj = NewBuiltinClassInstance(cx, &GeneratorClass); if (!obj) return NULL; @@ -1309,7 +1308,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, static JS_REQUIRES_STACK JSBool CloseGenerator(JSContext *cx, JSObject *obj) { - JS_ASSERT(obj->getClass() == &js_GeneratorClass); + JS_ASSERT(obj->isGenerator()); JSGenerator *gen = (JSGenerator *) obj->getPrivate(); if (!gen) { @@ -1334,8 +1333,8 @@ generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc) JSObject *obj = ToObject(cx, &vp[1]); if (!obj) return JS_FALSE; - if (obj->getClass() != &js_GeneratorClass) { - ReportIncompatibleMethod(cx, vp, &js_GeneratorClass); + if (!obj->isGenerator()) { + ReportIncompatibleMethod(cx, vp, &GeneratorClass); return JS_FALSE; } @@ -1426,11 +1425,11 @@ static JSFunctionSpec generator_methods[] = { static bool InitIteratorClass(JSContext *cx, GlobalObject *global) { - JSObject *iteratorProto = global->createBlankPrototype(cx, &js_IteratorClass); + JSObject *iteratorProto = global->createBlankPrototype(cx, &IteratorClass); if (!iteratorProto) return false; - JSFunction *ctor = global->createConstructor(cx, Iterator, &js_IteratorClass, + JSFunction *ctor = global->createConstructor(cx, Iterator, &IteratorClass, CLASS_ATOM(cx, Iterator), 2); if (!ctor) return false; @@ -1448,7 +1447,7 @@ static bool InitGeneratorClass(JSContext *cx, GlobalObject *global) { #if JS_HAS_GENERATORS - JSObject *proto = global->createBlankPrototype(cx, &js_GeneratorClass); + JSObject *proto = global->createBlankPrototype(cx, &GeneratorClass); if (!proto) return false; @@ -1465,7 +1464,7 @@ InitGeneratorClass(JSContext *cx, GlobalObject *global) static JSObject * InitStopIterationClass(JSContext *cx, GlobalObject *global) { - JSObject *proto = global->createBlankPrototype(cx, &js_StopIterationClass); + JSObject *proto = global->createBlankPrototype(cx, &StopIterationClass); if (!proto || !proto->freeze(cx)) return NULL; @@ -1473,7 +1472,7 @@ InitStopIterationClass(JSContext *cx, GlobalObject *global) if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto)) return NULL; - MarkStandardClassInitializedNoProto(global, &js_StopIterationClass); + MarkStandardClassInitializedNoProto(global, &StopIterationClass); return proto; } diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 0605d7bed5c6..7939df660f3a 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -231,16 +231,16 @@ js_LiveFrameIfGenerator(js::StackFrame *fp) #endif -extern js::Class js_GeneratorClass; -extern js::Class js_IteratorClass; -extern js::Class js_StopIterationClass; +namespace js { static inline bool -js_ValueIsStopIteration(const js::Value &v) +IsStopIteration(const js::Value &v) { - return v.isObject() && v.toObject().getClass() == &js_StopIterationClass; + return v.isObject() && v.toObject().isStopIteration(); } +} /* namespace js */ + extern JSObject * js_InitIteratorClasses(JSContext *cx, JSObject *obj); diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 463a1a43c004..c56741691df0 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -106,7 +106,7 @@ MathCache::MathCache() { JS_ASSERT(hash(-0.0) != hash(+0.0)); } -Class js_MathClass = { +Class js::MathClass = { js_Math_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Math), PropertyStub, /* addProperty */ @@ -865,7 +865,7 @@ js_IsMathFunction(JSNative native) JSObject * js_InitMathClass(JSContext *cx, JSObject *obj) { - JSObject *Math = NewNonFunction(cx, &js_MathClass, NULL, obj); + JSObject *Math = NewNonFunction(cx, &MathClass, NULL, obj); if (!Math || !Math->setSingletonType(cx)) return NULL; @@ -879,7 +879,7 @@ js_InitMathClass(JSContext *cx, JSObject *obj) if (!JS_DefineConstDoubles(cx, Math, math_constants)) return NULL; - MarkStandardClassInitializedNoProto(obj, &js_MathClass); + MarkStandardClassInitializedNoProto(obj, &MathClass); return Math; } diff --git a/js/src/jsmath.h b/js/src/jsmath.h index bb9e3c1a8b31..20c61ece9f4e 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -82,8 +82,6 @@ class MathCache * JS math functions. */ -extern js::Class js_MathClass; - extern JSObject * js_InitMathClass(JSContext *cx, JSObject *obj); diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index dfa1b99ee4c4..37de586d9ad8 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -559,7 +559,7 @@ static JSFunctionSpec number_functions[] = { JS_FS_END }; -Class js_NumberClass = { +Class js::NumberClass = { js_Number_str, JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), PropertyStub, /* addProperty */ @@ -588,7 +588,7 @@ Number(JSContext *cx, uintN argc, Value *vp) if (!isConstructing) return true; - JSObject *obj = NewBuiltinClassInstance(cx, &js_NumberClass); + JSObject *obj = NewBuiltinClassInstance(cx, &NumberClass); if (!obj) return false; obj->setPrimitiveThis(vp[0]); @@ -612,7 +612,7 @@ num_toSource(JSContext *cx, uintN argc, Value *vp) } char buf[64]; - JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); + JS_snprintf(buf, sizeof buf, "(new %s(%s))", NumberClass.name, numStr); JSString *str = js_NewStringCopyZ(cx, buf); if (!str) return false; @@ -1106,7 +1106,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) /* XXX must do at least once per new thread, so do it per JSContext... */ FIX_FPU(); - proto = js_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, + proto = js_InitClass(cx, obj, NULL, &NumberClass, Number, 1, NULL, number_methods, NULL, NULL); if (!proto || !(ctor = JS_GetConstructor(cx, proto))) return NULL; diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 792968bf0a0e..a12866238c65 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -160,14 +160,6 @@ FinishRuntimeNumberState(JSRuntime *rt); } /* namespace js */ /* Initialize the Number class, returning its prototype object. */ -extern js::Class js_NumberClass; - -inline bool -JSObject::isNumber() const -{ - return getClass() == &js_NumberClass; -} - extern JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 54ba25276b8a..5243f995214d 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -115,7 +115,7 @@ using namespace js::types; JS_FRIEND_DATA(js::Shape) Shape::sharedNonNative(SHAPELESS); -Class js_ObjectClass = { +Class js::ObjectClass = { js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Object), PropertyStub, /* addProperty */ @@ -1800,7 +1800,7 @@ PropDesc::initFromPropertyDescriptor(const PropertyDescriptor &desc) bool PropDesc::makeObject(JSContext *cx) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); if (!obj) return false; @@ -2630,7 +2630,7 @@ obj_create(JSContext *cx, uintN argc, Value *vp) * Use the callee's global as the parent of the new object to avoid dynamic * scoping (i.e., using the caller's global). */ - JSObject *obj = NewNonFunction(cx, &js_ObjectClass, proto, + JSObject *obj = NewNonFunction(cx, &ObjectClass, proto, vp->toObject().getGlobal()); if (!obj) return JS_FALSE; @@ -2717,6 +2717,15 @@ obj_preventExtensions(JSContext *cx, uintN argc, Value *vp) return obj->preventExtensions(cx, &props); } +size_t +JSObject::sizeOfSlotsArray(size_t(*mus)(void *)) +{ + if (!hasSlotsArray()) + return 0; + size_t usable = mus((void *)slots); + return usable ? usable : numSlots() * sizeof(js::Value); +} + bool JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it) { @@ -2913,8 +2922,8 @@ 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); - obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); + obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); if (!obj) return JS_FALSE; TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Object); @@ -2928,7 +2937,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) @@ -2966,8 +2975,8 @@ js_CreateThis(JSContext *cx, JSObject *callee) { Class *clasp = callee->getClass(); - Class *newclasp = &js_ObjectClass; - if (clasp == &js_FunctionClass) { + Class *newclasp = &ObjectClass; + if (clasp == &FunctionClass) { JSFunction *fun = callee->getFunctionPrivate(); if (fun->isNative() && fun->u.n.clasp) newclasp = fun->u.n.clasp; @@ -2979,7 +2988,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,35 +3004,34 @@ 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, &ObjectClass); return NewObjectWithType(cx, type, parent, kind); } JSObject * js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) { - JSScript *calleeScript = callee->getFunctionPrivate()->script(); JSObject *res; if (proto) { - types::TypeObject *type = proto->getNewType(cx, calleeScript); + types::TypeObject *type = proto->getNewType(cx, callee->getFunctionPrivate()); if (!type) return NULL; res = CreateThisForFunctionWithType(cx, type, callee->getParent()); } else { - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); - res = NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent(), kind); + gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); + res = NewNonFunction(cx, &ObjectClass, proto, callee->getParent(), kind); } if (res && cx->typeInferenceEnabled()) - TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(res)); + TypeScript::SetThis(cx, callee->getFunctionPrivate()->script(), types::Type::ObjectType(res)); return res; } @@ -3065,8 +3073,8 @@ js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType) JSObject* FASTCALL js_Object_tn(JSContext* cx, JSObject* proto) { - JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); - return NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8); + JS_ASSERT(!(ObjectClass.flags & JSCLASS_HAS_PRIVATE)); + return NewObjectWithClassProto(cx, &ObjectClass, proto, FINALIZE_OBJECT8); } JS_DEFINE_TRCINFO_1(js_Object, @@ -3077,8 +3085,8 @@ JSObject* FASTCALL js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj) { if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); - return NewObjectWithClassProto(cx, &js_ObjectClass, proto, kind); + gc::AllocKind kind = GuessObjectGCKind(0, false); + return NewObjectWithClassProto(cx, &ObjectClass, proto, kind); } /* :FIXME: bug 637856 new Objects do not have the right type when created on trace. */ @@ -3129,8 +3137,8 @@ js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot) return NULL; } - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); - return NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, kind); + gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); + return NewNativeClassInstance(cx, &ObjectClass, proto, parent, kind); } JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, OBJECT, UINTN, 0, nanojit::ACCSET_STORE_ANY) @@ -3309,7 +3317,7 @@ with_ThisObject(JSContext *cx, JSObject *obj) return obj->getWithThis(); } -Class js_WithClass = { +Class js::WithClass = { "With", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, PropertyStub, /* addProperty */ @@ -3359,7 +3367,7 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp()); - obj->init(cx, &js_WithClass, type, parent, priv, false); + obj->init(cx, &WithClass, type, parent, priv, false); EmptyShape *emptyWithShape = EmptyShape::getEmptyWithShape(cx); if (!emptyWithShape) @@ -3393,7 +3401,7 @@ js_NewBlockObject(JSContext *cx) EmptyShape *emptyBlockShape = EmptyShape::getEmptyBlockShape(cx); if (!emptyBlockShape) return NULL; - blockObj->init(cx, &js_BlockClass, &emptyTypeObject, NULL, NULL, false); + blockObj->init(cx, &BlockClass, &emptyTypeObject, NULL, NULL, false); blockObj->setMap(emptyBlockShape); return blockObj; @@ -3405,7 +3413,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) @@ -3483,7 +3491,7 @@ block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) } /* Values are in slots immediately following the class-reserved ones. */ - JS_ASSERT(obj->getSlot(JSSLOT_FREE(&js_BlockClass) + index) == *vp); + JS_ASSERT(obj->getSlot(JSSLOT_FREE(&BlockClass) + index) == *vp); return true; } @@ -3516,7 +3524,7 @@ JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index) JS_ASSERT(isStaticBlock()); /* Use JSPROP_ENUMERATE to aid the disassembler. */ - uint32 slot = JSSLOT_FREE(&js_BlockClass) + index; + uint32 slot = JSSLOT_FREE(&BlockClass) + index; const Shape *shape = addProperty(cx, id, block_getProperty, block_setProperty, slot, JSPROP_ENUMERATE | JSPROP_PERMANENT, @@ -3615,9 +3623,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()) { @@ -3733,6 +3739,10 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & JS_ASSERT(!a->isDenseArray() && !b->isDenseArray()); JS_ASSERT(!a->isArrayBuffer() && !b->isArrayBuffer()); + /* New types for a JSObject need to be stable when trading guts. */ + TypeObject *newTypeA = a->newType; + TypeObject *newTypeB = b->newType; + /* Trade the guts of the objects. */ const size_t size = a->structSize(); if (size == b->structSize()) { @@ -3803,6 +3813,9 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & reserved.newaslots = NULL; reserved.newbslots = NULL; } + + a->newType = newTypeA; + b->newType = newTypeB; } /* @@ -3987,7 +4000,7 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp) #endif -Class js_BlockClass = { +Class js::BlockClass = { "Block", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, PropertyStub, /* addProperty */ @@ -4002,7 +4015,7 @@ Class js_BlockClass = { JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj) { - JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1, + JSObject *proto = js_InitClass(cx, obj, NULL, &ObjectClass, js_Object, 1, object_props, object_methods, NULL, object_static_methods); if (!proto) return NULL; @@ -4104,10 +4117,10 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt * otherwise-uninitialized global. * * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is - * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing. + * &FunctionClass, not a JSObject-sized (smaller) GC-thing. * * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to - * be &js_FunctionClass (we could break compatibility easily). But fixing + * be &FunctionClass (we could break compatibility easily). But fixing * (3) is not enough without addressing the bootstrapping dependency on (1) * and (2). */ @@ -4126,7 +4139,7 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt if (!proto->setSingletonType(cx)) return NULL; - if (clasp == &js_ArrayClass && !proto->makeDenseArraySlow(cx)) + if (clasp == &ArrayClass && !proto->makeDenseArraySlow(cx)) return NULL; TypeObject *type = proto->getNewType(cx); @@ -4364,16 +4377,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); } @@ -4411,6 +4423,14 @@ JSObject::allocSlots(JSContext *cx, size_t newcap) bool JSObject::growSlots(JSContext *cx, size_t newcap) { + /* + * Slots are only allocated for call objects when new properties are + * added to them, which can only happen while the call is still on the + * stack (and an eval, DEFFUN, etc. happens). We thus do not need to + * worry about updating any active outer function args/vars. + */ + JS_ASSERT_IF(isCall(), maybeCallObjStackFrame() != NULL); + /* * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to * grow, double its capacity, to add N elements in amortized O(N) time. @@ -4477,6 +4497,15 @@ JSObject::growSlots(JSContext *cx, size_t newcap) void JSObject::shrinkSlots(JSContext *cx, size_t newcap) { + /* + * Refuse to shrink slots for call objects. This only happens in a very + * obscure situation (deleting names introduced by a direct 'eval') and + * allowing the slots pointer to change may require updating pointers in + * the function's active args/vars information. + */ + if (isCall()) + return; + uint32 oldcap = numSlots(); JS_ASSERT(newcap <= oldcap); JS_ASSERT(newcap >= slotSpan()); @@ -5101,7 +5130,7 @@ DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value, if (!shape) { /* Add a new property, or replace an existing one of the same id. */ if (defineHow & DNP_SET_METHOD) { - JS_ASSERT(clasp == &js_ObjectClass); + JS_ASSERT(clasp == &ObjectClass); JS_ASSERT(IsFunctionObject(value)); JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); JS_ASSERT(!getter && !setter); @@ -5403,10 +5432,9 @@ js_FindPropertyHelper(JSContext *cx, jsid id, bool cacheResult, bool global, if (prop) { #ifdef DEBUG if (parent) { - Class *clasp = obj->getClass(); JS_ASSERT(pobj->isNative()); - JS_ASSERT(pobj->getClass() == clasp); - if (clasp == &js_BlockClass) { + JS_ASSERT(pobj->getClass() == obj->getClass()); + if (obj->isBlock()) { /* * A block instance on the scope chain is immutable and * shares its shape with the compile-time prototype. Thus @@ -6299,9 +6327,9 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) Class *clasp = obj->getClass(); if (hint == JSTYPE_STRING) { /* Optimize (new String(...)).toString(). */ - if (clasp == &js_StringClass && + if (clasp == &StringClass && ClassMethodIsNative(cx, obj, - &js_StringClass, + &StringClass, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), js_str_toString)) { *vp = obj->getPrimitiveThis(); @@ -6319,12 +6347,12 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) return true; } else { /* Optimize (new String(...)).valueOf(). */ - if ((clasp == &js_StringClass && - ClassMethodIsNative(cx, obj, &js_StringClass, + if ((clasp == &StringClass && + ClassMethodIsNative(cx, obj, &StringClass, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), js_str_toString)) || - (clasp == &js_NumberClass && - ClassMethodIsNative(cx, obj, &js_NumberClass, + (clasp == &NumberClass && + ClassMethodIsNative(cx, obj, &NumberClass, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), js_num_valueOf))) { *vp = obj->getPrimitiveThis(); @@ -6393,7 +6421,7 @@ CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, JSSecurityCallbacks *callbacks; CheckAccessOp check; - while (JS_UNLIKELY(obj->getClass() == &js_WithClass)) + while (JS_UNLIKELY(obj->isWith())) obj = obj->getProto(); writing = (mode & JSACC_WRITE) != 0; @@ -6545,7 +6573,7 @@ PrimitiveToObject(JSContext *cx, const Value &v) return StringObject::create(cx, v.toString()); JS_ASSERT(v.isNumber() || v.isBoolean()); - Class *clasp = v.isNumber() ? &js_NumberClass : &js_BooleanClass; + Class *clasp = v.isNumber() ? &NumberClass : &BooleanClass; JSObject *obj = NewBuiltinClassInstance(cx, clasp); if (!obj) return NULL; @@ -6943,7 +6971,7 @@ dumpValue(const Value &v) Class *clasp = obj->getClass(); fprintf(stderr, "<%s%s at %p>", clasp->name, - (clasp == &js_ObjectClass) ? "" : " object", + (clasp == &ObjectClass) ? "" : " object", (void *) obj); } else if (v.isBoolean()) { if (v.toBoolean()) diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 540bf04c388d..5614cfb6778b 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -59,11 +59,15 @@ #include "jsvector.h" #include "jscell.h" +namespace nanojit { class ValidateWriter; } + namespace js { -class JSProxyHandler; class AutoPropDescArrayRooter; +class JSProxyHandler; +class RegExp; struct GCMarker; +struct NativeIterator; namespace mjit { class Compiler; } @@ -284,26 +288,54 @@ js_TypeOf(JSContext *cx, JSObject *obj); namespace js { -struct NativeIterator; -class RegExp; - -class GlobalObject; -class ArgumentsObject; -class NormalArgumentsObject; -class StrictArgumentsObject; -class StringObject; - /* ES5 8.12.8. */ extern JSBool DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); -} +extern JS_FRIEND_DATA(Class) AnyNameClass; +extern JS_FRIEND_DATA(Class) AttributeNameClass; +extern JS_FRIEND_DATA(Class) CallClass; +extern JS_FRIEND_DATA(Class) DeclEnvClass; +extern JS_FRIEND_DATA(Class) FunctionClass; +extern JS_FRIEND_DATA(Class) FunctionProxyClass; +extern JS_FRIEND_DATA(Class) NamespaceClass; +extern JS_FRIEND_DATA(Class) OuterWindowProxyClass; +extern JS_FRIEND_DATA(Class) ObjectProxyClass; +extern JS_FRIEND_DATA(Class) QNameClass; +extern JS_FRIEND_DATA(Class) ScriptClass; +extern JS_FRIEND_DATA(Class) XMLClass; -struct JSFunction; +extern Class ArrayClass; +extern Class ArrayBufferClass; +extern Class BlockClass; +extern Class BooleanClass; +extern Class CallableObjectClass; +extern Class DateClass; +extern Class ErrorClass; +extern Class GeneratorClass; +extern Class IteratorClass; +extern Class JSONClass; +extern Class MathClass; +extern Class NumberClass; +extern Class NormalArgumentsObjectClass; +extern Class ObjectClass; +extern Class ProxyClass; +extern Class RegExpClass; +extern Class SlowArrayClass; +extern Class StopIterationClass; +extern Class StringClass; +extern Class StrictArgumentsObjectClass; +extern Class WeakMapClass; +extern Class WithClass; +extern Class XMLFilterClass; -namespace nanojit { -class ValidateWriter; -} +class ArgumentsObject; +class GlobalObject; +class NormalArgumentsObject; +class StrictArgumentsObject; +class StringObject; + +} /* namespace js */ /* * JSObject struct, with members sized to fit in 32 bytes on 32-bit targets, @@ -376,9 +408,9 @@ struct JSObject : js::gc::Cell { */ js::Shape *lastProp; + private: js::Class *clasp; - private: inline void setLastProperty(const js::Shape *shape); inline void removeLastProperty(); @@ -446,6 +478,8 @@ struct JSObject : js::gc::Cell { jsuword initializedLength; }; + JS_FRIEND_API(size_t) sizeOfSlotsArray(size_t(*mus)(void *)); + JSObject *parent; /* object's parent */ void *privateData; /* private data */ jsuword capacity; /* total number of available slots */ @@ -470,6 +504,7 @@ struct JSObject : js::gc::Cell { inline bool isNative() const; inline bool isNewborn() const; + void setClass(js::Class *c) { clasp = c; } js::Class *getClass() const { return clasp; } JSClass *getJSClass() const { return Jsvalify(clasp); } @@ -664,8 +699,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; @@ -700,6 +733,7 @@ struct JSObject : js::gc::Cell { private: inline js::Value* fixedSlots() const; + inline bool hasContiguousSlots(size_t start, size_t count) const; public: /* Minimum size for dynamically allocated slots. */ @@ -760,12 +794,22 @@ struct JSObject : js::gc::Cell { void rollbackProperties(JSContext *cx, uint32 slotSpan); - js::Value& getSlotRef(uintN slot) { - JS_ASSERT(slot < capacity); + js::Value *getSlotAddress(uintN slot) { + /* + * This can be used to get the address of the end of the slots for the + * object, which may be necessary when fetching zero-length arrays of + * slots (e.g. for callObjVarArray). + */ + JS_ASSERT(slot <= capacity); size_t fixed = numFixedSlots(); if (slot < fixed) - return fixedSlots()[slot]; - return slots[slot - fixed]; + return fixedSlots() + slot; + return slots + (slot - fixed); + } + + js::Value &getSlotRef(uintN slot) { + JS_ASSERT(slot < capacity); + return *getSlotAddress(slot); } inline js::Value &nativeGetSlotRef(uintN slot); @@ -855,10 +899,10 @@ struct JSObject : js::gc::Cell { inline void clearType(); inline void setType(js::types::TypeObject *newType); - inline js::types::TypeObject *getNewType(JSContext *cx, JSScript *script = NULL, + inline js::types::TypeObject *getNewType(JSContext *cx, JSFunction *fun = NULL, bool markUnknown = false); private: - void makeNewType(JSContext *cx, JSScript *script, bool markUnknown); + void makeNewType(JSContext *cx, JSFunction *fun, bool markUnknown); public: /* Set a new prototype for an object with a singleton type. */ @@ -1065,6 +1109,14 @@ struct JSObject : js::gc::Cell { inline const js::Value &callObjVar(uintN i) const; inline void setCallObjVar(uintN i, const js::Value &v); + /* + * Get the actual arrays of arguments and variables. Only call if type + * inference is enabled, where we ensure that call object variables are in + * contiguous slots (see NewCallObject). + */ + inline js::Value *callObjArgArray(); + inline js::Value *callObjVarArray(); + /* * Date-specific getters and setters. */ @@ -1200,10 +1252,10 @@ struct JSObject : js::gc::Cell { /* * Slots for XML-related classes are as follows: - * - js_NamespaceClass.base reserves the *_NAME_* and *_NAMESPACE_* slots. - * - js_QNameClass.base, js_AttributeNameClass, js_AnyNameClass reserve + * - NamespaceClass.base reserves the *_NAME_* and *_NAMESPACE_* slots. + * - QNameClass.base, AttributeNameClass, AnyNameClass reserve * the *_NAME_* and *_QNAME_* slots. - * - Others (js_XMLClass, js_XMLFilterClass) don't reserve any slots. + * - Others (XMLClass, js_XMLFilterClass) don't reserve any slots. */ private: static const uint32 JSSLOT_NAME_PREFIX = 0; // shared @@ -1279,7 +1331,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); @@ -1433,43 +1485,57 @@ struct JSObject : js::gc::Cell { inline bool canHaveMethodBarrier() const; - inline bool isArguments() const; - inline bool isNormalArguments() const; - inline bool isStrictArguments() const; - inline bool isArray() const; - inline bool isDenseArray() const; - inline bool isSlowArray() const; - inline bool isNumber() const; - inline bool isBoolean() const; - inline bool isString() const; - inline bool isPrimitive() const; - inline bool isDate() const; - inline bool isFunction() const; - inline bool isObject() const; - inline bool isWith() const; - inline bool isBlock() const; - inline bool isStaticBlock() const; - inline bool isClonedBlock() const; - inline bool isCall() const; - inline bool isRegExp() const; - inline bool isScript() const; - inline bool isError() const; - inline bool isXML() const; - inline bool isXMLId() const; - inline bool isNamespace() const; - inline bool isQName() const; - inline bool isWeakMap() const; + inline bool isArguments() const { return isNormalArguments() || isStrictArguments(); } + inline bool isArrayBuffer() const { return clasp == &js::ArrayBufferClass; } + inline bool isNormalArguments() const { return clasp == &js::NormalArgumentsObjectClass; } + inline bool isStrictArguments() const { return clasp == &js::StrictArgumentsObjectClass; } + inline bool isArray() const { return isSlowArray() || isDenseArray(); } + inline bool isDenseArray() const { return clasp == &js::ArrayClass; } + inline bool isSlowArray() const { return clasp == &js::SlowArrayClass; } + inline bool isNumber() const { return clasp == &js::NumberClass; } + inline bool isBoolean() const { return clasp == &js::BooleanClass; } + inline bool isString() const { return clasp == &js::StringClass; } + inline bool isPrimitive() const { return isNumber() || isString() || isBoolean(); } + inline bool isDate() const { return clasp == &js::DateClass; } + inline bool isFunction() const { return clasp == &js::FunctionClass; } + inline bool isObject() const { return clasp == &js::ObjectClass; } + inline bool isWith() const { return clasp == &js::WithClass; } + inline bool isBlock() const { return clasp == &js::BlockClass; } + inline bool isStaticBlock() const { return isBlock() && !getProto(); } + inline bool isClonedBlock() const { return isBlock() && !!getProto(); } + inline bool isCall() const { return clasp == &js::CallClass; } + inline bool isDeclEnv() const { return clasp == &js::DeclEnvClass; } + inline bool isRegExp() const { return clasp == &js::RegExpClass; } + inline bool isScript() const { return clasp == &js::ScriptClass; } + inline bool isGenerator() const { return clasp == &js::GeneratorClass; } + inline bool isIterator() const { return clasp == &js::IteratorClass; } + inline bool isStopIteration() const { return clasp == &js::StopIterationClass; } + inline bool isError() const { return clasp == &js::ErrorClass; } + inline bool isXML() const { return clasp == &js::XMLClass; } + inline bool isNamespace() const { return clasp == &js::NamespaceClass; } + inline bool isWeakMap() const { return clasp == &js::WeakMapClass; } + inline bool isFunctionProxy() const { return clasp == &js::FunctionProxyClass; } + inline bool isProxy() const { return isObjectProxy() || isFunctionProxy(); } - inline bool isProxy() const; - inline bool isObjectProxy() const; - inline bool isFunctionProxy() const; - inline bool isArrayBuffer() const; + inline bool isXMLId() const { + return clasp == &js::QNameClass || clasp == &js::AttributeNameClass || clasp == &js::AnyNameClass; + } + inline bool isQName() const { + return clasp == &js::QNameClass || clasp == &js::AttributeNameClass || clasp == &js::AnyNameClass; + } + inline bool isObjectProxy() const { + return clasp == &js::ObjectProxyClass || clasp == &js::OuterWindowProxyClass; + } JS_FRIEND_API(bool) isWrapper() const; bool isCrossCompartmentWrapper() const; JS_FRIEND_API(JSObject *) unwrap(uintN *flagsp = NULL); inline void initArrayClass(); + + /*** For jit compiler: ***/ + + static size_t offsetOfClassPointer() { return offsetof(JSObject, clasp); } }; /* Check alignment for any fixed slots allocated after the object. */ @@ -1568,42 +1634,8 @@ class ValueArray { ValueArray(js::Value *v, size_t c) : array(v), length(c) {} }; -extern js::Class js_ArrayClass, js_SlowArrayClass, js_ArrayBufferClass; - -inline bool -JSObject::isDenseArray() const -{ - return getClass() == &js_ArrayClass; -} - -inline bool -JSObject::isSlowArray() const -{ - return getClass() == &js_SlowArrayClass; -} - -inline bool -JSObject::isArray() const -{ - return isDenseArray() || isSlowArray(); -} - -inline bool -JSObject::isArrayBuffer() const -{ - return getClass() == &js_ArrayBufferClass; -} - -extern js::Class js_ObjectClass; -extern js::Class js_WithClass; -extern js::Class js_BlockClass; - -inline bool JSObject::isObject() const { return getClass() == &js_ObjectClass; } -inline bool JSObject::isWith() const { return getClass() == &js_WithClass; } -inline bool JSObject::isBlock() const { return getClass() == &js_BlockClass; } - /* - * Block scope object macros. The slots reserved by js_BlockClass are: + * Block scope object macros. The slots reserved by BlockClass are: * * private StackFrame * active frame pointer or null * JSSLOT_BLOCK_DEPTH int depth of block slots in frame @@ -1827,9 +1859,6 @@ extern JSBool js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value &descriptor, JSBool *bp); -extern JS_FRIEND_DATA(js::Class) js_CallClass; -extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; - namespace js { /* @@ -1898,9 +1927,9 @@ IsCacheableNonGlobalScope(JSObject *obj) JS_ASSERT(obj->getParent()); js::Class *clasp = obj->getClass(); - bool cacheable = (clasp == &js_CallClass || - clasp == &js_BlockClass || - clasp == &js_DeclEnvClass); + bool cacheable = (clasp == &CallClass || + clasp == &BlockClass || + clasp == &DeclEnvClass); JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty); return cacheable; diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 8fcc22281d1a..0fe99af49e3c 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -143,7 +143,7 @@ JSObject::getProperty(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp) } else { if (!js_GetProperty(cx, this, receiver, id, vp)) return false; - JS_ASSERT_IF(!hasSingletonType(), + JS_ASSERT_IF(!hasSingletonType() && nativeContains(js_CheckForStringIndex(id)), js::types::TypeHasProperty(cx, type(), id, *vp)); } return true; @@ -198,7 +198,7 @@ JSObject::finalize(JSContext *cx) inline void JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent) { - init(cx, &js_CallClass, &js::types::emptyTypeObject, parent, NULL, false); + init(cx, &js::CallClass, &js::types::emptyTypeObject, parent, NULL, false); lastProp = bindings.lastShape(); /* @@ -218,7 +218,7 @@ JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent inline void JSObject::initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *frame) { - init(cx, &js_BlockClass, type, NULL, frame, false); + init(cx, &js::BlockClass, type, NULL, frame, false); /* Cloned blocks copy their prototype's map; it had better be shareable. */ JS_ASSERT(!getProto()->inDictionaryMode() || getProto()->lastProp->frozen()); @@ -382,12 +382,6 @@ JSObject::canHaveMethodBarrier() const return isObject() || isFunction() || isPrimitive() || isDate(); } -inline bool -JSObject::isPrimitive() const -{ - return isNumber() || isString() || isBoolean(); -} - inline const js::Value & JSObject::getPrimitiveThis() const { @@ -402,12 +396,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 { @@ -416,6 +404,17 @@ JSObject::hasSlotsArray() const return slots && slots != fixedSlots(); } +inline bool +JSObject::hasContiguousSlots(size_t start, size_t count) const +{ + /* + * Check that the range [start, start+count) is either all inline or all + * out of line. + */ + JS_ASSERT(start + count <= numSlots()); + return (start + count <= numFixedSlots()) || (start >= numFixedSlots()); +} + inline size_t JSObject::structSize() const { @@ -612,6 +611,14 @@ JSObject::setCallObjArg(uintN i, const js::Value &v) setSlot(JSObject::CALL_RESERVED_SLOTS + i, v); } +inline js::Value * +JSObject::callObjArgArray() +{ + js::DebugOnly fun = getCallObjCalleeFunction(); + JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS, fun->nargs)); + return getSlotAddress(JSObject::CALL_RESERVED_SLOTS); +} + inline const js::Value & JSObject::callObjVar(uintN i) const { @@ -630,6 +637,29 @@ JSObject::setCallObjVar(uintN i, const js::Value &v) setSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i, v); } +inline js::Value * +JSObject::callObjVarArray() +{ + JSFunction *fun = getCallObjCalleeFunction(); + JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS + fun->nargs, + fun->script()->bindings.countVars())); + return getSlotAddress(JSObject::CALL_RESERVED_SLOTS + fun->nargs); +} + +namespace js { + +/* + * Any name atom for a function which will be added as a DeclEnv object to the + * scope chain above call objects for fun. + */ +static inline JSAtom * +CallObjectLambdaName(JSFunction *fun) +{ + return (fun->flags & JSFUN_LAMBDA) ? fun->atom : NULL; +} + +} /* namespace js */ + inline const js::Value & JSObject::getDateUTCTime() const { @@ -864,7 +894,7 @@ JSObject::getType(JSContext *cx) } inline js::types::TypeObject * -JSObject::getNewType(JSContext *cx, JSScript *script, bool markUnknown) +JSObject::getNewType(JSContext *cx, JSFunction *fun, bool markUnknown) { if (isDenseArray() && !makeDenseArraySlow(cx)) return NULL; @@ -880,12 +910,12 @@ JSObject::getNewType(JSContext *cx, JSScript *script, bool markUnknown) * Object.create is called with a prototype object that is also the * 'prototype' property of some scripted function. */ - if (newType->newScript && newType->newScript->script != script) + if (newType->newScript && newType->newScript->fun != fun) newType->clearNewScript(cx); if (markUnknown && cx->typeInferenceEnabled() && !newType->unknownProperties()) newType->markUnknown(cx); } else { - makeNewType(cx, script, markUnknown); + makeNewType(cx, fun, markUnknown); } return newType; } @@ -917,7 +947,7 @@ JSObject::init(JSContext *cx, js::Class *aclasp, js::types::TypeObject *type, clasp = aclasp; flags = capacity << FIXED_SLOTS_SHIFT; - JS_ASSERT(denseArray == (aclasp == &js_ArrayClass)); + JS_ASSERT(denseArray == (aclasp == &js::ArrayClass)); #ifdef DEBUG /* @@ -964,7 +994,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); @@ -1174,22 +1204,10 @@ js_IsCallable(const js::Value &v) return v.isObject() && v.toObject().isCallable(); } -inline bool -JSObject::isStaticBlock() const -{ - return isBlock() && !getProto(); -} - -inline bool -JSObject::isClonedBlock() const -{ - return isBlock() && !!getProto(); -} - inline JSObject * js_UnwrapWithObject(JSContext *cx, JSObject *withobj) { - JS_ASSERT(withobj->getClass() == &js_WithClass); + JS_ASSERT(withobj->isWith()); return withobj->getProto(); } @@ -1245,7 +1263,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 +1291,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 +1299,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 +1318,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 +1334,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, */ if (CanBeFinalizedInBackground(kind, clasp)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); @@ -1325,7 +1343,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ - bool denseArray = (clasp == &js_ArrayClass); + bool denseArray = (clasp == &ArrayClass); obj->init(cx, clasp, type, parent, NULL, denseArray); JS_ASSERT(type->canProvideEmptyShape(clasp)); @@ -1343,7 +1361,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 +1376,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 +1410,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 +1475,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 +1496,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) @@ -1493,7 +1511,7 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, */ obj->init(cx, clasp, type, (!parent && proto) ? proto->getParent() : parent, - NULL, clasp == &js_ArrayClass); + NULL, clasp == &ArrayClass); if (clasp->isNative()) { if (!InitScopeForObject(cx, obj, clasp, type, kind)) { @@ -1516,21 +1534,21 @@ NewFunction(JSContext *cx, js::GlobalObject &global) JSObject *proto; if (!js_GetClassPrototype(cx, &global, JSProto_Function, &proto)) return NULL; - return detail::NewObject(cx, &js_FunctionClass, proto, &global, + return detail::NewObject(cx, &FunctionClass, proto, &global, gc::FINALIZE_OBJECT2); } static JS_ALWAYS_INLINE JSObject * NewFunction(JSContext *cx, JSObject *parent) { - return detail::NewObject(cx, &js_FunctionClass, NULL, parent, + return detail::NewObject(cx, &FunctionClass, NULL, parent, gc::FINALIZE_OBJECT2); } 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,16 +1557,16 @@ 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) + if (clasp == &FunctionClass) return detail::NewObject(cx, clasp, proto, parent, kind); return detail::NewObject(cx, clasp, proto, parent, kind); } @@ -1557,7 +1575,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 +1584,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); + if (CanBeFinalizedInBackground(kind, &ObjectClass)) + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); if (!obj) @@ -1581,11 +1599,11 @@ NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc:: * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ - obj->init(cx, &js_ObjectClass, type, + obj->init(cx, &ObjectClass, type, (!parent && type->proto) ? type->proto->getParent() : parent, NULL, false); - if (!InitScopeForObject(cx, obj, &js_ObjectClass, type, kind)) { + if (!InitScopeForObject(cx, obj, &ObjectClass, type, kind)) { obj = NULL; goto out; } @@ -1597,14 +1615,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,29 +1634,28 @@ 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) + if (clasp == &ArrayClass || clasp == &SlowArrayClass) return gc::FINALIZE_OBJECT8; - if (clasp == &js_FunctionClass) + if (clasp == &FunctionClass) return gc::FINALIZE_OBJECT2; return gc::FINALIZE_OBJECT4; } 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) @@ -1653,11 +1670,10 @@ NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto, static inline JSObject * CopyInitializerObject(JSContext *cx, JSObject *baseobj, types::TypeObject *type) { - JS_ASSERT(baseobj->getClass() == &js_ObjectClass); + JS_ASSERT(baseobj->getClass() == &ObjectClass); JS_ASSERT(!baseobj->inDictionaryMode()); - gc::FinalizeKind kind = gc::FinalizeKind(baseobj->finalizeKind()); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, baseobj->getAllocKind()); if (!obj || !obj->ensureSlots(cx, baseobj->numSlots())) return NULL; diff --git a/js/src/json.cpp b/js/src/json.cpp index bead7654433b..03e5fb7b0ceb 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -74,7 +74,7 @@ using namespace js; using namespace js::gc; using namespace js::types; -Class js_JSONClass = { +Class js::JSONClass = { js_JSON_str, JSCLASS_HAS_CACHED_PROTO(JSProto_JSON), PropertyStub, /* addProperty */ @@ -302,7 +302,6 @@ PreprocessValue(JSContext *cx, JSObject *holder, jsid key, Value *vp, StringifyC if (!keyStr) return false; - LeaveTrace(cx); InvokeArgsGuard args; if (!cx->stack.pushInvokeArgs(cx, 1, &args)) return false; @@ -344,17 +343,17 @@ PreprocessValue(JSContext *cx, JSObject *holder, jsid key, Value *vp, StringifyC if (vp->isObject()) { JSObject *obj = &vp->toObject(); Class *clasp = obj->getClass(); - if (clasp == &js_NumberClass) { + if (clasp == &NumberClass) { double d; if (!ToNumber(cx, *vp, &d)) return false; vp->setNumber(d); - } else if (clasp == &js_StringClass) { + } else if (clasp == &StringClass) { JSString *str = js_ValueToString(cx, *vp); if (!str) return false; vp->setString(str); - } else if (clasp == &js_BooleanClass) { + } else if (clasp == &BooleanClass) { *vp = obj->getPrimitiveThis(); JS_ASSERT(vp->isBoolean()); } @@ -725,7 +724,7 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu } /* Step 9. */ - JSObject *wrapper = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *wrapper = NewBuiltinClassInstance(cx, &ObjectClass); if (!wrapper) return false; @@ -856,7 +855,7 @@ static bool Revive(JSContext *cx, const Value &reviver, Value *vp) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); if (!obj) return false; @@ -910,7 +909,7 @@ static JSFunctionSpec json_static_methods[] = { JSObject * js_InitJSONClass(JSContext *cx, JSObject *obj) { - JSObject *JSON = NewNonFunction(cx, &js_JSONClass, NULL, obj); + JSObject *JSON = NewNonFunction(cx, &JSONClass, NULL, obj); if (!JSON || !JSON->setSingletonType(cx)) return NULL; @@ -921,7 +920,7 @@ js_InitJSONClass(JSContext *cx, JSObject *obj) if (!JS_DefineFunctions(cx, JSON, json_static_methods)) return NULL; - MarkStandardClassInitializedNoProto(obj, &js_JSONClass); + MarkStandardClassInitializedNoProto(obj, &JSONClass); return JSON; } diff --git a/js/src/json.h b/js/src/json.h index 907038473b58..d1b9d0e1f321 100644 --- a/js/src/json.h +++ b/js/src/json.h @@ -46,8 +46,6 @@ #define JSON_MAX_DEPTH 2048 #define JSON_PARSER_BUFSIZE 1024 -extern js::Class js_JSONClass; - extern JSObject * js_InitJSONClass(JSContext *cx, JSObject *obj); diff --git a/js/src/jsonparser.cpp b/js/src/jsonparser.cpp index 3c240be91c07..7ba941aeac03 100644 --- a/js/src/jsonparser.cpp +++ b/js/src/jsonparser.cpp @@ -634,7 +634,7 @@ JSONParser::parse(Value *vp) } case ObjectOpen: { - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); if (!obj || !valueStack.append(ObjectValue(*obj))) return false; token = advanceAfterObjectOpen(); diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 04480ebd528e..99d3855a0006 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -359,14 +359,40 @@ js_DumpScript(JSContext *cx, JSScript *script) return ok; } +static char * +QuoteString(Sprinter *sp, JSString *str, uint32 quote); + static bool ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) { + if (JSVAL_IS_STRING(v)) { + Sprinter sprinter; + void *mark = JS_ARENA_MARK(&cx->tempPool); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"'); + if (!nbytes) + return false; + nbytes = JS_sprintf_append(NULL, "%s", nbytes); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!nbytes) + return false; + bytes->initBytes(nbytes); + return true; + } + + if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) { + char *source = JS_sprintf_append(NULL, ""); + if (!source) + return false; + bytes->initBytes(source); + return true; + } + if (!JSVAL_IS_PRIMITIVE(v)) { JSObject *obj = JSVAL_TO_OBJECT(v); Class *clasp = obj->getClass(); - if (clasp == &js_BlockClass) { + if (clasp == &BlockClass) { char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj)); if (!source) return false; @@ -393,7 +419,7 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) return true; } - if (clasp == &js_FunctionClass) { + if (clasp == &FunctionClass) { JSFunction *fun = obj->getFunctionPrivate(); JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); if (!str) @@ -401,7 +427,7 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) return bytes->encode(cx, str); } - if (clasp == &js_RegExpClass) { + if (clasp == &RegExpClass) { AutoValueRooter tvr(cx); if (!js_regexp_toString(cx, obj, tvr.addr())) return false; diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index b61fe48a82b2..7c709c80caaa 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -903,7 +903,6 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF JSString *source /* = NULL */, uintN staticLevel /* = 0 */) { - JSArenaPool codePool, notePool; TokenKind tt; JSParseNode *pn; JSScript *script; @@ -923,13 +922,10 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF if (!compiler.init(chars, length, filename, lineno, version)) return NULL; - JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); - JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); - Parser &parser = compiler.parser; TokenStream &tokenStream = parser.tokenStream; - JSCodeGenerator cg(&parser, &codePool, ¬ePool, tokenStream.getLineno()); + JSCodeGenerator cg(&parser, tokenStream.getLineno()); if (!cg.init(cx, JSTreeContext::USED_AS_TREE_CONTEXT)) return NULL; @@ -1118,8 +1114,6 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF script = NULL; out: - JS_FinishArenaPool(&codePool); - JS_FinishArenaPool(¬ePool); Probes::compileScriptEnd(cx, script, filename, lineno); return script; @@ -1132,9 +1126,6 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF bool Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script) { - if (!globalScope.defs.length()) - return true; - JSObject *globalObj = globalScope.globalObj; /* Define and update global properties. */ @@ -1187,18 +1178,29 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip * object. */ while (worklist.length()) { - JSScript *inner = worklist.back(); + JSScript *outer = worklist.back(); worklist.popBack(); - if (JSScript::isValidOffset(inner->objectsOffset)) { - JSObjectArray *arr = inner->objects(); - for (size_t i = 0; i < arr->length; i++) { + if (JSScript::isValidOffset(outer->objectsOffset)) { + JSObjectArray *arr = outer->objects(); + + /* + * If this is an eval script, don't treat the saved caller function + * stored in the first object slot as an inner function. + */ + size_t start = outer->savedCallerFun ? 1 : 0; + + for (size_t i = start; i < arr->length; i++) { JSObject *obj = arr->vector[i]; if (!obj->isFunction()) continue; JSFunction *fun = obj->getFunctionPrivate(); JS_ASSERT(fun->isInterpreted()); JSScript *inner = fun->script(); + if (outer->isHeavyweightFunction) { + outer->isOuterFunction = true; + inner->isInnerFunction = true; + } if (!JSScript::isValidOffset(inner->globalsOffset) && !JSScript::isValidOffset(inner->objectsOffset)) { continue; @@ -1208,10 +1210,10 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip } } - if (!JSScript::isValidOffset(inner->globalsOffset)) + if (!JSScript::isValidOffset(outer->globalsOffset)) continue; - GlobalSlotArray *globalUses = inner->globals(); + GlobalSlotArray *globalUses = outer->globals(); uint32 nGlobalUses = globalUses->length; for (uint32 i = 0; i < nGlobalUses; i++) { uint32 index = globalUses->vector[i].slot; @@ -1774,15 +1776,10 @@ Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *prin if (!compiler.init(chars, length, filename, lineno, version)) return false; - /* No early return from after here until the js_FinishArenaPool calls. */ - JSArenaPool codePool, notePool; - JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); - JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); - Parser &parser = compiler.parser; TokenStream &tokenStream = parser.tokenStream; - JSCodeGenerator funcg(&parser, &codePool, ¬ePool, tokenStream.getLineno()); + JSCodeGenerator funcg(&parser, tokenStream.getLineno()); if (!funcg.init(cx, JSTreeContext::USED_AS_TREE_CONTEXT)) return false; @@ -1852,9 +1849,6 @@ Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *prin } } - /* Restore saved state and release code generation arenas. */ - JS_FinishArenaPool(&codePool); - JS_FinishArenaPool(¬ePool); return pn != NULL; } diff --git a/js/src/jsprobes.h b/js/src/jsprobes.h index dce663a23fab..ac3f55164b8d 100644 --- a/js/src/jsprobes.h +++ b/js/src/jsprobes.h @@ -354,13 +354,13 @@ Probes::resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize) #ifdef INCLUDE_MOZILLA_DTRACE static const char *ObjectClassname(JSObject *obj) { - if (! obj) + if (!obj) return "(null object)"; Class *clasp = obj->getClass(); - if (! clasp) + if (!clasp) return "(null)"; const char *class_name = clasp->name; - if (! class_name) + if (!class_name) return "(null class name)"; return class_name; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 44f157bdd270..7e7392f6307c 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -57,8 +57,6 @@ using namespace js; using namespace js::gc; -namespace js { - static inline const Value & GetCall(JSObject *proxy) { JS_ASSERT(proxy->isFunctionProxy()); @@ -413,7 +411,7 @@ ValueToBool(JSContext *cx, const Value &v, bool *bp) return true; } -bool +static bool ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props) { JS_ASSERT(props.length() == 0); @@ -1027,7 +1025,7 @@ proxy_TypeOf(JSContext *cx, JSObject *proxy) return JSProxy::typeOf(cx, proxy); } -JS_FRIEND_API(Class) ObjectProxyClass = { +JS_FRIEND_DATA(Class) js::ObjectProxyClass = { "Proxy", Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(3), PropertyStub, /* addProperty */ @@ -1062,7 +1060,7 @@ JS_FRIEND_API(Class) ObjectProxyClass = { } }; -JS_FRIEND_API(Class) OuterWindowProxyClass = { +JS_FRIEND_DATA(Class) js::OuterWindowProxyClass = { "Proxy", Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(3), PropertyStub, /* addProperty */ @@ -1102,7 +1100,7 @@ JS_FRIEND_API(Class) OuterWindowProxyClass = { } }; -JSBool +static JSBool proxy_Call(JSContext *cx, uintN argc, Value *vp) { JSObject *proxy = &JS_CALLEE(cx, vp).toObject(); @@ -1110,7 +1108,7 @@ proxy_Call(JSContext *cx, uintN argc, Value *vp) return JSProxy::call(cx, proxy, argc, vp); } -JSBool +static JSBool proxy_Construct(JSContext *cx, uintN argc, Value *vp) { JSObject *proxy = &JS_CALLEE(cx, vp).toObject(); @@ -1119,7 +1117,7 @@ proxy_Construct(JSContext *cx, uintN argc, Value *vp) return ok; } -JS_FRIEND_API(Class) FunctionProxyClass = { +JS_FRIEND_DATA(Class) js::FunctionProxyClass = { "Proxy", Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(5), PropertyStub, /* addProperty */ @@ -1135,7 +1133,7 @@ JS_FRIEND_API(Class) FunctionProxyClass = { proxy_Call, proxy_Construct, NULL, /* xdrObject */ - js_FunctionClass.hasInstance, + FunctionClass.hasInstance, proxy_TraceFunction, /* trace */ JS_NULL_CLASS_EXT, { @@ -1155,7 +1153,7 @@ JS_FRIEND_API(Class) FunctionProxyClass = { }; JS_FRIEND_API(JSObject *) -NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObject *proto, +js::NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObject *proto, JSObject *parent, JSObject *call, JSObject *construct) { JS_ASSERT_IF(proto, cx->compartment == proto->compartment()); @@ -1311,8 +1309,6 @@ static JSFunctionSpec static_methods[] = { JS_FS_END }; -extern Class CallableObjectClass; - static const uint32 JSSLOT_CALLABLE_CALL = 0; static const uint32 JSSLOT_CALLABLE_CONSTRUCT = 1; @@ -1355,7 +1351,7 @@ callable_Construct(JSContext *cx, uintN argc, Value *vp) return false; } - JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent()); + JSObject *newobj = NewNativeClassInstance(cx, &ObjectClass, proto, proto->getParent()); if (!newobj) return false; @@ -1376,7 +1372,7 @@ callable_Construct(JSContext *cx, uintN argc, Value *vp) return ok; } -Class CallableObjectClass = { +Class js::CallableObjectClass = { "Function", JSCLASS_HAS_RESERVED_SLOTS(2), PropertyStub, /* addProperty */ @@ -1394,7 +1390,7 @@ Class CallableObjectClass = { }; JS_FRIEND_API(JSBool) -FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) +js::FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) { if (OperationInProgress(cx, proxy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROXY_FIX); @@ -1415,13 +1411,13 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) JSObject *proto = proxy->getProto(); JSObject *parent = proxy->getParent(); - Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass; + Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &ObjectClass; /* * 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; @@ -1448,9 +1444,7 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) return true; } -} - -Class js_ProxyClass = { +Class js::ProxyClass = { "Proxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy), PropertyStub, /* addProperty */ @@ -1465,7 +1459,7 @@ Class js_ProxyClass = { JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, JSObject *obj) { - JSObject *module = NewNonFunction(cx, &js_ProxyClass, NULL, obj); + JSObject *module = NewNonFunction(cx, &ProxyClass, NULL, obj); if (!module || !module->setSingletonType(cx)) return NULL; @@ -1476,7 +1470,7 @@ js_InitProxyClass(JSContext *cx, JSObject *obj) if (!JS_DefineFunctions(cx, module, static_methods)) return NULL; - MarkStandardClassInitializedNoProto(obj, &js_ProxyClass); + MarkStandardClassInitializedNoProto(obj, &ProxyClass); return module; } diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 5f7b2769cfdd..078cd8c78f36 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -142,30 +142,6 @@ const uint32 JSSLOT_PROXY_EXTRA = 2; const uint32 JSSLOT_PROXY_CALL = 3; const uint32 JSSLOT_PROXY_CONSTRUCT = 4; -extern JS_FRIEND_API(js::Class) ObjectProxyClass; -extern JS_FRIEND_API(js::Class) FunctionProxyClass; -extern JS_FRIEND_API(js::Class) OuterWindowProxyClass; -extern js::Class CallableObjectClass; - -} - -inline bool -JSObject::isObjectProxy() const -{ - return getClass() == &js::ObjectProxyClass || - getClass() == &js::OuterWindowProxyClass; -} - -inline bool -JSObject::isFunctionProxy() const -{ - return getClass() == &js::FunctionProxyClass; -} - -inline bool -JSObject::isProxy() const -{ - return isObjectProxy() || isFunctionProxy(); } inline js::JSProxyHandler * @@ -217,8 +193,6 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp); JS_BEGIN_EXTERN_C -extern js::Class js_ProxyClass; - extern JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, JSObject *obj); diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 4a8adc15b5cf..9331dac8387c 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -134,7 +134,6 @@ class AutoStringRooter; class ExecuteArgsGuard; class InvokeFrameGuard; class InvokeArgsGuard; -class InvokeSessionGuard; class StringBuffer; class TraceRecorder; struct TraceMonitor; diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index a59a1b68c74e..d35de1776d69 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -323,7 +323,7 @@ class NodeBuilder } bool newObject(JSObject **dst) { - JSObject *nobj = NewNonFunction(cx, &js_ObjectClass, NULL, NULL); + JSObject *nobj = NewNonFunction(cx, &ObjectClass, NULL, NULL); if (!nobj) return false; @@ -633,7 +633,7 @@ NodeBuilder::newNode(ASTType type, TokenPos *pos, JSObject **dst) Value tv; - JSObject *node = NewNonFunction(cx, &js_ObjectClass, NULL, NULL); + JSObject *node = NewNonFunction(cx, &ObjectClass, NULL, NULL); if (!node || !setNodeLoc(node, pos) || !atomValue(nodeTypeNames[type], &tv) || @@ -3238,7 +3238,7 @@ JS_BEGIN_EXTERN_C JS_PUBLIC_API(JSObject *) JS_InitReflect(JSContext *cx, JSObject *obj) { - JSObject *Reflect = NewNonFunction(cx, &js_ObjectClass, NULL, obj); + JSObject *Reflect = NewNonFunction(cx, &ObjectClass, NULL, obj); if (!Reflect || !Reflect->setSingletonType(cx)) return NULL; diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index 56c9bd8dc414..83fbdc8f7114 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -141,11 +141,10 @@ SwapObjectRegExp(JSContext *cx, JSObject *obj, AlreadyIncRefed newRegExp JSObject * JS_FASTCALL js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto) { - JS_ASSERT(obj->getClass() == &js_RegExpClass); - JS_ASSERT(proto); - JS_ASSERT(proto->getClass() == &js_RegExpClass); + JS_ASSERT(obj->isRegExp()); + JS_ASSERT(proto->isRegExp()); - JSObject *clone = NewNativeClassInstance(cx, &js_RegExpClass, proto, proto->getParent()); + JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent()); if (!clone) return NULL; @@ -432,7 +431,7 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp) if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword)) return false; if (xdr->mode == JSXDR_DECODE) { - JSObject *obj = NewBuiltinClassInstance(xdr->cx, &js_RegExpClass); + JSObject *obj = NewBuiltinClassInstance(xdr->cx, &RegExpClass); if (!obj) return false; obj->clearParent(); @@ -460,7 +459,7 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp) #endif /* !JS_HAS_XDR */ -js::Class js_RegExpClass = { +Class js::RegExpClass = { js_RegExp_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_CLASS_RESERVED_SLOTS) | @@ -490,7 +489,7 @@ JSBool js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp) { if (!obj->isRegExp()) { - ReportIncompatibleMethod(cx, vp, &js_RegExpClass); + ReportIncompatibleMethod(cx, vp, &RegExpClass); return false; } @@ -609,7 +608,7 @@ ExecuteRegExp(JSContext *cx, ExecType execType, uintN argc, Value *vp) if (!obj) return false; if (!obj->isRegExp()) { - ReportIncompatibleMethod(cx, vp, &js_RegExpClass); + ReportIncompatibleMethod(cx, vp, &RegExpClass); return false; } @@ -703,7 +702,7 @@ CompileRegExpAndSwap(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Valu return SwapRegExpInternals(cx, obj, rval, cx->runtime->emptyString); Value sourceValue = argv[0]; - if (sourceValue.isObject() && sourceValue.toObject().getClass() == &js_RegExpClass) { + if (sourceValue.isObject() && sourceValue.toObject().isRegExp()) { /* * If we get passed in a RegExp object we return a new object with the * same RegExp (internal matcher program) guts. @@ -759,7 +758,7 @@ regexp_compile(JSContext *cx, uintN argc, Value *vp) if (!obj) return false; if (!obj->isRegExp()) { - ReportIncompatibleMethod(cx, vp, &js_RegExpClass); + ReportIncompatibleMethod(cx, vp, &RegExpClass); return false; } @@ -784,7 +783,7 @@ regexp_construct(JSContext *cx, uintN argc, Value *vp) } } - JSObject *obj = NewBuiltinClassInstance(cx, &js_RegExpClass); + JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass); if (!obj) return false; @@ -809,7 +808,7 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj) GlobalObject *global = obj->asGlobal(); - JSObject *proto = global->createBlankPrototype(cx, &js_RegExpClass); + JSObject *proto = global->createBlankPrototype(cx, &RegExpClass); if (!proto) return NULL; @@ -831,7 +830,7 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj) if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods)) return NULL; - JSFunction *ctor = global->createConstructor(cx, regexp_construct, &js_RegExpClass, + JSFunction *ctor = global->createConstructor(cx, regexp_construct, &RegExpClass, CLASS_ATOM(cx, RegExp), 2); if (!ctor) return NULL; diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 429cbd5df4c4..e1e85a4c8d76 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -52,8 +52,6 @@ #include "jsdhash.h" #endif -extern js::Class js_RegExpClass; - namespace js { class RegExpStatics @@ -388,12 +386,6 @@ JSObject::setRegExpSticky(bool sticky) namespace js { class AutoStringRooter; } -inline bool -JSObject::isRegExp() const -{ - return getClass() == &js_RegExpClass; -} - extern JS_FRIEND_API(JSBool) js_ObjectIsRegExp(JSObject *obj); diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 78cb55fd2258..2a1cea9d6d7c 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -448,7 +448,7 @@ RegExp::createObjectNoStatics(JSContext *cx, const jschar *chars, size_t length, AlreadyIncRefed re = RegExp::create(cx, str, flags, ts); if (!re) return NULL; - JSObject *obj = NewBuiltinClassInstance(cx, &js_RegExpClass); + JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass); if (!obj || !obj->initRegExp(cx, re.get())) { re->decref(cx); return NULL; diff --git a/js/src/jsscope.h b/js/src/jsscope.h index e4ed68c0d135..354d2a5a60b8 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -251,7 +251,12 @@ struct PropertyTable { /* Computes the size of the entries array for a given capacity. */ static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); } - size_t sizeOf() const { + size_t sizeOf(size_t(*mus)(void *)) const { + if (mus) { + size_t usable = mus((void*)this) + mus(entries); + if (usable) + return usable; + } return sizeOfEntries(capacity()) + sizeof(PropertyTable); } diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index ea13c6943a02..d0ba58f1af69 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); @@ -288,7 +288,7 @@ Shape::get(JSContext* cx, JSObject *receiver, JSObject* obj, JSObject *pobj, js: * |with (it) color;| ends up here, as do XML filter-expressions. * Avoid exposing the With object to native getters. */ - if (obj->getClass() == &js_WithClass) + if (obj->isWith()) obj = js_UnwrapWithObject(cx, obj); return js::CallJSPropertyOp(cx, getterOp(), receiver, SHAPE_USERID(this), vp); } @@ -307,7 +307,7 @@ Shape::set(JSContext* cx, JSObject* obj, bool strict, js::Value* vp) const return js_ReportGetterOnlyAssignment(cx); /* See the comment in js::Shape::get as to why we check for With. */ - if (obj->getClass() == &js_WithClass) + if (obj->isWith()) obj = js_UnwrapWithObject(cx, obj); return js::CallJSPropertyOpSetter(cx, setterOp(), obj, SHAPE_USERID(this), strict, vp); } @@ -364,37 +364,37 @@ EmptyShape::EmptyShape(JSCompartment *comp, js::Class *aclasp) /* static */ inline EmptyShape * EmptyShape::getEmptyArgumentsShape(JSContext *cx) { - return ensure(cx, &NormalArgumentsObject::jsClass, &cx->compartment->emptyArgumentsShape); + return ensure(cx, &NormalArgumentsObjectClass, &cx->compartment->emptyArgumentsShape); } /* static */ inline EmptyShape * EmptyShape::getEmptyBlockShape(JSContext *cx) { - return ensure(cx, &js_BlockClass, &cx->compartment->emptyBlockShape); + return ensure(cx, &BlockClass, &cx->compartment->emptyBlockShape); } /* static */ inline EmptyShape * EmptyShape::getEmptyCallShape(JSContext *cx) { - return ensure(cx, &js_CallClass, &cx->compartment->emptyCallShape); + return ensure(cx, &CallClass, &cx->compartment->emptyCallShape); } /* static */ inline EmptyShape * EmptyShape::getEmptyDeclEnvShape(JSContext *cx) { - return ensure(cx, &js_DeclEnvClass, &cx->compartment->emptyDeclEnvShape); + return ensure(cx, &DeclEnvClass, &cx->compartment->emptyDeclEnvShape); } /* static */ inline EmptyShape * EmptyShape::getEmptyEnumeratorShape(JSContext *cx) { - return ensure(cx, &js_IteratorClass, &cx->compartment->emptyEnumeratorShape); + return ensure(cx, &IteratorClass, &cx->compartment->emptyEnumeratorShape); } /* static */ inline EmptyShape * EmptyShape::getEmptyWithShape(JSContext *cx) { - return ensure(cx, &js_WithClass, &cx->compartment->emptyWithShape); + return ensure(cx, &WithClass, &cx->compartment->emptyWithShape); } } /* namespace js */ diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 5b9216facf18..9cf6d6fdb236 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -668,9 +668,9 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) uint32 isBlock; if (xdr->mode == JSXDR_ENCODE) { Class *clasp = (*objp)->getClass(); - JS_ASSERT(clasp == &js_FunctionClass || - clasp == &js_BlockClass); - isBlock = (clasp == &js_BlockClass) ? 1 : 0; + JS_ASSERT(clasp == &FunctionClass || + clasp == &BlockClass); + isBlock = (clasp == &BlockClass) ? 1 : 0; } if (!JS_XDRUint32(xdr, &isBlock)) goto error; @@ -778,7 +778,7 @@ script_trace(JSTracer *trc, JSObject *obj) } } -Class js_ScriptClass = { +Class js::ScriptClass = { "Script", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), @@ -1212,15 +1212,6 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) cg->upvarMap.clear(); } - /* Set global for compileAndGo scripts. */ - if (script->compileAndGo) { - GlobalScope *globalScope = cg->compiler()->globalScope; - if (globalScope->globalObj && globalScope->globalObj->isGlobal()) - script->where.global = globalScope->globalObj->asGlobal(); - else if (cx->globalObject->isGlobal()) - script->where.global = cx->globalObject->asGlobal(); - } - if (cg->globalUses.length()) { memcpy(script->globals()->vector, &cg->globalUses[0], cg->globalUses.length() * sizeof(GlobalSlotArray::Entry)); @@ -1387,7 +1378,7 @@ js_NewScriptObject(JSContext *cx, JSScript *script) { JS_ASSERT(!script->u.object); - JSObject *obj = NewNonFunction(cx, &js_ScriptClass, NULL, NULL); + JSObject *obj = NewNonFunction(cx, &ScriptClass, NULL, NULL); if (!obj) return NULL; obj->setPrivate(script); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index edb1cf5ccfa7..0529a7e4be72 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -511,12 +511,18 @@ struct JSScript : public js::gc::Cell { undefined properties in this script */ bool hasSingletons:1; /* script has singleton objects */ - bool hasFunction:1; /* function is active in 'where' union */ + bool hasFunction:1; /* script has an associated function */ + bool isHeavyweightFunction:1; /* function is heavyweight */ + bool isOuterFunction:1; /* function is heavyweight, with inner functions */ + bool isInnerFunction:1; /* function is directly nested in a heavyweight + * outer function */ bool isActiveEval:1; /* script came from eval(), and is still active */ bool isCachedEval:1; /* script came from eval(), and is in eval cache */ bool usedLazyArgs:1; /* script has used lazy arguments at some point */ bool createdArgs:1; /* script has had arguments objects created */ bool uninlineable:1; /* script is considered uninlineable by analysis */ + bool reentrantOuterFunction:1; /* outer function marked reentrant */ + bool typesPurged:1; /* TypeScript has been purged at some point */ #ifdef JS_METHODJIT bool debugMode:1; /* script was compiled in debug mode */ bool failedBoundsCheck:1; /* script has had hoisted bounds checks fail */ @@ -535,8 +541,10 @@ struct JSScript : public js::gc::Cell { * the script with 4 bytes. We use them to store tiny scripts like empty * scripts. */ +#if JS_BITS_PER_WORD == 64 #define JS_SCRIPT_INLINE_DATA_LIMIT 4 uint8 inlineData[JS_SCRIPT_INLINE_DATA_LIMIT]; +#endif const char *filename; /* source filename or null */ JSAtom **atoms; /* maps immediate index to literal struct */ @@ -552,7 +560,7 @@ struct JSScript : public js::gc::Cell { union { /* - * A script object of class js_ScriptClass, to ensure the script is GC'd. + * A script object of class ScriptClass, to ensure the script is GC'd. * - All scripts returned by JSAPI functions (JS_CompileScript, * JS_CompileFile, etc.) have these objects. * - Function scripts never have script objects; such scripts are owned @@ -572,19 +580,6 @@ struct JSScript : public js::gc::Cell { /* array of execution counters for every JSOp in the script, by runmode */ JSPCCounters pcCounters; - union { - /* Function this script is the body for, if there is one. */ - JSFunction *fun; - - /* Global object for this script, if compileAndGo. */ - js::GlobalObject *global; - } where; - - inline JSFunction *function() const { - JS_ASSERT(hasFunction); - return where.fun; - } - #ifdef JS_CRASH_DIAGNOSTICS JSObject *ownerObject; @@ -594,17 +589,6 @@ struct JSScript : public js::gc::Cell { void setOwnerObject(JSObject *owner); - /* - * Associates this script with a specific function, constructing a new type - * object for the function. - */ - bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false); - - inline bool hasGlobal() const; - inline js::GlobalObject *global() const; - - inline bool hasClearedGlobal() const; - #ifdef DEBUG /* * Unique identifier within the compartment for this script, used for @@ -620,19 +604,41 @@ struct JSScript : public js::gc::Cell { /* Persistent type information retained across GCs. */ js::types::TypeScript *types; - /* Ensure the script has types, bytecode and/or type inference results. */ - inline bool ensureHasTypes(JSContext *cx); - inline bool ensureRanBytecode(JSContext *cx); + /* Ensure the script has a TypeScript. */ + inline bool ensureHasTypes(JSContext *cx, JSFunction *fun = NULL); + + /* + * Ensure the script has scope and bytecode analysis information. + * Performed when the script first runs, or first runs after a TypeScript + * GC purge. If fun/scope are NULL then the script must already have types + * with scope information. + */ + inline bool ensureRanAnalysis(JSContext *cx, JSFunction *fun = NULL, JSObject *scope = NULL); + + /* Ensure the script has type inference analysis information. */ inline bool ensureRanInference(JSContext *cx); - /* Filled in by one of the above. */ inline bool hasAnalysis(); + inline void clearAnalysis(); inline js::analyze::ScriptAnalysis *analysis(); - inline bool isAboutToBeFinalized(JSContext *cx); + /* + * Associates this script with a specific function, constructing a new type + * object for the function if necessary. + */ + bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false); + + inline bool hasGlobal() const; + inline bool hasClearedGlobal() const; + + inline JSFunction *function() const; + inline js::GlobalObject *global() const; + inline js::types::TypeScriptNesting *nesting() const; + + inline void clearNesting(); private: - bool makeTypes(JSContext *cx); + bool makeTypes(JSContext *cx, JSFunction *fun); bool makeAnalysis(JSContext *cx); public: @@ -676,8 +682,9 @@ struct JSScript : public js::gc::Cell { return JITScript_Valid; } - // This method is implemented in MethodJIT.h. - JS_FRIEND_API(size_t) jitDataSize();/* Size of the JITScript and all sections */ + /* Size of the JITScript and all sections. (This method is implemented in MethodJIT.h.) */ + JS_FRIEND_API(size_t) jitDataSize(size_t(*mus)(void *)); + #endif jsbytecode *main() { @@ -845,7 +852,6 @@ StackDepth(JSScript *script) } \ JS_END_MACRO -extern JS_FRIEND_DATA(js::Class) js_ScriptClass; extern JSObject * js_InitScriptClass(JSContext *cx, JSObject *obj); @@ -968,12 +974,6 @@ js_CloneScript(JSContext *cx, JSScript *script); extern JSBool js_XDRScript(JSXDRState *xdr, JSScript **scriptp); -inline bool -JSObject::isScript() const -{ - return getClass() == &js_ScriptClass; -} - inline JSScript * JSObject::getScript() const { diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index d05dc38208cc..88076cb9ac28 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -153,7 +153,7 @@ JSScript::getRegExp(size_t index) JSObjectArray *arr = regexps(); JS_ASSERT((uint32) index < arr->length); JSObject *obj = arr->vector[index]; - JS_ASSERT(obj->getClass() == &js_RegExpClass); + JS_ASSERT(obj->isRegExp()); return obj; } @@ -177,9 +177,8 @@ JSScript::hasGlobal() const * which have had their scopes cleared. compileAndGo code should not run * anymore against such globals. */ - if (!compileAndGo) - return false; - js::GlobalObject *obj = hasFunction ? function()->getGlobal() : where.global; + JS_ASSERT(types && types->hasScope()); + js::GlobalObject *obj = types->global; return obj && !obj->isCleared(); } @@ -187,16 +186,39 @@ inline js::GlobalObject * JSScript::global() const { JS_ASSERT(hasGlobal()); - return hasFunction ? function()->getGlobal() : where.global; + return types->global; } inline bool JSScript::hasClearedGlobal() const { - if (!compileAndGo) - return false; - js::GlobalObject *obj = hasFunction ? function()->getGlobal() : where.global; + JS_ASSERT(types && types->hasScope()); + js::GlobalObject *obj = types->global; return obj && obj->isCleared(); } +inline JSFunction * +JSScript::function() const +{ + JS_ASSERT(hasFunction && types); + return types->function; +} + +inline js::types::TypeScriptNesting * +JSScript::nesting() const +{ + JS_ASSERT(hasFunction && types && types->hasScope()); + return types->nesting; +} + +inline void +JSScript::clearNesting() +{ + js::types::TypeScriptNesting *nesting = this->nesting(); + if (nesting) { + js::Foreground::delete_(nesting); + types->nesting = NULL; + } +} + #endif /* jsscriptinlines_h___ */ diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4ca3f40d5651..bff981bb69e0 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -384,7 +384,7 @@ str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, return JS_TRUE; } -Class js_StringClass = { +Class js::StringClass = { js_String_str, JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_String), @@ -401,7 +401,7 @@ Class js_StringClass = { * Returns a JSString * for the |this| value associated with vp, or throws a * TypeError if |this| is null or undefined. This algorithm is the same as * calling CheckObjectCoercible(this), then returning ToString(this), as all - * String.prototype.* methods do. + * String.prototype.* methods do (other than toString and valueOf). */ static JS_ALWAYS_INLINE JSString * ThisToStringForStringProto(JSContext *cx, Value *vp) @@ -413,9 +413,9 @@ ThisToStringForStringProto(JSContext *cx, Value *vp) if (vp[1].isObject()) { JSObject *obj = &vp[1].toObject(); - if (obj->getClass() == &js_StringClass && + if (obj->isString() && ClassMethodIsNative(cx, obj, - &js_StringClass, + &StringClass, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), js_str_toString)) { @@ -1622,8 +1622,7 @@ struct ReplaceData jsint leftIndex; /* left context index in str->chars */ JSSubString dollarStr; /* for "$$" InterpretDollar result */ bool calledBack; /* record whether callback has been called */ - InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */ - InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */ + InvokeArgsGuard args; /* arguments for lambda call */ StringBuffer sb; /* buffer built during DoMatch */ }; @@ -1750,6 +1749,10 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t JSObject *lambda = rdata.lambda; if (lambda) { + PreserveRegExpStatics staticsGuard(res); + if (!staticsGuard.init(cx)) + return false; + /* * In the lambda case, not only do we find the replacement string's * length, we compute repstr and return it via rdata for use within @@ -1761,36 +1764,33 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t uintN p = res->parenCount(); uintN argc = 1 + p + 2; - InvokeSessionGuard &session = rdata.session; - if (!session.started()) { - Value lambdav = ObjectValue(*lambda); - if (!session.start(cx, lambdav, UndefinedValue(), argc)) - return false; - } - - PreserveRegExpStatics staticsGuard(res); - if (!staticsGuard.init(cx)) + InvokeArgsGuard &args = rdata.args; + if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args)) return false; + args.calleeHasBeenReset(); + args.calleev() = ObjectValue(*lambda); + args.thisv() = UndefinedValue(); + /* Push $&, $1, $2, ... */ uintN argi = 0; - if (!res->createLastMatch(cx, &session[argi++])) + if (!res->createLastMatch(cx, &args[argi++])) return false; for (size_t i = 0; i < res->parenCount(); ++i) { - if (!res->createParen(cx, i + 1, &session[argi++])) + if (!res->createParen(cx, i + 1, &args[argi++])) return false; } /* Push match index and input string. */ - session[argi++].setInt32(res->matchStart()); - session[argi].setString(rdata.str); + args[argi++].setInt32(res->matchStart()); + args[argi].setString(rdata.str); - if (!session.invoke(cx)) + if (!Invoke(cx, args)) return false; /* root repstr: rdata is on the stack, so scanned by conservative gc. */ - JSString *repstr = ValueToString_TestForStringInline(cx, session.rval()); + JSString *repstr = ValueToString_TestForStringInline(cx, args.rval()); if (!repstr) return false; rdata.repstr = repstr->ensureLinear(cx); @@ -2074,7 +2074,6 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata const FlatMatch &fm) { JS_ASSERT(fm.match() >= 0); - LeaveTrace(cx); JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength()); if (!matchStr) @@ -2082,10 +2081,10 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata /* lambda(matchStr, matchStart, textstr) */ static const uint32 lambdaArgc = 3; - if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot)) + if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.args)) return false; - CallArgs &args = rdata.singleShot; + CallArgs &args = rdata.args; args.calleev().setObject(*rdata.lambda); args.thisv().setUndefined(); @@ -2094,7 +2093,7 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata sp[1].setInt32(fm.match()); sp[2].setString(rdata.str); - if (!Invoke(cx, rdata.singleShot)) + if (!Invoke(cx, rdata.args)) return false; JSString *repstr = js_ValueToString(cx, args.rval()); @@ -3191,12 +3190,12 @@ js_InitStringClass(JSContext *cx, JSObject *obj) GlobalObject *global = obj->asGlobal(); - JSObject *proto = global->createBlankPrototype(cx, &js_StringClass); + JSObject *proto = global->createBlankPrototype(cx, &StringClass); if (!proto || !proto->asString()->init(cx, cx->runtime->emptyString)) return NULL; /* Now create the String function. */ - JSFunction *ctor = global->createConstructor(cx, js_String, &js_StringClass, + JSFunction *ctor = global->createConstructor(cx, js_String, &StringClass, CLASS_ATOM(cx, String), 1); if (!ctor) return NULL; diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 3a86972d7301..dff8c49371e9 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -101,14 +101,6 @@ extern JSSubString js_EmptySubString; #define JS7_ISLET(c) ((c) < 128 && isalpha(c)) /* Initialize the String class, returning its prototype object. */ -extern js::Class js_StringClass; - -inline bool -JSObject::isString() const -{ - return getClass() == &js_StringClass; -} - extern JSObject * js_InitStringClass(JSContext *cx, JSObject *obj); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 6ffe74f56640..34fcef2469c4 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -183,9 +183,9 @@ using namespace js::tjit; * * FIXME: Bug 624590 is open to get rid of all this. */ -static const size_t DataReserveSize = 12500 * sizeof(uintptr_t); -static const size_t TraceReserveSize = 5000 * sizeof(uintptr_t); -static const size_t TempReserveSize = 1000 * sizeof(uintptr_t); +static const size_t DataReserveSize = 8192 * sizeof(uintptr_t); +static const size_t TraceReserveSize = 512 * sizeof(uintptr_t); +static const size_t TempReserveSize = 4096 * sizeof(uintptr_t); void* nanojit::Allocator::allocChunk(size_t nbytes, bool fallible) @@ -9832,9 +9832,9 @@ TraceRecorder::unbox_object(Address addr, LIns* tag_ins, JSValueType type, VMSid guard(true, w.name(w.eqi(tag_ins, w.nameImmui(JSVAL_TAG_OBJECT)), "isObj"), exit); LIns *payload_ins = w.ldiValuePayload(addr); if (type == JSVAL_TYPE_FUNOBJ) - guardClass(payload_ins, &js_FunctionClass, exit, LOAD_NORMAL); + guardClass(payload_ins, &FunctionClass, exit, LOAD_NORMAL); else - guardNotClass(payload_ins, &js_FunctionClass, exit, LOAD_NORMAL); + guardNotClass(payload_ins, &FunctionClass, exit, LOAD_NORMAL); return payload_ins; } @@ -9985,9 +9985,9 @@ TraceRecorder::unbox_object(LIns* v_ins, JSValueType type, VMSideExit* exit) exit); v_ins = unpack_ptr(v_ins); if (type == JSVAL_TYPE_FUNOBJ) - guardClass(v_ins, &js_FunctionClass, exit, LOAD_NORMAL); + guardClass(v_ins, &FunctionClass, exit, LOAD_NORMAL); else - guardNotClass(v_ins, &js_FunctionClass, exit, LOAD_NORMAL); + guardNotClass(v_ins, &FunctionClass, exit, LOAD_NORMAL); return v_ins; } @@ -10194,13 +10194,13 @@ TraceRecorder::guardNotClass(LIns* obj_ins, Class* clasp, VMSideExit* exit, Load JS_REQUIRES_STACK void TraceRecorder::guardDenseArray(LIns* obj_ins, ExitType exitType) { - guardClass(obj_ins, &js_ArrayClass, snapshot(exitType), LOAD_NORMAL); + guardClass(obj_ins, &ArrayClass, snapshot(exitType), LOAD_NORMAL); } JS_REQUIRES_STACK void TraceRecorder::guardDenseArray(LIns* obj_ins, VMSideExit* exit) { - guardClass(obj_ins, &js_ArrayClass, exit, LOAD_NORMAL); + guardClass(obj_ins, &ArrayClass, exit, LOAD_NORMAL); } JS_REQUIRES_STACK bool @@ -11065,7 +11065,7 @@ TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins) JS_ASSERT(!pval.isPrimitive()); JSObject *proto = &pval.toObject(); JS_ASSERT(!proto->isDenseArray()); - JS_ASSERT_IF(clasp != &js_ArrayClass, proto->getNewType(cx)->emptyShapes[0]->getClass() == clasp); + JS_ASSERT_IF(clasp != &ArrayClass, proto->getNewType(cx)->emptyShapes[0]->getClass() == clasp); proto_ins = w.immpObjGC(proto); return RECORD_CONTINUE; @@ -11608,15 +11608,15 @@ TraceRecorder::callNative(uintN argc, JSOp mode) LIns* this_ins; if (mode == JSOP_NEW) { Class* clasp = fun->u.n.clasp; - JS_ASSERT(clasp != &js_SlowArrayClass); + JS_ASSERT(clasp != &SlowArrayClass); if (!clasp) - clasp = &js_ObjectClass; + clasp = &ObjectClass; JS_ASSERT(((jsuword) clasp & 3) == 0); // Abort on |new Function|. (FIXME: This restriction might not // unnecessary now that the constructor creates the new function object // itself.) - if (clasp == &js_FunctionClass) + if (clasp == &FunctionClass) RETURN_STOP("new Function"); if (!IsFastTypedArrayClass(clasp) && !clasp->isNative()) @@ -12494,7 +12494,7 @@ TraceRecorder::recordInitPropertyOp(jsbytecode op) Value& l = stackval(-2); JSObject* obj = &l.toObject(); LIns* obj_ins = get(&l); - JS_ASSERT(obj->getClass() == &js_ObjectClass); + JS_ASSERT(obj->getClass() == &ObjectClass); Value& v = stackval(-1); LIns* v_ins = get(&v); @@ -12769,7 +12769,7 @@ GetPropertyWithNativeGetter(JSContext* cx, JSObject* obj, Shape* shape, Value* v // Shape::get contains a special case for With objects. We can elide it // here because With objects are, we claim, never on the operand stack // while recording. - JS_ASSERT(obj->getClass() != &js_WithClass); + JS_ASSERT(obj->getClass() != &WithClass); vp->setUndefined(); if (!shape->getterOp()(cx, obj, SHAPE_USERID(shape), vp)) { @@ -14823,8 +14823,8 @@ TraceRecorder::record_JSOP_MOREITER() * ni->flags (nor do we in unboxNextValue), because the different iteration * type will guarantee a different entry typemap. */ - if (iterobj->hasClass(&js_IteratorClass)) { - guardClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); + if (iterobj->hasClass(&IteratorClass)) { + guardClass(iterobj_ins, &IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); NativeIterator *ni = (NativeIterator *) iterobj->getPrivate(); if (ni->isKeyIter()) { @@ -14837,7 +14837,7 @@ TraceRecorder::record_JSOP_MOREITER() return ARECORD_CONTINUE; } } else { - guardNotClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); + guardNotClass(iterobj_ins, &IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); } enterDeepBailCall(); @@ -14922,8 +14922,8 @@ TraceRecorder::unboxNextValue(Value &iterobj_val, LIns* &v_ins) JSObject *iterobj = &iterobj_val.toObject(); LIns* iterobj_ins = get(&iterobj_val); - if (iterobj->hasClass(&js_IteratorClass)) { - guardClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); + if (iterobj->hasClass(&IteratorClass)) { + guardClass(iterobj_ins, &IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); NativeIterator *ni = (NativeIterator *) iterobj->getPrivate(); LIns *ni_ins = w.ldpObjPrivate(iterobj_ins); @@ -14966,7 +14966,7 @@ TraceRecorder::unboxNextValue(Value &iterobj_val, LIns* &v_ins) return ARECORD_CONTINUE; } } else { - guardNotClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); + guardNotClass(iterobj_ins, &IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); } @@ -16340,7 +16340,7 @@ TraceRecorder::record_JSOP_LENGTH() guardDenseArray(obj_ins, BRANCH_EXIT); } else { JS_ASSERT(obj->isSlowArray()); - guardClass(obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); + guardClass(obj_ins, &SlowArrayClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); } v_ins = w.lduiObjPrivate(obj_ins); if (obj->getArrayLength() <= JSVAL_INT_MAX) { diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index c3b9b04a0295..b5f14e231929 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -186,7 +186,7 @@ static JSObject * DelegateObject(JSContext *cx, JSObject *obj) { if (!obj->getPrivate()) { - JSObject *delegate = NewNonFunction(cx, &js_ObjectClass, obj->getProto(), NULL); + JSObject *delegate = NewNonFunction(cx, &ObjectClass, obj->getProto(), NULL); obj->setPrivate(delegate); return delegate; } @@ -212,7 +212,7 @@ ArrayBuffer::create(JSContext *cx, int32 nbytes) JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass); obj->setSharedNonNativeMap(); - obj->clasp = &js_ArrayBufferClass; + obj->setClass(&ArrayBufferClass); /* * The first 8 bytes hold the length. @@ -980,7 +980,7 @@ class TypedArrayTemplate JS_ASSERT(obj->getClass() == slowClass()); obj->setSharedNonNativeMap(); - obj->clasp = fastClass(); + obj->setClass(fastClass()); // FIXME Bug 599008: make it ok to call preventExtensions here. obj->flags |= JSObject::NOT_EXTENSIBLE; @@ -1694,7 +1694,7 @@ Class ArrayBuffer::slowClass = { FinalizeStub }; -Class js_ArrayBufferClass = { +Class js::ArrayBufferClass = { "ArrayBuffer", JSCLASS_HAS_PRIVATE | Class::NON_NATIVE | @@ -1902,7 +1902,7 @@ InitArrayBufferClass(JSContext *cx, GlobalObject *global) return NULL; JSFunction *ctor = - global->createConstructor(cx, ArrayBuffer::class_constructor, &js_ArrayBufferClass, + global->createConstructor(cx, ArrayBuffer::class_constructor, &ArrayBufferClass, CLASS_ATOM(cx, ArrayBuffer), 1); if (!ctor) return NULL; @@ -1953,7 +1953,7 @@ JS_FRIEND_API(JSBool) js_IsArrayBuffer(JSObject *obj) { JS_ASSERT(obj); - return obj->getClass() == &js_ArrayBufferClass; + return obj->isArrayBuffer(); } namespace js { diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index d7774d1ee3cd..91ba39010a00 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -78,12 +78,6 @@ WeakMapBase::sweepAll(JSTracer *tracer) } /* namespace js */ -bool -JSObject::isWeakMap() const -{ - return getClass() == &WeakMapClass; -} - typedef WeakMap ObjectValueMap; static ObjectValueMap * @@ -264,9 +258,7 @@ WeakMap_construct(JSContext *cx, uintN argc, Value *vp) return true; } -namespace js { - -Class WeakMapClass = { +Class js::WeakMapClass = { "WeakMap", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap), @@ -287,8 +279,6 @@ Class WeakMapClass = { WeakMap_mark }; -} - static JSFunctionSpec weak_map_methods[] = { JS_FN("has", WeakMap_has, 1, 0), JS_FN("get", WeakMap_get, 2, 0), diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h index 29a3a7038dca..aba994dd839f 100644 --- a/js/src/jsweakmap.h +++ b/js/src/jsweakmap.h @@ -317,9 +317,6 @@ class DefaultMarkPolicy { // typedef DefaultMarkPolicy CrossCompartmentMarkPolicy; -// The class of JavaScript WeakMap objects. -extern Class WeakMapClass; - } extern JSObject * diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 085afb6c586c..c61301ef2660 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -632,7 +632,7 @@ CanReify(Value *vp) { JSObject *obj; return vp->isObject() && - (obj = &vp->toObject())->getClass() == &js_IteratorClass && + (obj = &vp->toObject())->getClass() == &IteratorClass && (obj->getNativeIterator()->flags & JSITER_ENUMERATE); } diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index ec813da912ae..d89bb650338f 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -153,7 +153,7 @@ IsDeclared(const JSObject *obj) { jsval v; - JS_ASSERT(obj->getClass() == &js_NamespaceClass); + JS_ASSERT(obj->getClass() == &NamespaceClass); v = obj->getNamespaceDeclared(); JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE); return v == JSVAL_TRUE; @@ -197,9 +197,9 @@ NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp) * Namespace class and library functions. */ DEFINE_GETTER(NamePrefix_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefixVal()) + if (obj->getClass() == &NamespaceClass) *vp = obj->getNamePrefixVal()) DEFINE_GETTER(NameURI_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURIVal()) + if (obj->getClass() == &NamespaceClass) *vp = obj->getNameURIVal()) static JSBool namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) @@ -208,13 +208,13 @@ namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) JS_ASSERT(v->isObjectOrNull()); obj2 = v->toObjectOrNull(); - *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass) + *bp = (!obj2 || obj2->getClass() != &NamespaceClass) ? JS_FALSE : EqualStrings(obj->getNameURI(), obj2->getNameURI()); return JS_TRUE; } -JS_FRIEND_DATA(Class) js_NamespaceClass = { +JS_FRIEND_DATA(Class) js::NamespaceClass = { "Namespace", JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), @@ -258,7 +258,7 @@ namespace_toString(JSContext *cx, uintN argc, Value *vp) if (!obj) return JS_FALSE; if (!obj->isNamespace()) { - ReportIncompatibleMethod(cx, vp, &js_NamespaceClass); + ReportIncompatibleMethod(cx, vp, &NamespaceClass); return JS_FALSE; } *vp = Valueify(obj->getNameURIVal()); @@ -275,7 +275,7 @@ NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBo { JSObject *obj; - obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass); + obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass); if (!obj) return NULL; JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); @@ -299,10 +299,10 @@ NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBo * QName class and library functions. */ DEFINE_GETTER(QNameNameURI_getter, - if (obj->getClass() == &js_QNameClass) + if (obj->getClass() == &QNameClass) *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal()) DEFINE_GETTER(QNameLocalName_getter, - if (obj->getClass() == &js_QNameClass) + if (obj->getClass() == &QNameClass) *vp = obj->getQNameLocalNameVal()) static JSBool @@ -324,13 +324,13 @@ qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp) JSObject *obj2; obj2 = v->toObjectOrNull(); - *bp = (!obj2 || obj2->getClass() != &js_QNameClass) + *bp = (!obj2 || obj2->getClass() != &QNameClass) ? JS_FALSE : qname_identity(qn, obj2); return JS_TRUE; } -JS_FRIEND_DATA(Class) js_QNameClass = { +JS_FRIEND_DATA(Class) js::QNameClass = { "QName", JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_QName), @@ -364,7 +364,7 @@ JS_FRIEND_DATA(Class) js_QNameClass = { * qname_toString method, and therefore are exposed as constructable objects * in this implementation. */ -JS_FRIEND_DATA(Class) js_AttributeNameClass = { +JS_FRIEND_DATA(Class) js::AttributeNameClass = { js_AttributeName_str, JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) | JSCLASS_IS_ANONYMOUS, @@ -378,7 +378,7 @@ JS_FRIEND_DATA(Class) js_AttributeNameClass = { FinalizeStub }; -JS_FRIEND_DATA(Class) js_AnyNameClass = { +JS_FRIEND_DATA(Class) js::AnyNameClass = { js_AnyName_str, JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) | JSCLASS_IS_ANONYMOUS, @@ -422,7 +422,7 @@ ConvertQNameToString(JSContext *cx, JSObject *obj) if (!str) return NULL; - if (obj->getClass() == &js_AttributeNameClass) { + if (obj->getClass() == &AttributeNameClass) { JS::Anchor anchor(str); size_t length = str->length(); jschar *chars = (jschar *) cx->malloc_((length + 2) * sizeof(jschar)); @@ -453,7 +453,7 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) return false; if (!obj->isQName()) { - ReportIncompatibleMethod(cx, vp, &js_QNameClass); + ReportIncompatibleMethod(cx, vp, &QNameClass); return false; } @@ -497,7 +497,7 @@ static JSObject * NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, JSAtom *localName) { - JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass); + JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass); if (!obj) return NULL; if (!InitXMLQName(cx, obj, uri, prefix, localName)) @@ -514,7 +514,7 @@ NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, * exposed to scripts. */ JSObject *parent = GetGlobalForScopeChain(cx); - JSObject *obj = NewNonFunction(cx, &js_AttributeNameClass, NULL, parent); + JSObject *obj = NewNonFunction(cx, &AttributeNameClass, NULL, parent); if (!obj) return NULL; JS_ASSERT(obj->isQName()); @@ -534,13 +534,13 @@ js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval * production, step 2. */ if (nsval.isObject() && - nsval.toObject().getClass() == &js_AnyNameClass) { + nsval.toObject().getClass() == &AnyNameClass) { argv[0].setNull(); } else { argv[0] = nsval; } argv[1] = lnval; - return js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, argv); + return js_ConstructObject(cx, &QNameClass, NULL, NULL, 2, argv); } static JSBool @@ -616,8 +616,8 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, if (!JSVAL_IS_PRIMITIVE(urival)) { uriobj = JSVAL_TO_OBJECT(urival); clasp = uriobj->getClass(); - isNamespace = (clasp == &js_NamespaceClass); - isQName = (clasp == &js_QNameClass); + isNamespace = (clasp == &NamespaceClass); + isQName = (clasp == &QNameClass); } } @@ -629,7 +629,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, return JS_TRUE; } - obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass); + obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass); if (!obj) return JS_FALSE; } @@ -732,7 +732,7 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) nameval = argv[argc > 1]; isQName = !JSVAL_IS_PRIMITIVE(nameval) && - JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass; + JSVAL_TO_OBJECT(nameval)->getClass() == &QNameClass; } if (!obj) { @@ -744,7 +744,7 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) } /* Create and return a new QName object exactly as if constructed. */ - obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass); + obj = NewBuiltinClassInstanceXML(cx, &QNameClass); if (!obj) return JS_FALSE; } @@ -782,7 +782,7 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) return JS_FALSE; JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() == - &js_NamespaceClass); + &NamespaceClass); } if (JSVAL_IS_NULL(nsval)) { @@ -799,8 +799,8 @@ QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) isNamespace = isQName = JS_FALSE; if (!JSVAL_IS_PRIMITIVE(nsval)) { obj2 = JSVAL_TO_OBJECT(nsval); - isNamespace = (obj2->getClass() == &js_NamespaceClass); - isQName = (obj2->getClass() == &js_QNameClass); + isNamespace = (obj2->getClass() == &NamespaceClass); + isQName = (obj2->getClass() == &QNameClass); } #ifdef __GNUC__ /* suppress bogus gcc warnings */ else obj2 = NULL; @@ -1852,9 +1852,9 @@ ToXML(JSContext *cx, jsval v) JS_ASSERT(0); } - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { + if (clasp != &StringClass && + clasp != &NumberClass && + clasp != &BooleanClass) { goto bad; } } @@ -1933,9 +1933,9 @@ ToXMLList(JSContext *cx, jsval v) JS_ASSERT(0); } - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { + if (clasp != &StringClass && + clasp != &NumberClass && + clasp != &BooleanClass) { goto bad; } } @@ -2230,7 +2230,7 @@ GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) if (!match) { argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID; argv[1] = STRING_TO_JSVAL(uri); - ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, + ns = js_ConstructObject(cx, &NamespaceClass, NULL, NULL, 2, Valueify(argv)); if (!ns) return NULL; @@ -2783,16 +2783,16 @@ ToAttributeName(JSContext *cx, jsval v) obj = JSVAL_TO_OBJECT(v); clasp = obj->getClass(); - if (clasp == &js_AttributeNameClass) + if (clasp == &AttributeNameClass) return obj; - if (clasp == &js_QNameClass) { + if (clasp == &QNameClass) { qn = obj; uri = qn->getNameURI(); prefix = qn->getNamePrefix(); name = qn->getQNameLocalName(); } else { - if (clasp == &js_AnyNameClass) { + if (clasp == &AnyNameClass) { name = cx->runtime->atomState.starAtom; } else { if (!js_ValueToAtom(cx, Valueify(v), &name)) @@ -2853,9 +2853,9 @@ ToXMLName(JSContext *cx, jsval v, jsid *funidp) obj = JSVAL_TO_OBJECT(v); clasp = obj->getClass(); - if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass) + if (clasp == &AttributeNameClass || clasp == &QNameClass) goto out; - if (clasp == &js_AnyNameClass) { + if (clasp == &AnyNameClass) { name = cx->runtime->atomState.starAtom; goto construct; } @@ -2894,7 +2894,7 @@ ToXMLName(JSContext *cx, jsval v, jsid *funidp) construct: v = STRING_TO_JSVAL(name); - obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v)); + obj = js_ConstructObject(cx, &QNameClass, NULL, NULL, 1, Valueify(&v)); if (!obj) return NULL; @@ -3207,7 +3207,7 @@ DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list) JS_CHECK_RECURSION(cx, return JS_FALSE); if (xml->xml_class == JSXML_CLASS_ELEMENT && - nameqn->getClass() == &js_AttributeNameClass) { + nameqn->getClass() == &AttributeNameClass) { for (i = 0, n = xml->xml_attrs.length; i < n; i++) { attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); if (attr && MatchAttrName(nameqn, attr)) { @@ -3221,7 +3221,7 @@ DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list) kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (!kid) continue; - if (nameqn->getClass() != &js_AttributeNameClass && + if (nameqn->getClass() != &AttributeNameClass && MatchElemName(nameqn, kid)) { if (!Append(cx, list, kid)) return JS_FALSE; @@ -3670,7 +3670,7 @@ GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list) } } } else if (xml->xml_class == JSXML_CLASS_ELEMENT) { - attrs = (nameqn->getClass() == &js_AttributeNameClass); + attrs = (nameqn->getClass() == &AttributeNameClass); if (attrs) { array = &xml->xml_attrs; matcher = MatchAttrName; @@ -3921,7 +3921,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) goto bad; } else { nameobj = targetprop; - if (nameobj->getClass() == &js_AttributeNameClass) { + if (nameobj->getClass() == &AttributeNameClass) { /* * 2(c)(iii)(1-3). * Note that rxml can't be null here, because target @@ -4017,7 +4017,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) parent = kid->parent; if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { nameobj = kid->name; - if (nameobj->getClass() != &js_AttributeNameClass) { + if (nameobj->getClass() != &AttributeNameClass) { nameobj = NewXMLAttributeName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(), nameobj->getQNameLocalName()); if (!nameobj) @@ -4224,7 +4224,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) if (!ok) goto out; - if (nameobj->getClass() == &js_AttributeNameClass) { + if (nameobj->getClass() == &AttributeNameClass) { /* 7(a). */ if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) goto out; @@ -4477,7 +4477,7 @@ ResolveValue(JSContext *cx, JSXML *list, JSXML **result) return JS_TRUE; } - if (targetprop->getClass() == &js_AttributeNameClass) { + if (targetprop->getClass() == &AttributeNameClass) { *result = NULL; return JS_TRUE; } @@ -4533,7 +4533,7 @@ HasNamedProperty(JSXML *xml, JSObject *nameqn) } if (xml->xml_class == JSXML_CLASS_ELEMENT) { - if (nameqn->getClass() == &js_AttributeNameClass) { + if (nameqn->getClass() == &AttributeNameClass) { array = &xml->xml_attrs; matcher = MatchAttrName; } else { @@ -4572,7 +4572,7 @@ HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found) JSProperty *prop; JSXML *xml; - JS_ASSERT(obj->getClass() == &js_XMLClass); + JS_ASSERT(obj->getClass() == &XMLClass); if (!js_LookupProperty(cx, obj, funid, &pobj, &prop)) return false; @@ -4818,7 +4818,7 @@ xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool st return js_DeleteProperty(cx, obj, funid, rval, false); DeleteNamedProperty(cx, xml, nameqn, - nameqn->getClass() == &js_AttributeNameClass); + nameqn->getClass() == &AttributeNameClass); } /* @@ -5112,7 +5112,7 @@ out: return ok; } -JS_FRIEND_DATA(Class) js_XMLClass = { +JS_FRIEND_DATA(Class) js::XMLClass = { js_XML_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), @@ -5162,7 +5162,7 @@ StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) if (!*objp) return NULL; if (!(*objp)->isXML()) { - ReportIncompatibleMethod(cx, Valueify(vp), &js_XMLClass); + ReportIncompatibleMethod(cx, Valueify(vp), &XMLClass); return NULL; } xml = (JSXML *) (*objp)->getPrivate(); @@ -5196,7 +5196,7 @@ StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) if (!obj) \ return JS_FALSE; \ if (!obj->isXML()) { \ - ReportIncompatibleMethod(cx, Valueify(vp), &js_XMLClass); \ + ReportIncompatibleMethod(cx, Valueify(vp), &XMLClass); \ return JS_FALSE; \ } \ JSXML *xml = (JSXML *)obj->getPrivate(); \ @@ -5683,7 +5683,7 @@ xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp) if (!obj) return JS_FALSE; if (!obj->isXML()) { - ReportIncompatibleMethod(cx, Valueify(vp), &js_XMLClass); + ReportIncompatibleMethod(cx, Valueify(vp), &XMLClass); return JS_FALSE; } @@ -6480,13 +6480,13 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) } else { name = vp[2]; if (!JSVAL_IS_PRIMITIVE(name) && - JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass && + JSVAL_TO_OBJECT(name)->getClass() == &QNameClass && !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) { name = vp[2] = nameqn->getQNameLocalNameVal(); } } - nameqn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&name)); + nameqn = js_ConstructObject(cx, &QNameClass, NULL, NULL, 1, Valueify(&name)); if (!nameqn) return JS_FALSE; @@ -6588,7 +6588,7 @@ xml_setNamespace(JSContext *cx, uintN argc, jsval *vp) if (!xml) return JS_FALSE; - ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, + ns = js_ConstructObject(cx, &NamespaceClass, NULL, obj, argc == 0 ? 0 : 1, Valueify(vp + 2)); if (!ns) return JS_FALSE; @@ -6597,7 +6597,7 @@ xml_setNamespace(JSContext *cx, uintN argc, jsval *vp) qnargv[0] = OBJECT_TO_JSVAL(ns); qnargv[1] = OBJECT_TO_JSVAL(xml->name); - qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, Valueify(qnargv)); + qn = js_ConstructObject(cx, &QNameClass, NULL, NULL, 2, Valueify(qnargv)); if (!qn) return JS_FALSE; @@ -6925,7 +6925,7 @@ XML(JSContext *cx, uintN argc, Value *vp) if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); clasp = vobj->getClass(); - if (clasp == &js_XMLClass || + if (clasp == &XMLClass || (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { copy = DeepCopy(cx, xml, NULL, 0); if (!copy) @@ -7071,7 +7071,7 @@ NewXMLObject(JSContext *cx, JSXML *xml) JSObject *obj; JSObject *parent = GetGlobalForScopeChain(cx); - obj = NewNonFunction(cx, &js_XMLClass, NULL, parent); + obj = NewNonFunction(cx, &XMLClass, NULL, parent); if (!obj) return NULL; obj->setPrivate(xml); @@ -7103,7 +7103,7 @@ js_InitNamespaceClass(JSContext *cx, JSObject *obj) GlobalObject *global = obj->asGlobal(); - JSObject *namespaceProto = global->createBlankPrototype(cx, &js_NamespaceClass); + JSObject *namespaceProto = global->createBlankPrototype(cx, &NamespaceClass); if (!namespaceProto) return NULL; JSFlatString *empty = cx->runtime->emptyString; @@ -7112,7 +7112,7 @@ js_InitNamespaceClass(JSContext *cx, JSObject *obj) namespaceProto->syncSpecialEquality(); const uintN NAMESPACE_CTOR_LENGTH = 2; - JSFunction *ctor = global->createConstructor(cx, Namespace, &js_NamespaceClass, + JSFunction *ctor = global->createConstructor(cx, Namespace, &NamespaceClass, CLASS_ATOM(cx, Namespace), NAMESPACE_CTOR_LENGTH); if (!ctor) @@ -7137,7 +7137,7 @@ js_InitQNameClass(JSContext *cx, JSObject *obj) GlobalObject *global = obj->asGlobal(); - JSObject *qnameProto = global->createBlankPrototype(cx, &js_QNameClass); + JSObject *qnameProto = global->createBlankPrototype(cx, &QNameClass); if (!qnameProto) return NULL; JSAtom *empty = cx->runtime->emptyString; @@ -7146,7 +7146,7 @@ js_InitQNameClass(JSContext *cx, JSObject *obj) qnameProto->syncSpecialEquality(); const uintN QNAME_CTOR_LENGTH = 2; - JSFunction *ctor = global->createConstructor(cx, QName, &js_QNameClass, + JSFunction *ctor = global->createConstructor(cx, QName, &QNameClass, CLASS_ATOM(cx, QName), QNAME_CTOR_LENGTH); if (!ctor) return NULL; @@ -7170,7 +7170,7 @@ js_InitXMLClass(JSContext *cx, JSObject *obj) GlobalObject *global = obj->asGlobal(); - JSObject *xmlProto = global->createBlankPrototype(cx, &js_XMLClass); + JSObject *xmlProto = global->createBlankPrototype(cx, &XMLClass); if (!xmlProto) return NULL; JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT); @@ -7186,7 +7186,7 @@ js_InitXMLClass(JSContext *cx, JSObject *obj) } const uintN XML_CTOR_LENGTH = 1; - JSFunction *ctor = global->createConstructor(cx, XML, &js_XMLClass, CLASS_ATOM(cx, XML), + JSFunction *ctor = global->createConstructor(cx, XML, &XMLClass, CLASS_ATOM(cx, XML), XML_CTOR_LENGTH); if (!ctor) return NULL; @@ -7293,7 +7293,7 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) obj = NULL; for (tmp = scopeChain; tmp; tmp = tmp->getParent()) { Class *clasp = tmp->getClass(); - if (clasp == &js_BlockClass || clasp == &js_WithClass) + if (clasp == &BlockClass || clasp == &WithClass) continue; if (!tmp->getProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(&v))) return JS_FALSE; @@ -7304,7 +7304,7 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) obj = tmp; } - ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, 0, NULL); + ns = js_ConstructObject(cx, &NamespaceClass, NULL, obj, 0, NULL); if (!ns) return JS_FALSE; v = OBJECT_TO_JSVAL(ns); @@ -7322,7 +7322,7 @@ js_SetDefaultXMLNamespace(JSContext *cx, const Value &v) Value argv[2]; argv[0].setString(cx->runtime->emptyString); argv[1] = v; - JSObject *ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, 2, argv); + JSObject *ns = js_ConstructObject(cx, &NamespaceClass, NULL, NULL, 2, argv); if (!ns) return JS_FALSE; @@ -7407,7 +7407,7 @@ js_GetAnyName(JSContext *cx, jsid *idp) JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject; Value v = global->getReservedSlot(JSProto_AnyName); if (v.isUndefined()) { - JSObject *obj = NewNonFunction(cx, &js_AnyNameClass, NULL, global); + JSObject *obj = NewNonFunction(cx, &AnyNameClass, NULL, global); if (!obj) return false; @@ -7439,15 +7439,15 @@ js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *i JS_ASSERT(nameval.isObject()); nameobj = &nameval.toObject(); - if (nameobj->getClass() == &js_AnyNameClass) { + if (nameobj->getClass() == &AnyNameClass) { v = STRING_TO_JSVAL(cx->runtime->atomState.starAtom); - nameobj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, + nameobj = js_ConstructObject(cx, &QNameClass, NULL, NULL, 1, Valueify(&v)); if (!nameobj) return JS_FALSE; } else { - JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass || - nameobj->getClass() == &js_QNameClass); + JS_ASSERT(nameobj->getClass() == &AttributeNameClass || + nameobj->getClass() == &QNameClass); } qn = nameobj; @@ -7458,7 +7458,7 @@ js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *i do { /* Skip any With object that can wrap XML. */ target = obj; - while (target->getClass() == &js_WithClass) { + while (target->getClass() == &WithClass) { proto = target->getProto(); if (!proto) break; diff --git a/js/src/jsxml.h b/js/src/jsxml.h index 4f5ac22cac20..b8ef86fd3cdf 100644 --- a/js/src/jsxml.h +++ b/js/src/jsxml.h @@ -219,48 +219,12 @@ js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); extern JSObject * js_GetXMLObject(JSContext *cx, JSXML *xml); -extern JS_FRIEND_DATA(js::Class) js_XMLClass; -extern JS_FRIEND_DATA(js::Class) js_NamespaceClass; -extern JS_FRIEND_DATA(js::Class) js_QNameClass; -extern JS_FRIEND_DATA(js::Class) js_AttributeNameClass; -extern JS_FRIEND_DATA(js::Class) js_AnyNameClass; -extern js::Class js_XMLFilterClass; - /* * Methods to test whether an object or a value is of type "xml" (per typeof). */ -inline bool -JSObject::isXML() const -{ - return getClass() == &js_XMLClass; -} - -inline bool -JSObject::isXMLId() const -{ - js::Class *clasp = getClass(); - return clasp == &js_QNameClass || - clasp == &js_AttributeNameClass || - clasp == &js_AnyNameClass; -} #define VALUE_IS_XML(v) (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isXML()) -inline bool -JSObject::isNamespace() const -{ - return getClass() == &js_NamespaceClass; -} - -inline bool -JSObject::isQName() const -{ - js::Class* clasp = getClass(); - return clasp == &js_QNameClass || - clasp == &js_AttributeNameClass || - clasp == &js_AnyNameClass; -} - static inline bool IsXML(const js::Value &v) { diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 5c4ad7c3dcfe..d0cdc10d22ec 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -204,8 +204,8 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist } Jump testFunction(Condition cond, RegisterID fun) { - return branchPtr(cond, Address(fun, offsetof(JSObject, clasp)), - ImmPtr(&js_FunctionClass)); + return branchPtr(cond, Address(fun, JSObject::offsetOfClassPointer()), + ImmPtr(&FunctionClass)); } /* @@ -811,7 +811,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist } void loadObjClass(RegisterID objReg, RegisterID destReg) { - loadPtr(Address(objReg, offsetof(JSObject, clasp)), destReg); + loadPtr(Address(objReg, JSObject::offsetOfClassPointer()), destReg); } Jump testClass(Condition cond, RegisterID claspReg, js::Class *clasp) { @@ -819,7 +819,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist } Jump testObjClass(Condition cond, RegisterID objReg, js::Class *clasp) { - return branchPtr(cond, Address(objReg, offsetof(JSObject, clasp)), ImmPtr(clasp)); + return branchPtr(cond, Address(objReg, JSObject::offsetOfClassPointer()), ImmPtr(clasp)); } void branchValue(Condition cond, RegisterID reg, int32 value, RegisterID result) @@ -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); + int thingSize = (int)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); @@ -1279,7 +1280,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist if (templateObject->isDenseArray()) { JS_ASSERT(!templateObject->initializedLength); addPtr(Imm32(-thingSize + sizeof(JSObject)), result); - storePtr(result, Address(result, -sizeof(JSObject) + JSObject::offsetOfSlots())); + storePtr(result, Address(result, -(int)sizeof(JSObject) + JSObject::offsetOfSlots())); addPtr(Imm32(-(int)sizeof(JSObject)), result); } else { JS_ASSERT(!templateObject->newType); @@ -1288,7 +1289,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist } storePtr(ImmPtr(templateObject->lastProp), Address(result, offsetof(JSObject, lastProp))); - storePtr(ImmPtr(templateObject->clasp), Address(result, offsetof(JSObject, clasp))); + storePtr(ImmPtr(templateObject->getClass()), Address(result, JSObject::offsetOfClassPointer())); store32(Imm32(templateObject->flags), Address(result, offsetof(JSObject, flags))); store32(Imm32(templateObject->objShape), Address(result, offsetof(JSObject, objShape))); storePtr(ImmPtr(templateObject->newType), Address(result, offsetof(JSObject, newType))); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 539f7f25810a..f331e3b30cdc 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -133,6 +133,7 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructi inlining_(false), hasGlobalReallocation(false), oomInVector(false), + gcNumber(cx->runtime->gcNumber), applyTricks(NoApplyTricks), pcLengths(NULL) { @@ -188,7 +189,7 @@ mjit::Compiler::checkAnalysis(JSScript *script) return Compile_Abort; } - if (!script->ensureRanBytecode(cx)) + if (!script->ensureRanAnalysis(cx)) return Compile_Error; if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx)) return Compile_Error; @@ -338,6 +339,11 @@ mjit::Compiler::scanInlineCalls(uint32 index, uint32 depth) break; } + if (!script->types || !script->types->hasScope()) { + okay = false; + break; + } + CompileStatus status = checkAnalysis(script); if (status != Compile_Okay) return status; @@ -742,27 +748,60 @@ mjit::Compiler::generatePrologue() } } - /* Create the call object. */ + types::TypeScriptNesting *nesting = script->nesting(); + + /* + * Run the function prologue if necessary. This is always done in a + * stub for heavyweight functions (including nesting outer functions). + */ + JS_ASSERT_IF(nesting && nesting->children, script->function()->isHeavyweight()); if (script->function()->isHeavyweight()) { prepareStubCall(Uses(0)); - INLINE_STUBCALL(stubs::CreateFunCallObject, REJOIN_CREATE_CALL_OBJECT); - } - - j.linkTo(masm.label(), &masm); - - if (analysis->usesScopeChain() && !script->function()->isHeavyweight()) { + INLINE_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE); + } else { /* - * Load the scope chain into the frame if necessary. The scope chain - * is always set for global and eval frames, and will have been set by + * Load the scope chain into the frame if it will be needed by NAME + * opcodes or by the nesting prologue below. The scope chain is + * always set for global and eval frames, and will have been set by * CreateFunCallObject for heavyweight function frames. */ - RegisterID t0 = Registers::ReturnReg; - Jump hasScope = masm.branchTest32(Assembler::NonZero, - FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN)); - masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0); - masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0); - masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain())); - hasScope.linkTo(masm.label(), &masm); + if (analysis->usesScopeChain() || nesting) { + RegisterID t0 = Registers::ReturnReg; + Jump hasScope = masm.branchTest32(Assembler::NonZero, + FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN)); + masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0); + masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0); + masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain())); + hasScope.linkTo(masm.label(), &masm); + } + + if (nesting) { + /* + * Inline the common case for the nesting prologue: the + * function is a non-heavyweight inner function with no + * children of its own. We ensure during inference that the + * outer function does not add scope objects for 'let' or + * 'with', so that the frame's scope chain will be + * the parent's call object, and if it differs from the + * parent's current activation then the parent is reentrant. + */ + JSScript *parent = nesting->parent; + JS_ASSERT(parent); + JS_ASSERT_IF(parent->hasAnalysis() && parent->analysis()->ranBytecode(), + !parent->analysis()->addsScopeObjects()); + + RegisterID t0 = Registers::ReturnReg; + masm.move(ImmPtr(&parent->nesting()->activeCall), t0); + masm.loadPtr(Address(t0), t0); + + Address scopeChain(JSFrameReg, StackFrame::offsetOfScopeChain()); + Jump mismatch = masm.branchPtr(Assembler::NotEqual, t0, scopeChain); + masm.add32(Imm32(1), AbsoluteAddress(&nesting->activeFrames)); + + stubcc.linkExitDirect(mismatch, stubcc.masm.label()); + OOL_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + } } if (outerScript->usesArguments && !script->function()->isHeavyweight()) { @@ -780,6 +819,8 @@ mjit::Compiler::generatePrologue() Address(JSFrameReg, StackFrame::offsetOfArgs())); hasArgs.linkTo(masm.label(), &masm); } + + j.linkTo(masm.label(), &masm); } if (cx->typeInferenceEnabled()) { @@ -846,6 +887,13 @@ mjit::Compiler::finishThisUp(JITScript **jitp) if (globalSlots && globalObj->getRawSlots() != globalSlots) return Compile_Retry; + /* + * Watch for GCs which occurred during compilation. These may have + * renumbered shapes baked into the jitcode. + */ + if (cx->runtime->gcNumber != gcNumber) + return Compile_Retry; + for (size_t i = 0; i < branchPatches.length(); i++) { Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex); branchPatches[i].jump.linkTo(label, &masm); @@ -1328,7 +1376,8 @@ mjit::Compiler::finishThisUp(JITScript **jitp) #endif JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize); - JS_ASSERT(jit->scriptDataSize() == dataSize); + /* Pass in NULL here -- we don't want slop bytes to be counted. */ + JS_ASSERT(jit->scriptDataSize(NULL) == dataSize); /* Link fast and slow paths together. */ stubcc.fixCrossJumps(result, masm.size(), masm.size() + stubcc.size()); @@ -2700,9 +2749,8 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_UNBRAND) BEGIN_CASE(JSOP_UNBRANDTHIS) - jsop_this(); - jsop_unbrand(); - frame.pop(); + prepareStubCall(Uses(1)); + INLINE_STUBCALL(stubs::UnbrandThis, REJOIN_FALLTHROUGH); END_CASE(JSOP_UNBRANDTHIS) BEGIN_CASE(JSOP_GETGLOBAL) @@ -3111,22 +3159,31 @@ mjit::Compiler::emitReturn(FrameEntry *fe) * even on the entry frame. To avoid double-putting, EnterMethodJIT clears * out the entry frame's activation objects. */ - if (script->hasFunction && script->function()->isHeavyweight()) { - /* There will always be a call object. */ - prepareStubCall(Uses(fe ? 1 : 0)); - INLINE_STUBCALL(stubs::PutActivationObjects, REJOIN_NONE); - } else { - /* if (hasCallObj() || hasArgsObj()) */ - Jump putObjs = masm.branchTest32(Assembler::NonZero, - Address(JSFrameReg, StackFrame::offsetOfFlags()), - Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ)); - stubcc.linkExit(putObjs, Uses(frame.frameSlots())); + if (script->hasFunction) { + types::TypeScriptNesting *nesting = script->nesting(); + if (script->function()->isHeavyweight() || (nesting && nesting->children)) { + prepareStubCall(Uses(fe ? 1 : 0)); + INLINE_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE); + } else { + /* if (hasCallObj() || hasArgsObj()) */ + Jump putObjs = masm.branchTest32(Assembler::NonZero, + Address(JSFrameReg, StackFrame::offsetOfFlags()), + Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ)); + stubcc.linkExit(putObjs, Uses(frame.frameSlots())); - stubcc.leave(); - OOL_STUBCALL(stubs::PutActivationObjects, REJOIN_NONE); + stubcc.leave(); + OOL_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE); - emitReturnValue(&stubcc.masm, fe); - emitFinalReturn(stubcc.masm); + emitReturnValue(&stubcc.masm, fe); + emitFinalReturn(stubcc.masm); + + /* + * Do frame count balancing inline for inner functions in a nesting + * with no children of their own. + */ + if (nesting) + masm.sub32(Imm32(1), AbsoluteAddress(&nesting->activeFrames)); + } } emitReturnValue(&masm, fe); @@ -5038,6 +5095,22 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache, bool popGuaranteed return true; } + /* + * If this is a SETNAME to a variable of a non-reentrant outer function, + * set the variable's slot directly for the active call object. + */ + if (cx->typeInferenceEnabled() && js_CodeSpec[*PC].format & JOF_NAME) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true); + if (access.nesting) { + Address address = frame.loadNameAddress(access); + frame.storeTo(rhs, address, popGuaranteed); + frame.shimmy(1); + frame.freeReg(address.base); + return true; + } + } + /* * Set the property directly if we are accessing a known object which * always has the property in a particular inline slot. @@ -5200,6 +5273,26 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache, bool popGuaranteed void mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, bool isCall) { + /* + * If this is a NAME for a variable of a non-reentrant outer function, get + * the variable's slot directly for the active call object. We always need + * to check for undefined, however. + */ + if (cx->typeInferenceEnabled()) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true); + if (access.nesting) { + Address address = frame.loadNameAddress(access); + JSValueType type = knownPushedType(0); + BarrierState barrier = pushAddressMaybeBarrier(address, type, true, + /* testUndefined = */ true); + finishBarrier(barrier, REJOIN_GETTER, 0); + if (isCall) + jsop_callgname_epilogue(); + return; + } + } + PICGenInfo pic(isCall ? ic::PICInfo::CALLNAME : ic::PICInfo::NAME, JSOp(*PC), true); RESERVE_IC_SPACE(masm); @@ -5257,6 +5350,24 @@ mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, bool isCall) bool mjit::Compiler::jsop_xname(JSAtom *atom) { + /* + * If this is a GETXPROP for a variable of a non-reentrant outer function, + * treat in the same way as a NAME. + */ + if (cx->typeInferenceEnabled()) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true); + if (access.nesting) { + frame.pop(); + Address address = frame.loadNameAddress(access); + JSValueType type = knownPushedType(0); + BarrierState barrier = pushAddressMaybeBarrier(address, type, true, + /* testUndefined = */ true); + finishBarrier(barrier, REJOIN_GETTER, 0); + return true; + } + } + PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC), true); FrameEntry *fe = frame.peek(-1); @@ -5316,6 +5427,23 @@ mjit::Compiler::jsop_xname(JSAtom *atom) void mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache) { + /* + * If this is a BINDNAME for a variable of a non-reentrant outer function, + * the object is definitely the outer function's active call object. + */ + if (cx->typeInferenceEnabled()) { + ScriptAnalysis::NameAccess access = + analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true); + if (access.nesting) { + RegisterID reg = frame.allocReg(); + JSObject **pobj = &access.nesting->activeCall; + masm.move(ImmPtr(pobj), reg); + masm.loadPtr(Address(reg), reg); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); + return; + } + } + PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC), usePropCache); // This code does not check the frame flags to see if scopeChain has been @@ -5457,6 +5585,16 @@ mjit::Compiler::jsop_this() stubcc.rejoin(Changes(1)); } + /* + * Watch out for an obscure case where we don't know we are pushing + * an object: the script has not yet had a 'this' value assigned, + * so no pushed 'this' type has been inferred. Don't mark the type + * as known in this case, preserving the invariant that compiler + * types reflect inferred types. + */ + if (cx->typeInferenceEnabled() && knownPushedType(0) != JSVAL_TYPE_OBJECT) + return; + // Now we know that |this| is an object. frame.pop(); frame.learnThisIsObject(type != JSVAL_TYPE_OBJECT); @@ -5594,7 +5732,7 @@ mjit::Compiler::iterNext(ptrdiff_t offset) frame.unpinReg(reg); /* Test clasp */ - Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, &js_IteratorClass); + Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, &IteratorClass); stubcc.linkExit(notFast, Uses(1)); /* Get private from iter obj. */ @@ -5649,7 +5787,7 @@ mjit::Compiler::iterMore(jsbytecode *target) RegisterID tempreg = frame.allocReg(); /* Test clasp */ - Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, &js_IteratorClass); + Jump notFast = masm.testObjClass(Assembler::NotEqual, reg, &IteratorClass); stubcc.linkExitForBranch(notFast); /* Get private from iter obj. */ @@ -5688,7 +5826,7 @@ mjit::Compiler::iterEnd() frame.unpinReg(reg); /* Test clasp */ - Jump notIterator = masm.testObjClass(Assembler::NotEqual, reg, &js_IteratorClass); + Jump notIterator = masm.testObjClass(Assembler::NotEqual, reg, &IteratorClass); stubcc.linkExit(notIterator, Uses(1)); /* Get private from iter obj. */ @@ -5907,7 +6045,7 @@ mjit::Compiler::jsop_callgname_epilogue() /* Paths for known object callee. */ if (fval->isConstant()) { JSObject *obj = &fval->getValue().toObject(); - if (obj->getParent() == globalObj) { + if (obj->getGlobal() == globalObj) { frame.push(UndefinedValue()); } else { prepareStubCall(Uses(1)); @@ -5917,6 +6055,20 @@ mjit::Compiler::jsop_callgname_epilogue() return; } + /* + * Fast path for functions whose global is statically known to be the + * current global. This is primarily for calls on inner functions within + * nestings, whose direct parent is a call object rather than the global + * and which will make a stub call in the path below. + */ + if (cx->typeInferenceEnabled()) { + types::TypeSet *types = analysis->pushedTypes(PC, 0); + if (types->hasGlobalObject(cx, globalObj)) { + frame.push(UndefinedValue()); + return; + } + } + /* * Optimized version. This inlines the common case, calling a * (non-proxied) function that has the same global as the current @@ -6941,7 +7093,7 @@ mjit::Compiler::fixDoubleTypes(jsbytecode *target) } else { JS_ASSERT(vt.type == JSVAL_TYPE_DOUBLE); } - } else if (fe->isType(JSVAL_TYPE_DOUBLE)) { + } else if (vt.type == JSVAL_TYPE_DOUBLE) { fixedDoubleToAnyEntries.append(newv->slot); frame.syncAndForgetFe(fe); frame.forgetLoopReg(fe); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 0c689a643982..39e0e98c58de 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -468,6 +468,7 @@ class Compiler : public BaseCompiler bool inlining_; bool hasGlobalReallocation; bool oomInVector; // True if we have OOM'd appending to a vector. + uint32 gcNumber; enum { NoApplyTricks, LazyArgsObj } applyTricks; PCLengthEntry *pcLengths; diff --git a/js/src/methodjit/FastBuiltins.cpp b/js/src/methodjit/FastBuiltins.cpp index 454335b18a3c..acbf79342c7a 100644 --- a/js/src/methodjit/FastBuiltins.cpp +++ b/js/src/methodjit/FastBuiltins.cpp @@ -508,7 +508,7 @@ mjit::Compiler::compileArrayWithArgs(uint32 argc) stubcc.linkExit(emptyFreeList, Uses(0)); for (unsigned i = 0; i < argc; i++) { - FrameEntry *arg = frame.peek(-argc + i); + FrameEntry *arg = frame.peek(-(int)argc + i); frame.storeTo(arg, Address(result, JSObject::getFixedSlotOffset(i)), /* popped = */ true); } diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 72dd8f2d3c87..91c8fda2d9bd 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -793,83 +793,69 @@ mjit::Compiler::jsop_typeof() bool mjit::Compiler::booleanJumpScript(JSOp op, jsbytecode *target) { + // JSOP_AND and JSOP_OR may leave the value on the stack (despite + // the frame.pop() below), so we need to sync it. + if (op == JSOP_AND || op == JSOP_OR) { + frame.syncForBranch(target, Uses(0)); + } else { + JS_ASSERT(op == JSOP_IFEQ || op == JSOP_IFEQX || + op == JSOP_IFNE || op == JSOP_IFNEX); + frame.syncForBranch(target, Uses(1)); + } + FrameEntry *fe = frame.peek(-1); - - MaybeRegisterID type; - MaybeRegisterID data; - - if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) - type.setReg(frame.copyTypeIntoReg(fe)); - if (!fe->isType(JSVAL_TYPE_DOUBLE)) - data.setReg(frame.copyDataIntoReg(fe)); - - frame.syncAndForgetEverything(); - Assembler::Condition cond = (op == JSOP_IFNE || op == JSOP_IFNEX || op == JSOP_OR) ? Assembler::NonZero : Assembler::Zero; - Assembler::Condition ncond = (op == JSOP_IFNE || op == JSOP_IFNEX || op == JSOP_OR) - ? Assembler::Zero - : Assembler::NonZero; - /* Inline path: Boolean guard + call script. */ - MaybeJump jmpNotBool; - MaybeJump jmpNotExecScript; - if (type.isSet()) { - jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, type.reg())); - } else { - if (!fe->isTypeKnown()) { - jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, - frame.addressOf(fe))); - } else if (fe->isNotType(JSVAL_TYPE_BOOLEAN) && - fe->isNotType(JSVAL_TYPE_INT32)) { - jmpNotBool.setJump(masm.jump()); - } + // Load data register and pin it so that frame.testBoolean + // below cannot evict it. + MaybeRegisterID data; + if (!fe->isType(JSVAL_TYPE_DOUBLE)) { + data = frame.tempRegForData(fe); + frame.pinReg(data.reg()); } - /* - * TODO: We don't need the second jump if - * jumpInScript() can go from ool path to inline path. - */ + // Test for boolean if needed. + bool needStub = false; + if (!fe->isType(JSVAL_TYPE_BOOLEAN) && !fe->isType(JSVAL_TYPE_INT32)) { + Jump notBool; + if (fe->mightBeType(JSVAL_TYPE_BOOLEAN)) + notBool = frame.testBoolean(Assembler::NotEqual, fe); + else + notBool = masm.jump(); + + stubcc.linkExitForBranch(notBool); + needStub = true; + } + if (data.isSet()) + frame.unpinReg(data.reg()); + + // Test + branch. + Jump branch; if (!fe->isType(JSVAL_TYPE_DOUBLE)) - jmpNotExecScript.setJump(masm.branchTest32(ncond, data.reg(), data.reg())); - Label lblExecScript = masm.label(); - Jump j = masm.jump(); + branch = masm.branchTest32(cond, data.reg()); + else + branch = masm.jump(); // dummy jump + // OOL path: call ValueToBoolean and branch. + if (needStub) { + stubcc.leave(); - /* OOL path: Conversion to boolean. */ - MaybeJump jmpCvtExecScript; - MaybeJump jmpCvtRejoin; - Label lblCvtPath = stubcc.masm.label(); - - if (!fe->isTypeKnown() || - !(fe->isType(JSVAL_TYPE_BOOLEAN) || fe->isType(JSVAL_TYPE_INT32))) { - /* Note: this cannot overwrite slots holding loop invariants. */ + // Note: this cannot overwrite slots holding loop invariants. stubcc.masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::ValueToBoolean), frame.totalDepth()); - - jmpCvtExecScript.setJump(stubcc.masm.branchTest32(cond, Registers::ReturnReg, - Registers::ReturnReg)); - jmpCvtRejoin.setJump(stubcc.masm.jump()); } - /* Rejoin tag. */ - Label lblAfterScript = masm.label(); + Jump stubBranch = stubcc.masm.branchTest32(cond, Registers::ReturnReg); - /* Patch up jumps. */ - if (jmpNotBool.isSet()) - stubcc.linkExitDirect(jmpNotBool.getJump(), lblCvtPath); - if (jmpNotExecScript.isSet()) - jmpNotExecScript.getJump().linkTo(lblAfterScript, &masm); - - if (jmpCvtExecScript.isSet()) - stubcc.crossJump(jmpCvtExecScript.getJump(), lblExecScript); - if (jmpCvtRejoin.isSet()) - stubcc.crossJump(jmpCvtRejoin.getJump(), lblAfterScript); + // Rejoin from the stub call fallthrough. + if (needStub) + stubcc.rejoin(Changes(0)); frame.pop(); - return jumpAndTrace(j, target); + return jumpAndTrace(branch, target, &stubBranch); } bool @@ -1570,7 +1556,7 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed) ic.slowPathStart = stubcc.syncExit(Uses(3)); // Guard obj is a dense array. - ic.claspGuard = masm.testObjClass(Assembler::NotEqual, ic.objReg, &js_ArrayClass); + ic.claspGuard = masm.testObjClass(Assembler::NotEqual, ic.objReg, &ArrayClass); stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart); // Guard in range of initialized length. @@ -2120,7 +2106,7 @@ mjit::Compiler::jsop_getelem(bool isCall) } // Guard on the clasp. - ic.claspGuard = masm.testObjClass(Assembler::NotEqual, ic.objReg, &js_ArrayClass); + ic.claspGuard = masm.testObjClass(Assembler::NotEqual, ic.objReg, &ArrayClass); stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart); Int32Key key = id->isConstant() diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 2ecd238bb14b..36945119bdad 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -878,6 +878,19 @@ FrameState::syncAndForgetFe(FrameEntry *fe, bool markSynced) fe->data.setMemory(); } +inline JSC::MacroAssembler::Address +FrameState::loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access) +{ + JS_ASSERT(access.script && access.nesting); + + RegisterID reg = allocReg(); + Value **pbase = access.arg ? &access.nesting->argArray : &access.nesting->varArray; + masm.move(ImmPtr(pbase), reg); + masm.loadPtr(Address(reg), reg); + + return Address(reg, access.index * sizeof(Value)); +} + inline void FrameState::forgetLoopReg(FrameEntry *fe) { diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 21b2502ed522..347ba7d083d8 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -1949,9 +1949,6 @@ FrameState::pushCopyOf(FrameEntry *backing) FrameEntry * FrameState::walkTrackerForUncopy(FrameEntry *original) { - /* Temporary entries are immutable and should never be uncopied. */ - JS_ASSERT(!isTemporary(original)); - uint32 firstCopy = InvalidIndex; FrameEntry *bestFe = NULL; uint32 ncopies = 0; @@ -1978,7 +1975,7 @@ FrameState::walkTrackerForUncopy(FrameEntry *original) JS_ASSERT(firstCopy != InvalidIndex); JS_ASSERT(bestFe); - JS_ASSERT(bestFe > original); + JS_ASSERT_IF(!isTemporary(original), bestFe > original); /* Mark all extra copies as copies of the new backing index. */ bestFe->setCopyOf(NULL); @@ -2873,6 +2870,8 @@ FrameState::clearTemporaries() for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) { if (!fe->isTracked()) continue; + if (fe->isCopied()) + uncopy(fe); forgetAllRegs(fe); fe->resetSynced(); } diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index 2971903db29d..dd4f19c44d78 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -953,6 +953,12 @@ class FrameState inline void syncAndForgetFe(FrameEntry *fe, bool markSynced = false); inline void forgetLoopReg(FrameEntry *fe); + /* + * Get an address for the specified name access in another script. + * The compiler owns the result's base register. + */ + inline Address loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access); + private: inline AnyRegisterID allocAndLoadReg(FrameEntry *fe, bool fp, RematInfo::RematType type); inline void forgetReg(AnyRegisterID reg); diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index c678042d0aae..d45001f21ab0 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -115,7 +115,7 @@ top: jsbytecode *pc = script->main() + tn->start + tn->length; cx->regs().pc = pc; - JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE); + JSBool ok = UnwindScope(cx, tn->stackDepth, JS_TRUE); JS_ASSERT(cx->regs().sp == fp->base() + tn->stackDepth); switch (tn->kind) { @@ -178,7 +178,7 @@ static void InlineReturn(VMFrame &f) { JS_ASSERT(f.fp() != f.entryfp); - JS_ASSERT(!js_IsActiveWithOrBlock(f.cx, &f.fp()->scopeChain(), 0)); + JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0)); f.cx->stack.popInlineFrame(f.regs); DebugOnly op = js_GetOpcode(f.cx, f.fp()->script(), f.regs.pc); @@ -361,7 +361,7 @@ UncachedInlineCall(VMFrame &f, InitialFrameFlags initial, PreserveRegsGuard regsGuard(cx, regs); /* Scope with a call object parented by callee's parent. */ - if (newfun->isHeavyweight() && !js::CreateFunCallObject(cx, regs.fp())) + if (!regs.fp()->functionPrologue(cx)) return false; /* @@ -497,13 +497,6 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, bool lowered, UncachedCallRes return; } -void JS_FASTCALL -stubs::PutActivationObjects(VMFrame &f) -{ - JS_ASSERT(f.fp()->hasCallObj() || f.fp()->hasArgsObj()); - f.fp()->putActivationObjects(); -} - static void RemoveOrphanedNative(JSContext *cx, StackFrame *fp) { @@ -603,7 +596,7 @@ js_InternalThrow(VMFrame &f) // and epilogues. RunTracer(), Interpret(), and Invoke() all // rely on this property. JS_ASSERT(!f.fp()->finishedInInterpreter()); - js_UnwindScope(cx, 0, cx->isExceptionPending()); + UnwindScope(cx, 0, cx->isExceptionPending()); ScriptEpilogue(f.cx, f.fp(), false); // Don't remove the last frame, this is the responsibility of @@ -634,7 +627,7 @@ js_InternalThrow(VMFrame &f) */ cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished); - if (!script->ensureRanBytecode(cx)) { + if (!script->ensureRanAnalysis(cx)) { js_ReportOutOfMemory(cx); return NULL; } @@ -670,14 +663,6 @@ js_InternalThrow(VMFrame &f) return script->nativeCodeForPC(fp->isConstructing(), pc); } -void JS_FASTCALL -stubs::CreateFunCallObject(VMFrame &f) -{ - JS_ASSERT(f.fp()->fun()->isHeavyweight()); - if (!js::CreateFunCallObject(f.cx, f.fp())) - THROW(); -} - void JS_FASTCALL stubs::CreateThis(VMFrame &f, JSObject *proto) { @@ -782,7 +767,7 @@ HandleErrorInExcessFrame(VMFrame &f, StackFrame *stopFp, bool searchedTopmostFra break; /* Unwind and return. */ - returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->isExceptionPending())); + returnOK &= UnwindScope(cx, 0, returnOK || cx->isExceptionPending()); returnOK = ScriptEpilogue(cx, fp, returnOK); InlineReturn(f); } @@ -1251,7 +1236,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM JSOp op = JSOp(*pc); const JSCodeSpec *cs = &js_CodeSpec[op]; - if (!script->ensureRanBytecode(cx)) { + if (!script->ensureRanAnalysis(cx)) { js_ReportOutOfMemory(cx); return js_InternalThrow(f); } @@ -1395,7 +1380,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM break; } - case REJOIN_CHECK_ARGUMENTS: { + case REJOIN_CHECK_ARGUMENTS: /* * Do all the work needed in arity check JIT prologues after the * arguments check occurs (FixupArity has been called if needed, but @@ -1406,22 +1391,17 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM SetValueRangeToUndefined(fp->slots(), script->nfixed); - if (fp->fun()->isHeavyweight()) { - if (!js::CreateFunCallObject(cx, fp)) - return js_InternalThrow(f); - } - + if (!fp->functionPrologue(cx)) + return js_InternalThrow(f); /* FALLTHROUGH */ - } - case REJOIN_CREATE_CALL_OBJECT: { + case REJOIN_FUNCTION_PROLOGUE: fp->scopeChain(); /* Construct the 'this' object for the frame if necessary. */ if (!ScriptPrologueOrGeneratorResume(cx, fp, types::UseNewTypeAtEntry(cx, fp))) return js_InternalThrow(f); break; - } case REJOIN_CALL_PROLOGUE: case REJOIN_CALL_PROLOGUE_LOWERED_CALL: @@ -1493,6 +1473,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM break; case JSOP_CALLGNAME: + case JSOP_CALLNAME: if (!ComputeImplicitThis(cx, &fp->scopeChain(), nextsp[-2], &nextsp[-1])) return js_InternalThrow(f); f.regs.pc = nextpc; diff --git a/js/src/methodjit/LoopState.cpp b/js/src/methodjit/LoopState.cpp index 68d019e9ef2d..c14b92ef8686 100644 --- a/js/src/methodjit/LoopState.cpp +++ b/js/src/methodjit/LoopState.cpp @@ -576,11 +576,16 @@ LoopState::hoistArrayLengthCheck(InvariantArrayKind arrayKind, const CrossSSAVal if (indexSlot == UNASSIGNED) { /* Hoist checks on x[n] accesses for constant n. */ + if (indexConstant < 0) { + JaegerSpew(JSpew_Analysis, "Constant index is negative\n"); + return false; + } return addHoistedCheck(arrayKind, objSlot, UNASSIGNED, UNASSIGNED, indexConstant); } if (loopInvariantEntry(indexSlot)) { /* Hoist checks on x[y] accesses when y is loop invariant. */ + addNegativeCheck(indexSlot, indexConstant); return addHoistedCheck(arrayKind, objSlot, indexSlot, UNASSIGNED, indexConstant); } diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index f796039c1f98..52af165854dc 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -925,7 +925,8 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi } /* See comment in mjit::Compiler::emitReturn. */ - fp->markActivationObjectsAsPut(); + if (fp->isFunctionFrame()) + fp->markFunctionEpilogueDone(); return ok ? Jaeger_Returned : Jaeger_Throwing; } @@ -1137,21 +1138,23 @@ mjit::JITScript::~JITScript() } size_t -JSScript::jitDataSize() +JSScript::jitDataSize(size_t(*mus)(void *)) { size_t n = 0; if (jitNormal) - n += jitNormal->scriptDataSize(); + n += jitNormal->scriptDataSize(mus); if (jitCtor) - n += jitCtor->scriptDataSize(); + n += jitCtor->scriptDataSize(mus); return n; } /* Please keep in sync with Compiler::finishThisUp! */ size_t -mjit::JITScript::scriptDataSize() +mjit::JITScript::scriptDataSize(size_t(*mus)(void *)) { - return sizeof(JITScript) + + size_t usable = mus ? mus(this) : 0; + return usable ? usable : + sizeof(JITScript) + sizeof(NativeMapEntry) * nNmapPairs + sizeof(InlineFrame) * nInlineFrames + sizeof(CallSite) * nCallSites + diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 6e250ebf3d01..d6a29283ee4d 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -285,12 +285,15 @@ enum RejoinState { /* * Type check on arguments failed during prologue, need stack check and - * call object creation before script can execute. + * the rest of the JIT prologue before the script can execute. */ REJOIN_CHECK_ARGUMENTS, - /* A GC while making a call object occurred, discarding the script's jitcode. */ - REJOIN_CREATE_CALL_OBJECT, + /* + * The script's jitcode was discarded after marking an outer function as + * reentrant or due to a GC while creating a call object. + */ + REJOIN_FUNCTION_PROLOGUE, /* * State after calling a stub which returns a JIT code pointer for a call @@ -640,7 +643,7 @@ struct JITScript { void trace(JSTracer *trc); - size_t scriptDataSize(); + size_t scriptDataSize(size_t(*mus)(void *)); jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline) const; @@ -687,9 +690,9 @@ inline void ReleaseScriptCode(JSContext *cx, JSScript *script) { if (script->jitCtor) - mjit::ReleaseScriptCode(cx, script, CONSTRUCT); + mjit::ReleaseScriptCode(cx, script, true); if (script->jitNormal) - mjit::ReleaseScriptCode(cx, script, NO_CONSTRUCT); + mjit::ReleaseScriptCode(cx, script, false); } // Expand all stack frames inlined by the JIT within a compartment. diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 1db197b18f65..b28c717ab57b 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -775,7 +775,7 @@ class CallCompiler : public BaseCompiler RegisterID t0 = tempRegs.takeAnyReg().reg(); /* Guard that it's actually a function object. */ - Jump claspGuard = masm.testObjClass(Assembler::NotEqual, ic.funObjReg, &js_FunctionClass); + Jump claspGuard = masm.testObjClass(Assembler::NotEqual, ic.funObjReg, &FunctionClass); /* Guard that it's the same function. */ JSFunction *fun = obj->getFunctionPrivate(); @@ -1354,7 +1354,7 @@ ic::GenerateArgumentCheckStub(VMFrame &f) JSC::CodeLocationLabel cs = linker.finalize(); JaegerSpew(JSpew_PICs, "generated ARGS CHECK stub %p (%lu bytes)\n", - cs.executableAddress(), masm.size()); + cs.executableAddress(), (unsigned long)masm.size()); Repatcher repatch(jit); repatch.relink(jit->argsCheckJump, cs); diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 7576e2cf5752..cc4524bd08c9 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -323,7 +323,7 @@ class SetPropCompiler : public PICStubCompiler return error(); } - JS_ASSERT_IF(!shape->hasDefaultSetter(), obj->getClass() == &js_CallClass); + JS_ASSERT_IF(!shape->hasDefaultSetter(), obj->isCall()); MaybeJump skipOver; @@ -691,9 +691,10 @@ class SetPropCompiler : public PICStubCompiler * objects may differ due to eval(), DEFFUN, etc.). */ RecompilationMonitor monitor(cx); - JSScript *script = obj->getCallObjCalleeFunction()->script(); + JSFunction *fun = obj->getCallObjCalleeFunction(); + JSScript *script = fun->script(); uint16 slot = uint16(shape->shortid); - if (!script->ensureHasTypes(cx)) + if (!script->ensureHasTypes(cx, fun)) return error(); { types::AutoEnterTypeInference enter(cx); @@ -914,8 +915,8 @@ class GetPropCompiler : public PICStubCompiler Assembler masm; masm.loadObjClass(pic.objReg, pic.shapeReg); - Jump isDense = masm.testClass(Assembler::Equal, pic.shapeReg, &js_ArrayClass); - Jump notArray = masm.testClass(Assembler::NotEqual, pic.shapeReg, &js_SlowArrayClass); + Jump isDense = masm.testClass(Assembler::Equal, pic.shapeReg, &ArrayClass); + Jump notArray = masm.testClass(Assembler::NotEqual, pic.shapeReg, &SlowArrayClass); isDense.linkTo(masm.label(), &masm); masm.load32(Address(pic.objReg, offsetof(JSObject, privateData)), pic.objReg); @@ -1494,7 +1495,7 @@ class ScopeNameCompiler : public PICStubCompiler * tree in ComputeImplicitThis. */ if (pic.kind == ic::PICInfo::CALLNAME) { - JS_ASSERT(obj->getClass() == &js_CallClass); + JS_ASSERT(obj->isCall()); Value *thisVp = &cx->regs().sp[1]; Address thisSlot(JSFrameReg, StackFrame::offsetOfFixed(thisVp - cx->fp()->slots())); masm.storeValue(UndefinedValue(), thisSlot); @@ -1597,7 +1598,7 @@ class ScopeNameCompiler : public PICStubCompiler if (obj != getprop.holder) return disable("property is on proto of a scope object"); - if (obj->getClass() == &js_CallClass) + if (obj->isCall()) return generateCallStub(obj); LookupStatus status = getprop.testForGet(); @@ -1641,7 +1642,7 @@ class ScopeNameCompiler : public PICStubCompiler const Shape *shape = getprop.shape; JSObject *normalized = obj; - if (obj->getClass() == &js_WithClass && !shape->hasDefaultGetter()) + if (obj->isWith() && !shape->hasDefaultGetter()) normalized = js_UnwrapWithObject(cx, obj); NATIVE_GET(cx, normalized, holder, shape, JSGET_METHOD_BARRIER, vp, return false); if (thisvp) @@ -2049,7 +2050,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) { regs.sp[-2].setString(JSID_TO_STRING(id)); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) + if (!OnUnknownMethod(cx, regs.sp - 2)) THROW(); } #endif @@ -2805,7 +2806,7 @@ ic::CallElement(VMFrame &f, ic::GetElementIC *ic) if (JS_UNLIKELY(f.regs.sp[-2].isPrimitive()) && thisv.isObject()) { f.regs.sp[-2] = f.regs.sp[-1]; f.regs.sp[-1].setObject(*thisObj); - if (!js_OnUnknownMethod(cx, f.regs.sp - 2)) + if (!OnUnknownMethod(cx, f.regs.sp - 2)) THROW(); } else #endif diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 752680127f99..fc304b7abb54 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -389,7 +389,7 @@ NameOp(VMFrame &f, JSObject *obj, bool callname) } else { shape = (Shape *)prop; JSObject *normalized = obj; - if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter()) + if (normalized->isWith() && !shape->hasDefaultGetter()) normalized = js_UnwrapWithObject(cx, normalized); NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL); } @@ -552,7 +552,7 @@ stubs::CallElem(VMFrame &f) if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) { regs.sp[-2] = regs.sp[-1]; regs.sp[-1].setObject(*thisObj); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) + if (!OnUnknownMethod(cx, regs.sp - 2)) THROW(); } else #endif @@ -1350,8 +1350,8 @@ stubs::NewInitObject(VMFrame &f, JSObject *baseobj) TypeObject *type = (TypeObject *) f.scratch; if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + gc::AllocKind kind = GuessObjectGCKind(0, false); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); if (!obj) THROW(); if (type) @@ -1740,7 +1740,7 @@ stubs::CallProp(VMFrame &f, JSAtom *origAtom) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) { regs.sp[-2].setString(origAtom); - if (!js_OnUnknownMethod(cx, regs.sp - 2)) + if (!OnUnknownMethod(cx, regs.sp - 2)) THROW(); } #endif @@ -2016,10 +2016,9 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj) * static scope. */ JSObject *obj2 = &fp->scopeChain(); - Class *clasp; - while ((clasp = obj2->getClass()) == &js_WithClass) + while (obj2->isWith()) obj2 = obj2->getParent(); - if (clasp == &js_BlockClass && + if (obj2->isBlock() && obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) { JSObject *youngestProto = obj2->getProto(); JS_ASSERT(youngestProto->isStaticBlock()); @@ -2049,7 +2048,7 @@ stubs::LeaveBlock(VMFrame &f, JSObject *blockChain) */ JSObject *obj = &fp->scopeChain(); if (obj->getProto() == blockChain) { - JS_ASSERT(obj->getClass() == &js_BlockClass); + JS_ASSERT(obj->isBlock()); if (!js_PutBlockObject(cx, JS_TRUE)) THROW(); } @@ -2195,6 +2194,19 @@ stubs::Unbrand(VMFrame &f) obj->unbrand(f.cx); } +void JS_FASTCALL +stubs::UnbrandThis(VMFrame &f) +{ + if (!ComputeThis(f.cx, f.fp())) + THROW(); + Value &thisv = f.fp()->thisValue(); + if (!thisv.isObject()) + return; + JSObject *obj = &thisv.toObject(); + if (obj->isNative()) + obj->unbrand(f.cx); +} + void JS_FASTCALL stubs::Pos(VMFrame &f) { @@ -2521,6 +2533,27 @@ stubs::Exception(VMFrame &f) f.cx->clearPendingException(); } +void JS_FASTCALL +stubs::FunctionFramePrologue(VMFrame &f) +{ + if (!f.fp()->functionPrologue(f.cx)) + THROW(); +} + +void JS_FASTCALL +stubs::FunctionFrameEpilogue(VMFrame &f) +{ + f.fp()->functionEpilogue(); +} + +void JS_FASTCALL +stubs::AnyFrameEpilogue(VMFrame &f) +{ + if (f.fp()->isNonEvalFunctionFrame()) + f.fp()->functionEpilogue(); + stubs::ScriptDebugEpilogue(f); +} + template int32 JS_FASTCALL stubs::ConvertToTypedInt(JSContext *cx, Value *vp) diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index a59a77ba2bad..374ed93d4dd3 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -113,8 +113,6 @@ void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); void JS_FASTCALL Throw(VMFrame &f); -void JS_FASTCALL PutActivationObjects(VMFrame &f); -void JS_FASTCALL CreateFunCallObject(VMFrame &f); #if JS_MONOIC void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic); #else @@ -202,6 +200,7 @@ JSBool JS_FASTCALL InstanceOf(VMFrame &f); void JS_FASTCALL FastInstanceOf(VMFrame &f); void JS_FASTCALL ArgCnt(VMFrame &f); void JS_FASTCALL Unbrand(VMFrame &f); +void JS_FASTCALL UnbrandThis(VMFrame &f); /* * Helper for triggering recompilation should a name read miss a type barrier, @@ -227,6 +226,11 @@ void JS_FASTCALL ConvertToTypedFloat(JSContext *cx, Value *vp); void JS_FASTCALL Exception(VMFrame &f); +void JS_FASTCALL FunctionFramePrologue(VMFrame &f); +void JS_FASTCALL FunctionFrameEpilogue(VMFrame &f); + +void JS_FASTCALL AnyFrameEpilogue(VMFrame &f); + JSObject * JS_FASTCALL NewDenseUnallocatedArray(VMFrame &f, uint32 length); diff --git a/js/src/methodjit/TrampolineCompiler.cpp b/js/src/methodjit/TrampolineCompiler.cpp index 141b4a4dbccc..d37d79273386 100644 --- a/js/src/methodjit/TrampolineCompiler.cpp +++ b/js/src/methodjit/TrampolineCompiler.cpp @@ -110,7 +110,8 @@ TrampolineCompiler::compileTrampoline(Trampolines::TrampolinePtr *where, /* * This is shamelessly copied from emitReturn, but with several changes: * - There was always at least one inline call. - * - We don't know if there is a call object, so we always check. + * - We don't know if there are activation objects or a script with nesting + * state whose active frames need adjustment, so we always stub the epilogue. * - We don't know where we came from, so we don't know frame depth or PC. * - There is no stub buffer. */ @@ -120,13 +121,8 @@ TrampolineCompiler::generateForceReturn(Assembler &masm) /* The JSStackFrame register may have been clobbered while returning, reload it. */ masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); - masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::ScriptDebugEpilogue), NULL, NULL, 0); - - /* if (hasArgsObj() || hasCallObj()) stubs::PutActivationObjects() */ - Jump noActObjs = masm.branchTest32(Assembler::Zero, FrameFlagsAddress(), - Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ)); - masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::PutActivationObjects), NULL, NULL, 0); - noActObjs.linkTo(masm.label(), &masm); + /* Perform the frame epilogue. */ + masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::AnyFrameEpilogue), NULL, NULL, 0); /* Store any known return value */ masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 687e4c21bb32..31b38034a46b 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1560,9 +1560,9 @@ ValueToScript(JSContext *cx, jsval v) JSObject *obj = JSVAL_TO_OBJECT(v); JSClass *clasp = JS_GET_CLASS(cx, obj); - if (clasp == Jsvalify(&js_ScriptClass)) { + if (clasp == Jsvalify(&ScriptClass)) { script = (JSScript *) JS_GetPrivate(cx, obj); - } else if (clasp == Jsvalify(&js_GeneratorClass)) { + } else if (clasp == Jsvalify(&GeneratorClass)) { JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj); fun = gen->floatingFrame()->fun(); script = fun->script(); @@ -1620,8 +1620,8 @@ GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, v = argv[0]; intarg = 0; if (!JSVAL_IS_PRIMITIVE(v) && - (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&js_FunctionClass) || - JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&js_ScriptClass))) { + (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass) || + JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&ScriptClass))) { script = ValueToScript(cx, v); if (!script) return JS_FALSE; @@ -4070,13 +4070,13 @@ MJitCodeStats(JSContext *cx, uintN argc, jsval *vp) #ifdef JS_METHODJIT static void -SumJitDataSizeCallabck(JSContext *cx, void *data, void *thing, +SumJitDataSizeCallback(JSContext *cx, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize) { size_t *sump = static_cast(data); JS_ASSERT(traceKind == JSTRACE_SCRIPT); JSScript *script = static_cast(thing); - *sump += script->jitDataSize(); + *sump += script->jitDataSize(NULL); } #endif @@ -4086,7 +4086,7 @@ MJitDataStats(JSContext *cx, uintN argc, jsval *vp) { #ifdef JS_METHODJIT size_t n = 0; - IterateCells(cx, NULL, gc::FINALIZE_TYPE_OBJECT, &n, SumJitDataSizeCallabck); + IterateCells(cx, NULL, gc::FINALIZE_SCRIPT, &n, SumJitDataSizeCallback); JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n)); #else JS_SET_RVAL(cx, vp, JSVAL_VOID); @@ -4871,6 +4871,7 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) if (!report) { fprintf(gErrFile, "%s\n", message); + fflush(gErrFile); return; } @@ -4933,6 +4934,7 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) } fputs("^\n", gErrFile); out: + fflush(gErrFile); if (!JSREPORT_IS_WARNING(report->flags)) { if (report->errorNumber == JSMSG_OUT_OF_MEMORY) { gExitCode = EXITCODE_OUT_OF_MEMORY; @@ -5109,7 +5111,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/shell/jsheaptools.cpp b/js/src/shell/jsheaptools.cpp index 812a7289ebd0..cacc165dee79 100644 --- a/js/src/shell/jsheaptools.cpp +++ b/js/src/shell/jsheaptools.cpp @@ -368,12 +368,12 @@ class ReferenceFinder { JSObject *object = static_cast(cell); /* Certain classes of object are for internal use only. */ - JSClass *clasp = JS_GET_CLASS(context, object); - if (clasp == Jsvalify(&js_BlockClass) || - clasp == Jsvalify(&js_CallClass) || - clasp == Jsvalify(&js_WithClass) || - clasp == Jsvalify(&js_DeclEnvClass)) + if (object->isBlock() || + object->isCall() || + object->isWith() || + object->isDeclEnv()) { return JSVAL_VOID; + } /* Internal function objects should also not be revealed. */ if (JS_ObjectIsFunction(context, object) && IsInternalFunctionObject(object)) diff --git a/js/src/tracejit/Writer.h b/js/src/tracejit/Writer.h index fed6d7b5ea2c..6ffb161d7234 100644 --- a/js/src/tracejit/Writer.h +++ b/js/src/tracejit/Writer.h @@ -479,7 +479,7 @@ class Writer } nj::LIns *ldpObjClasp(nj::LIns *obj, nj::LoadQual loadQual) const { - return name(lir->insLoad(nj::LIR_ldp, obj, offsetof(JSObject, clasp), ACCSET_OBJ_CLASP, + return name(lir->insLoad(nj::LIR_ldp, obj, JSObject::offsetOfClassPointer(), ACCSET_OBJ_CLASP, loadQual), "clasp"); } diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 7e07cd59bc71..7e401db8f669 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -151,7 +151,7 @@ class ArgumentsObject : public ::JSObject static const uint32 INITIAL_LENGTH_SLOT = 0; static const uint32 DATA_SLOT = 1; - protected: + public: static const uint32 RESERVED_SLOTS = 2; private: @@ -240,8 +240,6 @@ class ArgumentsObject : public ::JSObject */ class NormalArgumentsObject : public ArgumentsObject { - static js::Class jsClass; - friend bool JSObject::isNormalArguments() const; friend struct EmptyShape; // for EmptyShape::getEmptyArgumentsShape friend ArgumentsObject * @@ -266,8 +264,6 @@ class NormalArgumentsObject : public ArgumentsObject */ class StrictArgumentsObject : public ArgumentsObject { - static js::Class jsClass; - friend bool JSObject::isStrictArguments() const; friend ArgumentsObject * ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee); @@ -275,12 +271,6 @@ class StrictArgumentsObject : public ArgumentsObject } // namespace js -inline bool -JSObject::isNormalArguments() const -{ - return getClass() == &js::NormalArgumentsObject::jsClass; -} - js::NormalArgumentsObject * JSObject::asNormalArguments() { @@ -288,12 +278,6 @@ JSObject::asNormalArguments() return reinterpret_cast(this); } -inline bool -JSObject::isStrictArguments() const -{ - return getClass() == &js::StrictArgumentsObject::jsClass; -} - js::StrictArgumentsObject * JSObject::asStrictArguments() { @@ -301,12 +285,6 @@ JSObject::asStrictArguments() return reinterpret_cast(this); } -inline bool -JSObject::isArguments() const -{ - return isNormalArguments() || isStrictArguments(); -} - js::ArgumentsObject * JSObject::asArguments() { diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index d2b3b1ee9915..23e6504ccbd5 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -225,7 +225,10 @@ BreakpointSite::clearTrap(JSContext *cx, BreakpointSiteMap::Enum *e, trapClosure.setUndefined(); if (enabledCount == 0) { *pc = realOpcode; - recompile(cx, true); /* ignore failure */ + if (!cx->runtime->gcRunning) { + /* If the GC is running then the script is being destroyed. */ + recompile(cx, true); /* ignore failure */ + } destroyIfEmpty(cx->runtime, e); } } @@ -347,9 +350,9 @@ JS_STATIC_ASSERT(uintN(JSSLOT_DEBUGFRAME_OWNER) == uintN(JSSLOT_DEBUGSCRIPT_OWNE Debugger * Debugger::fromChildJSObject(JSObject *obj) { - JS_ASSERT(obj->clasp == &DebuggerFrame_class || - obj->clasp == &DebuggerObject_class || - obj->clasp == &DebuggerScript_class); + JS_ASSERT(obj->getClass() == &DebuggerFrame_class || + obj->getClass() == &DebuggerObject_class || + obj->getClass() == &DebuggerScript_class); JSObject *dbgobj = &obj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER).toObject(); return fromJSObject(dbgobj); } @@ -523,9 +526,9 @@ Debugger::unwrapDebuggeeValue(JSContext *cx, Value *vp) assertSameCompartment(cx, object, *vp); if (vp->isObject()) { JSObject *dobj = &vp->toObject(); - if (dobj->clasp != &DebuggerObject_class) { + if (dobj->getClass() != &DebuggerObject_class) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_EXPECTED_TYPE, - "Debugger", "Debugger.Object", dobj->clasp->name); + "Debugger", "Debugger.Object", dobj->getClass()->name); return false; } @@ -587,7 +590,7 @@ Debugger::newCompletionValue(AutoCompartment &ac, bool ok, Value val, Value *vp) return true; } - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); if (!obj || !wrapDebuggeeValue(cx, &val) || !DefineNativeProperty(cx, obj, key, val, PropertyStub, StrictPropertyStub, @@ -1479,7 +1482,7 @@ Debugger::unwrapDebuggeeArgument(JSContext *cx, const Value &v) */ JSObject *obj = NonNullObject(cx, v); if (obj) { - if (obj->clasp == &DebuggerObject_class) { + if (obj->getClass() == &DebuggerObject_class) { Value rv = v; if (!unwrapDebuggeeValue(cx, &rv)) return NULL; @@ -1977,7 +1980,7 @@ DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const c return NULL; } JSObject *thisobj = &v.toObject(); - if (thisobj->clasp != &DebuggerScript_class) { + if (thisobj->getClass() != &DebuggerScript_class) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, clsname, fnname, thisobj->getClass()->name); return NULL; @@ -2947,7 +2950,7 @@ DebuggerFrameEval(JSContext *cx, uintN argc, Value *vp, EvalBindingsMode mode) /* If evalWithBindings, create the inner scope object. */ if (mode == WithBindings) { /* TODO - Should probably create a With object here. */ - scobj = NewNonFunction(cx, &js_ObjectClass, NULL, scobj); + scobj = NewNonFunction(cx, &ObjectClass, NULL, scobj); if (!scobj) return false; for (size_t i = 0; i < keys.length(); i++) { @@ -3040,7 +3043,7 @@ DebuggerObject_checkThis(JSContext *cx, const CallArgs &args, const char *fnname return NULL; } JSObject *thisobj = &args.thisv().toObject(); - if (thisobj->clasp != &DebuggerObject_class) { + if (thisobj->getClass() != &DebuggerObject_class) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, "Debugger.Object", fnname, thisobj->getClass()->name); return NULL; @@ -3098,7 +3101,7 @@ static JSBool DebuggerObject_getClass(JSContext *cx, uintN argc, Value *vp) { THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj); - const char *s = refobj->clasp->name; + const char *s = refobj->getClass()->name; JSAtom *str = js_Atomize(cx, s, strlen(s)); if (!str) return false; diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 82cff6e9ec5d..2366edf1f045 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -403,7 +403,7 @@ class Debugger { class BreakpointSite { friend class js::Breakpoint; - friend class ::JSCompartment; + friend struct ::JSCompartment; friend class js::Debugger; public: @@ -460,7 +460,7 @@ class BreakpointSite { * JSCompartment::sweepBreakpoints. */ class Breakpoint { - friend class ::JSCompartment; + friend struct ::JSCompartment; friend class js::Debugger; public: diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index c00f07c6e232..69c5cf215c73 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -227,8 +227,8 @@ GlobalObject::createConstructor(JSContext *cx, Native ctor, Class *clasp, JSAtom static JSObject * CreateBlankProto(JSContext *cx, Class *clasp, JSObject &proto, GlobalObject &global) { - JS_ASSERT(clasp != &js_ObjectClass); - JS_ASSERT(clasp != &js_FunctionClass); + JS_ASSERT(clasp != &ObjectClass); + JS_ASSERT(clasp != &FunctionClass); JSObject *blankProto = NewNonFunction(cx, clasp, &proto, &global); if (!blankProto || !blankProto->setSingletonType(cx)) @@ -300,7 +300,7 @@ GlobalObject::getDebuggers() Value debuggers = getReservedSlot(DEBUGGERS); if (debuggers.isUndefined()) return NULL; - JS_ASSERT(debuggers.toObject().clasp == &GlobalDebuggees_class); + JS_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class); return (DebuggerVector *) debuggers.toObject().getPrivate(); } diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 2c79e9b16e06..15619de28d8a 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -46,6 +46,7 @@ #include "Stack.h" +#include "jsscriptinlines.h" #include "ArgumentsObject-inl.h" #include "methodjit/MethodJIT.h" @@ -130,11 +131,6 @@ StackFrame::resetCallFrame(JSScript *script) { JS_ASSERT(script == this->script()); - /* Undo changes to frame made during execution; see also initCallFrame */ - - putActivationObjects(); - markActivationObjectsAsPut(); - if (flags_ & UNDERFLOW_ARGS) SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd()); @@ -358,16 +354,51 @@ StackFrame::callObj() const JS_ASSERT_IF(isNonEvalFunctionFrame() || isStrictEvalFrame(), hasCallObj()); JSObject *pobj = &scopeChain(); - while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) { + while (JS_UNLIKELY(!pobj->isCall())) { JS_ASSERT(IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } return *pobj; } -inline void -StackFrame::putActivationObjects() +inline bool +StackFrame::maintainNestingState() const { + /* + * Whether to invoke the nesting epilogue/prologue to maintain active + * frame counts and check for reentrant outer functions. + */ + return isNonEvalFunctionFrame() && !isGeneratorFrame() && script()->nesting(); +} + +inline bool +StackFrame::functionPrologue(JSContext *cx) +{ + JS_ASSERT(isNonEvalFunctionFrame()); + + JSFunction *fun = this->fun(); + + if (fun->isHeavyweight()) { + if (!CreateFunCallObject(cx, this)) + return false; + } else { + /* Force instantiation of the scope chain, for JIT frames. */ + scopeChain(); + } + + if (script()->nesting()) { + JS_ASSERT(maintainNestingState()); + types::NestingPrologue(cx, this); + } + + return true; +} + +inline void +StackFrame::functionEpilogue(bool objectsOnly) +{ + JS_ASSERT(isNonEvalFunctionFrame()); + if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) { /* NB: there is an ordering dependency here. */ if (hasCallObj()) @@ -375,10 +406,13 @@ StackFrame::putActivationObjects() else if (hasArgsObj()) js_PutArgsObject(this); } + + if (!objectsOnly && maintainNestingState()) + types::NestingEpilogue(this); } inline void -StackFrame::markActivationObjectsAsPut() +StackFrame::markFunctionEpilogueDone(bool activationOnly) { if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) { if (hasArgsObj() && !argsObj().getPrivate()) { @@ -399,6 +433,14 @@ StackFrame::markActivationObjectsAsPut() flags_ &= ~HAS_CALL_OBJ; } } + + /* + * For outer/inner function frames, undo the active frame balancing so that + * when we redo it in the epilogue we get the right final value. The other + * nesting epilogue changes (update active args/vars) are idempotent. + */ + if (!activationOnly && maintainNestingState()) + script()->nesting()->activeFrames++; } /*****************************************************************************/ @@ -547,7 +589,7 @@ ContextStack::popInlineFrame(FrameRegs ®s) JS_ASSERT(®s == &seg_->regs()); StackFrame *fp = regs.fp(); - fp->putActivationObjects(); + fp->functionEpilogue(); Value *newsp = fp->actualArgs() - 1; JS_ASSERT(newsp >= fp->prev()->base()); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 6536a9470c72..32c0d8e542d4 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -152,7 +152,7 @@ StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp, otherfp->flags_ &= ~HAS_CALL_OBJ; if (js_IsNamedLambda(fun())) { JSObject *env = obj.getParent(); - JS_ASSERT(env->getClass() == &js_DeclEnvClass); + JS_ASSERT(env->isDeclEnv()); env->setPrivate(this); } } @@ -644,6 +644,7 @@ ContextStack::popSegment() bool ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *iag) { + LeaveTrace(cx); JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX); uintN nvars = 2 + argc; @@ -778,7 +779,8 @@ ContextStack::popFrame(const FrameGuard &fg) JS_ASSERT(space().firstUnused() == fg.regs_.sp); JS_ASSERT(&fg.regs_ == &seg_->regs()); - fg.regs_.fp()->putActivationObjects(); + if (fg.regs_.fp()->isNonEvalFunctionFrame()) + fg.regs_.fp()->functionEpilogue(); seg_->popRegs(fg.prevRegs_); if (fg.pushedSeg_) @@ -1018,13 +1020,6 @@ StackIter::settleOnNewState() continue; } - /* Censor pushed-but-not-active frames from InvokeSessionGuard. */ - if (containsCall && !calls_->active() && fp_->hasArgs() && - calls_->argv() == fp_->actualArgs()) { - popFrame(); - continue; - } - /* * As an optimization, there is no CallArgsList element pushed for * natives called directly by a script (compiled or interpreted). diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 65c83639570f..0412df61e6c3 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -856,11 +856,28 @@ class StackFrame inline void setScopeChainWithOwnCallObj(JSObject &obj); /* - * NB: putActivationObjects does not mark activation objects as having been - * put (since the frame is about to be popped). + * Prologue for function frames: make a call object for heavyweight + * functions, and maintain type nesting invariants. */ - inline void putActivationObjects(); - inline void markActivationObjectsAsPut(); + inline bool functionPrologue(JSContext *cx); + + /* + * Epilogue for function frames: put any args or call object for the frame + * which may still be live, and maintain type nesting invariants. Only the + * args/call objects are put if activationOnly is set. Note: this does not + * mark the epilogue as having been completed, since the frame is about to + * be popped. Use markFunctionEpilogueDone for this. + */ + inline void functionEpilogue(bool activationOnly = false); + + /* + * Mark any work needed in the function's epilogue as done. Only the args + * and call objects are reset if activationOnly is set. If activationOnly + * is *NOT* set, this call must be followed by a later functionEpilogue. + */ + inline void markFunctionEpilogueDone(bool activationOnly = false); + + inline bool maintainNestingState() const; /* * Variables object 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/vm/StringObject-inl.h b/js/src/vm/StringObject-inl.h index db0f2b1b1434..b8068546ae44 100644 --- a/js/src/vm/StringObject-inl.h +++ b/js/src/vm/StringObject-inl.h @@ -55,7 +55,7 @@ namespace js { inline StringObject * StringObject::create(JSContext *cx, JSString *str) { - JSObject *obj = NewBuiltinClassInstance(cx, &js_StringClass); + JSObject *obj = NewBuiltinClassInstance(cx, &StringClass); if (!obj) return NULL; StringObject *strobj = obj->asString(); @@ -67,8 +67,8 @@ StringObject::create(JSContext *cx, JSString *str) inline StringObject * StringObject::createWithProto(JSContext *cx, JSString *str, JSObject &proto) { - JS_ASSERT(gc::FINALIZE_OBJECT2 == gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(&js_StringClass))); - JSObject *obj = NewObjectWithClassProto(cx, &js_StringClass, &proto, gc::FINALIZE_OBJECT2); + JS_ASSERT(gc::FINALIZE_OBJECT2 == gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(&StringClass))); + JSObject *obj = NewObjectWithClassProto(cx, &StringClass, &proto, gc::FINALIZE_OBJECT2); if (!obj) return NULL; StringObject *strobj = obj->asString(); diff --git a/js/src/xpconnect/shell/xpcshell.cpp b/js/src/xpconnect/shell/xpcshell.cpp index 9025466cced6..959097c485bc 100644 --- a/js/src/xpconnect/shell/xpcshell.cpp +++ b/js/src/xpconnect/shell/xpcshell.cpp @@ -876,8 +876,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/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index a82aad49fce7..13fd0a1065da 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -733,8 +733,8 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) JSContext *cx = mCycleCollectionContext->GetJSContext(); JSGCTraceKind traceKind = js_GetGCThingTraceKind(p); - JSObject *obj; - js::Class *clazz; + JSObject *obj = nsnull; + js::Class *clazz = nsnull; // We do not want to add wrappers to the cycle collector if they're not // explicitly marked as main thread only, because the cycle collector isn't @@ -794,8 +794,6 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) char name[72]; if(traceKind == JSTRACE_OBJECT) { - JSObject *obj = static_cast(p); - js::Class *clazz = obj->getClass(); XPCNativeScriptableInfo* si = nsnull; if(IS_PROTO_CLASS(clazz)) { @@ -808,7 +806,7 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) JS_snprintf(name, sizeof(name), "JS Object (%s - %s)", clazz->name, si->GetJSClass()->name); } - else if(clazz == &js_ScriptClass) + else if(clazz == &js::ScriptClass) { JSScript* script = (JSScript*) xpc_GetJSPrivate(obj); if(script->filename) @@ -822,7 +820,7 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) JS_snprintf(name, sizeof(name), "JS Object (Script)"); } } - else if(clazz == &js_FunctionClass) + else if(clazz == &js::FunctionClass) { JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(obj); JSString* str = JS_GetFunctionId(fun); diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 67b6d7ea5a66..a7f85bb1aa94 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -2119,7 +2119,8 @@ XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s, #define POPULATE(_mode, _t) \ PR_BEGIN_MACRO \ cleanupMode = _mode; \ - if (capacity > PR_UINT32_MAX / sizeof(_t) || \ + size_t max = PR_UINT32_MAX / sizeof(_t); \ + if (capacity > max || \ nsnull == (array = nsMemory::Alloc(capacity * sizeof(_t)))) \ { \ if(pErr) \ diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 8ba371b73e84..b33c4444e618 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 @@ -1345,7 +1346,7 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind, case JSTRACE_OBJECT: { JSObject *obj = static_cast(thing); - curr->objectSlots += JS_ObjectCountDynamicSlots(obj) * sizeof(js::Value); + curr->objectSlots += obj->sizeOfSlotsArray(moz_malloc_usable_size); break; } case JSTRACE_STRING: @@ -1358,15 +1359,22 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind, { js::Shape *shape = static_cast(thing); if(shape->hasTable()) - curr->propertyTables += shape->getTable()->sizeOf(); + curr->propertyTables += + shape->getTable()->sizeOf(moz_malloc_usable_size); break; } case JSTRACE_SCRIPT: { JSScript *script = static_cast(thing); - curr->scriptData += script->dataSize(); +#if JS_SCRIPT_INLINE_DATA_LIMIT + if (script->data != script->inlineData) +#endif + { + size_t usable = moz_malloc_usable_size(script->data); + curr->scriptData += usable ? usable : script->dataSize(); + } #ifdef JS_METHODJIT - curr->mjitData += script->jitDataSize(); + curr->mjitData += script->jitDataSize(moz_malloc_usable_size); #endif break; } @@ -1440,41 +1448,7 @@ MakeMemoryReporterPath(const nsACString &pathPrefix, } // anonymous namespace -class XPConnectGCChunkAllocator - : public js::GCChunkAllocator -{ -public: - XPConnectGCChunkAllocator() {} - -private: - virtual void *doAlloc() { - void *chunk; -#ifdef MOZ_MEMORY - // posix_memalign returns zero on success, nonzero on failure. - if (posix_memalign(&chunk, js::GC_CHUNK_SIZE, js::GC_CHUNK_SIZE)) - chunk = 0; -#else - chunk = js::AllocGCChunk(); -#endif - return chunk; - } - - virtual void doFree(void *chunk) { -#ifdef MOZ_MEMORY - free(chunk); -#else - js::FreeGCChunk(chunk); -#endif - } -}; - -static XPConnectGCChunkAllocator gXPCJSChunkAllocator; - -#ifdef MOZ_MEMORY -#define JS_GC_HEAP_KIND nsIMemoryReporter::KIND_HEAP -#else #define JS_GC_HEAP_KIND nsIMemoryReporter::KIND_NONHEAP -#endif // We have per-compartment GC heap totals, so we can't put the total GC heap // size in the explicit allocations tree. But it's a useful figure, so put it @@ -1627,6 +1601,8 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) for(js::ThreadDataIter i(rt); !i.empty(); i.popFront()) data->stackSize += i.threadData()->stackSpace.committedSize(); + size_t usable = moz_malloc_usable_size(rt); + data->runtimeObjectSize += usable ? usable : sizeof(JSRuntime); data->atomsTableSize += rt->atomState.atoms.tableSize(); } @@ -1673,6 +1649,9 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) return true; } +#define SLOP_BYTES_STRING \ + " The measurement includes slop bytes caused by the heap allocator rounding up request sizes." + static void ReportCompartmentStats(const CompartmentStats &stats, const nsACString &pathPrefix, @@ -1735,7 +1714,7 @@ ReportCompartmentStats(const CompartmentStats &stats, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, - "gc-heap/shapes"), + "gc-heap/type-objects"), JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_TYPE_OBJECT], "Memory on the compartment's garbage-collected JavaScript heap that holds " "type inference information.", @@ -1755,7 +1734,7 @@ ReportCompartmentStats(const CompartmentStats &stats, "which are used to represent object properties. Some objects also " "contain a fixed number of slots which are stored on the compartment's " "JavaScript heap; those slots are not counted here, but in " - "'gc-heap/objects' instead.", + "'gc-heap/objects' instead." SLOP_BYTES_STRING, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, @@ -1773,7 +1752,7 @@ ReportCompartmentStats(const CompartmentStats &stats, nsIMemoryReporter::KIND_HEAP, stats.propertyTables, "Memory allocated for the compartment's property tables. A property " "table is an internal data structure that makes JavaScript property " - "accesses fast.", + "accesses fast." SLOP_BYTES_STRING, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, @@ -1783,20 +1762,11 @@ ReportCompartmentStats(const CompartmentStats &stats, "Arrays attached to prototype JS objects managing shape information.", callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, - "scripts"), - nsIMemoryReporter::KIND_HEAP, - stats.gcHeapKinds[JSTRACE_SCRIPT], - "Memory allocated for the compartment's JSScripts. A JSScript is created " - "for each user-defined function in a script. One is also created for " - "the top-level code in a script.", - callback, closure); - ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "script-data"), nsIMemoryReporter::KIND_HEAP, stats.scriptData, "Memory allocated for JSScript bytecode and various variable-length " - "tables.", + "tables." SLOP_BYTES_STRING, callback, closure); #ifdef JS_METHODJIT @@ -1823,7 +1793,7 @@ ReportCompartmentStats(const CompartmentStats &stats, "mjit-data"), nsIMemoryReporter::KIND_HEAP, stats.mjitData, "Memory used by the method JIT for the compartment's compilation data: " - "JITScripts, native maps, and inline cache structs.", + "JITScripts, native maps, and inline cache structs." SLOP_BYTES_STRING, callback, closure); #endif #ifdef JS_TRACER @@ -1883,11 +1853,11 @@ ReportCompartmentStats(const CompartmentStats &stats, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, - "type-inference-temporary"), + "analysis-temporary"), nsIMemoryReporter::KIND_HEAP, stats.typeInferenceMemory.temporary, - "Memory used during type inference to hold transient analysis " - "information. Cleared on GC.", + "Memory used during type inference and compilation to hold transient " + "analysis information. Cleared on GC.", callback, closure); } @@ -1905,8 +1875,8 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, } ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"), - nsIMemoryReporter::KIND_NONHEAP, sizeof(JSRuntime), - "Memory used by the JSRuntime object.", + nsIMemoryReporter::KIND_NONHEAP, data.runtimeObjectSize, + "Memory used by the JSRuntime object." SLOP_BYTES_STRING, callback, closure); ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"), @@ -2086,8 +2056,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) mJSRuntime->setActivityCallback(ActivityCallback, this); - mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator); - NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap)); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount)); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount)); diff --git a/js/src/xpconnect/src/xpcpublic.h b/js/src/xpconnect/src/xpcpublic.h index b1f037efc7b8..520f2f7a2ab7 100644 --- a/js/src/xpconnect/src/xpcpublic.h +++ b/js/src/xpconnect/src/xpcpublic.h @@ -227,7 +227,8 @@ struct CompartmentStats struct IterateData { IterateData() - : atomsTableSize(0), + : runtimeObjectSize(0), + atomsTableSize(0), stackSize(0), gcHeapChunkTotal(0), gcHeapChunkCleanUnused(0), @@ -238,6 +239,7 @@ struct IterateData compartmentStatsVector(), currCompartmentStats(NULL) { } + PRInt64 runtimeObjectSize; PRInt64 atomsTableSize; PRInt64 stackSize; PRInt64 gcHeapChunkTotal; diff --git a/js/src/xpconnect/src/xpcwrappedjs.cpp b/js/src/xpconnect/src/xpcwrappedjs.cpp index 981f842fcbc5..79666f5df140 100644 --- a/js/src/xpconnect/src/xpcwrappedjs.cpp +++ b/js/src/xpconnect/src/xpcwrappedjs.cpp @@ -281,7 +281,7 @@ nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx, { JSObject2WrappedJSMap* map; JSObject* rootJSObj; - nsXPCWrappedJS* root; + nsXPCWrappedJS* root = nsnull; nsXPCWrappedJS* wrapper = nsnull; nsXPCWrappedJSClass* clazz = nsnull; XPCJSRuntime* rt = ccx.GetRuntime(); diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index a1d5990ca080..dd1c29c8e949 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1903,26 +1903,29 @@ InternalInvalidateThebesLayersInSubtree(nsIFrame* aFrame) foundContainerLayer = PR_TRUE; } - nsIFrame* frame = aFrame; - while (frame) { - nsIFrame::ChildListIterator lists(frame); - for (; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - if (InternalInvalidateThebesLayersInSubtree(childFrames.get())) { - foundContainerLayer = PR_TRUE; - } + nsAutoTArray childListArray; + if (!aFrame->GetFirstPrincipalChild()) { + nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(aFrame); + if (subdocumentFrame) { + // Descend into the subdocument + nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame(); + if (root) { + childListArray.AppendElement(nsIFrame::ChildList( + nsFrameList(root, nsLayoutUtils::GetLastSibling(root)), + nsIFrame::kPrincipalList)); } } - if (frame == aFrame && !frame->GetFirstPrincipalChild()) { - nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(frame); - if (subdocumentFrame) { - // Descend into the subdocument - frame = subdocumentFrame->GetSubdocumentRootFrame(); - continue; + } + + aFrame->GetChildLists(&childListArray); + nsIFrame::ChildListArrayIterator lists(childListArray); + for (; !lists.IsDone(); lists.Next()) { + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + if (InternalInvalidateThebesLayersInSubtree(childFrames.get())) { + foundContainerLayer = PR_TRUE; } } - break; } if (!foundContainerLayer) { diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 2e7355785889..bcf17aad7d39 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -41,10 +41,10 @@ load 265736-1.html load 265736-2.html asserts(2) load 265899-1.html # bug 575011 load 265973-1.html -asserts(8-12) load 265986-1.html # Bug 512405 -asserts(4) load 265999-1.html # bug 575011 +asserts(6-12) load 265986-1.html # Bug 512405 +asserts(2-4) load 265999-1.html # bug 575011 load 266222-1.html -asserts(5-7) load 266360-1.html # bug 575011 / bug 576358 +asserts(3-7) load 266360-1.html # bug 575011 / bug 576358 asserts(4) load 266445-1.html # Bug 575011 load 268157-1.html load 269566-1.html @@ -257,12 +257,12 @@ load 469861-2.xhtml load 471594-1.xhtml load 479114-1.html load 477333-1.xhtml -load 477731-1.html +asserts-if(Android,6) load 477731-1.html # 479360-1.xhtml will assert 6 times due to bug 439258 and then make the test # after the test after it also assert 6 times. -asserts(6) load 479360-1.xhtml # Bug 439258 -load 480686-1.html -asserts(6) load 481806-1.html # Bug 439258 +asserts-if(!Android,6) load 479360-1.xhtml # Bug 439258 +asserts-if(Android,6) load 480686-1.html +asserts-if(!Android,6) load 481806-1.html # Bug 439258 load 483604-1.xhtml load 485501-1.html load 487544-1.html diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 6a61f5e107d6..fe66b836516a 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2484,8 +2484,8 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, aFrame->PresContext(), dummy, bounds, aFactor); } else { - NS_ASSERTION(aFrame->Preserves3DChildren(), - "If we don't have a transform, then we must be at least preserving transforms of our children"); + NS_ASSERTION(aFrame->GetStyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D, + "If we don't have a transform, then we must be at least attempting to preserve the transforms of our children"); } const nsStyleDisplay* parentDisp = nsnull; diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 7f604d8d9ad0..9221d2916ac4 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -2080,6 +2080,7 @@ public: nsDisplayItem(aBuilder, aFrame), mStoredList(aBuilder, aFrame, aList) { MOZ_COUNT_CTOR(nsDisplayTransform); + NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); } nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, @@ -2087,6 +2088,7 @@ public: nsDisplayItem(aBuilder, aFrame), mStoredList(aBuilder, aFrame, aItem) { MOZ_COUNT_CTOR(nsDisplayTransform); + NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); } #ifdef NS_BUILD_REFCNT_LOGGING diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 126dd9967c78..bacda5c31e00 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6988,6 +6988,43 @@ static PRBool CanHandleContextMenuEvent(nsMouseEvent* aMouseEvent, return PR_TRUE; } +static PRBool +IsFullScreenAndRestrictedKeyEvent(nsIContent* aTarget, const nsEvent* aEvent) +{ + NS_ABORT_IF_FALSE(aEvent, "Must have an event to check."); + + // Bail out if the event is not a key event, or the target's document is + // not in DOM full screen mode, or full-screen key input is not restricted. + nsIDocument *doc; + if (!aTarget || + (aEvent->message != NS_KEY_DOWN && + aEvent->message != NS_KEY_UP && + aEvent->message != NS_KEY_PRESS) || + !(doc = aTarget->GetOwnerDoc()) || + !doc->IsFullScreenDoc() || + !nsContentUtils::IsFullScreenKeyInputRestricted()) { + return PR_FALSE; + } + + // Key input is restricted. Determine if the key event has a restricted + // key code. Non-restricted codes are: + // DOM_VK_CANCEL to DOM_VK_CAPS_LOCK, inclusive + // DOM_VK_SPACE to DOM_VK_DELETE, inclusive + // DOM_VK_SEMICOLON to DOM_VK_EQUALS, inclusive + // DOM_VK_MULTIPLY to DOM_VK_META, inclusive + int key = static_cast(aEvent)->keyCode; + if ((key >= NS_VK_CANCEL && key <= NS_VK_CAPS_LOCK) || + (key >= NS_VK_SPACE && key <= NS_VK_DELETE) || + (key >= NS_VK_SEMICOLON && key <= NS_VK_EQUALS) || + (key >= NS_VK_MULTIPLY && key <= NS_VK_META)) { + return PR_FALSE; + } + + // Otherwise, fullscreen is enabled, key input is restricted, and the key + // code is not an allowed key code. + return PR_TRUE; +} + nsresult PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView, nsEventStatus* aStatus) @@ -7029,11 +7066,21 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView, // XXX How about IME events and input events for plugins? if (NS_IS_TRUSTED_EVENT(aEvent)) { switch (aEvent->message) { - case NS_MOUSE_BUTTON_DOWN: - case NS_MOUSE_BUTTON_UP: case NS_KEY_PRESS: case NS_KEY_DOWN: case NS_KEY_UP: + if (IsFullScreenAndRestrictedKeyEvent(mCurrentEventContent, aEvent) && + aEvent->message == NS_KEY_DOWN) { + // We're in DOM full-screen mode, and a key with a restricted key + // code has been pressed. Exit full-screen mode. + NS_DispatchToCurrentThread( + NS_NewRunnableMethod(mCurrentEventContent->GetOwnerDoc(), + &nsIDocument::CancelFullScreen)); + } + // Else not full-screen mode or key code is unrestricted, fall + // through to normal handling. + case NS_MOUSE_BUTTON_DOWN: + case NS_MOUSE_BUTTON_UP: isHandlingUserInput = PR_TRUE; break; case NS_DRAGDROP_DROP: diff --git a/layout/forms/crashtests/crashtests.list b/layout/forms/crashtests/crashtests.list index 398467eec6ba..4633fbe2817e 100644 --- a/layout/forms/crashtests/crashtests.list +++ b/layout/forms/crashtests/crashtests.list @@ -20,11 +20,11 @@ load 367587-1.html load 370703-1.html load 370940-1.html load 373586-1.xhtml -asserts(8-10) load 378413-1.xhtml # bug 424225, bug 402850? +asserts(5-10) load 378413-1.xhtml # bug 424225, bug 402850? load 380116-1.xhtml load 382212-1.xhtml load 382610-1.html -asserts-if(winWidget,1) load 383887-1.html # bug 576434 +asserts-if(winWidget||Android,1) load 383887-1.html # bug 576434 load 386554-1.html load 388374-1.xhtml load 388374-2.html diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 75b32fa62ad1..b82f81092c77 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -71,7 +71,7 @@ load 372376-1.xhtml load 373859-1.html load 373868-1.xhtml load 379217-1.xhtml -asserts(2) load 379217-2.xhtml # Bug 439204 +asserts(1-2) load 379217-2.xhtml # Bug 439204 load 379917-1.xhtml load 380012-1.html load 381152-1.html @@ -124,7 +124,7 @@ load 393956-4.html load 394237-1.html load 394820-1.html load 394818-1.html -load 394818-2.html +asserts-if(Android,1) load 394818-2.html load 395316-1.html load 395450-1.xhtml load 397007-1.html @@ -183,8 +183,8 @@ load 413085-2.html load 413582-1.xhtml load 413582-2.html load 413712-1.xhtml -load 414061-1.html -asserts(6) load 414180-1.xul # Bug 439258 +asserts-if(Android,6) load 414061-1.html +asserts-if(!Android,6) load 414180-1.xul # Bug 439258 load 414719-1.html load 415685-1.html load 416264-1.html @@ -280,7 +280,7 @@ load 477928.html load 478131-1.html load 478170-1.html load 478185-1.html -asserts(1) load 479938-1.html # Bug 575011 +asserts-if(!Android,1) load 479938-1.html # Bug 575011 load 480345-1.html skip-if(Android) load 481921.html load 489462-1.html @@ -368,7 +368,7 @@ load text-overflow-bug666751-2.html asserts(2) load text-overflow-bug670564.xhtml # asserts(2) for bug 436470 load text-overflow-bug671796.xhtml load 667025.html -asserts(14) load 673770.html # bug 569193 and bug 459597 +asserts(14) asserts-if(Android,8) load 673770.html # bug 569193 and bug 459597 load 679933-1.html load 682649-1.html load 683702-1.xhtml diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index c9e39a01f9dc..0e1816e8922b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -786,7 +786,17 @@ nsIFrame::IsTransformed() const PRBool nsIFrame::Preserves3DChildren() const { - return GetStyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D && IsTransformed(); + if (GetStyleDisplay()->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D || !IsTransformed()) + return PR_FALSE; + + // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d. + if (GetType() == nsGkAtoms::scrollFrame) + return PR_FALSE; + + nsRect temp; + return (!ApplyOverflowClipping(nsnull, this, GetStyleDisplay(), &temp) && + !ApplyAbsPosClipping(nsnull, GetStyleDisplay(), this, &temp) && + !nsSVGIntegrationUtils::UsingEffectsForFrame(this)); } PRBool @@ -795,11 +805,7 @@ nsIFrame::Preserves3D() const if (!GetParent() || !GetParent()->Preserves3DChildren() || !IsTransformed()) { return PR_FALSE; } - - nsRect temp; - return (!ApplyOverflowClipping(nsnull, this, GetStyleDisplay(), &temp) && - !ApplyAbsPosClipping(nsnull, GetStyleDisplay(), this, &temp) && - !nsSVGIntegrationUtils::UsingEffectsForFrame(this)); + return PR_TRUE; } nsRect @@ -1477,12 +1483,14 @@ WrapPreserve3DList(nsIFrame *aFrame, nsDisplayListBuilder *aBuilder, nsDisplayLi nsresult rv = NS_OK; nsDisplayList newList; while (nsDisplayItem *item = aList->RemoveBottom()) { - if (item->GetUnderlyingFrame() && item->GetUnderlyingFrame()->GetParent()->Preserves3DChildren()) { + nsIFrame *childFrame = item->GetUnderlyingFrame(); + NS_ASSERTION(childFrame, "All display items to be wrapped must have a frame!"); + if (childFrame->GetParent()->Preserves3DChildren()) { switch (item->GetType()) { case nsDisplayItem::TYPE_TRANSFORM: { // The child transform frame should always preserve 3d. In the cases where preserve-3d is disabled // such as clipping, this would be wrapped in a clip display object, and we wouldn't reach this point. - NS_ASSERTION(item->GetUnderlyingFrame()->Preserves3D(), "Child transform frame must preserve 3d!"); + NS_ASSERTION(childFrame->Preserves3D(), "Child transform frame must preserve 3d!"); break; } case nsDisplayItem::TYPE_WRAP_LIST: { @@ -1496,12 +1504,12 @@ WrapPreserve3DList(nsIFrame *aFrame, nsDisplayListBuilder *aBuilder, nsDisplayLi break; } default: { - item = new (aBuilder) nsDisplayTransform(aBuilder, item->GetUnderlyingFrame(), item); + item = new (aBuilder) nsDisplayTransform(aBuilder, childFrame, item); break; } } } else { - item = new (aBuilder) nsDisplayTransform(aBuilder, item->GetUnderlyingFrame(), item); + item = new (aBuilder) nsDisplayTransform(aBuilder, childFrame, item); } if (NS_FAILED(rv) || !item) diff --git a/layout/reftests/font-matching/font-stretch-1-ref.html b/layout/reftests/font-matching/font-stretch-1-ref.html new file mode 100644 index 000000000000..2fe8f2cf31d5 --- /dev/null +++ b/layout/reftests/font-matching/font-stretch-1-ref.html @@ -0,0 +1,73 @@ + + + + + + + +
ultra-condensed italic bold italic light italic
+
extra-condensed italic bold italic light italic
+
condensed italic bold italic light italic
+
semi-condensed italic bold italic light italic
+ +
normal italic bold italic light italic
+
semi-expanded italic bold italic light italic
+
expanded italic bold italic light italic
+
extra-expanded italic bold italic light italic
+
ultra-expanded italic bold italic light italic
+ + + diff --git a/layout/reftests/font-matching/font-stretch-1.html b/layout/reftests/font-matching/font-stretch-1.html new file mode 100644 index 000000000000..ca86a08ed99b --- /dev/null +++ b/layout/reftests/font-matching/font-stretch-1.html @@ -0,0 +1,79 @@ + + + + + + + +
ultra-condensed italic bold italic light italic
+
extra-condensed italic bold italic light italic
+
condensed italic bold italic light italic
+
semi-condensed italic bold italic light italic
+ +
normal italic bold italic light italic
+
semi-expanded italic bold italic light italic
+
expanded italic bold italic light italic
+
extra-expanded italic bold italic light italic
+
ultra-expanded italic bold italic light italic
+ + + diff --git a/layout/reftests/font-matching/reftest.list b/layout/reftests/font-matching/reftest.list index 73e95de52ff7..1c0ac3103cfa 100644 --- a/layout/reftests/font-matching/reftest.list +++ b/layout/reftests/font-matching/reftest.list @@ -62,3 +62,11 @@ HTTP(..) == weightmapping-478.html weightmapping-478-ref.html HTTP(..) == weightmapping-7.html weightmapping-7-ref.html HTTP(..) == weightmapping-12579.html weightmapping-12579-ref.html +HTTP(..) == stretchmapping-all.html stretchmapping-all-ref.html +HTTP(..) == stretchmapping-reverse.html stretchmapping-reverse-ref.html +HTTP(..) == stretchmapping-35.html stretchmapping-35-ref.html +HTTP(..) == stretchmapping-137.html stretchmapping-137-ref.html + +# test for font-stretch using @font-face +HTTP(..) == font-stretch-1.html font-stretch-1-ref.html + diff --git a/layout/reftests/font-matching/stretchmapping-137-ref.html b/layout/reftests/font-matching/stretchmapping-137-ref.html new file mode 100644 index 000000000000..a42b81c8362a --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-137-ref.html @@ -0,0 +1,348 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with ultra-condensed 100, 400, condensed 200, 800 and expanded 500, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-137.html b/layout/reftests/font-matching/stretchmapping-137.html new file mode 100644 index 000000000000..81f45f66deab --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-137.html @@ -0,0 +1,268 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with ultra-condensed 100, 400, condensed 200, 800 and expanded 500, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-35-ref.html b/layout/reftests/font-matching/stretchmapping-35-ref.html new file mode 100644 index 000000000000..2124a6874250 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-35-ref.html @@ -0,0 +1,320 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with normal width 200, 500 and condensed 100, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-35.html b/layout/reftests/font-matching/stretchmapping-35.html new file mode 100644 index 000000000000..a6c1245b3936 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-35.html @@ -0,0 +1,240 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with normal width 200, 500 and condensed 100, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-all-ref.html b/layout/reftests/font-matching/stretchmapping-all-ref.html new file mode 100644 index 000000000000..267e50b4cae1 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-all-ref.html @@ -0,0 +1,366 @@ + + + +font-stretch matching tests + + + + + + + + + +

font-stretch mapping with different font family sets

+

(only numbers should appear in the body of the table)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
widthfull1-31-41-52-64-64-76-77-98-9
11111244678
22111244678
33311244678
44341244678
55345244678
66345667678
77345667778
88345667798
99345667799
+ + + \ No newline at end of file diff --git a/layout/reftests/font-matching/stretchmapping-all.html b/layout/reftests/font-matching/stretchmapping-all.html new file mode 100644 index 000000000000..904fcd8f3b5f --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-all.html @@ -0,0 +1,505 @@ + + + +font-stretch matching tests + + + + + + + + + +

font-stretch mapping with different font family sets

+

(only numbers should appear in the body of the table)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
widthfull1-31-41-52-64-64-76-77-98-9
1FFFFFFFFFF
2FFFFFFFFFF
3FFFFFFFFFF
4FFFFFFFFFF
5FFFFFFFFFF
6FFFFFFFFFF
7FFFFFFFFFF
8FFFFFFFFFF
9FFFFFFFFFF
+ + + + + \ No newline at end of file diff --git a/layout/reftests/font-matching/stretchmapping-reverse-ref.html b/layout/reftests/font-matching/stretchmapping-reverse-ref.html new file mode 100644 index 000000000000..2a8fde16cbb8 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-reverse-ref.html @@ -0,0 +1,54 @@ + + + +Assure OS/2 usWidthClass isn't referenced + + + + + + + +

The numbers below should appear in ascending sequence:

+ +

+1 +2 +3 +4 +5 +6 +7 +8 +9 +

+ + + \ No newline at end of file diff --git a/layout/reftests/font-matching/stretchmapping-reverse.html b/layout/reftests/font-matching/stretchmapping-reverse.html new file mode 100644 index 000000000000..ef3b0b1ed1c3 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-reverse.html @@ -0,0 +1,102 @@ + + + +Assure OS/2 usWidthClass isn't referenced + + + + + + + +

The numbers below should appear in ascending sequence:

+ +

+F +F +F +F +F +F +F +F +F +

+ + + \ No newline at end of file diff --git a/layout/reftests/fonts/csstest-widths-wd1.ttf b/layout/reftests/fonts/csstest-widths-wd1.ttf new file mode 100644 index 000000000000..efcc7ab33da0 Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd1.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd2.ttf b/layout/reftests/fonts/csstest-widths-wd2.ttf new file mode 100644 index 000000000000..20ecf37570ed Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd2.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd3.ttf b/layout/reftests/fonts/csstest-widths-wd3.ttf new file mode 100644 index 000000000000..28a9e311dee0 Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd3.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd4.ttf b/layout/reftests/fonts/csstest-widths-wd4.ttf new file mode 100644 index 000000000000..e40c65d54e24 Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd4.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd5.ttf b/layout/reftests/fonts/csstest-widths-wd5.ttf new file mode 100644 index 000000000000..7bf4fcb5401a Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd5.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd6.ttf b/layout/reftests/fonts/csstest-widths-wd6.ttf new file mode 100644 index 000000000000..b240d9bc8b62 Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd6.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd7.ttf b/layout/reftests/fonts/csstest-widths-wd7.ttf new file mode 100644 index 000000000000..39afcea20f79 Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd7.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd8.ttf b/layout/reftests/fonts/csstest-widths-wd8.ttf new file mode 100644 index 000000000000..82f500400b2f Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd8.ttf differ diff --git a/layout/reftests/fonts/csstest-widths-wd9.ttf b/layout/reftests/fonts/csstest-widths-wd9.ttf new file mode 100644 index 000000000000..4050a117133d Binary files /dev/null and b/layout/reftests/fonts/csstest-widths-wd9.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSans-Bold.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSans-Bold.ttf new file mode 100644 index 000000000000..99f323d75029 Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSans-Bold.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSans-BoldOblique.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSans-BoldOblique.ttf new file mode 100644 index 000000000000..b78275876753 Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSans-BoldOblique.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSans-ExtraLight.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSans-ExtraLight.ttf new file mode 100644 index 000000000000..013c0ec9def8 Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSans-ExtraLight.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSans-Oblique.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSans-Oblique.ttf new file mode 100644 index 000000000000..8459dc8cabb7 Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSans-Oblique.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSans.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSans.ttf new file mode 100644 index 000000000000..84ca1d750389 Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSans.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-Bold.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-Bold.ttf new file mode 100644 index 000000000000..a12913762a3a Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-Bold.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-BoldOblique.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-BoldOblique.ttf new file mode 100644 index 000000000000..b93b02a1ca4b Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-BoldOblique.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-Oblique.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-Oblique.ttf new file mode 100644 index 000000000000..68a783e969c7 Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed-Oblique.ttf differ diff --git a/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed.ttf b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed.ttf new file mode 100644 index 000000000000..d2a5e439eca9 Binary files /dev/null and b/layout/reftests/fonts/dejavu-sans/DejaVuSansCondensed.ttf differ diff --git a/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg new file mode 100644 index 000000000000..12c62da2c8c7 --- /dev/null +++ b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg new file mode 100644 index 000000000000..82f5a61a0556 --- /dev/null +++ b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/layout/reftests/svg/smil/motion/animateMotion-to-overridden-1.svg b/layout/reftests/svg/smil/motion/animateMotion-to-overridden-1.svg new file mode 100644 index 000000000000..cca246567c35 --- /dev/null +++ b/layout/reftests/svg/smil/motion/animateMotion-to-overridden-1.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/smil/motion/reftest.list b/layout/reftests/svg/smil/motion/reftest.list index 5735f2597dd3..b9ca38d62ad4 100644 --- a/layout/reftests/svg/smil/motion/reftest.list +++ b/layout/reftests/svg/smil/motion/reftest.list @@ -3,9 +3,12 @@ == animateMotion-by-1.svg lime.svg == animateMotion-from-to-1.svg lime.svg +== animateMotion-indefinite-to-1.svg lime.svg +== animateMotion-indefinite-to-2.svg lime.svg == animateMotion-rotate-1a.svg lime.svg == animateMotion-rotate-1b.svg lime.svg == animateMotion-rotate-2.svg lime.svg +== animateMotion-to-overridden-1.svg lime.svg == animateMotion-values-linear-1.svg animateMotion-values-linear-1-ref.svg == animateMotion-values-paced-1a.svg animateMotion-values-paced-1-ref.svg == animateMotion-values-paced-1b.svg animateMotion-values-paced-1-ref.svg diff --git a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg index 006898a6a5e6..1472123ee768 100644 --- a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg +++ b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg @@ -4,7 +4,7 @@ class="reftest-wait" onload="go()"> + + + + + + + + + + + + + diff --git a/toolkit/content/widgets/preferences.xml b/toolkit/content/widgets/preferences.xml index 18310c3883f0..f24a5dbdf20c 100644 --- a/toolkit/content/widgets/preferences.xml +++ b/toolkit/content/widgets/preferences.xml @@ -1253,7 +1253,9 @@ // This "command" event handler tracks changes made to preferences by - // the user in this window. + // the user in this window. + if (event.sourceEvent) + event = event.sourceEvent; this.userChangedValue(event.target); diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in index cee6f8608edd..b1471a17de54 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in @@ -52,7 +52,7 @@ ifdef MOZ_THUMB2 #{ # work around it by telling gcc that the THUMB frame pointer is a # vanilla callee-save register. OS_CXXFLAGS += -fomit-frame-pointer -MOZ_OPTIMIZE_FLAGS := $(filter-out -fno-omit-frame-pointer,$(MOZ_OPTIMIZE_FLAGS)) +MOZ_FRAMEPTR_FLAGS := -fomit-frame-pointer endif #} MODULE = handler diff --git a/toolkit/mozapps/downloads/DownloadLastDir.jsm b/toolkit/mozapps/downloads/DownloadLastDir.jsm index f4011865a93a..55b524b5f15a 100644 --- a/toolkit/mozapps/downloads/DownloadLastDir.jsm +++ b/toolkit/mozapps/downloads/DownloadLastDir.jsm @@ -83,14 +83,12 @@ let observer = { gDownloadLastDirFile = readLastDirPref(); else if (aData == "exit") { gDownloadLastDirFile = null; - gDownloadLastDirStore = new Dict(); } break; case "browser:purge-session-history": gDownloadLastDirFile = null; if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) Services.prefs.clearUserPref(LAST_DIR_PREF); - gDownloadLastDirStore = new Dict(); Services.contentPrefs.removePrefsByName(LAST_DIR_PREF); break; } @@ -112,21 +110,13 @@ function readLastDirPref() { } let gDownloadLastDirFile = readLastDirPref(); -let gDownloadLastDirStore = new Dict(); let gDownloadLastDir = { // compat shims get file() { return this.getFile(); }, set file(val) { this.setFile(null, val); }, getFile: function (aURI) { if (aURI) { - let lastDir; - if (pbSvc && pbSvc.privateBrowsingEnabled) { - let group = Services.contentPrefs.grouper.group(aURI); - lastDir = gDownloadLastDirStore.get(group, null); - } - if (!lastDir) { - lastDir = Services.contentPrefs.getPref(aURI, LAST_DIR_PREF); - } + let lastDir = Services.contentPrefs.getPref(aURI, LAST_DIR_PREF); if (lastDir) { var lastDirFile = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); @@ -144,12 +134,7 @@ let gDownloadLastDir = { }, setFile: function (aURI, aFile) { if (aURI) { - if (pbSvc && pbSvc.privateBrowsingEnabled) { - let group = Services.contentPrefs.grouper.group(aURI); - gDownloadLastDirStore.set(group, aFile.path); - } else { - Services.contentPrefs.setPref(aURI, LAST_DIR_PREF, aFile.path); - } + Services.contentPrefs.setPref(aURI, LAST_DIR_PREF, aFile.path); } if (pbSvc && pbSvc.privateBrowsingEnabled) { if (aFile instanceof Components.interfaces.nsIFile) diff --git a/toolkit/mozapps/downloads/nsHelperAppDlg.js b/toolkit/mozapps/downloads/nsHelperAppDlg.js index dee0af3d06ab..d88b1208d724 100644 --- a/toolkit/mozapps/downloads/nsHelperAppDlg.js +++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js @@ -611,7 +611,8 @@ nsUnknownContentTypeDialog.prototype = { else typeString = mimeInfo.MIMEType; } - if (this.mLauncher.contentLength) { + // When the length is unknown, contentLength would be -1 + if (this.mLauncher.contentLength >= 0) { let [size, unit] = DownloadUtils. convertByteUnits(this.mLauncher.contentLength); type.value = this.dialogElement("strings") diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm index 29f281ef9703..3eff3300ba4a 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -193,20 +193,37 @@ AddonAuthor.prototype = { * * @param aURL * The URL to the full version of the screenshot + * @param aWidth + * The width in pixels of the screenshot + * @param aHeight + * The height in pixels of the screenshot * @param aThumbnailURL * The URL to the thumbnail version of the screenshot + * @param aThumbnailWidth + * The width in pixels of the thumbnail version of the screenshot + * @param aThumbnailHeight + * The height in pixels of the thumbnail version of the screenshot * @param aCaption * The caption of the screenshot */ -function AddonScreenshot(aURL, aThumbnailURL, aCaption) { +function AddonScreenshot(aURL, aWidth, aHeight, aThumbnailURL, + aThumbnailWidth, aThumbnailHeight, aCaption) { this.url = aURL; - this.thumbnailURL = aThumbnailURL; - this.caption = aCaption; + if (aWidth) this.width = aWidth; + if (aHeight) this.height = aHeight; + if (aThumbnailURL) this.thumbnailURL = aThumbnailURL; + if (aThumbnailWidth) this.thumbnailWidth = aThumbnailWidth; + if (aThumbnailHeight) this.thumbnailHeight = aThumbnailHeight; + if (aCaption) this.caption = aCaption; } AddonScreenshot.prototype = { url: null, + width: null, + height: null, thumbnailURL: null, + thumbnailWidth: null, + thumbnailHeight: null, caption: null, // Returns the screenshot URL, defaulting to the empty string diff --git a/toolkit/mozapps/extensions/AddonRepository.jsm b/toolkit/mozapps/extensions/AddonRepository.jsm index 76c9fd1417f7..06b5fbb3b1ac 100644 --- a/toolkit/mozapps/extensions/AddonRepository.jsm +++ b/toolkit/mozapps/extensions/AddonRepository.jsm @@ -67,7 +67,7 @@ const DEFAULT_CACHE_TYPES = "extension,theme,locale"; const KEY_PROFILEDIR = "ProfD"; const FILE_DATABASE = "addons.sqlite"; -const DB_SCHEMA = 1; +const DB_SCHEMA = 2; ["LOG", "WARN", "ERROR"].forEach(function(aName) { this.__defineGetter__(aName, function() { @@ -959,13 +959,25 @@ var AddonRepository = { case "previews": let previewNodes = node.getElementsByTagName("preview"); Array.forEach(previewNodes, function(aPreviewNode) { - let full = self._getDescendantTextContent(aPreviewNode, "full"); + let full = self._getUniqueDescendant(aPreviewNode, "full"); if (full == null) return; - let thumbnail = self._getDescendantTextContent(aPreviewNode, "thumbnail"); + let fullURL = self._getTextContent(full); + let fullWidth = full.getAttribute("width"); + let fullHeight = full.getAttribute("height"); + + let thumbnailURL, thumbnailWidth, thumbnailHeight; + let thumbnail = self._getUniqueDescendant(aPreviewNode, "thumbnail"); + if (thumbnail) { + thumbnailURL = self._getTextContent(thumbnail); + thumbnailWidth = thumbnail.getAttribute("width"); + thumbnailHeight = thumbnail.getAttribute("height"); + } let caption = self._getDescendantTextContent(aPreviewNode, "caption"); - let screenshot = new AddonManagerPrivate.AddonScreenshot(full, thumbnail, caption); + let screenshot = new AddonManagerPrivate.AddonScreenshot(fullURL, fullWidth, fullHeight, + thumbnailURL, thumbnailWidth, + thumbnailHeight, caption); if (addon.screenshots == null) addon.screenshots = []; @@ -1252,7 +1264,8 @@ var AddonDatabase = { getAllDevelopers: "SELECT addon_internal_id, name, url FROM developer " + "ORDER BY addon_internal_id, num", - getAllScreenshots: "SELECT addon_internal_id, url, thumbnailURL, caption " + + getAllScreenshots: "SELECT addon_internal_id, url, width, height, " + + "thumbnailURL, thumbnailWidth, thumbnailHeight, caption " + "FROM screenshot ORDER BY addon_internal_id, num", insertAddon: "INSERT INTO addon VALUES (NULL, :id, :type, :name, :version, " + @@ -1265,8 +1278,14 @@ var AddonDatabase = { insertDeveloper: "INSERT INTO developer VALUES (:addon_internal_id, " + ":num, :name, :url)", - insertScreenshot: "INSERT INTO screenshot VALUES (:addon_internal_id, " + - ":num, :url, :thumbnailURL, :caption)", + // We specify column names here because the columns + // could be out of order due to schema changes. + insertScreenshot: "INSERT INTO screenshot (addon_internal_id, " + + "num, url, width, height, thumbnailURL, " + + "thumbnailWidth, thumbnailHeight, caption) " + + "VALUES (:addon_internal_id, " + + ":num, :url, :width, :height, :thumbnailURL, " + + ":thumbnailWidth, :thumbnailHeight, :caption)", emptyAddon: "DELETE FROM addon" }, @@ -1307,6 +1326,16 @@ var AddonDatabase = { let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true); let dbMissing = !dbfile.exists(); + function tryAgain() { + LOG("Deleting database, and attempting openConnection again"); + this.initialized = false; + if (this.connection.connectionReady) + this.connection.close(); + if (dbfile.exists()) + dbfile.remove(false); + return this.openConnection(true); + } + try { this.connection = Services.storage.openUnsharedDatabase(dbfile); } catch (e) { @@ -1316,16 +1345,38 @@ var AddonDatabase = { this.databaseOk = false; throw e; } - - LOG("Deleting database, and attempting openConnection again"); - dbfile.remove(false); - return this.openConnection(true); + return tryAgain(); } this.connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); - if (dbMissing || this.connection.schemaVersion == 0) + if (dbMissing) this._createSchema(); + switch (this.connection.schemaVersion) { + case 0: + this._createSchema(); + break; + case 1: + try { + this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN width INTEGER"); + this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN height INTEGER"); + this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailWidth INTEGER"); + this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailHeight INTEGER"); + this._createIndices(); + this.connection.schemaVersion = DB_SCHEMA; + } catch (e) { + ERROR("Failed to create database schema", e); + this.logSQLError(this.connection.lastError, this.connection.lastErrorString); + this.connection.rollbackTransaction(); + return tryAgain(); + } + break; + case 2: + break; + default: + return tryAgain(); + } + return this.connection; }, @@ -1733,7 +1784,11 @@ var AddonDatabase = { bp.bindByName("addon_internal_id", aInternalID); bp.bindByName("num", aIndex); bp.bindByName("url", aScreenshot.url); + bp.bindByName("width", aScreenshot.width); + bp.bindByName("height", aScreenshot.height); bp.bindByName("thumbnailURL", aScreenshot.thumbnailURL); + bp.bindByName("thumbnailWidth", aScreenshot.thumbnailWidth); + bp.bindByName("thumbnailHeight", aScreenshot.thumbnailHeight); bp.bindByName("caption", aScreenshot.caption); aParams.addParams(bp); }, @@ -1796,9 +1851,14 @@ var AddonDatabase = { */ _makeScreenshotFromAsyncRow: function AD__makeScreenshotFromAsyncRow(aRow) { let url = aRow.getResultByName("url"); + let width = aRow.getResultByName("width"); + let height = aRow.getResultByName("height"); let thumbnailURL = aRow.getResultByName("thumbnailURL"); - let caption =aRow.getResultByName("caption"); - return new AddonManagerPrivate.AddonScreenshot(url, thumbnailURL, caption); + let thumbnailWidth = aRow.getResultByName("thumbnailWidth"); + let thumbnailHeight = aRow.getResultByName("thumbnailHeight"); + let caption = aRow.getResultByName("caption"); + return new AddonManagerPrivate.AddonScreenshot(url, width, height, thumbnailURL, + thumbnailWidth, thumbnailHeight, caption); }, /** @@ -1849,10 +1909,16 @@ var AddonDatabase = { "addon_internal_id INTEGER, " + "num INTEGER, " + "url TEXT, " + + "width INTEGER, " + + "height INTEGER, " + "thumbnailURL TEXT, " + + "thumbnailWidth INTEGER, " + + "thumbnailHeight INTEGER, " + "caption TEXT, " + "PRIMARY KEY (addon_internal_id, num)"); + this._createIndices(); + this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " + "ON addon BEGIN " + "DELETE FROM developer WHERE addon_internal_id=old.internal_id; " + @@ -1867,5 +1933,15 @@ var AddonDatabase = { this.connection.rollbackTransaction(); throw e; } + }, + + /** + * Synchronously creates the indices in the database. + */ + _createIndices: function AD__createIndices() { + this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS developer_idx " + + "ON developer (addon_internal_id)"); + this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS screenshot_idx " + + "ON screenshot (addon_internal_id)"); } }; diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index 8ba4085de776..879a8b7426a9 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -2546,10 +2546,16 @@ var gDetailView = { var screenshot = document.getElementById("detail-screenshot"); if (aAddon.screenshots && aAddon.screenshots.length > 0) { - if (aAddon.screenshots[0].thumbnailURL) + if (aAddon.screenshots[0].thumbnailURL) { screenshot.src = aAddon.screenshots[0].thumbnailURL; - else + screenshot.width = aAddon.screenshots[0].thumbnailWidth; + screenshot.height = aAddon.screenshots[0].thumbnailHeight; + } else { screenshot.src = aAddon.screenshots[0].url; + screenshot.width = aAddon.screenshots[0].width; + screenshot.height = aAddon.screenshots[0].height; + } + screenshot.setAttribute("loading", "true"); screenshot.hidden = false; } else { screenshot.hidden = true; diff --git a/toolkit/mozapps/extensions/content/extensions.xul b/toolkit/mozapps/extensions/content/extensions.xul index 1baea7319882..c4c8b30d53eb 100644 --- a/toolkit/mozapps/extensions/content/extensions.xul +++ b/toolkit/mozapps/extensions/content/extensions.xul @@ -537,7 +537,9 @@ - + diff --git a/toolkit/mozapps/extensions/test/browser/browser_details.js b/toolkit/mozapps/extensions/test/browser/browser_details.js index 43a409a8417e..6af3962acbff 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_details.js +++ b/toolkit/mozapps/extensions/test/browser/browser_details.js @@ -81,7 +81,11 @@ function test() { contributionAmount: null, updateDate: gDate, permissions: 0, - screenshots: [{url: "http://example.com/screenshot"}], + screenshots: [{ + url: "chrome://branding/content/about.png", + width: 200, + height: 150 + }], }, { id: "addon3@tests.mozilla.org", name: "Test add-on 3", @@ -101,7 +105,11 @@ function test() { AddonManager.PERM_CAN_UPGRADE, screenshots: [{ url: "http://example.com/screenshot", - thumbnailURL: "http://example.com/thumbnail" + width: 400, + height: 300, + thumbnailURL: "chrome://branding/content/icon64.png", + thumbnailWidth: 160, + thumbnailHeight: 120 }], }, { id: "addon4@tests.mozilla.org", @@ -158,6 +166,8 @@ add_test(function() { is(get("detail-icon").src, "chrome://foo/skin/icon64.png", "Icon should be correct"); is_element_hidden(get("detail-creator"), "Creator should be hidden"); is_element_hidden(get("detail-screenshot"), "Screenshot should be hidden"); + is(get("detail-screenshot").width, "", "Screenshot dimensions should not be set"); + is(get("detail-screenshot").height, "", "Screenshot dimensions should not be set"); is(get("detail-desc").textContent, "Short description", "Description should be correct"); is(get("detail-fulldesc").textContent, "Longer description", "Full description should be correct"); @@ -262,7 +272,10 @@ add_test(function() { is_element_hidden(get("detail-creator")._creatorLink, "Creator link should be hidden"); is_element_visible(get("detail-screenshot"), "Screenshot should be visible"); - is(get("detail-screenshot").src, "http://example.com/screenshot", "Should be showing the full sized screenshot"); + is(get("detail-screenshot").src, "chrome://branding/content/about.png", "Should be showing the full sized screenshot"); + is(get("detail-screenshot").width, 200, "Screenshot dimensions should be set"); + is(get("detail-screenshot").height, 150, "Screenshot dimensions should be set"); + is(get("detail-screenshot").hasAttribute("loading"), true, "Screenshot should have loading attribute"); is(get("detail-desc").textContent, "Short description", "Description should be correct"); is_element_hidden(get("detail-fulldesc"), "Full description should be hidden"); @@ -294,7 +307,11 @@ add_test(function() { is_element_hidden(get("detail-error-link"), "Error link should be hidden"); is_element_hidden(get("detail-pending"), "Pending message should be hidden"); - run_next_test(); + get("detail-screenshot").addEventListener("load", function() { + this.removeEventListener("load", arguments.callee, false); + is(this.hasAttribute("loading"), false, "Screenshot should not have loading attribute"); + run_next_test(); + }, false); }); }); @@ -312,7 +329,10 @@ add_test(function() { is(get("detail-creator")._creatorLink.href, "http://www.mozilla.org", "Creator link href should be correct"); is_element_visible(get("detail-screenshot"), "Screenshot should be visible"); - is(get("detail-screenshot").src, "http://example.com/thumbnail", "Should be showing the thumbnail"); + is(get("detail-screenshot").src, "chrome://branding/content/icon64.png", "Should be showing the thumbnail"); + is(get("detail-screenshot").width, 160, "Screenshot dimensions should be set"); + is(get("detail-screenshot").height, 120, "Screenshot dimensions should be set"); + is(get("detail-screenshot").hasAttribute("loading"), true, "Screenshot should have loading attribute"); is_element_hidden(get("detail-contributions"), "Contributions section should be hidden"); @@ -369,7 +389,11 @@ add_test(function() { is_element_hidden(get("detail-error-link"), "Error link should be hidden"); is_element_hidden(get("detail-pending"), "Pending message should be hidden"); - run_next_test(); + get("detail-screenshot").addEventListener("load", function() { + this.removeEventListener("load", arguments.callee, false); + is(this.hasAttribute("loading"), false, "Screenshot should not have loading attribute"); + run_next_test(); + }, false); }); }); diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml index e74a1e74d7c9..7c189fd41923 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml +++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml @@ -32,8 +32,12 @@ - http://localhost:4444/full1-1.png - http://localhost:4444/thumbnail1-1.png + + http://localhost:4444/full1-1.png + + + http://localhost:4444/thumbnail1-1.png + Caption 1 - 1 diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js index d82d9882dda4..16de8d023019 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js +++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js @@ -272,7 +272,11 @@ function do_check_author(aActual, aExpected) { function do_check_screenshot(aActual, aExpected) { do_check_eq(aActual.toString(), aExpected.url); do_check_eq(aActual.url, aExpected.url); + do_check_eq(aActual.width, aExpected.width); + do_check_eq(aActual.height, aExpected.height); do_check_eq(aActual.thumbnailURL, aExpected.thumbnailURL); + do_check_eq(aActual.thumbnailWidth, aExpected.thumbnailWidth); + do_check_eq(aActual.thumbnailHeight, aExpected.thumbnailHeight); do_check_eq(aActual.caption, aExpected.caption); } diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js index 3291ab4a697f..bf77b0c73007 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js @@ -57,9 +57,13 @@ var GET_RESULTS = [{ eula: "Test EULA 1", iconURL: BASE_URL + "/icon1.png", screenshots: [{ - url: BASE_URL + "/full1-1.png", - thumbnailURL: BASE_URL + "/thumbnail1-1.png", - caption: "Caption 1 - 1" + url: BASE_URL + "/full1-1.png", + width: 400, + height: 300, + thumbnailURL: BASE_URL + "/thumbnail1-1.png", + thumbnailWidth: 200, + thumbnailHeight: 150, + caption: "Caption 1 - 1" }, { url: BASE_URL + "/full2-1.png", thumbnailURL: BASE_URL + "/thumbnail2-1.png", diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js new file mode 100644 index 000000000000..9132f15c983b --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js @@ -0,0 +1,104 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + + // Write out a minimal database. + let dbfile = gProfD.clone(); + dbfile.append("addons.sqlite"); + let db = AM_Cc["@mozilla.org/storage/service;1"]. + getService(AM_Ci.mozIStorageService). + openDatabase(dbfile); + + db.createTable("addon", + "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "id TEXT UNIQUE, " + + "type TEXT, " + + "name TEXT, " + + "version TEXT, " + + "creator TEXT, " + + "creatorURL TEXT, " + + "description TEXT, " + + "fullDescription TEXT, " + + "developerComments TEXT, " + + "eula TEXT, " + + "iconURL TEXT, " + + "homepageURL TEXT, " + + "supportURL TEXT, " + + "contributionURL TEXT, " + + "contributionAmount TEXT, " + + "averageRating INTEGER, " + + "reviewCount INTEGER, " + + "reviewURL TEXT, " + + "totalDownloads INTEGER, " + + "weeklyDownloads INTEGER, " + + "dailyUsers INTEGER, " + + "sourceURI TEXT, " + + "repositoryStatus INTEGER, " + + "size INTEGER, " + + "updateDate INTEGER"); + + db.createTable("developer", + "addon_internal_id INTEGER, " + + "num INTEGER, " + + "name TEXT, " + + "url TEXT, " + + "PRIMARY KEY (addon_internal_id, num)"); + + db.createTable("screenshot", + "addon_internal_id INTEGER, " + + "num INTEGER, " + + "url TEXT, " + + "thumbnailURL TEXT, " + + "caption TEXT, " + + "PRIMARY KEY (addon_internal_id, num)"); + + let stmt = db.createStatement("INSERT INTO addon (id) VALUES (:id)"); + stmt.params.id = "test1@tests.mozilla.org"; + stmt.execute(); + stmt.finalize(); + + stmt = db.createStatement("INSERT INTO screenshot VALUES " + + "(:addon_internal_id, :num, :url, :thumbnailURL, :caption)"); + + stmt.params.addon_internal_id = 1; + stmt.params.num = 0; + stmt.params.url = "http://localhost:4444/full1-1.png"; + stmt.params.thumbnailURL = "http://localhost:4444/thumbnail1-1.png"; + stmt.params.caption = "Caption 1 - 1"; + stmt.execute(); + stmt.finalize(); + + db.schemaVersion = 1; + db.close(); + + Services.obs.addObserver({ + observe: function () { + Services.obs.removeObserver(this, "addon-repository-shutdown"); + // Check the DB schema has changed once AddonRepository has freed it. + db = AM_Cc["@mozilla.org/storage/service;1"]. + getService(AM_Ci.mozIStorageService). + openDatabase(dbfile); + do_check_eq(db.schemaVersion, 2); + do_check_true(db.indexExists("developer_idx")); + do_check_true(db.indexExists("screenshot_idx")); + db.close(); + do_test_finished(); + } + }, "addon-repository-shutdown", null); + + Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true); + AddonRepository.getCachedAddonByID("test1@tests.mozilla.org", function (aAddon) { + do_check_neq(aAddon, null); + do_check_eq(aAddon.screenshots.length, 1); + do_check_true(aAddon.screenshots[0].width === null); + do_check_true(aAddon.screenshots[0].height === null); + do_check_true(aAddon.screenshots[0].thumbnailWidth === null); + do_check_true(aAddon.screenshots[0].thumbnailHeight === null); + AddonRepository.shutdown(); + }); +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini index 0f12380abf5a..335f5628bc9c 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini @@ -157,6 +157,7 @@ skip-if = os == "android" [test_migrate1.js] [test_migrate2.js] [test_migrate3.js] +[test_migrateAddonRepository.js] [test_permissions.js] [test_plugins.js] # Bug 676992: test consistently fails on Android diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index 5351343b758d..8c29f6ae4d1c 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -397,8 +397,18 @@ endif ifdef MOZ_OMNIJAR +# Set MOZ_CAN_RUN_PROGRAMS for trivial cases. +ifndef MOZ_CAN_RUN_PROGRAMS +ifdef UNIVERSAL_BINARY +MOZ_CAN_RUN_PROGRAMS=1 +endif +ifndef CROSS_COMPILE +MOZ_CAN_RUN_PROGRAMS=1 +endif +endif # MOZ_CAN_RUN_PROGRAMS + ifdef GENERATE_CACHE -ifneq (1_,$(if $(CROSS_COMPILE),1,0)_$(UNIVERSAL_BINARY)) +ifdef MOZ_CAN_RUN_PROGRAMS ifdef RUN_TEST_PROGRAM _ABS_RUN_TEST_PROGRAM = $(call core_abspath,$(RUN_TEST_PROGRAM)) endif @@ -414,7 +424,7 @@ PRECOMPILE_GRE=$$PWD endif GENERATE_CACHE = \ - $(_ABS_RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) -g "$(PRECOMPILE_GRE)" -a "$$PWD" -f $(MOZILLA_DIR)/toolkit/mozapps/installer/precompile_cache.js -e "populate_startupcache('$(PRECOMPILE_DIR)', 'omni.jar', 'startupCache.zip');" && \ + $(_ABS_RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) -g "$(PRECOMPILE_GRE)" -a "$$PWD" -f $(call core_abspath,$(MOZILLA_DIR)/toolkit/mozapps/installer/precompile_cache.js) -e "populate_startupcache('$(PRECOMPILE_DIR)', 'omni.jar', 'startupCache.zip');" && \ rm -rf jsloader && \ $(UNZIP) startupCache.zip && \ rm startupCache.zip && \ @@ -477,7 +487,7 @@ endif # dummy macro if we don't have PSM built SIGN_NSS = -ifneq (1_,$(if $(CROSS_COMPILE),1,0)_$(UNIVERSAL_BINARY)) +ifdef MOZ_CAN_RUN_PROGRAMS ifdef MOZ_PSM SIGN_NSS = @echo signing nss libraries; diff --git a/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css b/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css index f66cb4dde476..0a13c09a190a 100644 --- a/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css @@ -661,6 +661,19 @@ max-height: 300px; } +#detail-screenshot[loading] { + background-image: url("chrome://global/skin/icons/loading_16.png"); + background-position: 50% 50%; + background-repeat: no-repeat; + border: 1px threedshadow solid; + border-radius: 5px; + -moz-box-sizing: border-box; +} + +#detail-screenshot[loading="error"] { + background-image: url("chrome://global/skin/media/error.png"); +} + #detail-desc-container { margin-bottom: 2em; } diff --git a/toolkit/themes/pinstripe/mozapps/extensions/extensions.css b/toolkit/themes/pinstripe/mozapps/extensions/extensions.css index 4dfd3cedf1e3..7cbfe7bb92ea 100644 --- a/toolkit/themes/pinstripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/pinstripe/mozapps/extensions/extensions.css @@ -801,6 +801,19 @@ max-height: 300px; } +#detail-screenshot[loading] { + background-image: url("chrome://global/skin/icons/loading_16.png"), + -moz-linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); + background-position: 50% 50%; + background-repeat: no-repeat; + border-radius: 3px; +} + +#detail-screenshot[loading="error"] { + background-image: url("chrome://global/skin/media/error.png"), + -moz-linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); +} + #detail-desc-container { margin-bottom: 2em; } diff --git a/toolkit/themes/winstripe/mozapps/extensions/extensions.css b/toolkit/themes/winstripe/mozapps/extensions/extensions.css index 941918b6b238..b68c742a8a06 100644 --- a/toolkit/themes/winstripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/winstripe/mozapps/extensions/extensions.css @@ -779,6 +779,19 @@ max-height: 300px; } +#detail-screenshot[loading] { + background-image: url("chrome://global/skin/icons/loading_16.png"), + -moz-linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); + background-position: 50% 50%; + background-repeat: no-repeat; + border-radius: 3px; +} + +#detail-screenshot[loading="error"] { + background-image: url("chrome://global/skin/media/error.png"), + -moz-linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); +} + #detail-desc-container { margin-bottom: 2em; } diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index d0ebbbf071c6..4dd0249a52a2 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -51,11 +51,11 @@ #if defined(MOZ_WIDGET_QT) #include #include -#include #include #include #ifdef MOZ_ENABLE_MEEGOTOUCH #include +#include #endif // MOZ_ENABLE_MEEGOTOUCH #endif // MOZ_WIDGET_QT diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h index 200fcde9349d..ca312c2a6a31 100644 --- a/widget/public/nsGUIEvent.h +++ b/widget/public/nsGUIEvent.h @@ -541,6 +541,10 @@ class nsHashKey; #define NS_SHOW_EVENT 5000 +// Fullscreen DOM API +#define NS_FULL_SCREEN_START 5100 +#define NS_FULLSCREENCHANGE (NS_FULL_SCREEN_START) + /** * Return status for event processors, nsEventStatus, is defined in * nsEvent.h. diff --git a/widget/public/nsIJumpListBuilder.idl b/widget/public/nsIJumpListBuilder.idl index 2fa5ca4489a5..8c80ff3bc6dc 100644 --- a/widget/public/nsIJumpListBuilder.idl +++ b/widget/public/nsIJumpListBuilder.idl @@ -49,7 +49,7 @@ interface nsIJumpListBuilder : nsISupports * * Jump lists are built and then applied. Modifying an applied jump list is not * permitted. Callers should begin the creation of a new jump list using - * initListBuild, add sub lists using addListBuild, then commit the jump list + * initListBuild, add sub lists using addListToBuild, then commit the jump list * using commitListBuild. Lists are built in real-time during the sequence of * build calls, make sure to check for errors on each individual step. * @@ -66,7 +66,7 @@ interface nsIJumpListBuilder : nsISupports * users have removed, applications are encoraged to track removed items * internally. * - * Each list is made up of an array of nsIWinJumpListItems representing items + * Each list is made up of an array of nsIJumpListItem representing items * such as shortcuts, links, and separators. See nsIJumpListItem for information * on adding additional jump list types. */ diff --git a/widget/public/nsIJumpListItem.idl b/widget/public/nsIJumpListItem.idl index 13b87ce4b82b..254d8e14053c 100644 --- a/widget/public/nsIJumpListItem.idl +++ b/widget/public/nsIJumpListItem.idl @@ -129,23 +129,42 @@ interface nsIJumpListLink : nsIJumpListItem * A generic application shortcut with command line support. */ -[scriptable, uuid(9664389E-11D6-4bea-B68C-D70232162068)] +[scriptable, uuid(CBE3A37C-BCE1-4fec-80A5-5FFBC7F33EEA)] interface nsIJumpListShortcut : nsIJumpListItem { /** * Set or get the handler app for this shortcut item. + * + * The handler app may also be used along with iconIndex to generate an icon + * for the jump list item. * * @throw NS_ERROR_FILE_NOT_FOUND if the handler app can * not be found on the system. + * + * @see faviconPageUri */ attribute nsILocalHandlerApp app; /** * Set or get the icon displayed with the jump list item. * - * Indicates the resource index of the icon contained - * within the the handler executable. + * Indicates the resource index of the icon contained within the handler + * executable which may be used as the jump list icon. + * + * @see faviconPageUri */ attribute long iconIndex; + + /** + * Set or get the URI of a page whose favicon may be used as the icon. + * + * When a jump list build occurs, the favicon to be used for the item is + * obtained using the following steps: + * - First, attempt to use the asynchronously retrieved and scaled favicon + * associated with the faviconPageUri. + * - If faviconPageUri is null, or if retrieving the favicon fails, fall + * back to using the handler executable and iconIndex. + */ + attribute nsIURI faviconPageUri; }; diff --git a/widget/src/android/GfxInfo.cpp b/widget/src/android/GfxInfo.cpp index ec82e3da7531..b6f2d6c270dc 100644 --- a/widget/src/android/GfxInfo.cpp +++ b/widget/src/android/GfxInfo.cpp @@ -271,13 +271,16 @@ GfxInfo::GetFeatureStatusImpl(PRInt32 aFeature, PRInt32 *aStatus, nsAString & aS } if (aFeature == FEATURE_OPENGL_LAYERS) { - nsAutoString str; - /* Whitelist Galaxy S phones */ - if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) { - if (str != NS_LITERAL_STRING("smdkc110")) { - status = FEATURE_BLOCKED_DEVICE; - } - } + /* XXX: Use this code when we're ready to whitelist devices. */ + // nsAutoString str; + // /* Whitelist Galaxy S phones */ + // if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) { + // if (str != NS_LITERAL_STRING("smdkc110")) { + // status = FEATURE_BLOCKED_DEVICE; + // } + // } + + status = FEATURE_BLOCKED_DEVICE; } *aStatus = status; diff --git a/widget/src/windows/JumpListBuilder.cpp b/widget/src/windows/JumpListBuilder.cpp index 563caf29d397..5165cab0abb6 100644 --- a/widget/src/windows/JumpListBuilder.cpp +++ b/widget/src/windows/JumpListBuilder.cpp @@ -21,6 +21,7 @@ * * Contributor(s): * Jim Mathies + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -49,6 +50,14 @@ #include "nsIMutableArray.h" #include "nsWidgetsCID.h" #include "WinTaskbar.h" +#include "nsDirectoryServiceUtils.h" +#include "nsISimpleEnumerator.h" +#include "mozilla/Preferences.h" +#include "imgIContainer.h" +#include "imgITools.h" +#include "nsStringStream.h" +#include "nsNetUtil.h" +#include "nsThreadUtils.h" namespace mozilla { namespace widget { @@ -61,8 +70,13 @@ static NS_DEFINE_CID(kJumpListShortcutCID, NS_WIN_JUMPLISTSHORTCUT_CID); extern const wchar_t *gMozillaJumpListIDGeneric; PRPackedBool JumpListBuilder::sBuildingList = PR_FALSE; +const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled"; -NS_IMPL_ISUPPORTS1(JumpListBuilder, nsIJumpListBuilder) +NS_IMPL_ISUPPORTS2(JumpListBuilder, nsIJumpListBuilder, nsIObserver) +NS_IMPL_ISUPPORTS1(AsyncFaviconDataReady, nsIFaviconDataCallback) +NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncWriteIconToDisk, nsIRunnable) +NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteIconFromDisk, nsIRunnable) +NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteAllFaviconsFromDisk, nsIRunnable) JumpListBuilder::JumpListBuilder() : mMaxItems(0), @@ -72,10 +86,15 @@ JumpListBuilder::JumpListBuilder() : CoCreateInstance(CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, IID_ICustomDestinationList, getter_AddRefs(mJumpListMgr)); + + NS_NewThread(getter_AddRefs(mIOThread)); + Preferences::AddStrongObserver(this, kPrefTaskbarEnabled); } JumpListBuilder::~JumpListBuilder() { + mIOThread->Shutdown(); + Preferences::RemoveObserver(this, kPrefTaskbarEnabled); mJumpListMgr = nsnull; ::CoUninitialize(); } @@ -145,6 +164,9 @@ NS_IMETHODIMP JumpListBuilder::InitListBuild(nsIMutableArray *removedItems, PRBo TransferIObjectArrayToIMutableArray(objArray, removedItems); objArray->Release(); } + + RemoveIconCacheForItems(removedItems); + sBuildingList = PR_TRUE; *_retval = PR_TRUE; return NS_OK; @@ -153,6 +175,103 @@ NS_IMETHODIMP JumpListBuilder::InitListBuild(nsIMutableArray *removedItems, PRBo return NS_OK; } +// Ensures that we don't have old ICO files that aren't in our jump lists +// anymore left over in the cache. +nsresult JumpListBuilder::RemoveIconCacheForItems(nsIMutableArray *items) +{ + NS_ENSURE_ARG_POINTER(items); + + nsresult rv; + PRUint32 length; + items->GetLength(&length); + for (PRUint32 i = 0; i < length; ++i) { + + //Obtain an IJumpListItem and get the type + nsCOMPtr item = do_QueryElementAt(items, i); + if (!item) { + continue; + } + PRInt16 type; + if (NS_FAILED(item->GetType(&type))) { + continue; + } + + // If the item is a shortcut, remove its associated icon if any + if (type == nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT) { + nsCOMPtr shortcut = do_QueryInterface(item); + if (shortcut) { + nsCOMPtr uri; + rv = shortcut->GetFaviconPageUri(getter_AddRefs(uri)); + if (NS_SUCCEEDED(rv) && uri) { + + // The local file path is stored inside the nsIURI + // Get the nsIURI spec which stores the local path for the icon to remove + nsCAutoString spec; + nsresult rv = uri->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr event + = new AsyncDeleteIconFromDisk(NS_ConvertUTF8toUTF16(spec)); + mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); + + // The shortcut was generated from an IShellLinkW so IShellLinkW can + // only tell us what the original icon is and not the URI. + // So this field was used only temporarily as the actual icon file + // path. It should be cleared. + shortcut->SetFaviconPageUri(nsnull); + } + } + } + + } // end for + + return NS_OK; +} + +// Ensures that we have no old ICO files left in the jump list cache +nsresult JumpListBuilder::RemoveIconCacheForAllItems() +{ + // Construct the path of our jump list cache + nsCOMPtr jumpListCacheDir; + nsresult rv = NS_GetSpecialDirectory("ProfLDS", + getter_AddRefs(jumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + rv = jumpListCacheDir->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr entries; + rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + // Loop through each directory entry and remove all ICO files found + do { + PRBool hasMore = PR_FALSE; + if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore) + break; + + nsCOMPtr supp; + if (NS_FAILED(entries->GetNext(getter_AddRefs(supp)))) + break; + + nsCOMPtr currFile(do_QueryInterface(supp)); + nsAutoString path; + if (NS_FAILED(currFile->GetPath(path))) + continue; + + PRInt32 len = path.Length(); + if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) { + // Check if the cached ICO file exists + PRBool exists; + if (NS_FAILED(currFile->Exists(&exists)) || !exists) + continue; + + // We found an ICO file that exists, so we should remove it + currFile->Remove(PR_FALSE); + } + } while(true); + + return NS_OK; +} + /* boolean addListToBuild(in short aCatType, [optional] in nsIArray items, [optional] in AString catName); */ NS_IMETHODIMP JumpListBuilder::AddListToBuild(PRInt16 aCatType, nsIArray *items, const nsAString &catName, PRBool *_retval) { @@ -193,7 +312,7 @@ NS_IMETHODIMP JumpListBuilder::AddListToBuild(PRInt16 aCatType, nsIArray *items, } // These should all be ShellLinks nsRefPtr link; - rv = JumpListShortcut::GetShellLink(item, link); + rv = JumpListShortcut::GetShellLink(item, link, mIOThread); if (NS_FAILED(rv)) return rv; collection->AddObject(link); @@ -271,7 +390,7 @@ NS_IMETHODIMP JumpListBuilder::AddListToBuild(PRInt16 aCatType, nsIArray *items, case nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT: { nsRefPtr shellItem; - rv = JumpListShortcut::GetShellLink(item, shellItem); + rv = JumpListShortcut::GetShellLink(item, shellItem, mIOThread); if (NS_FAILED(rv)) return rv; collection->AddObject(shellItem); @@ -411,6 +530,241 @@ nsresult JumpListBuilder::TransferIObjectArrayToIMutableArray(IObjectArray *objA return NS_OK; } +NS_IMETHODIMP JumpListBuilder::Observe(nsISupports* aSubject, + const char* aTopic, + const PRUnichar* aData) +{ + if (nsDependentString(aData).EqualsASCII(kPrefTaskbarEnabled)) { + PRBool enabled = Preferences::GetBool(kPrefTaskbarEnabled, true); + if (!enabled) { + + nsCOMPtr event = new AsyncDeleteAllFaviconsFromDisk(); + mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); + } + } + return NS_OK; +} + + +AsyncFaviconDataReady::AsyncFaviconDataReady(nsIURI *aNewURI, + nsCOMPtr &aIOThread) + : mNewURI(aNewURI), + mIOThread(aIOThread) +{ +} + +NS_IMETHODIMP +AsyncFaviconDataReady::OnFaviconDataAvailable(nsIURI *aFaviconURI, + PRUint32 aDataLen, + const PRUint8 *aData, + const nsACString &aMimeType) +{ + if (!aDataLen || !aData) { + return NS_OK; + } + + nsCOMPtr icoFile; + nsresult rv = JumpListShortcut::GetOutputIconPath(mNewURI, icoFile); + NS_ENSURE_SUCCESS(rv, rv); + nsAutoString path; + rv = icoFile->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + // Allocate a new buffer that we own and can use out of line in + // another thread. Copy the favicon raw data into it. + const fallible_t fallible = fallible_t(); + PRUint8 *data = new (fallible) PRUint8[aDataLen]; + if (!data) { + return NS_ERROR_OUT_OF_MEMORY; + } + memcpy(data, aData, aDataLen); + + //AsyncWriteIconToDisk takes ownership of the heap allocated buffer. + nsCOMPtr event = new AsyncWriteIconToDisk(path, aMimeType, + data, + aDataLen); + mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); + + return NS_OK; +} + +// Warning: AsyncWriteIconToDisk assumes ownership of the aData buffer passed in +AsyncWriteIconToDisk::AsyncWriteIconToDisk(const nsAString &aIconPath, + const nsACString &aMimeTypeOfInputData, + PRUint8 *aBuffer, + PRUint32 aBufferLength) + : mIconPath(aIconPath), + mMimeTypeOfInputData(aMimeTypeOfInputData), + mBuffer(aBuffer), + mBufferLength(aBufferLength) + +{ +} + +NS_IMETHODIMP AsyncWriteIconToDisk::Run() +{ + NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread."); + + // Convert the obtained favicon data to an input stream + nsCOMPtr stream; + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), + reinterpret_cast(mBuffer.get()), + mBufferLength, + NS_ASSIGNMENT_DEPEND); + NS_ENSURE_SUCCESS(rv, rv); + + // Decode the image from the format it was returned to us in (probably PNG) + nsCOMPtr container; + nsCOMPtr imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); + rv = imgtool->DecodeImageData(stream, mMimeTypeOfInputData, + getter_AddRefs(container)); + NS_ENSURE_SUCCESS(rv, rv); + + // Get the recommended icon width and height, or if failure to obtain + // these settings, fall back to 16x16 ICOs. These values can be different + // if the user has a different DPI setting other than 100%. + // Windows would scale the 16x16 icon themselves, but it's better + // we let our ICO encoder do it. + PRInt32 systemIconWidth = GetSystemMetrics(SM_CXSMICON); + PRInt32 systemIconHeight = GetSystemMetrics(SM_CYSMICON); + if (systemIconWidth == 0 || systemIconHeight == 0) { + systemIconWidth = 16; + systemIconHeight = 16; + } + // Scale the image to the needed size and in ICO format + mMimeTypeOfInputData.AssignLiteral("image/vnd.microsoft.icon"); + nsCOMPtr iconStream; + rv = imgtool->EncodeScaledImage(container, mMimeTypeOfInputData, + systemIconWidth, + systemIconHeight, + getter_AddRefs(iconStream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr icoFile + = do_CreateInstance("@mozilla.org/file/local;1"); + NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE); + rv = icoFile->InitWithPath(mIconPath); + + // Setup the output stream for the ICO file on disk + nsCOMPtr outputStream; + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), icoFile); + NS_ENSURE_SUCCESS(rv, rv); + + // Obtain the ICO buffer size from the re-encoded ICO stream + PRUint32 bufSize; + rv = iconStream->Available(&bufSize); + NS_ENSURE_SUCCESS(rv, rv); + + // Setup a buffered output stream from the stream object + // so that we can simply use WriteFrom with the stream object + nsCOMPtr bufferedOutputStream; + rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), + outputStream, bufSize); + NS_ENSURE_SUCCESS(rv, rv); + + // Write out the icon stream to disk and make sure we wrote everything + PRUint32 wrote; + rv = bufferedOutputStream->WriteFrom(iconStream, bufSize, &wrote); + NS_ASSERTION(bufSize == wrote, "Icon wrote size should be equal to requested write size"); + + // Cleanup + bufferedOutputStream->Close(); + outputStream->Close(); + return rv; +} + +AsyncWriteIconToDisk::~AsyncWriteIconToDisk() +{ +} + +AsyncDeleteIconFromDisk::AsyncDeleteIconFromDisk(const nsAString &aIconPath) + : mIconPath(aIconPath) +{ +} + +NS_IMETHODIMP AsyncDeleteIconFromDisk::Run() +{ + // Construct the parent path of the passed in path + nsCOMPtr icoFile = do_CreateInstance("@mozilla.org/file/local;1"); + NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE); + nsresult rv = icoFile->InitWithPath(mIconPath); + NS_ENSURE_SUCCESS(rv, rv); + + // Check if the cached ICO file exists + PRBool exists; + rv = icoFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + // Check that we aren't deleting some arbitrary file that is not an icon + if (StringTail(mIconPath, 4).LowerCaseEqualsASCII(".ico")) { + // Check if the cached ICO file exists + PRBool exists; + if (NS_FAILED(icoFile->Exists(&exists)) || !exists) + return NS_ERROR_FAILURE; + + // We found an ICO file that exists, so we should remove it + icoFile->Remove(PR_FALSE); + } + + return NS_OK; +} + +AsyncDeleteIconFromDisk::~AsyncDeleteIconFromDisk() +{ +} + +AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk() +{ +} + +NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run() +{ + // Construct the path of our jump list cache + nsCOMPtr jumpListCacheDir; + nsresult rv = NS_GetSpecialDirectory("ProfLDS", + getter_AddRefs(jumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + rv = jumpListCacheDir->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr entries; + rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + // Loop through each directory entry and remove all ICO files found + do { + PRBool hasMore = PR_FALSE; + if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore) + break; + + nsCOMPtr supp; + if (NS_FAILED(entries->GetNext(getter_AddRefs(supp)))) + break; + + nsCOMPtr currFile(do_QueryInterface(supp)); + nsAutoString path; + if (NS_FAILED(currFile->GetPath(path))) + continue; + + PRInt32 len = path.Length(); + if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) { + // Check if the cached ICO file exists + PRBool exists; + if (NS_FAILED(currFile->Exists(&exists)) || !exists) + continue; + + // We found an ICO file that exists, so we should remove it + currFile->Remove(PR_FALSE); + } + } while(true); + + return NS_OK; +} + +AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk() +{ +} + + } // namespace widget } // namespace mozilla diff --git a/widget/src/windows/JumpListBuilder.h b/widget/src/windows/JumpListBuilder.h index 587d480f54e7..48458965fc59 100644 --- a/widget/src/windows/JumpListBuilder.h +++ b/widget/src/windows/JumpListBuilder.h @@ -21,6 +21,7 @@ * * Contributor(s): * Jim Mathies + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -54,15 +55,20 @@ #include "nsIJumpListBuilder.h" #include "nsIJumpListItem.h" #include "JumpListItem.h" +#include "nsIObserver.h" +#include "nsIFaviconService.h" +#include "nsThreadUtils.h" namespace mozilla { namespace widget { -class JumpListBuilder : public nsIJumpListBuilder +class JumpListBuilder : public nsIJumpListBuilder, + public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIJUMPLISTBUILDER + NS_DECL_NSIOBSERVER JumpListBuilder(); virtual ~JumpListBuilder(); @@ -74,13 +80,75 @@ private: nsRefPtr mJumpListMgr; PRUint32 mMaxItems; PRBool mHasCommit; + nsCOMPtr mIOThread; PRBool IsSeparator(nsCOMPtr& item); nsresult TransferIObjectArrayToIMutableArray(IObjectArray *objArray, nsIMutableArray *removedItems); + nsresult RemoveIconCacheForItems(nsIMutableArray *removedItems); + nsresult RemoveIconCacheForAllItems(); friend class WinTaskbar; }; + +class AsyncFaviconDataReady : public nsIFaviconDataCallback +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIFAVICONDATACALLBACK + + AsyncFaviconDataReady(nsIURI *aNewURI, nsCOMPtr &aIOThread); +private: + nsCOMPtr mNewURI; + nsCOMPtr mIOThread; +}; + +/** + * Asynchronously tries add the list to the build + */ +class AsyncWriteIconToDisk : public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + // Warning: AsyncWriteIconToDisk assumes ownership of the aData buffer passed in + AsyncWriteIconToDisk(const nsAString &aIconPath, + const nsACString &aMimeTypeOfInputData, + PRUint8 *aData, + PRUint32 aDataLen); + virtual ~AsyncWriteIconToDisk(); + +private: + nsAutoString mIconPath; + nsCAutoString mMimeTypeOfInputData; + nsAutoArrayPtr mBuffer; + PRUint32 mBufferLength; +}; + +class AsyncDeleteIconFromDisk : public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + AsyncDeleteIconFromDisk(const nsAString &aIconPath); + virtual ~AsyncDeleteIconFromDisk(); + +private: + nsAutoString mIconPath; +}; + +class AsyncDeleteAllFaviconsFromDisk : public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + AsyncDeleteAllFaviconsFromDisk(); + virtual ~AsyncDeleteAllFaviconsFromDisk(); +}; + } // namespace widget } // namespace mozilla diff --git a/widget/src/windows/JumpListItem.cpp b/widget/src/windows/JumpListItem.cpp index bd82a0e37143..2576c59ad393 100644 --- a/widget/src/windows/JumpListItem.cpp +++ b/widget/src/windows/JumpListItem.cpp @@ -21,6 +21,7 @@ * * Contributor(s): * Jim Mathies + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -51,6 +52,9 @@ #include "nsNetCID.h" #include "nsCExternalHandlerService.h" #include "nsCycleCollectionParticipant.h" +#include "mozIAsyncFavicons.h" +#include "mozilla/Preferences.h" +#include "JumpListBuilder.h" namespace mozilla { namespace widget { @@ -59,6 +63,7 @@ namespace widget { // need to call it on win7+. JumpListLink::SHCreateItemFromParsingNamePtr JumpListLink::createItemFromParsingName = nsnull; const PRUnichar JumpListLink::kSehllLibraryName[] = L"shell32.dll"; +const char JumpListItem::kJumpListCacheDir[] = "jumpListCache"; HMODULE JumpListLink::sShellDll = nsnull; // ISUPPORTS Impl's @@ -158,7 +163,7 @@ NS_IMETHODIMP JumpListLink::GetUriHash(nsACString& aUriHash) if (!mURI) return NS_ERROR_NOT_AVAILABLE; - return HashURI(mURI, aUriHash); + return JumpListItem::HashURI(mCryptoHash, mURI, aUriHash); } /* boolean compareHash(in nsIURI uri); */ @@ -175,9 +180,9 @@ NS_IMETHODIMP JumpListLink::CompareHash(nsIURI *aUri, PRBool *aResult) nsCAutoString hash1, hash2; - rv = HashURI(mURI, hash1); + rv = JumpListItem::HashURI(mCryptoHash, mURI, hash1); NS_ENSURE_SUCCESS(rv, rv); - rv = HashURI(aUri, hash2); + rv = JumpListItem::HashURI(mCryptoHash, aUri, hash2); NS_ENSURE_SUCCESS(rv, rv); *aResult = hash1.Equals(hash2); @@ -265,6 +270,20 @@ NS_IMETHODIMP JumpListShortcut::SetIconIndex(PRInt32 aIconIndex) return NS_OK; } +/* attribute long iconURI; */ +NS_IMETHODIMP JumpListShortcut::GetFaviconPageUri(nsIURI **aFaviconPageURI) +{ + NS_IF_ADDREF(*aFaviconPageURI = mFaviconPageURI); + + return NS_OK; +} + +NS_IMETHODIMP JumpListShortcut::SetFaviconPageUri(nsIURI *aFaviconPageURI) +{ + mFaviconPageURI = aFaviconPageURI; + return NS_OK; +} + /* boolean equals(nsIJumpListItem item); */ NS_IMETHODIMP JumpListShortcut::Equals(nsIJumpListItem *aItem, PRBool *aResult) { @@ -291,6 +310,7 @@ NS_IMETHODIMP JumpListShortcut::Equals(nsIJumpListItem *aItem, PRBool *aResult) //shortcut->GetIconIndex(&idx); //if (mIconIndex != idx) // return NS_OK; + // No need to check the icon page URI either // Call the internal object's equals() method to check. nsCOMPtr theApp; @@ -342,8 +362,139 @@ nsresult JumpListSeparator::GetSeparator(nsRefPtr& aShellLink) return NS_OK; } +// Obtains the jump list 'ICO cache timeout in seconds' pref +static PRInt32 GetICOCacheSecondsTimeout() { + + // Only obtain the setting at most once from the pref service. + // In the rare case that 2 threads call this at the same + // time it is no harm and we will simply obtain the pref twice. + // None of the taskbar list prefs are currently updated via a + // pref observer so I think this should suffice. + const PRInt32 kSecondsPerDay = 86400; + static PRBool alreadyObtained = PR_FALSE; + static PRInt32 icoReCacheSecondsTimeout = kSecondsPerDay; + if (alreadyObtained) { + return icoReCacheSecondsTimeout; + } + + // Obtain the pref + const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds"; + icoReCacheSecondsTimeout = Preferences::GetInt(PREF_ICOTIMEOUT, + kSecondsPerDay); + alreadyObtained = PR_TRUE; + return icoReCacheSecondsTimeout; +} + +// (static) If the data is available, will return the path on disk where +// the favicon for page aFaviconPageURI is stored. If the favicon does not +// exist, or its cache is expired, this method will kick off an async request +// for the icon so that next time the method is called it will be available. +nsresult JumpListShortcut::ObtainCachedIconFile(nsCOMPtr aFaviconPageURI, + nsString &aICOFilePath, + nsCOMPtr &aIOThread) +{ + // Obtain the ICO file path + nsCOMPtr icoFile; + nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile); + NS_ENSURE_SUCCESS(rv, rv); + + // Check if the cached ICO file already exists + PRBool exists; + rv = icoFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + + // Obtain the file's last modification date in seconds + PRInt64 fileModTime = LL_ZERO; + rv = icoFile->GetLastModifiedTime(&fileModTime); + fileModTime /= PR_MSEC_PER_SEC; + PRInt32 icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout(); + PRInt64 nowTime = PR_Now() / PRInt64(PR_USEC_PER_SEC); + + // If the last mod call failed or the icon is old then re-cache it + // This check is in case the favicon of a page changes + // the next time we try to build the jump list, the data will be available. + if (NS_FAILED(rv) || + (nowTime - fileModTime) > icoReCacheSecondsTimeout) { + CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread); + return NS_ERROR_NOT_AVAILABLE; + } + } else { + + // The file does not exist yet, obtain it async from the favicon service so that + // the next time we try to build the jump list it'll be available. + CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread); + return NS_ERROR_NOT_AVAILABLE; + } + + // The icoFile is filled with a path that exists, get its path + rv = icoFile->GetPath(aICOFilePath); + return rv; +} + +// (static) Obtains the ICO file for the favicon at page aFaviconPageURI +// If successful, the file path on disk is in the format: +// \jumpListCache\.ico +nsresult JumpListShortcut::GetOutputIconPath(nsCOMPtr aFaviconPageURI, + nsCOMPtr &aICOFile) +{ + // Hash the input URI and replace any / with _ + nsCAutoString inputURIHash; + nsCOMPtr cryptoHash; + nsresult rv = JumpListItem::HashURI(cryptoHash, aFaviconPageURI, + inputURIHash); + NS_ENSURE_SUCCESS(rv, rv); + char* cur = inputURIHash.BeginWriting(); + char* end = inputURIHash.EndWriting(); + for (; cur < end; ++cur) { + if ('/' == *cur) { + *cur = '_'; + } + } + + // Obtain the local profile directory and construct the output icon file path + rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = aICOFile->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + + // Try to create the directory if it's not there yet + rv = aICOFile->Create(nsIFile::DIRECTORY_TYPE, 0777); + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) { + return rv; + } + + // Append the icon extension + inputURIHash.Append(".ico"); + rv = aICOFile->AppendNative(inputURIHash); + + return rv; +} + +// (static) Asynchronously creates a cached ICO file on disk for the favicon of +// page aFaviconPageURI and stores it to disk at the path of aICOFile. +nsresult +JumpListShortcut::CacheIconFileFromFaviconURIAsync(nsCOMPtr aFaviconPageURI, + nsCOMPtr aICOFile, + nsCOMPtr &aIOThread) +{ + // Obtain the favicon service and get the favicon for the specified page + nsCOMPtr favIconSvc( + do_GetService("@mozilla.org/browser/favicon-service;1")); + NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE); + + nsCOMPtr callback = + new AsyncFaviconDataReady(aFaviconPageURI, aIOThread); + + favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback); + return NS_OK; +} + // (static) Creates a ShellLink that encapsulate a shortcut to local apps. -nsresult JumpListShortcut::GetShellLink(nsCOMPtr& item, nsRefPtr& aShellLink) +nsresult JumpListShortcut::GetShellLink(nsCOMPtr& item, + nsRefPtr& aShellLink, + nsCOMPtr &aIOThread) { HRESULT hr; IShellLinkW* psl; @@ -401,7 +552,16 @@ nsresult JumpListShortcut::GetShellLink(nsCOMPtr& item, nsRefPt handlerApp->GetName(appTitle); handlerApp->GetDetailedDescription(appDescription); + + PRBool useUriIcon = PR_FALSE; // if we want to use the URI icon + PRBool usedUriIcon = PR_FALSE; // if we did use the URI icon shortcut->GetIconIndex(&appIconIndex); + + nsCOMPtr iconUri; + rv = shortcut->GetFaviconPageUri(getter_AddRefs(iconUri)); + if (NS_SUCCEEDED(rv) && iconUri) { + useUriIcon = PR_TRUE; + } // Store the title of the app if (appTitle.Length() > 0) { @@ -424,13 +584,63 @@ nsresult JumpListShortcut::GetShellLink(nsCOMPtr& item, nsRefPt psl->SetPath(appPath.get()); psl->SetDescription(appDescription.get()); psl->SetArguments(appArgs.get()); - psl->SetIconLocation(appPath.get(), appIconIndex); + + if (useUriIcon) { + nsString icoFilePath; + rv = ObtainCachedIconFile(iconUri, icoFilePath, aIOThread); + if (NS_SUCCEEDED(rv)) { + // Always use the first icon in the ICO file + // our encoded icon only has 1 resource + psl->SetIconLocation(icoFilePath.get(), 0); + usedUriIcon = PR_TRUE; + } + } + + // We didn't use an ICO via URI so fall back to the app icon + if (!usedUriIcon) { + psl->SetIconLocation(appPath.get(), appIconIndex); + } aShellLink = dont_AddRef(psl); return NS_OK; } +// If successful fills in the aSame parameter +// aSame will be true if the path is in our icon cache +static nsresult IsPathInOurIconCache(nsCOMPtr& aShortcut, + PRUnichar *aPath, PRBool *aSame) +{ + NS_ENSURE_ARG_POINTER(aPath); + NS_ENSURE_ARG_POINTER(aSame); + + *aSame = PR_FALSE; + + // Construct the path of our jump list cache + nsCOMPtr jumpListCache; + nsresult rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCache)); + NS_ENSURE_SUCCESS(rv, rv); + rv = jumpListCache->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + nsAutoString jumpListCachePath; + rv = jumpListCache->GetPath(jumpListCachePath); + NS_ENSURE_SUCCESS(rv, rv); + + // Construct the parent path of the passed in path + nsCOMPtr passedInFile = do_CreateInstance("@mozilla.org/file/local;1"); + NS_ENSURE_TRUE(passedInFile, NS_ERROR_FAILURE); + nsAutoString passedInPath(aPath); + rv = passedInFile->InitWithPath(passedInPath); + nsCOMPtr passedInParentFile; + passedInFile->GetParent(getter_AddRefs(passedInParentFile)); + nsAutoString passedInParentPath; + rv = jumpListCache->GetPath(passedInParentPath); + NS_ENSURE_SUCCESS(rv, rv); + + *aSame = jumpListCachePath.Equals(passedInParentPath); + return NS_OK; +} + // (static) For a given IShellLink, create and return a populated nsIJumpListShortcut. nsresult JumpListShortcut::GetJumpListShortcut(IShellLinkW *pLink, nsCOMPtr& aShortcut) { @@ -485,6 +695,19 @@ nsresult JumpListShortcut::GetJumpListShortcut(IShellLinkW *pLink, nsCOMPtrSetIconIndex(iconIdx); + + // Obtain the local profile directory and construct the output icon file path + // We only set the Icon Uri if we're sure it was from our icon cache. + PRBool isInOurCache; + if (NS_SUCCEEDED(IsPathInOurIconCache(aShortcut, buf, &isInOurCache)) && + isInOurCache) { + nsCOMPtr iconUri; + nsAutoString path(buf); + rv = NS_NewURI(getter_AddRefs(iconUri), path); + if (NS_SUCCEEDED(rv)) { + aShortcut->SetFaviconPageUri(iconUri); + } + } } // Do we need the title and description? Probably not since handler app doesn't compare @@ -602,27 +825,28 @@ PRBool JumpListShortcut::ExecutableExists(nsCOMPtr& handlerA return PR_FALSE; } -nsresult JumpListLink::HashURI(nsIURI *aUri, nsACString& aUriHash) +// (static) Helper method which will hash a URI +nsresult JumpListItem::HashURI(nsCOMPtr &aCryptoHash, + nsIURI *aUri, nsACString& aUriHash) { - nsresult rv; - if (!aUri) return NS_ERROR_INVALID_ARG; nsCAutoString spec; - rv = aUri->GetSpec(spec); + nsresult rv = aUri->GetSpec(spec); NS_ENSURE_SUCCESS(rv, rv); - if (!mCryptoHash) { - mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + if (!aCryptoHash) { + aCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } - rv = mCryptoHash->Init(nsICryptoHash::MD5); + rv = aCryptoHash->Init(nsICryptoHash::MD5); NS_ENSURE_SUCCESS(rv, rv); - rv = mCryptoHash->Update(reinterpret_cast(spec.BeginReading()), spec.Length()); + rv = aCryptoHash->Update(reinterpret_cast(spec.BeginReading()), + spec.Length()); NS_ENSURE_SUCCESS(rv, rv); - rv = mCryptoHash->Finish(PR_TRUE, aUriHash); + rv = aCryptoHash->Finish(PR_TRUE, aUriHash); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; diff --git a/widget/src/windows/JumpListItem.h b/widget/src/windows/JumpListItem.h index 82249fdabb81..591d91eefbc1 100644 --- a/widget/src/windows/JumpListItem.h +++ b/widget/src/windows/JumpListItem.h @@ -21,6 +21,7 @@ * * Contributor(s): * Jim Mathies + * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -55,6 +56,8 @@ #include "nsString.h" #include "nsCycleCollectionParticipant.h" +class nsIThread; + namespace mozilla { namespace widget { @@ -75,9 +78,14 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIJUMPLISTITEM + static const char kJumpListCacheDir[]; + protected: short Type() { return mItemType; } short mItemType; + + static nsresult HashURI(nsCOMPtr &aCryptoHash, + nsIURI *aUri, nsACString& aUriHash); }; class JumpListSeparator : public JumpListItem, public nsIJumpListSeparator @@ -118,8 +126,6 @@ protected: static const PRUnichar kSehllLibraryName[]; static HMODULE sShellDll; static SHCreateItemFromParsingNamePtr createItemFromParsingName; - - nsresult HashURI(nsIURI *uri, nsACString& aUriHas); }; class JumpListShortcut : public JumpListItem, public nsIJumpListShortcut @@ -135,14 +141,25 @@ public: NS_IMETHOD Equals(nsIJumpListItem *item, PRBool *_retval); NS_DECL_NSIJUMPLISTSHORTCUT - static nsresult GetShellLink(nsCOMPtr& item, nsRefPtr& aShellLink); + static nsresult GetShellLink(nsCOMPtr& item, + nsRefPtr& aShellLink, + nsCOMPtr &aIOThread); static nsresult GetJumpListShortcut(IShellLinkW *pLink, nsCOMPtr& aShortcut); + static nsresult GetOutputIconPath(nsCOMPtr aFaviconPageURI, + nsCOMPtr &aICOFile); protected: PRInt32 mIconIndex; + nsCOMPtr mFaviconPageURI; nsCOMPtr mHandlerApp; PRBool ExecutableExists(nsCOMPtr& handlerApp); + static nsresult ObtainCachedIconFile(nsCOMPtr aFaviconPageURI, + nsString &aICOFilePath, + nsCOMPtr &aIOThread); + static nsresult CacheIconFileFromFaviconURIAsync(nsCOMPtr aFaviconPageURI, + nsCOMPtr aICOFile, + nsCOMPtr &aIOThread); }; } // namespace widget diff --git a/widget/src/windows/nsSound.cpp b/widget/src/windows/nsSound.cpp index 461e1ea4c000..c1575e17a7d1 100644 --- a/widget/src/windows/nsSound.cpp +++ b/widget/src/windows/nsSound.cpp @@ -64,13 +64,13 @@ PRLogModuleInfo* gWin32SoundLog = nsnull; class nsSoundPlayer: public nsRunnable { public: - nsSoundPlayer(nsISound *aSound, const wchar_t* aSoundName) : + nsSoundPlayer(nsSound *aSound, const wchar_t* aSoundName) : mSoundName(aSoundName), mSound(aSound) { Init(); } - nsSoundPlayer(nsISound *aSound, const nsAString& aSoundName) : + nsSoundPlayer(nsSound *aSound, const nsAString& aSoundName) : mSoundName(aSoundName), mSound(aSound) { Init(); @@ -80,7 +80,7 @@ public: protected: nsString mSoundName; - nsISound *mSound; // Strong, but this will be released from SoundReleaser. + nsSound *mSound; // Strong, but this will be released from SoundReleaser. nsCOMPtr mThread; void Init() @@ -92,7 +92,7 @@ protected: class SoundReleaser: public nsRunnable { public: - SoundReleaser(nsISound* aSound) : + SoundReleaser(nsSound* aSound) : mSound(aSound) { } @@ -100,7 +100,7 @@ protected: NS_DECL_NSIRUNNABLE protected: - nsISound *mSound; + nsSound *mSound; }; }; @@ -110,7 +110,7 @@ nsSoundPlayer::Run() NS_PRECONDITION(!mSoundName.IsEmpty(), "Sound name should not be empty"); ::PlaySoundW(mSoundName.get(), NULL, SND_NODEFAULT | SND_ALIAS | SND_ASYNC); nsCOMPtr releaser = new SoundReleaser(mSound); - // Don't release nsISound from here, because here is not an owning thread of + // Don't release nsSound from here, because here is not an owning thread of // the nsSound. nsSound must be released in its owning thread. mThread->Dispatch(releaser, NS_DISPATCH_NORMAL); return NS_OK; @@ -119,6 +119,7 @@ nsSoundPlayer::Run() NS_IMETHODIMP nsSoundPlayer::SoundReleaser::Run() { + mSound->ShutdownOldPlayerThread(); NS_IF_RELEASE(mSound); return NS_OK; } @@ -146,14 +147,20 @@ nsSound::nsSound() nsSound::~nsSound() { + NS_ASSERTION(!mPlayerThread, "player thread is not null but should be"); PurgeLastSound(); } -void nsSound::PurgeLastSound() { +void nsSound::ShutdownOldPlayerThread() +{ if (mPlayerThread) { mPlayerThread->Shutdown(); mPlayerThread = nsnull; } +} + +void nsSound::PurgeLastSound() +{ if (mLastSound) { // Halt any currently playing sound. ::PlaySound(nsnull, nsnull, SND_PURGE); @@ -201,6 +208,7 @@ NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader, return aStatus; } + ShutdownOldPlayerThread(); PurgeLastSound(); if (data && dataLen > 0) { @@ -251,6 +259,7 @@ NS_IMETHODIMP nsSound::Init() NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias) { + ShutdownOldPlayerThread(); PurgeLastSound(); if (!NS_IsMozAliasSound(aSoundAlias)) { @@ -284,6 +293,7 @@ NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias) NS_IMETHODIMP nsSound::PlayEventSound(PRUint32 aEventId) { + ShutdownOldPlayerThread(); PurgeLastSound(); const wchar_t *sound = nsnull; diff --git a/widget/src/windows/nsSound.h b/widget/src/windows/nsSound.h index cd2b9ce5baea..f22cb8bba264 100644 --- a/widget/src/windows/nsSound.h +++ b/widget/src/windows/nsSound.h @@ -51,6 +51,7 @@ class nsSound : public nsISound, public: nsSound(); virtual ~nsSound(); + void ShutdownOldPlayerThread(); NS_DECL_ISUPPORTS NS_DECL_NSISOUND diff --git a/widget/tests/unit/test_taskbar_jumplistitems.js b/widget/tests/unit/test_taskbar_jumplistitems.js index 8ab4d56e20f1..329a7a4e0e53 100644 --- a/widget/tests/unit/test_taskbar_jumplistitems.js +++ b/widget/tests/unit/test_taskbar_jumplistitems.js @@ -184,9 +184,14 @@ function test_shortcuts() handlerApp.appendParameter("-test"); sc.iconIndex = 1; - do_check_eq(sc.iconIndex, 1); + var faviconPageUri = Cc["@mozilla.org/network/simple-uri;1"] + .createInstance(Ci.nsIURI); + faviconPageUri.spec = "http://www.123.com/"; + sc.faviconPageUri = faviconPageUri; + do_check_eq(sc.faviconPageUri, faviconPageUri); + var dirSvc = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties). QueryInterface(Ci.nsIDirectoryService); diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 2b7d629f0592..f06a3de091d9 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -308,30 +308,22 @@ NS_MEMORY_REPORTER_IMPLEMENT(HeapCommitted, KIND_OTHER, UNITS_BYTES, GetHeapCommitted, - "This number reported only for completeness; it is not particularly " - "meaningful. On Windows, all mapped memory is committed (because jemalloc's " - "MALLOC_DECOMMIT flag is set). Thus heap-committed should equal " - "heap-allocated + heap-unallocated. Elsewhere, jemalloc uses " - "madvise(DONT_NEED) to instruct the OS to drop the physical memory backing " - "pages the allocator doesn't need. In this case, jemalloc counts the memory " - "as 'committed', but it's not taking up any space in physical memory or in " - "the swap file.") + "Memory mapped by the heap allocator that is committed, i.e. in physical " + "memory or paged to disk. When heap-committed is larger than " + "heap-allocated, the difference between the two values is likely due to " + "external fragmentation; that is, the allocator allocated a large block of " + "memory and is unable to decommit it because a small part of that block is " + "currently in use.") NS_MEMORY_REPORTER_IMPLEMENT(HeapDirty, "heap-dirty", KIND_OTHER, UNITS_BYTES, GetHeapDirty, - "Memory mapped by the heap allocator that was once part of an allocation " - "but which is now not allocated to the application. Since the application " - "may have modified the memory while it was allocated, we count this memory " - "as \"committed\", that is, as taking up space in physical memory or the " - "swap file. If memory is fragmented, and the allocator is unable to return " - "some mostly-free pages to the operating system because they contain a few " - "live objects, you might see a large value here. But even in the absence " - "of fragmentation, the allocator might not return some dirty memory to the " - "OS as an optimization, under the assumption that the application will need " - "the memory again soon.") + "Memory which the allocator could return to the operating system, but " + "hasn't. The allocator keeps this memory around as an optimization, so it " + "doesn't have to ask the OS the next time it needs to fulfill a request. " + "This value is typically not larger than a few megabytes.") #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY) #include @@ -446,8 +438,19 @@ nsMemoryReporterManager::Init() REGISTER(Private); #endif -#if defined(HAVE_JEMALLOC_STATS) +#if defined(HAVE_JEMALLOC_STATS) && defined(XP_WIN) + // heap-committed is only meaningful where we have MALLOC_DECOMMIT defined + // (currently, just on Windows). Elsewhere, it's the same as + // stats->mapped, which is heap-allocated + heap-unallocated. + // + // Ideally, we'd check for MALLOC_DECOMMIT in the #if defined above, but + // MALLOC_DECOMMIT is defined in jemalloc.c, not a header, so we'll just + // have to settle for the OS check for now. + REGISTER(HeapCommitted); +#endif + +#if defined(HAVE_JEMALLOC_STATS) REGISTER(HeapDirty); #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY) REGISTER(HeapZone0Committed); diff --git a/xpcom/glue/nsINIParser.cpp b/xpcom/glue/nsINIParser.cpp index b0f7be7e4df7..6db34112afaf 100644 --- a/xpcom/glue/nsINIParser.cpp +++ b/xpcom/glue/nsINIParser.cpp @@ -88,8 +88,6 @@ private: nsresult nsINIParser::Init(nsILocalFile* aFile) { - nsresult rv; - /* open the file. Don't use OpenANSIFileDesc, because you mustn't pass FILE* across shared library boundaries, which may be using different CRTs */ @@ -98,13 +96,13 @@ nsINIParser::Init(nsILocalFile* aFile) #ifdef XP_WIN nsAutoString path; - rv = aFile->GetPath(path); + nsresult rv = aFile->GetPath(path); NS_ENSURE_SUCCESS(rv, rv); fd = _wfopen(path.get(), READ_BINARYMODE); #else nsCAutoString path; - rv = aFile->GetNativePath(path); + aFile->GetNativePath(path); fd = fopen(path.get(), READ_BINARYMODE); #endif diff --git a/xpcom/reflect/xptcall/src/md/unix/Makefile.in b/xpcom/reflect/xptcall/src/md/unix/Makefile.in index 6ef8880429e7..c98555fb247b 100644 --- a/xpcom/reflect/xptcall/src/md/unix/Makefile.in +++ b/xpcom/reflect/xptcall/src/md/unix/Makefile.in @@ -68,10 +68,14 @@ ifeq (Darwin,$(OS_ARCH)) ifeq (86,$(findstring 86,$(OS_TEST))) ifneq (x86_64,$(OS_TEST)) # If we compile xptcinvoke_unixish_x86.cpp with -fomit-frame-pointer -# we end up crashing on startup, presumably because of the %esp munging we do -# when KEEP_STACK_16_BYTE_ALIGNED. So let's not use that flag here. -# NOTE: MODULE_OPTIMIZE_FLAGS must be set before including config.mk -MODULE_OPTIMIZE_FLAGS=-O3 +# we end up crashing on startup. This is because +# "movl %5, %%eax\n\t" /* function index */ +# becomes +# mov 0x1c(%esp),%eax +# but we have modified esp. +# NOTE: MOZ_FRAMEPTR_FLAGS must be set before including config.mk +# FIXME: change the file instead of using this hack. +MOZ_FRAMEPTR_FLAGS=-fno-omit-frame-pointer endif endif endif diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_irix.s b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_irix.s deleted file mode 100644 index 4e8e0895a383..000000000000 --- a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_irix.s +++ /dev/null @@ -1,149 +0,0 @@ - -#include -#include - -.text -.globl invoke_count_words -.globl invoke_copy_to_stack - -LOCALSZ=7 # a0, a1, a2, a3, s0, ra, gp -FRAMESZ=(((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK - -RAOFF=FRAMESZ-(1*SZREG) -A0OFF=FRAMESZ-(2*SZREG) -A1OFF=FRAMESZ-(3*SZREG) -A2OFF=FRAMESZ-(4*SZREG) -A3OFF=FRAMESZ-(5*SZREG) -S0OFF=FRAMESZ-(6*SZREG) -GPOFF=FRAMESZ-(7*SZREG) - - # - # _XPTC_InvokeByIndex(that, methodIndex, paramCount, params) - # a0 a1 a2 a3 - -NESTED(_XPTC_InvokeByIndex, FRAMESZ, ra) - SETUP_GP - PTR_SUBU sp, FRAMESZ - SETUP_GP64(GPOFF, _XPTC_InvokeByIndex) - SAVE_GP(GPOFF) - - REG_S ra, RAOFF(sp) - REG_S a0, A0OFF(sp) - REG_S a1, A1OFF(sp) - REG_S a2, A2OFF(sp) - REG_S a3, A3OFF(sp) - REG_S s0, S0OFF(sp) - REG_S gp, GPOFF(sp) - - # invoke_count_words(paramCount, params) - move a0, a2 - move a1, a3 - jal invoke_count_words - - # invoke_copy_to_stack(PRUint32* d, PRUint32 paramCount, - # nsXPTCVariant* s, PRUint32 *reg) - - REG_L a1, A2OFF(sp) # a1 - paramCount - REG_L a2, A3OFF(sp) # a2 - params - - # save sp before we copy the params to the stack - move t0, sp - - # assume full size of 8 bytes per param to be safe - sll v0, 4 # 8 bytes * num params - subu sp, sp, v0 # make room - move a0, sp # a0 - param stack address - - # create temporary stack space to write int and fp regs - subu sp, 64 # 64 = 8 regs of 8 bytes - - move a3, sp - - # save the old sp and save the arg stack - subu sp, sp, 16 - REG_S t0, 0(sp) - REG_S a0, 8(sp) - - # copy the param into the stack areas - jal invoke_copy_to_stack - - REG_L t3, 8(sp) # get previous a0 - REG_L sp, 0(sp) # get orig sp back - - REG_L a0, A0OFF(sp) # a0 - that - REG_L a1, A1OFF(sp) # a1 - methodIndex - -#ifdef __GNUC__ - # t1 = methodIndex * 8 - # (use shift instead of mult) - sll t1, a1, 3 -#else - # t1 = methodIndex * 12 - # (use shift and subtract trick instead of mult) - sll t1, a1, 2 - subu t1, t1, a1 - sll t1, t1, 2 -#endif - - # calculate the function we need to jump to, - # which must then be saved in t9 - lw t9, 0(a0) - addu t9, t9, t1 -#ifdef __GNUC__ - lw t9, 12(t9) # t9 = *(that+t1+12) -#else - li t2, 20 - addu t9, t9, t2 - lw t9, 0(t9) # t9 = *(that+t1+20) -#endif - - # calculate the proper "this" pointer for the - # function that they asked for - lw t0, 0(a0) - addu t0, t1 -#ifdef __GNUC__ - lh t0, 8(t0) -#else - lw t0, 12(t0) -#endif - - addu a0, a0, t0 - - # get register save area from invoke_copy_to_stack - subu t1, t3, 64 - - # a1..a7 and f13..f19 should now be set to what - # invoke_copy_to_stack told us. skip a0 and f12 - # because that's the "this" pointer - - REG_L a1, 0(t1) - REG_L a2, 8(t1) - REG_L a3, 16(t1) - REG_L a4, 24(t1) - REG_L a5, 32(t1) - REG_L a6, 40(t1) - REG_L a7, 48(t1) - - l.d $f13, 0(t1) - l.d $f14, 8(t1) - l.d $f15, 16(t1) - l.d $f16, 24(t1) - l.d $f17, 32(t1) - l.d $f18, 40(t1) - l.d $f19, 48(t1) - - # save away our stack pointer and create - # the stack pointer for the function - move s0, sp - move sp, t3 - - jalr ra, t9 - - move sp, s0 - - RESTORE_GP64 - REG_L ra, RAOFF(sp) - REG_L s0, S0OFF(sp) - PTR_ADDU sp, FRAMESZ - j ra -.end _XPTC_InvokeByIndex diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_irix.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_irix.cpp deleted file mode 100644 index 67aa614465e4..000000000000 --- a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_irix.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* Platform specific code to invoke XPCOM methods on native objects */ - -#include "xptcprivate.h" - -#if (_MIPS_SIM != _ABIN32) -#error "This code is for IRIX N32 only" -#endif - -extern "C" uint32 -invoke_count_words(PRUint32 paramCount, nsXPTCVariant* s) -{ - return(paramCount*2); -} - -extern "C" void -invoke_copy_to_stack(PRUint64* d, PRUint32 paramCount, - nsXPTCVariant* s, PRUint64 *regs) -{ -#define N_ARG_REGS 7 /* 8 regs minus 1 for "this" ptr */ - - for (PRUint32 i = 0; i < paramCount; i++, s++) - { - if (s->IsPtrData()) { - if (i < N_ARG_REGS) - regs[i] = (PRUint64)s->ptr; - else - *d++ = (PRUint64)s->ptr; - continue; - } - switch (s->type) { - // - // signed types first - // - case nsXPTType::T_I8: - if (i < N_ARG_REGS) - ((PRInt64*)regs)[i] = s->val.i8; - else - *d++ = s->val.i8; - break; - case nsXPTType::T_I16: - if (i < N_ARG_REGS) - ((PRInt64*)regs)[i] = s->val.i16; - else - *d++ = s->val.i16; - break; - case nsXPTType::T_I32: - if (i < N_ARG_REGS) - ((PRInt64*)regs)[i] = s->val.i32; - else - *d++ = s->val.i32; - break; - case nsXPTType::T_I64: - if (i < N_ARG_REGS) - ((PRInt64*)regs)[i] = s->val.i64; - else - *d++ = s->val.i64; - break; - // - // unsigned types next - // - case nsXPTType::T_U8: - if (i < N_ARG_REGS) - regs[i] = s->val.u8; - else - *d++ = s->val.u8; - break; - case nsXPTType::T_U16: - if (i < N_ARG_REGS) - regs[i] = s->val.u16; - else - *d++ = s->val.u16; - break; - case nsXPTType::T_U32: - if (i < N_ARG_REGS) - regs[i] = s->val.u32; - else - *d++ = s->val.u32; - break; - case nsXPTType::T_U64: - if (i < N_ARG_REGS) - regs[i] = s->val.u64; - else - *d++ = s->val.u64; - break; - case nsXPTType::T_FLOAT: - if (i < N_ARG_REGS) - // Place a float in least significant bytes. - *(float*)(((char*)®s[i+1]) - sizeof(float)) = s->val.f; - else - *(float*)d++ = s->val.f; - break; - case nsXPTType::T_DOUBLE: - if (i < N_ARG_REGS) - *(double*)®s[i] = s->val.d; - else - *(double*)d++ = s->val.d; - break; - case nsXPTType::T_BOOL: - if (i < N_ARG_REGS) - regs[i] = s->val.b; - else - *d++ = s->val.b; - break; - case nsXPTType::T_CHAR: - if (i < N_ARG_REGS) - regs[i] = s->val.c; - else - *d++ = s->val.c; - break; - case nsXPTType::T_WCHAR: - if (i < N_ARG_REGS) - regs[i] = s->val.wc; - else - *d++ = s->val.wc; - break; - default: - // all the others are plain pointer types - if (i < N_ARG_REGS) - regs[i] = (PRUint64)s->val.p; - else - *d++ = (PRUint64)s->val.p; - break; - } - } -} - -extern "C" nsresult _XPTC_InvokeByIndex(nsISupports* that, PRUint32 methodIndex, - PRUint32 paramCount, nsXPTCVariant* params); - -extern "C" -XPTC_PUBLIC_API(nsresult) -XPTC_InvokeByIndex(nsISupports* that, PRUint32 methodIndex, - PRUint32 paramCount, nsXPTCVariant* params) -{ - return _XPTC_InvokeByIndex(that, methodIndex, paramCount, params); -} diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_irix.s b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_irix.s deleted file mode 100644 index 4ca1e542b604..000000000000 --- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_irix.s +++ /dev/null @@ -1,97 +0,0 @@ - -#include -#include - - .text - .globl PrepareAndDispatch - -LOCALSZ=16 -FRAMESZ=(((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK - -A1OFF=FRAMESZ-(9*SZREG) -A2OFF=FRAMESZ-(8*SZREG) -A3OFF=FRAMESZ-(7*SZREG) -A4OFF=FRAMESZ-(6*SZREG) -A5OFF=FRAMESZ-(5*SZREG) -A6OFF=FRAMESZ-(4*SZREG) -A7OFF=FRAMESZ-(3*SZREG) -GPOFF=FRAMESZ-(2*SZREG) -RAOFF=FRAMESZ-(1*SZREG) - -F13OFF=FRAMESZ-(16*SZREG) -F14OFF=FRAMESZ-(15*SZREG) -F15OFF=FRAMESZ-(14*SZREG) -F16OFF=FRAMESZ-(13*SZREG) -F17OFF=FRAMESZ-(12*SZREG) -F18OFF=FRAMESZ-(11*SZREG) -F19OFF=FRAMESZ-(10*SZREG) - -#define SENTINEL_ENTRY(nn) /* defined in cpp file, not here */ - -#ifdef __GNUC__ -#define STUB_ENTRY(nn) MAKE_STUB(nn, Stub/**/nn/**/__14nsXPTCStubBase) -#else -#define STUB_ENTRY(nn) MAKE_STUB(nn, Stub/**/nn/**/__14nsXPTCStubBaseGv) -#endif - -#define MAKE_STUB(nn, name) \ -NESTED(name, FRAMESZ, ra); \ - SETUP_GP; \ - PTR_SUBU sp, FRAMESZ; \ - SETUP_GP64(GPOFF, name); \ - SAVE_GP(GPOFF); \ - li t0, nn; \ - b sharedstub; \ -.end name; \ - -#include "xptcstubsdef.inc" - - .globl sharedstub - .ent sharedstub -sharedstub: - - REG_S a1, A1OFF(sp) - REG_S a2, A2OFF(sp) - REG_S a3, A3OFF(sp) - REG_S a4, A4OFF(sp) - REG_S a5, A5OFF(sp) - REG_S a6, A6OFF(sp) - REG_S a7, A7OFF(sp) - REG_S ra, RAOFF(sp) - - s.d $f13, F13OFF(sp) - s.d $f14, F14OFF(sp) - s.d $f15, F15OFF(sp) - s.d $f16, F16OFF(sp) - s.d $f17, F17OFF(sp) - s.d $f18, F18OFF(sp) - s.d $f19, F19OFF(sp) - - # t0 is methodIndex - move a1, t0 - - # a2 is stack address where extra function params - # are stored that do not fit in registers - move a2, sp - addi a2, FRAMESZ - - # a3 is stack address of a1..a7 - move a3, sp - addi a3, A1OFF - - # a4 is stack address of f13..f19 - move a4, sp - addi a4, F13OFF - - # PrepareAndDispatch(that, methodIndex, args, gprArgs, fpArgs) - # a0 a1 a2 a3 a4 - # - jal PrepareAndDispatch - - REG_L ra, RAOFF(sp) - REG_L gp, GPOFF(sp) - - PTR_ADDU sp, FRAMESZ - j ra - -.end sharedstub diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_irix.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_irix.cpp deleted file mode 100644 index b98e5333ce12..000000000000 --- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_irix.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1999 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "xptcprivate.h" - -#if defined(IRIX) - -#if (_MIPS_SIM != _ABIN32) -#error "This code is for IRIX N32 only" -#endif - -/* - * This is for IRIX N32 ABI - * - * When we're called, the "gp" registers are stored in gprData and - * the "fp" registers are stored in fprData. There are 8 regs - * available which coorespond to the first 7 parameters of the - * function and the "this" pointer. If there are additional parms, - * they are stored on the stack at address "args". - * - */ -extern "C" nsresult -PrepareAndDispatch(nsXPTCStubBase* self, PRUint32 methodIndex, PRUint64* args, - PRUint64 *gprData, double *fprData) -{ -#define PARAM_BUFFER_COUNT 16 -#define PARAM_GPR_COUNT 7 -#define PARAM_FPR_COUNT 7 - - nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT]; - nsXPTCMiniVariant* dispatchParams = NULL; - nsIInterfaceInfo* iface_info = NULL; - const nsXPTMethodInfo* info; - PRUint8 paramCount; - PRUint8 i; - nsresult result = NS_ERROR_FAILURE; - - NS_ASSERTION(self,"no self"); - - self->GetInterfaceInfo(&iface_info); - NS_ASSERTION(iface_info,"no interface info"); - - iface_info->GetMethodInfo(PRUint16(methodIndex), &info); - NS_ASSERTION(info,"no interface info"); - - paramCount = info->GetParamCount(); - - // setup variant array pointer - if(paramCount > PARAM_BUFFER_COUNT) - dispatchParams = new nsXPTCMiniVariant[paramCount]; - else - dispatchParams = paramBuffer; - NS_ASSERTION(dispatchParams,"no place for params"); - - PRUint64* ap = args; - PRUint32 iCount = 0; - for(i = 0; i < paramCount; i++) - { - const nsXPTParamInfo& param = info->GetParam(i); - const nsXPTType& type = param.GetType(); - nsXPTCMiniVariant* dp = &dispatchParams[i]; - - if(param.IsOut() || !type.IsArithmetic()) - { - if (iCount < PARAM_GPR_COUNT) - dp->val.p = (void*)gprData[iCount++]; - else - dp->val.p = (void*)*ap++; - continue; - } - // else - switch(type) - { - case nsXPTType::T_I8: - if (iCount < PARAM_GPR_COUNT) - dp->val.i8 = (PRInt8)gprData[iCount++]; - else - dp->val.i8 = (PRInt8)*ap++; - break; - - case nsXPTType::T_I16: - if (iCount < PARAM_GPR_COUNT) - dp->val.i16 = (PRInt16)gprData[iCount++]; - else - dp->val.i16 = (PRInt16)*ap++; - break; - - case nsXPTType::T_I32: - if (iCount < PARAM_GPR_COUNT) - dp->val.i32 = (PRInt32)gprData[iCount++]; - else - dp->val.i32 = (PRInt32)*ap++; - break; - - case nsXPTType::T_I64: - if (iCount < PARAM_GPR_COUNT) - dp->val.i64 = (PRInt64)gprData[iCount++]; - else - dp->val.i64 = (PRInt64)*ap++; - break; - - case nsXPTType::T_U8: - if (iCount < PARAM_GPR_COUNT) - dp->val.u8 = (PRUint8)gprData[iCount++]; - else - dp->val.u8 = (PRUint8)*ap++; - break; - - case nsXPTType::T_U16: - if (iCount < PARAM_GPR_COUNT) - dp->val.u16 = (PRUint16)gprData[iCount++]; - else - dp->val.u16 = (PRUint16)*ap++; - break; - - case nsXPTType::T_U32: - if (iCount < PARAM_GPR_COUNT) - dp->val.u32 = (PRUint32)gprData[iCount++]; - else - dp->val.u32 = (PRUint32)*ap++; - break; - - case nsXPTType::T_U64: - if (iCount < PARAM_GPR_COUNT) - dp->val.u64 = (PRUint64)gprData[iCount++]; - else - dp->val.u64 = (PRUint64)*ap++; - break; - - case nsXPTType::T_FLOAT: - if (iCount < PARAM_FPR_COUNT) - dp->val.f = (double)fprData[iCount++]; - else - dp->val.f = *((double*)ap++); - break; - - case nsXPTType::T_DOUBLE: - if (iCount < PARAM_FPR_COUNT) - dp->val.d = (double)fprData[iCount++]; - else - dp->val.d = *((double*)ap++); - break; - - case nsXPTType::T_BOOL: - if (iCount < PARAM_GPR_COUNT) - dp->val.b = (PRBool)gprData[iCount++]; - else - dp->val.b = (PRBool)*ap++; - break; - - case nsXPTType::T_CHAR: - if (iCount < PARAM_GPR_COUNT) - dp->val.c = (char)gprData[iCount++]; - else - dp->val.c = (char)*ap++; - break; - - case nsXPTType::T_WCHAR: - if (iCount < PARAM_GPR_COUNT) - dp->val.wc = (wchar_t)gprData[iCount++]; - else - dp->val.wc = (wchar_t)*ap++; - break; - - default: - NS_ERROR("bad type"); - break; - } - } - - result = self->CallMethod((PRUint16)methodIndex, info, dispatchParams); - - NS_RELEASE(iface_info); - - if(dispatchParams != paramBuffer) - delete [] dispatchParams; - - return result; -} - -#define STUB_ENTRY(n) /* defined in the assembly file */ - -#define SENTINEL_ENTRY(n) \ -nsresult nsXPTCStubBase::Sentinel##n() \ -{ \ - NS_ERROR("nsXPTCStubBase::Sentinel called"); \ - return NS_ERROR_NOT_IMPLEMENTED; \ -} - -#include "xptcstubsdef.inc" - -#endif