зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
8f9ca11b22
|
@ -39,7 +39,7 @@ oopCommandlineHandler.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
helpInfo: " -oop Use out-of-process model in B2G\n",
|
||||
helpInfo: " --oop Use out-of-process model in B2G\n",
|
||||
classID: Components.ID("{e30b0e13-2d12-4cb0-bc4c-4e617a1bf76e}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
|
||||
};
|
||||
|
|
|
@ -35,9 +35,11 @@ int main(int argc, char* argv[], char* envp[]){
|
|||
sprintf(full_path, "%s/%s", cwd, B2G_NAME);
|
||||
sprintf(full_profile_path, "%s/%s", cwd, GAIA_PATH);
|
||||
free(cwd);
|
||||
printf("Running: %s -profile %s\n", full_path, full_profile_path);
|
||||
printf("Running: %s --profile %s\n", full_path, full_profile_path);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
// XXX: yes, the printf above says --profile and this execle uses -profile.
|
||||
// Bug 1088430 will change the execle to use --profile.
|
||||
execle(full_path, full_path, "-profile", full_profile_path, NULL, envp);
|
||||
error("unable to start");
|
||||
perror(argv[0]);
|
||||
|
|
|
@ -4259,7 +4259,7 @@ nsBrowserAccess.prototype = {
|
|||
var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||
|
||||
if (isExternal && aURI && aURI.schemeIs("chrome")) {
|
||||
dump("use -chrome command-line option to load external chrome urls\n");
|
||||
dump("use --chrome command-line option to load external chrome urls\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ function test() {
|
|||
Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
|
||||
Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||
is(gBrowser.tabs.length, tabCount + 1,
|
||||
"'-new-tab about:blank' opens a new tab");
|
||||
"'--new-tab about:blank' opens a new tab");
|
||||
is(gBrowser.selectedTab, gBrowser.tabs[tabCount],
|
||||
"'-new-tab about:blank' selects the new tab");
|
||||
"'--new-tab about:blank' selects the new tab");
|
||||
is(document.activeElement, gURLBar.inputField,
|
||||
"'-new-tab about:blank' focuses the location bar");
|
||||
"'--new-tab about:blank' focuses the location bar");
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ function test() {
|
|||
}
|
||||
|
||||
aWindow.gBrowser.addEventListener("pageshow", function pageShown(event) {
|
||||
// If data: -url PAC file isn't loaded soon enough, we may get about:privatebrowsing loaded
|
||||
// If data: --url PAC file isn't loaded soon enough, we may get about:privatebrowsing loaded
|
||||
if (event.target.location == "about:blank" ||
|
||||
event.target.location == "about:privatebrowsing") {
|
||||
aWindow.gBrowser.selectedBrowser.loadURI(testURI);
|
||||
|
|
|
@ -48,7 +48,7 @@ function shouldLoadURI(aURI) {
|
|||
return true;
|
||||
|
||||
dump("*** Preventing external load of chrome: URI into browser window\n");
|
||||
dump(" Use -chrome <uri> instead\n");
|
||||
dump(" Use --chrome <uri> instead\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -456,16 +456,16 @@ nsBrowserContentHandler.prototype = {
|
|||
#endif
|
||||
},
|
||||
|
||||
helpInfo : " -browser Open a browser window.\n" +
|
||||
" -new-window <url> Open <url> in a new window.\n" +
|
||||
" -new-tab <url> Open <url> in a new tab.\n" +
|
||||
" -private-window <url> Open <url> in a new private window.\n" +
|
||||
helpInfo : " --browser Open a browser window.\n" +
|
||||
" --new-window <url> Open <url> in a new window.\n" +
|
||||
" --new-tab <url> Open <url> in a new tab.\n" +
|
||||
" --private-window <url> Open <url> in a new private window.\n" +
|
||||
#ifdef XP_WIN
|
||||
" -preferences Open Options dialog.\n" +
|
||||
" --preferences Open Options dialog.\n" +
|
||||
#else
|
||||
" -preferences Open Preferences dialog.\n" +
|
||||
" --preferences Open Preferences dialog.\n" +
|
||||
#endif
|
||||
" -search <term> Search <term> with your default search engine.\n",
|
||||
" --search <term> Search <term> with your default search engine.\n",
|
||||
|
||||
/* nsIBrowserHandler */
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* -setDefaultBrowser commandline handler
|
||||
* --setDefaultBrowser commandline handler
|
||||
* Makes the current executable the "default browser".
|
||||
*/
|
||||
|
||||
|
@ -22,7 +22,7 @@ nsSetDefaultBrowser.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
helpInfo: " -setDefaultBrowser Set this app as the default browser.\n",
|
||||
helpInfo: " --setDefaultBrowser Set this app as the default browser.\n",
|
||||
|
||||
classID: Components.ID("{F57899D0-4E2C-4ac6-9E29-50C736103B0C}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
|
||||
|
|
|
@ -67,8 +67,8 @@ devtoolsCommandlineHandler.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
helpInfo : " -jsconsole Open the Browser Console.\n" +
|
||||
" -jsdebugger Open the Browser Toolbox.\n",
|
||||
helpInfo : " --jsconsole Open the Browser Console.\n" +
|
||||
" --jsdebugger Open the Browser Toolbox.\n",
|
||||
|
||||
classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
|
||||
|
|
|
@ -10,7 +10,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Handles -webide command line option.
|
||||
* Handles --webide command line option.
|
||||
*/
|
||||
|
||||
function webideCli() { }
|
||||
|
@ -23,10 +23,10 @@ webideCli.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
// If -webide is used remotely, we don't want to open
|
||||
// If --webide is used remotely, we don't want to open
|
||||
// a new tab.
|
||||
//
|
||||
// If -webide is used for a new Firefox instance, we
|
||||
// If --webide is used for a new Firefox instance, we
|
||||
// want to open webide only.
|
||||
cmdLine.preventDefault = true;
|
||||
|
||||
|
|
|
@ -137,10 +137,10 @@ BrowserCLH.prototype = {
|
|||
#endif
|
||||
// Instantiate the search service so the search engine cache is created now
|
||||
// instead when the application is running. The install process will register
|
||||
// this component by using the -silent command line flag, thereby creating
|
||||
// this component by using the --silent command line flag, thereby creating
|
||||
// the cache during install, not runtime.
|
||||
// NOTE: This code assumes this CLH is run before the nsDefaultCLH, which
|
||||
// consumes the "-silent" flag.
|
||||
// consumes the "--silent" flag.
|
||||
if (aCmdLine.findFlag("silent", false) > -1) {
|
||||
let searchService = Services.search;
|
||||
let autoComplete = Cc["@mozilla.org/autocomplete/search;1?name=history"].
|
||||
|
|
|
@ -616,7 +616,7 @@ void LaunchDesktopBrowserWithParams(CStringW& aBrowserPath, CStringW& aVerb,
|
|||
params += "\"";
|
||||
}
|
||||
|
||||
// Tack on any extra parameters we received (for example -profilemanager)
|
||||
// Tack on any extra parameters we received (for example --profilemanager)
|
||||
if (!aParameters.IsEmpty()) {
|
||||
params += " ";
|
||||
params += aParameters;
|
||||
|
|
|
@ -4005,7 +4005,7 @@ else
|
|||
# Support comm-central.
|
||||
if test -n "$EXTERNAL_SOURCE_DIR" ; then
|
||||
MOZ_BUILD_APP="$EXTERNAL_SOURCE_DIR/$MOZ_BUILD_APP"
|
||||
MOZ_BUILD_APP=`${PYTHON} -c "import os.path; print(os.path.relpath(\"${MOZ_BUILD_APP}\", \"${srcdir}\"))"`
|
||||
MOZ_BUILD_APP=`${PYTHON} -c "import mozpack.path as mozpath; print(mozpath.relpath(\"${MOZ_BUILD_APP}\", \"${srcdir}\"))"`
|
||||
fi
|
||||
# We have a valid application only if it has a build.mk file in its top
|
||||
# directory.
|
||||
|
|
|
@ -9642,7 +9642,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
|||
// Disallow external chrome: loads targetted at content windows
|
||||
bool isChrome = false;
|
||||
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
|
||||
NS_WARNING("blocked external chrome: url -- use '-chrome' option");
|
||||
NS_WARNING("blocked external chrome: url -- use '--chrome' option");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -4141,11 +4141,6 @@ nsContentUtils::CreateContextualFragment(nsINode* aContextNode,
|
|||
|
||||
while (content && content->IsElement()) {
|
||||
nsString& tagName = *tagStack.AppendElement();
|
||||
if (!&tagName) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
tagName = content->NodeInfo()->QualifiedName();
|
||||
|
||||
// see if we need to add xmlns declarations
|
||||
|
|
|
@ -9355,21 +9355,30 @@ nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, in
|
|||
SelectionChangeEventInit init;
|
||||
init.mBubbles = true;
|
||||
if (aSel) {
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
|
||||
if (NS_SUCCEEDED(rv) && range) {
|
||||
nsRefPtr<nsRange> nsrange = static_cast<nsRange*>(range.get());
|
||||
init.mBoundingClientRect = nsrange->GetBoundingClientRect(true, false);
|
||||
range->ToString(init.mSelectedText);
|
||||
Selection* selection = static_cast<Selection*>(aSel);
|
||||
int32_t rangeCount = selection->GetRangeCount();
|
||||
nsLayoutUtils::RectAccumulator accumulator;
|
||||
for (int32_t idx = 0; idx < rangeCount; ++idx) {
|
||||
nsRange* range = selection->GetRangeAt(idx);
|
||||
nsRange::CollectClientRects(&accumulator, range,
|
||||
range->GetStartParent(), range->StartOffset(),
|
||||
range->GetEndParent(), range->EndOffset(),
|
||||
true, false);
|
||||
}
|
||||
nsRect rect = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
|
||||
accumulator.mResultRect;
|
||||
nsRefPtr<DOMRect> domRect = new DOMRect(ToSupports(this));
|
||||
domRect->SetLayoutRect(rect);
|
||||
init.mBoundingClientRect = domRect;
|
||||
|
||||
for (uint32_t reasonType = 0;
|
||||
reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
|
||||
++reasonType) {
|
||||
SelectionChangeReason strongReasonType =
|
||||
static_cast<SelectionChangeReason>(reasonType);
|
||||
if (CheckReason(aReason, strongReasonType)) {
|
||||
init.mReasons.AppendElement(strongReasonType);
|
||||
}
|
||||
selection->Stringify(init.mSelectedText);
|
||||
for (uint32_t reasonType = 0;
|
||||
reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
|
||||
++reasonType) {
|
||||
SelectionChangeReason strongReasonType =
|
||||
static_cast<SelectionChangeReason>(reasonType);
|
||||
if (CheckReason(aReason, strongReasonType)) {
|
||||
init.mReasons.AppendElement(strongReasonType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=811701
|
|||
is(math.outerHTML, "<math><mtext>test</mtext></math>",
|
||||
"<math> should have innerHTML");
|
||||
math.innerHTML = "<mo>+</mo>";
|
||||
is(math.firstChild.namespaceURI, "http://www.w3.org/1999/xhtml",
|
||||
is(math.firstChild.namespaceURI, "http://www.w3.org/1998/Math/MathML",
|
||||
"Should have the right namespace after setting innerHTML on <math>");
|
||||
|
||||
var polygon = document.querySelector("polygon");
|
||||
|
@ -41,8 +41,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=811701
|
|||
|
||||
var svg = document.querySelector("svg");
|
||||
svg.innerHTML = "<rect/>";
|
||||
is(svg.firstChild.namespaceURI, "http://www.w3.org/1999/xhtml",
|
||||
"Should have the right namespace after setting innerHTML on <math>");
|
||||
is(svg.firstChild.namespaceURI, "http://www.w3.org/2000/svg",
|
||||
"Should have the right namespace after setting innerHTML on <svg>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -173,10 +173,6 @@ protected:
|
|||
// yet. We want to make sure to only do this once.
|
||||
bool mNotifiedRootInsertion;
|
||||
|
||||
uint8_t mScriptEnabled : 1;
|
||||
uint8_t mFramesEnabled : 1;
|
||||
uint8_t unused : 6; // bits available if someone needs one
|
||||
|
||||
mozilla::dom::NodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1];
|
||||
|
||||
nsresult FlushTags();
|
||||
|
@ -724,25 +720,6 @@ NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink)
|
|||
NS_IMPL_ADDREF_INHERITED(HTMLContentSink, nsContentSink)
|
||||
NS_IMPL_RELEASE_INHERITED(HTMLContentSink, nsContentSink)
|
||||
|
||||
static bool
|
||||
IsScriptEnabled(nsIDocument *aDoc, nsIDocShell *aContainer)
|
||||
{
|
||||
NS_ENSURE_TRUE(aDoc && aContainer, true);
|
||||
|
||||
nsCOMPtr<nsIScriptGlobalObject> globalObject =
|
||||
do_QueryInterface(aDoc->GetInnerWindow());
|
||||
|
||||
// Getting context is tricky if the document hasn't had its
|
||||
// GlobalObject set yet
|
||||
if (!globalObject) {
|
||||
globalObject = aContainer->GetScriptGlobalObject();
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), true);
|
||||
return nsContentUtils::GetSecurityManager()->
|
||||
ScriptAllowed(globalObject->GetGlobalJSObject());
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLContentSink::Init(nsIDocument* aDoc,
|
||||
nsIURI* aURI,
|
||||
|
@ -762,21 +739,6 @@ HTMLContentSink::Init(nsIDocument* aDoc,
|
|||
|
||||
NS_ASSERTION(mDocShell, "oops no docshell!");
|
||||
|
||||
// Find out if subframes are enabled
|
||||
if (mDocShell) {
|
||||
bool subFramesEnabled = true;
|
||||
mDocShell->GetAllowSubframes(&subFramesEnabled);
|
||||
if (subFramesEnabled) {
|
||||
mFramesEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find out if scripts are enabled, if not, show <noscript> content
|
||||
if (IsScriptEnabled(aDoc, mDocShell)) {
|
||||
mScriptEnabled = true;
|
||||
}
|
||||
|
||||
|
||||
// Changed from 8192 to greatly improve page loading performance on
|
||||
// large pages. See bugzilla bug 77540.
|
||||
mMaxTextRun = Preferences::GetInt("content.maxtextrun", 8191);
|
||||
|
|
|
@ -213,7 +213,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
mDropVideoUntilNextDiscontinuity(false),
|
||||
mDecodeToSeekTarget(false),
|
||||
mCurrentTimeBeforeSeek(0),
|
||||
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
|
||||
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
|
||||
mDecodingFrozenAtStateMetadata(false),
|
||||
mDecodingFrozenAtStateDecoding(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
@ -1356,8 +1358,9 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant)
|
|||
SetState(DECODER_STATE_DORMANT);
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
} else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
|
||||
mDecodingFrozenAtStateMetadata = true;
|
||||
mDecodingFrozenAtStateDecoding = true;
|
||||
ScheduleStateMachine();
|
||||
mStartTime = 0;
|
||||
mCurrentFrameTime = 0;
|
||||
SetState(DECODER_STATE_DECODING_NONE);
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
|
@ -1454,6 +1457,10 @@ void MediaDecoderStateMachine::Play()
|
|||
SetState(DECODER_STATE_DECODING);
|
||||
mDecodeStartTime = TimeStamp::Now();
|
||||
}
|
||||
if (mDecodingFrozenAtStateDecoding) {
|
||||
mDecodingFrozenAtStateDecoding = false;
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
}
|
||||
// Once we start playing, we don't want to minimize our prerolling, as we
|
||||
// assume the user is likely to want to keep playing in future.
|
||||
mMinimizePreroll = false;
|
||||
|
@ -1515,6 +1522,8 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
|
|||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
mDecodingFrozenAtStateDecoding = false;
|
||||
|
||||
if (mState == DECODER_STATE_SHUTDOWN) {
|
||||
return;
|
||||
}
|
||||
|
@ -1622,6 +1631,11 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
|
|||
return;
|
||||
}
|
||||
|
||||
if (mState == DECODER_STATE_DECODING && mDecodingFrozenAtStateDecoding) {
|
||||
DECODER_LOG("DispatchDecodeTasksIfNeeded return due to "
|
||||
"mFreezeDecodingAtStateDecoding");
|
||||
return;
|
||||
}
|
||||
// NeedToDecodeAudio() can go from false to true while we hold the
|
||||
// monitor, but it can't go from true to false. This can happen because
|
||||
// NeedToDecodeAudio() takes into account the amount of decoded audio
|
||||
|
@ -1972,6 +1986,10 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||
SetStartTime(0);
|
||||
res = FinishDecodeMetadata();
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
} else if (mDecodingFrozenAtStateMetadata) {
|
||||
SetStartTime(mStartTime);
|
||||
res = FinishDecodeMetadata();
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
} else {
|
||||
if (HasAudio()) {
|
||||
ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor());
|
||||
|
@ -1997,7 +2015,7 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mScheduler->IsRealTime()) {
|
||||
if (!mScheduler->IsRealTime() && !mDecodingFrozenAtStateMetadata) {
|
||||
|
||||
const VideoData* v = VideoQueue().PeekFront();
|
||||
const AudioData* a = AudioQueue().PeekFront();
|
||||
|
@ -2029,6 +2047,8 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
|
|||
mStartTime, mEndTime, GetDuration(),
|
||||
mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable());
|
||||
|
||||
mDecodingFrozenAtStateMetadata = false;
|
||||
|
||||
if (HasAudio() && !HasVideo()) {
|
||||
// We're playing audio only. We don't need to worry about slow video
|
||||
// decodes causing audio underruns, so don't buffer so much audio in
|
||||
|
|
|
@ -921,6 +921,17 @@ protected:
|
|||
mozilla::MediaMetadataManager mMetadataManager;
|
||||
|
||||
MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
|
||||
|
||||
// True if we are back from DECODER_STATE_DORMANT state, and we can skip
|
||||
// SetStartTime because the mStartTime already set before. Also we don't need
|
||||
// to decode any audio/video since the MediaDecoder will trigger a seek
|
||||
// operation soon.
|
||||
// mDecodingFrozenAtStateMetadata: turn on/off at
|
||||
// SetDormant/FinishDecodeMetadata.
|
||||
// mDecodingFrozenAtStateDecoding: turn on/off at
|
||||
// SetDormant/Seek,Play.
|
||||
bool mDecodingFrozenAtStateMetadata;
|
||||
bool mDecodingFrozenAtStateDecoding;
|
||||
};
|
||||
|
||||
} // namespace mozilla;
|
||||
|
|
|
@ -101,16 +101,4 @@ RtspMediaCodecReader::ReadMetadata(MediaInfo* aInfo,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// Called on Binder thread.
|
||||
void
|
||||
RtspMediaCodecReader::codecReserved(Track& aTrack)
|
||||
{
|
||||
// TODO: fix me, we need a SeekTime(0) here because the
|
||||
// MediaDecoderStateMachine will update the mStartTime after ReadMetadata.
|
||||
MediaCodecReader::codecReserved(aTrack);
|
||||
if (aTrack.mCodec != nullptr) {
|
||||
mRtspResource->SeekTime(0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -65,8 +65,6 @@ public:
|
|||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) MOZ_OVERRIDE;
|
||||
|
||||
virtual void codecReserved(Track& aTrack) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// A pointer to RtspMediaResource for calling the Rtsp specific function.
|
||||
// The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "nsGkAtoms.h"
|
||||
#include "mozilla/dom/SVGRectElementBinding.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -108,6 +110,36 @@ SVGRectElement::GetLengthInfo()
|
|||
//----------------------------------------------------------------------
|
||||
// nsSVGPathGeometryElement methods
|
||||
|
||||
bool
|
||||
SVGRectElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
|
||||
const Matrix& aTransform)
|
||||
{
|
||||
Rect r;
|
||||
Float rx, ry;
|
||||
GetAnimatedLengthValues(&r.x, &r.y, &r.width, &r.height, &rx, &ry, nullptr);
|
||||
|
||||
if (r.IsEmpty()) {
|
||||
// Rendering of the element disabled
|
||||
r.SetEmpty(); // make sure width/height are actually zero
|
||||
*aBounds = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
rx = std::max(rx, 0.0f);
|
||||
ry = std::max(ry, 0.0f);
|
||||
|
||||
if (rx != 0 || ry != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aStrokeWidth > 0.f) {
|
||||
r.Inflate(aStrokeWidth / 2.f);
|
||||
}
|
||||
|
||||
*aBounds = aTransform.TransformBounds(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,8 @@ public:
|
|||
virtual bool HasValidDimensions() const MOZ_OVERRIDE;
|
||||
|
||||
// nsSVGPathGeometryElement methods:
|
||||
virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
|
||||
const Matrix& aTransform) MOZ_OVERRIDE;
|
||||
virtual void GetAsSimplePath(SimplePath* aSimplePath) MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder = nullptr) MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ protected:
|
|||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
typedef mozilla::gfx::FillRule FillRule;
|
||||
typedef mozilla::gfx::Float Float;
|
||||
typedef mozilla::gfx::Matrix Matrix;
|
||||
typedef mozilla::gfx::Path Path;
|
||||
typedef mozilla::gfx::Point Point;
|
||||
typedef mozilla::gfx::PathBuilder PathBuilder;
|
||||
|
@ -69,6 +70,11 @@ public:
|
|||
virtual bool IsMarkable();
|
||||
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks);
|
||||
|
||||
virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
|
||||
const Matrix& aTransform) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use with GetAsSimplePath.
|
||||
*/
|
||||
|
|
|
@ -174,6 +174,9 @@ uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
|
|||
// Does not hold an owning reference.
|
||||
RuntimeService* gRuntimeService = nullptr;
|
||||
|
||||
// Only true during the call to Init.
|
||||
bool gRuntimeServiceDuringInit = false;
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
bool gTestPBackground = false;
|
||||
#endif // ENABLE_TESTS
|
||||
|
@ -464,13 +467,14 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
// If we're running in Init() then do this for every pref we care about.
|
||||
// Otherwise we just want to update the parameter that changed.
|
||||
for (uint32_t index = rts ? JSSettings::kGCSettingsArraySize - 1 : 0;
|
||||
for (uint32_t index = !gRuntimeServiceDuringInit
|
||||
? JSSettings::kGCSettingsArraySize - 1 : 0;
|
||||
index < JSSettings::kGCSettingsArraySize;
|
||||
index++) {
|
||||
LiteralRebindingCString matchName;
|
||||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max");
|
||||
if (memPrefName == matchName || (!rts && index == 0)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 0)) {
|
||||
int32_t prefValue = GetWorkerPref(matchName, -1);
|
||||
uint32_t value = (prefValue <= 0 || prefValue >= 0x1000) ?
|
||||
uint32_t(-1) :
|
||||
|
@ -480,7 +484,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
}
|
||||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark");
|
||||
if (memPrefName == matchName || (!rts && index == 1)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 1)) {
|
||||
int32_t prefValue = GetWorkerPref(matchName, 128);
|
||||
UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES,
|
||||
uint32_t(prefValue) * 1024 * 1024);
|
||||
|
@ -489,7 +493,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
|
||||
"gc_high_frequency_time_limit_ms");
|
||||
if (memPrefName == matchName || (!rts && index == 2)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 2)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName,
|
||||
JSGC_HIGH_FREQUENCY_TIME_LIMIT);
|
||||
continue;
|
||||
|
@ -497,7 +501,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
|
||||
"gc_low_frequency_heap_growth");
|
||||
if (memPrefName == matchName || (!rts && index == 3)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 3)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName,
|
||||
JSGC_LOW_FREQUENCY_HEAP_GROWTH);
|
||||
continue;
|
||||
|
@ -505,7 +509,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
|
||||
"gc_high_frequency_heap_growth_min");
|
||||
if (memPrefName == matchName || (!rts && index == 4)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 4)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName,
|
||||
JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
|
||||
continue;
|
||||
|
@ -513,7 +517,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
|
||||
"gc_high_frequency_heap_growth_max");
|
||||
if (memPrefName == matchName || (!rts && index == 5)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 5)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName,
|
||||
JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
|
||||
continue;
|
||||
|
@ -521,7 +525,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
|
||||
"gc_high_frequency_low_limit_mb");
|
||||
if (memPrefName == matchName || (!rts && index == 6)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 6)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName,
|
||||
JSGC_HIGH_FREQUENCY_LOW_LIMIT);
|
||||
continue;
|
||||
|
@ -529,7 +533,7 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
|
||||
"gc_high_frequency_high_limit_mb");
|
||||
if (memPrefName == matchName || (!rts && index == 7)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 7)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName,
|
||||
JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
|
||||
continue;
|
||||
|
@ -537,13 +541,13 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
|
||||
"gc_allocation_threshold_mb");
|
||||
if (memPrefName == matchName || (!rts && index == 8)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 8)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD);
|
||||
continue;
|
||||
}
|
||||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms");
|
||||
if (memPrefName == matchName || (!rts && index == 9)) {
|
||||
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 9)) {
|
||||
int32_t prefValue = GetWorkerPref(matchName, -1);
|
||||
uint32_t value =
|
||||
(prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue);
|
||||
|
@ -552,7 +556,8 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
}
|
||||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth");
|
||||
if (memPrefName == matchName || (!rts && index == 10)) {
|
||||
if (memPrefName == matchName ||
|
||||
(gRuntimeServiceDuringInit && index == 10)) {
|
||||
bool prefValue = GetWorkerPref(matchName, false);
|
||||
UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH,
|
||||
prefValue ? 0 : 1);
|
||||
|
@ -560,7 +565,8 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
}
|
||||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice");
|
||||
if (memPrefName == matchName || (!rts && index == 11)) {
|
||||
if (memPrefName == matchName ||
|
||||
(gRuntimeServiceDuringInit && index == 11)) {
|
||||
bool prefValue = GetWorkerPref(matchName, false);
|
||||
UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
|
||||
prefValue ? 0 : 1);
|
||||
|
@ -568,13 +574,15 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
|
|||
}
|
||||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_min_empty_chunk_count");
|
||||
if (memPrefName == matchName || (!rts && index == 12)) {
|
||||
if (memPrefName == matchName ||
|
||||
(gRuntimeServiceDuringInit && index == 12)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MIN_EMPTY_CHUNK_COUNT);
|
||||
continue;
|
||||
}
|
||||
|
||||
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_max_empty_chunk_count");
|
||||
if (memPrefName == matchName || (!rts && index == 13)) {
|
||||
if (memPrefName == matchName ||
|
||||
(gRuntimeServiceDuringInit && index == 13)) {
|
||||
UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MAX_EMPTY_CHUNK_COUNT);
|
||||
continue;
|
||||
}
|
||||
|
@ -1765,6 +1773,9 @@ RuntimeService::Init()
|
|||
NS_WARNING("Failed to register for offline notification event!");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!");
|
||||
gRuntimeServiceDuringInit = true;
|
||||
|
||||
if (NS_FAILED(Preferences::RegisterCallback(
|
||||
LoadJSGCMemoryOptions,
|
||||
PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX,
|
||||
|
@ -1822,6 +1833,9 @@ RuntimeService::Init()
|
|||
NS_WARNING("Failed to register pref callbacks!");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
|
||||
gRuntimeServiceDuringInit = false;
|
||||
|
||||
// We assume atomic 32bit reads/writes. If this assumption doesn't hold on
|
||||
// some wacky platform then the worst that could happen is that the close
|
||||
// handler will run for a slightly different amount of time.
|
||||
|
|
|
@ -262,7 +262,7 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
|
|||
apzc = insertResult.first->second;
|
||||
PrintAPZCInfo(aLayer, apzc);
|
||||
}
|
||||
APZCTM_LOG("Found APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
|
||||
APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
|
||||
|
||||
// If we haven't encountered a layer already with the same metrics, then we need to
|
||||
// do the full reuse-or-make-an-APZC algorithm, which is contained inside the block
|
||||
|
@ -315,7 +315,7 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
|
|||
apzc->SetPrevSibling(nullptr);
|
||||
apzc->SetLastChild(nullptr);
|
||||
}
|
||||
APZCTM_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
|
||||
APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
|
||||
|
||||
apzc->NotifyLayersUpdated(aMetrics,
|
||||
aState.mIsFirstPaint && (aLayersId == aState.mOriginatingLayersId));
|
||||
|
|
|
@ -105,7 +105,7 @@ uint64_t
|
|||
InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
|
||||
{
|
||||
TouchBlockState* block = StartNewTouchBlock(aTarget, true);
|
||||
INPQ_LOG("%p injecting new touch block with id %llu and target %p\n",
|
||||
INPQ_LOG("%p injecting new touch block with id %" PRIu64 " and target %p\n",
|
||||
this, block->GetBlockId(), aTarget);
|
||||
ScheduleContentResponseTimeout(aTarget, block->GetBlockId());
|
||||
return block->GetBlockId();
|
||||
|
@ -162,7 +162,7 @@ void
|
|||
InputQueue::ContentResponseTimeout(const uint64_t& aInputBlockId) {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
INPQ_LOG("got a content response timeout; block=%llu\n", aInputBlockId);
|
||||
INPQ_LOG("got a content response timeout; block=%" PRIu64 "\n", aInputBlockId);
|
||||
bool success = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
|
||||
|
@ -179,7 +179,7 @@ void
|
|||
InputQueue::ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault) {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
INPQ_LOG("got a content response; block=%llu\n", aInputBlockId);
|
||||
INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
|
||||
bool success = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
|
||||
|
@ -196,7 +196,7 @@ void
|
|||
InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
INPQ_LOG("got allowed touch behaviours; block=%llu\n", aInputBlockId);
|
||||
INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
|
||||
bool success = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
|
||||
|
|
|
@ -1521,7 +1521,6 @@ TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
|
|||
int time = 0;
|
||||
uint64_t blockIds[2];
|
||||
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
|
||||
printf_stderr("blockids %llu %llu\n", blockIds[0], blockIds[1]);
|
||||
|
||||
// responses to the two touchstarts
|
||||
apzc->ContentReceivedTouch(blockIds[0], true);
|
||||
|
|
|
@ -324,22 +324,6 @@ gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
|
|||
mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2));
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
|
||||
gfxFloat angle1, gfxFloat angle2)
|
||||
{
|
||||
EnsurePathBuilder();
|
||||
mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2));
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius,
|
||||
gfxFloat angle1, gfxFloat angle2)
|
||||
{
|
||||
EnsurePathBuilder();
|
||||
mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1));
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
|
||||
{
|
||||
|
|
|
@ -135,7 +135,7 @@ public:
|
|||
mozilla::TemporaryRef<Path> GetPath();
|
||||
|
||||
/**
|
||||
* Appends the given path to the current path.
|
||||
* Sets the given path as the current path.
|
||||
*/
|
||||
void SetPath(Path* path);
|
||||
|
||||
|
@ -172,27 +172,6 @@ public:
|
|||
*/
|
||||
void QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2);
|
||||
|
||||
/**
|
||||
* Draws a clockwise arc (i.e. a circle segment).
|
||||
* @param center The center of the circle
|
||||
* @param radius The radius of the circle
|
||||
* @param angle1 Starting angle for the segment
|
||||
* @param angle2 Ending angle
|
||||
*/
|
||||
void Arc(const gfxPoint& center, gfxFloat radius,
|
||||
gfxFloat angle1, gfxFloat angle2);
|
||||
|
||||
/**
|
||||
* Draws a counter-clockwise arc (i.e. a circle segment).
|
||||
* @param center The center of the circle
|
||||
* @param radius The radius of the circle
|
||||
* @param angle1 Starting angle for the segment
|
||||
* @param angle2 Ending angle
|
||||
*/
|
||||
|
||||
void NegativeArc(const gfxPoint& center, gfxFloat radius,
|
||||
gfxFloat angle1, gfxFloat angle2);
|
||||
|
||||
// path helpers
|
||||
/**
|
||||
* Draws a line from start to end.
|
||||
|
|
|
@ -1572,6 +1572,16 @@ gfxWindowsPlatform::InitD3D11Devices()
|
|||
{
|
||||
mD3D11DeviceInitialized = true;
|
||||
|
||||
nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
|
||||
if (gfxInfo) {
|
||||
int32_t status;
|
||||
if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
|
||||
if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
|
||||
decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
|
||||
GetProcAddress(d3d11Module, "D3D11CreateDevice");
|
||||
|
|
|
@ -135,7 +135,7 @@ this.PluralForm = {
|
|||
// Check for array out of bounds or empty strings
|
||||
if ((ret == undefined) || (ret == "")) {
|
||||
// Report the caller to help figure out who is causing badness
|
||||
let caller = PluralForm.get.caller ? PluralForm.get.caller.name : "top";
|
||||
let caller = Components.stack.caller ? Components.stack.caller.name : "top";
|
||||
|
||||
// Display a message in the error console
|
||||
log(["Index #", index, " of '", aWords, "' for value ", aNum,
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This unit test makes sure that PluralForm.get can be called from strict mode
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
||||
|
||||
delete PluralForm.numForms;
|
||||
delete PluralForm.get;
|
||||
[PluralForm.get, PluralForm.numForms] = PluralForm.makeGetter(9);
|
||||
|
||||
function run_test() {
|
||||
"use strict";
|
||||
|
||||
do_check_eq(3, PluralForm.numForms());
|
||||
do_check_eq("one", PluralForm.get(5, 'one;many'));
|
||||
}
|
|
@ -13,6 +13,7 @@ skip-if = toolkit == "windows" || toolkit == "cocoa"
|
|||
[test_collation_mac_icu.js]
|
||||
run-if = toolkit == "cocoa"
|
||||
|
||||
[test_bug1086527.js]
|
||||
[test_pluralForm.js]
|
||||
[test_pluralForm_english.js]
|
||||
[test_pluralForm_makeGetter.js]
|
||||
|
|
|
@ -466,7 +466,7 @@ namespace js {
|
|||
// Unary SIMD operators
|
||||
template<typename T>
|
||||
struct Abs {
|
||||
static inline T apply(T x) { return x < 0 ? -1 * x : x; }
|
||||
static inline T apply(T x) { return mozilla::Abs(x); }
|
||||
};
|
||||
template<typename T>
|
||||
struct Neg {
|
||||
|
@ -712,14 +712,14 @@ Swizzle(JSContext *cx, unsigned argc, Value *vp)
|
|||
if (args.length() != (V::lanes + 1) || !IsVectorObject<V>(args[0]))
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
int32_t lanes[V::lanes];
|
||||
uint32_t lanes[V::lanes];
|
||||
for (unsigned i = 0; i < V::lanes; i++) {
|
||||
int32_t lane = -1;
|
||||
if (!ToInt32(cx, args[i + 1], &lane))
|
||||
return false;
|
||||
if (lane < 0 || lane >= V::lanes)
|
||||
if (lane < 0 || uint32_t(lane) >= V::lanes)
|
||||
return ErrorBadArgs(cx);
|
||||
lanes[i] = lane;
|
||||
lanes[i] = uint32_t(lane);
|
||||
}
|
||||
|
||||
Elem *val = TypedObjectMemory<Elem *>(args[0]);
|
||||
|
@ -741,14 +741,14 @@ Shuffle(JSContext *cx, unsigned argc, Value *vp)
|
|||
if (args.length() != (V::lanes + 2) || !IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1]))
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
int32_t lanes[V::lanes];
|
||||
uint32_t lanes[V::lanes];
|
||||
for (unsigned i = 0; i < V::lanes; i++) {
|
||||
int32_t lane = -1;
|
||||
if (!ToInt32(cx, args[i + 2], &lane))
|
||||
return false;
|
||||
if (lane < 0 || lane >= (2 * V::lanes))
|
||||
if (lane < 0 || uint32_t(lane) >= (2 * V::lanes))
|
||||
return ErrorBadArgs(cx);
|
||||
lanes[i] = lane;
|
||||
lanes[i] = uint32_t(lane);
|
||||
}
|
||||
|
||||
Elem *lhs = TypedObjectMemory<Elem *>(args[0]);
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'float32x4 abs';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(-1, 2, -3, 4);
|
||||
var c = SIMD.float32x4.abs(a);
|
||||
assertEq(c.x, 1);
|
||||
|
@ -17,6 +15,20 @@ function test() {
|
|||
assertEq(c.z, 3);
|
||||
assertEq(c.w, 4);
|
||||
|
||||
var d = float32x4(-1.63, 2.46, -3.17, 4.94);
|
||||
var f = SIMD.float32x4.abs(d);
|
||||
assertEq(f.x, Math.fround(1.63));
|
||||
assertEq(f.y, Math.fround(2.46));
|
||||
assertEq(f.z, Math.fround(3.17));
|
||||
assertEq(f.w, Math.fround(4.94));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.abs(g);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, 0);
|
||||
assertEq(i.z, Infinity);
|
||||
assertEq(i.w, Infinity);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ var int32x4 = SIMD.int32x4;
|
|||
|
||||
var summary = 'float32x4 add';
|
||||
|
||||
function addf(a, b) {
|
||||
return Math.fround(Math.fround(a) + Math.fround(b));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var b = float32x4(10, 20, 30, 40);
|
||||
var c = SIMD.float32x4.add(a, b);
|
||||
|
@ -18,6 +20,22 @@ function test() {
|
|||
assertEq(c.z, 33);
|
||||
assertEq(c.w, 44);
|
||||
|
||||
var d = float32x4(1.57, 2.27, 3.57, 4.19);
|
||||
var e = float32x4(10.31, 20.49, 30.41, 40.72);
|
||||
var f = SIMD.float32x4.add(d, e);
|
||||
assertEq(f.x, addf(1.57, 10.31));
|
||||
assertEq(f.y, addf(2.27, 20.49));
|
||||
assertEq(f.z, addf(3.57, 30.41));
|
||||
assertEq(f.w, addf(4.19, 40.72));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var h = float32x4(0, -0, -Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.add(g, h);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, -0);
|
||||
assertEq(i.z, NaN);
|
||||
assertEq(i.w, -Infinity);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ var andf = (function() {
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var b = float32x4(10, 20, 30, 40);
|
||||
var c = SIMD.float32x4.and(a, b);
|
||||
|
@ -29,6 +27,22 @@ function test() {
|
|||
assertEq(c.z, andf(3, 30));
|
||||
assertEq(c.w, andf(4, 40));
|
||||
|
||||
var d = float32x4(1.51, 2.98, 3.65, 4.34);
|
||||
var e = float32x4(10.29, 20.12, 30.79, 40.41);
|
||||
var f = SIMD.float32x4.and(d, e);
|
||||
assertEq(f.x, andf(1.51, 10.29));
|
||||
assertEq(f.y, andf(2.98, 20.12));
|
||||
assertEq(f.z, andf(3.65, 30.79));
|
||||
assertEq(f.w, andf(4.34, 40.41));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var h = float32x4(NaN, -0, -Infinity, Infinity);
|
||||
var i = SIMD.float32x4.and(g, h);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, -0);
|
||||
assertEq(i.z, Infinity);
|
||||
assertEq(i.w, Infinity);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,14 +8,34 @@ var summary = 'float32x4 clamp';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float32x4(-13.37, 10.46, 31.79, 0.54);
|
||||
var lower = float32x4(2.1, 1.1, 50.13, 0.0);
|
||||
var upper = float32x4(2.56, 5.55, 55.93, 1.1);
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(-20, 10, 30, 0.5);
|
||||
var lower = float32x4(2, 1, 50, 0);
|
||||
var upper = float32x4(2.5, 5, 55, 1);
|
||||
var c = SIMD.float32x4.clamp(a, lower, upper);
|
||||
assertEq(c.x, Math.fround(2.1));
|
||||
assertEq(c.y, Math.fround(5.55));
|
||||
assertEq(c.z, Math.fround(50.13));
|
||||
assertEq(c.w, Math.fround(0.54));
|
||||
assertEq(c.x, 2);
|
||||
assertEq(c.y, 5);
|
||||
assertEq(c.z, 50);
|
||||
assertEq(c.w, 0.5);
|
||||
|
||||
var d = float32x4(-13.37, 10.46, 31.79, 0.54);
|
||||
var lower1 = float32x4(2.1, 1.1, 50.13, 0.0);
|
||||
var upper1 = float32x4(2.56, 5.55, 55.93, 1.1);
|
||||
var f = SIMD.float32x4.clamp(d, lower1, upper1);
|
||||
assertEq(f.x, Math.fround(2.1));
|
||||
assertEq(f.y, Math.fround(5.55));
|
||||
assertEq(f.z, Math.fround(50.13));
|
||||
assertEq(f.w, Math.fround(0.54));
|
||||
|
||||
var g = float32x4(4, -Infinity, 10, -10);
|
||||
var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity);
|
||||
var upper2 = float32x4(Infinity, 5, Infinity, Infinity);
|
||||
var i = SIMD.float32x4.clamp(g, lower2, upper2);
|
||||
assertEq(i.x, 5);
|
||||
assertEq(i.y, -Infinity);
|
||||
assertEq(i.z, 10);
|
||||
assertEq(i.w, -10);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -5,22 +5,39 @@ var int32x4 = SIMD.int32x4;
|
|||
|
||||
var summary = 'float32x4 div';
|
||||
|
||||
function divf(a, b) {
|
||||
return Math.fround(Math.fround(a) / Math.fround(b));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var b = float32x4(10, 20, 30, 40);
|
||||
var c = SIMD.float32x4.div(b,a);
|
||||
var c = SIMD.float32x4.div(b, a);
|
||||
assertEq(c.x, 10);
|
||||
assertEq(c.y, 10);
|
||||
assertEq(c.z, 10);
|
||||
assertEq(c.w, 10);
|
||||
|
||||
var d = float32x4(1.26, 2.03, 3.17, 4.59);
|
||||
var e = float32x4(11.025, 17.3768, 29.1957, 46.4049);
|
||||
var f = SIMD.float32x4.div(e, d);
|
||||
assertEq(f.x, divf(11.025, 1.26));
|
||||
assertEq(f.y, divf(17.3768, 2.03));
|
||||
assertEq(f.z, divf(29.1957, 3.17));
|
||||
assertEq(f.w, divf(46.4049, 4.59));
|
||||
|
||||
var g = float32x4(0, -0, Infinity, -Infinity);
|
||||
var h = float32x4(1, 1, -Infinity, Infinity);
|
||||
var i = SIMD.float32x4.div(h, g);
|
||||
assertEq(i.x, Infinity);
|
||||
assertEq(i.y, -Infinity);
|
||||
assertEq(i.z, NaN);
|
||||
assertEq(i.w, NaN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ var summary = 'float32x4 equal';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of NaN/-0/Infinity/-Infinity border cases.
|
||||
|
||||
var a = float32x4(1, 20, 30, 40);
|
||||
var b = float32x4(10, 20, 30, 4);
|
||||
|
@ -18,6 +18,14 @@ function test() {
|
|||
assertEq(c.z, -1);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var d = float32x4(1.89, 20.51, 30.46, 40.12);
|
||||
var e = float32x4(10.89, 20.51, Math.fround(30.46), 4.12);
|
||||
var f = SIMD.float32x4.equal(d, e);
|
||||
assertEq(c.x, 0);
|
||||
assertEq(c.y, -1);
|
||||
assertEq(c.z, -1);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ var summary = 'float32x4 fromInt32x4';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var c = SIMD.float32x4.fromInt32x4(a);
|
||||
|
@ -17,6 +18,15 @@ function test() {
|
|||
assertEq(c.z, 3);
|
||||
assertEq(c.w, 4);
|
||||
|
||||
var value1 = Math.pow(2, 30) - 1;
|
||||
var value2 = -Math.pow(2, 30);
|
||||
var d = int32x4(INT32_MIN, INT32_MAX, value1, value2);
|
||||
var f = float32x4.fromInt32x4(d);
|
||||
assertEq(f.x, Math.fround(INT32_MIN));
|
||||
assertEq(f.y, Math.fround(INT32_MAX));
|
||||
assertEq(f.z, Math.fround(value1));
|
||||
assertEq(f.w, Math.fround(value2));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ var summary = 'float32x4 fromInt32x4Bits';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
var INT32_MAX = Math.pow(2, 31) - 1;
|
||||
var INT32_MIN = -Math.pow(2, 31);
|
||||
|
||||
var a = int32x4(100, 200, 300, 400);
|
||||
var c = SIMD.float32x4.fromInt32x4Bits(a);
|
||||
|
@ -17,6 +18,13 @@ function test() {
|
|||
assertEq(c.z, 4.203895392974451e-43);
|
||||
assertEq(c.w, 5.605193857299268e-43);
|
||||
|
||||
var d = int32x4(INT32_MIN, INT32_MAX, 0, 0);
|
||||
var f = float32x4.fromInt32x4Bits(d);
|
||||
assertEq(f.x, -0);
|
||||
assertEq(f.y, NaN);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,16 +8,25 @@ var summary = 'float32x4 greaterThan';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 3, 40);
|
||||
var b = float32x4(10, 2, 30, 4);
|
||||
var c = SIMD.float32x4.greaterThan(b,a);
|
||||
var c = SIMD.float32x4.greaterThan(b, a);
|
||||
assertEq(c.x, -1);
|
||||
assertEq(c.y, 0);
|
||||
assertEq(c.z, -1);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var d = float32x4(10.8399, 20.37, 3.07, 4.6802);
|
||||
var e = float32x4(10.8401, 20.367, 3.1, 4.6801);
|
||||
var f = float32x4.greaterThan(e, d);
|
||||
assertEq(f.x, -1);
|
||||
assertEq(f.y, 0);
|
||||
assertEq(f.z, -1);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,16 +8,25 @@ var summary = 'float32x4 greaterThanOrEqual';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 30, 40);
|
||||
var b = float32x4(10, 20, 30, 4);
|
||||
var c = SIMD.float32x4.greaterThanOrEqual(b,a);
|
||||
var c = SIMD.float32x4.greaterThanOrEqual(b, a);
|
||||
assertEq(c.x, -1);
|
||||
assertEq(c.y, -1);
|
||||
assertEq(c.z, -1);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var d = float32x4(10.029, 20.87, 30.56, 4.7);
|
||||
var e = float32x4(10.03, 20.87, 30.56, 4.698);
|
||||
var f = float32x4.greaterThanOrEqual(e, d);
|
||||
assertEq(f.x, -1);
|
||||
assertEq(f.y, -1);
|
||||
assertEq(f.z, -1);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ var summary = 'float32x4 lessThan';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 3, 40);
|
||||
var b = float32x4(10, 2, 30, 4);
|
||||
|
@ -18,6 +19,14 @@ function test() {
|
|||
assertEq(c.z, -1);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var d = float32x4(1.5399, 20.001, 30.045, 4.74);
|
||||
var e = float32x4(1.54, 19.999, 30.05, 4.72);
|
||||
var f = float32x4.lessThan(a, b);
|
||||
assertEq(f.x, -1);
|
||||
assertEq(f.y, 0);
|
||||
assertEq(f.z, -1);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ var summary = 'float32x4 lessThanOrEqual';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 30, 40);
|
||||
var b = float32x4(10, 20, 30, 4);
|
||||
|
@ -18,6 +19,14 @@ function test() {
|
|||
assertEq(c.z, -1);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var d = float32x4(9.999, 20.78, 30.14, 40.1235);
|
||||
var e = float32x4(10, 20.78, 30.14, 40.123);
|
||||
var f = float32x4.lessThanOrEqual(d, e);
|
||||
assertEq(f.x, -1);
|
||||
assertEq(f.y, -1);
|
||||
assertEq(f.z, -1);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ var summary = 'float32x4 max';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 30, 4);
|
||||
var b = float32x4(10, 2, 3, 40);
|
||||
|
@ -18,6 +19,14 @@ function test() {
|
|||
assertEq(c.z, 30);
|
||||
assertEq(c.w, 40);
|
||||
|
||||
var d = float32x4(9.999, 2.1234, 30.4443, 4);
|
||||
var e = float32x4(10, 2.1233, 30.4444, 4.0001);
|
||||
var f = float32x4.max(d, e);
|
||||
assertEq(f.x, 10);
|
||||
assertEq(f.y, Math.fround(2.1234));
|
||||
assertEq(f.z, Math.fround(30.4444));
|
||||
assertEq(f.w, Math.fround(4.0001));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ var summary = 'float32x4 min';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 3, 40);
|
||||
var b = float32x4(10, 2, 30, 4);
|
||||
|
@ -18,6 +19,14 @@ function test() {
|
|||
assertEq(c.z, 3);
|
||||
assertEq(c.w, 4);
|
||||
|
||||
var d = float32x4(1.4321, 20.5567, 30.8999, 4.0002);
|
||||
var e = float32x4(1.432, 20.5568, 30.8998, 4.0001);
|
||||
var f = float32x4.min(d, e);
|
||||
assertEq(f.x, Math.fround(1.432));
|
||||
assertEq(f.y, Math.fround(20.5567));
|
||||
assertEq(f.z, Math.fround(30.8998));
|
||||
assertEq(f.w, Math.fround(4.0001));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -3,13 +3,15 @@ var BUGNUMBER = 946042;
|
|||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float32x4 add';
|
||||
var summary = 'float32x4 mul';
|
||||
|
||||
function mulf(a, b) {
|
||||
return Math.fround(Math.fround(a) * Math.fround(b));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var b = float32x4(10, 20, 30, 40);
|
||||
var c = SIMD.float32x4.mul(a, b);
|
||||
|
@ -18,9 +20,24 @@ function test() {
|
|||
assertEq(c.z, 90);
|
||||
assertEq(c.w, 160);
|
||||
|
||||
var d = float32x4(1.66, 2.57, 3.73, 4.12);
|
||||
var e = float32x4(10.67, 20.68, 30.02, 40.58);
|
||||
var f = SIMD.float32x4.mul(d, e);
|
||||
assertEq(f.x, mulf(1.66, 10.67));
|
||||
assertEq(f.y, mulf(2.57, 20.68));
|
||||
assertEq(f.z, mulf(3.73, 30.02));
|
||||
assertEq(f.w, mulf(4.12, 40.58));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var h = float32x4(NaN, -0, -Infinity, 0);
|
||||
var i = SIMD.float32x4.mul(g, h);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, 0);
|
||||
assertEq(i.z, -Infinity);
|
||||
assertEq(i.w, NaN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ var summary = 'float32x4 neg';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var c = SIMD.float32x4.neg(a);
|
||||
assertEq(c.x, -1);
|
||||
|
@ -17,6 +15,20 @@ function test() {
|
|||
assertEq(c.z, -3);
|
||||
assertEq(c.w, -4);
|
||||
|
||||
var d = float32x4(0.999, -0.001, 3.78, 4.05);
|
||||
var f = SIMD.float32x4.neg(d);
|
||||
assertEq(f.x, Math.fround(-0.999));
|
||||
assertEq(f.y, Math.fround(0.001));
|
||||
assertEq(f.z, Math.fround(-3.78));
|
||||
assertEq(f.w, Math.fround(-4.05));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.neg(g);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, 0);
|
||||
assertEq(i.z, -Infinity);
|
||||
assertEq(i.w, Infinity);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ var notf = (function() {
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(2, 13, -37, 4.2);
|
||||
var c = SIMD.float32x4.not(a);
|
||||
assertEq(c.x, notf(2));
|
||||
|
@ -27,6 +25,20 @@ function test() {
|
|||
assertEq(c.z, notf(-37));
|
||||
assertEq(c.w, notf(4.2));
|
||||
|
||||
var d = float32x4(2.897, 13.245, -37.781, 5.28);
|
||||
var f = SIMD.float32x4.not(d);
|
||||
assertEq(f.x, notf(2.897));
|
||||
assertEq(f.y, notf(13.245));
|
||||
assertEq(f.z, notf(-37.781));
|
||||
assertEq(f.w, notf(5.28));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.not(g);
|
||||
assertEq(i.x, notf(NaN));
|
||||
assertEq(i.y, notf(-0));
|
||||
assertEq(i.z, notf(Infinity));
|
||||
assertEq(i.w, notf(-Infinity));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ var summary = 'float32x4 notEqual';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of NaN/-0/Infinity/-Infinity border cases.
|
||||
|
||||
var a = float32x4(1, 20, 30, 40);
|
||||
var b = float32x4(10, 20, 30, 4);
|
||||
|
@ -18,6 +18,14 @@ function test() {
|
|||
assertEq(c.z, 0);
|
||||
assertEq(c.w, -1);
|
||||
|
||||
var d = float32x4(9.98, 20.65, 30.14, 4.235);
|
||||
var e = float32x4(9.99, 20.65, Math.fround(30.14), 4.23);
|
||||
var f = SIMD.float32x4.notEqual(d, e);
|
||||
assertEq(f.x, -1);
|
||||
assertEq(f.y, 0);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, -1);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ var orf = (function() {
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var b = float32x4(10, 20, 30, 40);
|
||||
var c = SIMD.float32x4.or(a, b);
|
||||
|
@ -29,6 +27,22 @@ function test() {
|
|||
assertEq(c.z, orf(3, 30));
|
||||
assertEq(c.w, orf(4, 40));
|
||||
|
||||
var d = float32x4(1.12, 2.39, 3.83, 4.57);
|
||||
var e = float32x4(10.76, 20.41, 30.96, 40.23);
|
||||
var f = SIMD.float32x4.or(d, e);
|
||||
assertEq(f.x, orf(1.12, 10.76));
|
||||
assertEq(f.y, orf(2.39, 20.41));
|
||||
assertEq(f.z, orf(3.83, 30.96));
|
||||
assertEq(f.w, orf(4.57, 40.23));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var h = float32x4(5, 5, -Infinity, Infinity);
|
||||
var i = SIMD.float32x4.or(g, h);
|
||||
assertEq(i.x, orf(NaN, 5));
|
||||
assertEq(i.y, orf(-0, 5));
|
||||
assertEq(i.z, orf(Infinity, -Infinity));
|
||||
assertEq(i.w, orf(-Infinity, Infinity));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ var int32x4 = SIMD.int32x4;
|
|||
|
||||
var summary = 'float32x4 reciprocal';
|
||||
|
||||
function reciprocalf(a) {
|
||||
return Math.fround(1 / Math.fround(a));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 0.5, 0.25, 0.125);
|
||||
var c = SIMD.float32x4.reciprocal(a);
|
||||
assertEq(c.x, 1);
|
||||
|
@ -17,6 +19,20 @@ function test() {
|
|||
assertEq(c.z, 4);
|
||||
assertEq(c.w, 8);
|
||||
|
||||
var d = float32x4(1.6, 0.8, 0.4, 0.2);
|
||||
var f = SIMD.float32x4.reciprocal(d);
|
||||
assertEq(f.x, reciprocalf(1.6));
|
||||
assertEq(f.y, reciprocalf(0.8));
|
||||
assertEq(f.z, reciprocalf(0.4));
|
||||
assertEq(f.w, reciprocalf(0.2));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.reciprocal(g);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, -Infinity);
|
||||
assertEq(i.z, 0);
|
||||
assertEq(i.w, -0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ var int32x4 = SIMD.int32x4;
|
|||
|
||||
var summary = 'float32x4 reciprocalSqrt';
|
||||
|
||||
function reciprocalsqrtf(a) {
|
||||
return Math.fround(Math.sqrt(1 / Math.fround(a)));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 1, 0.25, 0.25);
|
||||
var c = SIMD.float32x4.reciprocalSqrt(a);
|
||||
assertEq(c.x, 1);
|
||||
|
@ -17,6 +19,20 @@ function test() {
|
|||
assertEq(c.z, 2);
|
||||
assertEq(c.w, 2);
|
||||
|
||||
var d = float32x4(25, 16, 6.25, 1.5625);
|
||||
var f = SIMD.float32x4.reciprocalSqrt(d);
|
||||
assertEq(f.x, reciprocalsqrtf(25));
|
||||
assertEq(f.y, reciprocalsqrtf(16));
|
||||
assertEq(f.z, reciprocalsqrtf(6.25));
|
||||
assertEq(f.w, reciprocalsqrtf(1.5625));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.reciprocalSqrt(g);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, -Infinity);
|
||||
assertEq(i.z, 0);
|
||||
assertEq(i.w, NaN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ var int32x4 = SIMD.int32x4;
|
|||
|
||||
var summary = 'float32x4 scale';
|
||||
|
||||
function mulf(a, b) {
|
||||
return Math.fround(Math.fround(a) * Math.fround(b));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var c = SIMD.float32x4.scale(a, 2);
|
||||
assertEq(c.x, 2);
|
||||
|
@ -17,6 +19,20 @@ function test() {
|
|||
assertEq(c.z, 6);
|
||||
assertEq(c.w, 8);
|
||||
|
||||
var d = float32x4(1.34, 2.76, 3.21, 4.09);
|
||||
var f = float32x4.scale(d, 2.54);
|
||||
assertEq(f.x, mulf(1.34, 2.54));
|
||||
assertEq(f.y, mulf(2.76, 2.54));
|
||||
assertEq(f.z, mulf(3.21, 2.54));
|
||||
assertEq(f.w, mulf(4.09, 2.54));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = float32x4.scale(g, 2.54);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, -0);
|
||||
assertEq(i.z, Infinity);
|
||||
assertEq(i.w, -Infinity);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ var int32x4 = SIMD.int32x4;
|
|||
|
||||
var summary = 'float32x4 sqrt';
|
||||
|
||||
function sqrtf(a) {
|
||||
return Math.fround(Math.sqrt(Math.fround(a)));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 4, 9, 16);
|
||||
var c = SIMD.float32x4.sqrt(a);
|
||||
assertEq(c.x, 1);
|
||||
|
@ -17,6 +19,20 @@ function test() {
|
|||
assertEq(c.z, 3);
|
||||
assertEq(c.w, 4);
|
||||
|
||||
var d = float32x4(2.7225, 7.3441, 9.4249, -1);
|
||||
var f = SIMD.float32x4.sqrt(d);
|
||||
assertEq(f.x, sqrtf(2.7225));
|
||||
assertEq(f.y, sqrtf(7.3441));
|
||||
assertEq(f.z, sqrtf(9.4249));
|
||||
assertEq(f.w, NaN);
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.sqrt(g);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, -0);
|
||||
assertEq(i.z, Infinity);
|
||||
assertEq(i.w, NaN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -5,19 +5,37 @@ var int32x4 = SIMD.int32x4;
|
|||
|
||||
var summary = 'float32x4 sub';
|
||||
|
||||
function subf(a, b) {
|
||||
return Math.fround(Math.fround(a) - Math.fround(b));
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var b = float32x4(10, 20, 30, 40);
|
||||
var c = SIMD.float32x4.sub(b,a);
|
||||
var c = SIMD.float32x4.sub(b, a);
|
||||
assertEq(c.x, 9);
|
||||
assertEq(c.y, 18);
|
||||
assertEq(c.z, 27);
|
||||
assertEq(c.w, 36);
|
||||
|
||||
var d = float32x4(1.34, 2.95, 3.17, 4.29);
|
||||
var e = float32x4(10.18, 20.43, 30.63, 40.38);
|
||||
var f = SIMD.float32x4.sub(e, d);
|
||||
assertEq(f.x, subf(10.18, 1.34));
|
||||
assertEq(f.y, subf(20.43, 2.95));
|
||||
assertEq(f.z, subf(30.63, 3.17));
|
||||
assertEq(f.w, subf(40.38, 4.29));
|
||||
|
||||
var g = float32x4(NaN, -0, -Infinity, -Infinity);
|
||||
var h = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var i = SIMD.float32x4.sub(h, g);
|
||||
assertEq(i.x, NaN);
|
||||
assertEq(i.y, 0);
|
||||
assertEq(i.z, Infinity);
|
||||
assertEq(i.w, NaN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -8,18 +8,81 @@ var summary = 'float32x4 with';
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var x = SIMD.float32x4.withX(a, 5);
|
||||
var y = SIMD.float32x4.withY(a, 5);
|
||||
var z = SIMD.float32x4.withZ(a, 5);
|
||||
var w = SIMD.float32x4.withW(a, 5);
|
||||
assertEq(x.x, 5);
|
||||
assertEq(x.y, 2);
|
||||
assertEq(x.z, 3);
|
||||
assertEq(x.w, 4);
|
||||
|
||||
assertEq(y.x, 1);
|
||||
assertEq(y.y, 5);
|
||||
assertEq(y.z, 3);
|
||||
assertEq(y.w, 4);
|
||||
|
||||
assertEq(z.x, 1);
|
||||
assertEq(z.y, 2);
|
||||
assertEq(z.z, 5);
|
||||
assertEq(z.w, 4);
|
||||
|
||||
assertEq(w.x, 1);
|
||||
assertEq(w.y, 2);
|
||||
assertEq(w.z, 3);
|
||||
assertEq(w.w, 5);
|
||||
|
||||
var b = float32x4(1.87, 2.08, 3.84, 4.17);
|
||||
var x1 = SIMD.float32x4.withX(b, 5.38);
|
||||
var y1 = SIMD.float32x4.withY(b, 5.19);
|
||||
var z1 = SIMD.float32x4.withZ(b, 5.11);
|
||||
var w1 = SIMD.float32x4.withW(b, 5.07);
|
||||
assertEq(x1.x, Math.fround(5.38));
|
||||
assertEq(x1.y, Math.fround(2.08));
|
||||
assertEq(x1.z, Math.fround(3.84));
|
||||
assertEq(x1.w, Math.fround(4.17));
|
||||
|
||||
assertEq(y1.x, Math.fround(1.87));
|
||||
assertEq(y1.y, Math.fround(5.19));
|
||||
assertEq(y1.z, Math.fround(3.84));
|
||||
assertEq(y1.w, Math.fround(4.17));
|
||||
|
||||
assertEq(z1.x, Math.fround(1.87));
|
||||
assertEq(z1.y, Math.fround(2.08));
|
||||
assertEq(z1.z, Math.fround(5.11));
|
||||
assertEq(z1.w, Math.fround(4.17));
|
||||
|
||||
assertEq(w1.x, Math.fround(1.87));
|
||||
assertEq(w1.y, Math.fround(2.08));
|
||||
assertEq(w1.z, Math.fround(3.84));
|
||||
assertEq(w1.w, Math.fround(5.07));
|
||||
|
||||
var c = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var x2 = SIMD.float32x4.withX(c, 0);
|
||||
var y2 = SIMD.float32x4.withY(c, 0);
|
||||
var z2 = SIMD.float32x4.withZ(c, 0);
|
||||
var w2 = SIMD.float32x4.withW(c, 0);
|
||||
assertEq(x2.x, 0);
|
||||
assertEq(x2.y, -0);
|
||||
assertEq(x2.z, Infinity);
|
||||
assertEq(x2.w, -Infinity);
|
||||
|
||||
assertEq(y2.x, NaN);
|
||||
assertEq(y2.y, 0);
|
||||
assertEq(y2.z, Infinity);
|
||||
assertEq(y2.w, -Infinity);
|
||||
|
||||
assertEq(z2.x, NaN);
|
||||
assertEq(z2.y, -0);
|
||||
assertEq(z2.z, 0);
|
||||
assertEq(z2.w, -Infinity);
|
||||
|
||||
assertEq(w2.x, NaN);
|
||||
assertEq(w2.y, -0);
|
||||
assertEq(w2.z, Infinity);
|
||||
assertEq(w2.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ var xorf = (function() {
|
|||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var b = float32x4(10, 20, 30, 40);
|
||||
var c = SIMD.float32x4.xor(a, b);
|
||||
|
@ -29,6 +27,22 @@ function test() {
|
|||
assertEq(c.z, xorf(3, 30));
|
||||
assertEq(c.w, xorf(4, 40));
|
||||
|
||||
var d = float32x4(1.07, 2.62, 3.79, 4.15);
|
||||
var e = float32x4(10.38, 20.47, 30.44, 40.16);
|
||||
var f = SIMD.float32x4.xor(d, e);
|
||||
assertEq(f.x, xorf(1.07, 10.38));
|
||||
assertEq(f.y, xorf(2.62, 20.47));
|
||||
assertEq(f.z, xorf(3.79, 30.44));
|
||||
assertEq(f.w, xorf(4.15, 40.16));
|
||||
|
||||
var g = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var h = float32x4(-0, Infinity, -Infinity, NaN);
|
||||
var i = SIMD.float32x4.xor(g, h);
|
||||
assertEq(i.x, xorf(NaN, -0));
|
||||
assertEq(i.y, xorf(-0, Infinity));
|
||||
assertEq(i.z, xorf(Infinity, -Infinity));
|
||||
assertEq(i.w, xorf(-Infinity, NaN));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -3213,15 +3213,11 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
const size_t kStackQuota = 2 * kDefaultStackQuota;
|
||||
const size_t kTrustedScriptBuffer = 246 * 1024;
|
||||
#elif defined(XP_WIN)
|
||||
// 1MB is the default stack size on Windows, so use 900k. And since 32-bit
|
||||
// Windows stack frames are 3.4k each, let's use a buffer of 48k and double
|
||||
// that for 64-bit.
|
||||
//
|
||||
// Note - Frames on Win32 PGO builds seem to have grown recently, and 48k
|
||||
// stacks seem about 20-30% too small - so we bump it to 64k.
|
||||
// 1MB is the default stack size on Windows, so use 900k.
|
||||
// Windows PGO stack frames have unfortunately gotten pretty large lately. :-(
|
||||
const size_t kStackQuota = 900 * 1024;
|
||||
const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 96 * 1024
|
||||
: 64 * 1024;
|
||||
const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 120 * 1024
|
||||
: 80 * 1024;
|
||||
// The following two configurations are linux-only. Given the numbers above,
|
||||
// we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively.
|
||||
#elif defined(DEBUG)
|
||||
|
|
|
@ -384,21 +384,24 @@ SelectionCarets::UpdateSelectionCarets()
|
|||
return;
|
||||
}
|
||||
|
||||
if (selection->GetRangeCount() <= 0) {
|
||||
if (selection->IsCollapsed()) {
|
||||
SetVisibility(false);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
|
||||
if (range->Collapsed()) {
|
||||
SetVisibility(false);
|
||||
return;
|
||||
}
|
||||
int32_t rangeCount = selection->GetRangeCount();
|
||||
nsRefPtr<nsRange> firstRange = selection->GetRangeAt(0);
|
||||
nsRefPtr<nsRange> lastRange = selection->GetRangeAt(rangeCount - 1);
|
||||
|
||||
nsLayoutUtils::FirstAndLastRectCollector collector;
|
||||
nsRange::CollectClientRects(&collector, range,
|
||||
range->GetStartParent(), range->StartOffset(),
|
||||
range->GetEndParent(), range->EndOffset(), true, true);
|
||||
nsLayoutUtils::FirstAndLastRectCollector collectorStart;
|
||||
nsRange::CollectClientRects(&collectorStart, firstRange,
|
||||
firstRange->GetStartParent(), firstRange->StartOffset(),
|
||||
firstRange->GetEndParent(), firstRange->EndOffset(), true, true);
|
||||
|
||||
nsLayoutUtils::FirstAndLastRectCollector collectorEnd;
|
||||
nsRange::CollectClientRects(&collectorEnd, lastRange,
|
||||
lastRange->GetStartParent(), lastRange->StartOffset(),
|
||||
lastRange->GetEndParent(), lastRange->EndOffset(), true, true);
|
||||
|
||||
nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
|
||||
nsIFrame* rootFrame = mPresShell->GetRootFrame();
|
||||
|
@ -412,11 +415,11 @@ SelectionCarets::UpdateSelectionCarets()
|
|||
nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
|
||||
int32_t startOffset;
|
||||
nsIFrame* startFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
|
||||
range, fs, false, startOffset);
|
||||
firstRange, fs, false, startOffset);
|
||||
|
||||
int32_t endOffset;
|
||||
nsIFrame* endFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
|
||||
range, fs, true, endOffset);
|
||||
lastRange, fs, true, endOffset);
|
||||
|
||||
if (!startFrame || !endFrame) {
|
||||
SetVisibility(false);
|
||||
|
@ -442,15 +445,15 @@ SelectionCarets::UpdateSelectionCarets()
|
|||
|
||||
// If start frame is LTR, then place start caret in first rect's leftmost
|
||||
// otherwise put it to first rect's rightmost.
|
||||
ReduceRectToVerticalEdge(collector.mFirstRect, startFrameIsRTL);
|
||||
ReduceRectToVerticalEdge(collectorStart.mFirstRect, startFrameIsRTL);
|
||||
|
||||
// Contrary to start frame, if end frame is LTR, put end caret to last
|
||||
// rect's rightmost position, otherwise, put it to last rect's leftmost.
|
||||
ReduceRectToVerticalEdge(collector.mLastRect, !endFrameIsRTL);
|
||||
ReduceRectToVerticalEdge(collectorEnd.mLastRect, !endFrameIsRTL);
|
||||
|
||||
nsAutoTArray<nsIFrame*, 16> hitFramesInFirstRect;
|
||||
nsLayoutUtils::GetFramesForArea(rootFrame,
|
||||
collector.mFirstRect,
|
||||
collectorStart.mFirstRect,
|
||||
hitFramesInFirstRect,
|
||||
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
|
||||
nsLayoutUtils::IGNORE_CROSS_DOC |
|
||||
|
@ -458,7 +461,7 @@ SelectionCarets::UpdateSelectionCarets()
|
|||
|
||||
nsAutoTArray<nsIFrame*, 16> hitFramesInLastRect;
|
||||
nsLayoutUtils::GetFramesForArea(rootFrame,
|
||||
collector.mLastRect,
|
||||
collectorEnd.mLastRect,
|
||||
hitFramesInLastRect,
|
||||
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
|
||||
nsLayoutUtils::IGNORE_CROSS_DOC |
|
||||
|
@ -467,11 +470,11 @@ SelectionCarets::UpdateSelectionCarets()
|
|||
SetStartFrameVisibility(hitFramesInFirstRect.Contains(startFrame));
|
||||
SetEndFrameVisibility(hitFramesInLastRect.Contains(endFrame));
|
||||
|
||||
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mFirstRect);
|
||||
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mLastRect);
|
||||
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorStart.mFirstRect);
|
||||
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorEnd.mLastRect);
|
||||
|
||||
SetStartFramePos(collector.mFirstRect.BottomLeft());
|
||||
SetEndFramePos(collector.mLastRect.BottomRight());
|
||||
SetStartFramePos(collectorStart.mFirstRect.BottomLeft());
|
||||
SetEndFramePos(collectorEnd.mLastRect.BottomRight());
|
||||
SetVisibility(true);
|
||||
|
||||
// If range select only one character, append tilt class name to it.
|
||||
|
@ -689,11 +692,13 @@ SelectionCarets::DragSelection(const nsPoint &movePoint)
|
|||
}
|
||||
|
||||
nsRefPtr<dom::Selection> selection = GetSelection();
|
||||
if (selection->GetRangeCount() <= 0) {
|
||||
int32_t rangeCount = selection->GetRangeCount();
|
||||
if (rangeCount <= 0) {
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
|
||||
nsRefPtr<nsRange> range = mDragMode == START_FRAME ?
|
||||
selection->GetRangeAt(0) : selection->GetRangeAt(rangeCount - 1);
|
||||
if (!CompareRangeWithContentOffset(range, fs, offsets, mDragMode)) {
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
@ -737,20 +742,22 @@ SelectionCarets::GetCaretYCenterPosition()
|
|||
}
|
||||
|
||||
nsRefPtr<dom::Selection> selection = GetSelection();
|
||||
if (selection->GetRangeCount() <= 0) {
|
||||
int32_t rangeCount = selection->GetRangeCount();
|
||||
if (rangeCount <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
|
||||
nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
|
||||
|
||||
MOZ_ASSERT(mDragMode != NONE);
|
||||
nsCOMPtr<nsIContent> node;
|
||||
uint32_t nodeOffset;
|
||||
if (mDragMode == START_FRAME) {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
|
||||
node = do_QueryInterface(range->GetStartParent());
|
||||
nodeOffset = range->StartOffset();
|
||||
} else {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeCount - 1);
|
||||
node = do_QueryInterface(range->GetEndParent());
|
||||
nodeOffset = range->EndOffset();
|
||||
}
|
||||
|
@ -885,12 +892,6 @@ SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc,
|
|||
nsISelection* aSel,
|
||||
int16_t aReason)
|
||||
{
|
||||
bool isCollapsed;
|
||||
aSel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed) {
|
||||
SetVisibility(false);
|
||||
return NS_OK;
|
||||
}
|
||||
if (!aReason || (aReason & (nsISelectionListener::DRAG_REASON |
|
||||
nsISelectionListener::KEYPRESS_REASON |
|
||||
nsISelectionListener::MOUSEDOWN_REASON))) {
|
||||
|
|
|
@ -4680,15 +4680,6 @@ static int32_t GetMaxChunkLength(nsFontMetrics& aFontMetrics)
|
|||
return std::min(aFontMetrics.GetMaxStringLength(), MAX_GFX_TEXT_BUF_SIZE);
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsLayoutUtils::AppUnitWidthOfString(const nsString& aString,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
nsRenderingContext& aContext)
|
||||
{
|
||||
return AppUnitWidthOfString(aString.get(), aString.Length(),
|
||||
aFontMetrics, aContext);
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsLayoutUtils::AppUnitWidthOfString(const char16_t *aString,
|
||||
uint32_t aLength,
|
||||
|
@ -4706,6 +4697,26 @@ nsLayoutUtils::AppUnitWidthOfString(const char16_t *aString,
|
|||
return width;
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsLayoutUtils::AppUnitWidthOfStringBidi(const char16_t* aString,
|
||||
uint32_t aLength,
|
||||
const nsIFrame* aFrame,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
nsRenderingContext& aContext)
|
||||
{
|
||||
nsPresContext* presContext = aFrame->PresContext();
|
||||
if (presContext->BidiEnabled()) {
|
||||
nsBidiLevel level =
|
||||
nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());
|
||||
return nsBidiPresUtils::MeasureTextWidth(aString, aLength, level,
|
||||
presContext, aContext,
|
||||
aFontMetrics);
|
||||
}
|
||||
aFontMetrics.SetTextRunRTL(false);
|
||||
return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
|
||||
aContext);
|
||||
}
|
||||
|
||||
nsBoundingMetrics
|
||||
nsLayoutUtils::AppUnitBoundsOfString(const char16_t* aString,
|
||||
uint32_t aLength,
|
||||
|
@ -4799,26 +4810,6 @@ nsLayoutUtils::DrawUniDirString(const char16_t* aString,
|
|||
}
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsLayoutUtils::GetStringWidth(const nsIFrame* aFrame,
|
||||
nsRenderingContext* aContext,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
const char16_t* aString,
|
||||
int32_t aLength)
|
||||
{
|
||||
nsPresContext* presContext = aFrame->PresContext();
|
||||
if (presContext->BidiEnabled()) {
|
||||
nsBidiLevel level =
|
||||
nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());
|
||||
return nsBidiPresUtils::MeasureTextWidth(aString, aLength,
|
||||
level, presContext, *aContext,
|
||||
aFontMetrics);
|
||||
}
|
||||
aFontMetrics.SetTextRunRTL(false);
|
||||
return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
|
||||
*aContext);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
|
||||
nsRenderingContext* aContext,
|
||||
|
|
|
@ -1326,11 +1326,28 @@ public:
|
|||
}
|
||||
static nscoord AppUnitWidthOfString(const nsString& aString,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
nsRenderingContext& aContext);
|
||||
nsRenderingContext& aContext) {
|
||||
return nsLayoutUtils::AppUnitWidthOfString(aString.get(), aString.Length(),
|
||||
aFontMetrics, aContext);
|
||||
}
|
||||
static nscoord AppUnitWidthOfString(const char16_t *aString,
|
||||
uint32_t aLength,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
nsRenderingContext& aContext);
|
||||
static nscoord AppUnitWidthOfStringBidi(const nsString& aString,
|
||||
const nsIFrame* aFrame,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
nsRenderingContext& aContext) {
|
||||
return nsLayoutUtils::AppUnitWidthOfStringBidi(aString.get(),
|
||||
aString.Length(), aFrame,
|
||||
aFontMetrics, aContext);
|
||||
}
|
||||
static nscoord AppUnitWidthOfStringBidi(const char16_t* aString,
|
||||
uint32_t aLength,
|
||||
const nsIFrame* aFrame,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
nsRenderingContext& aContext);
|
||||
|
||||
static nsBoundingMetrics AppUnitBoundsOfString(const char16_t* aString,
|
||||
uint32_t aLength,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
|
@ -1353,12 +1370,6 @@ public:
|
|||
nsFontMetrics& aFontMetrics,
|
||||
nsRenderingContext& aContext);
|
||||
|
||||
static nscoord GetStringWidth(const nsIFrame* aFrame,
|
||||
nsRenderingContext* aContext,
|
||||
nsFontMetrics& aFontMetrics,
|
||||
const char16_t* aString,
|
||||
int32_t aLength);
|
||||
|
||||
/**
|
||||
* Helper function for drawing text-shadow. The callback's job
|
||||
* is to draw whatever needs to be blurred onto the given context.
|
||||
|
|
|
@ -13,3 +13,4 @@ skip = false
|
|||
|
||||
[test_touchcaret.py]
|
||||
[test_selectioncarets.py]
|
||||
[test_selectioncarets_multiplerange.py]
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from by import By
|
||||
from marionette import Actions
|
||||
from marionette_test import MarionetteTestCase
|
||||
from selection import SelectionManager
|
||||
|
||||
|
||||
class SelectionCaretsMultipleRangeTest(MarionetteTestCase):
|
||||
_long_press_time = 1 # 1 second
|
||||
|
||||
def setUp(self):
|
||||
# Code to execute before a tests are run.
|
||||
MarionetteTestCase.setUp(self)
|
||||
self.actions = Actions(self.marionette)
|
||||
|
||||
def openTestHtml(self, enabled=True):
|
||||
# Open html for testing and enable selectioncaret and
|
||||
# non-editable support
|
||||
self.marionette.execute_script(
|
||||
'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' %
|
||||
('true' if enabled else 'false'))
|
||||
self.marionette.execute_script(
|
||||
'SpecialPowers.setBoolPref("selectioncaret.noneditable", %s);' %
|
||||
('true' if enabled else 'false'))
|
||||
|
||||
test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html')
|
||||
self.marionette.navigate(test_html)
|
||||
|
||||
self._body = self.marionette.find_element(By.ID, 'bd')
|
||||
self._sel3 = self.marionette.find_element(By.ID, 'sel3')
|
||||
self._sel4 = self.marionette.find_element(By.ID, 'sel4')
|
||||
self._sel6 = self.marionette.find_element(By.ID, 'sel6')
|
||||
self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1')
|
||||
|
||||
def _long_press_without_contextmenu(self, el, x, y):
|
||||
return self.actions.press(el, x, y).move_by_offset(0, 0).\
|
||||
wait(self._long_press_time).release()
|
||||
|
||||
def _long_press_to_select_word(self, el, wordOrdinal):
|
||||
sel = SelectionManager(el)
|
||||
original_content = sel.content
|
||||
words = original_content.split()
|
||||
self.assertTrue(wordOrdinal < len(words),
|
||||
'Expect at least %d words in the content.' % wordOrdinal)
|
||||
|
||||
# Calc offset
|
||||
offset = 0
|
||||
for i in range(wordOrdinal):
|
||||
offset += (len(words[i]) + 1)
|
||||
|
||||
# Move caret inside the word.
|
||||
el.tap()
|
||||
sel.move_caret_to_front()
|
||||
sel.move_caret_by_offset(offset)
|
||||
x, y = sel.caret_location()
|
||||
|
||||
# Long press the caret position. Selection carets should appear, and the
|
||||
# word will be selected. On Windows, those spaces after the word
|
||||
# will also be selected.
|
||||
self._long_press_without_contextmenu(el, x, y).perform()
|
||||
|
||||
def _to_unix_line_ending(self, s):
|
||||
"""Changes all Windows/Mac line endings in s to UNIX line endings."""
|
||||
|
||||
return s.replace('\r\n', '\n').replace('\r', '\n')
|
||||
|
||||
def test_long_press_to_select_non_selectable_word(self):
|
||||
'''Testing long press on non selectable field.
|
||||
We should not select anything when long press on non selectable fields.'''
|
||||
|
||||
self.openTestHtml(enabled=True)
|
||||
halfY = self._nonsel1.size['height'] / 2
|
||||
self._long_press_without_contextmenu(self._nonsel1, 0, halfY).perform()
|
||||
sel = SelectionManager(self._nonsel1)
|
||||
range_count = sel.range_count()
|
||||
self.assertEqual(range_count, 0)
|
||||
|
||||
def test_drag_caret_over_non_selectable_field(self):
|
||||
'''Testing drag caret over non selectable field.
|
||||
So that the selected content should exclude non selectable field and
|
||||
end selection caret should appear in last range's position.'''
|
||||
self.openTestHtml(enabled=True)
|
||||
|
||||
# Select target element and get target caret location
|
||||
self._long_press_to_select_word(self._sel4, 3)
|
||||
sel = SelectionManager(self._body)
|
||||
(_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
|
||||
|
||||
self._long_press_to_select_word(self._sel6, 0)
|
||||
(_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location()
|
||||
|
||||
# Select start element
|
||||
self._long_press_to_select_word(self._sel3, 3)
|
||||
|
||||
# Drag end caret to target location
|
||||
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
|
||||
self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
|
||||
self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
|
||||
'this 3\nuser can select this')
|
||||
|
||||
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
|
||||
self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y).perform()
|
||||
self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
|
||||
'this 3\nuser can select this 4\nuser can select this 5\nuser')
|
|
@ -763,9 +763,8 @@ TextOverflow::Marker::SetupString(nsIFrame* aFrame)
|
|||
nsRefPtr<nsFontMetrics> fm;
|
||||
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
|
||||
nsLayoutUtils::FontSizeInflationFor(aFrame));
|
||||
mWidth = nsLayoutUtils::GetStringWidth(aFrame, rc, *fm,
|
||||
mStyle->mString.get(),
|
||||
mStyle->mString.Length());
|
||||
mWidth = nsLayoutUtils::AppUnitWidthOfStringBidi(mStyle->mString, aFrame,
|
||||
*fm, *rc);
|
||||
}
|
||||
mIntrinsicISize = mWidth;
|
||||
mInitialized = true;
|
||||
|
|
|
@ -602,8 +602,8 @@ nsBulletFrame::GetDesiredSize(nsPresContext* aCX,
|
|||
GetListItemText(text);
|
||||
finalSize.BSize(wm) = fm->MaxHeight();
|
||||
finalSize.ISize(wm) =
|
||||
nsLayoutUtils::GetStringWidth(this, aRenderingContext, *fm,
|
||||
text.get(), text.Length());
|
||||
nsLayoutUtils::AppUnitWidthOfStringBidi(text, this, *fm,
|
||||
*aRenderingContext);
|
||||
aMetrics.SetBlockStartAscent(fm->MaxAscent());
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1035,7 +1035,8 @@ nsImageFrame::MeasureString(const char16_t* aString,
|
|||
|
||||
// Measure this chunk of text, and see if it fits
|
||||
nscoord width =
|
||||
nsLayoutUtils::GetStringWidth(this, &aContext, aFontMetrics, aString, len);
|
||||
nsLayoutUtils::AppUnitWidthOfStringBidi(aString, len, this, aFontMetrics,
|
||||
aContext);
|
||||
bool fits = (totalWidth + width) <= aMaxWidth;
|
||||
|
||||
// If it fits on the line, or it's the first word we've processed then
|
||||
|
|
|
@ -243,10 +243,9 @@ nscoord nsPageFrame::GetXPosition(nsRenderingContext& aRenderingContext,
|
|||
int32_t aJust,
|
||||
const nsString& aStr)
|
||||
{
|
||||
nscoord width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
|
||||
aFontMetrics,
|
||||
aStr.get(), aStr.Length());
|
||||
|
||||
nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(aStr, this,
|
||||
aFontMetrics,
|
||||
aRenderingContext);
|
||||
nscoord x = aRect.x;
|
||||
switch (aJust) {
|
||||
case nsIPrintSettings::kJustLeft:
|
||||
|
|
|
@ -100,10 +100,8 @@ nsSubDocumentFrame::Init(nsIContent* aContent,
|
|||
nsIFrame* aPrevInFlow)
|
||||
{
|
||||
// determine if we are a <frame> or <iframe>
|
||||
if (aContent) {
|
||||
nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
|
||||
mIsInline = frameElem ? false : true;
|
||||
}
|
||||
nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
|
||||
mIsInline = frameElem ? false : true;
|
||||
|
||||
nsLeafFrame::Init(aContent, aParent, aPrevInFlow);
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
|
||||
<math style="font-size:25px; position: absolute; top: 10px; left:10px;">
|
||||
<mrow id="outer">
|
||||
<mo>(</mo>
|
||||
<mrow>
|
||||
<mn id="a" style="visibility:hidden;">a</mn>
|
||||
<mo>&</mo>
|
||||
<mn id="c" style="visibility:hidden;">c</mn>
|
||||
</mrow>
|
||||
<mo>)</mo>
|
||||
</mrow>
|
||||
</math>
|
||||
|
||||
<!-- Implementation kludge. <mfenced> renders the position of the ampersand in
|
||||
a slightly different position compared to <mo>+<mrow>.
|
||||
In this test we are only concerned about the size of the fences "(" and
|
||||
")", so the ampersand gets redacted. -->
|
||||
<div id="div" style="position: absolute; background:black; top: 0px;
|
||||
height: 120px;"></div>
|
||||
|
||||
<script>
|
||||
function doTest()
|
||||
{
|
||||
a = document.getElementById("a");
|
||||
c = document.getElementById("c");
|
||||
div = document.getElementById("div");
|
||||
outer = document.getElementById("outer");
|
||||
|
||||
left = a.getBoundingClientRect().left; // div's left
|
||||
div.style.left = left + 'px';
|
||||
div.style.width = (c.getBoundingClientRect().right - left ) + 'px';
|
||||
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
|
||||
<math style="font-size:25px; position: absolute; top: 10px; left:10px;">
|
||||
<mfenced id="outer" separators="&">
|
||||
<mn id="a" style="visibility:hidden;">a</mn>
|
||||
<mn id="c" style="visibility:hidden;">c</mn>
|
||||
</mfenced>
|
||||
</math>
|
||||
|
||||
<!-- Implementation kludge. <mfenced> renders the position of the ampersand in
|
||||
a slightly different position compared to <mo>+<mrow>.
|
||||
In this test we are only concerned about the size of the fences "(" and
|
||||
")", so the ampersand gets redacted. -->
|
||||
<div id="div" style="position: absolute; background:black; top: 0px;
|
||||
height: 120px;"></div>
|
||||
|
||||
<script>
|
||||
function doTest()
|
||||
{
|
||||
a = document.getElementById("a");
|
||||
c = document.getElementById("c");
|
||||
div = document.getElementById("div");
|
||||
outer = document.getElementById("outer");
|
||||
|
||||
left = a.getBoundingClientRect().left; // div's left
|
||||
div.style.left = left + 'px';
|
||||
div.style.width = (c.getBoundingClientRect().right - left ) + 'px';
|
||||
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
</script>
|
||||
|
||||
</html>
|
|
@ -38,6 +38,7 @@ skip-if(B2G&&browserIsRemote) random-if(smallScreen&&Android) fuzzy(255,200) ==
|
|||
== mfenced-9.html mfenced-9-ref.html
|
||||
fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == mfenced-10.html mfenced-10-ref.html # Windows versions without Cambria Math, see bug 670592
|
||||
fails-if(winWidget&&d2d) == mfenced-11.html mfenced-11-ref.html
|
||||
== mfenced-12.html mfenced-12-ref.html
|
||||
== mi-mathvariant-1.xhtml mi-mathvariant-1-ref.xhtml
|
||||
== mi-mathvariant-2.xhtml mi-mathvariant-2-ref.xhtml
|
||||
!= mi-mathvariant-3.html mi-mathvariant-3-ref.html
|
||||
|
|
|
@ -3040,6 +3040,10 @@ struct nsStyleSVGReset {
|
|||
return mFilters.Length() > 0;
|
||||
}
|
||||
|
||||
bool HasNonScalingStroke() const {
|
||||
return mVectorEffect == NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE;
|
||||
}
|
||||
|
||||
nsStyleClipPath mClipPath; // [reset]
|
||||
nsTArray<nsStyleFilter> mFilters; // [reset]
|
||||
nsCOMPtr<nsIURI> mMask; // [reset]
|
||||
|
|
|
@ -463,113 +463,132 @@ nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
|
|||
nsSVGPathGeometryElement* element =
|
||||
static_cast<nsSVGPathGeometryElement*>(mContent);
|
||||
|
||||
RefPtr<DrawTarget> tmpDT;
|
||||
bool getFill = (aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
|
||||
((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
|
||||
StyleSVG()->mFill.mType != eStyleSVGPaintType_None);
|
||||
|
||||
bool getStroke = (aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
|
||||
((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
|
||||
nsSVGUtils::HasStroke(this));
|
||||
|
||||
bool gotSimpleBounds = false;
|
||||
if (!StyleSVGReset()->HasNonScalingStroke()) {
|
||||
Float strokeWidth = getStroke ? nsSVGUtils::GetStrokeWidth(this) : 0.f;
|
||||
Rect simpleBounds;
|
||||
gotSimpleBounds = element->GetGeometryBounds(&simpleBounds, strokeWidth,
|
||||
aToBBoxUserspace);
|
||||
if (gotSimpleBounds) {
|
||||
bbox = simpleBounds;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotSimpleBounds) {
|
||||
// Get the bounds using a Moz2D Path object (more expensive):
|
||||
RefPtr<DrawTarget> tmpDT;
|
||||
#ifdef XP_WIN
|
||||
// Unfortunately D2D backed DrawTarget produces bounds with rounding errors
|
||||
// when whole number results are expected, even in the case of trivial
|
||||
// calculations. To avoid that and meet the expectations of web content we
|
||||
// have to use a CAIRO DrawTarget. The most efficient way to do that is to
|
||||
// wrap the cached cairo_surface_t from ScreenReferenceSurface():
|
||||
nsRefPtr<gfxASurface> refSurf =
|
||||
gfxPlatform::GetPlatform()->ScreenReferenceSurface();
|
||||
tmpDT = gfxPlatform::GetPlatform()->
|
||||
CreateDrawTargetForSurface(refSurf, IntSize(1, 1));
|
||||
// Unfortunately D2D backed DrawTarget produces bounds with rounding errors
|
||||
// when whole number results are expected, even in the case of trivial
|
||||
// calculations. To avoid that and meet the expectations of web content we
|
||||
// have to use a CAIRO DrawTarget. The most efficient way to do that is to
|
||||
// wrap the cached cairo_surface_t from ScreenReferenceSurface():
|
||||
nsRefPtr<gfxASurface> refSurf =
|
||||
gfxPlatform::GetPlatform()->ScreenReferenceSurface();
|
||||
tmpDT = gfxPlatform::GetPlatform()->
|
||||
CreateDrawTargetForSurface(refSurf, IntSize(1, 1));
|
||||
#else
|
||||
tmpDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
tmpDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
#endif
|
||||
|
||||
FillRule fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
|
||||
RefPtr<Path> pathInUserSpace = element->GetOrBuildPath(*tmpDT, fillRule);
|
||||
if (!pathInUserSpace) {
|
||||
return bbox;
|
||||
}
|
||||
RefPtr<Path> pathInBBoxSpace;
|
||||
if (aToBBoxUserspace.IsIdentity()) {
|
||||
pathInBBoxSpace = pathInUserSpace;
|
||||
} else {
|
||||
RefPtr<PathBuilder> builder =
|
||||
pathInUserSpace->TransformedCopyToBuilder(aToBBoxUserspace, fillRule);
|
||||
pathInBBoxSpace = builder->Finish();
|
||||
if (!pathInBBoxSpace) {
|
||||
FillRule fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
|
||||
RefPtr<Path> pathInUserSpace = element->GetOrBuildPath(*tmpDT, fillRule);
|
||||
if (!pathInUserSpace) {
|
||||
return bbox;
|
||||
}
|
||||
}
|
||||
|
||||
// Be careful when replacing the following logic to get the fill and stroke
|
||||
// extents independently (instead of computing the stroke extents from the
|
||||
// path extents). You may think that you can just use the stroke extents if
|
||||
// there is both a fill and a stroke. In reality it's necessary to calculate
|
||||
// both the fill and stroke extents, and take the union of the two. There are
|
||||
// two reasons for this:
|
||||
//
|
||||
// # Due to stroke dashing, in certain cases the fill extents could actually
|
||||
// extend outside the stroke extents.
|
||||
// # If the stroke is very thin, cairo won't paint any stroke, and so the
|
||||
// stroke bounds that it will return will be empty.
|
||||
|
||||
Rect pathBBoxExtents = pathInBBoxSpace->GetBounds();
|
||||
if (!pathBBoxExtents.IsFinite()) {
|
||||
// This can happen in the case that we only have a move-to command in the
|
||||
// path commands, in which case we know nothing gets rendered.
|
||||
return bbox;
|
||||
}
|
||||
|
||||
// Account for fill:
|
||||
if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
|
||||
((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
|
||||
StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
|
||||
bbox = pathBBoxExtents;
|
||||
}
|
||||
|
||||
// Account for stroke:
|
||||
if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
|
||||
((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
|
||||
nsSVGUtils::HasStroke(this))) {
|
||||
#if 0
|
||||
// This disabled code is how we would calculate the stroke bounds using
|
||||
// Moz2D Path::GetStrokedBounds(). Unfortunately at the time of writing it
|
||||
// there are two problems that prevent us from using it.
|
||||
//
|
||||
// First, it seems that some of the Moz2D backends are really dumb. Not
|
||||
// only do some GetStrokeOptions() implementations sometimes significantly
|
||||
// overestimate the stroke bounds, but if an argument is passed for the
|
||||
// aTransform parameter then they just return bounds-of-transformed-bounds.
|
||||
// These two things combined can lead the bounds to be unacceptably
|
||||
// oversized, leading to massive over-invalidation.
|
||||
//
|
||||
// Second, the way we account for non-scaling-stroke by transforming the
|
||||
// path using the transform to the outer-<svg> element is not compatible
|
||||
// with the way that nsSVGPathGeometryFrame::Reflow() inserts a scale into
|
||||
// aToBBoxUserspace and then scales the bounds that we return.
|
||||
SVGContentUtils::AutoStrokeOptions strokeOptions;
|
||||
SVGContentUtils::GetStrokeOptions(&strokeOptions, element, StyleContext(),
|
||||
nullptr, SVGContentUtils::eIgnoreStrokeDashing);
|
||||
Rect strokeBBoxExtents;
|
||||
gfxMatrix userToOuterSVG;
|
||||
if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
|
||||
Matrix outerSVGToUser = ToMatrix(userToOuterSVG);
|
||||
outerSVGToUser.Invert();
|
||||
Matrix outerSVGToBBox = aToBBoxUserspace * outerSVGToUser;
|
||||
RefPtr<PathBuilder> builder =
|
||||
pathInUserSpace->TransformedCopyToBuilder(ToMatrix(userToOuterSVG));
|
||||
RefPtr<Path> pathInOuterSVGSpace = builder->Finish();
|
||||
strokeBBoxExtents =
|
||||
pathInOuterSVGSpace->GetStrokedBounds(strokeOptions, outerSVGToBBox);
|
||||
RefPtr<Path> pathInBBoxSpace;
|
||||
if (aToBBoxUserspace.IsIdentity()) {
|
||||
pathInBBoxSpace = pathInUserSpace;
|
||||
} else {
|
||||
strokeBBoxExtents =
|
||||
pathInUserSpace->GetStrokedBounds(strokeOptions, aToBBoxUserspace);
|
||||
RefPtr<PathBuilder> builder =
|
||||
pathInUserSpace->TransformedCopyToBuilder(aToBBoxUserspace, fillRule);
|
||||
pathInBBoxSpace = builder->Finish();
|
||||
if (!pathInBBoxSpace) {
|
||||
return bbox;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(strokeBBoxExtents.IsFinite(), "bbox is about to go bad");
|
||||
bbox.UnionEdges(strokeBBoxExtents);
|
||||
|
||||
// Be careful when replacing the following logic to get the fill and stroke
|
||||
// extents independently (instead of computing the stroke extents from the
|
||||
// path extents). You may think that you can just use the stroke extents if
|
||||
// there is both a fill and a stroke. In reality it's necessary to
|
||||
// calculate both the fill and stroke extents, and take the union of the
|
||||
// two. There are two reasons for this:
|
||||
//
|
||||
// # Due to stroke dashing, in certain cases the fill extents could
|
||||
// actually extend outside the stroke extents.
|
||||
// # If the stroke is very thin, cairo won't paint any stroke, and so the
|
||||
// stroke bounds that it will return will be empty.
|
||||
|
||||
Rect pathBBoxExtents = pathInBBoxSpace->GetBounds();
|
||||
if (!pathBBoxExtents.IsFinite()) {
|
||||
// This can happen in the case that we only have a move-to command in the
|
||||
// path commands, in which case we know nothing gets rendered.
|
||||
return bbox;
|
||||
}
|
||||
|
||||
// Account for fill:
|
||||
if (getFill) {
|
||||
bbox = pathBBoxExtents;
|
||||
}
|
||||
|
||||
// Account for stroke:
|
||||
if (getStroke) {
|
||||
#if 0
|
||||
// This disabled code is how we would calculate the stroke bounds using
|
||||
// Moz2D Path::GetStrokedBounds(). Unfortunately at the time of writing
|
||||
// it there are two problems that prevent us from using it.
|
||||
//
|
||||
// First, it seems that some of the Moz2D backends are really dumb. Not
|
||||
// only do some GetStrokeOptions() implementations sometimes
|
||||
// significantly overestimate the stroke bounds, but if an argument is
|
||||
// passed for the aTransform parameter then they just return bounds-of-
|
||||
// transformed-bounds. These two things combined can lead the bounds to
|
||||
// be unacceptably oversized, leading to massive over-invalidation.
|
||||
//
|
||||
// Second, the way we account for non-scaling-stroke by transforming the
|
||||
// path using the transform to the outer-<svg> element is not compatible
|
||||
// with the way that nsSVGPathGeometryFrame::Reflow() inserts a scale
|
||||
// into aToBBoxUserspace and then scales the bounds that we return.
|
||||
SVGContentUtils::AutoStrokeOptions strokeOptions;
|
||||
SVGContentUtils::GetStrokeOptions(&strokeOptions, element,
|
||||
StyleContext(), nullptr,
|
||||
SVGContentUtils::eIgnoreStrokeDashing);
|
||||
Rect strokeBBoxExtents;
|
||||
gfxMatrix userToOuterSVG;
|
||||
if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
|
||||
Matrix outerSVGToUser = ToMatrix(userToOuterSVG);
|
||||
outerSVGToUser.Invert();
|
||||
Matrix outerSVGToBBox = aToBBoxUserspace * outerSVGToUser;
|
||||
RefPtr<PathBuilder> builder =
|
||||
pathInUserSpace->TransformedCopyToBuilder(ToMatrix(userToOuterSVG));
|
||||
RefPtr<Path> pathInOuterSVGSpace = builder->Finish();
|
||||
strokeBBoxExtents =
|
||||
pathInOuterSVGSpace->GetStrokedBounds(strokeOptions, outerSVGToBBox);
|
||||
} else {
|
||||
strokeBBoxExtents =
|
||||
pathInUserSpace->GetStrokedBounds(strokeOptions, aToBBoxUserspace);
|
||||
}
|
||||
MOZ_ASSERT(strokeBBoxExtents.IsFinite(), "bbox is about to go bad");
|
||||
bbox.UnionEdges(strokeBBoxExtents);
|
||||
#else
|
||||
// For now we just use nsSVGUtils::PathExtentsToMaxStrokeExtents:
|
||||
gfxRect strokeBBoxExtents =
|
||||
nsSVGUtils::PathExtentsToMaxStrokeExtents(ThebesRect(pathBBoxExtents),
|
||||
this,
|
||||
ThebesMatrix(aToBBoxUserspace));
|
||||
MOZ_ASSERT(ToRect(strokeBBoxExtents).IsFinite(), "bbox is about to go bad");
|
||||
bbox.UnionEdges(strokeBBoxExtents);
|
||||
gfxRect strokeBBoxExtents =
|
||||
nsSVGUtils::PathExtentsToMaxStrokeExtents(ThebesRect(pathBBoxExtents),
|
||||
this,
|
||||
ThebesMatrix(aToBBoxUserspace));
|
||||
MOZ_ASSERT(ToRect(strokeBBoxExtents).IsFinite(), "bbox is about to go bad");
|
||||
bbox.UnionEdges(strokeBBoxExtents);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Account for markers:
|
||||
|
|
|
@ -67,8 +67,8 @@ RecordingCmdLineHandler.prototype =
|
|||
cmdLine.preventDefault = true;
|
||||
},
|
||||
|
||||
helpInfo : " -recording <file> Record drawing for a given URL.\n" +
|
||||
" -recording-output <file> Specify destination file for a drawing recording.\n"
|
||||
helpInfo : " --recording <file> Record drawing for a given URL.\n" +
|
||||
" --recording-output <file> Specify destination file for a drawing recording.\n"
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RecordingCmdLineHandler]);
|
||||
|
|
|
@ -105,7 +105,7 @@ RefTestCmdLineHandler.prototype =
|
|||
cmdLine.preventDefault = true;
|
||||
},
|
||||
|
||||
helpInfo : " -reftest <file> Run layout acceptance tests on given manifest.\n"
|
||||
helpInfo : " --reftest <file> Run layout acceptance tests on given manifest.\n"
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RefTestCmdLineHandler]);
|
||||
|
|
|
@ -727,8 +727,8 @@ nsListBoxBodyFrame::ComputeIntrinsicISize(nsBoxLayoutState& aBoxLayoutState)
|
|||
getter_AddRefs(fm));
|
||||
|
||||
nscoord textWidth =
|
||||
nsLayoutUtils::GetStringWidth(this, rendContext, *fm,
|
||||
value.get(), value.Length());
|
||||
nsLayoutUtils::AppUnitWidthOfStringBidi(value, this, *fm,
|
||||
*rendContext);
|
||||
textWidth += width;
|
||||
|
||||
if (textWidth > largestWidth)
|
||||
|
|
|
@ -613,10 +613,9 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext,
|
|||
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
|
||||
|
||||
// see if the text will completely fit in the width given
|
||||
nscoord titleWidth = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
|
||||
*fm,
|
||||
mTitle.get(), mTitle.Length());
|
||||
|
||||
nscoord titleWidth =
|
||||
nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle, this, *fm,
|
||||
aRenderingContext);
|
||||
if (titleWidth <= aWidth) {
|
||||
mCroppedTitle = mTitle;
|
||||
if (HasRTLChars(mTitle)) {
|
||||
|
@ -713,8 +712,8 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext,
|
|||
case CropCenter:
|
||||
{
|
||||
nscoord stringWidth =
|
||||
nsLayoutUtils::GetStringWidth(this, &aRenderingContext, *fm,
|
||||
mTitle.get(), mTitle.Length());
|
||||
nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle, this, *fm,
|
||||
aRenderingContext);
|
||||
if (stringWidth <= aWidth) {
|
||||
// the entire string will fit in the maximum width
|
||||
mCroppedTitle.Insert(mTitle, 0);
|
||||
|
@ -771,8 +770,8 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext,
|
|||
break;
|
||||
}
|
||||
|
||||
return nsLayoutUtils::GetStringWidth(this, &aRenderingContext, *fm,
|
||||
mCroppedTitle.get(), mCroppedTitle.Length());
|
||||
return nsLayoutUtils::AppUnitWidthOfStringBidi(mCroppedTitle, this, *fm,
|
||||
aRenderingContext);
|
||||
}
|
||||
|
||||
#define OLD_ELLIPSIS NS_LITERAL_STRING("...")
|
||||
|
@ -1000,8 +999,8 @@ nsTextBoxFrame::GetTextSize(nsPresContext* aPresContext,
|
|||
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
|
||||
aSize.height = fontMet->MaxHeight();
|
||||
aSize.width =
|
||||
nsLayoutUtils::GetStringWidth(this, &aRenderingContext, *fontMet,
|
||||
aString.get(), aString.Length());
|
||||
nsLayoutUtils::AppUnitWidthOfStringBidi(aString, this, *fontMet,
|
||||
aRenderingContext);
|
||||
aAscent = fontMet->MaxAscent();
|
||||
}
|
||||
|
||||
|
|
|
@ -1319,9 +1319,9 @@ nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText,
|
|||
{
|
||||
NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
|
||||
|
||||
nscoord width =
|
||||
nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aFontMetrics,
|
||||
aText.get(), aText.Length());
|
||||
nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this,
|
||||
aFontMetrics,
|
||||
aRenderingContext);
|
||||
nscoord maxWidth = aTextRect.width;
|
||||
|
||||
if (aColumn->Overflow()) {
|
||||
|
@ -1459,9 +1459,8 @@ nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText,
|
|||
}
|
||||
}
|
||||
|
||||
width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
|
||||
aFontMetrics, aText.get(),
|
||||
aText.Length());
|
||||
width = nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this, aFontMetrics,
|
||||
aRenderingContext);
|
||||
}
|
||||
|
||||
switch (aColumn->GetTextAlignment()) {
|
||||
|
@ -1748,9 +1747,8 @@ nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
|
|||
nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
|
||||
getter_AddRefs(fm));
|
||||
// Get the width of the text itself
|
||||
nscoord width =
|
||||
nsLayoutUtils::GetStringWidth(this, aRenderingContext, *fm,
|
||||
cellText.get(), cellText.Length());
|
||||
nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(cellText, this, *fm,
|
||||
*aRenderingContext);
|
||||
nscoord totalTextWidth = width + bp.left + bp.right;
|
||||
aDesiredSize += totalTextWidth;
|
||||
return NS_OK;
|
||||
|
|
|
@ -227,18 +227,6 @@ InfallibleAllocPolicy::ExitOnFailure(const void* aP)
|
|||
}
|
||||
}
|
||||
|
||||
class FpWriteFunc : public JSONWriteFunc
|
||||
{
|
||||
public:
|
||||
explicit FpWriteFunc(FILE* aFp) : mFp(aFp) {}
|
||||
~FpWriteFunc() { fclose(mFp); }
|
||||
|
||||
void Write(const char* aStr) { fputs(aStr, mFp); }
|
||||
|
||||
private:
|
||||
FILE* mFp;
|
||||
};
|
||||
|
||||
static double
|
||||
Percent(size_t part, size_t whole)
|
||||
{
|
||||
|
@ -289,17 +277,11 @@ class Options
|
|||
{}
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
Normal, // run normally
|
||||
Test // do some basic correctness tests
|
||||
};
|
||||
|
||||
char* mDMDEnvVar; // a saved copy, for later printing
|
||||
|
||||
NumOption<size_t> mSampleBelowSize;
|
||||
NumOption<uint32_t> mMaxFrames;
|
||||
bool mShowDumpStats;
|
||||
Mode mMode;
|
||||
|
||||
void BadArg(const char* aArg);
|
||||
static const char* ValueIfMatch(const char* aArg, const char* aOptionName);
|
||||
|
@ -316,9 +298,7 @@ public:
|
|||
size_t MaxFrames() const { return mMaxFrames.mActual; }
|
||||
size_t ShowDumpStats() const { return mShowDumpStats; }
|
||||
|
||||
void SetSampleBelowSize(size_t aN) { mSampleBelowSize.mActual = aN; }
|
||||
|
||||
bool IsTestMode() const { return mMode == Test; }
|
||||
void SetSampleBelowSize(size_t aSize) { mSampleBelowSize.mActual = aSize; }
|
||||
};
|
||||
|
||||
static Options *gOptions;
|
||||
|
@ -338,25 +318,11 @@ class MutexBase
|
|||
DISALLOW_COPY_AND_ASSIGN(MutexBase);
|
||||
|
||||
public:
|
||||
MutexBase()
|
||||
{
|
||||
InitializeCriticalSection(&mCS);
|
||||
}
|
||||
MutexBase() { InitializeCriticalSection(&mCS); }
|
||||
~MutexBase() { DeleteCriticalSection(&mCS); }
|
||||
|
||||
~MutexBase()
|
||||
{
|
||||
DeleteCriticalSection(&mCS);
|
||||
}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
EnterCriticalSection(&mCS);
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
LeaveCriticalSection(&mCS);
|
||||
}
|
||||
void Lock() { EnterCriticalSection(&mCS); }
|
||||
void Unlock() { LeaveCriticalSection(&mCS); }
|
||||
};
|
||||
|
||||
#else
|
||||
|
@ -371,20 +337,10 @@ class MutexBase
|
|||
DISALLOW_COPY_AND_ASSIGN(MutexBase);
|
||||
|
||||
public:
|
||||
MutexBase()
|
||||
{
|
||||
pthread_mutex_init(&mMutex, nullptr);
|
||||
}
|
||||
MutexBase() { pthread_mutex_init(&mMutex, nullptr); }
|
||||
|
||||
void Lock()
|
||||
{
|
||||
pthread_mutex_lock(&mMutex);
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
}
|
||||
void Lock() { pthread_mutex_lock(&mMutex); }
|
||||
void Unlock() { pthread_mutex_unlock(&mMutex); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -414,10 +370,7 @@ public:
|
|||
MutexBase::Unlock();
|
||||
}
|
||||
|
||||
bool IsLocked()
|
||||
{
|
||||
return mIsLocked;
|
||||
}
|
||||
bool IsLocked() { return mIsLocked; }
|
||||
};
|
||||
|
||||
// This lock must be held while manipulating global state, such as
|
||||
|
@ -429,14 +382,8 @@ class AutoLockState
|
|||
DISALLOW_COPY_AND_ASSIGN(AutoLockState);
|
||||
|
||||
public:
|
||||
AutoLockState()
|
||||
{
|
||||
gStateLock->Lock();
|
||||
}
|
||||
~AutoLockState()
|
||||
{
|
||||
gStateLock->Unlock();
|
||||
}
|
||||
AutoLockState() { gStateLock->Lock(); }
|
||||
~AutoLockState() { gStateLock->Unlock(); }
|
||||
};
|
||||
|
||||
class AutoUnlockState
|
||||
|
@ -444,14 +391,8 @@ class AutoUnlockState
|
|||
DISALLOW_COPY_AND_ASSIGN(AutoUnlockState);
|
||||
|
||||
public:
|
||||
AutoUnlockState()
|
||||
{
|
||||
gStateLock->Unlock();
|
||||
}
|
||||
~AutoUnlockState()
|
||||
{
|
||||
gStateLock->Lock();
|
||||
}
|
||||
AutoUnlockState() { gStateLock->Unlock(); }
|
||||
~AutoUnlockState() { gStateLock->Lock(); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -514,10 +455,7 @@ public:
|
|||
return mBlockIntercepts = false;
|
||||
}
|
||||
|
||||
bool InterceptsAreBlocked() const
|
||||
{
|
||||
return mBlockIntercepts;
|
||||
}
|
||||
bool InterceptsAreBlocked() const { return mBlockIntercepts; }
|
||||
};
|
||||
|
||||
/* static */ Thread*
|
||||
|
@ -563,10 +501,7 @@ public:
|
|||
class StringTable
|
||||
{
|
||||
public:
|
||||
StringTable()
|
||||
{
|
||||
(void)mSet.init(64);
|
||||
}
|
||||
StringTable() { (void)mSet.init(64); }
|
||||
|
||||
const char*
|
||||
Intern(const char* aString)
|
||||
|
@ -618,13 +553,11 @@ private:
|
|||
class StringAlloc
|
||||
{
|
||||
public:
|
||||
static char*
|
||||
copy(const char* aString)
|
||||
static char* copy(const char* aString)
|
||||
{
|
||||
return InfallibleAllocPolicy::strdup_(aString);
|
||||
}
|
||||
static void
|
||||
free(char* aString)
|
||||
static void free(char* aString)
|
||||
{
|
||||
InfallibleAllocPolicy::free_(aString);
|
||||
}
|
||||
|
@ -1278,8 +1211,7 @@ Options::Options(const char* aDMDEnvVar)
|
|||
: mDMDEnvVar(InfallibleAllocPolicy::strdup_(aDMDEnvVar)),
|
||||
mSampleBelowSize(4093, 100 * 100 * 1000),
|
||||
mMaxFrames(StackTrace::MaxFrames, StackTrace::MaxFrames),
|
||||
mShowDumpStats(false),
|
||||
mMode(Normal)
|
||||
mShowDumpStats(false)
|
||||
{
|
||||
char* e = mDMDEnvVar;
|
||||
if (strcmp(e, "1") != 0) {
|
||||
|
@ -1314,11 +1246,6 @@ Options::Options(const char* aDMDEnvVar)
|
|||
} else if (GetBool(arg, "--show-dump-stats", &myBool)) {
|
||||
mShowDumpStats = myBool;
|
||||
|
||||
} else if (strcmp(arg, "--mode=normal") == 0) {
|
||||
mMode = Options::Normal;
|
||||
} else if (strcmp(arg, "--mode=test") == 0) {
|
||||
mMode = Options::Test;
|
||||
|
||||
} else if (strcmp(arg, "") == 0) {
|
||||
// This can only happen if there is trailing whitespace. Ignore.
|
||||
MOZ_ASSERT(isEnd);
|
||||
|
@ -1354,7 +1281,6 @@ Options::BadArg(const char* aArg)
|
|||
int(mMaxFrames.mMax),
|
||||
int(mMaxFrames.mDefault));
|
||||
StatusMsg(" --show-dump-stats=<yes|no> Show stats about dumps? [no]\n");
|
||||
StatusMsg(" --mode=<normal|test> Mode of operation [normal]\n");
|
||||
StatusMsg("\n");
|
||||
exit(1);
|
||||
}
|
||||
|
@ -1371,21 +1297,6 @@ NopStackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp,
|
|||
}
|
||||
#endif
|
||||
|
||||
// Note that fopen() can allocate.
|
||||
static FILE*
|
||||
OpenOutputFile(const char* aFilename)
|
||||
{
|
||||
FILE* fp = fopen(aFilename, "w");
|
||||
if (!fp) {
|
||||
StatusMsg("can't create %s file: %s\n", aFilename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static void RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
|
||||
UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4);
|
||||
|
||||
// WARNING: this function runs *very* early -- before all static initializers
|
||||
// have run. For this reason, non-scalar globals such as gStateLock and
|
||||
// gStackTraceTable are allocated dynamically (so we can guarantee their
|
||||
|
@ -1439,31 +1350,7 @@ Init(const malloc_table_t* aMallocTable)
|
|||
gBlockTable->init(8192);
|
||||
}
|
||||
|
||||
if (gOptions->IsTestMode()) {
|
||||
// Do all necessary allocations before setting gIsDMDRunning so those
|
||||
// allocations don't show up in our results. Once gIsDMDRunning is set we
|
||||
// are intercepting malloc et al. in earnest.
|
||||
//
|
||||
// These files are written to $CWD. It would probably be better to write
|
||||
// them to "TmpD" using the directory service, but that would require
|
||||
// linking DMD with XPCOM.
|
||||
auto f1 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-empty.json"));
|
||||
auto f2 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-unsampled1.json"));
|
||||
auto f3 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-unsampled2.json"));
|
||||
auto f4 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-sampled.json"));
|
||||
gIsDMDRunning = true;
|
||||
|
||||
StatusMsg("running test mode...\n");
|
||||
RunTestMode(Move(f1), Move(f2), Move(f3), Move(f4));
|
||||
StatusMsg("finished test mode; DMD is now disabled again\n");
|
||||
|
||||
// Continue running so that the xpcshell test can complete, but DMD no
|
||||
// longer needs to be running.
|
||||
gIsDMDRunning = false;
|
||||
|
||||
} else {
|
||||
gIsDMDRunning = true;
|
||||
}
|
||||
gIsDMDRunning = true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1850,260 +1737,17 @@ AnalyzeReports(JSONWriter& aWriter)
|
|||
// Testing
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// This function checks that heap blocks that have the same stack trace but
|
||||
// different (or no) reporters get aggregated separately.
|
||||
void Foo(int aSeven)
|
||||
MOZ_EXPORT void
|
||||
SetSampleBelowSize(size_t aSize)
|
||||
{
|
||||
char* a[6];
|
||||
for (int i = 0; i < aSeven - 1; i++) {
|
||||
a[i] = (char*) replace_malloc(128 - 16*i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < aSeven - 5; i++) {
|
||||
Report(a[i]); // reported
|
||||
}
|
||||
Report(a[2]); // reported
|
||||
Report(a[3]); // reported
|
||||
// a[4], a[5] unreported
|
||||
gOptions->SetSampleBelowSize(aSize);
|
||||
}
|
||||
|
||||
// This stops otherwise-unused variables from being optimized away.
|
||||
static void
|
||||
UseItOrLoseIt(void* aPtr, int aSeven)
|
||||
MOZ_EXPORT void
|
||||
ClearBlocks()
|
||||
{
|
||||
char buf[64];
|
||||
int n = sprintf(buf, "%p\n", aPtr);
|
||||
if (n == 20 + aSeven) {
|
||||
fprintf(stderr, "well, that is surprising");
|
||||
}
|
||||
}
|
||||
|
||||
// The output from this function feeds into DMD's xpcshell test.
|
||||
static void
|
||||
RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
|
||||
UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4)
|
||||
{
|
||||
// This test relies on the compiler not doing various optimizations, such as
|
||||
// eliding unused replace_malloc() calls or unrolling loops with fixed
|
||||
// iteration counts. So we want a constant value that the compiler can't
|
||||
// determine statically, and we use that in various ways to prevent the above
|
||||
// optimizations from happening.
|
||||
//
|
||||
// This code always sets |seven| to the value 7. It works because we know
|
||||
// that "--mode=test" must be within the DMD environment variable if we reach
|
||||
// here, but the compiler almost certainly does not.
|
||||
//
|
||||
char* env = getenv("DMD");
|
||||
char* p1 = strstr(env, "--mode=t");
|
||||
char* p2 = strstr(p1, "test");
|
||||
int seven = p2 - p1;
|
||||
|
||||
// The first part of this test requires sampling to be disabled.
|
||||
gOptions->SetSampleBelowSize(1);
|
||||
|
||||
//---------
|
||||
|
||||
// AnalyzeReports 1. Zero for everything.
|
||||
JSONWriter writer1(Move(aF1));
|
||||
AnalyzeReports(writer1);
|
||||
|
||||
//---------
|
||||
|
||||
// AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
|
||||
// AnalyzeReports 3: still present and unreported.
|
||||
int i;
|
||||
char* a = nullptr;
|
||||
for (i = 0; i < seven + 3; i++) {
|
||||
a = (char*) replace_malloc(100);
|
||||
UseItOrLoseIt(a, seven);
|
||||
}
|
||||
replace_free(a);
|
||||
|
||||
// Note: 8 bytes is the smallest requested size that gives consistent
|
||||
// behaviour across all platforms with jemalloc.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: thrice-reported.
|
||||
char* a2 = (char*) replace_malloc(8);
|
||||
Report(a2);
|
||||
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
|
||||
char* b = (char*) replace_malloc(10);
|
||||
ReportOnAlloc(b);
|
||||
|
||||
// ReportOnAlloc, then freed.
|
||||
// AnalyzeReports 2: freed, irrelevant.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* b2 = (char*) replace_malloc(1);
|
||||
ReportOnAlloc(b2);
|
||||
replace_free(b2);
|
||||
|
||||
// AnalyzeReports 2: reported 4 times.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* c = (char*) replace_calloc(10, 3);
|
||||
Report(c);
|
||||
for (int i = 0; i < seven - 4; i++) {
|
||||
Report(c);
|
||||
}
|
||||
|
||||
// AnalyzeReports 2: ignored.
|
||||
// AnalyzeReports 3: irrelevant.
|
||||
Report((void*)(intptr_t)i);
|
||||
|
||||
// jemalloc rounds this up to 8192.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: freed.
|
||||
char* e = (char*) replace_malloc(4096);
|
||||
e = (char*) replace_realloc(e, 4097);
|
||||
Report(e);
|
||||
|
||||
// First realloc is like malloc; second realloc is shrinking.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: re-reported.
|
||||
char* e2 = (char*) replace_realloc(nullptr, 1024);
|
||||
e2 = (char*) replace_realloc(e2, 512);
|
||||
Report(e2);
|
||||
|
||||
// First realloc is like malloc; second realloc creates a min-sized block.
|
||||
// XXX: on Windows, second realloc frees the block.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* e3 = (char*) replace_realloc(nullptr, 1023);
|
||||
//e3 = (char*) replace_realloc(e3, 0);
|
||||
MOZ_ASSERT(e3);
|
||||
Report(e3);
|
||||
|
||||
// AnalyzeReports 2: freed, irrelevant.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* f = (char*) replace_malloc(64);
|
||||
replace_free(f);
|
||||
|
||||
// AnalyzeReports 2: ignored.
|
||||
// AnalyzeReports 3: irrelevant.
|
||||
Report((void*)(intptr_t)0x0);
|
||||
|
||||
// AnalyzeReports 2: mixture of reported and unreported.
|
||||
// AnalyzeReports 3: all unreported.
|
||||
Foo(seven);
|
||||
Foo(seven);
|
||||
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: twice-reported.
|
||||
char* g1 = (char*) replace_malloc(77);
|
||||
ReportOnAlloc(g1);
|
||||
ReportOnAlloc(g1);
|
||||
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: once-reported.
|
||||
char* g2 = (char*) replace_malloc(78);
|
||||
Report(g2);
|
||||
ReportOnAlloc(g2);
|
||||
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: once-reported.
|
||||
char* g3 = (char*) replace_malloc(79);
|
||||
ReportOnAlloc(g3);
|
||||
Report(g3);
|
||||
|
||||
// All the odd-ball ones.
|
||||
// AnalyzeReports 2: all unreported.
|
||||
// AnalyzeReports 3: all freed, irrelevant.
|
||||
// XXX: no memalign on Mac
|
||||
//void* x = memalign(64, 65); // rounds up to 128
|
||||
//UseItOrLoseIt(x, seven);
|
||||
// XXX: posix_memalign doesn't work on B2G
|
||||
//void* y;
|
||||
//posix_memalign(&y, 128, 129); // rounds up to 256
|
||||
//UseItOrLoseIt(y, seven);
|
||||
// XXX: valloc doesn't work on Windows.
|
||||
//void* z = valloc(1); // rounds up to 4096
|
||||
//UseItOrLoseIt(z, seven);
|
||||
//aligned_alloc(64, 256); // XXX: C11 only
|
||||
|
||||
// AnalyzeReports 2.
|
||||
JSONWriter writer2(Move(aF2));
|
||||
AnalyzeReports(writer2);
|
||||
|
||||
//---------
|
||||
|
||||
Report(a2);
|
||||
Report(a2);
|
||||
replace_free(c);
|
||||
replace_free(e);
|
||||
Report(e2);
|
||||
replace_free(e3);
|
||||
//replace_free(x);
|
||||
//replace_free(y);
|
||||
//replace_free(z);
|
||||
|
||||
// AnalyzeReports 3.
|
||||
JSONWriter writer3(Move(aF3));
|
||||
AnalyzeReports(writer3);
|
||||
|
||||
//---------
|
||||
|
||||
// Clear all knowledge of existing blocks to give us a clean slate.
|
||||
gBlockTable->clear();
|
||||
|
||||
gOptions->SetSampleBelowSize(128);
|
||||
|
||||
char* s;
|
||||
|
||||
// This equals the sample size, and so is reported exactly. It should be
|
||||
// listed before records of the same size that are sampled.
|
||||
s = (char*) replace_malloc(128);
|
||||
UseItOrLoseIt(s, seven);
|
||||
|
||||
// This exceeds the sample size, and so is reported exactly.
|
||||
s = (char*) replace_malloc(144);
|
||||
UseItOrLoseIt(s, seven);
|
||||
|
||||
// These together constitute exactly one sample.
|
||||
for (int i = 0; i < seven + 9; i++) {
|
||||
s = (char*) replace_malloc(8);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
MOZ_ASSERT(gSmallBlockActualSizeCounter == 0);
|
||||
|
||||
// These fall 8 bytes short of a full sample.
|
||||
for (int i = 0; i < seven + 8; i++) {
|
||||
s = (char*) replace_malloc(8);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
|
||||
|
||||
// This exceeds the sample size, and so is recorded exactly.
|
||||
s = (char*) replace_malloc(256);
|
||||
UseItOrLoseIt(s, seven);
|
||||
MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
|
||||
|
||||
// This gets more than to a full sample from the |i < 15| loop above.
|
||||
s = (char*) replace_malloc(96);
|
||||
UseItOrLoseIt(s, seven);
|
||||
MOZ_ASSERT(gSmallBlockActualSizeCounter == 88);
|
||||
|
||||
// This gets to another full sample.
|
||||
for (int i = 0; i < seven - 2; i++) {
|
||||
s = (char*) replace_malloc(8);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
MOZ_ASSERT(gSmallBlockActualSizeCounter == 0);
|
||||
|
||||
// This allocates 16, 32, ..., 128 bytes, which results in a heap block
|
||||
// record that contains a mix of sample and non-sampled blocks, and so should
|
||||
// be printed with '~' signs.
|
||||
for (int i = 1; i <= seven + 1; i++) {
|
||||
s = (char*) replace_malloc(i * 16);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
MOZ_ASSERT(gSmallBlockActualSizeCounter == 64);
|
||||
|
||||
// At the end we're 64 bytes into the current sample so we report ~1,424
|
||||
// bytes of allocation overall, which is 64 less than the real value 1,488.
|
||||
|
||||
// AnalyzeReports 4.
|
||||
JSONWriter writer4(Move(aF4));
|
||||
AnalyzeReports(writer4);
|
||||
gSmallBlockActualSizeCounter = 0;
|
||||
}
|
||||
|
||||
} // namespace dmd
|
||||
|
|
|
@ -147,6 +147,14 @@ StatusMsg(const char* aFmt, ...);
|
|||
MOZ_EXPORT bool
|
||||
IsRunning();
|
||||
|
||||
// Sets the sample-below size. Only used for testing purposes.
|
||||
MOZ_EXPORT void
|
||||
SetSampleBelowSize(size_t aSize);
|
||||
|
||||
// Clears all records of live allocations. Only used for testing purposes.
|
||||
MOZ_EXPORT void
|
||||
ClearBlocks();
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace dmd
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#! /usr/bin/python
|
||||
#! /usr/bin/env python
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -58,14 +58,73 @@ allocatorFns = [
|
|||
]
|
||||
|
||||
class Record(object):
|
||||
'''A record is an aggregation of heap blocks that have identical stack
|
||||
traces. It can also be used to represent the difference between two
|
||||
records.'''
|
||||
|
||||
def __init__(self):
|
||||
self.numBlocks = 0
|
||||
self.reqSize = 0
|
||||
self.slopSize = 0
|
||||
self.usableSize = 0
|
||||
self.isSampled = False
|
||||
self.allocatedAtDesc = None
|
||||
self.reportedAtDescs = []
|
||||
self.usableSizes = collections.defaultdict(int)
|
||||
|
||||
def isZero(self, args):
|
||||
return self.numBlocks == 0 and \
|
||||
self.reqSize == 0 and \
|
||||
self.slopSize == 0 and \
|
||||
self.usableSize == 0 and \
|
||||
len(self.usableSizes) == 0
|
||||
|
||||
def negate(self):
|
||||
self.numBlocks = -self.numBlocks
|
||||
self.reqSize = -self.reqSize
|
||||
self.slopSize = -self.slopSize
|
||||
self.usableSize = -self.usableSize
|
||||
|
||||
negatedUsableSizes = collections.defaultdict(int)
|
||||
for (usableSize, isSampled), count in self.usableSizes.items():
|
||||
negatedUsableSizes[(-usableSize, isSampled)] = count
|
||||
self.usableSizes = negatedUsableSizes
|
||||
|
||||
def subtract(self, r):
|
||||
# We should only be calling this on records with matching stack traces.
|
||||
# Check this.
|
||||
assert self.allocatedAtDesc == r.allocatedAtDesc
|
||||
assert self.reportedAtDescs == r.reportedAtDescs
|
||||
|
||||
self.numBlocks -= r.numBlocks
|
||||
self.reqSize -= r.reqSize
|
||||
self.slopSize -= r.slopSize
|
||||
self.usableSize -= r.usableSize
|
||||
self.isSampled = self.isSampled or r.isSampled
|
||||
|
||||
usableSizes1 = self.usableSizes
|
||||
usableSizes2 = r.usableSizes
|
||||
usableSizes3 = collections.defaultdict(int)
|
||||
for usableSize, isSampled in usableSizes1:
|
||||
counts1 = usableSizes1[usableSize, isSampled]
|
||||
if (usableSize, isSampled) in usableSizes2:
|
||||
counts2 = usableSizes2[usableSize, isSampled]
|
||||
del usableSizes2[usableSize, isSampled]
|
||||
counts3 = counts1 - counts2
|
||||
if counts3 != 0:
|
||||
if counts3 < 0:
|
||||
usableSize = -usableSize
|
||||
counts3 = -counts3
|
||||
usableSizes3[usableSize, isSampled] = counts3
|
||||
else:
|
||||
usableSizes3[usableSize, isSampled] = counts1
|
||||
|
||||
for usableSize, isSampled in usableSizes2:
|
||||
usableSizes3[-usableSize, isSampled] = \
|
||||
usableSizes2[usableSize, isSampled]
|
||||
|
||||
self.usableSizes = usableSizes3
|
||||
|
||||
@staticmethod
|
||||
def cmpByIsSampled(r1, r2):
|
||||
# Treat sampled as smaller than non-sampled.
|
||||
|
@ -74,17 +133,20 @@ class Record(object):
|
|||
@staticmethod
|
||||
def cmpByUsableSize(r1, r2):
|
||||
# Sort by usable size, then req size, then by isSampled.
|
||||
return cmp(r1.usableSize, r2.usableSize) or Record.cmpByReqSize(r1, r2)
|
||||
return cmp(abs(r1.usableSize), abs(r2.usableSize)) or \
|
||||
Record.cmpByReqSize(r1, r2)
|
||||
|
||||
@staticmethod
|
||||
def cmpByReqSize(r1, r2):
|
||||
# Sort by req size, then by isSampled.
|
||||
return cmp(r1.reqSize, r2.reqSize) or Record.cmpByIsSampled(r1, r2)
|
||||
return cmp(abs(r1.reqSize), abs(r2.reqSize)) or \
|
||||
Record.cmpByIsSampled(r1, r2)
|
||||
|
||||
@staticmethod
|
||||
def cmpBySlopSize(r1, r2):
|
||||
# Sort by slop size, then by isSampled.
|
||||
return cmp(r1.slopSize, r2.slopSize) or Record.cmpByIsSampled(r1, r2)
|
||||
return cmp(abs(r1.slopSize), abs(r2.slopSize)) or \
|
||||
Record.cmpByIsSampled(r1, r2)
|
||||
|
||||
|
||||
sortByChoices = {
|
||||
|
@ -105,7 +167,9 @@ def parseCommandLine():
|
|||
|
||||
description = '''
|
||||
Analyze heap data produced by DMD.
|
||||
If no files are specified, read from stdin; input can be gzipped.
|
||||
If one file is specified, analyze it; if two files are specified, analyze the
|
||||
difference.
|
||||
Input files can be gzipped.
|
||||
Write to stdout unless -o/--output is specified.
|
||||
Stack traces are fixed to show function names, filenames and line numbers
|
||||
unless --no-fix-stacks is specified; stack fixing modifies the original file
|
||||
|
@ -131,16 +195,17 @@ variable is used to find breakpad symbols for stack fixing.
|
|||
p.add_argument('-a', '--ignore-alloc-fns', action='store_true',
|
||||
help='ignore allocation functions at the start of traces')
|
||||
|
||||
p.add_argument('-b', '--show-all-block-sizes', action='store_true',
|
||||
help='show individual block sizes for each record')
|
||||
|
||||
p.add_argument('--no-fix-stacks', action='store_true',
|
||||
help='do not fix stacks')
|
||||
|
||||
p.add_argument('--filter-stacks-for-testing', action='store_true',
|
||||
help='filter stack traces; only useful for testing purposes')
|
||||
|
||||
p.add_argument('input_file')
|
||||
p.add_argument('input_file',
|
||||
help='a file produced by DMD')
|
||||
|
||||
p.add_argument('input_file2', nargs='?',
|
||||
help='a file produced by DMD; if present, it is diff\'d with input_file')
|
||||
|
||||
return p.parse_args(sys.argv[1:])
|
||||
|
||||
|
@ -195,18 +260,16 @@ def fixStackTraces(inputFilename, isZipped, opener):
|
|||
shutil.move(tmpFilename, inputFilename)
|
||||
|
||||
|
||||
def main():
|
||||
args = parseCommandLine()
|
||||
|
||||
def getDigestFromFile(args, inputFile):
|
||||
# Handle gzipped input if necessary.
|
||||
isZipped = args.input_file.endswith('.gz')
|
||||
isZipped = inputFile.endswith('.gz')
|
||||
opener = gzip.open if isZipped else open
|
||||
|
||||
# Fix stack traces unless otherwise instructed.
|
||||
if not args.no_fix_stacks:
|
||||
fixStackTraces(args.input_file, isZipped, opener)
|
||||
fixStackTraces(inputFile, isZipped, opener)
|
||||
|
||||
with opener(args.input_file, 'rb') as f:
|
||||
with opener(inputFile, 'rb') as f:
|
||||
j = json.load(f)
|
||||
|
||||
if j['version'] != outputVersion:
|
||||
|
@ -245,6 +308,31 @@ def main():
|
|||
if len(frameKeys) > args.max_frames:
|
||||
traceTable[traceKey] = frameKeys[:args.max_frames]
|
||||
|
||||
def buildTraceDescription(traceTable, frameTable, traceKey):
|
||||
frameKeys = traceTable[traceKey]
|
||||
fmt = ' #{:02d}{:}'
|
||||
|
||||
if args.filter_stacks_for_testing:
|
||||
# When running SmokeDMD.cpp, every stack trace should contain at
|
||||
# least one frame that contains 'DMD.cpp', from either |DMD.cpp| or
|
||||
# |SmokeDMD.cpp|. (Or 'dmd.cpp' on Windows.) If we see such a
|
||||
# frame, we replace the entire stack trace with a single,
|
||||
# predictable frame. There is too much variation in the stack
|
||||
# traces across different machines and platforms to do more precise
|
||||
# matching, but this level of matching will result in failure if
|
||||
# stack fixing fails completely.
|
||||
for frameKey in frameKeys:
|
||||
frameDesc = frameTable[frameKey]
|
||||
if 'DMD.cpp' in frameDesc or 'dmd.cpp' in frameDesc:
|
||||
return [fmt.format(1, ': ... DMD.cpp ...')]
|
||||
|
||||
# The frame number is always '#00' (see DMD.h for why), so we have to
|
||||
# replace that with the correct frame number.
|
||||
desc = []
|
||||
for n, frameKey in enumerate(traceTable[traceKey], start=1):
|
||||
desc.append(fmt.format(n, frameTable[frameKey][3:]))
|
||||
return desc
|
||||
|
||||
# Aggregate blocks into records. All sufficiently similar blocks go into a
|
||||
# single record.
|
||||
|
||||
|
@ -264,24 +352,33 @@ def main():
|
|||
# derived from the block's 'alloc' and 'reps' (if present) stack
|
||||
# traces.
|
||||
#
|
||||
# Each stack trace has a key in the JSON file. But we don't use that
|
||||
# key to construct |recordKey|; instead we use the frame keys.
|
||||
# This is because the stack trimming done for --max-frames can cause
|
||||
# stack traces with distinct trace keys to end up with the same frame
|
||||
# keys, and these should be considered equivalent. E.g. if we have
|
||||
# distinct traces T1:[A,B,C] and T2:[A,B,D] and we trim the final frame
|
||||
# of each they should be considered equivalent.
|
||||
allocatedAt = block['alloc']
|
||||
# We use frame descriptions (e.g. "#00: foo (X.cpp:99)") when comparing
|
||||
# traces for equality. We can't use trace keys or frame keys because
|
||||
# they're not comparable across different DMD runs (which is relevant
|
||||
# when doing diffs).
|
||||
#
|
||||
# Using frame descriptions also fits in with the stack trimming done
|
||||
# for --max-frames, which requires that stack traces with common
|
||||
# beginnings but different endings to be considered equivalent. E.g. if
|
||||
# we have distinct traces T1:[A:D1,B:D2,C:D3] and T2:[X:D1,Y:D2,Z:D4]
|
||||
# and we trim the final frame of each they should be considered
|
||||
# equivalent because the untrimmed frame descriptions (D1 and D2)
|
||||
# match.
|
||||
def makeRecordKeyPart(traceKey):
|
||||
return str(map(lambda frameKey: frameTable[frameKey],
|
||||
traceTable[traceKey]))
|
||||
|
||||
allocatedAtTraceKey = block['alloc']
|
||||
if args.ignore_reports:
|
||||
recordKey = str(traceTable[allocatedAt])
|
||||
recordKey = makeRecordKeyPart(allocatedAtTraceKey)
|
||||
records = liveRecords
|
||||
else:
|
||||
recordKey = str(traceTable[allocatedAt])
|
||||
recordKey = makeRecordKeyPart(allocatedAtTraceKey)
|
||||
if 'reps' in block:
|
||||
reportedAts = block['reps']
|
||||
for reportedAt in reportedAts:
|
||||
recordKey += str(traceTable[reportedAt])
|
||||
if len(reportedAts) == 1:
|
||||
reportedAtTraceKeys = block['reps']
|
||||
for reportedAtTraceKey in reportedAtTraceKeys:
|
||||
recordKey += makeRecordKeyPart(reportedAtTraceKey)
|
||||
if len(reportedAtTraceKeys) == 1:
|
||||
records = onceReportedRecords
|
||||
else:
|
||||
records = twiceReportedRecords
|
||||
|
@ -312,15 +409,92 @@ def main():
|
|||
record.slopSize += slopSize
|
||||
record.usableSize += usableSize
|
||||
record.isSampled = record.isSampled or isSampled
|
||||
record.allocatedAt = block['alloc']
|
||||
if record.allocatedAtDesc == None:
|
||||
record.allocatedAtDesc = \
|
||||
buildTraceDescription(traceTable, frameTable,
|
||||
allocatedAtTraceKey)
|
||||
|
||||
if args.ignore_reports:
|
||||
pass
|
||||
else:
|
||||
if 'reps' in block:
|
||||
record.reportedAts = block['reps']
|
||||
if 'reps' in block and record.reportedAtDescs == []:
|
||||
f = lambda k: buildTraceDescription(traceTable, frameTable, k)
|
||||
record.reportedAtDescs = map(f, reportedAtTraceKeys)
|
||||
record.usableSizes[(usableSize, isSampled)] += 1
|
||||
|
||||
# Print records.
|
||||
# All the processed data for a single DMD file is called a "digest".
|
||||
digest = {}
|
||||
digest['dmdEnvVar'] = dmdEnvVar
|
||||
digest['sampleBelowSize'] = sampleBelowSize
|
||||
digest['heapUsableSize'] = heapUsableSize
|
||||
digest['heapBlocks'] = heapBlocks
|
||||
digest['heapIsSampled'] = heapIsSampled
|
||||
if args.ignore_reports:
|
||||
digest['liveRecords'] = liveRecords
|
||||
else:
|
||||
digest['unreportedRecords'] = unreportedRecords
|
||||
digest['onceReportedRecords'] = onceReportedRecords
|
||||
digest['twiceReportedRecords'] = twiceReportedRecords
|
||||
return digest
|
||||
|
||||
|
||||
def diffRecords(args, records1, records2):
|
||||
records3 = {}
|
||||
|
||||
# Process records1.
|
||||
for k in records1:
|
||||
r1 = records1[k]
|
||||
if k in records2:
|
||||
# This record is present in both records1 and records2.
|
||||
r2 = records2[k]
|
||||
del records2[k]
|
||||
r2.subtract(r1)
|
||||
if not r2.isZero(args):
|
||||
records3[k] = r2
|
||||
else:
|
||||
# This record is present only in records1.
|
||||
r1.negate()
|
||||
records3[k] = r1
|
||||
|
||||
for k in records2:
|
||||
# This record is present only in records2.
|
||||
records3[k] = records2[k]
|
||||
|
||||
return records3
|
||||
|
||||
|
||||
def diffDigests(args, d1, d2):
|
||||
d3 = {}
|
||||
d3['dmdEnvVar'] = (d1['dmdEnvVar'], d2['dmdEnvVar'])
|
||||
d3['sampleBelowSize'] = (d1['sampleBelowSize'], d2['sampleBelowSize'])
|
||||
d3['heapUsableSize'] = d2['heapUsableSize'] - d1['heapUsableSize']
|
||||
d3['heapBlocks'] = d2['heapBlocks'] - d1['heapBlocks']
|
||||
d3['heapIsSampled'] = d2['heapIsSampled'] or d1['heapIsSampled']
|
||||
if args.ignore_reports:
|
||||
d3['liveRecords'] = diffRecords(args, d1['liveRecords'],
|
||||
d2['liveRecords'])
|
||||
else:
|
||||
d3['unreportedRecords'] = diffRecords(args, d1['unreportedRecords'],
|
||||
d2['unreportedRecords'])
|
||||
d3['onceReportedRecords'] = diffRecords(args, d1['onceReportedRecords'],
|
||||
d2['onceReportedRecords'])
|
||||
d3['twiceReportedRecords'] = diffRecords(args, d1['twiceReportedRecords'],
|
||||
d2['twiceReportedRecords'])
|
||||
return d3
|
||||
|
||||
|
||||
def printDigest(args, digest):
|
||||
dmdEnvVar = digest['dmdEnvVar']
|
||||
sampleBelowSize = digest['sampleBelowSize']
|
||||
heapUsableSize = digest['heapUsableSize']
|
||||
heapIsSampled = digest['heapIsSampled']
|
||||
heapBlocks = digest['heapBlocks']
|
||||
if args.ignore_reports:
|
||||
liveRecords = digest['liveRecords']
|
||||
else:
|
||||
unreportedRecords = digest['unreportedRecords']
|
||||
onceReportedRecords = digest['onceReportedRecords']
|
||||
twiceReportedRecords = digest['twiceReportedRecords']
|
||||
|
||||
separator = '#' + '-' * 65 + '\n'
|
||||
|
||||
|
@ -339,27 +513,9 @@ def main():
|
|||
def out(*arguments, **kwargs):
|
||||
print(*arguments, file=args.output, **kwargs)
|
||||
|
||||
def printStack(traceTable, frameTable, traceKey):
|
||||
frameKeys = traceTable[traceKey]
|
||||
fmt = ' #{:02d}{:}'
|
||||
|
||||
if args.filter_stacks_for_testing:
|
||||
# If any frame has "DMD.cpp" or "replace_malloc.c" in its
|
||||
# description -- as should be the case for every stack trace when
|
||||
# running DMD in test mode -- we replace the entire trace with a
|
||||
# single, predictable frame. There is too much variation in the
|
||||
# stack traces across different machines and platforms to do more
|
||||
# specific matching.
|
||||
for frameKey in frameKeys:
|
||||
frameDesc = frameTable[frameKey]
|
||||
if 'DMD.cpp' in frameDesc or 'replace_malloc.c' in frameDesc:
|
||||
out(fmt.format(1, ': ... DMD.cpp ...'))
|
||||
return
|
||||
|
||||
# The frame number is always '#00' (see DMD.h for why), so we have to
|
||||
# replace that with the correct frame number.
|
||||
for n, frameKey in enumerate(traceTable[traceKey], start=1):
|
||||
out(fmt.format(n, frameTable[frameKey][3:]))
|
||||
def printStack(traceDesc):
|
||||
for frameDesc in traceDesc:
|
||||
out(frameDesc)
|
||||
|
||||
def printRecords(recordKind, records, heapUsableSize):
|
||||
RecordKind = recordKind.capitalize()
|
||||
|
@ -400,6 +556,30 @@ def main():
|
|||
format(number(record.usableSize, isSampled),
|
||||
number(record.reqSize, isSampled),
|
||||
number(record.slopSize, isSampled)))
|
||||
|
||||
abscmp = lambda ((usableSize1, _1a), _1b), \
|
||||
((usableSize2, _2a), _2b): \
|
||||
cmp(abs(usableSize1), abs(usableSize2))
|
||||
usableSizes = sorted(record.usableSizes.items(), cmp=abscmp,
|
||||
reverse=True)
|
||||
|
||||
hasSingleBlock = len(usableSizes) == 1 and usableSizes[0][1] == 1
|
||||
|
||||
if not hasSingleBlock:
|
||||
out(' Individual block sizes: ', end='')
|
||||
if len(usableSizes) == 0:
|
||||
out('(no change)', end='')
|
||||
else:
|
||||
isFirst = True
|
||||
for (usableSize, isSampled), count in usableSizes:
|
||||
if not isFirst:
|
||||
out('; ', end='')
|
||||
out('{:}'.format(number(usableSize, isSampled)), end='')
|
||||
if count > 1:
|
||||
out(' x {:,d}'.format(count), end='')
|
||||
isFirst = False
|
||||
out()
|
||||
|
||||
out(' {:4.2f}% of the heap ({:4.2f}% cumulative)'.
|
||||
format(perc(record.usableSize, heapUsableSize),
|
||||
perc(kindCumulativeUsableSize, heapUsableSize)))
|
||||
|
@ -410,44 +590,39 @@ def main():
|
|||
format(perc(record.usableSize, kindUsableSize),
|
||||
recordKind,
|
||||
perc(kindCumulativeUsableSize, kindUsableSize)))
|
||||
|
||||
if args.show_all_block_sizes:
|
||||
usableSizes = sorted(record.usableSizes.items(), reverse=True)
|
||||
|
||||
out(' Individual block sizes: ', end='')
|
||||
isFirst = True
|
||||
for (usableSize, isSampled), count in usableSizes:
|
||||
if not isFirst:
|
||||
out('; ', end='')
|
||||
out('{:}'.format(number(usableSize, isSampled)), end='')
|
||||
if count > 1:
|
||||
out(' x {:,d}'.format(count), end='')
|
||||
isFirst = False
|
||||
out()
|
||||
|
||||
out(' Allocated at {')
|
||||
printStack(traceTable, frameTable, record.allocatedAt)
|
||||
printStack(record.allocatedAtDesc)
|
||||
out(' }')
|
||||
if args.ignore_reports:
|
||||
pass
|
||||
else:
|
||||
if hasattr(record, 'reportedAts'):
|
||||
for n, reportedAt in enumerate(record.reportedAts):
|
||||
again = 'again ' if n > 0 else ''
|
||||
out(' Reported {:}at {{'.format(again))
|
||||
printStack(traceTable, frameTable, reportedAt)
|
||||
out(' }')
|
||||
for n, reportedAtDesc in enumerate(record.reportedAtDescs):
|
||||
again = 'again ' if n > 0 else ''
|
||||
out(' Reported {:}at {{'.format(again))
|
||||
printStack(reportedAtDesc)
|
||||
out(' }')
|
||||
out('}\n')
|
||||
|
||||
return (kindUsableSize, kindBlocks)
|
||||
|
||||
|
||||
# Print header.
|
||||
out(separator)
|
||||
out('Invocation {')
|
||||
out(' $DMD = \'' + dmdEnvVar + '\'')
|
||||
out(' Sample-below size = ' + str(sampleBelowSize))
|
||||
out('}\n')
|
||||
def printInvocation(n, dmdEnvVar, sampleBelowSize):
|
||||
out('Invocation{:} {{'.format(n))
|
||||
out(' $DMD = \'' + dmdEnvVar + '\'')
|
||||
out(' Sample-below size = ' + str(sampleBelowSize))
|
||||
out('}\n')
|
||||
|
||||
# Print command line. Strip dirs so the output is deterministic, which is
|
||||
# needed for testing.
|
||||
out(separator, end='')
|
||||
out('# ' + ' '.join(map(os.path.basename, sys.argv)) + '\n')
|
||||
|
||||
# Print invocation(s).
|
||||
if type(dmdEnvVar) is not tuple:
|
||||
printInvocation('', dmdEnvVar, sampleBelowSize)
|
||||
else:
|
||||
printInvocation(' 1', dmdEnvVar[0], sampleBelowSize[0])
|
||||
printInvocation(' 2', dmdEnvVar[1], sampleBelowSize[1])
|
||||
|
||||
# Print records.
|
||||
if args.ignore_reports:
|
||||
|
@ -458,10 +633,10 @@ def main():
|
|||
printRecords('twice-reported', twiceReportedRecords, heapUsableSize)
|
||||
|
||||
unreportedUsableSize, unreportedBlocks = \
|
||||
printRecords('unreported', unreportedRecords, heapUsableSize)
|
||||
printRecords('unreported', unreportedRecords, heapUsableSize)
|
||||
|
||||
onceReportedUsableSize, onceReportedBlocks = \
|
||||
printRecords('once-reported', onceReportedRecords, heapUsableSize)
|
||||
printRecords('once-reported', onceReportedRecords, heapUsableSize)
|
||||
|
||||
# Print summary.
|
||||
out(separator)
|
||||
|
@ -499,5 +674,15 @@ def main():
|
|||
out('}\n')
|
||||
|
||||
|
||||
def main():
|
||||
args = parseCommandLine()
|
||||
digest = getDigestFromFile(args, args.input_file)
|
||||
if args.input_file2:
|
||||
digest2 = getDigestFromFile(args, args.input_file2)
|
||||
digest = diffDigests(args, digest, digest2)
|
||||
printDigest(args, digest)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
|
|
@ -33,7 +33,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
'dbghelp',
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'test/xpcshell.ini',
|
||||
]
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This program is used by the DMD xpcshell test. It is run under DMD and
|
||||
// produces some output. The xpcshell test then post-processes and checks this
|
||||
// output.
|
||||
//
|
||||
// Note that this file does not have "Test" or "test" in its name, because that
|
||||
// will cause the build system to not record breakpad symbols for it, which
|
||||
// will stop the post-processing (which includes stack fixing) from working
|
||||
// correctly.
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/JSONWriter.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "DMD.h"
|
||||
|
||||
using mozilla::JSONWriter;
|
||||
using mozilla::MakeUnique;
|
||||
using namespace mozilla::dmd;
|
||||
|
||||
class FpWriteFunc : public mozilla::JSONWriteFunc
|
||||
{
|
||||
public:
|
||||
explicit FpWriteFunc(const char* aFilename)
|
||||
{
|
||||
mFp = fopen(aFilename, "w");
|
||||
if (!mFp) {
|
||||
fprintf(stderr, "SmokeDMD: can't create %s file: %s\n",
|
||||
aFilename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
~FpWriteFunc() { fclose(mFp); }
|
||||
|
||||
void Write(const char* aStr) { fputs(aStr, mFp); }
|
||||
|
||||
private:
|
||||
FILE* mFp;
|
||||
};
|
||||
|
||||
// This stops otherwise-unused variables from being optimized away.
|
||||
static void
|
||||
UseItOrLoseIt(void* aPtr, int aSeven)
|
||||
{
|
||||
char buf[64];
|
||||
int n = sprintf(buf, "%p\n", aPtr);
|
||||
if (n == 20 + aSeven) {
|
||||
fprintf(stderr, "well, that is surprising");
|
||||
}
|
||||
}
|
||||
|
||||
// This function checks that heap blocks that have the same stack trace but
|
||||
// different (or no) reporters get aggregated separately.
|
||||
void Foo(int aSeven)
|
||||
{
|
||||
char* a[6];
|
||||
for (int i = 0; i < aSeven - 1; i++) {
|
||||
a[i] = (char*) malloc(128 - 16*i);
|
||||
}
|
||||
|
||||
// Oddly, some versions of clang will cause identical stack traces to be
|
||||
// generated for adjacent calls to Report(), which breaks the test. Inserting
|
||||
// the UseItOrLoseIt() calls in between is enough to prevent this.
|
||||
|
||||
Report(a[2]); // reported
|
||||
|
||||
UseItOrLoseIt(a[2], aSeven);
|
||||
|
||||
for (int i = 0; i < aSeven - 5; i++) {
|
||||
Report(a[i]); // reported
|
||||
}
|
||||
|
||||
UseItOrLoseIt(a[2], aSeven);
|
||||
|
||||
Report(a[3]); // reported
|
||||
|
||||
// a[4], a[5] unreported
|
||||
}
|
||||
|
||||
void
|
||||
RunTests()
|
||||
{
|
||||
// These files are written to $CWD.
|
||||
auto f1 = MakeUnique<FpWriteFunc>("full-empty.json");
|
||||
auto f2 = MakeUnique<FpWriteFunc>("full-unsampled1.json");
|
||||
auto f3 = MakeUnique<FpWriteFunc>("full-unsampled2.json");
|
||||
auto f4 = MakeUnique<FpWriteFunc>("full-sampled.json");
|
||||
|
||||
// This test relies on the compiler not doing various optimizations, such as
|
||||
// eliding unused malloc() calls or unrolling loops with fixed iteration
|
||||
// counts. So we compile it with -O0 (or equivalent), which probably prevents
|
||||
// that. We also use the following variable for various loop iteration
|
||||
// counts, just in case compilers might unroll very small loops even with
|
||||
// -O0.
|
||||
int seven = 7;
|
||||
|
||||
// Make sure that DMD is actually running; it is initialized on the first
|
||||
// allocation.
|
||||
int *x = (int*)malloc(100);
|
||||
UseItOrLoseIt(x, seven);
|
||||
MOZ_RELEASE_ASSERT(IsRunning());
|
||||
|
||||
// The first part of this test requires sampling to be disabled.
|
||||
SetSampleBelowSize(1);
|
||||
|
||||
// The file manipulations above may have done some heap allocations.
|
||||
// Clear all knowledge of existing blocks to give us a clean slate.
|
||||
ClearBlocks();
|
||||
|
||||
//---------
|
||||
|
||||
// AnalyzeReports 1. Zero for everything.
|
||||
JSONWriter writer1(Move(f1));
|
||||
AnalyzeReports(writer1);
|
||||
|
||||
//---------
|
||||
|
||||
// AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
|
||||
// AnalyzeReports 3: still present and unreported.
|
||||
int i;
|
||||
char* a = nullptr;
|
||||
for (i = 0; i < seven + 3; i++) {
|
||||
a = (char*) malloc(100);
|
||||
UseItOrLoseIt(a, seven);
|
||||
}
|
||||
free(a);
|
||||
|
||||
// Note: 8 bytes is the smallest requested size that gives consistent
|
||||
// behaviour across all platforms with jemalloc.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: thrice-reported.
|
||||
char* a2 = (char*) malloc(8);
|
||||
Report(a2);
|
||||
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
|
||||
char* b = (char*) malloc(10);
|
||||
ReportOnAlloc(b);
|
||||
|
||||
// ReportOnAlloc, then freed.
|
||||
// AnalyzeReports 2: freed, irrelevant.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* b2 = (char*) malloc(1);
|
||||
ReportOnAlloc(b2);
|
||||
free(b2);
|
||||
|
||||
// AnalyzeReports 2: reported 4 times.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* c = (char*) calloc(10, 3);
|
||||
Report(c);
|
||||
for (int i = 0; i < seven - 4; i++) {
|
||||
Report(c);
|
||||
}
|
||||
|
||||
// AnalyzeReports 2: ignored.
|
||||
// AnalyzeReports 3: irrelevant.
|
||||
Report((void*)(intptr_t)i);
|
||||
|
||||
// jemalloc rounds this up to 8192.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: freed.
|
||||
char* e = (char*) malloc(4096);
|
||||
e = (char*) realloc(e, 4097);
|
||||
Report(e);
|
||||
|
||||
// First realloc is like malloc; second realloc is shrinking.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: re-reported.
|
||||
char* e2 = (char*) realloc(nullptr, 1024);
|
||||
e2 = (char*) realloc(e2, 512);
|
||||
Report(e2);
|
||||
|
||||
// First realloc is like malloc; second realloc creates a min-sized block.
|
||||
// XXX: on Windows, second realloc frees the block.
|
||||
// AnalyzeReports 2: reported.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* e3 = (char*) realloc(nullptr, 1023);
|
||||
//e3 = (char*) realloc(e3, 0);
|
||||
MOZ_ASSERT(e3);
|
||||
Report(e3);
|
||||
|
||||
// AnalyzeReports 2: freed, irrelevant.
|
||||
// AnalyzeReports 3: freed, irrelevant.
|
||||
char* f = (char*) malloc(64);
|
||||
free(f);
|
||||
|
||||
// AnalyzeReports 2: ignored.
|
||||
// AnalyzeReports 3: irrelevant.
|
||||
Report((void*)(intptr_t)0x0);
|
||||
|
||||
// AnalyzeReports 2: mixture of reported and unreported.
|
||||
// AnalyzeReports 3: all unreported.
|
||||
Foo(seven);
|
||||
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: twice-reported.
|
||||
char* g1 = (char*) malloc(77);
|
||||
ReportOnAlloc(g1);
|
||||
ReportOnAlloc(g1);
|
||||
|
||||
// AnalyzeReports 2: mixture of reported and unreported.
|
||||
// AnalyzeReports 3: all unreported.
|
||||
// Nb: this Foo() call is deliberately not adjacent to the previous one. See
|
||||
// the comment about adjacent calls in Foo() for more details.
|
||||
Foo(seven);
|
||||
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: once-reported.
|
||||
char* g2 = (char*) malloc(78);
|
||||
Report(g2);
|
||||
ReportOnAlloc(g2);
|
||||
|
||||
// AnalyzeReports 2: twice-reported.
|
||||
// AnalyzeReports 3: once-reported.
|
||||
char* g3 = (char*) malloc(79);
|
||||
ReportOnAlloc(g3);
|
||||
Report(g3);
|
||||
|
||||
// All the odd-ball ones.
|
||||
// AnalyzeReports 2: all unreported.
|
||||
// AnalyzeReports 3: all freed, irrelevant.
|
||||
// XXX: no memalign on Mac
|
||||
//void* w = memalign(64, 65); // rounds up to 128
|
||||
//UseItOrLoseIt(w, seven);
|
||||
|
||||
// XXX: posix_memalign doesn't work on B2G
|
||||
//void* x;
|
||||
//posix_memalign(&y, 128, 129); // rounds up to 256
|
||||
//UseItOrLoseIt(x, seven);
|
||||
|
||||
// XXX: valloc doesn't work on Windows.
|
||||
//void* y = valloc(1); // rounds up to 4096
|
||||
//UseItOrLoseIt(y, seven);
|
||||
|
||||
// XXX: C11 only
|
||||
//void* z = aligned_alloc(64, 256);
|
||||
//UseItOrLoseIt(z, seven);
|
||||
|
||||
// AnalyzeReports 2.
|
||||
JSONWriter writer2(Move(f2));
|
||||
AnalyzeReports(writer2);
|
||||
|
||||
//---------
|
||||
|
||||
Report(a2);
|
||||
Report(a2);
|
||||
free(c);
|
||||
free(e);
|
||||
Report(e2);
|
||||
free(e3);
|
||||
//free(w);
|
||||
//free(x);
|
||||
//free(y);
|
||||
//free(z);
|
||||
|
||||
// AnalyzeReports 3.
|
||||
JSONWriter writer3(Move(f3));
|
||||
AnalyzeReports(writer3);
|
||||
|
||||
//---------
|
||||
|
||||
// The first part of this test requires sampling to be disabled.
|
||||
SetSampleBelowSize(128);
|
||||
|
||||
// Clear all knowledge of existing blocks to give us a clean slate.
|
||||
ClearBlocks();
|
||||
|
||||
char* s;
|
||||
|
||||
// This equals the sample size, and so is reported exactly. It should be
|
||||
// listed before records of the same size that are sampled.
|
||||
s = (char*) malloc(128);
|
||||
UseItOrLoseIt(s, seven);
|
||||
|
||||
// This exceeds the sample size, and so is reported exactly.
|
||||
s = (char*) malloc(144);
|
||||
UseItOrLoseIt(s, seven);
|
||||
|
||||
// These together constitute exactly one sample.
|
||||
for (int i = 0; i < seven + 9; i++) {
|
||||
s = (char*) malloc(8);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
|
||||
// These fall 8 bytes short of a full sample.
|
||||
for (int i = 0; i < seven + 8; i++) {
|
||||
s = (char*) malloc(8);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
|
||||
// This exceeds the sample size, and so is recorded exactly.
|
||||
s = (char*) malloc(256);
|
||||
UseItOrLoseIt(s, seven);
|
||||
|
||||
// This gets more than to a full sample from the |i < seven + 8| loop above.
|
||||
s = (char*) malloc(96);
|
||||
UseItOrLoseIt(s, seven);
|
||||
|
||||
// This gets to another full sample.
|
||||
for (int i = 0; i < seven - 2; i++) {
|
||||
s = (char*) malloc(8);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
|
||||
// This allocates 16, 32, ..., 128 bytes, which results in a heap block
|
||||
// record that contains a mix of sample and non-sampled blocks, and so should
|
||||
// be printed with '~' signs.
|
||||
for (int i = 1; i <= seven + 1; i++) {
|
||||
s = (char*) malloc(i * 16);
|
||||
UseItOrLoseIt(s, seven);
|
||||
}
|
||||
|
||||
// At the end we're 64 bytes into the current sample so we report ~1,424
|
||||
// bytes of allocation overall, which is 64 less than the real value 1,488.
|
||||
|
||||
// AnalyzeReports 4.
|
||||
JSONWriter writer4(Move(f4));
|
||||
AnalyzeReports(writer4);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
RunTests();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-heap-empty-actual.txt --ignore-reports full-empty.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-heap-sampled-actual.txt --ignore-reports full-sampled.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 128
|
||||
}
|
||||
|
||||
|
@ -10,6 +11,7 @@ Invocation {
|
|||
Live {
|
||||
~4 blocks in heap block record 1 of 7
|
||||
~512 bytes (~512 requested / ~0 slop)
|
||||
Individual block sizes: ~128 x 3; 128
|
||||
35.96% of the heap (35.96% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-heap-unsampled1-actual.txt --ignore-reports full-unsampled1.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
@ -28,6 +29,7 @@ Live {
|
|||
Live {
|
||||
9 blocks in heap block record 3 of 12
|
||||
1,008 bytes (900 requested / 108 slop)
|
||||
Individual block sizes: 112 x 9
|
||||
8.34% of the heap (84.58% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
|
@ -37,6 +39,7 @@ Live {
|
|||
Live {
|
||||
6 blocks in heap block record 4 of 12
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
4.37% of the heap (88.95% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
|
@ -46,6 +49,7 @@ Live {
|
|||
Live {
|
||||
6 blocks in heap block record 5 of 12
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
4.37% of the heap (93.32% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-heap-unsampled2-actual.txt --ignore-reports full-unsampled2.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
@ -10,6 +11,7 @@ Invocation {
|
|||
Live {
|
||||
9 blocks in heap block record 1 of 9
|
||||
1,008 bytes (900 requested / 108 slop)
|
||||
Individual block sizes: 112 x 9
|
||||
35.49% of the heap (35.49% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
|
@ -19,6 +21,7 @@ Live {
|
|||
Live {
|
||||
6 blocks in heap block record 2 of 9
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
18.59% of the heap (54.08% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
|
@ -28,6 +31,7 @@ Live {
|
|||
Live {
|
||||
6 blocks in heap block record 3 of 9
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
18.59% of the heap (72.68% cumulative)
|
||||
Allocated at {
|
||||
#01: ... DMD.cpp ...
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-reports-empty-actual.txt full-empty.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-reports-sampled-actual.txt full-sampled.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 128
|
||||
}
|
||||
|
||||
|
@ -14,6 +15,7 @@ Invocation {
|
|||
Unreported {
|
||||
~4 blocks in heap block record 1 of 7
|
||||
~512 bytes (~512 requested / ~0 slop)
|
||||
Individual block sizes: ~128 x 3; 128
|
||||
35.96% of the heap (35.96% cumulative)
|
||||
35.96% of unreported (35.96% cumulative)
|
||||
Allocated at {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-reports-unsampled1-actual.txt full-unsampled1.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
@ -76,6 +77,7 @@ Twice-reported {
|
|||
Unreported {
|
||||
9 blocks in heap block record 1 of 3
|
||||
1,008 bytes (900 requested / 108 slop)
|
||||
Individual block sizes: 112 x 9
|
||||
8.34% of the heap (8.34% cumulative)
|
||||
81.82% of unreported (81.82% cumulative)
|
||||
Allocated at {
|
||||
|
@ -86,6 +88,7 @@ Unreported {
|
|||
Unreported {
|
||||
2 blocks in heap block record 2 of 3
|
||||
112 bytes (112 requested / 0 slop)
|
||||
Individual block sizes: 64; 48
|
||||
0.93% of the heap (9.27% cumulative)
|
||||
9.09% of unreported (90.91% cumulative)
|
||||
Allocated at {
|
||||
|
@ -96,6 +99,7 @@ Unreported {
|
|||
Unreported {
|
||||
2 blocks in heap block record 3 of 3
|
||||
112 bytes (112 requested / 0 slop)
|
||||
Individual block sizes: 64; 48
|
||||
0.93% of the heap (10.19% cumulative)
|
||||
9.09% of unreported (100.00% cumulative)
|
||||
Allocated at {
|
||||
|
@ -147,6 +151,7 @@ Once-reported {
|
|||
Once-reported {
|
||||
2 blocks in heap block record 4 of 11
|
||||
240 bytes (240 requested / 0 slop)
|
||||
Individual block sizes: 128; 112
|
||||
1.99% of the heap (82.46% cumulative)
|
||||
2.27% of once-reported (94.18% cumulative)
|
||||
Allocated at {
|
||||
|
@ -160,6 +165,7 @@ Once-reported {
|
|||
Once-reported {
|
||||
2 blocks in heap block record 5 of 11
|
||||
240 bytes (240 requested / 0 slop)
|
||||
Individual block sizes: 128; 112
|
||||
1.99% of the heap (84.45% cumulative)
|
||||
2.27% of once-reported (96.45% cumulative)
|
||||
Allocated at {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o full-reports-unsampled2-actual.txt full-unsampled2.json
|
||||
|
||||
Invocation {
|
||||
$DMD = '--mode=test'
|
||||
$DMD = '1'
|
||||
Sample-below size = 1
|
||||
}
|
||||
|
||||
|
@ -44,6 +45,7 @@ Twice-reported {
|
|||
Unreported {
|
||||
9 blocks in heap block record 1 of 3
|
||||
1,008 bytes (900 requested / 108 slop)
|
||||
Individual block sizes: 112 x 9
|
||||
35.49% of the heap (35.49% cumulative)
|
||||
48.84% of unreported (48.84% cumulative)
|
||||
Allocated at {
|
||||
|
@ -54,6 +56,7 @@ Unreported {
|
|||
Unreported {
|
||||
6 blocks in heap block record 2 of 3
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
18.59% of the heap (54.08% cumulative)
|
||||
25.58% of unreported (74.42% cumulative)
|
||||
Allocated at {
|
||||
|
@ -64,6 +67,7 @@ Unreported {
|
|||
Unreported {
|
||||
6 blocks in heap block record 3 of 3
|
||||
528 bytes (528 requested / 0 slop)
|
||||
Individual block sizes: 128; 112; 96; 80; 64; 48
|
||||
18.59% of the heap (72.68% cumulative)
|
||||
25.58% of unreported (100.00% cumulative)
|
||||
Allocated at {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SimplePrograms([
|
||||
'SmokeDMD',
|
||||
])
|
||||
|
||||
# See the comment at the top of SmokeDMD.cpp:RunTests().
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
CXXFLAGS += ['-Og-']
|
||||
else:
|
||||
CXXFLAGS += ['-O0']
|
||||
|
||||
DEFINES['MOZ_NO_MOZALLOC'] = True
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
||||
USE_LIBS += ['dmd']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'xpcshell.ini',
|
||||
]
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o script-diff-basic-actual.txt script-diff1.json script-diff2.json
|
||||
|
||||
Invocation 1 {
|
||||
$DMD = '--sample-below=127'
|
||||
Sample-below size = 127
|
||||
}
|
||||
|
||||
Invocation 2 {
|
||||
$DMD = '--sample-below=63'
|
||||
Sample-below size = 63
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Twice-reported {
|
||||
~-1 blocks in heap block record 1 of 1
|
||||
~-1,088 bytes (~-1,064 requested / ~-24 slop)
|
||||
Individual block sizes: -1,024; ~-127; ~63
|
||||
15.46% of the heap (15.46% cumulative)
|
||||
100.00% of twice-reported (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: F (F.cpp:99)
|
||||
}
|
||||
Reported at {
|
||||
#01: R1 (R1.cpp:99)
|
||||
}
|
||||
Reported again at {
|
||||
#01: R2 (R2.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Unreported {
|
||||
4 blocks in heap block record 1 of 5
|
||||
16,384 bytes (16,384 requested / 0 slop)
|
||||
Individual block sizes: 4,096 x 4
|
||||
-232.76% of the heap (-232.76% cumulative)
|
||||
371.01% of unreported (371.01% cumulative)
|
||||
Allocated at {
|
||||
#01: E (E.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Unreported {
|
||||
~7 blocks in heap block record 2 of 5
|
||||
~-11,968 bytes (~-12,016 requested / ~48 slop)
|
||||
Individual block sizes: -15,360; 2,048; 512 x 2; 128; ~-127; 64 x 4; ~63
|
||||
170.02% of the heap (-62.74% cumulative)
|
||||
-271.01% of unreported (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: F (F.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Unreported {
|
||||
0 blocks in heap block record 3 of 5
|
||||
0 bytes (-384 requested / 384 slop)
|
||||
Individual block sizes: (no change)
|
||||
-0.00% of the heap (-62.74% cumulative)
|
||||
0.00% of unreported (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: C (C.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Unreported {
|
||||
-2 blocks in heap block record 4 of 5
|
||||
0 bytes (0 requested / 0 slop)
|
||||
Individual block sizes: 8,192 x 2; -4,096 x 4
|
||||
-0.00% of the heap (-62.74% cumulative)
|
||||
0.00% of unreported (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: B (B.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Unreported {
|
||||
0 blocks in heap block record 5 of 5
|
||||
0 bytes (0 requested / 0 slop)
|
||||
Individual block sizes: 20,480; -16,384; -8,192; 4,096
|
||||
-0.00% of the heap (-62.74% cumulative)
|
||||
0.00% of unreported (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: G (G.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Once-reported {
|
||||
-3 blocks in heap block record 1 of 2
|
||||
-10,240 bytes (-10,192 requested / -48 slop)
|
||||
Individual block sizes: -4,096 x 2; -2,048
|
||||
145.48% of the heap (145.48% cumulative)
|
||||
98.77% of once-reported (98.77% cumulative)
|
||||
Allocated at {
|
||||
#01: D (D.cpp:99)
|
||||
}
|
||||
Reported at {
|
||||
#01: R1 (R1.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Once-reported {
|
||||
~-1 blocks in heap block record 2 of 2
|
||||
~-127 bytes (~-151 requested / ~24 slop)
|
||||
1.80% of the heap (147.28% cumulative)
|
||||
1.23% of once-reported (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: F (F.cpp:99)
|
||||
}
|
||||
Reported at {
|
||||
#01: R1 (R1.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: ~-7,039 bytes (100.00%) in ~4 blocks (100.00%)
|
||||
Unreported: ~4,416 bytes (-62.74%) in ~9 blocks (225.00%)
|
||||
Once-reported: ~-10,367 bytes (147.28%) in ~-4 blocks (-100.00%)
|
||||
Twice-reported: ~-1,088 bytes ( 15.46%) in ~-1 blocks (-25.00%)
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#-----------------------------------------------------------------
|
||||
# dmd.py --filter-stacks-for-testing -o script-diff-options-actual.txt --ignore-reports script-diff1.json script-diff2.json
|
||||
|
||||
Invocation 1 {
|
||||
$DMD = '--sample-below=127'
|
||||
Sample-below size = 127
|
||||
}
|
||||
|
||||
Invocation 2 {
|
||||
$DMD = '--sample-below=63'
|
||||
Sample-below size = 63
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Live {
|
||||
4 blocks in heap block record 1 of 6
|
||||
16,384 bytes (16,384 requested / 0 slop)
|
||||
Individual block sizes: 4,096 x 4
|
||||
-232.76% of the heap (-232.76% cumulative)
|
||||
Allocated at {
|
||||
#01: E (E.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
~5 blocks in heap block record 2 of 6
|
||||
~-13,183 bytes (~-13,231 requested / ~48 slop)
|
||||
Individual block sizes: -15,360; 2,048; -1,024; 512 x 2; 128; ~-127 x 3; 64 x 4; ~63 x 2
|
||||
187.29% of the heap (-45.48% cumulative)
|
||||
Allocated at {
|
||||
#01: F (F.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
-3 blocks in heap block record 3 of 6
|
||||
-10,240 bytes (-10,192 requested / -48 slop)
|
||||
Individual block sizes: -4,096 x 2; -2,048
|
||||
145.48% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: D (D.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
0 blocks in heap block record 4 of 6
|
||||
0 bytes (-384 requested / 384 slop)
|
||||
Individual block sizes: (no change)
|
||||
-0.00% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: C (C.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
0 blocks in heap block record 5 of 6
|
||||
0 bytes (0 requested / 0 slop)
|
||||
Individual block sizes: 20,480; -16,384; -8,192; 4,096
|
||||
-0.00% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: G (G.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
Live {
|
||||
-2 blocks in heap block record 6 of 6
|
||||
0 bytes (0 requested / 0 slop)
|
||||
Individual block sizes: 8,192 x 2; -4,096 x 4
|
||||
-0.00% of the heap (100.00% cumulative)
|
||||
Allocated at {
|
||||
#01: B (B.cpp:99)
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
Summary {
|
||||
Total: ~-7,039 bytes in ~4 blocks
|
||||
}
|
||||
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче