mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Merge last green PGO from inbound to central
This commit is contained in:
commit
9cd7f532f6
@ -616,6 +616,9 @@ nsAccessible::VisibilityState()
|
||||
} while (accessible = accessible->Parent());
|
||||
|
||||
nsIFrame* frame = GetFrame();
|
||||
if (!frame)
|
||||
return vstates;
|
||||
|
||||
const nsCOMPtr<nsIPresShell> shell(GetPresShell());
|
||||
|
||||
// We need to know if at least a kMinPixels around the object is visible,
|
||||
|
@ -56,6 +56,7 @@ CMMSRCS = nsAccessNodeWrap.mm \
|
||||
mozDocAccessible.mm \
|
||||
mozActionElements.mm \
|
||||
mozTextAccessible.mm \
|
||||
mozHTMLAccessible.mm \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
45
accessible/src/mac/mozHTMLAccessible.h
Normal file
45
accessible/src/mac/mozHTMLAccessible.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
||||
*/
|
||||
/* ***** 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
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Original Author: Hub Figuière <hub@mozilla.com>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#import "mozAccessible.h"
|
||||
|
||||
@interface mozHeadingAccessible : mozAccessible
|
||||
|
||||
@end
|
56
accessible/src/mac/mozHTMLAccessible.mm
Normal file
56
accessible/src/mac/mozHTMLAccessible.mm
Normal file
@ -0,0 +1,56 @@
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
||||
*/
|
||||
/* ***** 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
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Original Author: Hub Figuière <hub@mozilla.com>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#import "mozHTMLAccessible.h"
|
||||
|
||||
#import "nsHyperTextAccessible.h"
|
||||
|
||||
@implementation mozHeadingAccessible
|
||||
|
||||
- (id)value
|
||||
{
|
||||
if (!mGeckoAccessible || !mGeckoAccessible->IsHyperText())
|
||||
return nil;
|
||||
|
||||
PRUint32 level = mGeckoAccessible->AsHyperText()->GetLevelInternal();
|
||||
return [NSNumber numberWithInt:level];
|
||||
}
|
||||
|
||||
@end
|
@ -45,6 +45,7 @@
|
||||
|
||||
#import "mozAccessible.h"
|
||||
#import "mozActionElements.h"
|
||||
#import "mozHTMLAccessible.h"
|
||||
#import "mozTextAccessible.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
@ -108,10 +109,12 @@ nsAccessibleWrap::GetNativeType ()
|
||||
|
||||
case roles::AUTOCOMPLETE:
|
||||
return [mozComboboxAccessible class];
|
||||
|
||||
|
||||
case roles::HEADING:
|
||||
return [mozHeadingAccessible class];
|
||||
|
||||
case roles::ENTRY:
|
||||
case roles::STATICTEXT:
|
||||
case roles::HEADING:
|
||||
case roles::LABEL:
|
||||
case roles::CAPTION:
|
||||
case roles::ACCEL_LABEL:
|
||||
|
@ -147,7 +147,7 @@ static const NSString* AXRoles [] = {
|
||||
NSAccessibilityTextFieldRole, // ROLE_ENTRY
|
||||
NSAccessibilityStaticTextRole, // ROLE_CAPTION
|
||||
@"AXWebArea", // ROLE_DOCUMENT_FRAME
|
||||
NSAccessibilityStaticTextRole, // ROLE_HEADING
|
||||
@"AXHeading", // ROLE_HEADING
|
||||
NSAccessibilityGroupRole, // ROLE_PAGE
|
||||
NSAccessibilityGroupRole, // ROLE_SECTION
|
||||
NSAccessibilityUnknownRole, // ROLE_REDUNDANT_OBJECT
|
||||
|
@ -5201,7 +5201,7 @@ nsBrowserAccess.prototype = {
|
||||
let win, needToFocusWin;
|
||||
|
||||
// try the current window. if we're in a popup, fall back on the most recent browser window
|
||||
if (!window.document.documentElement.getAttribute("chromehidden"))
|
||||
if (window.toolbar.visible)
|
||||
win = window;
|
||||
else {
|
||||
win = Cc["@mozilla.org/browser/browserglue;1"]
|
||||
|
@ -3811,49 +3811,34 @@
|
||||
<binding id="tabbrowser-alltabs-popup"
|
||||
extends="chrome://global/content/bindings/popup.xml#popup">
|
||||
<implementation implements="nsIDOMEventListener">
|
||||
<method name="_menuItemOnCommand">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
gBrowser.selectedTab = aEvent.target.tab;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_tabOnAttrModified">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
var tab = aEvent.target;
|
||||
this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
|
||||
if (tab.mCorrespondingMenuitem)
|
||||
this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_tabOnTabClose">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
var menuItem = aEvent.target.mCorrespondingMenuitem;
|
||||
if (menuItem)
|
||||
this.removeChild(menuItem);
|
||||
var tab = aEvent.target;
|
||||
if (tab.mCorrespondingMenuitem)
|
||||
this.removeChild(tab.mCorrespondingMenuitem);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="handleEvent">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
if (!aEvent.isTrusted)
|
||||
return;
|
||||
|
||||
switch (aEvent.type) {
|
||||
case "command":
|
||||
this._menuItemOnCommand(aEvent);
|
||||
break;
|
||||
case "TabAttrModified":
|
||||
this._tabOnAttrModified(aEvent);
|
||||
break;
|
||||
case "TabClose":
|
||||
this._tabOnTabClose(aEvent);
|
||||
break;
|
||||
case "TabOpen":
|
||||
this._createTabMenuItem(aEvent.originalTarget);
|
||||
break;
|
||||
case "scroll":
|
||||
this._updateTabsVisibilityStatus();
|
||||
break;
|
||||
@ -3874,8 +3859,6 @@
|
||||
if (!curTab) // "Tab Groups" menuitem and its menuseparator
|
||||
continue;
|
||||
let curTabBO = curTab.boxObject;
|
||||
if (!curTabBO) // "Tabs From Other Computers" menuitem
|
||||
continue;
|
||||
if (curTabBO.screenX >= tabstripBO.screenX &&
|
||||
curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
|
||||
this.childNodes[i].setAttribute("tabIsVisible", "true");
|
||||
@ -3896,14 +3879,10 @@
|
||||
|
||||
this._setMenuitemAttributes(menuItem, aTab);
|
||||
|
||||
// Keep some attributes of the menuitem in sync with its
|
||||
// corresponding tab (e.g. the tab label)
|
||||
aTab.mCorrespondingMenuitem = menuItem;
|
||||
menuItem.tab = aTab;
|
||||
menuItem.addEventListener("command", this, false);
|
||||
|
||||
this.appendChild(menuItem);
|
||||
return menuItem;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -3938,18 +3917,17 @@
|
||||
<handlers>
|
||||
<handler event="popupshowing">
|
||||
<![CDATA[
|
||||
// set up the menu popup
|
||||
var tabcontainer = gBrowser.tabContainer;
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
|
||||
// Listen for changes in the tab bar.
|
||||
tabcontainer.addEventListener("TabOpen", this, false);
|
||||
tabcontainer.addEventListener("TabAttrModified", this, false);
|
||||
tabcontainer.addEventListener("TabClose", this, false);
|
||||
tabcontainer.mTabstrip.addEventListener("scroll", this, false);
|
||||
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
this._createTabMenuItem(tabs[i]);
|
||||
if (!tabs[i].pinned)
|
||||
this._createTabMenuItem(tabs[i]);
|
||||
}
|
||||
this._updateTabsVisibilityStatus();
|
||||
]]></handler>
|
||||
@ -3960,14 +3938,12 @@
|
||||
for (let i = this.childNodes.length - 1; i > 0; i--) {
|
||||
let menuItem = this.childNodes[i];
|
||||
if (menuItem.tab) {
|
||||
menuItem.removeEventListener("command", this, false);
|
||||
menuItem.tab.mCorrespondingMenuitem = null;
|
||||
this.removeChild(menuItem);
|
||||
}
|
||||
}
|
||||
var tabcontainer = gBrowser.tabContainer;
|
||||
tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
|
||||
tabcontainer.removeEventListener("TabOpen", this, false);
|
||||
tabcontainer.removeEventListener("TabAttrModified", this, false);
|
||||
tabcontainer.removeEventListener("TabClose", this, false);
|
||||
]]></handler>
|
||||
@ -3988,6 +3964,11 @@
|
||||
XULBrowserWindow.setOverLink("", null);
|
||||
]]></handler>
|
||||
|
||||
<handler event="command"><![CDATA[
|
||||
if (event.target.tab)
|
||||
gBrowser.selectedTab = event.target.tab;
|
||||
]]></handler>
|
||||
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
|
@ -56,7 +56,7 @@ function getTopWin(skipPopups) {
|
||||
// whether it's the frontmost window, since commands can be executed in
|
||||
// background windows (bug 626148).
|
||||
if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
|
||||
(!skipPopups || !top.document.documentElement.getAttribute("chromehidden")))
|
||||
(!skipPopups || top.toolbar.visible))
|
||||
return top;
|
||||
|
||||
if (skipPopups) {
|
||||
@ -206,7 +206,7 @@ function openLinkIn(url, where, params) {
|
||||
|
||||
var w = getTopWin();
|
||||
if ((where == "tab" || where == "tabshifted") &&
|
||||
w && w.document.documentElement.getAttribute("chromehidden")) {
|
||||
w && !w.toolbar.visible) {
|
||||
w = getTopWin(true);
|
||||
aRelatedToCurrent = false;
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1442,7 +1442,7 @@ BrowserGlue.prototype = {
|
||||
getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() {
|
||||
function isFullBrowserWindow(win) {
|
||||
return !win.closed &&
|
||||
!win.document.documentElement.getAttribute("chromehidden");
|
||||
win.toolbar.visible;
|
||||
}
|
||||
|
||||
#ifdef BROKEN_WM_Z_ORDER
|
||||
|
14
configure.in
14
configure.in
@ -1823,10 +1823,24 @@ if test "$OS_TARGET" = "Android"; then
|
||||
LIBS="$LIBS $STLPORT_LIBS"
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl Suppress Clang Argument Warnings
|
||||
dnl ========================================================
|
||||
if test -n "$CLANG_CC"; then
|
||||
_WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
|
||||
CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
|
||||
fi
|
||||
if test -n "$CLANG_CXX"; then
|
||||
_WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}"
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl GNU specific defaults
|
||||
dnl ========================================================
|
||||
if test "$GNU_CC"; then
|
||||
# Per bug 719659 comment 2, some of the headers on ancient build machines
|
||||
# require gnu89 inline semantics. But otherwise, we use C99.
|
||||
CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline"
|
||||
# FIXME: Let us build with strict aliasing. bug 414641.
|
||||
CFLAGS="$CFLAGS -fno-strict-aliasing"
|
||||
MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
|
||||
|
@ -1451,13 +1451,6 @@ public:
|
||||
*/
|
||||
static void AddScriptBlocker();
|
||||
|
||||
/**
|
||||
* Increases the count of blockers preventing scripts from running.
|
||||
* Also, while this script blocker is active, script runners must not be
|
||||
* added --- we'll assert if one is, and ignore it.
|
||||
*/
|
||||
static void AddScriptBlockerAndPreventAddingRunners();
|
||||
|
||||
/**
|
||||
* Decreases the count of blockers preventing scripts from running.
|
||||
* NOTE: You might want to use nsAutoScriptBlocker rather than calling
|
||||
|
@ -260,7 +260,6 @@ PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
|
||||
#endif
|
||||
nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nsnull;
|
||||
PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
|
||||
PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
|
||||
nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
|
||||
|
||||
bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
|
||||
@ -4417,25 +4416,12 @@ nsContentUtils::AddScriptBlocker()
|
||||
++sScriptBlockerCount;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsContentUtils::AddScriptBlockerAndPreventAddingRunners()
|
||||
{
|
||||
AddScriptBlocker();
|
||||
if (sScriptBlockerCountWhereRunnersPrevented == 0) {
|
||||
sScriptBlockerCountWhereRunnersPrevented = sScriptBlockerCount;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsContentUtils::RemoveScriptBlocker()
|
||||
{
|
||||
NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
|
||||
--sScriptBlockerCount;
|
||||
if (sScriptBlockerCount < sScriptBlockerCountWhereRunnersPrevented) {
|
||||
sScriptBlockerCountWhereRunnersPrevented = 0;
|
||||
}
|
||||
if (sScriptBlockerCount) {
|
||||
return;
|
||||
}
|
||||
@ -4469,10 +4455,6 @@ nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
|
||||
}
|
||||
|
||||
if (sScriptBlockerCount) {
|
||||
if (sScriptBlockerCountWhereRunnersPrevented > 0) {
|
||||
NS_ERROR("Adding a script runner when that is prevented!");
|
||||
return false;
|
||||
}
|
||||
return sBlockedScriptRunners->AppendElement(aRunnable) != nsnull;
|
||||
}
|
||||
|
||||
|
@ -7097,8 +7097,16 @@ nsDocument::BlockOnload()
|
||||
// block onload only when there are no script blockers.
|
||||
++mAsyncOnloadBlockCount;
|
||||
if (mAsyncOnloadBlockCount == 1) {
|
||||
nsContentUtils::AddScriptRunner(
|
||||
bool success = nsContentUtils::AddScriptRunner(
|
||||
NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
|
||||
|
||||
// The script runner shouldn't fail to add. But if somebody broke
|
||||
// something and it does, we'll thrash at 100% cpu forever. The best
|
||||
// response is just to ignore the onload blocking request. See bug 579535.
|
||||
if (!success) {
|
||||
NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
|
||||
mAsyncOnloadBlockCount = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -6,4 +6,12 @@
|
||||
<text id="b" x="20" y="20">b</text>
|
||||
<text id="a" x="20" y="30">a</text>
|
||||
<text id="y" x="20" y="40">y</text>
|
||||
<g id="v">
|
||||
<circle cx="100" cy="50" r="5"/>
|
||||
<path d="M 100,100 L 100,200"/>
|
||||
</g>
|
||||
<g id="h">
|
||||
<circle cx="200" cy="50" r="5"/>
|
||||
<path d="M 200,100 L 300,100"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 459 B |
@ -42,6 +42,8 @@ function run()
|
||||
checkBBox("f", 0, 0, 100, 100);
|
||||
checkBBoxHeight("a", "b");
|
||||
checkBBoxHeight("a", "y");
|
||||
checkBBox("v", 95, 45, 10, 155);
|
||||
checkBBox("h", 195, 45, 105, 55);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
@ -309,6 +309,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLBinding)
|
||||
tmp->mInsertionPointTable = nsnull;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLBinding)
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mPrototypeBinding->XBLDocumentInfo()");
|
||||
cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(
|
||||
tmp->mPrototypeBinding->XBLDocumentInfo()));
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContent)
|
||||
|
@ -476,6 +476,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo)
|
||||
if (tmp->mBindingTable) {
|
||||
tmp->mBindingTable->Enumerate(TraverseProtos, &cb);
|
||||
}
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mGlobalObject");
|
||||
cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObject*>(tmp->mGlobalObject));
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
@ -385,6 +385,7 @@ TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure)
|
||||
{
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME((*cb), "proto mInterfaceTable data");
|
||||
cb->NoteXPCOMChild(static_cast<nsISupports*>(aData));
|
||||
return kHashEnumerateNext;
|
||||
}
|
||||
@ -392,9 +393,12 @@ TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure)
|
||||
void
|
||||
nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
|
||||
{
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding");
|
||||
cb.NoteXPCOMChild(mBinding);
|
||||
if (mResources)
|
||||
if (mResources) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mResources mLoader");
|
||||
cb.NoteXPCOMChild(mResources->mLoader);
|
||||
}
|
||||
if (mInsertionPointTable)
|
||||
mInsertionPointTable->Enumerate(TraverseInsertionPoint, &cb);
|
||||
if (mInterfaceTable)
|
||||
|
@ -705,6 +705,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptEventHandlerOwnerTearoff)
|
||||
tmp->mElement = nsnull;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptEventHandlerOwnerTearoff)
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mElement");
|
||||
cb.NoteXPCOMChild(static_cast<nsIContent*>(tmp->mElement));
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@ -2545,12 +2546,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXULPrototypeNode)
|
||||
if (tmp->mType == nsXULPrototypeNode::eType_Element) {
|
||||
nsXULPrototypeElement *elem =
|
||||
static_cast<nsXULPrototypeElement*>(tmp);
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo");
|
||||
cb.NoteXPCOMChild(elem->mNodeInfo);
|
||||
PRUint32 i;
|
||||
for (i = 0; i < elem->mNumAttributes; ++i) {
|
||||
const nsAttrName& name = elem->mAttributes[i].mName;
|
||||
if (!name.IsAtom())
|
||||
if (!name.IsAtom()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mAttributes[i].mName.NodeInfo()");
|
||||
cb.NoteXPCOMChild(name.NodeInfo());
|
||||
}
|
||||
}
|
||||
for (i = 0; i < elem->mChildren.Length(); ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i].get(),
|
||||
|
@ -426,7 +426,8 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
|
||||
aBuffer += toCopy;
|
||||
aCount -= toCopy;
|
||||
}
|
||||
if (mBIH.compression == BI_BITFIELDS && mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH) {
|
||||
if (mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH &&
|
||||
mBIH.compression == BI_BITFIELDS) {
|
||||
mBitFields.red = LITTLE_TO_NATIVE32(*(PRUint32*)mRawBuf);
|
||||
mBitFields.green = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 4));
|
||||
mBitFields.blue = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 8));
|
||||
|
@ -1761,10 +1761,24 @@ if test "$OS_TARGET" = "Android"; then
|
||||
ANDROID_LDFLAGS="$LDFLAGS"
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl Suppress Clang Argument Warnings
|
||||
dnl ========================================================
|
||||
if test -n "$CLANG_CC"; then
|
||||
_WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
|
||||
CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
|
||||
fi
|
||||
if test -n "$CLANG_CXX"; then
|
||||
_WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}"
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl GNU specific defaults
|
||||
dnl ========================================================
|
||||
if test "$GNU_CC"; then
|
||||
# Per bug 719659 comment 2, some of the headers on ancient build machines
|
||||
# require gnu89 inline semantics. But otherwise, we use C99.
|
||||
CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline"
|
||||
MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
|
||||
MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
|
||||
DSO_LDOPTS='-shared'
|
||||
|
@ -82,29 +82,6 @@ BumpChunk::canAlloc(size_t n)
|
||||
return bumped <= limit && bumped > headerBase();
|
||||
}
|
||||
|
||||
bool
|
||||
BumpChunk::canAllocUnaligned(size_t n)
|
||||
{
|
||||
char *bumped = bump + n;
|
||||
return bumped <= limit && bumped > headerBase();
|
||||
}
|
||||
|
||||
void *
|
||||
BumpChunk::tryAllocUnaligned(size_t n)
|
||||
{
|
||||
char *oldBump = bump;
|
||||
char *newBump = bump + n;
|
||||
if (newBump > limit)
|
||||
return NULL;
|
||||
|
||||
if (JS_UNLIKELY(newBump < oldBump))
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(canAllocUnaligned(n)); /* Ensure consistency between "can" and "try". */
|
||||
setBump(newBump);
|
||||
return oldBump;
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace js */
|
||||
|
||||
@ -195,37 +172,3 @@ LifoAlloc::getOrCreateChunk(size_t n)
|
||||
}
|
||||
return newChunk;
|
||||
}
|
||||
|
||||
void *
|
||||
LifoAlloc::allocUnaligned(size_t n)
|
||||
{
|
||||
void *result;
|
||||
if (latest && (result = latest->tryAllocUnaligned(n)))
|
||||
return result;
|
||||
|
||||
return alloc(n);
|
||||
}
|
||||
|
||||
void *
|
||||
LifoAlloc::reallocUnaligned(void *origPtr, size_t origSize, size_t incr)
|
||||
{
|
||||
JS_ASSERT(first && latest);
|
||||
|
||||
/*
|
||||
* Maybe we can grow the latest allocation in a BumpChunk.
|
||||
*
|
||||
* Note: we could also realloc the whole BumpChunk in the !canAlloc
|
||||
* case, but this should not be a frequently hit case.
|
||||
*/
|
||||
if (latest
|
||||
&& origPtr == (char *) latest->mark() - origSize
|
||||
&& latest->canAllocUnaligned(incr)) {
|
||||
JS_ALWAYS_TRUE(allocUnaligned(incr));
|
||||
return origPtr;
|
||||
}
|
||||
|
||||
/* Otherwise, memcpy. */
|
||||
size_t newSize = origSize + incr;
|
||||
void *newPtr = allocUnaligned(newSize);
|
||||
return newPtr ? js_memcpy(newPtr, origPtr, origSize) : NULL;
|
||||
}
|
||||
|
@ -134,7 +134,6 @@ class BumpChunk
|
||||
}
|
||||
|
||||
bool canAlloc(size_t n);
|
||||
bool canAllocUnaligned(size_t n);
|
||||
|
||||
/* Try to perform an allocation of size |n|, return null if not possible. */
|
||||
JS_ALWAYS_INLINE
|
||||
@ -154,8 +153,6 @@ class BumpChunk
|
||||
return aligned;
|
||||
}
|
||||
|
||||
void *tryAllocUnaligned(size_t n);
|
||||
|
||||
void *allocInfallible(size_t n) {
|
||||
void *result = tryAlloc(n);
|
||||
JS_ASSERT(result);
|
||||
@ -320,11 +317,6 @@ class LifoAlloc
|
||||
}
|
||||
|
||||
JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE)
|
||||
|
||||
/* Some legacy clients (ab)use LifoAlloc to act like a vector, see bug 688891. */
|
||||
|
||||
void *allocUnaligned(size_t n);
|
||||
void *reallocUnaligned(void *origPtr, size_t origSize, size_t incr);
|
||||
};
|
||||
|
||||
class LifoAllocScope
|
||||
|
@ -334,11 +334,7 @@ frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
|
||||
len = (cs->length > 0)
|
||||
? (uintN) cs->length
|
||||
: js_GetVariableBytecodeLength(code);
|
||||
if ((cs->format & JOF_SHARPSLOT) ||
|
||||
JOF_TYPE(cs->format) == JOF_LOCAL ||
|
||||
(JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
|
||||
JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
|
||||
JOF_TYPE(cs->format) != JOF_SLOTATOM);
|
||||
if ((cs->format & JOF_SHARPSLOT) || JOF_TYPE(cs->format) == JOF_LOCAL) {
|
||||
slot = GET_SLOTNO(code);
|
||||
if (!(cs->format & JOF_SHARPSLOT))
|
||||
slot += bce.sharpSlots();
|
||||
|
@ -539,7 +539,7 @@ UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, uintN line)
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
EmitTraceOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
|
||||
EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
|
||||
{
|
||||
if (nextpn) {
|
||||
/*
|
||||
@ -557,6 +557,21 @@ EmitTraceOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
|
||||
return Emit1(cx, bce, JSOP_LOOPHEAD);
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
|
||||
{
|
||||
if (nextpn) {
|
||||
/* Update the line number, as for LOOPHEAD. */
|
||||
JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
|
||||
if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
|
||||
nextpn = nextpn->pn_head;
|
||||
if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
|
||||
return false;
|
||||
}
|
||||
|
||||
return Emit1(cx, bce, JSOP_LOOPENTRY) >= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
|
||||
* a type set to store its result.
|
||||
@ -1000,6 +1015,29 @@ EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
|
||||
return EmitIndexOp(cx, op, bce->objectList.index(objbox), bce);
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
|
||||
{
|
||||
const size_t len = 1 + UINT32_INDEX_LEN;
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, len);
|
||||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = jsbytecode(op);
|
||||
SET_UINT32_INDEX(next, index);
|
||||
bce->current->next = next + len;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
CheckTypeSet(cx, bce, op);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitRegExp(JSContext *cx, uint32_t index, BytecodeEmitter *bce)
|
||||
{
|
||||
return EmitIndex32(cx, JSOP_REGEXP, index, bce);
|
||||
}
|
||||
|
||||
/*
|
||||
* What good are ARGNO_LEN and SLOTNO_LEN, you ask? The answer is that, apart
|
||||
* from EmitSlotIndexOp, they abstract out the detail that both are 2, and in
|
||||
@ -1017,8 +1055,7 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, BytecodeEmitter
|
||||
ptrdiff_t off;
|
||||
jsbytecode *pc;
|
||||
|
||||
JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTATOM ||
|
||||
JOF_OPTYPE(op) == JOF_SLOTOBJECT);
|
||||
JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTOBJECT);
|
||||
bigSuffix = EmitBigIndexPrefix(cx, bce, index);
|
||||
if (bigSuffix == JSOP_FALSE)
|
||||
return JS_FALSE;
|
||||
@ -3220,7 +3257,7 @@ EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
|
||||
JS_ASSERT((bce->stackDepth - bce->stackDepth) >= -1);
|
||||
uintN pickDistance = (uintN)((bce->stackDepth + 1) - depthBefore);
|
||||
if (pickDistance > 0) {
|
||||
if (pickDistance > jsbytecode(-1)) {
|
||||
if (pickDistance > UINT8_MAX) {
|
||||
ReportCompileErrorNumber(cx, bce->tokenStream(), pn3, JSREPORT_ERROR,
|
||||
JSMSG_TOO_MANY_LOCALS);
|
||||
return JS_FALSE;
|
||||
@ -4831,7 +4868,7 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
|
||||
top = bce->offset();
|
||||
SET_STATEMENT_TOP(&stmtInfo, top);
|
||||
if (EmitTraceOp(cx, bce, NULL) < 0)
|
||||
if (EmitLoopHead(cx, bce, NULL) < 0)
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -4874,6 +4911,8 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
* Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
|
||||
*/
|
||||
SetJumpOffsetAt(bce, jmp);
|
||||
if (!EmitLoopEntry(cx, bce, NULL))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_MOREITER) < 0)
|
||||
return false;
|
||||
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
|
||||
@ -4978,7 +5017,9 @@ EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
SET_STATEMENT_TOP(&stmtInfo, top);
|
||||
|
||||
/* Emit code for the loop body. */
|
||||
if (EmitTraceOp(cx, bce, forBody) < 0)
|
||||
if (EmitLoopHead(cx, bce, forBody) < 0)
|
||||
return false;
|
||||
if (jmp == -1 && !EmitLoopEntry(cx, bce, forBody))
|
||||
return false;
|
||||
if (!EmitTree(cx, bce, forBody))
|
||||
return false;
|
||||
@ -5026,6 +5067,8 @@ EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
/* Fix up the goto from top to target the loop condition. */
|
||||
JS_ASSERT(jmp >= 0);
|
||||
SetJumpOffsetAt(bce, jmp);
|
||||
if (!EmitLoopEntry(cx, bce, forHead->pn_kid2))
|
||||
return false;
|
||||
|
||||
if (!EmitTree(cx, bce, forHead->pn_kid2))
|
||||
return false;
|
||||
@ -5191,9 +5234,11 @@ EmitDo(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return false;
|
||||
|
||||
/* Compile the loop body. */
|
||||
ptrdiff_t top = EmitTraceOp(cx, bce, pn->pn_left);
|
||||
ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left);
|
||||
if (top < 0)
|
||||
return false;
|
||||
if (!EmitLoopEntry(cx, bce, NULL))
|
||||
return false;
|
||||
|
||||
StmtInfo stmtInfo;
|
||||
PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
|
||||
@ -5259,7 +5304,7 @@ EmitWhile(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
if (jmp < 0)
|
||||
return false;
|
||||
|
||||
top = EmitTraceOp(cx, bce, pn->pn_right);
|
||||
top = EmitLoopHead(cx, bce, pn->pn_right);
|
||||
if (top < 0)
|
||||
return false;
|
||||
|
||||
@ -5267,6 +5312,8 @@ EmitWhile(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
return false;
|
||||
|
||||
SetJumpOffsetAt(bce, jmp);
|
||||
if (!EmitLoopEntry(cx, bce, pn->pn_left))
|
||||
return false;
|
||||
if (!EmitTree(cx, bce, pn->pn_left))
|
||||
return false;
|
||||
|
||||
@ -6497,12 +6544,14 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
ptrdiff_t jmp = EmitJump(cx, bce, JSOP_FILTER, 0);
|
||||
if (jmp < 0)
|
||||
return JS_FALSE;
|
||||
top = EmitTraceOp(cx, bce, pn->pn_right);
|
||||
top = EmitLoopHead(cx, bce, pn->pn_right);
|
||||
if (top < 0)
|
||||
return JS_FALSE;
|
||||
if (!EmitTree(cx, bce, pn->pn_right))
|
||||
return JS_FALSE;
|
||||
SetJumpOffsetAt(bce, jmp);
|
||||
if (!EmitLoopEntry(cx, bce, NULL))
|
||||
return false;
|
||||
if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
@ -6644,7 +6693,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
case PNK_REGEXP:
|
||||
JS_ASSERT(pn->isOp(JSOP_REGEXP));
|
||||
ok = EmitIndexOp(cx, JSOP_REGEXP, bce->regexpList.index(pn->pn_objbox), bce);
|
||||
ok = EmitRegExp(cx, bce->regexpList.index(pn->pn_objbox), bce);
|
||||
break;
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
300
js/src/gc/Root.h
Normal file
300
js/src/gc/Root.h
Normal file
@ -0,0 +1,300 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=78:
|
||||
*
|
||||
* ***** 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 SpiderMonkey global object code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef jsgc_root_h__
|
||||
#define jsgc_root_h__
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsprvtd.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Moving GC Stack Rooting
|
||||
*
|
||||
* A moving GC may change the physical location of GC allocated things, even
|
||||
* when they are rooted, updating all pointers to the thing to refer to its new
|
||||
* location. The GC must therefore know about all live pointers to a thing,
|
||||
* not just one of them, in order to behave correctly.
|
||||
*
|
||||
* The classes below are used to root stack locations whose value may be held
|
||||
* live across a call that can trigger GC (i.e. a call which might allocate any
|
||||
* GC things). For a code fragment such as:
|
||||
*
|
||||
* Foo();
|
||||
* ... = obj->lastProperty();
|
||||
*
|
||||
* If Foo() can trigger a GC, the stack location of obj must be rooted to
|
||||
* ensure that the GC does not move the JSObject referred to by obj without
|
||||
* updating obj's location itself. This rooting must happen regardless of
|
||||
* whether there are other roots which ensure that the object itself will not
|
||||
* be collected.
|
||||
*
|
||||
* If Foo() cannot trigger a GC, and the same holds for all other calls made
|
||||
* between obj's definitions and its last uses, then no rooting is required.
|
||||
*
|
||||
* Several classes are available for rooting stack locations. All are templated
|
||||
* on the type T of the value being rooted, for which RootMethods<T> must
|
||||
* have an instantiation.
|
||||
*
|
||||
* - Root<T> roots an existing stack allocated variable or other location of
|
||||
* type T. This is typically used either when a variable only needs to be
|
||||
* rooted on certain rare paths, or when a function takes a bare GC thing
|
||||
* pointer as an argument and needs to root it. In the latter case a
|
||||
* Handle<T> is generally preferred, see below.
|
||||
*
|
||||
* - RootedVar<T> declares a variable of type T, whose value is always rooted.
|
||||
*
|
||||
* - Handle<T> is a const reference to a Root<T> or RootedVar<T>. Handles are
|
||||
* coerced automatically from such a Root<T> or RootedVar<T>. Functions which
|
||||
* take GC things or values as arguments and need to root those arguments
|
||||
* should generally replace those arguments with handles and avoid any
|
||||
* explicit rooting. This has two benefits. First, when several such
|
||||
* functions call each other then redundant rooting of multiple copies of the
|
||||
* GC thing can be avoided. Second, if the caller does not pass a rooted
|
||||
* value a compile error will be generated, which is quicker and easier to
|
||||
* fix than when relying on a separate rooting analysis.
|
||||
*/
|
||||
|
||||
template <> struct RootMethods<const jsid>
|
||||
{
|
||||
static jsid initial() { return JSID_VOID; }
|
||||
static ThingRootKind kind() { return THING_ROOT_ID; }
|
||||
static bool poisoned(jsid id) { return IsPoisonedId(id); }
|
||||
};
|
||||
|
||||
template <> struct RootMethods<jsid>
|
||||
{
|
||||
static jsid initial() { return JSID_VOID; }
|
||||
static ThingRootKind kind() { return THING_ROOT_ID; }
|
||||
static bool poisoned(jsid id) { return IsPoisonedId(id); }
|
||||
};
|
||||
|
||||
template <> struct RootMethods<const Value>
|
||||
{
|
||||
static Value initial() { return UndefinedValue(); }
|
||||
static ThingRootKind kind() { return THING_ROOT_VALUE; }
|
||||
static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
|
||||
};
|
||||
|
||||
template <> struct RootMethods<Value>
|
||||
{
|
||||
static Value initial() { return UndefinedValue(); }
|
||||
static ThingRootKind kind() { return THING_ROOT_VALUE; }
|
||||
static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RootMethods<T *>
|
||||
{
|
||||
static T *initial() { return NULL; }
|
||||
static ThingRootKind kind() { return T::rootKind(); }
|
||||
static bool poisoned(T *v) { return IsPoisonedPtr(v); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Root a stack location holding a GC thing. This takes a stack pointer
|
||||
* and ensures that throughout its lifetime the referenced variable
|
||||
* will remain pinned against a moving GC.
|
||||
*
|
||||
* It is important to ensure that the location referenced by a Root is
|
||||
* initialized, as otherwise the GC may try to use the the uninitialized value.
|
||||
* It is generally preferable to use either RootedVar for local variables, or
|
||||
* Handle for arguments.
|
||||
*/
|
||||
template <typename T>
|
||||
class Root
|
||||
{
|
||||
public:
|
||||
Root(JSContext *cx, const T *ptr
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
ThingRootKind kind = RootMethods<T>::kind();
|
||||
this->stack = reinterpret_cast<Root<T>**>(&cx->thingGCRooters[kind]);
|
||||
this->prev = *stack;
|
||||
*stack = this;
|
||||
#endif
|
||||
|
||||
JS_ASSERT(!RootMethods<T>::poisoned(*ptr));
|
||||
|
||||
this->ptr = ptr;
|
||||
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
~Root()
|
||||
{
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
JS_ASSERT(*stack == this);
|
||||
*stack = prev;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
Root<T> *previous() { return prev; }
|
||||
#endif
|
||||
|
||||
const T *address() const { return ptr; }
|
||||
|
||||
private:
|
||||
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
Root<T> **stack, *prev;
|
||||
#endif
|
||||
const T *ptr;
|
||||
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
template<typename T> template <typename S>
|
||||
inline
|
||||
Handle<T>::Handle(const Root<S> &root)
|
||||
{
|
||||
testAssign<S>();
|
||||
ptr = reinterpret_cast<const T *>(root.address());
|
||||
}
|
||||
|
||||
typedef Root<JSObject*> RootObject;
|
||||
typedef Root<JSFunction*> RootFunction;
|
||||
typedef Root<Shape*> RootShape;
|
||||
typedef Root<BaseShape*> RootBaseShape;
|
||||
typedef Root<types::TypeObject*> RootTypeObject;
|
||||
typedef Root<JSString*> RootString;
|
||||
typedef Root<JSAtom*> RootAtom;
|
||||
typedef Root<jsid> RootId;
|
||||
typedef Root<Value> RootValue;
|
||||
|
||||
/* Mark a stack location as a root for a rooting analysis. */
|
||||
class CheckRoot
|
||||
{
|
||||
#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS)
|
||||
|
||||
CheckRoot **stack, *prev;
|
||||
const uint8_t *ptr;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
CheckRoot(JSContext *cx, const T *ptr
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
this->stack = &cx->checkGCRooters;
|
||||
this->prev = *stack;
|
||||
*stack = this;
|
||||
this->ptr = static_cast<const uint8_t*>(ptr);
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
~CheckRoot()
|
||||
{
|
||||
JS_ASSERT(*stack == this);
|
||||
*stack = prev;
|
||||
}
|
||||
|
||||
CheckRoot *previous() { return prev; }
|
||||
|
||||
bool contains(const uint8_t *v, size_t len) {
|
||||
return ptr >= v && ptr < v + len;
|
||||
}
|
||||
|
||||
#else /* DEBUG && JSGC_ROOT_ANALYSIS */
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
CheckRoot(JSContext *cx, const T *ptr
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
|
||||
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
/* Make a local variable which stays rooted throughout its lifetime. */
|
||||
template <typename T>
|
||||
class RootedVar
|
||||
{
|
||||
public:
|
||||
RootedVar(JSContext *cx)
|
||||
: ptr(RootMethods<T>::initial()), root(cx, &ptr)
|
||||
{}
|
||||
|
||||
RootedVar(JSContext *cx, T initial)
|
||||
: ptr(initial), root(cx, &ptr)
|
||||
{}
|
||||
|
||||
operator T () { return ptr; }
|
||||
T operator ->() { return ptr; }
|
||||
T * address() { return &ptr; }
|
||||
const T * address() const { return &ptr; }
|
||||
T raw() { return ptr; }
|
||||
|
||||
T & operator =(T value)
|
||||
{
|
||||
JS_ASSERT(!RootMethods<T>::poisoned(value));
|
||||
ptr = value;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T ptr;
|
||||
Root<T> root;
|
||||
};
|
||||
|
||||
template <typename T> template <typename S>
|
||||
inline
|
||||
Handle<T>::Handle(const RootedVar<S> &root)
|
||||
{
|
||||
ptr = reinterpret_cast<const T *>(root.address());
|
||||
}
|
||||
|
||||
typedef RootedVar<JSObject*> RootedVarObject;
|
||||
typedef RootedVar<JSFunction*> RootedVarFunction;
|
||||
typedef RootedVar<Shape*> RootedVarShape;
|
||||
typedef RootedVar<BaseShape*> RootedVarBaseShape;
|
||||
typedef RootedVar<types::TypeObject*> RootedVarTypeObject;
|
||||
typedef RootedVar<JSString*> RootedVarString;
|
||||
typedef RootedVar<JSAtom*> RootedVarAtom;
|
||||
typedef RootedVar<jsid> RootedVarId;
|
||||
typedef RootedVar<Value> RootedVarValue;
|
||||
|
||||
} /* namespace js */
|
||||
#endif /* jsgc_root_h___ */
|
4
js/src/jit-test/tests/basic/bug716013.js
Normal file
4
js/src/jit-test/tests/basic/bug716013.js
Normal file
@ -0,0 +1,4 @@
|
||||
f = (function() {
|
||||
for (x in [arguments, arguments]) yield(gczeal(4, function(){}))
|
||||
})
|
||||
for (i in f()) {}
|
9
js/src/jit-test/tests/basic/terminate.js
Normal file
9
js/src/jit-test/tests/basic/terminate.js
Normal file
@ -0,0 +1,9 @@
|
||||
try {
|
||||
terminate();
|
||||
assertEq("execution continued", "execution should not continue");
|
||||
} catch (x) {
|
||||
assertEq("caught exception", "uncatchable");
|
||||
} finally {
|
||||
assertEq("'finally' clause ran", "'finally' clause should not run");
|
||||
}
|
||||
assertEq("top-level execution continued", "top-level execution should not continue");
|
18
js/src/jit-test/tests/debug/onExceptionUnwind-08.js
Normal file
18
js/src/jit-test/tests/debug/onExceptionUnwind-08.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind
|
||||
// throws an uncaught exception.
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = Debugger(g);
|
||||
var frame;
|
||||
dbg.onExceptionUnwind = function (f, x) {
|
||||
frame = f;
|
||||
assertEq(frame.live, true);
|
||||
throw 'unhandled';
|
||||
};
|
||||
dbg.onDebuggerStatement = function(f) {
|
||||
assertEq(f.eval('throw 42'), null);
|
||||
assertEq(frame.live, false);
|
||||
};
|
||||
g.eval('debugger');
|
||||
|
||||
// Don't fail just because we reported an uncaught exception.
|
||||
quit(0);
|
15
js/src/jit-test/tests/debug/onExceptionUnwind-09.js
Normal file
15
js/src/jit-test/tests/debug/onExceptionUnwind-09.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind
|
||||
// terminates execution.
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = Debugger(g);
|
||||
var frame;
|
||||
dbg.onExceptionUnwind = function (f, x) {
|
||||
frame = f;
|
||||
assertEq(frame.live, true);
|
||||
return null;
|
||||
};
|
||||
dbg.onDebuggerStatement = function(f) {
|
||||
assertEq(f.eval('throw 42'), null);
|
||||
assertEq(frame.live, false);
|
||||
};
|
||||
g.eval('debugger');
|
16
js/src/jit-test/tests/debug/onExceptionUnwind-10.js
Normal file
16
js/src/jit-test/tests/debug/onExceptionUnwind-10.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind
|
||||
// terminates execution.
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = Debugger(g);
|
||||
var frame;
|
||||
dbg.onExceptionUnwind = function (f, x) {
|
||||
frame = f;
|
||||
assertEq(frame.type, 'eval');
|
||||
assertEq(frame.live, true);
|
||||
terminate();
|
||||
};
|
||||
dbg.onDebuggerStatement = function(f) {
|
||||
assertEq(f.eval('throw 42'), null);
|
||||
assertEq(frame.live, false);
|
||||
};
|
||||
g.eval('debugger');
|
19
js/src/jit-test/tests/jaeger/bug719918.js
Normal file
19
js/src/jit-test/tests/jaeger/bug719918.js
Normal file
@ -0,0 +1,19 @@
|
||||
function test(m) {
|
||||
do {
|
||||
if (m = arr[0]) break;
|
||||
m = 0;
|
||||
}
|
||||
while (0);
|
||||
arr[1] = m;
|
||||
}
|
||||
|
||||
mjitChunkLimit(10);
|
||||
|
||||
arr = new Float64Array(2);
|
||||
|
||||
// run function a lot to trigger methodjit compile
|
||||
for(var i=0; i<200; i++)
|
||||
test(0);
|
||||
|
||||
// should return 0, not NaN
|
||||
assertEq(arr[1], 0)
|
@ -57,11 +57,11 @@ void
|
||||
PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
printf("#%u:", script->id());
|
||||
LifoAlloc lifoAlloc(1024);
|
||||
Sprinter sprinter;
|
||||
INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0);
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return;
|
||||
js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter);
|
||||
fprintf(stdout, "%s", sprinter.base);
|
||||
fprintf(stdout, "%s", sprinter.string());
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -815,7 +815,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx)
|
||||
|
||||
jsbytecode *entrypc = script->code + entry;
|
||||
|
||||
if (JSOp(*entrypc) == JSOP_GOTO)
|
||||
if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER)
|
||||
loop->entry = entry + GET_JUMP_OFFSET(entrypc);
|
||||
else
|
||||
loop->entry = targetOffset;
|
||||
@ -823,6 +823,8 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx)
|
||||
/* Do-while loop at the start of the script. */
|
||||
loop->entry = targetOffset;
|
||||
}
|
||||
JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD ||
|
||||
script->code[loop->entry] == JSOP_LOOPENTRY);
|
||||
} else {
|
||||
for (unsigned i = 0; i < savedCount; i++) {
|
||||
LifetimeVariable &var = *saved[i];
|
||||
|
@ -5600,7 +5600,7 @@ JS_NewStringCopyZ(JSContext *cx, const char *s)
|
||||
|
||||
AssertNoGC(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
if (!s)
|
||||
if (!s || !*s)
|
||||
return cx->runtime->emptyString;
|
||||
n = strlen(s);
|
||||
js = InflateString(cx, s, &n);
|
||||
@ -6714,79 +6714,6 @@ js_GetCompartmentPrivate(JSCompartment *compartment)
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_RegisterReference(void **ref)
|
||||
{
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_ModifyReference(void **ref, void *newval)
|
||||
{
|
||||
// XPConnect uses the lower bits of its JSObject refs for evil purposes,
|
||||
// so we need to fix this.
|
||||
void *thing = *ref;
|
||||
*ref = newval;
|
||||
thing = (void *)((uintptr_t)thing & ~7);
|
||||
if (!thing)
|
||||
return;
|
||||
JS_ASSERT(!static_cast<gc::Cell *>(thing)->compartment()->rt->gcRunning);
|
||||
uint32_t kind = GetGCThingTraceKind(thing);
|
||||
if (kind == JSTRACE_OBJECT)
|
||||
JSObject::writeBarrierPre((JSObject *) thing);
|
||||
else if (kind == JSTRACE_STRING)
|
||||
JSString::writeBarrierPre((JSString *) thing);
|
||||
else
|
||||
JS_NOT_REACHED("invalid trace kind");
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_UnregisterReference(void **ref)
|
||||
{
|
||||
// For now we just want to trigger a write barrier.
|
||||
JS_ModifyReference(ref, NULL);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_UnregisterReferenceRT(JSRuntime *rt, void **ref)
|
||||
{
|
||||
// For now we just want to trigger a write barrier.
|
||||
if (!rt->gcRunning)
|
||||
JS_ModifyReference(ref, NULL);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_RegisterValue(jsval *val)
|
||||
{
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_ModifyValue(jsval *val, jsval newval)
|
||||
{
|
||||
HeapValue::writeBarrierPre(*val);
|
||||
*val = newval;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_UnregisterValue(jsval *val)
|
||||
{
|
||||
JS_ModifyValue(val, JSVAL_VOID);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_UnregisterValueRT(JSRuntime *rt, jsval *val)
|
||||
{
|
||||
if (!rt->gcRunning)
|
||||
JS_ModifyValue(val, JSVAL_VOID);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSTracer *)
|
||||
JS_GetIncrementalGCTracer(JSRuntime *rt)
|
||||
{
|
||||
return rt->gcIncrementalTracer;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
#if !defined(STATIC_EXPORTABLE_JS_API) && !defined(STATIC_JS_API) && defined(XP_WIN)
|
||||
|
||||
#include "jswin.h"
|
||||
|
@ -3227,105 +3227,6 @@ JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, JSGCTraceKind kind,
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Write barrier API.
|
||||
*
|
||||
* This API is used to inform SpiderMonkey of pointers to JS GC things in the
|
||||
* malloc heap. There is no need to use this API unless incremental GC is
|
||||
* enabled. When they are, the requirements for using the API are as follows:
|
||||
*
|
||||
* All pointers to JS GC things from the malloc heap must be registered and
|
||||
* unregistered with the API functions below. This is *in addition* to the
|
||||
* normal rooting and tracing that must be done normally--these functions will
|
||||
* not take care of rooting for you.
|
||||
*
|
||||
* Besides registration, the JS_ModifyReference function must be called to
|
||||
* change the value of these references. You should not change them using
|
||||
* assignment.
|
||||
*
|
||||
* Only the RT versions of these functions (which take a JSRuntime argument)
|
||||
* should be called during GC. Without a JSRuntime, it is not possible to know
|
||||
* if the object being barriered has already been finalized.
|
||||
*
|
||||
* To avoid the headache of using these API functions, the JSBarrieredObjectPtr
|
||||
* C++ class is provided--simply replace your JSObject* with a
|
||||
* JSBarrieredObjectPtr. It will take care of calling the registration and
|
||||
* modification APIs.
|
||||
*
|
||||
* For more explanation, see the comment in gc/Barrier.h.
|
||||
*/
|
||||
|
||||
/* These functions are to be used for objects and strings. */
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_RegisterReference(void **ref);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ModifyReference(void **ref, void *newval);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_UnregisterReference(void **ref);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_UnregisterReferenceRT(JSRuntime *rt, void **ref);
|
||||
|
||||
/* These functions are for values. */
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_RegisterValue(jsval *val);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ModifyValue(jsval *val, jsval newval);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_UnregisterValue(jsval *val);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_UnregisterValueRT(JSRuntime *rt, jsval *val);
|
||||
|
||||
extern JS_PUBLIC_API(JSTracer *)
|
||||
JS_GetIncrementalGCTracer(JSRuntime *rt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
JS_END_EXTERN_C
|
||||
|
||||
namespace JS {
|
||||
|
||||
class HeapPtrObject
|
||||
{
|
||||
JSObject *value;
|
||||
|
||||
public:
|
||||
HeapPtrObject() : value(NULL) { JS_RegisterReference((void **) &value); }
|
||||
|
||||
HeapPtrObject(JSObject *obj) : value(obj) { JS_RegisterReference((void **) &value); }
|
||||
|
||||
/* Always call finalize before the destructor. */
|
||||
~HeapPtrObject() { JS_ASSERT(!value); }
|
||||
|
||||
void finalize(JSRuntime *rt) {
|
||||
JS_UnregisterReferenceRT(rt, (void **) &value);
|
||||
value = NULL;
|
||||
}
|
||||
void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); }
|
||||
|
||||
void init(JSObject *obj) { value = obj; }
|
||||
|
||||
JSObject *get() const { return value; }
|
||||
|
||||
HeapPtrObject &operator=(JSObject *obj) {
|
||||
JS_ModifyReference((void **) &value, obj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSObject &operator*() const { return *value; }
|
||||
JSObject *operator->() const { return value; }
|
||||
operator JSObject *() const { return value; }
|
||||
};
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Garbage collector API.
|
||||
*/
|
||||
|
@ -1625,15 +1625,36 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
||||
StringBuffer sb(cx);
|
||||
|
||||
if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
|
||||
/* Elements beyond the initialized length are 'undefined' and thus can be ignored. */
|
||||
const Value *beg = obj->getDenseArrayElements();
|
||||
const Value *end = beg + Min(length, obj->getDenseArrayInitializedLength());
|
||||
for (const Value *vp = beg; vp != end; ++vp) {
|
||||
const Value *start = obj->getDenseArrayElements();
|
||||
const Value *end = start + obj->getDenseArrayInitializedLength();
|
||||
const Value *elem;
|
||||
for (elem = start; elem < end; elem++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
if (!vp->isMagic(JS_ARRAY_HOLE) && !vp->isNullOrUndefined()) {
|
||||
if (!ValueToStringBuffer(cx, *vp, sb))
|
||||
/*
|
||||
* Object stringifying is slow; delegate it to a separate loop to
|
||||
* keep this one tight.
|
||||
*/
|
||||
if (elem->isObject())
|
||||
break;
|
||||
|
||||
if (!elem->isMagic(JS_ARRAY_HOLE) && !elem->isNullOrUndefined()) {
|
||||
if (!ValueToStringBuffer(cx, *elem, sb))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = uint32_t(PointerRangeSize(start, elem)); i < length; i++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
JSBool hole;
|
||||
Value v;
|
||||
if (!GetElement(cx, obj, i, &hole, &v))
|
||||
return false;
|
||||
if (!hole && !v.isNullOrUndefined()) {
|
||||
if (!ValueToStringBuffer(cx, v, sb))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1981,7 +2002,89 @@ CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrE
|
||||
return true;
|
||||
}
|
||||
|
||||
struct SortComparatorStrings {
|
||||
static int32_t const powersOf10Int32[] = {
|
||||
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
|
||||
};
|
||||
|
||||
static inline int
|
||||
NumDigitsBase10(int32_t n)
|
||||
{
|
||||
/*
|
||||
* This is just floor_log10(n) + 1
|
||||
* Algorithm taken from
|
||||
* http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
|
||||
*/
|
||||
int32_t log2, t;
|
||||
JS_CEILING_LOG2(log2, n);
|
||||
t = log2 * 1233 >> 12;
|
||||
return t - (n < powersOf10Int32[t]) + 1;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CompareLexicographicInt32(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
|
||||
{
|
||||
int32_t aint = a.toInt32();
|
||||
int32_t bint = b.toInt32();
|
||||
|
||||
/*
|
||||
* If both numbers are equal ... trivial
|
||||
* If only one of both is negative --> arithmetic comparison as char code
|
||||
* of '-' is always less than any other digit
|
||||
* If both numbers are negative convert them to positive and continue
|
||||
* handling ...
|
||||
*/
|
||||
if (aint == bint) {
|
||||
*lessOrEqualp = true;
|
||||
} else if ((aint < 0) && (bint >= 0)) {
|
||||
*lessOrEqualp = true;
|
||||
} else if ((aint >= 0) && (bint < 0)) {
|
||||
*lessOrEqualp = false;
|
||||
} else if (bint == INT32_MIN) { /* a is negative too --> a <= b */
|
||||
*lessOrEqualp = true;
|
||||
} else if (aint == INT32_MIN) { /* b is negative too but not INT32_MIN --> a > b */
|
||||
*lessOrEqualp = false;
|
||||
} else {
|
||||
if (aint < 0) { /* b is also negative */
|
||||
aint = -aint;
|
||||
bint = -bint;
|
||||
}
|
||||
|
||||
/*
|
||||
* ... get number of digits of both integers.
|
||||
* If they have the same number of digits --> arithmetic comparison.
|
||||
* If digits_a > digits_b: a < b*10e(digits_a - digits_b).
|
||||
* If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
|
||||
*/
|
||||
int digitsa = NumDigitsBase10(aint);
|
||||
int digitsb = NumDigitsBase10(bint);
|
||||
if (digitsa == digitsb)
|
||||
*lessOrEqualp = (aint <= bint);
|
||||
else if (digitsa > digitsb)
|
||||
*lessOrEqualp = (aint < bint*powersOf10Int32[digitsa - digitsb]);
|
||||
else /* if (digitsb > digitsa) */
|
||||
*lessOrEqualp = (aint*powersOf10Int32[digitsb - digitsa] <= bint);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1,
|
||||
const jschar *s2, size_t l2, bool *lessOrEqualp)
|
||||
{
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
int32_t result;
|
||||
if (!s1 || !s2 || !CompareChars(s1, l1, s2, l2, &result))
|
||||
return false;
|
||||
|
||||
*lessOrEqualp = (result <= 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct SortComparatorStrings
|
||||
{
|
||||
JSContext *const cx;
|
||||
|
||||
SortComparatorStrings(JSContext *cx)
|
||||
@ -1992,23 +2095,42 @@ struct SortComparatorStrings {
|
||||
}
|
||||
};
|
||||
|
||||
struct StringValuePair {
|
||||
Value str;
|
||||
Value v;
|
||||
};
|
||||
|
||||
struct SortComparatorStringValuePairs {
|
||||
struct SortComparatorLexicographicInt32
|
||||
{
|
||||
JSContext *const cx;
|
||||
|
||||
SortComparatorStringValuePairs(JSContext *cx)
|
||||
SortComparatorLexicographicInt32(JSContext *cx)
|
||||
: cx(cx) {}
|
||||
|
||||
bool operator()(const StringValuePair &a, const StringValuePair &b, bool *lessOrEqualp) {
|
||||
return CompareStringValues(cx, a.str, b.str, lessOrEqualp);
|
||||
bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
|
||||
return CompareLexicographicInt32(cx, a, b, lessOrEqualp);
|
||||
}
|
||||
};
|
||||
|
||||
struct SortComparatorFunction {
|
||||
struct StringifiedElement
|
||||
{
|
||||
size_t charsBegin;
|
||||
size_t charsEnd;
|
||||
size_t elementIndex;
|
||||
};
|
||||
|
||||
struct SortComparatorStringifiedElements
|
||||
{
|
||||
JSContext *const cx;
|
||||
const StringBuffer &sb;
|
||||
|
||||
SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb)
|
||||
: cx(cx), sb(sb) {}
|
||||
|
||||
bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) {
|
||||
return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin,
|
||||
sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin,
|
||||
lessOrEqualp);
|
||||
}
|
||||
};
|
||||
|
||||
struct SortComparatorFunction
|
||||
{
|
||||
JSContext *const cx;
|
||||
const Value &fval;
|
||||
InvokeArgsGuard &ag;
|
||||
@ -2123,6 +2245,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
*/
|
||||
undefs = 0;
|
||||
bool allStrings = true;
|
||||
bool allInts = true;
|
||||
for (jsuint i = 0; i < len; i++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
@ -2140,6 +2263,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
vec.infallibleAppend(v);
|
||||
allStrings = allStrings && v.isString();
|
||||
allInts = allInts && v.isInt32();
|
||||
}
|
||||
|
||||
n = vec.length();
|
||||
@ -2151,6 +2275,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
JS_ALWAYS_TRUE(vec.resize(n * 2));
|
||||
|
||||
/* Here len == n + undefs + number_of_holes. */
|
||||
Value *result = vec.begin();
|
||||
if (fval.isNull()) {
|
||||
/*
|
||||
* Sort using the default comparator converting all elements to
|
||||
@ -2159,69 +2284,60 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
if (allStrings) {
|
||||
if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
|
||||
return false;
|
||||
} else if (allInts) {
|
||||
if (!MergeSort(vec.begin(), n, vec.begin() + n,
|
||||
SortComparatorLexicographicInt32(cx))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* To avoid string conversion on each compare we do it only once
|
||||
* prior to sorting. But we also need the space for the original
|
||||
* values to recover the sorting result. For that we move the
|
||||
* original values to the odd indexes in vec, put the string
|
||||
* conversion results in the even indexes and do the merge sort
|
||||
* over resulting string-value pairs using an extra allocated
|
||||
* scratch space.
|
||||
* Convert all elements to a jschar array in StringBuffer.
|
||||
* Store the index and length of each stringified element with
|
||||
* the corresponding index of the element in the array. Sort
|
||||
* the stringified elements and with this result order the
|
||||
* original array.
|
||||
*/
|
||||
size_t i = n;
|
||||
do {
|
||||
--i;
|
||||
StringBuffer sb(cx);
|
||||
Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
|
||||
if (!strElements.reserve(2 * n))
|
||||
return false;
|
||||
|
||||
int cursor = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
const Value &v = vec[i];
|
||||
JSString *str = ToString(cx, v);
|
||||
if (!str)
|
||||
|
||||
if (!ValueToStringBuffer(cx, vec[i], sb))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Copying v must come first, because the following line
|
||||
* overwrites v when i == 0.
|
||||
*/
|
||||
vec[2 * i + 1] = v;
|
||||
vec[2 * i].setString(str);
|
||||
} while (i != 0);
|
||||
StringifiedElement el = { cursor, sb.length(), i };
|
||||
strElements.infallibleAppend(el);
|
||||
cursor = sb.length();
|
||||
}
|
||||
|
||||
AutoValueVector extraScratch(cx);
|
||||
if (!extraScratch.resize(n * 2))
|
||||
return false;
|
||||
if (!MergeSort(reinterpret_cast<StringValuePair *>(vec.begin()), n,
|
||||
reinterpret_cast<StringValuePair *>(extraScratch.begin()),
|
||||
SortComparatorStringValuePairs(cx))) {
|
||||
/* Resize strElements so we can perform the sorting */
|
||||
JS_ALWAYS_TRUE(strElements.resize(2 * n));
|
||||
|
||||
if (!MergeSort(strElements.begin(), n, strElements.begin() + n,
|
||||
SortComparatorStringifiedElements(cx, sb))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to unroot the cached results of toString calls
|
||||
* before the operation callback has a chance to run the GC.
|
||||
* So we do not call JS_CHECK_OPERATION_LIMIT in the loop.
|
||||
*/
|
||||
i = 0;
|
||||
do {
|
||||
vec[i] = vec[2 * i + 1];
|
||||
} while (++i != n);
|
||||
/* Order vec[n:2n-1] using strElements.index */
|
||||
for (size_t i = 0; i < n; i ++)
|
||||
vec[n + i] = vec[strElements[i].elementIndex];
|
||||
|
||||
result = vec.begin() + n;
|
||||
}
|
||||
} else {
|
||||
InvokeArgsGuard args;
|
||||
if (!MergeSort(vec.begin(), n, vec.begin() + n,
|
||||
SortComparatorFunction(cx, fval, args)))
|
||||
{
|
||||
SortComparatorFunction(cx, fval, args))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We no longer need to root the scratch space for the merge sort, so
|
||||
* unroot it now to make the job of a potential GC under
|
||||
* InitArrayElements easier.
|
||||
*/
|
||||
vec.resize(n);
|
||||
if (!InitArrayElements(cx, obj, 0, jsuint(n), vec.begin(), DontUpdateTypes))
|
||||
if (!InitArrayElements(cx, obj, 0, jsuint(n), result, DontUpdateTypes))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
263
js/src/jscntxt.h
263
js/src/jscntxt.h
@ -64,7 +64,7 @@
|
||||
#include "gc/Statistics.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "js/Vector.h"
|
||||
#include "vm/StackSpace.h"
|
||||
#include "vm/Stack.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
@ -921,11 +921,11 @@ struct JSContext
|
||||
js::ContextStack stack;
|
||||
|
||||
/* ContextStack convenience functions */
|
||||
inline bool hasfp() const;
|
||||
inline js::StackFrame* fp() const;
|
||||
inline js::StackFrame* maybefp() const;
|
||||
inline js::FrameRegs& regs() const;
|
||||
inline js::FrameRegs* maybeRegs() const;
|
||||
inline bool hasfp() const { return stack.hasfp(); }
|
||||
inline js::StackFrame* fp() const { return stack.fp(); }
|
||||
inline js::StackFrame* maybefp() const { return stack.maybefp(); }
|
||||
inline js::FrameRegs& regs() const { return stack.regs(); }
|
||||
inline js::FrameRegs* maybeRegs() const { return stack.maybeRegs(); }
|
||||
|
||||
/* Set cx->compartment based on the current scope chain. */
|
||||
void resetCompartment();
|
||||
@ -1306,257 +1306,6 @@ struct AutoResolving {
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
/*
|
||||
* Moving GC Stack Rooting
|
||||
*
|
||||
* A moving GC may change the physical location of GC allocated things, even
|
||||
* when they are rooted, updating all pointers to the thing to refer to its new
|
||||
* location. The GC must therefore know about all live pointers to a thing,
|
||||
* not just one of them, in order to behave correctly.
|
||||
*
|
||||
* The classes below are used to root stack locations whose value may be held
|
||||
* live across a call that can trigger GC (i.e. a call which might allocate any
|
||||
* GC things). For a code fragment such as:
|
||||
*
|
||||
* Foo();
|
||||
* ... = obj->lastProperty();
|
||||
*
|
||||
* If Foo() can trigger a GC, the stack location of obj must be rooted to
|
||||
* ensure that the GC does not move the JSObject referred to by obj without
|
||||
* updating obj's location itself. This rooting must happen regardless of
|
||||
* whether there are other roots which ensure that the object itself will not
|
||||
* be collected.
|
||||
*
|
||||
* If Foo() cannot trigger a GC, and the same holds for all other calls made
|
||||
* between obj's definitions and its last uses, then no rooting is required.
|
||||
*
|
||||
* Several classes are available for rooting stack locations. All are templated
|
||||
* on the type T of the value being rooted, for which RootMethods<T> must
|
||||
* have an instantiation.
|
||||
*
|
||||
* - Root<T> roots an existing stack allocated variable or other location of
|
||||
* type T. This is typically used either when a variable only needs to be
|
||||
* rooted on certain rare paths, or when a function takes a bare GC thing
|
||||
* pointer as an argument and needs to root it. In the latter case a
|
||||
* Handle<T> is generally preferred, see below.
|
||||
*
|
||||
* - RootedVar<T> declares a variable of type T, whose value is always rooted.
|
||||
*
|
||||
* - Handle<T> is a const reference to a Root<T> or RootedVar<T>. Handles are
|
||||
* coerced automatically from such a Root<T> or RootedVar<T>. Functions which
|
||||
* take GC things or values as arguments and need to root those arguments
|
||||
* should generally replace those arguments with handles and avoid any
|
||||
* explicit rooting. This has two benefits. First, when several such
|
||||
* functions call each other then redundant rooting of multiple copies of the
|
||||
* GC thing can be avoided. Second, if the caller does not pass a rooted
|
||||
* value a compile error will be generated, which is quicker and easier to
|
||||
* fix than when relying on a separate rooting analysis.
|
||||
*/
|
||||
|
||||
template <> struct RootMethods<const jsid>
|
||||
{
|
||||
static jsid initial() { return JSID_VOID; }
|
||||
static ThingRootKind kind() { return THING_ROOT_ID; }
|
||||
static bool poisoned(jsid id) { return IsPoisonedId(id); }
|
||||
};
|
||||
|
||||
template <> struct RootMethods<jsid>
|
||||
{
|
||||
static jsid initial() { return JSID_VOID; }
|
||||
static ThingRootKind kind() { return THING_ROOT_ID; }
|
||||
static bool poisoned(jsid id) { return IsPoisonedId(id); }
|
||||
};
|
||||
|
||||
template <> struct RootMethods<const Value>
|
||||
{
|
||||
static Value initial() { return UndefinedValue(); }
|
||||
static ThingRootKind kind() { return THING_ROOT_VALUE; }
|
||||
static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
|
||||
};
|
||||
|
||||
template <> struct RootMethods<Value>
|
||||
{
|
||||
static Value initial() { return UndefinedValue(); }
|
||||
static ThingRootKind kind() { return THING_ROOT_VALUE; }
|
||||
static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RootMethods<T *>
|
||||
{
|
||||
static T *initial() { return NULL; }
|
||||
static ThingRootKind kind() { return T::rootKind(); }
|
||||
static bool poisoned(T *v) { return IsPoisonedPtr(v); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Root a stack location holding a GC thing. This takes a stack pointer
|
||||
* and ensures that throughout its lifetime the referenced variable
|
||||
* will remain pinned against a moving GC.
|
||||
*
|
||||
* It is important to ensure that the location referenced by a Root is
|
||||
* initialized, as otherwise the GC may try to use the the uninitialized value.
|
||||
* It is generally preferable to use either RootedVar for local variables, or
|
||||
* Handle for arguments.
|
||||
*/
|
||||
template <typename T>
|
||||
class Root
|
||||
{
|
||||
public:
|
||||
Root(JSContext *cx, const T *ptr
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
ThingRootKind kind = RootMethods<T>::kind();
|
||||
this->stack = reinterpret_cast<Root<T>**>(&cx->thingGCRooters[kind]);
|
||||
this->prev = *stack;
|
||||
*stack = this;
|
||||
#endif
|
||||
|
||||
JS_ASSERT(!RootMethods<T>::poisoned(*ptr));
|
||||
|
||||
this->ptr = ptr;
|
||||
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
~Root()
|
||||
{
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
JS_ASSERT(*stack == this);
|
||||
*stack = prev;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
Root<T> *previous() { return prev; }
|
||||
#endif
|
||||
|
||||
const T *address() const { return ptr; }
|
||||
|
||||
private:
|
||||
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
Root<T> **stack, *prev;
|
||||
#endif
|
||||
const T *ptr;
|
||||
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
template<typename T> template <typename S>
|
||||
inline
|
||||
Handle<T>::Handle(const Root<S> &root)
|
||||
{
|
||||
testAssign<S>();
|
||||
ptr = reinterpret_cast<const T *>(root.address());
|
||||
}
|
||||
|
||||
typedef Root<JSObject*> RootObject;
|
||||
typedef Root<JSFunction*> RootFunction;
|
||||
typedef Root<Shape*> RootShape;
|
||||
typedef Root<BaseShape*> RootBaseShape;
|
||||
typedef Root<types::TypeObject*> RootTypeObject;
|
||||
typedef Root<JSString*> RootString;
|
||||
typedef Root<JSAtom*> RootAtom;
|
||||
typedef Root<jsid> RootId;
|
||||
typedef Root<Value> RootValue;
|
||||
|
||||
/* Mark a stack location as a root for a rooting analysis. */
|
||||
class CheckRoot
|
||||
{
|
||||
#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS)
|
||||
|
||||
CheckRoot **stack, *prev;
|
||||
const uint8_t *ptr;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
CheckRoot(JSContext *cx, const T *ptr
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
this->stack = &cx->checkGCRooters;
|
||||
this->prev = *stack;
|
||||
*stack = this;
|
||||
this->ptr = static_cast<const uint8_t*>(ptr);
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
~CheckRoot()
|
||||
{
|
||||
JS_ASSERT(*stack == this);
|
||||
*stack = prev;
|
||||
}
|
||||
|
||||
CheckRoot *previous() { return prev; }
|
||||
|
||||
bool contains(const uint8_t *v, size_t len) {
|
||||
return ptr >= v && ptr < v + len;
|
||||
}
|
||||
|
||||
#else /* DEBUG && JSGC_ROOT_ANALYSIS */
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
CheckRoot(JSContext *cx, const T *ptr
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
{
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
|
||||
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
/* Make a local variable which stays rooted throughout its lifetime. */
|
||||
template <typename T>
|
||||
class RootedVar
|
||||
{
|
||||
public:
|
||||
RootedVar(JSContext *cx)
|
||||
: ptr(RootMethods<T>::initial()), root(cx, &ptr)
|
||||
{}
|
||||
|
||||
RootedVar(JSContext *cx, T initial)
|
||||
: ptr(initial), root(cx, &ptr)
|
||||
{}
|
||||
|
||||
operator T () { return ptr; }
|
||||
T operator ->() { return ptr; }
|
||||
T * address() { return &ptr; }
|
||||
const T * address() const { return &ptr; }
|
||||
T raw() { return ptr; }
|
||||
|
||||
T & operator =(T value)
|
||||
{
|
||||
JS_ASSERT(!RootMethods<T>::poisoned(value));
|
||||
ptr = value;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T ptr;
|
||||
Root<T> root;
|
||||
};
|
||||
|
||||
template <typename T> template <typename S>
|
||||
inline
|
||||
Handle<T>::Handle(const RootedVar<S> &root)
|
||||
{
|
||||
ptr = reinterpret_cast<const T *>(root.address());
|
||||
}
|
||||
|
||||
typedef RootedVar<JSObject*> RootedVarObject;
|
||||
typedef RootedVar<JSFunction*> RootedVarFunction;
|
||||
typedef RootedVar<Shape*> RootedVarShape;
|
||||
typedef RootedVar<BaseShape*> RootedVarBaseShape;
|
||||
typedef RootedVar<types::TypeObject*> RootedVarTypeObject;
|
||||
typedef RootedVar<JSString*> RootedVarString;
|
||||
typedef RootedVar<JSAtom*> RootedVarAtom;
|
||||
typedef RootedVar<jsid> RootedVarId;
|
||||
typedef RootedVar<Value> RootedVarValue;
|
||||
|
||||
#ifdef JS_HAS_XML_SUPPORT
|
||||
class AutoXMLRooter : private AutoGCRooter {
|
||||
public:
|
||||
|
@ -1620,13 +1620,13 @@ JS_PUBLIC_API(void)
|
||||
JS_DumpBytecode(JSContext *cx, JSScript *script)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
LifoAlloc lifoAlloc(1024);
|
||||
Sprinter sprinter;
|
||||
INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0);
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return;
|
||||
|
||||
fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno);
|
||||
js_Disassemble(cx, script, true, &sprinter);
|
||||
fputs(sprinter.base, stdout);
|
||||
fputs(sprinter.string(), stdout);
|
||||
fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
|
||||
#endif
|
||||
}
|
||||
@ -1637,13 +1637,13 @@ JS_DumpPCCounts(JSContext *cx, JSScript *script)
|
||||
#if defined(DEBUG)
|
||||
JS_ASSERT(script->pcCounters);
|
||||
|
||||
LifoAlloc lifoAlloc(1024);
|
||||
Sprinter sprinter;
|
||||
INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0);
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return;
|
||||
|
||||
fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno);
|
||||
js_DumpPCCounts(cx, script, &sprinter);
|
||||
fputs(sprinter.base, stdout);
|
||||
fputs(sprinter.string(), stdout);
|
||||
fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
|
||||
#endif
|
||||
}
|
||||
|
@ -323,6 +323,12 @@ js::SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val)
|
||||
fun->toFunction()->setExtendedSlot(which, val);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const js::Value &value)
|
||||
{
|
||||
obj->setSlot(slot, value);
|
||||
}
|
||||
|
||||
void
|
||||
js::SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback)
|
||||
{
|
||||
@ -469,6 +475,39 @@ js::DumpHeapComplete(JSContext *cx, FILE *fp)
|
||||
|
||||
namespace js {
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
IsIncrementalBarrierNeeded(JSRuntime *rt)
|
||||
{
|
||||
return !!rt->gcIncrementalTracer && !rt->gcRunning;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
IsIncrementalBarrierNeeded(JSContext *cx)
|
||||
{
|
||||
return IsIncrementalBarrierNeeded(cx->runtime);
|
||||
}
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
IncrementalReferenceBarrier(void *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
JS_ASSERT(!static_cast<gc::Cell *>(ptr)->compartment()->rt->gcRunning);
|
||||
uint32_t kind = gc::GetGCThingTraceKind(ptr);
|
||||
if (kind == JSTRACE_OBJECT)
|
||||
JSObject::writeBarrierPre((JSObject *) ptr);
|
||||
else if (kind == JSTRACE_STRING)
|
||||
JSString::writeBarrierPre((JSString *) ptr);
|
||||
else
|
||||
JS_NOT_REACHED("invalid trace kind");
|
||||
}
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
IncrementalValueBarrier(const Value &v)
|
||||
{
|
||||
HeapValue::writeBarrierPre(v);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
AutoLockGC::LockGC(JSRuntime *rt)
|
||||
{
|
||||
|
@ -378,11 +378,18 @@ GetReservedSlot(const JSObject *obj, size_t slot)
|
||||
return reinterpret_cast<const shadow::Object *>(obj)->slotRef(slot);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const Value &value);
|
||||
|
||||
inline void
|
||||
SetReservedSlot(JSObject *obj, size_t slot, const Value &value)
|
||||
{
|
||||
JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
|
||||
reinterpret_cast<shadow::Object *>(obj)->slotRef(slot) = value;
|
||||
shadow::Object *sobj = reinterpret_cast<shadow::Object *>(obj);
|
||||
if (sobj->slotRef(slot).isMarkable())
|
||||
SetReservedSlotWithBarrier(obj, slot, value);
|
||||
else
|
||||
sobj->slotRef(slot) = value;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uint32_t)
|
||||
@ -565,6 +572,56 @@ GetRuntimeCompartments(JSRuntime *rt);
|
||||
extern JS_FRIEND_API(size_t)
|
||||
SizeOfJSContext();
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
IsIncrementalBarrierNeeded(JSRuntime *rt);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
IsIncrementalBarrierNeeded(JSContext *cx);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
IncrementalReferenceBarrier(void *ptr);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
IncrementalValueBarrier(const Value &v);
|
||||
|
||||
class ObjectPtr
|
||||
{
|
||||
JSObject *value;
|
||||
|
||||
public:
|
||||
ObjectPtr() : value(NULL) {}
|
||||
|
||||
ObjectPtr(JSObject *obj) : value(obj) {}
|
||||
|
||||
/* Always call finalize before the destructor. */
|
||||
~ObjectPtr() { JS_ASSERT(!value); }
|
||||
|
||||
void finalize(JSRuntime *rt) {
|
||||
if (IsIncrementalBarrierNeeded(rt))
|
||||
IncrementalReferenceBarrier(value);
|
||||
value = NULL;
|
||||
}
|
||||
void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); }
|
||||
|
||||
void init(JSObject *obj) { value = obj; }
|
||||
|
||||
JSObject *get() const { return value; }
|
||||
|
||||
void writeBarrierPre(JSRuntime *rt) {
|
||||
IncrementalReferenceBarrier(value);
|
||||
}
|
||||
|
||||
ObjectPtr &operator=(JSObject *obj) {
|
||||
IncrementalReferenceBarrier(value);
|
||||
value = obj;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSObject &operator*() const { return *value; }
|
||||
JSObject *operator->() const { return value; }
|
||||
operator JSObject *() const { return value; }
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
|
@ -2153,10 +2153,8 @@ TypeCompartment::processPendingRecompiles(JSContext *cx)
|
||||
for (unsigned i = 0; i < pending->length(); i++) {
|
||||
const RecompileInfo &info = (*pending)[i];
|
||||
mjit::JITScript *jit = info.script->getJIT(info.constructing);
|
||||
if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) {
|
||||
mjit::Recompiler::clearStackReferences(cx, info.script);
|
||||
jit->destroyChunk(cx, info.chunkIndex);
|
||||
}
|
||||
if (jit && jit->chunkDescriptor(info.chunkIndex).chunk)
|
||||
mjit::Recompiler::clearStackReferencesAndChunk(cx, info.script, jit, info.chunkIndex);
|
||||
}
|
||||
|
||||
#endif /* JS_METHODJIT */
|
||||
@ -3257,7 +3255,7 @@ GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
JSOp op = JSOp(*pc);
|
||||
JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
|
||||
|
||||
bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array));
|
||||
bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
|
||||
return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object);
|
||||
}
|
||||
|
||||
@ -3430,6 +3428,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
case JSOP_POP:
|
||||
case JSOP_NOP:
|
||||
case JSOP_LOOPHEAD:
|
||||
case JSOP_LOOPENTRY:
|
||||
case JSOP_GOTO:
|
||||
case JSOP_IFEQ:
|
||||
case JSOP_IFNE:
|
||||
@ -3561,7 +3560,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
|
||||
case JSOP_SWAP:
|
||||
case JSOP_PICK: {
|
||||
unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]);
|
||||
unsigned pickedDepth = (op == JSOP_SWAP ? 1 : GET_UINT8(pc));
|
||||
/* The last popped value is the last pushed. */
|
||||
poppedTypes(pc, pickedDepth)->addSubset(cx, &pushed[pickedDepth]);
|
||||
for (unsigned i = 0; i < pickedDepth; i++)
|
||||
@ -4024,7 +4023,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pc[1] & JSITER_FOREACH)
|
||||
if (GET_UINT8(pc) & JSITER_FOREACH)
|
||||
state.forTypes->addType(cx, Type::UnknownType());
|
||||
else
|
||||
state.forTypes->addType(cx, Type::StringType());
|
||||
|
@ -1603,8 +1603,8 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
||||
* Initialize the index segment register used by LOAD_ATOM and
|
||||
* GET_FULL_INDEX macros below. As a register we use a pointer based on
|
||||
* the atom map to turn frequently executed LOAD_ATOM into simple array
|
||||
* access. For less frequent object and regexp loads we have to recover
|
||||
* the segment from atoms pointer first.
|
||||
* access. For less frequent object loads we have to recover the segment
|
||||
* from atoms pointer first.
|
||||
*/
|
||||
JSAtom **atoms = script->atoms;
|
||||
|
||||
@ -1803,6 +1803,7 @@ ADD_EMPTY_CASE(JSOP_STARTXML)
|
||||
ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
|
||||
#endif
|
||||
ADD_EMPTY_CASE(JSOP_LOOPHEAD)
|
||||
ADD_EMPTY_CASE(JSOP_LOOPENTRY)
|
||||
END_EMPTY_CASES
|
||||
|
||||
BEGIN_CASE(JSOP_LABEL)
|
||||
@ -2046,7 +2047,7 @@ END_CASE(JSOP_AND)
|
||||
#define TRY_BRANCH_AFTER_COND(cond,spdec) \
|
||||
JS_BEGIN_MACRO \
|
||||
JS_ASSERT(js_CodeSpec[op].length == 1); \
|
||||
uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \
|
||||
uintN diff_ = (uintN) GET_UINT8(regs.pc) - (uintN) JSOP_IFEQ; \
|
||||
if (diff_ <= 1) { \
|
||||
regs.sp -= spdec; \
|
||||
if (cond == (diff_ != 0)) { \
|
||||
@ -2083,7 +2084,7 @@ END_CASE(JSOP_IN)
|
||||
BEGIN_CASE(JSOP_ITER)
|
||||
{
|
||||
JS_ASSERT(regs.sp > regs.fp()->base());
|
||||
uintN flags = regs.pc[1];
|
||||
uint8_t flags = GET_UINT8(regs.pc);
|
||||
if (!js_ValueToIterator(cx, flags, ®s.sp[-1]))
|
||||
goto error;
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
@ -2154,10 +2155,10 @@ END_CASE(JSOP_SWAP)
|
||||
|
||||
BEGIN_CASE(JSOP_PICK)
|
||||
{
|
||||
jsint i = regs.pc[1];
|
||||
JS_ASSERT(regs.sp - (i+1) >= regs.fp()->base());
|
||||
Value lval = regs.sp[-(i+1)];
|
||||
memmove(regs.sp - (i+1), regs.sp - i, sizeof(Value)*i);
|
||||
unsigned i = GET_UINT8(regs.pc);
|
||||
JS_ASSERT(regs.sp - (i + 1) >= regs.fp()->base());
|
||||
Value lval = regs.sp[-int(i + 1)];
|
||||
memmove(regs.sp - (i + 1), regs.sp - i, sizeof(Value) * i);
|
||||
regs.sp[-1] = lval;
|
||||
}
|
||||
END_CASE(JSOP_PICK)
|
||||
@ -3084,7 +3085,7 @@ BEGIN_CASE(JSOP_REGEXP)
|
||||
* Push a regexp object cloned from the regexp literal object mapped by the
|
||||
* bytecode at pc.
|
||||
*/
|
||||
jsatomid index = GET_FULL_INDEX(0);
|
||||
uint32_t index = GET_UINT32_INDEX(regs.pc);
|
||||
JSObject *proto = regs.fp()->scopeChain().global().getOrCreateRegExpPrototype(cx);
|
||||
if (!proto)
|
||||
goto error;
|
||||
@ -3771,18 +3772,16 @@ END_CASE(JSOP_HOLE)
|
||||
|
||||
BEGIN_CASE(JSOP_NEWINIT)
|
||||
{
|
||||
jsint i = regs.pc[1];
|
||||
|
||||
uint8_t i = GET_UINT8(regs.pc);
|
||||
JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
|
||||
JSObject *obj;
|
||||
|
||||
JSObject *obj;
|
||||
if (i == JSProto_Array) {
|
||||
obj = NewDenseEmptyArray(cx);
|
||||
} else {
|
||||
gc::AllocKind kind = GuessObjectGCKind(0);
|
||||
obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
|
@ -92,11 +92,8 @@ JSDOUBLE_IS_NaN(jsdouble d)
|
||||
{
|
||||
jsdpun u;
|
||||
u.d = d;
|
||||
#if defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)
|
||||
return (u.u64 & ~JSDOUBLE_SIGNBIT) > JSDOUBLE_EXPMASK;
|
||||
#else
|
||||
return (u.s.hi & JSDOUBLE_HI32_NAN) == JSDOUBLE_HI32_NAN;
|
||||
#endif
|
||||
return (u.u64 & JSDOUBLE_EXPMASK) == JSDOUBLE_EXPMASK &&
|
||||
(u.u64 & JSDOUBLE_MANTMASK) != 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -82,14 +82,14 @@ typedef enum JSOp {
|
||||
#define JOF_LOOKUPSWITCH 5 /* lookup switch */
|
||||
#define JOF_QARG 6 /* quickened get/set function argument ops */
|
||||
#define JOF_LOCAL 7 /* var or block-local variable */
|
||||
#define JOF_SLOTATOM 8 /* uint16_t slot + constant index */
|
||||
/* 8 is unused */
|
||||
#define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */
|
||||
#define JOF_UINT8 13 /* uint8_t immediate, e.g. top 8 bits of 24-bit
|
||||
atom index */
|
||||
#define JOF_INT32 14 /* int32_t immediate operand */
|
||||
#define JOF_OBJECT 15 /* unsigned 16-bit object index */
|
||||
#define JOF_SLOTOBJECT 16 /* uint16_t slot index + object index */
|
||||
#define JOF_REGEXP 17 /* unsigned 16-bit regexp index */
|
||||
#define JOF_REGEXP 17 /* unsigned 32-bit regexp index */
|
||||
#define JOF_INT8 18 /* int8_t immediate operand */
|
||||
#define JOF_ATOMOBJECT 19 /* uint16_t constant index + object index */
|
||||
#define JOF_UINT16PAIR 20 /* pair of uint16_t immediates */
|
||||
@ -151,6 +151,18 @@ typedef enum JSOp {
|
||||
* Immediate operand getters, setters, and bounds.
|
||||
*/
|
||||
|
||||
static JS_ALWAYS_INLINE uint8_t
|
||||
GET_UINT8(jsbytecode *pc)
|
||||
{
|
||||
return (uint8_t) pc[1];
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE void
|
||||
SET_UINT8(jsbytecode *pc, uint8_t u)
|
||||
{
|
||||
pc[1] = (jsbytecode) u;
|
||||
}
|
||||
|
||||
/* Common uint16_t immediate format helpers. */
|
||||
#define UINT16_LEN 2
|
||||
#define UINT16_HI(i) ((jsbytecode)((i) >> 8))
|
||||
@ -179,10 +191,27 @@ SET_JUMP_OFFSET(jsbytecode *pc, int32_t off)
|
||||
pc[4] = (jsbytecode)off;
|
||||
}
|
||||
|
||||
#define UINT32_INDEX_LEN 4
|
||||
|
||||
static JS_ALWAYS_INLINE uint32_t
|
||||
GET_UINT32_INDEX(jsbytecode *pc)
|
||||
{
|
||||
return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4];
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE void
|
||||
SET_UINT32_INDEX(jsbytecode *pc, uint32_t index)
|
||||
{
|
||||
pc[1] = (jsbytecode)(index >> 24);
|
||||
pc[2] = (jsbytecode)(index >> 16);
|
||||
pc[3] = (jsbytecode)(index >> 8);
|
||||
pc[4] = (jsbytecode)index;
|
||||
}
|
||||
|
||||
/*
|
||||
* A literal is indexed by a per-script atom or object maps. Most scripts
|
||||
* have relatively few literals, so the standard JOF_ATOM, JOF_OBJECT and
|
||||
* JOF_REGEXP formats specifies a fixed 16 bits of immediate operand index.
|
||||
* have relatively few literals, so the standard JOF_ATOM and JOF_OBJECT
|
||||
* format specifies a fixed 16 bits of immediate operand index.
|
||||
* A script with more than 64K literals must wrap the bytecode into
|
||||
* JSOP_INDEXBASE and JSOP_RESETBASE pair.
|
||||
*/
|
||||
@ -345,12 +374,6 @@ js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff);
|
||||
fun = (script)->getFunction(index_); \
|
||||
JS_END_MACRO
|
||||
|
||||
#define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj) \
|
||||
JS_BEGIN_MACRO \
|
||||
uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \
|
||||
obj = (script)->getRegExp(index_); \
|
||||
JS_END_MACRO
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace js {
|
||||
|
||||
@ -440,27 +463,81 @@ DecompileValueGenerator(JSContext *cx, intN spindex, const Value &v,
|
||||
/*
|
||||
* Sprintf, but with unlimited and automatically allocated buffering.
|
||||
*/
|
||||
struct Sprinter {
|
||||
JSContext *context; /* context executing the decompiler */
|
||||
LifoAlloc *pool; /* string allocation pool */
|
||||
char *base; /* base address of buffer in pool */
|
||||
size_t size; /* size of buffer allocated at base */
|
||||
ptrdiff_t offset; /* offset of next free char in buffer */
|
||||
class Sprinter
|
||||
{
|
||||
public:
|
||||
struct InvariantChecker
|
||||
{
|
||||
const Sprinter *parent;
|
||||
|
||||
explicit InvariantChecker(const Sprinter *p) : parent(p) {
|
||||
parent->checkInvariants();
|
||||
}
|
||||
|
||||
~InvariantChecker() {
|
||||
parent->checkInvariants();
|
||||
}
|
||||
};
|
||||
|
||||
JSContext *context; /* context executing the decompiler */
|
||||
|
||||
private:
|
||||
static const size_t DefaultSize;
|
||||
#ifdef DEBUG
|
||||
bool initialized; /* true if this is initialized, use for debug builds */
|
||||
#endif
|
||||
char *base; /* malloc'd buffer address */
|
||||
size_t size; /* size of buffer allocated at base */
|
||||
ptrdiff_t offset; /* offset of next free char in buffer */
|
||||
|
||||
bool realloc_(size_t newSize);
|
||||
|
||||
public:
|
||||
explicit Sprinter(JSContext *cx);
|
||||
~Sprinter();
|
||||
|
||||
/* Initialize this sprinter, returns false on error */
|
||||
bool init();
|
||||
|
||||
void checkInvariants() const;
|
||||
|
||||
const char *string() const;
|
||||
const char *stringEnd() const;
|
||||
/* Returns the string at offset |off| */
|
||||
char *stringAt(ptrdiff_t off) const;
|
||||
/* Returns the char at offset |off| */
|
||||
char &operator[](size_t off);
|
||||
/* Test if this Sprinter is empty */
|
||||
bool empty() const;
|
||||
|
||||
/*
|
||||
* Attempt to reserve len + 1 space (for a trailing NULL byte). If the
|
||||
* attempt succeeds, return a pointer to the start of that space and adjust the
|
||||
* internal content. The caller *must* completely fill this space on success.
|
||||
*/
|
||||
char *reserve(size_t len);
|
||||
/* Like reserve, but memory is initialized to 0 */
|
||||
char *reserveAndClear(size_t len);
|
||||
|
||||
/*
|
||||
* Puts |len| characters from |s| at the current position and return an offset to
|
||||
* the beginning of this new data
|
||||
*/
|
||||
ptrdiff_t put(const char *s, size_t len);
|
||||
ptrdiff_t putString(JSString *str);
|
||||
|
||||
/* Prints a formatted string into the buffer */
|
||||
int printf(const char *fmt, ...);
|
||||
|
||||
/* Change the offset */
|
||||
void setOffset(const char *end);
|
||||
void setOffset(ptrdiff_t off);
|
||||
|
||||
/* Get the offset */
|
||||
ptrdiff_t getOffset() const;
|
||||
ptrdiff_t getOffsetOf(const char *string) const;
|
||||
};
|
||||
|
||||
#define INIT_SPRINTER(cx, sp, ap, off) \
|
||||
((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
|
||||
(sp)->offset = off)
|
||||
|
||||
/*
|
||||
* Attempt to reserve len space in sp (including a trailing NULL byte). If the
|
||||
* attempt succeeds, return a pointer to the start of that space and adjust the
|
||||
* length of sp's contents. The caller *must* completely fill this space
|
||||
* (including the space for the trailing NULL byte) on success.
|
||||
*/
|
||||
extern char *
|
||||
SprintReserveAmount(Sprinter *sp, size_t len);
|
||||
|
||||
extern ptrdiff_t
|
||||
SprintPut(Sprinter *sp, const char *s, size_t len);
|
||||
|
||||
|
@ -411,7 +411,7 @@ OPDEF(JSOP_GNAMEINC, 158,"gnameinc", NULL, 4, 0, 1, 15, JOF_ATOM|
|
||||
OPDEF(JSOP_GNAMEDEC, 159,"gnamedec", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
|
||||
|
||||
/* Regular expression literal requiring special "fork on exec" handling. */
|
||||
OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_REGEXP)
|
||||
OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 5, 0, 1, 19, JOF_REGEXP)
|
||||
|
||||
/* XML (ECMA-357, a.k.a. "E4X") support. */
|
||||
OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
@ -572,3 +572,6 @@ OPDEF(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||
|
||||
/* Push the implicit 'this' value for calls to the associated name. */
|
||||
OPDEF(JSOP_IMPLICITTHIS, 226, "implicitthis", "", 3, 0, 1, 0, JOF_ATOM)
|
||||
|
||||
/* This opcode is the target of the entry jump for some loop. */
|
||||
OPDEF(JSOP_LOOPENTRY, 227, "loopentry", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
@ -54,9 +54,8 @@ GetNameFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs
|
||||
return cx->runtime->atomState.classPrototypeAtom;
|
||||
|
||||
JSScript *script = cx->stack.currentScript();
|
||||
ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
||||
PropertyName *name;
|
||||
GET_NAME_FROM_BYTECODE(script, pc, pcoff, name);
|
||||
GET_NAME_FROM_BYTECODE(script, pc, 0, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
|
@ -48,15 +48,13 @@
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "jsdhash.h"
|
||||
#include "jsobj.h"
|
||||
#include "jspropertytree.h"
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jspropertytree.h"
|
||||
|
||||
#include "js/HashTable.h"
|
||||
#include "gc/Root.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -1746,7 +1746,7 @@ JSScript::recompileForStepMode(JSContext *cx)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
if (jitNormal || jitCtor) {
|
||||
mjit::ClearAllFrames(cx->compartment);
|
||||
mjit::Recompiler::clearStackReferences(cx, this);
|
||||
mjit::ReleaseScriptCode(cx, this);
|
||||
}
|
||||
#endif
|
||||
|
@ -3430,25 +3430,15 @@ CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32_t *resul
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t l1 = str1->length();
|
||||
const jschar *s1 = str1->getChars(cx);
|
||||
if (!s1)
|
||||
return false;
|
||||
|
||||
size_t l2 = str2->length();
|
||||
const jschar *s2 = str2->getChars(cx);
|
||||
if (!s2)
|
||||
return false;
|
||||
|
||||
size_t n = JS_MIN(l1, l2);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (int32_t cmp = s1[i] - s2[i]) {
|
||||
*result = cmp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*result = (int32_t)(l1 - l2);
|
||||
return true;
|
||||
return CompareChars(s1, str1->length(), s2, str2->length(), result);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -329,6 +329,25 @@ SkipSpace(const jschar *s, const jschar *end)
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return less than, equal to, or greater than zero depending on whether
|
||||
* s1 is less than, equal to, or greater than s2.
|
||||
*/
|
||||
inline bool
|
||||
CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2, int32_t *result)
|
||||
{
|
||||
size_t n = JS_MIN(l1, l2);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (int32_t cmp = s1[i] - s2[i]) {
|
||||
*result = cmp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*result = (int32_t)(l1 - l2);
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsstrinlines_h___ */
|
||||
|
@ -226,7 +226,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32_t id);
|
||||
* and saved versions. If deserialization fails, the data should be
|
||||
* invalidated if possible.
|
||||
*/
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 103)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 105)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
@ -732,10 +732,6 @@ MakeJITScript(JSContext *cx, JSScript *script, bool construct)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Watch for cross-chunk edges in a table switch. Don't handle
|
||||
* lookup switches, as these are always stubbed.
|
||||
*/
|
||||
if (op == JSOP_TABLESWITCH) {
|
||||
jsbytecode *pc2 = pc;
|
||||
unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
|
||||
@ -768,6 +764,31 @@ MakeJITScript(JSContext *cx, JSScript *script, bool construct)
|
||||
}
|
||||
}
|
||||
|
||||
if (op == JSOP_LOOKUPSWITCH) {
|
||||
unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
|
||||
jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
|
||||
unsigned npairs = GET_UINT16(pc2);
|
||||
pc2 += UINT16_LEN;
|
||||
|
||||
CrossChunkEdge edge;
|
||||
edge.source = offset;
|
||||
edge.target = defaultOffset;
|
||||
if (!currentEdges.append(edge))
|
||||
return NULL;
|
||||
|
||||
while (npairs) {
|
||||
pc2 += INDEX_LEN;
|
||||
unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
|
||||
CrossChunkEdge edge;
|
||||
edge.source = offset;
|
||||
edge.target = targetOffset;
|
||||
if (!currentEdges.append(edge))
|
||||
return NULL;
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
npairs--;
|
||||
}
|
||||
}
|
||||
|
||||
if (unsigned(offset - chunkStart) > CHUNK_LIMIT)
|
||||
finishChunk = true;
|
||||
|
||||
@ -1840,13 +1861,12 @@ mjit::Compiler::finishThisUp()
|
||||
#define SPEW_OPCODE() \
|
||||
JS_BEGIN_MACRO \
|
||||
if (IsJaegerSpewChannelActive(JSpew_JSOps)) { \
|
||||
LifoAllocScope las(&cx->tempLifoAlloc()); \
|
||||
Sprinter sprinter; \
|
||||
INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); \
|
||||
Sprinter sprinter(cx); \
|
||||
sprinter.init(); \
|
||||
js_Disassemble1(cx, script, PC, PC - script->code, \
|
||||
JS_TRUE, &sprinter); \
|
||||
JaegerSpew(JSpew_JSOps, " %2d %s", \
|
||||
frame.stackDepth(), sprinter.base); \
|
||||
frame.stackDepth(), sprinter.string()); \
|
||||
} \
|
||||
JS_END_MACRO;
|
||||
#else
|
||||
@ -1993,6 +2013,8 @@ mjit::Compiler::generateMethod()
|
||||
|
||||
if (PC >= script->code + chunkEnd) {
|
||||
if (fallthrough) {
|
||||
if (opinfo->jumpTarget)
|
||||
fixDoubleTypes(PC);
|
||||
frame.syncAndForgetEverything();
|
||||
jsbytecode *curPC = PC;
|
||||
do {
|
||||
@ -2260,12 +2282,12 @@ mjit::Compiler::generateMethod()
|
||||
|
||||
BEGIN_CASE(JSOP_PICK)
|
||||
{
|
||||
int32_t amt = GET_INT8(PC);
|
||||
uint32_t amt = GET_UINT8(PC);
|
||||
|
||||
// Push -(amt + 1), say amt == 2
|
||||
// Stack before: X3 X2 X1
|
||||
// Stack after: X3 X2 X1 X3
|
||||
frame.dupAt(-(amt + 1));
|
||||
frame.dupAt(-int32_t(amt + 1));
|
||||
|
||||
// For each item X[i...1] push it then move it down.
|
||||
// The above would transition like so:
|
||||
@ -2273,7 +2295,7 @@ mjit::Compiler::generateMethod()
|
||||
// X2 X2 X1 X3 (shift)
|
||||
// X2 X2 X1 X3 X1 (dupAt)
|
||||
// X2 X1 X1 X3 (shift)
|
||||
for (int32_t i = -amt; i < 0; i++) {
|
||||
for (int32_t i = -int32_t(amt); i < 0; i++) {
|
||||
frame.dupAt(i - 1);
|
||||
frame.shift(i - 2);
|
||||
}
|
||||
@ -2682,7 +2704,7 @@ mjit::Compiler::generateMethod()
|
||||
masm.move(ImmPtr(PC), Registers::ArgReg1);
|
||||
|
||||
/* prepareStubCall() is not needed due to syncAndForgetEverything() */
|
||||
INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
|
||||
INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
|
||||
frame.pop();
|
||||
|
||||
masm.jump(Registers::ReturnReg);
|
||||
@ -2701,7 +2723,7 @@ mjit::Compiler::generateMethod()
|
||||
masm.move(ImmPtr(PC), Registers::ArgReg1);
|
||||
|
||||
/* prepareStubCall() is not needed due to syncAndForgetEverything() */
|
||||
INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_JUMP);
|
||||
INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_NONE);
|
||||
frame.pop();
|
||||
|
||||
masm.jump(Registers::ReturnReg);
|
||||
@ -2732,7 +2754,7 @@ mjit::Compiler::generateMethod()
|
||||
END_CASE(JSOP_STRICTEQ)
|
||||
|
||||
BEGIN_CASE(JSOP_ITER)
|
||||
if (!iter(PC[1]))
|
||||
if (!iter(GET_UINT8(PC)))
|
||||
return Compile_Error;
|
||||
END_CASE(JSOP_ITER)
|
||||
|
||||
@ -3227,6 +3249,9 @@ mjit::Compiler::generateMethod()
|
||||
}
|
||||
END_CASE(JSOP_LOOPHEAD)
|
||||
|
||||
BEGIN_CASE(JSOP_LOOPENTRY)
|
||||
END_CASE(JSOP_LOOPENTRY)
|
||||
|
||||
BEGIN_CASE(JSOP_DEBUGGER)
|
||||
{
|
||||
prepareStubCall(Uses(0));
|
||||
@ -6775,7 +6800,7 @@ mjit::Compiler::jsop_newinit()
|
||||
JSObject *baseobj = NULL;
|
||||
switch (*PC) {
|
||||
case JSOP_NEWINIT:
|
||||
isArray = (PC[1] == JSProto_Array);
|
||||
isArray = (GET_UINT8(PC) == JSProto_Array);
|
||||
break;
|
||||
case JSOP_NEWARRAY:
|
||||
isArray = true;
|
||||
@ -6868,7 +6893,7 @@ mjit::Compiler::jsop_newinit()
|
||||
bool
|
||||
mjit::Compiler::jsop_regexp()
|
||||
{
|
||||
JSObject *obj = script->getRegExp(fullAtomIndex(PC));
|
||||
JSObject *obj = script->getRegExp(GET_UINT32_INDEX(PC));
|
||||
RegExpStatics *res = globalObj ? globalObj->getRegExpStatics() : NULL;
|
||||
|
||||
if (!globalObj ||
|
||||
@ -7354,7 +7379,7 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc)
|
||||
masm.move(ImmPtr(originalPC), Registers::ArgReg1);
|
||||
|
||||
/* prepareStubCall() is not needed due to forgetEverything() */
|
||||
INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
|
||||
INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
|
||||
frame.pop();
|
||||
masm.jump(Registers::ReturnReg);
|
||||
return true;
|
||||
@ -7401,7 +7426,7 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc)
|
||||
stubcc.linkExitDirect(notInt.get(), stubcc.masm.label());
|
||||
stubcc.leave();
|
||||
stubcc.masm.move(ImmPtr(originalPC), Registers::ArgReg1);
|
||||
OOL_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
|
||||
OOL_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
|
||||
stubcc.masm.jump(Registers::ReturnReg);
|
||||
}
|
||||
frame.pop();
|
||||
|
@ -547,10 +547,10 @@ js_InternalThrow(VMFrame &f)
|
||||
switch (st) {
|
||||
case JSTRAP_ERROR:
|
||||
cx->clearPendingException();
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case JSTRAP_CONTINUE:
|
||||
break;
|
||||
break;
|
||||
|
||||
case JSTRAP_RETURN:
|
||||
cx->clearPendingException();
|
||||
@ -872,11 +872,6 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
|
||||
f.regs.pc = nextpc;
|
||||
break;
|
||||
|
||||
case REJOIN_JUMP:
|
||||
f.regs.pc = (jsbytecode *) returnReg;
|
||||
JS_ASSERT(unsigned(f.regs.pc - script->code) < script->length);
|
||||
break;
|
||||
|
||||
case REJOIN_NATIVE:
|
||||
case REJOIN_NATIVE_LOWERED:
|
||||
case REJOIN_NATIVE_GETTER: {
|
||||
|
@ -1897,6 +1897,7 @@ LoopState::analyzeLoopBody(unsigned frame)
|
||||
break;
|
||||
|
||||
case JSOP_LOOPHEAD:
|
||||
case JSOP_LOOPENTRY:
|
||||
case JSOP_POP:
|
||||
case JSOP_ZERO:
|
||||
case JSOP_ONE:
|
||||
|
@ -1296,6 +1296,9 @@ JITScript::destroy(JSContext *cx)
|
||||
{
|
||||
for (unsigned i = 0; i < nchunks; i++)
|
||||
destroyChunk(cx, i);
|
||||
|
||||
if (shimPool)
|
||||
shimPool->release();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -309,9 +309,6 @@ enum RejoinState {
|
||||
/* State is coherent for the start of the next (fallthrough) bytecode. */
|
||||
REJOIN_FALLTHROUGH,
|
||||
|
||||
/* State is coherent for the start of the bytecode returned by the call. */
|
||||
REJOIN_JUMP,
|
||||
|
||||
/*
|
||||
* As for REJOIN_FALLTHROUGH, but holds a reference on the compartment's
|
||||
* orphaned native pools which needs to be reclaimed by InternalInterpret.
|
||||
|
@ -456,6 +456,39 @@ Recompiler::clearStackReferences(JSContext *cx, JSScript *script)
|
||||
cx->compartment->types.recompilations++;
|
||||
}
|
||||
|
||||
void
|
||||
Recompiler::clearStackReferencesAndChunk(JSContext *cx, JSScript *script,
|
||||
JITScript *jit, size_t chunkIndex,
|
||||
bool resetUses)
|
||||
{
|
||||
Recompiler::clearStackReferences(cx, script);
|
||||
|
||||
bool releaseChunk = true;
|
||||
if (jit->nchunks > 1) {
|
||||
// If we are in the middle of a native call from a native or getter IC,
|
||||
// we need to make sure all JIT code for the script is purged, as
|
||||
// otherwise we will have orphaned the native stub but pointers to it
|
||||
// still exist in the containing chunk.
|
||||
for (VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame();
|
||||
f != NULL;
|
||||
f = f->previous) {
|
||||
if (f->fp()->script() == script) {
|
||||
JS_ASSERT(f->stubRejoin != REJOIN_NATIVE &&
|
||||
f->stubRejoin != REJOIN_NATIVE_LOWERED &&
|
||||
f->stubRejoin != REJOIN_NATIVE_GETTER);
|
||||
if (f->stubRejoin == REJOIN_NATIVE_PATCHED) {
|
||||
mjit::ReleaseScriptCode(cx, script);
|
||||
releaseChunk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (releaseChunk)
|
||||
jit->destroyChunk(cx, chunkIndex, resetUses);
|
||||
}
|
||||
|
||||
} /* namespace mjit */
|
||||
} /* namespace js */
|
||||
|
||||
|
@ -65,9 +65,18 @@ namespace mjit {
|
||||
class Recompiler {
|
||||
public:
|
||||
|
||||
// Clear all uses of compiled code for script on the stack. This must be
|
||||
// followed by destroying all JIT code for the script.
|
||||
static void
|
||||
clearStackReferences(JSContext *cx, JSScript *script);
|
||||
|
||||
// Clear all uses of compiled code for script on the stack, along with
|
||||
// the specified compiled chunk.
|
||||
static void
|
||||
clearStackReferencesAndChunk(JSContext *cx, JSScript *script,
|
||||
JITScript *jit, size_t chunkIndex,
|
||||
bool resetUses = true);
|
||||
|
||||
static void
|
||||
expandInlineFrames(JSCompartment *compartment, StackFrame *fp, mjit::CallSite *inlined,
|
||||
StackFrame *next, VMFrame *f);
|
||||
|
@ -896,23 +896,8 @@ void JS_FASTCALL
|
||||
stubs::RecompileForInline(VMFrame &f)
|
||||
{
|
||||
ExpandInlineFrames(f.cx->compartment);
|
||||
Recompiler::clearStackReferences(f.cx, f.script());
|
||||
|
||||
bool releaseChunk = true;
|
||||
if (f.jit()->nchunks > 1) {
|
||||
StackFrame *fp = f.fp();
|
||||
for (FrameRegsIter i(f.cx); !i.done(); ++i) {
|
||||
StackFrame *xfp = i.fp();
|
||||
if (xfp->script() == fp->script() && xfp != fp) {
|
||||
mjit::ReleaseScriptCode(f.cx, fp->script());
|
||||
releaseChunk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (releaseChunk)
|
||||
f.jit()->destroyChunk(f.cx, f.chunkIndex(), /* resetUses = */ false);
|
||||
Recompiler::clearStackReferencesAndChunk(f.cx, f.script(), f.jit(), f.chunkIndex(),
|
||||
/* resetUses = */ false);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
@ -1501,13 +1486,18 @@ FindNativeCode(VMFrame &f, jsbytecode *target)
|
||||
if (native)
|
||||
return native;
|
||||
|
||||
CompileStatus status = CanMethodJIT(f.cx, f.script(), target, f.fp()->isConstructing(),
|
||||
CompileRequest_Interpreter);
|
||||
if (status == Compile_Error)
|
||||
THROWV(NULL);
|
||||
uint32_t sourceOffset = f.pc() - f.script()->code;
|
||||
uint32_t targetOffset = target - f.script()->code;
|
||||
|
||||
mjit::ClearAllFrames(f.cx->compartment);
|
||||
return target;
|
||||
CrossChunkEdge *edges = f.jit()->edges();
|
||||
for (size_t i = 0; i < f.jit()->nedges; i++) {
|
||||
const CrossChunkEdge &edge = edges[i];
|
||||
if (edge.source == sourceOffset && edge.target == targetOffset)
|
||||
return edge.shimLabel;
|
||||
}
|
||||
|
||||
JS_NOT_REACHED("Missing edge");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void * JS_FASTCALL
|
||||
|
@ -1916,9 +1916,9 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
|
||||
JSAtom *atom = script->getAtom(index);
|
||||
Sprint(sp, " atom %u (", index);
|
||||
size_t len = PutEscapedString(NULL, 0, atom, '\0');
|
||||
if (char *buf = SprintReserveAmount(sp, len)) {
|
||||
if (char *buf = sp->reserve(len)) {
|
||||
PutEscapedString(buf, len, atom, 0);
|
||||
buf[len] = '\0';
|
||||
buf[len] = 0;
|
||||
}
|
||||
Sprint(sp, ")");
|
||||
break;
|
||||
@ -1964,9 +1964,9 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
|
||||
static JSBool
|
||||
Notes(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
Sprinter sprinter;
|
||||
INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return JS_FALSE;
|
||||
|
||||
jsval *argv = JS_ARGV(cx, vp);
|
||||
for (uintN i = 0; i < argc; i++) {
|
||||
@ -1977,7 +1977,7 @@ Notes(JSContext *cx, uintN argc, jsval *vp)
|
||||
SrcNotes(cx, script, &sprinter);
|
||||
}
|
||||
|
||||
JSString *str = JS_NewStringCopyZ(cx, sprinter.base);
|
||||
JSString *str = JS_NewStringCopyZ(cx, sprinter.string());
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
|
||||
@ -2117,19 +2117,18 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp)
|
||||
if (!p.parse(cx))
|
||||
return false;
|
||||
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
Sprinter sprinter;
|
||||
INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
|
||||
Sprinter *sp = &sprinter;
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return false;
|
||||
|
||||
bool ok = true;
|
||||
if (p.argc == 0) {
|
||||
/* Without arguments, disassemble the current script. */
|
||||
if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) {
|
||||
JSScript *script = JS_GetFrameScript(cx, frame);
|
||||
if (js_Disassemble(cx, script, p.lines, sp)) {
|
||||
SrcNotes(cx, script, sp);
|
||||
TryNotes(cx, script, sp);
|
||||
if (js_Disassemble(cx, script, p.lines, &sprinter)) {
|
||||
SrcNotes(cx, script, &sprinter);
|
||||
TryNotes(cx, script, &sprinter);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
@ -2138,11 +2137,11 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp)
|
||||
for (uintN i = 0; i < p.argc; i++) {
|
||||
JSFunction *fun;
|
||||
JSScript *script = ValueToScript(cx, p.argv[i], &fun);
|
||||
ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp);
|
||||
ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
|
||||
}
|
||||
}
|
||||
|
||||
JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.base) : NULL;
|
||||
JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.string()) : NULL;
|
||||
if (!str)
|
||||
return false;
|
||||
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
|
||||
@ -2156,19 +2155,18 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp)
|
||||
if (!p.parse(cx))
|
||||
return false;
|
||||
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
Sprinter sprinter;
|
||||
INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
|
||||
Sprinter *sp = &sprinter;
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return false;
|
||||
|
||||
bool ok = true;
|
||||
if (p.argc == 0) {
|
||||
/* Without arguments, disassemble the current script. */
|
||||
if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) {
|
||||
JSScript *script = JS_GetFrameScript(cx, frame);
|
||||
if (js_Disassemble(cx, script, p.lines, sp)) {
|
||||
SrcNotes(cx, script, sp);
|
||||
TryNotes(cx, script, sp);
|
||||
if (js_Disassemble(cx, script, p.lines, &sprinter)) {
|
||||
SrcNotes(cx, script, &sprinter);
|
||||
TryNotes(cx, script, &sprinter);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
@ -2177,12 +2175,12 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp)
|
||||
for (uintN i = 0; i < p.argc; i++) {
|
||||
JSFunction *fun;
|
||||
JSScript *script = ValueToScript(cx, p.argv[i], &fun);
|
||||
ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp);
|
||||
ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
fprintf(stdout, "%s\n", sprinter.base);
|
||||
fprintf(stdout, "%s\n", sprinter.string());
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
return ok;
|
||||
}
|
||||
@ -2218,12 +2216,12 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp)
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
Sprinter sprinter;
|
||||
INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return false;
|
||||
bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter);
|
||||
if (ok)
|
||||
fprintf(stdout, "%s\n", sprinter.base);
|
||||
fprintf(stdout, "%s\n", sprinter.string());
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
@ -2267,10 +2265,11 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp)
|
||||
pc = script->code;
|
||||
end = pc + script->length;
|
||||
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
Sprinter sprinter;
|
||||
Sprinter *sp = &sprinter;
|
||||
INIT_SPRINTER(cx, sp, &cx->tempLifoAlloc(), 0);
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init()) {
|
||||
ok = JS_FALSE;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* burn the leading lines */
|
||||
line2 = JS_PCToLineNumber(cx, script, pc);
|
||||
@ -2290,11 +2289,11 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp)
|
||||
if (line2 < line1) {
|
||||
if (bupline != line2) {
|
||||
bupline = line2;
|
||||
Sprint(sp, "%s %3u: BACKUP\n", sep, line2);
|
||||
Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2);
|
||||
}
|
||||
} else {
|
||||
if (bupline && line1 == line2)
|
||||
Sprint(sp, "%s %3u: RESTORE\n", sep, line2);
|
||||
Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2);
|
||||
bupline = 0;
|
||||
while (line1 < line2) {
|
||||
if (!fgets(linebuf, LINE_BUF_LEN, file)) {
|
||||
@ -2305,11 +2304,11 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp)
|
||||
goto bail;
|
||||
}
|
||||
line1++;
|
||||
Sprint(sp, "%s %3u: %s", sep, line1, linebuf);
|
||||
Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf);
|
||||
}
|
||||
}
|
||||
|
||||
len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, sp);
|
||||
len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, &sprinter);
|
||||
if (!len) {
|
||||
ok = JS_FALSE;
|
||||
goto bail;
|
||||
@ -3959,6 +3958,13 @@ GetMaxArgs(JSContext *cx, uintN arg, jsval *vp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Terminate(JSContext *cx, uintN arg, jsval *vp)
|
||||
{
|
||||
JS_ClearPendingException(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN("version", Version, 0,0),
|
||||
JS_FN("revertVersion", RevertVersion, 0,0),
|
||||
@ -4047,6 +4053,7 @@ static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN("parseLegacyJSON",ParseLegacyJSON,1,0),
|
||||
JS_FN("enableStackWalkingAssertion",EnableStackWalkingAssertion,1,0),
|
||||
JS_FN("getMaxArgs", GetMaxArgs, 0,0),
|
||||
JS_FN("terminate", Terminate, 0,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@ -4201,6 +4208,8 @@ static const char *const shell_help_messages[] = {
|
||||
" assertion increases test duration by an order of magnitude, you shouldn't\n"
|
||||
" use this.",
|
||||
"getMaxArgs() Return the maximum number of supported args for a call.",
|
||||
"terminate() Terminate JavaScript execution, as if we had run out of\n"
|
||||
" memory or been terminated by the slow script dialog.",
|
||||
|
||||
/* Keep these last: see the static assertion below. */
|
||||
#ifdef MOZ_PROFILING
|
||||
|
76
js/src/tests/ecma_5/Array/join-01.js
Normal file
76
js/src/tests/ecma_5/Array/join-01.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
* Contributor:
|
||||
* Jeff Walden <jwalden+code@mit.edu>
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
print("ES5: Array.prototype.join");
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
var count;
|
||||
var stringifyCounter = { toString: function() { count++; return "obj"; } };
|
||||
|
||||
var arr = [1, 2, 3, 4, 5];
|
||||
assertEq(arr.join(), "1,2,3,4,5");
|
||||
assertEq(arr.join(","), "1,2,3,4,5");
|
||||
assertEq(arr.join(undefined), "1,2,3,4,5");
|
||||
assertEq(arr.join(4), "142434445");
|
||||
assertEq(arr.join(""), "12345");
|
||||
|
||||
count = 0;
|
||||
assertEq(arr.join(stringifyCounter), "1obj2obj3obj4obj5");
|
||||
assertEq(count, 1);
|
||||
|
||||
var holey = [1, 2, , 4, 5];
|
||||
assertEq(holey.join(), "1,2,,4,5");
|
||||
assertEq(holey.join(","), "1,2,,4,5");
|
||||
assertEq(holey.join(undefined), "1,2,,4,5");
|
||||
assertEq(holey.join(4), "14244445");
|
||||
|
||||
count = 0;
|
||||
assertEq(holey.join(stringifyCounter), "1obj2objobj4obj5");
|
||||
assertEq(count, 1);
|
||||
|
||||
var nully = [1, 2, 3, null, 5];
|
||||
assertEq(nully.join(), "1,2,3,,5");
|
||||
assertEq(nully.join(","), "1,2,3,,5");
|
||||
assertEq(nully.join(undefined), "1,2,3,,5");
|
||||
assertEq(nully.join(4), "14243445");
|
||||
|
||||
count = 0;
|
||||
assertEq(nully.join(stringifyCounter), "1obj2obj3objobj5");
|
||||
assertEq(count, 1);
|
||||
|
||||
var undefiney = [1, undefined, 3, 4, 5];
|
||||
assertEq(undefiney.join(), "1,,3,4,5");
|
||||
assertEq(undefiney.join(","), "1,,3,4,5");
|
||||
assertEq(undefiney.join(undefined), "1,,3,4,5");
|
||||
assertEq(undefiney.join(4), "14434445");
|
||||
|
||||
count = 0;
|
||||
assertEq(undefiney.join(stringifyCounter), "1objobj3obj4obj5");
|
||||
assertEq(count, 1);
|
||||
|
||||
var funky =
|
||||
{
|
||||
toString: function()
|
||||
{
|
||||
Array.prototype[1] = "chorp";
|
||||
Object.prototype[3] = "fnord";
|
||||
return "funky";
|
||||
}
|
||||
};
|
||||
var trailingHoles = [0, funky, /* 2 */, /* 3 */,];
|
||||
assertEq(trailingHoles.join(""), "0funkyfnord");
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("Tests complete");
|
@ -1,4 +1,5 @@
|
||||
url-prefix ../../jsreftest.html?test=ecma_5/Array/
|
||||
script join-01.js
|
||||
script length-01.js
|
||||
script length-set-object.js
|
||||
script regress-599159.js
|
||||
|
@ -794,6 +794,17 @@ ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrame
|
||||
gfg->gen_ = gen;
|
||||
gfg->stackvp_ = stackvp;
|
||||
|
||||
/*
|
||||
* Trigger incremental barrier on the floating frame's generator object.
|
||||
* This is normally traced through only by associated arguments/call
|
||||
* objects, but only when the generator is not actually on the stack.
|
||||
* We don't need to worry about generational barriers as the generator
|
||||
* object has a trace hook and cannot be nursery allocated.
|
||||
*/
|
||||
JSObject *genobj = js_FloatingFrameToGenerator(genfp)->obj;
|
||||
JS_ASSERT(genobj->getClass()->trace);
|
||||
JSObject::writeBarrierPre(genobj);
|
||||
|
||||
/* Copy from the generator's floating frame to the stack. */
|
||||
stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp);
|
||||
stackfp->resetGeneratorPrev(cx);
|
||||
|
@ -46,6 +46,15 @@
|
||||
struct JSContext;
|
||||
struct JSCompartment;
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
namespace js { namespace mjit { struct CallSite; }}
|
||||
typedef js::mjit::CallSite JSInlinedSite;
|
||||
#else
|
||||
struct JSInlinedSite {};
|
||||
#endif
|
||||
|
||||
typedef /* js::mjit::RejoinState */ size_t JSRejoinState;
|
||||
|
||||
namespace js {
|
||||
|
||||
class StackFrame;
|
||||
@ -61,13 +70,6 @@ class ExecuteFrameGuard;
|
||||
class DummyFrameGuard;
|
||||
class GeneratorFrameGuard;
|
||||
|
||||
namespace mjit {
|
||||
struct JITScript;
|
||||
struct CallSite;
|
||||
jsbytecode *NativeToPC(JITScript *jit, void *ncode, CallSite **pinline);
|
||||
}
|
||||
namespace detail { struct OOMCheck; }
|
||||
|
||||
class CallIter;
|
||||
class FrameRegsIter;
|
||||
class AllFramesIter;
|
||||
@ -75,13 +77,15 @@ class AllFramesIter;
|
||||
class ArgumentsObject;
|
||||
class StaticBlockObject;
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
typedef js::mjit::CallSite JSInlinedSite;
|
||||
#else
|
||||
struct JSInlinedSite {};
|
||||
#endif
|
||||
namespace mjit {
|
||||
struct JITScript;
|
||||
jsbytecode *NativeToPC(JITScript *jit, void *ncode, CallSite **pinline);
|
||||
}
|
||||
namespace detail {
|
||||
struct OOMCheck;
|
||||
}
|
||||
|
||||
typedef /* js::mjit::RejoinState */ size_t JSRejoinState;
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* VM stack layout
|
||||
@ -313,6 +317,22 @@ CallArgsListFromVp(uintN argc, Value *vp, CallArgsList *prev)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Flags specified for a frame as it is constructed. */
|
||||
enum InitialFrameFlags {
|
||||
INITIAL_NONE = 0,
|
||||
INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */
|
||||
INITIAL_LOWERED = 0x800000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
|
||||
};
|
||||
|
||||
enum ExecuteType {
|
||||
EXECUTE_GLOBAL = 0x1, /* == StackFrame::GLOBAL */
|
||||
EXECUTE_DIRECT_EVAL = 0x8, /* == StackFrame::EVAL */
|
||||
EXECUTE_INDIRECT_EVAL = 0x9, /* == StackFrame::GLOBAL | EVAL */
|
||||
EXECUTE_DEBUG = 0x18 /* == StackFrame::EVAL | DEBUGGER */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class StackFrame
|
||||
{
|
||||
public:
|
||||
@ -1397,27 +1417,302 @@ class StackSegment
|
||||
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
|
||||
JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
|
||||
|
||||
inline Value *
|
||||
StackSpace::firstUnused() const { return seg_ ? seg_->end() : base_; }
|
||||
/*****************************************************************************/
|
||||
|
||||
inline bool ContextStack::hasfp() const { return seg_ && seg_->maybeRegs(); }
|
||||
inline FrameRegs * ContextStack::maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; }
|
||||
inline StackFrame * ContextStack::maybefp() const { return seg_ ? seg_->maybefp() : NULL; }
|
||||
inline FrameRegs & ContextStack::regs() const { JS_ASSERT(hasfp()); return seg_->regs(); }
|
||||
inline StackFrame * ContextStack::fp() const { JS_ASSERT(hasfp()); return seg_->fp(); }
|
||||
class StackSpace
|
||||
{
|
||||
StackSegment *seg_;
|
||||
Value *base_;
|
||||
mutable Value *conservativeEnd_;
|
||||
#ifdef XP_WIN
|
||||
mutable Value *commitEnd_;
|
||||
#endif
|
||||
Value *defaultEnd_;
|
||||
Value *trustedEnd_;
|
||||
|
||||
} /* namespace js */
|
||||
void assertInvariants() const {
|
||||
JS_ASSERT(base_ <= conservativeEnd_);
|
||||
#ifdef XP_WIN
|
||||
JS_ASSERT(conservativeEnd_ <= commitEnd_);
|
||||
JS_ASSERT(commitEnd_ <= trustedEnd_);
|
||||
#endif
|
||||
JS_ASSERT(conservativeEnd_ <= defaultEnd_);
|
||||
JS_ASSERT(defaultEnd_ <= trustedEnd_);
|
||||
}
|
||||
|
||||
inline bool JSContext::hasfp() const { return stack.hasfp(); }
|
||||
inline js::StackFrame* JSContext::fp() const { return stack.fp(); }
|
||||
inline js::StackFrame* JSContext::maybefp() const { return stack.maybefp(); }
|
||||
inline js::FrameRegs& JSContext::regs() const { return stack.regs(); }
|
||||
inline js::FrameRegs* JSContext::maybeRegs() const { return stack.maybeRegs(); }
|
||||
/* The total number of values/bytes reserved for the stack. */
|
||||
static const size_t CAPACITY_VALS = 512 * 1024;
|
||||
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
|
||||
|
||||
namespace js {
|
||||
/* How much of the stack is initially committed. */
|
||||
static const size_t COMMIT_VALS = 16 * 1024;
|
||||
static const size_t COMMIT_BYTES = COMMIT_VALS * sizeof(Value);
|
||||
|
||||
inline void
|
||||
ContextStack::repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
|
||||
/* How much space is reserved at the top of the stack for trusted JS. */
|
||||
static const size_t BUFFER_VALS = 16 * 1024;
|
||||
static const size_t BUFFER_BYTES = BUFFER_VALS * sizeof(Value);
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
|
||||
}
|
||||
|
||||
friend class AllFramesIter;
|
||||
friend class ContextStack;
|
||||
friend class StackFrame;
|
||||
|
||||
/*
|
||||
* Except when changing compartment (see pushDummyFrame), the 'dest'
|
||||
* parameter of ensureSpace is cx->compartment. Ideally, we'd just pass
|
||||
* this directly (and introduce a helper that supplies cx->compartment when
|
||||
* no 'dest' is given). For some compilers, this really hurts performance,
|
||||
* so, instead, a trivially sinkable magic constant is used to indicate
|
||||
* that dest should be cx->compartment.
|
||||
*/
|
||||
static const size_t CX_COMPARTMENT = 0xc;
|
||||
|
||||
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest = (JSCompartment *)CX_COMPARTMENT) const;
|
||||
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest) const;
|
||||
|
||||
StackSegment &findContainingSegment(const StackFrame *target) const;
|
||||
|
||||
public:
|
||||
StackSpace();
|
||||
bool init();
|
||||
~StackSpace();
|
||||
|
||||
/*
|
||||
* Maximum supported value of arguments.length. This bounds the maximum
|
||||
* number of arguments that can be supplied to Function.prototype.apply.
|
||||
* This value also bounds the number of elements parsed in an array
|
||||
* initialiser.
|
||||
*
|
||||
* Since arguments are copied onto the stack, the stack size is the
|
||||
* limiting factor for this constant. Use the max stack size (available to
|
||||
* untrusted code) with an extra buffer so that, after such an apply, the
|
||||
* callee can do a little work without OOMing.
|
||||
*/
|
||||
static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS);
|
||||
|
||||
/* See stack layout comment in Stack.h. */
|
||||
inline Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
|
||||
|
||||
StackSegment &containingSegment(const StackFrame *target) const;
|
||||
|
||||
/*
|
||||
* Extra space to reserve on the stack for method JIT frames, beyond the
|
||||
* frame's nslots. This may be used for inlined stack frames, slots storing
|
||||
* loop invariant code, or to reserve space for pushed callee frames. Note
|
||||
* that this space should be reserved when pushing interpreter frames as
|
||||
* well, so that we don't need to check the stack when entering the method
|
||||
* JIT at loop heads or safe points.
|
||||
*/
|
||||
static const size_t STACK_JIT_EXTRA = (/*~VALUES_PER_STACK_FRAME*/ 8 + 18) * 10;
|
||||
|
||||
/*
|
||||
* Return a limit against which jit code can check for. This limit is not
|
||||
* necessarily the end of the stack since we lazily commit stack memory on
|
||||
* some platforms. Thus, when the stack limit is exceeded, the caller should
|
||||
* use tryBumpLimit to attempt to increase the stack limit by committing
|
||||
* more memory. If the stack is truly exhausted, tryBumpLimit will report an
|
||||
* error and return NULL.
|
||||
*
|
||||
* An invariant of the methodjit is that there is always space to push a
|
||||
* frame on top of the current frame's expression stack (which can be at
|
||||
* most script->nslots deep). getStackLimit ensures that the returned limit
|
||||
* does indeed have this required space and reports an error and returns
|
||||
* NULL if this reserve space cannot be allocated.
|
||||
*/
|
||||
inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
|
||||
bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
|
||||
|
||||
/* Called during GC: mark segments, frames, and slots under firstUnused. */
|
||||
void mark(JSTracer *trc);
|
||||
|
||||
/* We only report the committed size; uncommitted size is uninteresting. */
|
||||
JS_FRIEND_API(size_t) sizeOfCommitted();
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class ContextStack
|
||||
{
|
||||
StackSegment *seg_;
|
||||
StackSpace *space_;
|
||||
JSContext *cx_;
|
||||
|
||||
/*
|
||||
* Return whether this ContextStack is at the top of the contiguous stack.
|
||||
* This is a precondition for extending the current segment by pushing
|
||||
* stack frames or overrides etc.
|
||||
*
|
||||
* NB: Just because a stack is onTop() doesn't mean there is necessarily
|
||||
* a frame pushed on the stack. For this, use hasfp().
|
||||
*/
|
||||
bool onTop() const;
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertSpaceInSync() const;
|
||||
#else
|
||||
void assertSpaceInSync() const {}
|
||||
#endif
|
||||
|
||||
/* Implementation details of push* public interface. */
|
||||
StackSegment *pushSegment(JSContext *cx);
|
||||
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
|
||||
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
MaybeExtend extend, bool *pushedSeg,
|
||||
JSCompartment *dest = (JSCompartment *)StackSpace::CX_COMPARTMENT);
|
||||
|
||||
inline StackFrame *
|
||||
getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, /*StackFrame::Flags*/ uint32_t *pflags) const;
|
||||
|
||||
/* Make pop* functions private since only called by guard classes. */
|
||||
void popSegment();
|
||||
friend class InvokeArgsGuard;
|
||||
void popInvokeArgs(const InvokeArgsGuard &iag);
|
||||
friend class FrameGuard;
|
||||
void popFrame(const FrameGuard &fg);
|
||||
friend class GeneratorFrameGuard;
|
||||
void popGeneratorFrame(const GeneratorFrameGuard &gfg);
|
||||
|
||||
friend class StackIter;
|
||||
|
||||
public:
|
||||
ContextStack(JSContext *cx);
|
||||
~ContextStack();
|
||||
|
||||
/*** Stack accessors ***/
|
||||
|
||||
/*
|
||||
* A context's stack is "empty" if there are no scripts or natives
|
||||
* executing. Note that JS_SaveFrameChain does not factor into this definition.
|
||||
*/
|
||||
bool empty() const { return !seg_; }
|
||||
|
||||
/*
|
||||
* Return whether there has been at least one frame pushed since the most
|
||||
* recent call to JS_SaveFrameChain. Note that natives do not have frames
|
||||
* and dummy frames are frames that do not represent script execution hence
|
||||
* this query has little semantic meaning past "you can call fp()".
|
||||
*/
|
||||
inline bool hasfp() const { return seg_ && seg_->maybeRegs(); }
|
||||
|
||||
/*
|
||||
* Return the most recent script activation's registers with the same
|
||||
* caveat as hasfp regarding JS_SaveFrameChain.
|
||||
*/
|
||||
inline FrameRegs *maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; }
|
||||
inline StackFrame *maybefp() const { return seg_ ? seg_->maybefp() : NULL; }
|
||||
|
||||
/* Faster alternatives to maybe* functions. */
|
||||
inline FrameRegs ®s() const { JS_ASSERT(hasfp()); return seg_->regs(); }
|
||||
inline StackFrame *fp() const { JS_ASSERT(hasfp()); return seg_->fp(); }
|
||||
|
||||
/* The StackSpace currently hosting this ContextStack. */
|
||||
StackSpace &space() const { return *space_; }
|
||||
|
||||
/* Return whether the given frame is in this context's stack. */
|
||||
bool containsSlow(const StackFrame *target) const;
|
||||
|
||||
/*** Stack manipulation ***/
|
||||
|
||||
/*
|
||||
* pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
|
||||
* the arguments to Invoke. A single allocation can be used for multiple
|
||||
* Invoke calls. The InvokeArgumentsGuard passed to Invoke must come from
|
||||
* an immediately-enclosing (stack-wise) call to pushInvokeArgs.
|
||||
*/
|
||||
bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
|
||||
|
||||
/* Called by Invoke for a scripted function call. */
|
||||
bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
||||
InitialFrameFlags initial, InvokeFrameGuard *ifg);
|
||||
|
||||
/* Called by Execute for execution of eval or global code. */
|
||||
bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
|
||||
JSObject &scopeChain, ExecuteType type,
|
||||
StackFrame *evalInFrame, ExecuteFrameGuard *efg);
|
||||
|
||||
/*
|
||||
* Called by SendToGenerator to resume a yielded generator. In addition to
|
||||
* pushing a frame onto the VM stack, this function copies over the
|
||||
* floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
|
||||
* will copy the frame back to the floating frame.
|
||||
*/
|
||||
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
|
||||
|
||||
/*
|
||||
* When changing the compartment of a cx, it is necessary to immediately
|
||||
* change the scope chain to a global in the right compartment since any
|
||||
* amount of general VM code can run before the first scripted frame is
|
||||
* pushed (if at all). This is currently and hackily accomplished by
|
||||
* pushing a "dummy frame" with the correct scope chain. On success, this
|
||||
* function will change the compartment to 'scopeChain.compartment()' and
|
||||
* push a dummy frame for 'scopeChain'. On failure, nothing is changed.
|
||||
*/
|
||||
bool pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg);
|
||||
|
||||
/*
|
||||
* An "inline frame" may only be pushed from within the top, active
|
||||
* segment. This is the case for calls made inside mjit code and Interpret.
|
||||
* The 'stackLimit' overload updates 'stackLimit' if it changes.
|
||||
*/
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction &callee, JSScript *script,
|
||||
InitialFrameFlags initial);
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction &callee, JSScript *script,
|
||||
InitialFrameFlags initial, Value **stackLimit);
|
||||
void popInlineFrame(FrameRegs ®s);
|
||||
|
||||
/* Pop a partially-pushed frame after hitting the limit before throwing. */
|
||||
void popFrameAfterOverflow();
|
||||
|
||||
/* Get the topmost script and optional pc on the stack. */
|
||||
inline JSScript *currentScript(jsbytecode **pc = NULL) const;
|
||||
|
||||
/* Get the scope chain for the topmost scripted call on the stack. */
|
||||
inline JSObject *currentScriptedScopeChain() const;
|
||||
|
||||
/*
|
||||
* Called by the methodjit for an arity mismatch. Arity mismatch can be
|
||||
* hot, so getFixupFrame avoids doing call setup performed by jit code when
|
||||
* FixupArity returns. In terms of work done:
|
||||
*
|
||||
* getFixupFrame = pushInlineFrame -
|
||||
* (fp->initJitFrameLatePrologue + regs->prepareToRun)
|
||||
*/
|
||||
StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report,
|
||||
const CallArgs &args, JSFunction *fun, JSScript *script,
|
||||
void *ncode, InitialFrameFlags initial, Value **stackLimit);
|
||||
|
||||
bool saveFrameChain();
|
||||
void restoreFrameChain();
|
||||
|
||||
/*
|
||||
* As an optimization, the interpreter/mjit can operate on a local
|
||||
* FrameRegs instance repoint the ContextStack to this local instance.
|
||||
*/
|
||||
inline void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
|
||||
|
||||
/*** For JSContext: ***/
|
||||
|
||||
/*
|
||||
* To avoid indirection, ContextSpace caches a pointer to the StackSpace.
|
||||
* This must be kept coherent with cx->thread->data.space by calling
|
||||
* 'threadReset' whenver cx->thread changes.
|
||||
*/
|
||||
void threadReset();
|
||||
|
||||
/*** For jit compiler: ***/
|
||||
|
||||
static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); }
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@ -1485,6 +1780,7 @@ class GeneratorFrameGuard : public FrameGuard
|
||||
* JS_ASSERT(i.isNativeCall());
|
||||
* ... i.args();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The SavedOption parameter additionally lets the iterator continue through
|
||||
* breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
|
||||
@ -1582,5 +1878,4 @@ class AllFramesIter
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* Stack_h__ */
|
||||
|
@ -1,367 +0,0 @@
|
||||
/* -*- 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 *****
|
||||
* 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 SpiderMonkey JavaScript engine.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Luke Wagner <luke@mozilla.com>
|
||||
*
|
||||
* 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
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef StackSpace_h__
|
||||
#define StackSpace_h__
|
||||
|
||||
#include "jsprvtd.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Forward declarations. */
|
||||
class FrameGuard;
|
||||
class DummyFrameGuard;
|
||||
class ExecuteFrameGuard;
|
||||
class GeneratorFrameGuard;
|
||||
|
||||
/* Flags specified for a frame as it is constructed. */
|
||||
enum InitialFrameFlags {
|
||||
INITIAL_NONE = 0,
|
||||
INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */
|
||||
INITIAL_LOWERED = 0x800000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
|
||||
};
|
||||
|
||||
enum ExecuteType {
|
||||
EXECUTE_GLOBAL = 0x1, /* == StackFrame::GLOBAL */
|
||||
EXECUTE_DIRECT_EVAL = 0x8, /* == StackFrame::EVAL */
|
||||
EXECUTE_INDIRECT_EVAL = 0x9, /* == StackFrame::GLOBAL | EVAL */
|
||||
EXECUTE_DEBUG = 0x18 /* == StackFrame::EVAL | DEBUGGER */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class StackSpace
|
||||
{
|
||||
StackSegment *seg_;
|
||||
Value *base_;
|
||||
mutable Value *conservativeEnd_;
|
||||
#ifdef XP_WIN
|
||||
mutable Value *commitEnd_;
|
||||
#endif
|
||||
Value *defaultEnd_;
|
||||
Value *trustedEnd_;
|
||||
|
||||
void assertInvariants() const {
|
||||
JS_ASSERT(base_ <= conservativeEnd_);
|
||||
#ifdef XP_WIN
|
||||
JS_ASSERT(conservativeEnd_ <= commitEnd_);
|
||||
JS_ASSERT(commitEnd_ <= trustedEnd_);
|
||||
#endif
|
||||
JS_ASSERT(conservativeEnd_ <= defaultEnd_);
|
||||
JS_ASSERT(defaultEnd_ <= trustedEnd_);
|
||||
}
|
||||
|
||||
/* The total number of values/bytes reserved for the stack. */
|
||||
static const size_t CAPACITY_VALS = 512 * 1024;
|
||||
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
|
||||
|
||||
/* How much of the stack is initially committed. */
|
||||
static const size_t COMMIT_VALS = 16 * 1024;
|
||||
static const size_t COMMIT_BYTES = COMMIT_VALS * sizeof(Value);
|
||||
|
||||
/* How much space is reserved at the top of the stack for trusted JS. */
|
||||
static const size_t BUFFER_VALS = 16 * 1024;
|
||||
static const size_t BUFFER_BYTES = BUFFER_VALS * sizeof(Value);
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
|
||||
}
|
||||
|
||||
friend class AllFramesIter;
|
||||
friend class ContextStack;
|
||||
friend class StackFrame;
|
||||
|
||||
/*
|
||||
* Except when changing compartment (see pushDummyFrame), the 'dest'
|
||||
* parameter of ensureSpace is cx->compartment. Ideally, we'd just pass
|
||||
* this directly (and introduce a helper that supplies cx->compartment when
|
||||
* no 'dest' is given). For some compilers, this really hurts performance,
|
||||
* so, instead, a trivially sinkable magic constant is used to indicate
|
||||
* that dest should be cx->compartment.
|
||||
*/
|
||||
static const size_t CX_COMPARTMENT = 0xc;
|
||||
|
||||
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest = (JSCompartment *)CX_COMPARTMENT) const;
|
||||
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest) const;
|
||||
|
||||
StackSegment &findContainingSegment(const StackFrame *target) const;
|
||||
|
||||
public:
|
||||
StackSpace();
|
||||
bool init();
|
||||
~StackSpace();
|
||||
|
||||
/*
|
||||
* Maximum supported value of arguments.length. This bounds the maximum
|
||||
* number of arguments that can be supplied to Function.prototype.apply.
|
||||
* This value also bounds the number of elements parsed in an array
|
||||
* initialiser.
|
||||
*
|
||||
* Since arguments are copied onto the stack, the stack size is the
|
||||
* limiting factor for this constant. Use the max stack size (available to
|
||||
* untrusted code) with an extra buffer so that, after such an apply, the
|
||||
* callee can do a little work without OOMing.
|
||||
*/
|
||||
static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS);
|
||||
|
||||
/* See stack layout comment in Stack.h. */
|
||||
inline Value *firstUnused() const;
|
||||
|
||||
StackSegment &containingSegment(const StackFrame *target) const;
|
||||
|
||||
/*
|
||||
* Extra space to reserve on the stack for method JIT frames, beyond the
|
||||
* frame's nslots. This may be used for inlined stack frames, slots storing
|
||||
* loop invariant code, or to reserve space for pushed callee frames. Note
|
||||
* that this space should be reserved when pushing interpreter frames as
|
||||
* well, so that we don't need to check the stack when entering the method
|
||||
* JIT at loop heads or safe points.
|
||||
*/
|
||||
static const size_t STACK_JIT_EXTRA = (/*~VALUES_PER_STACK_FRAME*/ 8 + 18) * 10;
|
||||
|
||||
/*
|
||||
* Return a limit against which jit code can check for. This limit is not
|
||||
* necessarily the end of the stack since we lazily commit stack memory on
|
||||
* some platforms. Thus, when the stack limit is exceeded, the caller should
|
||||
* use tryBumpLimit to attempt to increase the stack limit by committing
|
||||
* more memory. If the stack is truly exhausted, tryBumpLimit will report an
|
||||
* error and return NULL.
|
||||
*
|
||||
* An invariant of the methodjit is that there is always space to push a
|
||||
* frame on top of the current frame's expression stack (which can be at
|
||||
* most script->nslots deep). getStackLimit ensures that the returned limit
|
||||
* does indeed have this required space and reports an error and returns
|
||||
* NULL if this reserve space cannot be allocated.
|
||||
*/
|
||||
inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
|
||||
bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
|
||||
|
||||
/* Called during GC: mark segments, frames, and slots under firstUnused. */
|
||||
void mark(JSTracer *trc);
|
||||
|
||||
/* We only report the committed size; uncommitted size is uninteresting. */
|
||||
JS_FRIEND_API(size_t) sizeOfCommitted();
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class ContextStack
|
||||
{
|
||||
StackSegment *seg_;
|
||||
StackSpace *space_;
|
||||
JSContext *cx_;
|
||||
|
||||
/*
|
||||
* Return whether this ContextStack is at the top of the contiguous stack.
|
||||
* This is a precondition for extending the current segment by pushing
|
||||
* stack frames or overrides etc.
|
||||
*
|
||||
* NB: Just because a stack is onTop() doesn't mean there is necessarily
|
||||
* a frame pushed on the stack. For this, use hasfp().
|
||||
*/
|
||||
bool onTop() const;
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertSpaceInSync() const;
|
||||
#else
|
||||
void assertSpaceInSync() const {}
|
||||
#endif
|
||||
|
||||
/* Implementation details of push* public interface. */
|
||||
StackSegment *pushSegment(JSContext *cx);
|
||||
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
|
||||
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
MaybeExtend extend, bool *pushedSeg,
|
||||
JSCompartment *dest = (JSCompartment *)StackSpace::CX_COMPARTMENT);
|
||||
|
||||
inline StackFrame *
|
||||
getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, /*StackFrame::Flags*/ uint32_t *pflags) const;
|
||||
|
||||
/* Make pop* functions private since only called by guard classes. */
|
||||
void popSegment();
|
||||
friend class InvokeArgsGuard;
|
||||
void popInvokeArgs(const InvokeArgsGuard &iag);
|
||||
friend class FrameGuard;
|
||||
void popFrame(const FrameGuard &fg);
|
||||
friend class GeneratorFrameGuard;
|
||||
void popGeneratorFrame(const GeneratorFrameGuard &gfg);
|
||||
|
||||
friend class StackIter;
|
||||
|
||||
public:
|
||||
ContextStack(JSContext *cx);
|
||||
~ContextStack();
|
||||
|
||||
/*** Stack accessors ***/
|
||||
|
||||
/*
|
||||
* A context's stack is "empty" if there are no scripts or natives
|
||||
* executing. Note that JS_SaveFrameChain does factor into this definition.
|
||||
*/
|
||||
bool empty() const { return !seg_; }
|
||||
|
||||
/*
|
||||
* Return whether there has been at least one frame pushed since the most
|
||||
* recent call to JS_SaveFrameChain. Note that natives do not have frames
|
||||
* and dummy frames are frames that do not represent script execution hence
|
||||
* this query has little semantic meaning past "you can call fp()".
|
||||
*/
|
||||
inline bool hasfp() const;
|
||||
|
||||
/*
|
||||
* Return the most recent script activation's registers with the same
|
||||
* caveat as hasfp regarding JS_SaveFrameChain.
|
||||
*/
|
||||
inline FrameRegs *maybeRegs() const;
|
||||
inline StackFrame *maybefp() const;
|
||||
|
||||
/* Faster alternatives to maybe* functions. */
|
||||
inline FrameRegs ®s() const;
|
||||
inline StackFrame *fp() const;
|
||||
|
||||
/* The StackSpace currently hosting this ContextStack. */
|
||||
StackSpace &space() const { assertSpaceInSync(); return *space_; }
|
||||
|
||||
/* Return whether the given frame is in this context's stack. */
|
||||
bool containsSlow(const StackFrame *target) const;
|
||||
|
||||
/*** Stack manipulation ***/
|
||||
|
||||
/*
|
||||
* pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
|
||||
* the arguments to Invoke. A single allocation can be used for multiple
|
||||
* Invoke calls. The InvokeArgumentsGuard passed to Invoke must come from
|
||||
* an immediately-enclosing (stack-wise) call to pushInvokeArgs.
|
||||
*/
|
||||
bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
|
||||
|
||||
/* Called by Invoke for a scripted function call. */
|
||||
bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
||||
InitialFrameFlags initial, InvokeFrameGuard *ifg);
|
||||
|
||||
/* Called by Execute for execution of eval or global code. */
|
||||
bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
|
||||
JSObject &scopeChain, ExecuteType type,
|
||||
StackFrame *evalInFrame, ExecuteFrameGuard *efg);
|
||||
|
||||
/*
|
||||
* Called by SendToGenerator to resume a yielded generator. In addition to
|
||||
* pushing a frame onto the VM stack, this function copies over the
|
||||
* floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
|
||||
* will copy the frame back to the floating frame.
|
||||
*/
|
||||
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
|
||||
|
||||
/*
|
||||
* When changing the compartment of a cx, it is necessary to immediately
|
||||
* change the scope chain to a global in the right compartment since any
|
||||
* amount of general VM code can run before the first scripted frame is
|
||||
* pushed (if at all). This is currently and hackily accomplished by
|
||||
* pushing a "dummy frame" with the correct scope chain. On success, this
|
||||
* function will change the compartment to 'scopeChain.compartment()' and
|
||||
* push a dummy frame for 'scopeChain'. On failure, nothing is changed.
|
||||
*/
|
||||
bool pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg);
|
||||
|
||||
/*
|
||||
* An "inline frame" may only be pushed from within the top, active
|
||||
* segment. This is the case for calls made inside mjit code and Interpret.
|
||||
* The 'stackLimit' overload updates 'stackLimit' if it changes.
|
||||
*/
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction &callee, JSScript *script,
|
||||
InitialFrameFlags initial);
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction &callee, JSScript *script,
|
||||
InitialFrameFlags initial, Value **stackLimit);
|
||||
void popInlineFrame(FrameRegs ®s);
|
||||
|
||||
/* Pop a partially-pushed frame after hitting the limit before throwing. */
|
||||
void popFrameAfterOverflow();
|
||||
|
||||
/* Get the topmost script and optional pc on the stack. */
|
||||
inline JSScript *currentScript(jsbytecode **pc = NULL) const;
|
||||
|
||||
/* Get the scope chain for the topmost scripted call on the stack. */
|
||||
inline JSObject *currentScriptedScopeChain() const;
|
||||
|
||||
/*
|
||||
* Called by the methodjit for an arity mismatch. Arity mismatch can be
|
||||
* hot, so getFixupFrame avoids doing call setup performed by jit code when
|
||||
* FixupArity returns. In terms of work done:
|
||||
*
|
||||
* getFixupFrame = pushInlineFrame -
|
||||
* (fp->initJitFrameLatePrologue + regs->prepareToRun)
|
||||
*/
|
||||
StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report,
|
||||
const CallArgs &args, JSFunction *fun, JSScript *script,
|
||||
void *ncode, InitialFrameFlags initial, Value **stackLimit);
|
||||
|
||||
bool saveFrameChain();
|
||||
void restoreFrameChain();
|
||||
|
||||
/*
|
||||
* As an optimization, the interpreter/mjit can operate on a local
|
||||
* FrameRegs instance repoint the ContextStack to this local instance.
|
||||
*/
|
||||
inline void repointRegs(FrameRegs *regs);
|
||||
|
||||
/*** For JSContext: ***/
|
||||
|
||||
/*
|
||||
* To avoid indirection, ContextSpace caches a pointer to the StackSpace.
|
||||
* This must be kept coherent with cx->thread->data.space by calling
|
||||
* 'threadReset' whenver cx->thread changes.
|
||||
*/
|
||||
void threadReset();
|
||||
|
||||
/*** For jit compiler: ***/
|
||||
|
||||
static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); }
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* StackSpace_h__ */
|
@ -118,9 +118,11 @@ XPCTraceableVariant::PrintTraceName(JSTracer* trc, char *buf, size_t bufsize)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
|
||||
jsval val = tmp->GetJSValPreserveColor();
|
||||
if (JSVAL_IS_OBJECT(val))
|
||||
if (JSVAL_IS_OBJECT(val)) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSVal");
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
JSVAL_TO_OBJECT(val));
|
||||
}
|
||||
|
||||
nsVariant::Traverse(tmp->mData, cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
@ -75,21 +75,27 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
|
||||
|
||||
// nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the
|
||||
// comment above nsXPCWrappedJS::AddRef.
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
|
||||
cb.NoteXPCOMChild(s);
|
||||
|
||||
if (refcnt > 1)
|
||||
if (refcnt > 1) {
|
||||
// nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see
|
||||
// the comment above nsXPCWrappedJS::AddRef.
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
tmp->GetJSObjectPreserveColor());
|
||||
}
|
||||
|
||||
nsXPCWrappedJS* root = tmp->GetRootWrapper();
|
||||
if (root == tmp)
|
||||
if (root == tmp) {
|
||||
// The root wrapper keeps the aggregated native object alive.
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
|
||||
cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
|
||||
else
|
||||
} else {
|
||||
// Non-root wrappers keep their root alive.
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
|
||||
cb.NoteXPCOMChild(static_cast<nsIXPConnectWrappedJS*>(root));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -826,8 +826,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
|
||||
NS_ASSERTION(mSet, "bad ctor param");
|
||||
|
||||
DEBUG_TrackNewWrapper(this);
|
||||
|
||||
JS_RegisterReference((void **) &mWrapperWord);
|
||||
}
|
||||
|
||||
// This ctor is used if this object will NOT have a proto.
|
||||
@ -850,8 +848,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
|
||||
NS_ASSERTION(aSet, "bad ctor param");
|
||||
|
||||
DEBUG_TrackNewWrapper(this);
|
||||
|
||||
JS_RegisterReference((void **) &mWrapperWord);
|
||||
}
|
||||
|
||||
XPCWrappedNative::~XPCWrappedNative()
|
||||
@ -909,7 +905,8 @@ XPCWrappedNative::Destroy()
|
||||
* the first time because mWrapperWord isn't used afterwards.
|
||||
*/
|
||||
if (XPCJSRuntime *rt = GetRuntime()) {
|
||||
JS_UnregisterReferenceRT(rt->GetJSRuntime(), (void **) &mWrapperWord);
|
||||
if (js::IsIncrementalBarrierNeeded(rt->GetJSRuntime()))
|
||||
js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
|
||||
mWrapperWord = WRAPPER_WORD_POISON;
|
||||
} else {
|
||||
MOZ_ASSERT(mWrapperWord == WRAPPER_WORD_POISON);
|
||||
@ -925,7 +922,7 @@ XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si)
|
||||
|
||||
// Write barrier for incremental GC.
|
||||
JSRuntime* rt = GetRuntime()->GetJSRuntime();
|
||||
if (JS_GetIncrementalGCTracer(rt))
|
||||
if (js::IsIncrementalBarrierNeeded(rt))
|
||||
mScriptableInfo->Mark();
|
||||
|
||||
mScriptableInfo = si;
|
||||
@ -936,12 +933,11 @@ XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
|
||||
{
|
||||
NS_ASSERTION(!IsWrapperExpired(), "bad ptr!");
|
||||
|
||||
MOZ_ASSERT(HasProto());
|
||||
|
||||
// Write barrier for incremental GC.
|
||||
if (HasProto()) {
|
||||
JSRuntime* rt = GetRuntime()->GetJSRuntime();
|
||||
if (JS_GetIncrementalGCTracer(rt))
|
||||
GetProto()->TraceJS(JS_GetIncrementalGCTracer(rt));
|
||||
}
|
||||
JSRuntime* rt = GetRuntime()->GetJSRuntime();
|
||||
GetProto()->WriteBarrierPre(rt);
|
||||
|
||||
mMaybeProto = p;
|
||||
}
|
||||
|
@ -1023,8 +1023,8 @@ public:
|
||||
// collected.
|
||||
unsigned refCount = nsXPConnect::GetXPConnect()->GetOutstandingRequests(cx) + 1;
|
||||
cb.DescribeRefCountedNode(refCount, js::SizeOfJSContext(), "JSContext");
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]");
|
||||
if (JSObject *global = JS_GetGlobalObject(cx)) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]");
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, global);
|
||||
}
|
||||
|
||||
|
@ -1654,10 +1654,10 @@ private:
|
||||
// default parent for the XPCWrappedNatives that have us as the scope,
|
||||
// unless a PreCreate hook overrides it. Note that this _may_ be null (see
|
||||
// constructor).
|
||||
JS::HeapPtrObject mGlobalJSObject;
|
||||
js::ObjectPtr mGlobalJSObject;
|
||||
|
||||
// Cached value of Object.prototype
|
||||
JS::HeapPtrObject mPrototypeJSObject;
|
||||
js::ObjectPtr mPrototypeJSObject;
|
||||
// Prototype to use for wrappers with no helper.
|
||||
JSObject* mPrototypeNoHelper;
|
||||
|
||||
@ -2306,6 +2306,12 @@ public:
|
||||
mScriptableInfo->Mark();
|
||||
}
|
||||
|
||||
void WriteBarrierPre(JSRuntime* rt)
|
||||
{
|
||||
if (js::IsIncrementalBarrierNeeded(rt) && mJSProtoObject)
|
||||
mJSProtoObject.writeBarrierPre(rt);
|
||||
}
|
||||
|
||||
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
||||
inline void AutoTrace(JSTracer* trc) {}
|
||||
|
||||
@ -2348,7 +2354,7 @@ private:
|
||||
}
|
||||
|
||||
XPCWrappedNativeScope* mScope;
|
||||
JS::HeapPtrObject mJSProtoObject;
|
||||
js::ObjectPtr mJSProtoObject;
|
||||
nsCOMPtr<nsIClassInfo> mClassInfo;
|
||||
PRUint32 mClassInfoFlags;
|
||||
XPCNativeSet* mSet;
|
||||
@ -2737,8 +2743,9 @@ public:
|
||||
}
|
||||
void SetWrapper(JSObject *obj)
|
||||
{
|
||||
js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
|
||||
PRWord newval = PRWord(obj) | (mWrapperWord & FLAG_MASK);
|
||||
JS_ModifyReference((void **)&mWrapperWord, (void *)newval);
|
||||
mWrapperWord = newval;
|
||||
}
|
||||
|
||||
void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
|
||||
|
@ -235,14 +235,12 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
|
||||
-I$(srcdir)/../forms \
|
||||
-I$(srcdir)/../tables \
|
||||
-I$(srcdir)/../style \
|
||||
-I$(srcdir)/../xul/content/src \
|
||||
-I$(srcdir)/../xul/base/src \
|
||||
-I$(srcdir)/../mathml \
|
||||
-I$(topsrcdir)/content/base/src \
|
||||
-I$(topsrcdir)/content/canvas/src \
|
||||
-I$(topsrcdir)/content/html/content/src \
|
||||
-I$(topsrcdir)/content/html/document/src \
|
||||
-I$(topsrcdir)/content/html/style/src \
|
||||
-I$(topsrcdir)/content/xslt/src/base \
|
||||
-I$(topsrcdir)/content/xslt/src/xml \
|
||||
-I$(topsrcdir)/content/xslt/src/xpath \
|
||||
|
@ -54,7 +54,6 @@ LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../base \
|
||||
-I$(srcdir)/../generic \
|
||||
-I$(srcdir)/../tables \
|
||||
-I$(srcdir)/content/src \
|
||||
-I$(topsrcdir)/content/base/src \
|
||||
-I$(topsrcdir)/content/mathml/content/src \
|
||||
-I$(srcdir)/../xul/base/src \
|
||||
|
10
layout/reftests/svg/dynamic-text-08-ref.svg
Normal file
10
layout/reftests/svg/dynamic-text-08-ref.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<title>Reference for scaling from zero</title>
|
||||
|
||||
<text x="100" y="50" font-size="50" text-anchor="middle">ABC</text>
|
||||
</svg>
|
After Width: | Height: | Size: 287 B |
24
layout/reftests/svg/dynamic-text-08.svg
Normal file
24
layout/reftests/svg/dynamic-text-08.svg
Normal file
@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="m();">
|
||||
|
||||
<title>Testcase to scaling from zero</title>
|
||||
|
||||
<g id="g" transform="scale(0)">
|
||||
<text x="100" y="50" font-size="50" text-anchor="middle">ABC</text>
|
||||
</g>
|
||||
<script>
|
||||
function m()
|
||||
{
|
||||
// Force frame construction
|
||||
document.documentElement.getBoundingClientRect();
|
||||
|
||||
// A dynamic change
|
||||
document.getElementById("g").removeAttribute("transform");
|
||||
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
</svg>
|
After Width: | Height: | Size: 661 B |
@ -265,7 +265,8 @@ nsSVGDisplayContainerFrame::GetBBoxContribution(
|
||||
const gfxMatrix &aToBBoxUserspace,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
gfxRect bboxUnion(0.0, 0.0, 0.0, 0.0);
|
||||
gfxRect bboxUnion;
|
||||
bool firstChild = true;
|
||||
|
||||
nsIFrame* kid = mFrames.FirstChild();
|
||||
while (kid) {
|
||||
@ -277,8 +278,16 @@ nsSVGDisplayContainerFrame::GetBBoxContribution(
|
||||
transform = static_cast<nsSVGElement*>(content)->
|
||||
PrependLocalTransformTo(aToBBoxUserspace);
|
||||
}
|
||||
bboxUnion =
|
||||
bboxUnion.Union(svgKid->GetBBoxContribution(transform, aFlags));
|
||||
// We need to include zero width/height vertical/horizontal lines, so we have
|
||||
// to use UnionEdges, but we must special case the first bbox so that we don't
|
||||
// include the initial gfxRect(0,0,0,0).
|
||||
gfxRect childBBox = svgKid->GetBBoxContribution(transform, aFlags);
|
||||
if (firstChild) {
|
||||
bboxUnion = childBBox;
|
||||
firstChild = false;
|
||||
continue;
|
||||
}
|
||||
bboxUnion = bboxUnion.UnionEdges(childBBox);
|
||||
}
|
||||
kid = kid->GetNextSibling();
|
||||
}
|
||||
|
@ -203,14 +203,15 @@ nsSVGMarkerFrame::GetMarkBBoxContribution(const gfxMatrix &aToBBoxUserspace,
|
||||
mY = aMark->y;
|
||||
mAutoAngle = aMark->angle;
|
||||
|
||||
gfxRect bbox;
|
||||
|
||||
gfxMatrix markerTM =
|
||||
content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle);
|
||||
gfxMatrix viewBoxTM = content->GetViewBoxTransform();
|
||||
|
||||
gfxMatrix tm = viewBoxTM * markerTM * aToBBoxUserspace;
|
||||
|
||||
gfxRect bbox;
|
||||
bool firstChild = true;
|
||||
|
||||
for (nsIFrame* kid = mFrames.FirstChild();
|
||||
kid;
|
||||
kid = kid->GetNextSibling()) {
|
||||
@ -219,7 +220,17 @@ nsSVGMarkerFrame::GetMarkBBoxContribution(const gfxMatrix &aToBBoxUserspace,
|
||||
// When we're being called to obtain the invalidation area, we need to
|
||||
// pass down all the flags so that stroke is included. However, once DOM
|
||||
// getBBox() accepts flags, maybe we should strip some of those here?
|
||||
bbox.UnionRect(bbox, child->GetBBoxContribution(tm, aFlags));
|
||||
|
||||
// We need to include zero width/height vertical/horizontal lines, so we have
|
||||
// to use UnionEdges, but we must special case the first bbox so that we don't
|
||||
// include the initial gfxRect(0,0,0,0).
|
||||
gfxRect childBBox = child->GetBBoxContribution(tm, aFlags);
|
||||
if (firstChild) {
|
||||
bbox = childBBox;
|
||||
firstChild = false;
|
||||
continue;
|
||||
}
|
||||
bbox = bbox.UnionEdges(childBBox);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,22 +197,6 @@ abstract public class GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
void showErrorDialog(String message)
|
||||
{
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(message)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.exit_label,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog,
|
||||
int id)
|
||||
{
|
||||
GeckoApp.this.finish();
|
||||
System.exit(0);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
|
||||
|
||||
/**
|
||||
@ -440,6 +424,7 @@ abstract public class GeckoApp
|
||||
MenuItem share = aMenu.findItem(R.id.share);
|
||||
MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
|
||||
MenuItem downloads = aMenu.findItem(R.id.downloads);
|
||||
MenuItem charEncoding = aMenu.findItem(R.id.char_encoding);
|
||||
|
||||
if (tab == null) {
|
||||
bookmark.setEnabled(false);
|
||||
@ -476,6 +461,8 @@ abstract public class GeckoApp
|
||||
if (Build.VERSION.SDK_INT < 12)
|
||||
downloads.setVisible(false);
|
||||
|
||||
charEncoding.setVisible(GeckoPreferences.getCharEncodingState());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -538,6 +525,9 @@ abstract public class GeckoApp
|
||||
intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.char_encoding:
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent("CharEncoding:Get", null));
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@ -985,6 +975,51 @@ abstract public class GeckoApp
|
||||
int size = message.getInt("size");
|
||||
|
||||
handleDownloadDone(displayName, path, mimeType, size);
|
||||
} else if (event.equals("CharEncoding:Data")) {
|
||||
final JSONArray charsets = message.getJSONArray("charsets");
|
||||
int selected = message.getInt("selected");
|
||||
|
||||
final int len = charsets.length();
|
||||
final String[] titleArray = new String[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
JSONObject charset = charsets.getJSONObject(i);
|
||||
titleArray[i] = charset.getString("title");
|
||||
}
|
||||
|
||||
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
|
||||
dialogBuilder.setSingleChoiceItems(titleArray, selected, new AlertDialog.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
try {
|
||||
JSONObject charset = charsets.getJSONObject(which);
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent("CharEncoding:Set", charset.getString("code")));
|
||||
dialog.dismiss();
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "error parsing json", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
dialogBuilder.setNegativeButton(R.string.button_cancel, new AlertDialog.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
dialogBuilder.show();
|
||||
}
|
||||
});
|
||||
} else if (event.equals("CharEncoding:State")) {
|
||||
final boolean visible = message.getString("visible").equals("true");
|
||||
GeckoPreferences.setCharEncodingState(visible);
|
||||
if (sMenu != null) {
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
sMenu.findItem(R.id.char_encoding).setVisible(visible);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
|
||||
@ -1559,12 +1594,7 @@ abstract public class GeckoApp
|
||||
try {
|
||||
Looper.loop();
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "top level exception", e);
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
pw.flush();
|
||||
GeckoAppShell.reportJavaCrash(sw.toString());
|
||||
GeckoAppShell.reportJavaCrash(e);
|
||||
}
|
||||
// resetting this is kinda pointless, but oh well
|
||||
sTryCatchAttached = false;
|
||||
@ -1598,6 +1628,8 @@ abstract public class GeckoApp
|
||||
GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("Downloads:Done", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext);
|
||||
|
||||
mConnectivityFilter = new IntentFilter();
|
||||
mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
@ -1926,6 +1958,8 @@ abstract public class GeckoApp
|
||||
GeckoAppShell.unregisterGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext);
|
||||
GeckoAppShell.unregisterGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
|
||||
GeckoAppShell.unregisterGeckoEventListener("Downloads:Done", GeckoApp.mAppContext);
|
||||
GeckoAppShell.unregisterGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext);
|
||||
GeckoAppShell.unregisterGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext);
|
||||
|
||||
mFavicons.close();
|
||||
|
||||
|
@ -133,7 +133,18 @@ public class GeckoAppShell
|
||||
public static native void loadGeckoLibsNative(String apkName);
|
||||
public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
|
||||
public static native void onChangeNetworkLinkStatus(String status);
|
||||
public static native void reportJavaCrash(String stack);
|
||||
|
||||
public static void reportJavaCrash(Throwable e) {
|
||||
Log.e(LOGTAG, "top level exception", e);
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
pw.flush();
|
||||
reportJavaCrash(sw.toString());
|
||||
}
|
||||
|
||||
private static native void reportJavaCrash(String stackTrace);
|
||||
|
||||
public static void notifyUriVisited(String uri) {
|
||||
sendEventToGecko(new GeckoEvent(GeckoEvent.VISTITED, uri));
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public class GeckoPreferences
|
||||
|
||||
private ArrayList<String> mPreferencesList = new ArrayList<String>();
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
private static boolean sIsCharEncodingEnabled = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -140,12 +141,22 @@ public class GeckoPreferences
|
||||
final private int DIALOG_CREATE_MASTER_PASSWORD = 0;
|
||||
final private int DIALOG_REMOVE_MASTER_PASSWORD = 1;
|
||||
|
||||
public static void setCharEncodingState(boolean enabled) {
|
||||
sIsCharEncodingEnabled = enabled;
|
||||
}
|
||||
|
||||
public static boolean getCharEncodingState() {
|
||||
return sIsCharEncodingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String prefName = preference.getKey();
|
||||
if (prefName != null && prefName.equals("privacy.masterpassword.enabled")) {
|
||||
showDialog((Boolean)newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD);
|
||||
return false;
|
||||
} else if (prefName != null && prefName.equals("browser.menu.showCharacterEncoding")) {
|
||||
setCharEncodingState(((String) newValue).equals("true"));
|
||||
}
|
||||
|
||||
setPreference(prefName, newValue);
|
||||
|
@ -106,12 +106,7 @@ public class GeckoThread extends Thread {
|
||||
mUri,
|
||||
mRestoreSession);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "top level exception", e);
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
pw.flush();
|
||||
GeckoAppShell.reportJavaCrash(sw.toString());
|
||||
GeckoAppShell.reportJavaCrash(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ FENNEC_JAVA_FILES = \
|
||||
gfx/WidgetTileLayer.java \
|
||||
ui/Axis.java \
|
||||
ui/PanZoomController.java \
|
||||
ui/SimpleScaleGestureDetector.java \
|
||||
ui/SubdocumentScrollHelper.java \
|
||||
GeckoNetworkManager.java \
|
||||
$(NULL)
|
||||
@ -141,6 +142,7 @@ FENNEC_PP_JAVA_FILES = \
|
||||
db/BrowserContract.java \
|
||||
db/BrowserProvider.java \
|
||||
SmsManager.java \
|
||||
SyncPreference.java \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
81
mobile/android/base/SyncPreference.java.in
Normal file
81
mobile/android/base/SyncPreference.java.in
Normal file
@ -0,0 +1,81 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* ***** 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 Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Nicholson <bnicholson@mozilla.com>
|
||||
*
|
||||
* 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
|
||||
* 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 ***** */
|
||||
|
||||
#filter substitution
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
class SyncPreference extends Preference {
|
||||
private static final String FEEDS_PACKAGE_NAME = "com.android.providers.subscribedfeeds";
|
||||
private static final String ACCOUNT_SYNC_CLASS_NAME = "com.android.settings.AccountSyncSettings";
|
||||
private static final String ACCOUNT_KEY = "account";
|
||||
private static final String FENNEC_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@";
|
||||
private static final String FENNEC_SYNC_CLASS_NAME = "org.mozilla.gecko.sync.setup.activities.SetupSyncActivity";
|
||||
private static final String FENNEC_ACCOUNT_TYPE = "org.mozilla.firefox_sync";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public SyncPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
Account[] accounts = AccountManager.get(mContext).getAccountsByType(FENNEC_ACCOUNT_TYPE);
|
||||
if (accounts.length > 0) {
|
||||
// show sync account
|
||||
// we assume there's exactly one sync account. see bugs 716906 and 710407.
|
||||
intent.setComponent(new ComponentName(FEEDS_PACKAGE_NAME, ACCOUNT_SYNC_CLASS_NAME));
|
||||
Account account = accounts[0];
|
||||
intent.putExtra(ACCOUNT_KEY, account);
|
||||
} else {
|
||||
// show sync setup
|
||||
intent.setComponent(new ComponentName(FENNEC_PACKAGE_NAME, FENNEC_SYNC_CLASS_NAME));
|
||||
}
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
}
|
@ -43,6 +43,7 @@ import org.mozilla.gecko.gfx.Layer;
|
||||
import org.mozilla.gecko.gfx.LayerClient;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.ui.PanZoomController;
|
||||
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
@ -147,7 +148,9 @@ public class LayerController {
|
||||
public Bitmap getShadowPattern() { return getDrawable("shadow"); }
|
||||
|
||||
public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; }
|
||||
public ScaleGestureDetector.OnScaleGestureListener getScaleGestureListener() { return mPanZoomController; }
|
||||
public SimpleScaleGestureDetector.SimpleScaleGestureListener getScaleGestureListener() {
|
||||
return mPanZoomController;
|
||||
}
|
||||
public GestureDetector.OnDoubleTapListener getDoubleTapListener() { return mPanZoomController; }
|
||||
|
||||
private Bitmap getDrawable(String name) {
|
||||
|
@ -40,6 +40,7 @@ package org.mozilla.gecko.gfx;
|
||||
import org.mozilla.gecko.gfx.FloatSize;
|
||||
import org.mozilla.gecko.gfx.InputConnectionHandler;
|
||||
import org.mozilla.gecko.gfx.LayerController;
|
||||
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.view.GestureDetector;
|
||||
@ -47,7 +48,6 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.ScaleGestureDetector;
|
||||
|
||||
/**
|
||||
* A view rendered by the layer compositor.
|
||||
@ -61,7 +61,7 @@ public class LayerView extends GLSurfaceView {
|
||||
private InputConnectionHandler mInputConnectionHandler;
|
||||
private LayerRenderer mRenderer;
|
||||
private GestureDetector mGestureDetector;
|
||||
private ScaleGestureDetector mScaleGestureDetector;
|
||||
private SimpleScaleGestureDetector mScaleGestureDetector;
|
||||
private long mRenderTime;
|
||||
private boolean mRenderTimeReset;
|
||||
|
||||
@ -74,7 +74,8 @@ public class LayerView extends GLSurfaceView {
|
||||
setRenderer(mRenderer);
|
||||
setRenderMode(RENDERMODE_WHEN_DIRTY);
|
||||
mGestureDetector = new GestureDetector(context, controller.getGestureListener());
|
||||
mScaleGestureDetector = new ScaleGestureDetector(context, controller.getScaleGestureListener());
|
||||
mScaleGestureDetector =
|
||||
new SimpleScaleGestureDetector(controller.getScaleGestureListener());
|
||||
mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener());
|
||||
mInputConnectionHandler = null;
|
||||
|
||||
|
@ -39,6 +39,7 @@ package org.mozilla.gecko.gfx;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.util.FloatMath;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
@ -77,6 +78,11 @@ public final class PointUtils {
|
||||
return (float)Math.sqrt(point.x * point.x + point.y * point.y);
|
||||
}
|
||||
|
||||
/** Computes the scalar distance between two points. */
|
||||
public static float distance(PointF one, PointF two) {
|
||||
return PointF.length(one.x - two.x, one.y - two.y);
|
||||
}
|
||||
|
||||
public static JSONObject toJSON(PointF point) throws JSONException {
|
||||
// Ensure we put ints, not longs, because Gecko message handlers call getInt().
|
||||
int x = Math.round(point.x);
|
||||
|
@ -51,7 +51,9 @@
|
||||
<!ENTITY pref_telemetry "Send performance data">
|
||||
<!ENTITY pref_remember_signons "Remember passwords">
|
||||
<!ENTITY pref_cookies "Enable cookies">
|
||||
<!ENTITY pref_char_encoding "Show character encoding">
|
||||
<!ENTITY pref_char_encoding "Character encoding">
|
||||
<!ENTITY pref_char_encoding_on "Show menu">
|
||||
<!ENTITY pref_char_encoding_off "Don\'t show menu">
|
||||
<!ENTITY pref_clear_history "Clear history">
|
||||
<!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
|
||||
<!ENTITY pref_clear_private_data "Clear private data">
|
||||
@ -67,11 +69,13 @@
|
||||
<!ENTITY pref_font_size_large "Large">
|
||||
<!ENTITY pref_font_size_xlarge "Extra Large">
|
||||
<!ENTITY pref_use_master_password "Use master password">
|
||||
<!ENTITY pref_sync "Sync">
|
||||
|
||||
<!ENTITY quit "Quit">
|
||||
|
||||
<!ENTITY addons "Add-ons">
|
||||
<!ENTITY downloads "Downloads">
|
||||
<!ENTITY char_encoding "Character Encoding">
|
||||
|
||||
<!ENTITY share "Share">
|
||||
<!ENTITY save_as_pdf "Save as PDF">
|
||||
|
@ -30,6 +30,10 @@
|
||||
<item android:id="@+id/downloads"
|
||||
android:title="@string/downloads"/>
|
||||
|
||||
<item android:id="@+id/char_encoding"
|
||||
android:visible="false"
|
||||
android:title="@string/char_encoding"/>
|
||||
|
||||
<item android:id="@+id/settings"
|
||||
android:title="@string/settings" />
|
||||
|
||||
|
@ -25,4 +25,12 @@
|
||||
<item>160</item>
|
||||
<item>240</item>
|
||||
</string-array>
|
||||
<string-array name="pref_char_encoding_entries">
|
||||
<item>@string/pref_char_encoding_on</item>
|
||||
<item>@string/pref_char_encoding_off</item>
|
||||
</string-array>
|
||||
<string-array name="pref_char_encoding_values">
|
||||
<item>true</item>
|
||||
<item>false</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
@ -7,16 +7,17 @@
|
||||
<org.mozilla.gecko.LinkPreference android:title="@string/pref_about_firefox"
|
||||
url="about:" />
|
||||
|
||||
<!-- TODO: Default Search Engine -->
|
||||
|
||||
<!-- TODO: Sync -->
|
||||
<org.mozilla.gecko.SyncPreference android:title="@string/pref_sync"
|
||||
android:persistent="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_category_content">
|
||||
|
||||
<CheckBoxPreference android:key="browser.menu.showCharacterEncoding"
|
||||
<ListPreference android:key="browser.menu.showCharacterEncoding"
|
||||
android:title="@string/pref_char_encoding"
|
||||
android:entries="@array/pref_char_encoding_entries"
|
||||
android:entryValues="@array/pref_char_encoding_values"
|
||||
android:persistent="false" />
|
||||
|
||||
<ListPreference android:key="plugin.enable"
|
||||
|
@ -58,6 +58,8 @@
|
||||
<string name="pref_remember_signons">&pref_remember_signons;</string>
|
||||
<string name="pref_cookies">&pref_cookies;</string>
|
||||
<string name="pref_char_encoding">&pref_char_encoding;</string>
|
||||
<string name="pref_char_encoding_on">&pref_char_encoding_on;</string>
|
||||
<string name="pref_char_encoding_off">&pref_char_encoding_off;</string>
|
||||
<string name="pref_clear_history">&pref_clear_history;</string>
|
||||
<string name="pref_clear_history_confirm">&pref_clear_history_confirm;</string>
|
||||
<string name="pref_clear_private_data">&pref_clear_private_data;</string>
|
||||
@ -72,12 +74,14 @@
|
||||
<string name="pref_font_size_medium">&pref_font_size_medium;</string>
|
||||
<string name="pref_font_size_large">&pref_font_size_large;</string>
|
||||
<string name="pref_font_size_xlarge">&pref_font_size_xlarge;</string>
|
||||
<string name="pref_sync">&pref_sync;</string>
|
||||
|
||||
<string name="reload">&reload;</string>
|
||||
<string name="forward">&forward;</string>
|
||||
<string name="new_tab">&new_tab;</string>
|
||||
<string name="addons">&addons;</string>
|
||||
<string name="downloads">&downloads;</string>
|
||||
<string name="char_encoding">&char_encoding;</string>
|
||||
|
||||
<string name="site_settings_title">&site_settings_title;</string>
|
||||
<string name="site_settings_cancel">&site_settings_cancel;</string>
|
||||
|
@ -55,7 +55,6 @@ import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
@ -67,7 +66,7 @@ import java.util.TimerTask;
|
||||
*/
|
||||
public class PanZoomController
|
||||
extends GestureDetector.SimpleOnGestureListener
|
||||
implements ScaleGestureDetector.OnScaleGestureListener, GeckoEventListener
|
||||
implements SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener
|
||||
{
|
||||
private static final String LOGTAG = "GeckoPanZoomController";
|
||||
|
||||
@ -721,7 +720,7 @@ public class PanZoomController
|
||||
* Zooming
|
||||
*/
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
public boolean onScaleBegin(SimpleScaleGestureDetector detector) {
|
||||
Log.d(LOGTAG, "onScaleBegin in " + mState);
|
||||
|
||||
if (mState == PanZoomState.ANIMATED_ZOOM)
|
||||
@ -737,7 +736,7 @@ public class PanZoomController
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
public boolean onScale(SimpleScaleGestureDetector detector) {
|
||||
Log.d(LOGTAG, "onScale in state " + mState);
|
||||
|
||||
if (mState == PanZoomState.ANIMATED_ZOOM)
|
||||
@ -784,7 +783,7 @@ public class PanZoomController
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
public void onScaleEnd(SimpleScaleGestureDetector detector) {
|
||||
Log.d(LOGTAG, "onScaleEnd in " + mState);
|
||||
|
||||
if (mState == PanZoomState.ANIMATED_ZOOM)
|
||||
|
327
mobile/android/base/ui/SimpleScaleGestureDetector.java
Normal file
327
mobile/android/base/ui/SimpleScaleGestureDetector.java
Normal file
@ -0,0 +1,327 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** 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 Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* 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
|
||||
* 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 ***** */
|
||||
|
||||
package org.mozilla.gecko.ui;
|
||||
|
||||
import org.mozilla.gecko.gfx.PointUtils;
|
||||
import org.json.JSONException;
|
||||
import android.graphics.PointF;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector.
|
||||
*
|
||||
* This gesture detector is more reliable than the built-in ScaleGestureDetector because:
|
||||
*
|
||||
* - It doesn't assume that pointer IDs are numbered 0 and 1.
|
||||
*
|
||||
* - It doesn't attempt to correct for "slop" when resting one's hand on the device. On some
|
||||
* devices (e.g. the Droid X) this can cause the ScaleGestureDetector to lose track of how many
|
||||
* pointers are down, with disastrous results (bug 706684).
|
||||
*
|
||||
* - Cancelling a zoom into a pan is handled correctly.
|
||||
*
|
||||
* - Starting with three or more fingers down, releasing fingers so that only two are down, and
|
||||
* then performing a scale gesture is handled correctly.
|
||||
*
|
||||
* - It doesn't take pressure into account, which results in smoother scaling.
|
||||
*/
|
||||
public class SimpleScaleGestureDetector {
|
||||
private static final String LOGTAG = "GeckoSimpleScaleGestureDetector";
|
||||
|
||||
private SimpleScaleGestureListener mListener;
|
||||
private long mLastEventTime;
|
||||
|
||||
/* Information about all pointers that are down. */
|
||||
private LinkedList<PointerInfo> mPointerInfo;
|
||||
|
||||
/** Creates a new gesture detector with the given listener. */
|
||||
public SimpleScaleGestureDetector(SimpleScaleGestureListener listener) {
|
||||
mListener = listener;
|
||||
mPointerInfo = new LinkedList<PointerInfo>();
|
||||
}
|
||||
|
||||
/** Forward touch events to this function. */
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction() & event.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
onTouchStart(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
onTouchMove(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
onTouchEnd(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private int getPointersDown() {
|
||||
return mPointerInfo.size();
|
||||
}
|
||||
|
||||
private void onTouchStart(MotionEvent event) {
|
||||
mLastEventTime = event.getEventTime();
|
||||
mPointerInfo.push(PointerInfo.create(event, event.getActionIndex()));
|
||||
if (getPointersDown() == 2) {
|
||||
sendScaleGesture(EventType.BEGIN);
|
||||
}
|
||||
}
|
||||
|
||||
private void onTouchMove(MotionEvent event) {
|
||||
mLastEventTime = event.getEventTime();
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
PointerInfo pointerInfo = pointerInfoForEventIndex(event, i);
|
||||
if (pointerInfo != null) {
|
||||
pointerInfo.populate(event, i);
|
||||
}
|
||||
}
|
||||
|
||||
if (getPointersDown() == 2) {
|
||||
sendScaleGesture(EventType.CONTINUE);
|
||||
}
|
||||
}
|
||||
|
||||
private void onTouchEnd(MotionEvent event) {
|
||||
mLastEventTime = event.getEventTime();
|
||||
|
||||
int id = event.getPointerId(event.getActionIndex());
|
||||
ListIterator<PointerInfo> iterator = mPointerInfo.listIterator();
|
||||
while (iterator.hasNext()) {
|
||||
PointerInfo pointerInfo = iterator.next();
|
||||
if (pointerInfo.getId() != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// One of the pointers we were tracking was lifted. Remove its info object from the
|
||||
// list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this
|
||||
// ended the gesture.
|
||||
iterator.remove();
|
||||
pointerInfo.recycle();
|
||||
if (getPointersDown() == 1) {
|
||||
sendScaleGesture(EventType.END);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the X coordinate of the focus location (the midpoint of the two fingers). If only
|
||||
* one finger is down, returns the location of that finger.
|
||||
*/
|
||||
public float getFocusX() {
|
||||
switch (getPointersDown()) {
|
||||
case 1:
|
||||
return mPointerInfo.getFirst().getCurrent().x;
|
||||
case 2:
|
||||
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
|
||||
return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f;
|
||||
}
|
||||
|
||||
Log.e(LOGTAG, "No gesture taking place in getFocusX()!");
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y coordinate of the focus location (the midpoint of the two fingers). If only
|
||||
* one finger is down, returns the location of that finger.
|
||||
*/
|
||||
public float getFocusY() {
|
||||
switch (getPointersDown()) {
|
||||
case 1:
|
||||
return mPointerInfo.getFirst().getCurrent().y;
|
||||
case 2:
|
||||
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
|
||||
return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f;
|
||||
}
|
||||
|
||||
Log.e(LOGTAG, "No gesture taking place in getFocusY()!");
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/** Returns the most recent distance between the two pointers. */
|
||||
public float getCurrentSpan() {
|
||||
if (getPointersDown() != 2) {
|
||||
Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!");
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
|
||||
return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent());
|
||||
}
|
||||
|
||||
/** Returns the second most recent distance between the two pointers. */
|
||||
public float getPreviousSpan() {
|
||||
if (getPointersDown() != 2) {
|
||||
Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!");
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
|
||||
PointF a = pointerA.getPrevious(), b = pointerB.getPrevious();
|
||||
if (a == null || b == null) {
|
||||
a = pointerA.getCurrent();
|
||||
b = pointerB.getCurrent();
|
||||
}
|
||||
|
||||
return PointUtils.distance(a, b);
|
||||
}
|
||||
|
||||
/** Returns the time of the last event related to the gesture. */
|
||||
public long getEventTime() {
|
||||
return mLastEventTime;
|
||||
}
|
||||
|
||||
/** Returns true if the scale gesture is in progress and false otherwise. */
|
||||
public boolean isInProgress() {
|
||||
return getPointersDown() == 2;
|
||||
}
|
||||
|
||||
/* Sends the requested scale gesture notification to the listener. */
|
||||
private void sendScaleGesture(EventType eventType) {
|
||||
switch (eventType) {
|
||||
case BEGIN: mListener.onScaleBegin(this); break;
|
||||
case CONTINUE: mListener.onScale(this); break;
|
||||
case END: mListener.onScaleEnd(this); break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the pointer info corresponding to the given pointer index, or null if the pointer
|
||||
* isn't one that's being tracked.
|
||||
*/
|
||||
private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) {
|
||||
int id = event.getPointerId(index);
|
||||
for (PointerInfo pointerInfo : mPointerInfo) {
|
||||
if (pointerInfo.getId() == id) {
|
||||
return pointerInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum EventType {
|
||||
BEGIN,
|
||||
CONTINUE,
|
||||
END,
|
||||
}
|
||||
|
||||
/* Encapsulates information about one of the two fingers involved in the gesture. */
|
||||
private static class PointerInfo {
|
||||
/* A free list that recycles pointer info objects, to reduce GC pauses. */
|
||||
private static Stack<PointerInfo> sPointerInfoFreeList;
|
||||
|
||||
private int mId;
|
||||
private PointF mCurrent, mPrevious;
|
||||
|
||||
private PointerInfo() {
|
||||
// External users should use create() instead.
|
||||
}
|
||||
|
||||
/* Creates or recycles a new PointerInfo instance from an event and a pointer index. */
|
||||
public static PointerInfo create(MotionEvent event, int index) {
|
||||
if (sPointerInfoFreeList == null) {
|
||||
sPointerInfoFreeList = new Stack<PointerInfo>();
|
||||
}
|
||||
|
||||
PointerInfo pointerInfo;
|
||||
if (sPointerInfoFreeList.empty()) {
|
||||
pointerInfo = new PointerInfo();
|
||||
} else {
|
||||
pointerInfo = sPointerInfoFreeList.pop();
|
||||
}
|
||||
|
||||
pointerInfo.populate(event, index);
|
||||
return pointerInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills in the fields of this instance from the given motion event and pointer index
|
||||
* within that event.
|
||||
*/
|
||||
public void populate(MotionEvent event, int index) {
|
||||
mId = event.getPointerId(index);
|
||||
mPrevious = mCurrent;
|
||||
mCurrent = new PointF(event.getX(index), event.getY(index));
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
mId = -1;
|
||||
mPrevious = mCurrent = null;
|
||||
sPointerInfoFreeList.push(this);
|
||||
}
|
||||
|
||||
public int getId() { return mId; }
|
||||
public PointF getCurrent() { return mCurrent; }
|
||||
public PointF getPrevious() { return mPrevious; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (mId == -1) {
|
||||
return "(up)";
|
||||
}
|
||||
|
||||
try {
|
||||
String prevString;
|
||||
if (mPrevious == null) {
|
||||
prevString = "n/a";
|
||||
} else {
|
||||
prevString = PointUtils.toJSON(mPrevious).toString();
|
||||
}
|
||||
|
||||
// The current position should always be non-null.
|
||||
String currentString = PointUtils.toJSON(mCurrent).toString();
|
||||
return "id=" + mId + " cur=" + currentString + " prev=" + prevString;
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static interface SimpleScaleGestureListener {
|
||||
public boolean onScale(SimpleScaleGestureDetector detector);
|
||||
public boolean onScaleBegin(SimpleScaleGestureDetector detector);
|
||||
public void onScaleEnd(SimpleScaleGestureDetector detector);
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,8 @@ function resolveGeckoURI(aURI) {
|
||||
var Strings = {};
|
||||
[
|
||||
["brand", "chrome://branding/locale/brand.properties"],
|
||||
["browser", "chrome://browser/locale/browser.properties"]
|
||||
["browser", "chrome://browser/locale/browser.properties"],
|
||||
["charset", "chrome://global/locale/charsetTitles.properties"]
|
||||
].forEach(function (aStringBundle) {
|
||||
let [name, bundle] = aStringBundle;
|
||||
XPCOMUtils.defineLazyGetter(Strings, name, function() {
|
||||
@ -252,6 +253,7 @@ var BrowserApp = {
|
||||
ConsoleAPI.init();
|
||||
ClipboardHelper.init();
|
||||
PermissionsHelper.init();
|
||||
CharacterEncoding.init();
|
||||
|
||||
// Init LoginManager
|
||||
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
@ -357,6 +359,7 @@ var BrowserApp = {
|
||||
ViewportHandler.uninit();
|
||||
XPInstallObserver.uninit();
|
||||
ConsoleAPI.uninit();
|
||||
CharacterEncoding.uninit();
|
||||
},
|
||||
|
||||
get tabs() {
|
||||
@ -621,7 +624,7 @@ var BrowserApp = {
|
||||
case Ci.nsIPrefBranch.PREF_STRING:
|
||||
default:
|
||||
pref.type = "string";
|
||||
pref.value = Services.prefs.getComplexValue(prefName, Ci.nsISupportsString).data;
|
||||
pref.value = Services.prefs.getComplexValue(prefName, Ci.nsIPrefLocalizedString).data;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
@ -638,10 +641,6 @@ var BrowserApp = {
|
||||
pref.type = "bool";
|
||||
pref.value = pref.value == 0;
|
||||
break;
|
||||
case "browser.menu.showCharacterEncoding":
|
||||
pref.type = "bool";
|
||||
pref.value = pref.value == "true";
|
||||
break;
|
||||
case "font.size.inflation.minTwips":
|
||||
pref.type = "string";
|
||||
pref.value = pref.value.toString();
|
||||
@ -683,10 +682,6 @@ var BrowserApp = {
|
||||
json.type = "int";
|
||||
json.value = (json.value ? 0 : 2);
|
||||
break;
|
||||
case "browser.menu.showCharacterEncoding":
|
||||
json.type = "string";
|
||||
json.value = (json.value ? "true" : "false");
|
||||
break;
|
||||
case "font.size.inflation.minTwips":
|
||||
json.type = "int";
|
||||
json.value = parseInt(json.value);
|
||||
@ -3819,7 +3814,7 @@ var PermissionsHelper = {
|
||||
Services.contentPrefs.removePref(aURI, aType + ".request.remember");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var MasterPassword = {
|
||||
pref: "privacy.masterpassword.enabled",
|
||||
@ -3899,4 +3894,99 @@ var MasterPassword = {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var CharacterEncoding = {
|
||||
_charsets: [],
|
||||
|
||||
init: function init() {
|
||||
Services.obs.addObserver(this, "CharEncoding:Get", false);
|
||||
Services.obs.addObserver(this, "CharEncoding:Set", false);
|
||||
this.sendState();
|
||||
},
|
||||
|
||||
uninit: function uninit() {
|
||||
Services.obs.removeObserver(this, "CharEncoding:Get", false);
|
||||
Services.obs.removeObserver(this, "CharEncoding:Set", false);
|
||||
},
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "CharEncoding:Get":
|
||||
this.getEncoding();
|
||||
break;
|
||||
case "CharEncoding:Set":
|
||||
this.setEncoding(aData);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
sendState: function sendState() {
|
||||
let showCharEncoding = "false";
|
||||
try {
|
||||
showCharEncoding = Services.prefs.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data;
|
||||
} catch (e) { /* Optional */ }
|
||||
|
||||
sendMessageToJava({
|
||||
gecko: {
|
||||
type: "CharEncoding:State",
|
||||
visible: showCharEncoding
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getEncoding: function getEncoding() {
|
||||
function normalizeCharsetCode(charsetCode) {
|
||||
return charsetCode.trim().toLowerCase();
|
||||
}
|
||||
|
||||
function getTitle(charsetCode) {
|
||||
let charsetTitle = charsetCode;
|
||||
try {
|
||||
charsetTitle = Strings.charset.GetStringFromName(charsetCode + ".title");
|
||||
} catch (e) {
|
||||
dump("error: title not found for " + charsetCode);
|
||||
}
|
||||
return charsetTitle;
|
||||
}
|
||||
|
||||
if (!this._charsets.length) {
|
||||
let charsets = Services.prefs.getComplexValue("intl.charsetmenu.browser.static", Ci.nsIPrefLocalizedString).data;
|
||||
this._charsets = charsets.split(",").map(function (charset) {
|
||||
return {
|
||||
code: normalizeCharsetCode(charset),
|
||||
title: getTitle(charset)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// if document charset is not in charset options, add it
|
||||
let docCharset = normalizeCharsetCode(BrowserApp.selectedBrowser.contentDocument.characterSet);
|
||||
let selected = 0;
|
||||
let charsetCount = this._charsets.length;
|
||||
for (; selected < charsetCount && this._charsets[selected].code != docCharset; selected++);
|
||||
if (selected == charsetCount) {
|
||||
this._charsets.push({
|
||||
code: docCharset,
|
||||
title: getTitle(docCharset)
|
||||
});
|
||||
}
|
||||
|
||||
sendMessageToJava({
|
||||
gecko: {
|
||||
type: "CharEncoding:Data",
|
||||
charsets: this._charsets,
|
||||
selected: selected
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setEncoding: function setEncoding(aEncoding) {
|
||||
let browser = BrowserApp.selectedBrowser;
|
||||
let docCharset = browser.docShell.QueryInterface(Ci.nsIDocCharset);
|
||||
docCharset.charset = aEncoding;
|
||||
browser.reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
@ -172,7 +172,7 @@ browser.menu.showCharacterEncoding=false
|
||||
|
||||
# LOCALIZATION NOTE (intl.charsetmenu.browser.static): Set to a series of comma separated
|
||||
# values for charsets that the user can select from in the Character Encoding menu.
|
||||
intl.charsetmenu.browser.static=iso-8859-1,utf-8,x-gbk,big5,iso-2022-jp,shift_jis,euc-jp
|
||||
intl.charsetmenu.browser.static=iso-8859-1,utf-8,big5,iso-2022-jp,shift_jis,euc-jp
|
||||
|
||||
# Application Menu
|
||||
appMenu.more=More
|
||||
|
@ -891,9 +891,11 @@ SpdySession::HandleSynReply(SpdySession *self)
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE,
|
||||
self->mFrameDataSize - 6);
|
||||
PRUint32 ratio =
|
||||
(self->mFrameDataSize - 6) * 100 / self->mDecompressBufferUsed;
|
||||
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
|
||||
if (self->mDecompressBufferUsed) {
|
||||
PRUint32 ratio =
|
||||
(self->mFrameDataSize - 6) * 100 / self->mDecompressBufferUsed;
|
||||
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
|
||||
}
|
||||
|
||||
// status and version are required.
|
||||
nsDependentCSubstring status, version;
|
||||
|
@ -439,11 +439,19 @@ SocksTestServer.prototype = {
|
||||
var client = new SocksClient(this, input, output);
|
||||
this.client_connections.push(client);
|
||||
},
|
||||
|
||||
onStopListening: function(socket)
|
||||
{
|
||||
},
|
||||
|
||||
close: function()
|
||||
{
|
||||
if (this.client_subprocess) {
|
||||
this.client_subprocess.kill();
|
||||
try {
|
||||
this.client_subprocess.kill();
|
||||
} catch (x) {
|
||||
do_note_exception(x, 'Killing subprocess failed');
|
||||
}
|
||||
this.client_subprocess = null;
|
||||
}
|
||||
for each (var client in this.client_connections)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user