Merge last green PGO from inbound to central

This commit is contained in:
Marco Bonardo 2012-01-24 13:27:20 +01:00
commit 9cd7f532f6
108 changed files with 3303 additions and 1567 deletions

View File

@ -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,

View File

@ -56,6 +56,7 @@ CMMSRCS = nsAccessNodeWrap.mm \
mozDocAccessible.mm \
mozActionElements.mm \
mozTextAccessible.mm \
mozHTMLAccessible.mm \
$(NULL)

View 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

View 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

View File

@ -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:

View File

@ -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

View File

@ -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"]

View File

@ -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>

View File

@ -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.

View File

@ -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

View File

@ -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 $@'

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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(),

View File

@ -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));

View File

@ -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'

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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
View 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___ */

View File

@ -0,0 +1,4 @@
f = (function() {
for (x in [arguments, arguments]) yield(gczeal(4, function(){}))
})
for (i in f()) {}

View 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");

View 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);

View 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');

View 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');

View 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)

View File

@ -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];

View File

@ -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"

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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:

View File

@ -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
}

View File

@ -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)
{

View File

@ -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 */
/*

View File

@ -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());

View File

@ -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, &regs.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;

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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___ */

View File

@ -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.

View File

@ -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();

View File

@ -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: {

View File

@ -1897,6 +1897,7 @@ LoopState::analyzeLoopBody(unsigned frame)
break;
case JSOP_LOOPHEAD:
case JSOP_LOOPENTRY:
case JSOP_POP:
case JSOP_ZERO:
case JSOP_ONE:

View File

@ -1296,6 +1296,9 @@ JITScript::destroy(JSContext *cx)
{
for (unsigned i = 0; i < nchunks; i++)
destroyChunk(cx, i);
if (shimPool)
shimPool->release();
}
void

View File

@ -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.

View File

@ -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 */

View File

@ -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);

View File

@ -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

View File

@ -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

View 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");

View File

@ -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

View File

@ -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);

View File

@ -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 &regs() 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 &regs, const CallArgs &args,
JSFunction &callee, JSScript *script,
InitialFrameFlags initial);
bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSFunction &callee, JSScript *script,
InitialFrameFlags initial, Value **stackLimit);
void popInlineFrame(FrameRegs &regs);
/* 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__ */

View File

@ -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 &regs() 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 &regs, const CallArgs &args,
JSFunction &callee, JSScript *script,
InitialFrameFlags initial);
bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSFunction &callee, JSScript *script,
InitialFrameFlags initial, Value **stackLimit);
void popInlineFrame(FrameRegs &regs);
/* 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__ */

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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 \

View File

@ -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 \

View 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

View 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

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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));
}

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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)

View 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);
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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">

View File

@ -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" />

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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)

View 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);
}
}

View File

@ -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);
},
};

View File

@ -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

View File

@ -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;

View File

@ -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