Merge m-c to fx-team.

--HG--
rename : content/media/mediasource/SourceBuffer.cpp => content/media/mediasource/ContainerParser.cpp
extra : rebase_source : 8e7ce8e6b3b7dd41e6691465e6e65437d694a3ab
This commit is contained in:
Ryan VanderMeulen 2014-09-19 14:19:13 -04:00
commit 93d73ce5fb
118 changed files with 2213 additions and 903 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -135,9 +135,9 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
<project name="kernel/common" path="kernel" revision="28aab3bd1139b6beea545f50dee8903c0634de84"/>
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
<project name="u-boot" path="u-boot" revision="2d7a801a3e002078f885e8085fad374a564682e5"/>
<project name="u-boot" path="u-boot" revision="982c1fd67b89d5573317c1796cf5b0143de44e8a"/>
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="69c8c336794666b010e34b2f501d89118513c546"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="1d4697b16ed039fd1de0a23bda150523e743e2ad"/>
<project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
<project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
</manifest>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -132,7 +132,7 @@
<!-- Flame specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
<project name="device/qcom/common" path="device/qcom/common" revision="54c32c2ddef066fbdf611d29e4b7c47e0363599e"/>
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="6f72b9d7a2322043fd0c4ba889ad689b084081c5"/>
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="960533f716ce31dfad357e87fa2f1d9ee5e94674"/>
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="893238eb1215f8fd4f3747169170cc5e1cc33969"/>
<project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="fda40423ffa573dc6cafd3780515010cb2a086be"/>
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="b2af89ae378a119819a9c86d9a12e573c7130459"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "98ad49da1fb006f0dcd8ed1af37382ab531ef016",
"revision": "02fabec9910191bf6f99cb9879a1e6603d5ed7c3",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -1469,11 +1469,16 @@ notification[value="translation"] menulist > .menulist-dropmarker {
.ac-result-type-keyword,
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
list-style-image: url(moz-icon://stock/gtk-find?size=menu);
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
width: 16px;
height: 16px;
}
.ac-result-type-keyword[selected="true"],
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
}
.ac-result-type-tag,
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
list-style-image: url("chrome://browser/skin/places/tag.png");

View File

@ -2175,10 +2175,14 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
.ac-result-type-keyword,
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
list-style-image: url(chrome://global/skin/icons/search-textbox.png);
margin: 2px;
width: 12px;
height: 12px;
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
width: 16px;
height: 16px;
}
.ac-result-type-keyword[selected="true"],
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
}
richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,

View File

@ -1395,12 +1395,22 @@ richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-
.ac-result-type-keyword,
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
list-style-image: url(chrome://global/skin/icons/Search-glass.png);
-moz-image-region: rect(0px 32px 16px 16px);
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
width: 16px;
height: 16px;
}
%ifdef WINDOWS_AERO
@media not all and (-moz-windows-default-theme) {
%endif
.ac-result-type-keyword[selected="true"],
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
}
%ifdef WINDOWS_AERO
}
%endif
.ac-result-type-tag,
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
list-style-image: url("chrome://browser/skin/places/tag.png");

View File

@ -320,12 +320,15 @@ GetDirectionFromText(const char16_t* aText, const uint32_t aLength,
current++;
}
Directionality dir = GetDirectionFromChar(ch);
if (dir != eDir_NotSet) {
if (aFirstStrong) {
*aFirstStrong = current;
// Just ignore lone surrogates
if (!IS_SURROGATE(ch)) {
Directionality dir = GetDirectionFromChar(ch);
if (dir != eDir_NotSet) {
if (aFirstStrong) {
*aFirstStrong = current;
}
return dir;
}
return dir;
}
}

View File

@ -10,6 +10,7 @@
#include "DOMMediaStream.h"
#include "EncodedBufferCache.h"
#include "MediaEncoder.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/AudioStreamTrack.h"
@ -35,6 +36,73 @@ namespace mozilla {
namespace dom {
/**
+ * MediaRecorderReporter measures memory being used by the Media Recorder.
+ *
+ * It is a singleton reporter and the single class object lives as long as at
+ * least one Recorder is registered. In MediaRecorder, the reporter is unregistered
+ * when it is destroyed.
+ */
class MediaRecorderReporter MOZ_FINAL : public nsIMemoryReporter
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
MediaRecorderReporter() {};
static MediaRecorderReporter* UniqueInstance();
void InitMemoryReporter();
static void AddMediaRecorder(MediaRecorder *aRecorder)
{
GetRecorders().AppendElement(aRecorder);
}
static void RemoveMediaRecorder(MediaRecorder *aRecorder)
{
RecordersArray& recorders = GetRecorders();
recorders.RemoveElement(aRecorder);
if (recorders.IsEmpty()) {
sUniqueInstance = nullptr;
}
}
NS_METHOD
CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)
{
int64_t amount = 0;
RecordersArray& recorders = GetRecorders();
for (size_t i = 0; i < recorders.Length(); ++i) {
amount += recorders[i]->SizeOfExcludingThis(MallocSizeOf);
}
#define MEMREPORT(_path, _amount, _desc) \
do { \
nsresult rv; \
rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
KIND_HEAP, UNITS_BYTES, _amount, \
NS_LITERAL_CSTRING(_desc), aData); \
NS_ENSURE_SUCCESS(rv, rv); \
} while (0)
MEMREPORT("explicit/media/recorder", amount,
"Memory used by media recorder.");
return NS_OK;
}
private:
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
virtual ~MediaRecorderReporter();
static StaticRefPtr<MediaRecorderReporter> sUniqueInstance;
typedef nsTArray<MediaRecorder*> RecordersArray;
static RecordersArray& GetRecorders()
{
return UniqueInstance()->mRecorders;
}
RecordersArray mRecorders;
};
NS_IMPL_ISUPPORTS(MediaRecorderReporter, nsIMemoryReporter);
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaRecorder, DOMEventTargetHelper,
mDOMStream, mAudioNode)
@ -332,6 +400,14 @@ public:
return false;
}
size_t
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t amount = mEncoder->SizeOfExcludingThis(aMallocSizeOf);
return amount;
}
private:
// Only DestroyRunnable is allowed to delete Session object.
virtual ~Session()
@ -681,7 +757,7 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
timeSlice = aTimeSlice.Value();
}
MediaRecorderReporter::AddMediaRecorder(this);
mState = RecordingState::Recording;
// Start a session.
mSessions.AppendElement();
@ -693,6 +769,7 @@ void
MediaRecorder::Stop(ErrorResult& aResult)
{
LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
MediaRecorderReporter::RemoveMediaRecorder(this);
if (mState == RecordingState::Inactive) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
@ -988,5 +1065,36 @@ MediaRecorder::GetSourcePrincipal()
return doc ? doc->NodePrincipal() : nullptr;
}
size_t
MediaRecorder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t amount = 42;
for (size_t i = 0; i < mSessions.Length(); ++i) {
amount += mSessions[i]->SizeOfExcludingThis(aMallocSizeOf);
}
return amount;
}
StaticRefPtr<MediaRecorderReporter> MediaRecorderReporter::sUniqueInstance;
MediaRecorderReporter* MediaRecorderReporter::UniqueInstance()
{
if (!sUniqueInstance) {
sUniqueInstance = new MediaRecorderReporter();
sUniqueInstance->InitMemoryReporter();
}
return sUniqueInstance;
}
void MediaRecorderReporter::InitMemoryReporter()
{
RegisterWeakMemoryReporter(this);
}
MediaRecorderReporter::~MediaRecorderReporter()
{
UnregisterWeakMemoryReporter(this);
}
}
}

View File

@ -9,6 +9,7 @@
#include "mozilla/dom/MediaRecorderBinding.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/MemoryReporting.h"
#include "nsIDocumentActivity.h"
// Max size for allowing queue encoded data in memory
@ -90,6 +91,11 @@ public:
const MediaRecorderOptions& aInitDict,
ErrorResult& aRv);
/*
* Measure the size of the buffer, and memory occupied by mAudioEncoder
* and mVideoEncoder
*/
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// EventHandler
IMPL_EVENT_HANDLER(dataavailable)
IMPL_EVENT_HANDLER(error)

View File

@ -8,6 +8,7 @@
#include "nsMimeTypes.h"
#include "prlog.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPtr.h"
#include "OggWriter.h"
#ifdef MOZ_OPUS
@ -204,6 +205,9 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
rv = mWriter->GetContainerData(aOutputBufs,
ContainerWriter::GET_HEADER);
if (aOutputBufs != nullptr) {
mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
}
if (NS_FAILED(rv)) {
LOG(PR_LOG_ERROR,("Error! writer fail to generate header!"));
mState = ENCODE_ERROR;
@ -236,6 +240,9 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
rv = mWriter->GetContainerData(aOutputBufs,
isAudioCompleted && isVideoCompleted ?
ContainerWriter::FLUSH_NEEDED : 0);
if (aOutputBufs != nullptr) {
mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
}
if (NS_SUCCEEDED(rv)) {
// Successfully get the copy of final container data from writer.
reloop = false;
@ -250,6 +257,7 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
case ENCODE_DONE:
case ENCODE_ERROR:
LOG(PR_LOG_DEBUG, ("MediaEncoder has been shutdown."));
mSizeOfBuffer = 0;
mShutdown = true;
reloop = false;
break;
@ -323,4 +331,21 @@ MediaEncoder::IsOMXEncoderEnabled()
}
#endif
/*
* SizeOfExcludingThis measures memory being used by the Media Encoder.
* Currently it measures the size of the Encoder buffer and memory occupied
* by mAudioEncoder and mVideoEncoder.
*/
size_t
MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t amount = 0;
if (mState == ENCODE_TRACK) {
amount = mSizeOfBuffer +
(mAudioEncoder != nullptr ? mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0) +
(mVideoEncoder != nullptr ? mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0);
}
return amount;
}
}

View File

@ -10,6 +10,8 @@
#include "TrackEncoder.h"
#include "ContainerWriter.h"
#include "MediaStreamGraph.h"
#include "nsIMemoryReporter.h"
#include "mozilla/MemoryReporting.h"
namespace mozilla {
@ -66,6 +68,7 @@ public :
, mVideoEncoder(aVideoEncoder)
, mStartTime(TimeStamp::Now())
, mMIMEType(aMIMEType)
, mSizeOfBuffer(0)
, mState(MediaEncoder::ENCODE_METADDATA)
, mShutdown(false)
{}
@ -140,6 +143,13 @@ public :
static bool IsOMXEncoderEnabled();
#endif
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
/*
* Measure the size of the buffer, and memory occupied by mAudioEncoder
* and mVideoEncoder
*/
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
// Get encoded data from trackEncoder and write to muxer
nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);
@ -150,6 +160,7 @@ private:
nsAutoPtr<VideoTrackEncoder> mVideoEncoder;
TimeStamp mStartTime;
nsString mMIMEType;
int64_t mSizeOfBuffer;
int mState;
bool mShutdown;
// Get duration from create encoder, for logging purpose

View File

@ -173,6 +173,12 @@ AudioTrackEncoder::DeInterleaveTrackData(AudioDataValue* aInput,
}
}
size_t
AudioTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
}
void
VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
TrackID aID,
@ -263,4 +269,10 @@ VideoTrackEncoder::NotifyEndOfStream()
mReentrantMonitor.NotifyAll();
}
size_t
VideoTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
}
}

View File

@ -168,6 +168,10 @@ public:
*/
static void DeInterleaveTrackData(AudioDataValue* aInput, int32_t aDuration,
int32_t aChannels, AudioDataValue* aOutput);
/**
* Measure size of mRawSegment
*/
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
protected:
/**
@ -241,6 +245,10 @@ public:
TrackTicks aTrackOffset,
uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
/**
* Measure size of mRawSegment
*/
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
protected:
/**

View File

@ -0,0 +1,281 @@
/* -*- 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/. */
#include "ContainerParser.h"
#include "WebMBufferedParser.h"
#include "mozilla/Endian.h"
#include "mp4_demuxer/BufferStream.h"
#include "mp4_demuxer/MoofParser.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetMediaSourceLog();
extern PRLogModuleInfo* GetMediaSourceAPILog();
#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define MSE_DEBUG(...)
#define MSE_DEBUGV(...)
#define MSE_API(...)
#endif
namespace mozilla {
bool
ContainerParser::IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
this, aLength,
aLength > 0 ? aData[0] : 0,
aLength > 1 ? aData[1] : 0,
aLength > 2 ? aData[2] : 0,
aLength > 3 ? aData[3] : 0);
return false;
}
bool
ContainerParser::IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
this, aLength,
aLength > 0 ? aData[0] : 0,
aLength > 1 ? aData[1] : 0,
aLength > 2 ? aData[2] : 0,
aLength > 3 ? aData[3] : 0);
return false;
}
bool
ContainerParser::ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd)
{
return false;
}
bool
ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
{
NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
return aLhs == aRhs;
}
const nsTArray<uint8_t>&
ContainerParser::InitData()
{
MOZ_ASSERT(mHasInitData);
return mInitData;
}
class WebMContainerParser : public ContainerParser {
public:
WebMContainerParser()
: mParser(0), mOffset(0)
{}
static const unsigned NS_PER_USEC = 1000;
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
ContainerParser::IsInitSegmentPresent(aData, aLength);
// XXX: This is overly primitive, needs to collect data as it's appended
// to the SB and handle, rather than assuming everything is present in a
// single aData segment.
// 0x1a45dfa3 // EBML
// ...
// DocType == "webm"
// ...
// 0x18538067 // Segment (must be "unknown" size)
// 0x1549a966 // -> Segment Info
// 0x1654ae6b // -> One or more Tracks
if (aLength >= 4 &&
aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
return true;
}
return false;
}
bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
ContainerParser::IsMediaSegmentPresent(aData, aLength);
// XXX: This is overly primitive, needs to collect data as it's appended
// to the SB and handle, rather than assuming everything is present in a
// single aData segment.
// 0x1a45dfa3 // EBML
// ...
// DocType == "webm"
// ...
// 0x18538067 // Segment (must be "unknown" size)
// 0x1549a966 // -> Segment Info
// 0x1654ae6b // -> One or more Tracks
if (aLength >= 4 &&
aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) {
return true;
}
return false;
}
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd)
{
bool initSegment = IsInitSegmentPresent(aData, aLength);
if (initSegment) {
mOffset = 0;
mParser = WebMBufferedParser(0);
mOverlappedMapping.Clear();
}
// XXX if it only adds new mappings, overlapped but not available
// (e.g. overlap < 0) frames are "lost" from the reported mappings here.
nsTArray<WebMTimeDataOffset> mapping;
mapping.AppendElements(mOverlappedMapping);
mOverlappedMapping.Clear();
ReentrantMonitor dummy("dummy");
mParser.Append(aData, aLength, mapping, dummy);
// XXX This is a bit of a hack. Assume if there are no timecodes
// present and it's an init segment that it's _just_ an init segment.
// We should be more precise.
if (initSegment) {
uint32_t length = aLength;
if (!mapping.IsEmpty()) {
length = mapping[0].mSyncOffset;
MOZ_ASSERT(length <= aLength);
}
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, length);
mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
mHasInitData = true;
}
mOffset += aLength;
if (mapping.IsEmpty()) {
return false;
}
// Exclude frames that we don't enough data to cover the end of.
uint32_t endIdx = mapping.Length() - 1;
while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
endIdx -= 1;
}
if (endIdx == 0) {
return false;
}
uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
aStart = mapping[0].mTimecode / NS_PER_USEC;
aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
mapping.RemoveElementsAt(0, endIdx + 1);
mOverlappedMapping.AppendElements(mapping);
return true;
}
bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
{
int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
return llabs(aLhs - aRhs) <= error * 2;
}
private:
WebMBufferedParser mParser;
nsTArray<WebMTimeDataOffset> mOverlappedMapping;
int64_t mOffset;
};
class MP4ContainerParser : public ContainerParser {
public:
MP4ContainerParser() {}
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
ContainerParser::IsInitSegmentPresent(aData, aLength);
// Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
// file is the 'ftyp' atom followed by a file type. We just check for a
// vaguely valid 'ftyp' atom.
if (aLength < 8) {
return false;
}
uint32_t chunk_size = BigEndian::readUint32(aData);
if (chunk_size < 8) {
return false;
}
return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' &&
aData[7] == 'p';
}
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd)
{
bool initSegment = IsInitSegmentPresent(aData, aLength);
if (initSegment) {
mStream = new mp4_demuxer::BufferStream();
mParser = new mp4_demuxer::MoofParser(mStream, 0);
} else if (!mStream || !mParser) {
return false;
}
mStream->AppendBytes(aData, aLength);
nsTArray<MediaByteRange> byteRanges;
byteRanges.AppendElement(mStream->GetByteRange());
mParser->RebuildFragmentedIndex(byteRanges);
if (initSegment) {
const MediaByteRange& range = mParser->mInitRange;
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, range.mEnd - range.mStart);
mInitData.ReplaceElementsAt(0, mInitData.Length(),
aData + range.mStart,
range.mEnd - range.mStart);
mHasInitData = true;
}
mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
mParser->GetCompositionRange(byteRanges);
mStream->DiscardBefore(mParser->mOffset);
if (compositionRange.IsNull()) {
return false;
}
aStart = compositionRange.start;
aEnd = compositionRange.end;
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
this, aStart, aEnd);
return true;
}
private:
nsRefPtr<mp4_demuxer::BufferStream> mStream;
nsAutoPtr<mp4_demuxer::MoofParser> mParser;
};
/*static*/ ContainerParser*
ContainerParser::CreateForMIMEType(const nsACString& aType)
{
if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
return new WebMContainerParser();
}
if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
return new MP4ContainerParser();
}
return new ContainerParser();
}
} // namespace mozilla

View File

@ -0,0 +1,55 @@
/* -*- 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/. */
#ifndef MOZILLA_CONTAINERPARSER_H_
#define MOZILLA_CONTAINERPARSER_H_
#include "nsTArray.h"
namespace mozilla {
class ContainerParser {
public:
ContainerParser() : mHasInitData(false) {}
virtual ~ContainerParser() {}
// Return true if aData starts with an initialization segment.
// The base implementation exists only for debug logging and is expected
// to be called first from the overriding implementation.
virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength);
// Return true if aData starts with a media segment.
// The base implementation exists only for debug logging and is expected
// to be called first from the overriding implementation.
virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength);
// Parse aData to extract the start and end frame times from the media
// segment. aData may not start on a parser sync boundary. Return true
// if aStart and aEnd have been updated.
virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd);
// Compare aLhs and rHs, considering any error that may exist in the
// timestamps from the format's base representation. Return true if aLhs
// == aRhs within the error epsilon.
virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs);
const nsTArray<uint8_t>& InitData();
bool HasInitData()
{
return mHasInitData;
}
static ContainerParser* CreateForMIMEType(const nsACString& aType);
protected:
nsTArray<uint8_t> mInitData;
bool mHasInitData;
};
} // namespace mozilla
#endif /* MOZILLA_CONTAINERPARSER_H_ */

View File

@ -9,15 +9,11 @@
#include "AsyncEventRunner.h"
#include "MediaSourceUtils.h"
#include "TrackBuffer.h"
#include "WebMBufferedParser.h"
#include "mozilla/Endian.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/MediaSourceBinding.h"
#include "mozilla/dom/TimeRanges.h"
#include "mp4_demuxer/BufferStream.h"
#include "mp4_demuxer/MoofParser.h"
#include "nsError.h"
#include "nsIEventTarget.h"
#include "nsIRunnable.h"
@ -42,273 +38,6 @@ extern PRLogModuleInfo* GetMediaSourceAPILog();
namespace mozilla {
class ContainerParser {
public:
virtual ~ContainerParser() {}
// Return true if aData starts with an initialization segment.
// The base implementation exists only for debug logging and is expected
// to be called first from the overriding implementation.
virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
this, aLength,
aLength > 0 ? aData[0] : 0,
aLength > 1 ? aData[1] : 0,
aLength > 2 ? aData[2] : 0,
aLength > 3 ? aData[3] : 0);
return false;
}
// Return true if aData starts with a media segment.
// The base implementation exists only for debug logging and is expected
// to be called first from the overriding implementation.
virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
this, aLength,
aLength > 0 ? aData[0] : 0,
aLength > 1 ? aData[1] : 0,
aLength > 2 ? aData[2] : 0,
aLength > 3 ? aData[3] : 0);
return false;
}
// Parse aData to extract the start and end frame times from the media
// segment. aData may not start on a parser sync boundary. Return true
// if aStart and aEnd have been updated.
virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd)
{
return false;
}
// Compare aLhs and rHs, considering any error that may exist in the
// timestamps from the format's base representation. Return true if aLhs
// == aRhs within the error epsilon.
virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
{
NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
return aLhs == aRhs;
}
virtual const nsTArray<uint8_t>& InitData()
{
MOZ_ASSERT(mInitData.Length() > 0);
return mInitData;
}
static ContainerParser* CreateForMIMEType(const nsACString& aType);
protected:
nsTArray<uint8_t> mInitData;
};
class WebMContainerParser : public ContainerParser {
public:
WebMContainerParser()
: mParser(0), mOffset(0)
{}
static const unsigned NS_PER_USEC = 1000;
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
ContainerParser::IsInitSegmentPresent(aData, aLength);
// XXX: This is overly primitive, needs to collect data as it's appended
// to the SB and handle, rather than assuming everything is present in a
// single aData segment.
// 0x1a45dfa3 // EBML
// ...
// DocType == "webm"
// ...
// 0x18538067 // Segment (must be "unknown" size)
// 0x1549a966 // -> Segment Info
// 0x1654ae6b // -> One or more Tracks
if (aLength >= 4 &&
aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
return true;
}
return false;
}
bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
ContainerParser::IsMediaSegmentPresent(aData, aLength);
// XXX: This is overly primitive, needs to collect data as it's appended
// to the SB and handle, rather than assuming everything is present in a
// single aData segment.
// 0x1a45dfa3 // EBML
// ...
// DocType == "webm"
// ...
// 0x18538067 // Segment (must be "unknown" size)
// 0x1549a966 // -> Segment Info
// 0x1654ae6b // -> One or more Tracks
if (aLength >= 4 &&
aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) {
return true;
}
return false;
}
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd)
{
bool initSegment = IsInitSegmentPresent(aData, aLength);
if (initSegment) {
mOffset = 0;
mParser = WebMBufferedParser(0);
mOverlappedMapping.Clear();
}
// XXX if it only adds new mappings, overlapped but not available
// (e.g. overlap < 0) frames are "lost" from the reported mappings here.
nsTArray<WebMTimeDataOffset> mapping;
mapping.AppendElements(mOverlappedMapping);
mOverlappedMapping.Clear();
ReentrantMonitor dummy("dummy");
mParser.Append(aData, aLength, mapping, dummy);
// XXX This is a bit of a hack. Assume if there are no timecodes
// present and it's an init segment that it's _just_ an init segment.
// We should be more precise.
if (initSegment) {
uint32_t length = aLength;
if (!mapping.IsEmpty()) {
length = mapping[0].mSyncOffset;
MOZ_ASSERT(length <= aLength);
}
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, length);
mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
}
mOffset += aLength;
if (mapping.IsEmpty()) {
return false;
}
// Exclude frames that we don't enough data to cover the end of.
uint32_t endIdx = mapping.Length() - 1;
while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
endIdx -= 1;
}
if (endIdx == 0) {
return false;
}
uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
aStart = mapping[0].mTimecode / NS_PER_USEC;
aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
mapping.RemoveElementsAt(0, endIdx + 1);
mOverlappedMapping.AppendElements(mapping);
return true;
}
bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
{
int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
return llabs(aLhs - aRhs) <= error * 2;
}
private:
WebMBufferedParser mParser;
nsTArray<WebMTimeDataOffset> mOverlappedMapping;
int64_t mOffset;
};
class MP4ContainerParser : public ContainerParser {
public:
MP4ContainerParser() {}
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
ContainerParser::IsInitSegmentPresent(aData, aLength);
// Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
// file is the 'ftyp' atom followed by a file type. We just check for a
// vaguely valid 'ftyp' atom.
if (aLength < 8) {
return false;
}
uint32_t chunk_size = BigEndian::readUint32(aData);
if (chunk_size < 8) {
return false;
}
return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' &&
aData[7] == 'p';
}
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd)
{
bool initSegment = IsInitSegmentPresent(aData, aLength);
if (initSegment) {
mStream = new mp4_demuxer::BufferStream();
mParser = new mp4_demuxer::MoofParser(mStream, 0);
} else if (!mStream || !mParser) {
return false;
}
mStream->AppendBytes(aData, aLength);
nsTArray<MediaByteRange> byteRanges;
byteRanges.AppendElement(mStream->GetByteRange());
mParser->RebuildFragmentedIndex(byteRanges);
if (initSegment) {
const MediaByteRange& range = mParser->mInitRange;
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
this, range.mEnd - range.mStart);
mInitData.ReplaceElementsAt(0, mInitData.Length(),
aData + range.mStart,
range.mEnd - range.mStart);
}
mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
mParser->GetCompositionRange(byteRanges);
mStream->DiscardBefore(mParser->mOffset);
if (compositionRange.IsNull()) {
return false;
}
aStart = compositionRange.start;
aEnd = compositionRange.end;
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
this, aStart, aEnd);
return true;
}
private:
nsRefPtr<mp4_demuxer::BufferStream> mStream;
nsAutoPtr<mp4_demuxer::MoofParser> mParser;
};
/*static*/ ContainerParser*
ContainerParser::CreateForMIMEType(const nsACString& aType)
{
if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
return new WebMContainerParser();
}
if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
return new MP4ContainerParser();
}
return new ContainerParser();
}
namespace dom {
void
@ -497,7 +226,6 @@ SourceBuffer::Ended()
SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
: DOMEventTargetHelper(aMediaSource->GetParentObject())
, mMediaSource(aMediaSource)
, mType(aType)
, mAppendWindowStart(0)
, mAppendWindowEnd(PositiveInfinity<double>())
, mTimestampOffset(0)
@ -508,10 +236,9 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
MOZ_ASSERT(aMediaSource);
mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
75 * (1 << 20));
mParser = ContainerParser::CreateForMIMEType(aType);
mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mParser=%p mTrackBuffer=%p",
this, mParser.get(), mTrackBuffer.get());
MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mTrackBuffer=%p",
this, mTrackBuffer.get());
}
SourceBuffer::~SourceBuffer()
@ -586,52 +313,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
return;
}
StartUpdating();
// TODO: Run more of the buffer append algorithm asynchronously.
if (mParser->IsInitSegmentPresent(aData, aLength)) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
mMediaSource->QueueInitializationEvent();
mTrackBuffer->DiscardDecoder();
if (!mTrackBuffer->NewDecoder()) {
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
return;
}
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
} else if (!mTrackBuffer->HasInitSegment()) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
ErrorResult dummy;
mMediaSource->EndOfStream(decodeError, dummy);
aRv.Throw(NS_ERROR_FAILURE);
return;
}
int64_t start, end;
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
int64_t lastStart, lastEnd;
mTrackBuffer->LastTimestamp(lastStart, lastEnd);
if (mParser->IsMediaSegmentPresent(aData, aLength) &&
!mParser->TimestampsFuzzyEqual(start, lastEnd)) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
this, lastStart, lastEnd, start, end);
// This data is earlier in the timeline than data we have already
// processed, so we must create a new decoder to handle the decoding.
mTrackBuffer->DiscardDecoder();
// If we've got a decoder here, it's not initialized, so we can use it
// rather than creating a new one.
if (!mTrackBuffer->NewDecoder()) {
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
return;
}
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
const nsTArray<uint8_t>& initData = mParser->InitData();
mTrackBuffer->AppendData(initData.Elements(), initData.Length());
mTrackBuffer->SetLastStartTimestamp(start);
}
mTrackBuffer->SetLastEndTimestamp(end);
MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
this, lastStart, lastEnd, start, end);
}
if (!mTrackBuffer->AppendData(aData, aLength)) {
Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
ErrorResult dummy;
@ -640,9 +322,9 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
return;
}
// Schedule the state machine thread to ensure playback starts
// if required when data is appended.
mMediaSource->GetDecoder()->ScheduleStateMachineThread();
if (mTrackBuffer->HasInitSegment()) {
mMediaSource->QueueInitializationEvent();
}
// Run the final step of the buffer append algorithm asynchronously to
// ensure the SourceBuffer's updating flag transition behaves as required

View File

@ -28,7 +28,6 @@ struct JSContext;
namespace mozilla {
class ContainerParser;
class ErrorResult;
class TrackBuffer;
template <typename T> class AsyncEventRunner;
@ -120,13 +119,6 @@ private:
void DispatchSimpleEvent(const char* aName);
void QueueAsyncSimpleEvent(const char* aName);
// Create a new decoder for mType, and store the result in mDecoder.
// Returns true if mDecoder was set.
bool InitNewDecoder();
// Set mDecoder to null and reset mDecoderInitialized.
void DiscardDecoder();
// Update mUpdating and fire the appropriate events.
void StartUpdating();
void StopUpdating();
@ -141,12 +133,8 @@ private:
nsRefPtr<MediaSource> mMediaSource;
const nsCString mType;
uint32_t mEvictionThreshold;
nsAutoPtr<ContainerParser> mParser;
nsRefPtr<TrackBuffer> mTrackBuffer;
double mAppendWindowStart;

View File

@ -6,27 +6,19 @@
#include "TrackBuffer.h"
#include "ContainerParser.h"
#include "MediaSourceDecoder.h"
#include "SharedThreadPool.h"
#include "MediaTaskQueue.h"
#include "SourceBufferDecoder.h"
#include "SourceBufferResource.h"
#include "VideoUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/dom/TimeRanges.h"
#include "nsError.h"
#include "nsIRunnable.h"
#include "nsThreadUtils.h"
#include "prlog.h"
#if defined(DEBUG)
#include <sys/stat.h>
#include <sys/types.h>
#endif
struct JSContext;
class JSObject;
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetMediaSourceLog();
extern PRLogModuleInfo* GetMediaSourceAPILog();
@ -47,9 +39,9 @@ TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& a
, mType(aType)
, mLastStartTimestamp(0)
, mLastEndTimestamp(0)
, mHasInit(false)
{
MOZ_COUNT_CTOR(TrackBuffer);
mParser = ContainerParser::CreateForMIMEType(aType);
mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
aParentDecoder->AddTrackBuffer(this);
}
@ -102,6 +94,53 @@ TrackBuffer::Shutdown()
bool
TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Run more of the buffer append algorithm asynchronously.
if (mParser->IsInitSegmentPresent(aData, aLength)) {
MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
if (!NewDecoder()) {
return false;
}
} else if (!mParser->HasInitData()) {
MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
return false;
}
int64_t start, end;
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
if (mParser->IsMediaSegmentPresent(aData, aLength) &&
!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp)) {
MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
this, mLastStartTimestamp, mLastEndTimestamp, start, end);
// This data is earlier in the timeline than data we have already
// processed, so we must create a new decoder to handle the decoding.
if (!NewDecoder()) {
return false;
}
MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
const nsTArray<uint8_t>& initData = mParser->InitData();
AppendDataToCurrentResource(initData.Elements(), initData.Length());
mLastStartTimestamp = start;
}
mLastEndTimestamp = end;
MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
this, mLastStartTimestamp, mLastEndTimestamp, start, end);
}
if (!AppendDataToCurrentResource(aData, aLength)) {
return false;
}
// Schedule the state machine thread to ensure playback starts if required
// when data is appended.
mParentDecoder->ScheduleStateMachineThread();
return true;
}
bool
TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mCurrentDecoder) {
@ -179,7 +218,9 @@ bool
TrackBuffer::NewDecoder()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mCurrentDecoder && mParentDecoder);
MOZ_ASSERT(mParentDecoder);
DiscardDecoder();
nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType);
if (!decoder) {
@ -191,7 +232,6 @@ TrackBuffer::NewDecoder()
mLastStartTimestamp = 0;
mLastEndTimestamp = 0;
mHasInit = true;
return QueueInitializeDecoder(decoder);
}
@ -316,7 +356,7 @@ bool
TrackBuffer::HasInitSegment()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
return mHasInit;
return mParser->HasInitData();
}
bool
@ -324,29 +364,7 @@ TrackBuffer::IsReady()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
MOZ_ASSERT((mInfo.HasAudio() || mInfo.HasVideo()) || mInitializedDecoders.IsEmpty());
return HasInitSegment() && (mInfo.HasAudio() || mInfo.HasVideo());
}
void
TrackBuffer::LastTimestamp(int64_t& aStart, int64_t& aEnd)
{
MOZ_ASSERT(NS_IsMainThread());
aStart = mLastStartTimestamp;
aEnd = mLastEndTimestamp;
}
void
TrackBuffer::SetLastStartTimestamp(int64_t aStart)
{
MOZ_ASSERT(NS_IsMainThread());
mLastStartTimestamp = aStart;
}
void
TrackBuffer::SetLastEndTimestamp(int64_t aEnd)
{
MOZ_ASSERT(NS_IsMainThread());
mLastEndTimestamp = aEnd;
return mParser->HasInitData() && (mInfo.HasAudio() || mInfo.HasVideo());
}
bool

View File

@ -17,6 +17,7 @@
namespace mozilla {
class ContainerParser;
class MediaSourceDecoder;
namespace dom {
@ -45,11 +46,6 @@ public:
// decoders buffered ranges in aRanges.
double Buffered(dom::TimeRanges* aRanges);
// Create a new decoder, set mCurrentDecoder to the new decoder, and queue
// the decoder for initialization. The decoder is not considered
// initialized until it is added to mDecoders.
bool NewDecoder();
// Mark the current decoder's resource as ended, clear mCurrentDecoder and
// reset mLast{Start,End}Timestamp.
void DiscardDecoder();
@ -59,15 +55,10 @@ public:
// Returns true if an init segment has been appended.
bool HasInitSegment();
// Returns true iff HasInitSegment() and the decoder using that init
// Returns true iff mParser->HasInitData() and the decoder using that init
// segment has successfully initialized by setting mHas{Audio,Video}..
bool IsReady();
// Query and update mLast{Start,End}Timestamp.
void LastTimestamp(int64_t& aStart, int64_t& aEnd);
void SetLastStartTimestamp(int64_t aStart);
void SetLastEndTimestamp(int64_t aEnd);
// Returns true if any of the decoders managed by this track buffer
// contain aTime in their buffered ranges.
bool ContainsTime(int64_t aTime);
@ -89,6 +80,15 @@ public:
private:
~TrackBuffer();
// Create a new decoder, set mCurrentDecoder to the new decoder, and queue
// the decoder for initialization. The decoder is not considered
// initialized until it is added to mDecoders.
bool NewDecoder();
// Helper for AppendData, ensures NotifyDataArrived is called whenever
// data is appended to the current decoder's SourceBufferResource.
bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength);
// Queue execution of InitializeDecoder on mTaskQueue.
bool QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
@ -112,6 +112,8 @@ private:
// function.
void RemoveDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
nsAutoPtr<ContainerParser> mParser;
// A task queue using the shared media thread pool. Used exclusively to
// initialize (i.e. call ReadMetadata on) decoders as they are created via
// NewDecoder.
@ -136,10 +138,6 @@ private:
int64_t mLastStartTimestamp;
int64_t mLastEndTimestamp;
// Set when the initialization segment is first seen and cached (implied
// by new decoder creation). Protected by mParentDecoder's monitor.
bool mHasInit;
// Set when the first decoder used by this TrackBuffer is initialized.
// Protected by mParentDecoder's monitor.
MediaInfo mInfo;

View File

@ -17,6 +17,7 @@ EXPORTS.mozilla.dom += [
]
UNIFIED_SOURCES += [
'ContainerParser.cpp',
'MediaSource.cpp',
'MediaSourceDecoder.cpp',
'MediaSourceReader.cpp',

View File

@ -669,10 +669,12 @@ public:
unsigned flags,
JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
static void ObjectMoved(JSObject *obj, const JSObject *old);
static const nsOuterWindowProxy singleton;
protected:
nsGlobalWindow* GetWindow(JSObject *proxy) const
static nsGlobalWindow* GetWindow(JSObject *proxy)
{
return nsGlobalWindow::FromSupports(
static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
@ -704,7 +706,8 @@ const js::Class OuterWindowProxyClass =
nullptr, /* outerObject */
js::proxy_innerObject,
nullptr, /* iteratorObject */
false /* isWrappedNative */
false, /* isWrappedNative */
nsOuterWindowProxy::ObjectMoved
));
bool
@ -1024,6 +1027,15 @@ nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
return js::UnwatchGuts(cx, proxy, id);
}
void
nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old)
{
nsGlobalWindow* global = GetWindow(obj);
if (global) {
global->UpdateWrapper(obj, old);
}
}
const nsOuterWindowProxy
nsOuterWindowProxy::singleton;

View File

@ -8,6 +8,7 @@
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Assertions.h"
#include "js/Class.h"
#include "js/Id.h" // must come before js/RootingAPI.h
#include "js/Value.h" // must come before js/RootingAPI.h
#include "js/RootingAPI.h"
@ -44,6 +45,14 @@ class XPCWrappedNativeScope;
*
* The finalizer for the wrapper clears the cache.
*
* A compacting GC can move the wrapper object. Pointers to moved objects are
* usually found and updated by tracing the heap, however non-preserved wrappers
* are weak references and are not traced, so another approach is
* necessary. Instead a class hook (objectMovedOp) is provided that is called
* when an object is moved and is responsible for ensuring pointers are
* updated. It does this by calling UpdateWrapper() on the wrapper
* cache. SetWrapper() asserts that the hook is implemented for any wrapper set.
*
* A number of the methods are implemented in nsWrapperCacheInlines.h because we
* have to include some JS headers that don't play nicely with the rest of the
* codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
@ -89,6 +98,8 @@ public:
{
MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
MOZ_ASSERT(js::HasObjectMovedOp(aWrapper),
"Object has not provided the hook to update the wrapper if it is moved");
SetWrapperJSObject(aWrapper);
}
@ -104,6 +115,18 @@ public:
SetWrapperJSObject(nullptr);
}
/**
* Update the wrapper if the object it contains is moved.
*
* This method must be called from the objectMovedOp class extension hook for
* any wrapper cached object.
*/
void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject)
{
MOZ_ASSERT(mWrapper == aOldObject);
mWrapper = aNewObject;
}
bool PreservingWrapper()
{
return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
@ -162,7 +185,7 @@ public:
}
}
/*
/*
* The following methods for getting and manipulating flags allow the unused
* bits of mFlags to be used by derived classes.
*/

View File

@ -1213,6 +1213,24 @@ ClearWrapper(T* p, void*)
ClearWrapper(p, cache);
}
template<class T>
inline void
UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj, const JSObject* old)
{
JS::AutoAssertGCCallback inCallback(obj);
cache->UpdateWrapper(obj, old);
}
template<class T>
inline void
UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old)
{
JS::AutoAssertGCCallback inCallback(obj);
nsWrapperCache* cache;
CallQueryInterface(p, &cache);
UpdateWrapper(p, cache, obj, old);
}
// Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
// Return true if we successfully preserved the wrapper, or there is no wrapper
// to preserve. In the latter case we don't need to preserve the wrapper, because

View File

@ -17,6 +17,7 @@ AUTOGENERATED_WARNING_COMMENT = \
"/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
ADDPROPERTY_HOOK_NAME = '_addProperty'
FINALIZE_HOOK_NAME = '_finalize'
OBJECT_MOVED_HOOK_NAME = '_objectMoved'
CONSTRUCT_HOOK_NAME = '_constructor'
LEGACYCALLER_HOOK_NAME = '_legacycaller'
HASINSTANCE_HOOK_NAME = '_hasInstance'
@ -364,48 +365,61 @@ class CGDOMJSClass(CGThing):
def define(self):
traceHook = 'nullptr'
callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
classFlags = "JSCLASS_IS_DOMJSCLASS | "
classExtensionAndObjectOps = """\
JS_NULL_CLASS_EXT,
JS_NULL_OBJECT_OPS
"""
classExtensionAndObjectOps = fill(
"""
{
nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
${objectMoved} /* objectMovedOp */
},
JS_NULL_OBJECT_OPS
""",
objectMoved=objectMovedHook)
if self.descriptor.isGlobal():
classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
traceHook = "JS_GlobalObjectTraceHook"
reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
if not self.descriptor.workers:
classExtensionAndObjectOps = """\
{
nsGlobalWindow::OuterObject, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr /* weakmapKeyDelegateOp */
},
{
nullptr, /* lookupGeneric */
nullptr, /* lookupProperty */
nullptr, /* lookupElement */
nullptr, /* defineGeneric */
nullptr, /* defineProperty */
nullptr, /* defineElement */
nullptr, /* getGeneric */
nullptr, /* getProperty */
nullptr, /* getElement */
nullptr, /* setGeneric */
nullptr, /* setProperty */
nullptr, /* setElement */
nullptr, /* getGenericAttributes */
nullptr, /* setGenericAttributes */
nullptr, /* deleteGeneric */
nullptr, /* watch */
nullptr, /* unwatch */
nullptr, /* slice */
nullptr, /* enumerate */
JS_ObjectToOuterObject /* thisObject */
}
"""
classExtensionAndObjectOps = fill(
"""
{
nsGlobalWindow::OuterObject, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
${objectMoved} /* objectMovedOp */
},
{
nullptr, /* lookupGeneric */
nullptr, /* lookupProperty */
nullptr, /* lookupElement */
nullptr, /* defineGeneric */
nullptr, /* defineProperty */
nullptr, /* defineElement */
nullptr, /* getGeneric */
nullptr, /* getProperty */
nullptr, /* getElement */
nullptr, /* setGeneric */
nullptr, /* setProperty */
nullptr, /* setElement */
nullptr, /* getGenericAttributes */
nullptr, /* setGenericAttributes */
nullptr, /* deleteGeneric */
nullptr, /* watch */
nullptr, /* unwatch */
nullptr, /* slice */
nullptr, /* enumerate */
JS_ObjectToOuterObject /* thisObject */
}
""",
objectMoved=objectMovedHook)
else:
classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
reservedSlots = slotCount
@ -481,17 +495,24 @@ class CGDOMProxyJSClass(CGThing):
# HTMLAllCollection. So just hardcode it here.
if self.descriptor.interface.identifier.name == "HTMLAllCollection":
flags.append("JSCLASS_EMULATES_UNDEFINED")
objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
return fill(
"""
static const DOMJSClass Class = {
PROXY_CLASS_DEF("${name}",
0, /* extra slots */
${flags}),
PROXY_CLASS_WITH_EXT("${name}",
0, /* extra slots */
${flags},
PROXY_MAKE_EXT(nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
${objectMoved})),
$*{descriptor}
};
""",
name=self.descriptor.interface.identifier.name,
flags=" | ".join(flags),
objectMoved=objectMovedHook,
descriptor=DOMClass(self.descriptor))
@ -1549,11 +1570,28 @@ class CGClassFinalizeHook(CGAbstractClassHook):
return finalizeHook(self.descriptor, self.name, self.args[0].name).define()
class CGClassObjectMovedHook(CGAbstractClassHook):
"""
A hook for objectMovedOp, used to update the wrapper cache when an object it
is holding moves.
"""
def __init__(self, descriptor):
args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
'void', args)
def generate_code(self):
assert self.descriptor.wrapperCache
return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
"self").define()
def JSNativeArguments():
return [Argument('JSContext*', 'cx'),
Argument('unsigned', 'argc'),
Argument('JS::Value*', 'vp')]
class CGClassConstructor(CGAbstractStaticMethod):
"""
JS-visible constructor for our objects
@ -10919,6 +10957,9 @@ class CGDescriptor(CGThing):
# wants a custom hook.
cgThings.append(CGClassFinalizeHook(descriptor))
if descriptor.concrete and descriptor.wrapperCache:
cgThings.append(CGClassObjectMovedHook(descriptor))
if len(descriptor.permissions):
for (k, v) in sorted(descriptor.permissions.items()):
perms = CGList((CGGeneric('"%s",' % p) for p in k), joiner="\n")

View File

@ -341,6 +341,7 @@ namespace mozilla {
namespace dom {
#ifdef MOZ_NUWA_PROCESS
int32_t ContentParent::sNuwaPid = 0;
bool ContentParent::sNuwaReady = false;
#endif
@ -586,6 +587,7 @@ ContentParent::RunNuwaProcess()
/* aIsNuwaProcess = */ true);
nuwaProcess->Init();
#ifdef MOZ_NUWA_PROCESS
sNuwaPid = nuwaProcess->Pid();
sNuwaReady = false;
#endif
return nuwaProcess.forget();
@ -1987,6 +1989,7 @@ ContentParent::~ContentParent()
#ifdef MOZ_NUWA_PROCESS
if (IsNuwaProcess()) {
sNuwaReady = false;
sNuwaPid = 0;
}
#endif
}
@ -3678,6 +3681,12 @@ ContentParent::DoSendAsyncMessage(JSContext* aCx,
if (aCpows && !GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return false;
}
#ifdef MOZ_NUWA_PROCESS
if (IsNuwaProcess() && IsNuwaReady()) {
// Nuwa won't receive frame messages after it is frozen.
return true;
}
#endif
return SendAsyncMessage(nsString(aMessage), data, cpows, Principal(aPrincipal));
}

View File

@ -79,6 +79,10 @@ class ContentParent MOZ_FINAL : public PContentParent
public:
#ifdef MOZ_NUWA_PROCESS
static int32_t NuwaPid() {
return sNuwaPid;
}
static bool IsNuwaReady() {
return sNuwaReady;
}
@ -712,6 +716,7 @@ private:
#endif
#ifdef MOZ_NUWA_PROCESS
static int32_t sNuwaPid;
static bool sNuwaReady;
#endif
};

View File

@ -1898,8 +1898,9 @@ TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid&
bool
TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
{
TABC_LOG("Handling single tap at %s with %p %p %d\n",
Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get(), mTouchEndCancelled);
TABC_LOG("Handling single tap at %s on %s with %p %p %d\n",
Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mGlobal.get(),
mTabChildGlobal.get(), mTouchEndCancelled);
if (!mGlobal || !mTabChildGlobal) {
return true;

View File

@ -20,7 +20,7 @@
var test = new PeerConnectionTest();
// TODO: Stop using old constraint-like RTCOptions soon (Bug 1064223).
// Watch out for case-difference when fixing: { offerToReceiveVideo: true }
test.setOfferOptions({ optional: [{ OfferToReceiveAudio: true }] });
test.setOfferOptions({ optional: [{ OfferToReceiveVideo: true }] });
test.run();
});
</script>

View File

@ -589,8 +589,15 @@ DOMStorageDBParent::Observe(const char* aTopic,
const nsACString& aScopePrefix)
{
if (mIPCOpen) {
mozilla::unused << SendObserve(nsDependentCString(aTopic),
nsCString(aScopePrefix));
#ifdef MOZ_NUWA_PROCESS
if (!(static_cast<ContentParent*>(Manager())->IsNuwaProcess() &&
ContentParent::IsNuwaReady())) {
#endif
mozilla::unused << SendObserve(nsDependentCString(aTopic),
nsCString(aScopePrefix));
#ifdef MOZ_NUWA_PROCESS
}
#endif
}
return NS_OK;

View File

@ -345,47 +345,47 @@ DOMWifiManager.prototype = {
case "WifiManager:onconnecting":
this._currentNetwork = this._convertWifiNetwork(msg.network);
this._connectionStatus = "connecting";
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:onassociate":
this._currentNetwork = this._convertWifiNetwork(msg.network);
this._connectionStatus = "associated";
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:onconnect":
this._currentNetwork = this._convertWifiNetwork(msg.network);
this._connectionStatus = "connected";
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:ondisconnect":
this._currentNetwork = null;
this._connectionStatus = "disconnected";
this._lastConnectionInfo = null;
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:onwpstimeout":
this._currentNetwork = null;
this._connectionStatus = "wps-timedout";
this._lastConnectionInfo = null;
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:onwpsfail":
this._currentNetwork = null;
this._connectionStatus = "wps-failed";
this._lastConnectionInfo = null;
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:onwpsoverlap":
this._currentNetwork = null;
this._connectionStatus = "wps-overlapped";
this._lastConnectionInfo = null;
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:connectioninfoupdate":
@ -396,12 +396,12 @@ DOMWifiManager.prototype = {
this._currentNetwork = null;
this._connectionStatus = "connectingfailed";
this._lastConnectionInfo = null;
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:onauthenticating":
this._currentNetwork = msg.network;
this._currentNetwork = this._convertWifiNetwork(msg.network);
this._connectionStatus = "authenticating";
this._fireStatusChangeEvent();
this._fireStatusChangeEvent(msg.network);
break;
case "WifiManager:stationinfoupdate":
this._stationNumber = msg.station;
@ -410,9 +410,9 @@ DOMWifiManager.prototype = {
}
},
_fireStatusChangeEvent: function StatusChangeEvent() {
_fireStatusChangeEvent: function StatusChangeEvent(aNetwork) {
var event = new this._window.MozWifiStatusChangeEvent("statuschange",
{ network: this._currentNetwork,
{ network: this._convertWifiNetwork(aNetwork),
status: this._connectionStatus
});
this.__DOM_IMPL__.dispatchEvent(event);

View File

@ -1842,6 +1842,9 @@ function WifiWorker() {
// Given a connection status network, takes a network from
// self.configuredNetworks and prepares it for the DOM.
netToDOM = function(net) {
if (!net) {
return null;
}
var ssid = dequote(net.ssid);
var security = (net.key_mgmt === "NONE" && net.wep_key0) ? ["WEP"] :
(net.key_mgmt && net.key_mgmt !== "NONE") ? [net.key_mgmt.split(" ")[0]] :
@ -2026,7 +2029,7 @@ function WifiWorker() {
WifiManager.disableNetwork(netId, function() {});
}
});
self._fireEvent("onconnectingfailed", {network: self.currentNetwork});
self._fireEvent("onconnectingfailed", {network: netToDOM(self.currentNetwork)});
}
WifiManager.onstatechange = function() {
@ -2069,7 +2072,10 @@ function WifiWorker() {
}
self.currentNetwork.netId = this.id;
WifiManager.getNetworkConfiguration(self.currentNetwork, function (){});
WifiManager.getNetworkConfiguration(self.currentNetwork, function (){
// Notify again because we get complete network information.
self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
});
break;
case "COMPLETED":
// Now that we've successfully completed the connection, re-enable the
@ -2082,27 +2088,32 @@ function WifiWorker() {
self._needToEnableNetworks = false;
}
var _oncompleted = function() {
// Update http proxy when connected to network.
let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
if (netConnect)
WifiManager.setHttpProxy(netConnect);
// The full authentication process is completed, reset the count.
WifiManager.authenticationFailuresCount = 0;
WifiManager.loopDetectionCount = 0;
self._startConnectionInfoTimer();
self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
};
// We get the ASSOCIATED event when we've associated but not connected, so
// wait until the handshake is complete.
if (this.fromStatus || !self.currentNetwork) {
// In this case, we connected to an already-connected wpa_supplicant,
// because of that we need to gather information about the current
// network here.
self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
self.currentNetwork = { bssid: WifiManager.connectionInfo.bssid,
ssid: quote(WifiManager.connectionInfo.ssid),
netId: WifiManager.connectionInfo.id };
WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
WifiManager.getNetworkConfiguration(self.currentNetwork, _oncompleted);
} else {
_oncompleted();
}
// Update http proxy when connected to network.
let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
if (netConnect)
WifiManager.setHttpProxy(netConnect);
// The full authentication process is completed, reset the count.
WifiManager.authenticationFailuresCount = 0;
WifiManager.loopDetectionCount = 0;
self._startConnectionInfoTimer();
self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
break;
case "CONNECTED":
// BSSID is read after connected, update it.
@ -2121,7 +2132,7 @@ function WifiWorker() {
return;
}
self._fireEvent("ondisconnect", {});
self._fireEvent("ondisconnect", {network: netToDOM(self.currentNetwork)});
// When disconnected, clear the http proxy setting if it exists.
// Temporarily set http proxy to empty and restore user setting after setHttpProxy.

View File

@ -152,6 +152,15 @@ AppendToString(std::stringstream& aStream, const FrameMetrics& m,
aStream << sfx;
}
void
AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
const char* pfx, const char* sfx)
{
aStream << pfx
<< nsPrintfCString("{ l=%llu, p=%u, v=%llu }", s.mLayersId, s.mPresShellId, s.mScrollId).get()
<< sfx;
}
void
AppendToString(std::stringstream& aStream, const Matrix4x4& m,
const char* pfx, const char* sfx)

View File

@ -97,6 +97,10 @@ void
AppendToString(std::stringstream& aStream, const FrameMetrics& m,
const char* pfx="", const char* sfx="", bool detailed = false);
void
AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
const char* pfx="", const char* sfx="");
template<class T>
void
AppendToString(std::stringstream& aStream, const mozilla::gfx::MarginTyped<T>& m,

View File

@ -2750,7 +2750,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
// more "legitimate" sources like content scripts.
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
if (controller) {
APZC_LOG("%p sending scroll update acknowledgement with gen %lu\n", this, aLayerMetrics.GetScrollGeneration());
APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, aLayerMetrics.GetScrollGeneration());
controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(),
aLayerMetrics.GetScrollGeneration());
}

View File

@ -292,6 +292,15 @@ public:
sf->ResetScrollInfoIfGeneration(mScrollGeneration);
}
// Since the APZ and content are in sync, we need to clear any callback transform
// that might have been set on the last repaint request (which might have failed
// due to the inflight scroll update that this message is acknowledging).
nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(mScrollId);
if (content) {
content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(),
nsINode::DeleteProperty<CSSPoint>);
}
return NS_OK;
}

View File

@ -205,7 +205,6 @@
#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0
#define SK_SUPPORT_LEGACY_GETDEVICE
#define SK_SUPPORT_LEGACY_GETTOPDEVICE
#define SK_IGNORE_ETC1_SUPPORT
#define SK_RASTERIZE_EVEN_ROUNDING

View File

@ -1106,10 +1106,9 @@ void imgLoader::GlobalInit()
int32_t cachesize;
rv = Preferences::GetInt("image.cache.size", &cachesize);
if (NS_SUCCEEDED(rv))
sCacheMaxSize = cachesize;
sCacheMaxSize = cachesize > 0 ? cachesize : 0;
else
sCacheMaxSize = 5 * 1024 * 1024;
sCacheMaxSize = sCacheMaxSize > 0 ? sCacheMaxSize : 0;
sMemReporter = new imgMemoryReporter();
RegisterStrongMemoryReporter(sMemReporter);

View File

@ -42,7 +42,7 @@ FileDescriptor::FileDescriptor(PlatformHandleType aHandle)
void
FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle)
{
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess && IsValid(),
mHandleCreatedByOtherProcessWasUsed);
if (IsValid(aHandle)) {
@ -65,7 +65,7 @@ FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle)
void
FileDescriptor::CloseCurrentProcessHandle()
{
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess && IsValid(),
mHandleCreatedByOtherProcessWasUsed);
// Don't actually close handles created by another process.

View File

@ -13,6 +13,7 @@
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/ContentParent.h"
#endif
#include "mozilla/Assertions.h"
@ -70,6 +71,9 @@ ProcessLink::ProcessLink(MessageChannel *aChan)
, mTransport(nullptr)
, mIOLoop(nullptr)
, mExistingListener(nullptr)
#ifdef MOZ_NUWA_PROCESS
, mIsToNuwaProcess(false)
#endif
{
}
@ -168,6 +172,20 @@ ProcessLink::SendMessage(Message *msg)
mChan->AssertWorkerThread();
mChan->mMonitor->AssertCurrentThreadOwns();
#ifdef MOZ_NUWA_PROCESS
if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
switch (msg->type()) {
case mozilla::dom::PContent::Msg_NuwaFork__ID:
case mozilla::dom::PContent::Reply_AddNewProcess__ID:
case mozilla::dom::PContent::Msg_NotifyPhoneStateChange__ID:
case GOODBYE_MESSAGE_TYPE:
break;
default:
MOZ_CRASH();
}
}
#endif
mIOLoop->PostTask(
FROM_HERE,
NewRunnableMethod(mTransport, &Transport::Send, msg));
@ -360,6 +378,10 @@ ProcessLink::OnChannelConnected(int32_t peer_pid)
if (mExistingListener)
mExistingListener->OnChannelConnected(peer_pid);
#ifdef MOZ_NUWA_PROCESS
mIsToNuwaProcess = (peer_pid == mozilla::dom::ContentParent::NuwaPid());
#endif
if (notifyChannel) {
mChan->OnChannelConnected(peer_pid);
}

View File

@ -170,6 +170,9 @@ class ProcessLink
Transport* mTransport;
MessageLoop* mIOLoop; // thread where IO happens
Transport::Listener* mExistingListener; // channel's previous listener
#ifdef MOZ_NUWA_PROCESS
bool mIsToNuwaProcess;
#endif
};
class ThreadLink : public MessageLink

View File

@ -393,10 +393,32 @@ class JS_PUBLIC_API(AutoAssertOnGC)
};
/*
* Disable the static rooting hazard analysis in the live region, but assert if
* any GC occurs while this guard object is live. This is most useful to help
* the exact rooting hazard analysis in complex regions, since it cannot
* understand dataflow.
* Assert if an allocation of a GC thing occurs while this class is live. This
* class does not disable the static rooting hazard analysis.
*/
class JS_PUBLIC_API(AutoAssertNoAlloc)
{
#ifdef JS_DEBUG
js::gc::GCRuntime *gc;
public:
AutoAssertNoAlloc() : gc(nullptr) {}
explicit AutoAssertNoAlloc(JSRuntime *rt);
void disallowAlloc(JSRuntime *rt);
~AutoAssertNoAlloc();
#else
public:
AutoAssertNoAlloc() {}
explicit AutoAssertNoAlloc(JSRuntime *rt) {}
void disallowAlloc(JSRuntime *rt) {}
#endif
};
/*
* Disable the static rooting hazard analysis in the live region and assert if
* any allocation that could potentially trigger a GC occurs while this guard
* object is live. This is most useful to help the exact rooting hazard analysis
* in complex regions, since it cannot understand dataflow.
*
* Note: GC behavior is unpredictable even when deterministice and is generally
* non-deterministic in practice. The fact that this guard has not
@ -406,11 +428,25 @@ class JS_PUBLIC_API(AutoAssertOnGC)
* that the hazard analysis is correct for that code, rather than relying
* on this class.
*/
class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertOnGC
class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc
{
public:
AutoSuppressGCAnalysis() : AutoAssertOnGC() {}
explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertOnGC(rt) {}
AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {}
explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertNoAlloc(rt) {}
};
/*
* Assert that code is only ever called from a GC callback, disable the static
* rooting hazard analysis and assert if any allocation that could potentially
* trigger a GC occurs while this guard object is live.
*
* This is useful to make the static analysis ignore code that runs in GC
* callbacks.
*/
class JS_PUBLIC_API(AutoAssertGCCallback) : public AutoSuppressGCAnalysis
{
public:
explicit AutoAssertGCCallback(JSObject *obj);
};
/*

View File

@ -360,6 +360,10 @@ ValidateSimdOperation(JSContext *cx, AsmJSModule::Global &global, HandleValue gl
case AsmJSSimdOperation_xor: native = simd_int32x4_xor; break;
case AsmJSSimdOperation_select: native = simd_int32x4_select; break;
case AsmJSSimdOperation_splat: native = simd_int32x4_splat; break;
case AsmJSSimdOperation_withX: native = simd_int32x4_withX; break;
case AsmJSSimdOperation_withY: native = simd_int32x4_withY; break;
case AsmJSSimdOperation_withZ: native = simd_int32x4_withZ; break;
case AsmJSSimdOperation_withW: native = simd_int32x4_withW; break;
case AsmJSSimdOperation_lessThanOrEqual:
case AsmJSSimdOperation_greaterThanOrEqual:
case AsmJSSimdOperation_notEqual:
@ -390,6 +394,10 @@ ValidateSimdOperation(JSContext *cx, AsmJSModule::Global &global, HandleValue gl
case AsmJSSimdOperation_xor: native = simd_float32x4_xor; break;
case AsmJSSimdOperation_select: native = simd_float32x4_select; break;
case AsmJSSimdOperation_splat: native = simd_float32x4_splat; break;
case AsmJSSimdOperation_withX: native = simd_float32x4_withX; break;
case AsmJSSimdOperation_withY: native = simd_float32x4_withY; break;
case AsmJSSimdOperation_withZ: native = simd_float32x4_withZ; break;
case AsmJSSimdOperation_withW: native = simd_float32x4_withW; break;
}
break;
}

View File

@ -92,7 +92,11 @@ enum AsmJSSimdOperation
AsmJSSimdOperation_or,
AsmJSSimdOperation_xor,
AsmJSSimdOperation_select,
AsmJSSimdOperation_splat
AsmJSSimdOperation_splat,
AsmJSSimdOperation_withX,
AsmJSSimdOperation_withY,
AsmJSSimdOperation_withZ,
AsmJSSimdOperation_withW
};
// These labels describe positions in the prologue/epilogue of functions while

View File

@ -1423,7 +1423,11 @@ class MOZ_STACK_CLASS ModuleCompiler
!addStandardLibrarySimdOpName("select", AsmJSSimdOperation_select) ||
!addStandardLibrarySimdOpName("splat", AsmJSSimdOperation_splat) ||
!addStandardLibrarySimdOpName("max", AsmJSSimdOperation_max) ||
!addStandardLibrarySimdOpName("min", AsmJSSimdOperation_min))
!addStandardLibrarySimdOpName("min", AsmJSSimdOperation_min) ||
!addStandardLibrarySimdOpName("withX", AsmJSSimdOperation_withX) ||
!addStandardLibrarySimdOpName("withY", AsmJSSimdOperation_withY) ||
!addStandardLibrarySimdOpName("withZ", AsmJSSimdOperation_withZ) ||
!addStandardLibrarySimdOpName("withW", AsmJSSimdOperation_withW))
{
return false;
}
@ -2478,6 +2482,18 @@ class FunctionCompiler
return ins;
}
MDefinition *insertElementSimd(MDefinition *vec, MDefinition *val, SimdLane lane, MIRType type)
{
if (inDeadCode())
return nullptr;
MOZ_ASSERT(IsSimdType(vec->type()) && vec->type() == type);
MOZ_ASSERT(!IsSimdType(val->type()));
MSimdInsertElement *ins = MSimdInsertElement::NewAsmJS(alloc(), vec, val, type, lane);
curBlock_->add(ins);
return ins;
}
MDefinition *ternarySimd(MDefinition *mask, MDefinition *lhs, MDefinition *rhs,
MSimdTernaryBitwise::Operation op, MIRType type)
{
@ -3567,6 +3583,10 @@ IsSimdValidOperationType(AsmJSSimdType type, AsmJSSimdOperation op)
case AsmJSSimdOperation_xor:
case AsmJSSimdOperation_select:
case AsmJSSimdOperation_splat:
case AsmJSSimdOperation_withX:
case AsmJSSimdOperation_withY:
case AsmJSSimdOperation_withZ:
case AsmJSSimdOperation_withW:
return true;
case AsmJSSimdOperation_mul:
case AsmJSSimdOperation_div:
@ -4353,13 +4373,21 @@ CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, boo
if (!CheckExpr(f, firstArg, &firstDef, &firstType))
return false;
bool opIsDouble = firstType.isMaybeDouble();
bool opIsInteger = firstType.isInt();
if (firstType.isMaybeDouble()) {
*type = MathRetType::Double;
firstType = Type::MaybeDouble;
} else if (firstType.isMaybeFloat()) {
*type = MathRetType::Float;
firstType = Type::MaybeFloat;
} else if (firstType.isInt()) {
*type = MathRetType::Signed;
firstType = Type::Int;
} else {
return f.failf(firstArg, "%s is not a subtype of double?, float? or int",
firstType.toChars());
}
MIRType opType = firstType.toMIRType();
if (!opIsDouble && !opIsInteger)
return f.failf(firstArg, "%s is not a subtype of double? or int", firstType.toChars());
MDefinition *lastDef = firstDef;
ParseNode *nextArg = NextNode(firstArg);
for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) {
@ -4368,15 +4396,12 @@ CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, boo
if (!CheckExpr(f, nextArg, &nextDef, &nextType))
return false;
if (opIsDouble && !nextType.isMaybeDouble())
return f.failf(nextArg, "%s is not a subtype of double?", nextType.toChars());
if (opIsInteger && !nextType.isInt())
return f.failf(nextArg, "%s is not a subtype of int", nextType.toChars());
if (!(nextType <= firstType))
return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
lastDef = f.minMax(lastDef, nextDef, opType, isMax);
}
*type = MathRetType(opIsDouble ? MathRetType::Double : MathRetType::Signed);
*def = lastDef;
return true;
}
@ -4818,6 +4843,35 @@ class CheckSimdSelectArgs
}
};
class CheckSimdVectorScalarArgs
{
Type formalType_;
public:
explicit CheckSimdVectorScalarArgs(Type t) : formalType_(t) {}
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType) const
{
MOZ_ASSERT(argIndex < 2);
if (argIndex == 0) {
// First argument is the vector
if (!(actualType <= formalType_)) {
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
formalType_.toChars());
}
return true;
}
// Second argument is the scalar
Type coercedFormalType = formalType_.simdToCoercedScalarType();
if (!(actualType <= coercedFormalType)) {
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
coercedFormalType.toChars());
}
return true;
}
};
} // anonymous namespace
template<class OpEnum>
@ -4847,6 +4901,18 @@ CheckSimdBinary<MSimdBinaryComp::Operation>(FunctionCompiler &f, ParseNode *call
return true;
}
static bool
CheckSimdWith(FunctionCompiler &f, ParseNode *call, Type retType, SimdLane lane, MDefinition **def,
Type *type)
{
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(retType), &defs))
return false;
*def = f.insertElementSimd(defs[0], defs[1], lane, retType.toMIRType());
*type = retType;
return true;
}
static bool
CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
MDefinition **def, Type *type)
@ -4889,6 +4955,15 @@ CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompile
case AsmJSSimdOperation_xor:
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::xor_, def, type);
case AsmJSSimdOperation_withX:
return CheckSimdWith(f, call, retType, SimdLane::LaneX, def, type);
case AsmJSSimdOperation_withY:
return CheckSimdWith(f, call, retType, SimdLane::LaneY, def, type);
case AsmJSSimdOperation_withZ:
return CheckSimdWith(f, call, retType, SimdLane::LaneZ, def, type);
case AsmJSSimdOperation_withW:
return CheckSimdWith(f, call, retType, SimdLane::LaneW, def, type);
case AsmJSSimdOperation_splat: {
DefinitionVector defs;
Type formalType = retType.simdToCoercedScalarType();

View File

@ -222,7 +222,7 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
* scheduled for GC). Otherwise, we collect all compartments.
*/
bool compartment = false;
if (args.length() == 1) {
if (args.length() >= 1) {
Value arg = args[0];
if (arg.isString()) {
if (!JS_StringEqualsAscii(cx, arg.toString(), "compartment", &compartment))
@ -233,6 +233,15 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
}
}
bool shrinking = false;
if (args.length() >= 2) {
Value arg = args[1];
if (arg.isString()) {
if (!JS_StringEqualsAscii(cx, arg.toString(), "shrinking", &shrinking))
return false;
}
}
#ifndef JS_MORE_DETERMINISTIC
size_t preBytes = cx->runtime()->gc.usage.gcBytes();
#endif
@ -241,7 +250,11 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
PrepareForDebugGC(cx->runtime());
else
PrepareForFullGC(cx->runtime());
GCForReason(cx->runtime(), gcreason::API);
if (shrinking)
ShrinkingGC(cx->runtime(), gcreason::API);
else
GCForReason(cx->runtime(), gcreason::API);
char buf[256] = { '\0' };
#ifndef JS_MORE_DETERMINISTIC
@ -2001,10 +2014,12 @@ IsSimdAvailable(JSContext *cx, unsigned argc, Value *vp)
static const JSFunctionSpecWithHelp TestingFunctions[] = {
JS_FN_HELP("gc", ::GC, 0, 0,
"gc([obj] | 'compartment')",
"gc([obj] | 'compartment' [, 'shrinking'])",
" Run the garbage collector. When obj is given, GC only its compartment.\n"
" If 'compartment' is given, GC any compartments that were scheduled for\n"
" GC via schedulegc."),
" GC via schedulegc.\n"
" If 'shrinking' is passes as the optional second argument, perform a\n"
" shrinking GC rather than a normal GC."),
JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
"minorgc([aboutToOverflow])",

View File

@ -2889,38 +2889,6 @@ js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp)
JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo,
js::ClampToUint8);
bool
js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(args.length() == 5);
JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
JS_ASSERT(args[1].isInt32());
JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedObject>());
JS_ASSERT(args[3].isInt32());
JS_ASSERT(args[4].isInt32());
TypedObject &targetTypedObj = args[0].toObject().as<TypedObject>();
int32_t targetOffset = args[1].toInt32();
TypedObject &sourceTypedObj = args[2].toObject().as<TypedObject>();
int32_t sourceOffset = args[3].toInt32();
int32_t size = args[4].toInt32();
JS_ASSERT(targetOffset >= 0);
JS_ASSERT(sourceOffset >= 0);
JS_ASSERT(size >= 0);
JS_ASSERT(size + targetOffset <= targetTypedObj.size());
JS_ASSERT(size + sourceOffset <= sourceTypedObj.size());
uint8_t *target = targetTypedObj.typedMem(targetOffset);
uint8_t *source = sourceTypedObj.typedMem(sourceOffset);
memcpy(target, source, size);
args.rval().setUndefined();
return true;
}
JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy);
bool
js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp)
{

View File

@ -851,20 +851,6 @@ extern const JSJitInfo TypedObjectIsAttachedJitInfo;
bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo ClampToUint8JitInfo;
/*
* Usage: Memcpy(targetDatum, targetOffset,
* sourceDatum, sourceOffset,
* size)
*
* Intrinsic function. Copies size bytes from the data for
* `sourceDatum` at `sourceOffset` into the data for
* `targetDatum` at `targetOffset`.
*
* Both `sourceDatum` and `targetDatum` must be attached.
*/
bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo MemcpyJitInfo;
/*
* Usage: GetTypedObjectModule()
*

View File

@ -195,20 +195,6 @@ function TypedObjectSet(descr, typedObj, offset, fromValue) {
if (!TypedObjectIsAttached(typedObj))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
// Fast path: `fromValue` is a typed object with same type
// representation as the destination. In that case, we can just do a
// memcpy.
if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) {
if (!descr.variable && DescrsEquiv(descr, TypedObjectTypeDescr(fromValue))) {
if (!TypedObjectIsAttached(fromValue))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
var size = DESCR_SIZE(descr);
Memcpy(typedObj, offset, fromValue, 0, size);
return;
}
}
switch (DESCR_KIND(descr)) {
case JS_TYPEREPR_SCALAR_KIND:
TypedObjectSetScalar(descr, typedObj, offset, fromValue);
@ -343,13 +329,33 @@ function TypedObjectSetReference(descr, typedObj, offset, fromValue) {
// Sets `fromValue` to `this` assuming that `this` is a scalar type.
function TypedObjectSetSimd(descr, typedObj, offset, fromValue) {
// It is only permitted to set a float32x4/int32x4 value from another
// float32x4/int32x4; in that case, the "fast path" that uses memcopy will
// have already matched. So if we get to this point, we're supposed
// to "adapt" fromValue, but there are no legal adaptions.
ThrowError(JSMSG_CANT_CONVERT_TO,
typeof(fromValue),
DESCR_STRING_REPR(descr));
if (!IsObject(fromValue) || !ObjectIsTypedObject(fromValue))
ThrowError(JSMSG_CANT_CONVERT_TO,
typeof(fromValue),
DESCR_STRING_REPR(descr));
if (!DescrsEquiv(descr, TypedObjectTypeDescr(fromValue)))
ThrowError(JSMSG_CANT_CONVERT_TO,
typeof(fromValue),
DESCR_STRING_REPR(descr));
var type = DESCR_TYPE(descr);
switch (type) {
case JS_SIMDTYPEREPR_FLOAT32:
Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0));
Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4));
Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8));
Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12));
break;
case JS_SIMDTYPEREPR_INT32:
Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0));
Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4));
Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8));
Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12));
break;
default:
assert(false, "Unhandled Simd type: " + type);
}
}
///////////////////////////////////////////////////////////////////////////

View File

@ -222,6 +222,7 @@ function isRootedPointerTypeName(name)
function isSuppressConstructor(name)
{
return name.indexOf("::AutoSuppressGC") != -1
|| name.indexOf("::AutoAssertGCCallback") != -1
|| name.indexOf("::AutoEnterAnalysis") != -1
|| name.indexOf("::AutoSuppressGCAnalysis") != -1
|| name.indexOf("::AutoIgnoreRootingHazards") != -1;

View File

@ -138,7 +138,7 @@ CheckHashTablesAfterMovingGC(JSRuntime *rt);
#ifdef JSGC_COMPACTING
struct MovingTracer : JSTracer {
MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapValues) {}
MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {}
static void Visit(JSTracer *jstrc, void **thingp, JSGCTraceKind kind);
static void Sweep(JSTracer *jstrc);

View File

@ -624,6 +624,8 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
inline ArenaHeader *getNextAllocDuringSweep() const;
inline void setNextAllocDuringSweep(ArenaHeader *aheader);
inline void unsetAllocDuringSweep();
void unmarkAll();
};
struct Arena

View File

@ -66,7 +66,10 @@ for (n of [0, 1, 2, 15, 16, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1,
assertEq(f(n), Math.clz32(n|0));
var doubleNumbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
var floatNumbers = [];
for (var x of doubleNumbers) floatNumbers.push(Math.fround(x));
var intNumbers = [-10000, -3, -1, 0, 3, 10000];
function testBinary(f, g, numbers) {
for (n of numbers)
for (o of numbers)
@ -82,7 +85,6 @@ assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2, doubleNumbers);
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; return +min(d) } return f');
assertAsmTypeFail('glob', USE_ASM + 'var f32=glob.Math.fround; var min=glob.Math.min; function f(d) { d=f32(d); return +min(d, f32(5)) } return f');
assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + 'var i32=new glob.Int32Array(heap); var min=glob.Math.min; function f() { return min(i32[0], 5)|0 } return f');
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(3 + x, 5)|0 } return f');
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(5, 3 + x)|0 } return f');
@ -90,11 +92,13 @@ assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.sin}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:null}});
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(min(d,e)) } return f'), this), Math.min, floatNumbers);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d,e)|0} return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.sin}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:null}});
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(max(d,e)) } return f'), this), Math.max, floatNumbers);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d,e)|0} return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
function testTernary(f, g, numbers) {
@ -110,8 +114,10 @@ assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) {
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d,e,g)|0 } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=+d;e=+e;g=+g; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(max(d,e,g)) } return f'), this), Math.max, floatNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d,e,g)|0 } return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return +min(d,e,g) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(min(d,e,g)) } return f'), this), Math.min, floatNumbers);
// Implicit return coercions of math functions
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; i = im(i,i); return i|0 } return f'), this)(3), 9);
@ -134,7 +140,7 @@ assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; d = min(d, 13.); return +d } return f'), this)(12), 12);
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=d|0; d = min(d, 11); return d|0 } return f'), this)(12), 11);
assertAsmTypeFail('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(d) { d=fround(d); d = max(d) } return f');
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); d = min(d, fround(13.37)); return fround(d) } return f'), this)(14), Math.fround(13.37));
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); return +d } return f'), this)(Math.PI), Math.sin(Math.PI));
assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sin=glob.Math.sin; function f(d) { d=fround(d); d = sin(d) } return f');
@ -153,9 +159,13 @@ assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; var d=0.0; d = +im(i,i); return +d } return f'), this)(42), Math.imul(42, 42));
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; d = +abs(i|0); return +d } return f'), this)(-42), 42);
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(i) { i=i|0; var d=0.0; d = +min(i, 0); return +d } return f'), this)(-42), -42);
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(i) { i=i|0; var d=fround(0); d = fround(min(i, 0)); return +d } return f'), this)(-42), -42);
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(i) { i=i|0; var d=0.0; d = +max(i, 0); return +d } return f'), this)(-42), 0);
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(i) { i=i|0; var d=fround(0); d = fround(max(i, 0)); return +d } return f'), this)(-42), 0);
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; var i=0; i = ~~min(d, 0.)|0; return i|0 } return f'), this)(-42), -42);
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); var i=0; i = ~~min(d, fround(0))|0; return i|0 } return f'), this)(-42), -42);
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d) { d=+d; var i=0; i = ~~max(d, 0.)|0; return i|0 } return f'), this)(-42), 0);
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(d) { d=fround(d); var i=0; i = ~~max(d, fround(0))|0; return i|0 } return f'), this)(-42), 0);
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; return +d; +abs(i|0); return 3.0;} return f'), this)(-42), 0);

View File

@ -477,6 +477,35 @@ var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
var another = SIMD.float32x4(0, -0, 0, 0);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
// With
const WXF = 'var w = f4.withX;';
const WYF = 'var w = f4.withY;';
const WZF = 'var w = f4.withZ;';
const WWF = 'var w = f4.withW;';
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1.0);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, x);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1, f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1., f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(f32(1), f32(1));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); var y = i4(1,2,3,4); x = w(y, f32(1));} return f");
CheckF4(WXF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [Math.fround(13.37), 2, 3, 4]);
CheckF4(WYF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, Math.fround(13.37), 3, 4]);
CheckF4(WZF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, Math.fround(13.37), 4]);
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, 3, Math.fround(13.37)]);
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37) + f32(6.63));', [1, 2, 3, Math.fround(Math.fround(13.37) + Math.fround(6.63))]);
const WXI = 'var w = i4.withX;';
const WYI = 'var w = i4.withY;';
const WZI = 'var w = i4.withZ;';
const WWI = 'var w = i4.withW;';
CheckI4(WXI, 'var x = i4(1,2,3,4); x = w(x, 42);', [42, 2, 3, 4]);
CheckI4(WYI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 42, 3, 4]);
CheckI4(WZI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 42, 4]);
CheckI4(WWI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 3, 42]);
// Comparisons
// True yields all bits set to 1 (i.e as an int32, 0xFFFFFFFF === -1), false
// yields all bits set to 0 (i.e 0).

View File

@ -461,6 +461,14 @@ function rmin_number(i) {
return i;
}
var uceFault_min_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_float'));
function rmin_float(i) {
var x = Math.fround(Math.min(Math.fround(20), Math.fround(13.37)));
if (uceFault_min_number(i) || uceFault_min_number(i))
assertEq(x, Math.fround(13.37));
return i;
}
var uceFault_min_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_object'));
function rmin_object(i) {
var t = i;
@ -480,6 +488,14 @@ function rmax_number(i) {
return i;
}
var uceFault_max_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_float'));
function rmax_float(i) {
var x = Math.fround(Math.max(Math.fround(2), Math.fround(13.37)));
if (uceFault_max_number(i) || uceFault_max_number(i))
assertEq(x, Math.fround(13.37));
return i;
}
var uceFault_max_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_object'));
function rmax_object(i) {
var t = i;
@ -975,8 +991,10 @@ for (i = 0; i < 100; i++) {
rpowhalf_number(i);
rpowhalf_object(i);
rmin_number(i);
rmin_float(i);
rmin_object(i);
rmax_number(i);
rmax_float(i);
rmax_object(i);
rabs_number(i);
rabs_object(i);

View File

@ -89,6 +89,38 @@ function sqrt() {
}
test(setupSqrt, sqrt);
// MMinMax
function setupMinMax() {
f32[0] = -0;
f32[1] = 0;
f32[2] = 1;
f32[3] = 4;
f32[4] = -1;
f32[5] = Infinity;
f32[6] = NaN;
f32[7] = 13.37;
f32[8] = -Infinity;
f32[9] = Math.pow(2,31) - 1;
}
function minMax() {
for(var i = 0; i < 9; ++i) {
for(var j = 0; j < 9; j++) {
var minf = Math.fround(Math.min(f32[i], f32[j]));
var mind = 1 / (1 / Math.min(f32[i], f32[j])); // force no float32 by chaining arith ops
assertFloat32(minf, true);
assertFloat32(mind, false);
assertEq( minf, Math.fround(mind) );
var maxf = Math.fround(Math.max(f32[i], f32[j]));
var maxd = 1 / (1 / Math.max(f32[i], f32[j])); // force no float32 by chaining arith ops
assertFloat32(maxf, true);
assertFloat32(maxd, false);
assertEq( maxf, Math.fround(maxd) );
}
}
}
test(setupMinMax, minMax);
// MTruncateToInt32
// The only way to get a MTruncateToInt32 with a Float32 input is to use Math.imul
function setupTruncateToInt32() {

View File

@ -61,8 +61,8 @@ function test(f) {
f();
}
var f32 = new Float32Array(2);
var f64 = new Float64Array(2);
var f32 = new Float32Array(4);
var f64 = new Float64Array(4);
function acceptAdd() {
var use = f32[0] + 1;
@ -151,6 +151,79 @@ function refuseSqrt() {
}
test(refuseSqrt);
function acceptMin() {
var res = Math.min(f32[0], f32[1]);
assertFloat32(res, true);
f64[0] = res;
}
test(acceptMin);
// In theory, we could do it, as Math.min/max actually behave as a Phi (it's a
// float32 producer iff its inputs are producers, it's a consumer iff its uses
// are consumers). In practice, this would involve some overhead for big chains
// of min/max.
function refuseMinAdd() {
var res = Math.min(f32[0], f32[1]) + f32[2];
assertFloat32(res, false);
f32[3] = res;
}
test(refuseMinAdd);
function acceptSeveralMinMax() {
var x = Math.min(f32[0], f32[1]);
var y = Math.max(f32[2], f32[3]);
var res = Math.min(x, y);
assertFloat32(res, true);
f64[0] = res;
}
test(acceptSeveralMinMax);
function acceptSeveralMinMax2() {
var res = Math.min(f32[0], f32[1], f32[2], f32[3]);
assertFloat32(res, true);
f64[0] = res;
}
test(acceptSeveralMinMax2);
function partialMinMax() {
var x = Math.min(f32[0], f32[1]);
var y = Math.min(f64[0], f32[1]);
var res = Math.min(x, y);
assertFloat32(x, true);
assertFloat32(y, false);
assertFloat32(res, false);
f64[0] = res;
}
test(partialMinMax);
function refuseSeveralMinMax() {
var res = Math.min(f32[0], f32[1] + f32[2], f32[2], f32[3]);
assertFloat32(res, false);
f64[0] = res;
}
test(refuseSeveralMinMax);
function refuseMin() {
var res = Math.min(f32[0], 42.13 + f32[1]);
assertFloat32(res, false);
f64[0] = res;
}
test(refuseMin);
function acceptMax() {
var res = Math.max(f32[0], f32[1]);
assertFloat32(res, true);
f64[0] = res;
}
test(acceptMax);
function refuseMax() {
var res = Math.max(f32[0], 42.13 + f32[1]);
assertFloat32(res, false);
f64[0] = res;
}
test(refuseMax);
function acceptAbs() {
var res = Math.abs(f32[0]);
assertFloat32(res, true);

View File

@ -5,9 +5,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/JitOptions.h"
#include "mozilla/TypeTraits.h"
#include <cstdlib>
#include "jsfun.h"
using namespace js;
using namespace js::jit;
@ -16,58 +17,87 @@ namespace jit {
JitOptions js_JitOptions;
template<typename T> struct IsBool : mozilla::FalseType {};
template<> struct IsBool<bool> : mozilla::TrueType {};
template<typename T>
T overrideDefault(const char *param, T dflt) {
char *str = getenv(param);
if (!str)
return dflt;
if (IsBool<T>::value) {
if (strcmp(str, "true") == 0 ||
strcmp(str, "yes")) {
return true;
}
if (strcmp(str, "false") == 0 ||
strcmp(str, "no")) {
return false;
}
fprintf(stderr, "Warning: I didn't understand %s=\"%s\"", param, str);
} else {
char *endp;
int retval = strtol(str, &endp, 0);
if (*endp == '\0')
return retval;
fprintf(stderr, "Warning: I didn't understand %s=\"%s\"", param, str);
}
return dflt;
}
#define SET_DEFAULT(var, dflt) var = overrideDefault("JIT_OPTION_" #var, dflt)
JitOptions::JitOptions()
{
// Whether to perform expensive graph-consistency DEBUG-only assertions.
// It can be useful to disable this to reduce DEBUG-compile time of large
// asm.js programs.
checkGraphConsistency = true;
SET_DEFAULT(checkGraphConsistency, true);
#ifdef CHECK_OSIPOINT_REGISTERS
// Emit extra code to verify live regs at the start of a VM call
// are not modified before its OsiPoint.
checkOsiPointRegisters = false;
SET_DEFAULT(checkOsiPointRegisters, false);
#endif
// Whether to enable extra code to perform dynamic validation of
// RangeAnalysis results.
checkRangeAnalysis = false;
SET_DEFAULT(checkRangeAnalysis, false);
// Whether Ion should compile try-catch statements.
compileTryCatch = true;
SET_DEFAULT(compileTryCatch, true);
// Toggle whether eager scalar replacement is globally disabled.
disableScalarReplacement = true; // experimental
SET_DEFAULT(disableScalarReplacement, true); // experimental
// Toggle whether global value numbering is globally disabled.
disableGvn = false;
SET_DEFAULT(disableGvn, false);
// Toggles whether loop invariant code motion is globally disabled.
disableLicm = false;
SET_DEFAULT(disableLicm, false);
// Toggles whether inlining is globally disabled.
disableInlining = false;
SET_DEFAULT(disableInlining, false);
// Toggles whether Edge Case Analysis is gobally disabled.
disableEdgeCaseAnalysis = false;
SET_DEFAULT(disableEdgeCaseAnalysis, false);
// Toggles whether Range Analysis is globally disabled.
disableRangeAnalysis = false;
SET_DEFAULT(disableRangeAnalysis, false);
// Toggles whether Loop Unrolling is globally disabled.
disableLoopUnrolling = true;
SET_DEFAULT(disableLoopUnrolling, true);
// Toggles whether Effective Address Analysis is globally disabled.
disableEaa = false;
SET_DEFAULT(disableEaa, false);
// Whether functions are compiled immediately.
eagerCompilation = false;
SET_DEFAULT(eagerCompilation, false);
// Force how many invocation or loop iterations are needed before compiling
// a function with the highest ionmonkey optimization level.
// (i.e. OptimizationLevel_Normal)
forceDefaultIonWarmUpThreshold = false;
forcedDefaultIonWarmUpThreshold = 1000;
SET_DEFAULT(forceDefaultIonWarmUpThreshold, false);
SET_DEFAULT(forcedDefaultIonWarmUpThreshold, 1000);
// Force the used register allocator instead of letting the
// optimization pass decide.
@ -75,39 +105,39 @@ JitOptions::JitOptions()
forcedRegisterAllocator = RegisterAllocator_LSRA;
// Toggles whether large scripts are rejected.
limitScriptSize = true;
SET_DEFAULT(limitScriptSize, true);
// Toggles whether functions may be entered at loop headers.
osr = true;
SET_DEFAULT(osr, true);
// How many invocations or loop iterations are needed before functions
// are compiled with the baseline compiler.
baselineWarmUpThreshold = 10;
SET_DEFAULT(baselineWarmUpThreshold, 10);
// Number of exception bailouts (resuming into catch/finally block) before
// we invalidate and forbid Ion compilation.
exceptionBailoutThreshold = 10;
SET_DEFAULT(exceptionBailoutThreshold, 10);
// Number of bailouts without invalidation before we set
// JSScript::hadFrequentBailouts and invalidate.
frequentBailoutThreshold = 10;
SET_DEFAULT(frequentBailoutThreshold, 10);
// How many actual arguments are accepted on the C stack.
maxStackArgs = 4096;
SET_DEFAULT(maxStackArgs, 4096);
// How many times we will try to enter a script via OSR before
// invalidating the script.
osrPcMismatchesBeforeRecompile = 6000;
SET_DEFAULT(osrPcMismatchesBeforeRecompile, 6000);
// The bytecode length limit for small function.
//
// The default for this was arrived at empirically via benchmarking.
// We may want to tune it further after other optimizations have gone
// in.
smallFunctionMaxBytecodeLength_ = 100;
SET_DEFAULT(smallFunctionMaxBytecodeLength_, 100);
// How many uses of a parallel kernel before we attempt compilation.
compilerWarmUpThresholdPar = 1;
SET_DEFAULT(compilerWarmUpThresholdPar, 1);
}
bool

View File

@ -143,42 +143,80 @@ class LSimdSplatX4 : public LInstructionHelper<1, 1, 0>
}
};
// Extracts an element from a given SIMD int32x4 lane.
class LSimdExtractElementI : public LInstructionHelper<1, 1, 0>
class LSimdExtractElementBase : public LInstructionHelper<1, 1, 0>
{
SimdLane lane_;
public:
LIR_HEADER(SimdExtractElementI);
LSimdExtractElementI(const LAllocation &base, SimdLane lane) : lane_(lane) {
protected:
LSimdExtractElementBase(const LAllocation &base) {
setOperand(0, base);
}
public:
const LAllocation *getBase() {
return getOperand(0);
}
SimdLane lane() const {
return lane_;
return mir_->toSimdExtractElement()->lane();
}
};
// Extracts an element from a given SIMD float32x4 lane.
class LSimdExtractElementF : public LInstructionHelper<1, 1, 0>
// Extracts an element from a given SIMD int32x4 lane.
class LSimdExtractElementI : public LSimdExtractElementBase
{
public:
LIR_HEADER(SimdExtractElementI);
LSimdExtractElementI(const LAllocation &base)
: LSimdExtractElementBase(base)
{}
};
// Extracts an element from a given SIMD float32x4 lane.
class LSimdExtractElementF : public LSimdExtractElementBase
{
SimdLane lane_;
public:
LIR_HEADER(SimdExtractElementF);
LSimdExtractElementF(const LAllocation &base)
: LSimdExtractElementBase(base)
{}
};
LSimdExtractElementF(const LAllocation &base, SimdLane lane) : lane_(lane) {
setOperand(0, base);
class LSimdInsertElementBase : public LInstructionHelper<1, 2, 0>
{
protected:
LSimdInsertElementBase(const LAllocation &vec, const LAllocation &val)
{
setOperand(0, vec);
setOperand(1, val);
}
const LAllocation *getBase() {
public:
const LAllocation *vector() {
return getOperand(0);
}
SimdLane lane() const {
return lane_;
const LAllocation *value() {
return getOperand(1);
}
const SimdLane lane() const {
return mir_->toSimdInsertElement()->lane();
}
};
// Replace an element from a given SIMD int32x4 lane with a given value.
class LSimdInsertElementI : public LSimdInsertElementBase
{
public:
LIR_HEADER(SimdInsertElementI);
LSimdInsertElementI(const LAllocation &vec, const LAllocation &val)
: LSimdInsertElementBase(vec, val)
{}
};
// Replace an element from a given SIMD float32x4 lane with a given value.
class LSimdInsertElementF : public LSimdInsertElementBase
{
public:
LIR_HEADER(SimdInsertElementF);
LSimdInsertElementF(const LAllocation &vec, const LAllocation &val)
: LSimdInsertElementBase(vec, val)
{}
};
class LSimdSignMaskX4 : public LInstructionHelper<1, 1, 0>
@ -2656,16 +2694,16 @@ class LThrow : public LCallInstructionHelper<0, BOX_PIECES, 0>
static const size_t Value = 0;
};
class LMinMaxI : public LInstructionHelper<1, 2, 0>
class LMinMaxBase : public LInstructionHelper<1, 2, 0>
{
public:
LIR_HEADER(MinMaxI)
LMinMaxI(const LAllocation &first, const LAllocation &second)
protected:
LMinMaxBase(const LAllocation &first, const LAllocation &second)
{
setOperand(0, first);
setOperand(1, second);
}
public:
const LAllocation *first() {
return this->getOperand(0);
}
@ -2683,31 +2721,28 @@ class LMinMaxI : public LInstructionHelper<1, 2, 0>
}
};
class LMinMaxD : public LInstructionHelper<1, 2, 0>
class LMinMaxI : public LMinMaxBase
{
public:
LIR_HEADER(MinMaxI)
LMinMaxI(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
{}
};
class LMinMaxD : public LMinMaxBase
{
public:
LIR_HEADER(MinMaxD)
LMinMaxD(const LAllocation &first, const LAllocation &second)
{
setOperand(0, first);
setOperand(1, second);
}
LMinMaxD(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
{}
};
const LAllocation *first() {
return this->getOperand(0);
}
const LAllocation *second() {
return this->getOperand(1);
}
const LDefinition *output() {
return this->getDef(0);
}
MMinMax *mir() const {
return mir_->toMinMax();
}
const char *extraName() const {
return mir()->isMax() ? "Max" : "Min";
}
class LMinMaxF : public LMinMaxBase
{
public:
LIR_HEADER(MinMaxF)
LMinMaxF(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
{}
};
// Negative of an integer

View File

@ -21,6 +21,8 @@
_(Float32x4) \
_(SimdExtractElementI) \
_(SimdExtractElementF) \
_(SimdInsertElementI) \
_(SimdInsertElementF) \
_(SimdSignMaskX4) \
_(SimdBinaryCompIx4) \
_(SimdBinaryCompFx4) \
@ -115,6 +117,7 @@
_(EmulatesUndefinedAndBranch) \
_(MinMaxI) \
_(MinMaxD) \
_(MinMaxF) \
_(NegI) \
_(NegD) \
_(NegF) \

View File

@ -1288,6 +1288,12 @@ LIRGenerator::visitMinMax(MMinMax *ins)
return defineReuseInput(lir, ins, 0);
}
if (ins->specialization() == MIRType_Float32) {
LMinMaxF *lir = new(alloc()) LMinMaxF(useRegisterAtStart(first), useRegister(second));
return defineReuseInput(lir, ins, 0);
}
MOZ_ASSERT(ins->specialization() == MIRType_Double);
LMinMaxD *lir = new(alloc()) LMinMaxD(useRegisterAtStart(first), useRegister(second));
return defineReuseInput(lir, ins, 0);
}
@ -3721,17 +3727,32 @@ LIRGenerator::visitSimdExtractElement(MSimdExtractElement *ins)
// Note: there could be int16x8 in the future, which doesn't use the
// same instruction. We either need to pass the arity or create new LIns.
LUse use = useRegisterAtStart(ins->input());
return define(new(alloc()) LSimdExtractElementI(use, ins->lane()), ins);
return define(new(alloc()) LSimdExtractElementI(use), ins);
}
if (ins->input()->type() == MIRType_Float32x4) {
LUse use = useRegisterAtStart(ins->input());
return define(new(alloc()) LSimdExtractElementF(use, ins->lane()), ins);
return define(new(alloc()) LSimdExtractElementF(use), ins);
}
MOZ_CRASH("Unknown SIMD kind when extracting element");
}
bool
LIRGenerator::visitSimdInsertElement(MSimdInsertElement *ins)
{
JS_ASSERT(IsSimdType(ins->type()));
LUse vec = useRegisterAtStart(ins->vector());
LUse val = useRegister(ins->value());
if (ins->type() == MIRType_Int32x4)
return defineReuseInput(new(alloc()) LSimdInsertElementI(vec, val), ins, 0);
if (ins->type() == MIRType_Float32x4)
return defineReuseInput(new(alloc()) LSimdInsertElementF(vec, val), ins, 0);
MOZ_CRASH("Unknown SIMD kind when generating constant");
}
bool
LIRGenerator::visitSimdSignMask(MSimdSignMask *ins)
{

View File

@ -269,6 +269,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitGetDOMMember(MGetDOMMember *ins);
bool visitRecompileCheck(MRecompileCheck *ins);
bool visitSimdExtractElement(MSimdExtractElement *ins);
bool visitSimdInsertElement(MSimdInsertElement *ins);
bool visitSimdSignMask(MSimdSignMask *ins);
bool visitSimdBinaryComp(MSimdBinaryComp *ins);
bool visitSimdBinaryArith(MSimdBinaryArith *ins);

View File

@ -1698,8 +1698,31 @@ MBinaryArithInstruction::trySpecializeFloat32(TempAllocator &alloc)
MDefinition *left = lhs();
MDefinition *right = rhs();
if (!left->canProduceFloat32() || !right->canProduceFloat32()
|| !CheckUsesAreFloat32Consumers(this))
if (!left->canProduceFloat32() || !right->canProduceFloat32() ||
!CheckUsesAreFloat32Consumers(this))
{
if (left->type() == MIRType_Float32)
ConvertDefinitionToDouble<0>(alloc, left, this);
if (right->type() == MIRType_Float32)
ConvertDefinitionToDouble<1>(alloc, right, this);
return;
}
specialization_ = MIRType_Float32;
setResultType(MIRType_Float32);
}
void
MMinMax::trySpecializeFloat32(TempAllocator &alloc)
{
if (specialization_ == MIRType_Int32)
return;
MDefinition *left = lhs();
MDefinition *right = rhs();
if (!(left->canProduceFloat32() || (left->isMinMax() && left->type() == MIRType_Float32)) ||
!(right->canProduceFloat32() || (right->isMinMax() && right->type() == MIRType_Float32)))
{
if (left->type() == MIRType_Float32)
ConvertDefinitionToDouble<0>(alloc, left, this);

View File

@ -1402,6 +1402,50 @@ class MSimdExtractElement : public MUnaryInstruction
}
};
// Replaces the datum in the given lane by a scalar value of the same type.
class MSimdInsertElement : public MBinaryInstruction
{
private:
SimdLane lane_;
MSimdInsertElement(MDefinition *vec, MDefinition *val, MIRType type, SimdLane lane)
: MBinaryInstruction(vec, val), lane_(lane)
{
MOZ_ASSERT(IsSimdType(type) && vec->type() == type);
MOZ_ASSERT(SimdTypeToScalarType(type) == val->type());
setMovable();
setResultType(type);
}
public:
INSTRUCTION_HEADER(SimdInsertElement)
static MSimdInsertElement *NewAsmJS(TempAllocator &alloc, MDefinition *vec, MDefinition *val,
MIRType type, SimdLane lane)
{
return new(alloc) MSimdInsertElement(vec, val, type, lane);
}
MDefinition *vector() {
return getOperand(0);
}
MDefinition *value() {
return getOperand(1);
}
SimdLane lane() const {
return lane_;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
bool congruentTo(const MDefinition *ins) const {
return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
}
};
// Extracts the sign bits from a given vector, returning an MIRType_Int32.
class MSimdSignMask : public MUnaryInstruction
{
@ -4539,7 +4583,7 @@ class MMinMax
: MBinaryInstruction(left, right),
isMax_(isMax)
{
JS_ASSERT(type == MIRType_Double || type == MIRType_Int32);
JS_ASSERT(IsNumberType(type));
setResultType(type);
setMovable();
specialization_ = type;
@ -4574,6 +4618,9 @@ class MMinMax
return true;
}
bool isFloat32Commutative() const { return true; }
void trySpecializeFloat32(TempAllocator &alloc);
ALLOW_CLONE(MMinMax)
};

View File

@ -16,6 +16,7 @@ namespace jit {
_(SimdSplatX4) \
_(SimdConstant) \
_(SimdExtractElement) \
_(SimdInsertElement) \
_(SimdSignMask) \
_(SimdBinaryComp) \
_(SimdBinaryArith) \

View File

@ -115,6 +115,7 @@ class ParallelSafetyVisitor : public MDefinitionVisitor
SAFE_OP(SimdSplatX4)
SAFE_OP(SimdConstant)
SAFE_OP(SimdExtractElement)
SAFE_OP(SimdInsertElement)
SAFE_OP(SimdSignMask)
SAFE_OP(SimdBinaryComp)
SAFE_OP(SimdBinaryArith)

View File

@ -290,6 +290,55 @@ CodeGeneratorARM::visitMinMaxD(LMinMaxD *ins)
return true;
}
bool
CodeGeneratorARM::visitMinMaxF(LMinMaxF *ins)
{
FloatRegister first = ToFloatRegister(ins->first());
FloatRegister second = ToFloatRegister(ins->second());
FloatRegister output = ToFloatRegister(ins->output());
JS_ASSERT(first == output);
Assembler::Condition cond = ins->mir()->isMax()
? Assembler::VFP_LessThanOrEqual
: Assembler::VFP_GreaterThanOrEqual;
Label nan, equal, returnSecond, done;
masm.compareFloat(first, second);
// First or second is NaN, result is NaN.
masm.ma_b(&nan, Assembler::VFP_Unordered);
// Make sure we handle -0 and 0 right.
masm.ma_b(&equal, Assembler::VFP_Equal);
masm.ma_b(&returnSecond, cond);
masm.ma_b(&done);
// Check for zero.
masm.bind(&equal);
masm.compareFloat(first, NoVFPRegister);
// First wasn't 0 or -0, so just return it.
masm.ma_b(&done, Assembler::VFP_NotEqualOrUnordered);
// So now both operands are either -0 or 0.
if (ins->mir()->isMax()) {
// -0 + -0 = -0 and -0 + 0 = 0.
masm.ma_vadd_f32(second, first, first);
} else {
masm.ma_vneg_f32(first, first);
masm.ma_vsub_f32(first, second, first);
masm.ma_vneg_f32(first, first);
}
masm.ma_b(&done);
masm.bind(&nan);
masm.loadConstantFloat32(GenericNaN(), output);
masm.ma_b(&done);
masm.bind(&returnSecond);
masm.ma_vmov_f32(second, output);
masm.bind(&done);
return true;
}
bool
CodeGeneratorARM::visitAbsD(LAbsD *ins)
{

View File

@ -102,6 +102,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
public:
// Instruction visitors.
virtual bool visitMinMaxD(LMinMaxD *ins);
virtual bool visitMinMaxF(LMinMaxF *ins);
virtual bool visitAbsD(LAbsD *ins);
virtual bool visitAbsF(LAbsF *ins);
virtual bool visitSqrtD(LSqrtD *ins);

View File

@ -525,7 +525,7 @@ class AssemblerX86Shared : public AssemblerShared
}
}
// movsd and movss are only provided in load/store form since the
// movsd is only provided in load/store form since the
// register-to-register form has different semantics (it doesn't clobber
// the whole output register) and isn't needed currently.
void movsd(const Address &src, FloatRegister dest) {
@ -540,6 +540,10 @@ class AssemblerX86Shared : public AssemblerShared
void movsd(FloatRegister src, const BaseIndex &dest) {
masm.movsd_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale);
}
// Although movss is not only provided in load/store form (for the same
// reasons as movsd above), the register to register form should be only
// used in contexts where we care about not clearing the higher lanes of
// the FloatRegister.
void movss(const Address &src, FloatRegister dest) {
masm.movss_mr(src.offset, src.base.code(), dest.code());
}
@ -552,6 +556,9 @@ class AssemblerX86Shared : public AssemblerShared
void movss(FloatRegister src, const BaseIndex &dest) {
masm.movss_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale);
}
void movss(FloatRegister src, const FloatRegister &dest) {
masm.movss_rr(src.code(), dest.code());
}
void movdqu(const Operand &src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
switch (src.kind()) {
@ -1412,11 +1419,11 @@ class AssemblerX86Shared : public AssemblerShared
masm.unpcklps_rr(src.code(), dest.code());
}
void pinsrd(unsigned lane, Register src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
JS_ASSERT(HasSSE41());
masm.pinsrd_irr(lane, src.code(), dest.code());
}
void pinsrd(unsigned lane, const Operand &src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
JS_ASSERT(HasSSE41());
switch (src.kind()) {
case Operand::REG:
masm.pinsrd_irr(lane, src.reg(), dest.code());
@ -1931,6 +1938,10 @@ class AssemblerX86Shared : public AssemblerShared
JS_ASSERT(HasSSE2());
masm.orpd_rr(src.code(), dest.code());
}
void orps(FloatRegister src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.orps_rr(src.code(), dest.code());
}
void andpd(FloatRegister src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.andpd_rr(src.code(), dest.code());
@ -1947,18 +1958,29 @@ class AssemblerX86Shared : public AssemblerShared
JS_ASSERT(HasSSE2());
masm.sqrtss_rr(src.code(), dest.code());
}
void roundsd(FloatRegister src, FloatRegister dest,
X86Assembler::RoundingMode mode)
{
void roundsd(FloatRegister src, FloatRegister dest, X86Assembler::RoundingMode mode) {
JS_ASSERT(HasSSE41());
masm.roundsd_rr(src.code(), dest.code(), mode);
}
void roundss(FloatRegister src, FloatRegister dest,
X86Assembler::RoundingMode mode)
{
void roundss(FloatRegister src, FloatRegister dest, X86Assembler::RoundingMode mode) {
JS_ASSERT(HasSSE41());
masm.roundss_rr(src.code(), dest.code(), mode);
}
unsigned insertpsMask(SimdLane sourceLane, SimdLane destLane, unsigned zeroMask = 0)
{
// Note that the sourceLane bits are ignored in the case of a source
// memory operand, and the source is the given 32-bits memory location.
MOZ_ASSERT(zeroMask < 16);
unsigned ret = zeroMask ;
ret |= unsigned(destLane) << 4;
ret |= unsigned(sourceLane) << 6;
MOZ_ASSERT(ret < 256);
return ret;
}
void insertps(FloatRegister src, FloatRegister dest, unsigned mask) {
JS_ASSERT(HasSSE41());
masm.insertps_irr(mask, src.code(), dest.code());
}
void minsd(FloatRegister src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.minsd_rr(src.code(), dest.code());
@ -1976,6 +1998,10 @@ class AssemblerX86Shared : public AssemblerShared
MOZ_CRASH("unexpected operand kind");
}
}
void minss(FloatRegister src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.minss_rr(src.code(), dest.code());
}
void maxsd(FloatRegister src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.maxsd_rr(src.code(), dest.code());
@ -1993,6 +2019,10 @@ class AssemblerX86Shared : public AssemblerShared
MOZ_CRASH("unexpected operand kind");
}
}
void maxss(FloatRegister src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.maxss_rr(src.code(), dest.code());
}
void fisttp(const Operand &dest) {
JS_ASSERT(HasSSE3());
switch (dest.kind()) {

View File

@ -308,10 +308,12 @@ private:
OP2_SUBSD_VsdWsd = 0x5C,
OP2_SUBPS_VpsWps = 0x5C,
OP2_MINSD_VsdWsd = 0x5D,
OP2_MINSS_VssWss = 0x5D,
OP2_MINPS_VpsWps = 0x5D,
OP2_DIVSD_VsdWsd = 0x5E,
OP2_DIVPS_VpsWps = 0x5E,
OP2_MAXSD_VsdWsd = 0x5F,
OP2_MAXSS_VssWss = 0x5F,
OP2_MAXPS_VpsWps = 0x5F,
OP2_SQRTSD_VsdWsd = 0x51,
OP2_SQRTSS_VssWss = 0x51,
@ -350,13 +352,15 @@ private:
OP3_ROUNDSS_VsdWsd = 0x0A,
OP3_ROUNDSD_VsdWsd = 0x0B,
OP3_PTEST_VdVd = 0x17,
OP3_INSERTPS_VpsUps = 0x21,
OP3_PINSRD_VdqEdIb = 0x22
} ThreeByteOpcodeID;
typedef enum {
ESCAPE_PTEST = 0x38,
ESCAPE_PINSRD = 0x3A,
ESCAPE_ROUNDSD = 0x3A
ESCAPE_ROUNDSD = 0x3A,
ESCAPE_INSERTPS = 0x3A
} ThreeByteEscape;
TwoByteOpcodeID jccRel32(Condition cond)
@ -3602,6 +3606,16 @@ public:
m_formatter.immediate8(mode); // modes are the same for roundsd and roundss
}
void insertps_irr(unsigned mask, XMMRegisterID src, XMMRegisterID dst)
{
MOZ_ASSERT(mask < 256);
spew("insertps $%u, %s, %s",
mask, nameFPReg(src), nameFPReg(dst));
m_formatter.prefix(PRE_SSE_66);
m_formatter.threeByteOp(OP3_INSERTPS_VpsUps, ESCAPE_INSERTPS, (RegisterID)dst, (RegisterID)src);
m_formatter.immediate8(uint8_t(mask));
}
void pinsrd_irr(unsigned lane, RegisterID src, XMMRegisterID dst)
{
MOZ_ASSERT(lane < 4);
@ -3639,6 +3653,14 @@ public:
m_formatter.twoByteOp(OP2_MINSD_VsdWsd, (RegisterID)dst, base, offset);
}
void minss_rr(XMMRegisterID src, XMMRegisterID dst)
{
spew("minss %s, %s",
nameFPReg(src), nameFPReg(dst));
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MINSS_VssWss, (RegisterID)dst, (RegisterID)src);
}
void maxsd_rr(XMMRegisterID src, XMMRegisterID dst)
{
spew("maxsd %s, %s",
@ -3655,6 +3677,14 @@ public:
m_formatter.twoByteOp(OP2_MAXSD_VsdWsd, (RegisterID)dst, base, offset);
}
void maxss_rr(XMMRegisterID src, XMMRegisterID dst)
{
spew("maxss %s, %s",
nameFPReg(src), nameFPReg(dst));
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MAXSS_VssWss, (RegisterID)dst, (RegisterID)src);
}
// Misc instructions:
void int3()

View File

@ -536,6 +536,58 @@ CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins)
return true;
}
bool
CodeGeneratorX86Shared::visitMinMaxF(LMinMaxF *ins)
{
FloatRegister first = ToFloatRegister(ins->first());
FloatRegister second = ToFloatRegister(ins->second());
#ifdef DEBUG
FloatRegister output = ToFloatRegister(ins->output());
JS_ASSERT(first == output);
#endif
Label done, nan, minMaxInst;
// Do a ucomiss to catch equality and NaNs, which both require special
// handling. If the operands are ordered and inequal, we branch straight to
// the min/max instruction. If we wanted, we could also branch for less-than
// or greater-than here instead of using min/max, however these conditions
// will sometimes be hard on the branch predictor.
masm.ucomiss(first, second);
masm.j(Assembler::NotEqual, &minMaxInst);
if (!ins->mir()->range() || ins->mir()->range()->canBeNaN())
masm.j(Assembler::Parity, &nan);
// Ordered and equal. The operands are bit-identical unless they are zero
// and negative zero. These instructions merge the sign bits in that
// case, and are no-ops otherwise.
if (ins->mir()->isMax())
masm.andps(second, first);
else
masm.orps(second, first);
masm.jump(&done);
// x86's min/max are not symmetric; if either operand is a NaN, they return
// the read-only operand. We need to return a NaN if either operand is a
// NaN, so we explicitly check for a NaN in the read-write operand.
if (!ins->mir()->range() || ins->mir()->range()->canBeNaN()) {
masm.bind(&nan);
masm.ucomiss(first, first);
masm.j(Assembler::Parity, &done);
}
// When the values are inequal, or second is NaN, x86's min and max will
// return the value we need.
masm.bind(&minMaxInst);
if (ins->mir()->isMax())
masm.maxss(second, first);
else
masm.minss(second, first);
masm.bind(&done);
return true;
}
bool
CodeGeneratorX86Shared::visitAbsD(LAbsD *ins)
{
@ -2232,6 +2284,63 @@ CodeGeneratorX86Shared::visitSimdExtractElementF(LSimdExtractElementF *ins)
return true;
}
bool
CodeGeneratorX86Shared::visitSimdInsertElementI(LSimdInsertElementI *ins)
{
FloatRegister vector = ToFloatRegister(ins->vector());
Register value = ToRegister(ins->value());
FloatRegister output = ToFloatRegister(ins->output());
MOZ_ASSERT(vector == output); // defineReuseInput(0)
unsigned component = unsigned(ins->lane());
// Note that, contrarily to float32x4, we cannot use movd if the inserted
// value goes into the first component, as movd clears out the higher lanes
// of the output.
if (AssemblerX86Shared::HasSSE41()) {
masm.pinsrd(component, value, output);
return true;
}
masm.reserveStack(Simd128DataSize);
masm.storeAlignedInt32x4(vector, Address(StackPointer, 0));
masm.store32(value, Address(StackPointer, component * sizeof(int32_t)));
masm.loadAlignedInt32x4(Address(StackPointer, 0), output);
masm.freeStack(Simd128DataSize);
return true;
}
bool
CodeGeneratorX86Shared::visitSimdInsertElementF(LSimdInsertElementF *ins)
{
FloatRegister vector = ToFloatRegister(ins->vector());
FloatRegister value = ToFloatRegister(ins->value());
FloatRegister output = ToFloatRegister(ins->output());
MOZ_ASSERT(vector == output); // defineReuseInput(0)
if (ins->lane() == SimdLane::LaneX) {
// As both operands are registers, movss doesn't modify the upper bits
// of the destination operand.
if (value != output)
masm.movss(value, output);
return true;
}
if (AssemblerX86Shared::HasSSE41()) {
// The input value is in the low float32 of the 'value' FloatRegister.
masm.insertps(value, output, masm.insertpsMask(SimdLane::LaneX, ins->lane()));
return true;
}
unsigned component = unsigned(ins->lane());
masm.reserveStack(Simd128DataSize);
masm.storeAlignedFloat32x4(vector, Address(StackPointer, 0));
masm.storeFloat32(value, Address(StackPointer, component * sizeof(int32_t)));
masm.loadAlignedFloat32x4(Address(StackPointer, 0), output);
masm.freeStack(Simd128DataSize);
return true;
}
bool
CodeGeneratorX86Shared::visitSimdSignMaskX4(LSimdSignMaskX4 *ins)
{

View File

@ -151,6 +151,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
virtual bool visitDouble(LDouble *ins);
virtual bool visitFloat32(LFloat32 *ins);
virtual bool visitMinMaxD(LMinMaxD *ins);
virtual bool visitMinMaxF(LMinMaxF *ins);
virtual bool visitAbsD(LAbsD *ins);
virtual bool visitAbsF(LAbsF *ins);
virtual bool visitClzI(LClzI *ins);
@ -213,6 +214,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
bool visitFloat32x4(LFloat32x4 *ins);
bool visitSimdExtractElementI(LSimdExtractElementI *lir);
bool visitSimdExtractElementF(LSimdExtractElementF *lir);
bool visitSimdInsertElementI(LSimdInsertElementI *lir);
bool visitSimdInsertElementF(LSimdInsertElementF *lir);
bool visitSimdSignMaskX4(LSimdSignMaskX4 *ins);
bool visitSimdBinaryCompIx4(LSimdBinaryCompIx4 *lir);
bool visitSimdBinaryCompFx4(LSimdBinaryCompFx4 *lir);

View File

@ -23,7 +23,8 @@ const js::Class OuterWrapperClass =
nullptr, /* outerObject */
js::proxy_innerObject,
nullptr, /* iteratorObject */
false /* isWrappedNative */
false, /* isWrappedNative */
nullptr /* objectMoved */
));
static JSObject *

View File

@ -252,14 +252,14 @@ namespace js {
* allow for potention JSClass extensions.
*/
#define PROXY_MAKE_EXT(outerObject, innerObject, iteratorObject, \
isWrappedNative) \
isWrappedNative, objectMoved) \
{ \
outerObject, \
innerObject, \
iteratorObject, \
isWrappedNative, \
js::proxy_WeakmapKeyDelegate, \
js::proxy_ObjectMoved \
objectMoved \
}
#define PROXY_CLASS_WITH_EXT(name, extraSlots, flags, ext) \
@ -313,7 +313,8 @@ namespace js {
nullptr, /* outerObject */ \
nullptr, /* innerObject */ \
nullptr, /* iteratorObject */ \
false /* isWrappedNative */ \
false, /* isWrappedNative */ \
js::proxy_ObjectMoved \
))
/*

View File

@ -455,6 +455,13 @@ ArenaHeader::checkSynchronizedWithFreeList() const
}
#endif
void
ArenaHeader::unmarkAll()
{
uintptr_t *word = chunk()->bitmap.arenaBits(this);
memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
}
/* static */ void
Arena::staticAsserts()
{
@ -2492,6 +2499,9 @@ GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
ArenaHeader *aheader = relocatedList;
relocatedList = relocatedList->next;
// Clear the mark bits
aheader->unmarkAll();
// Mark arena as empty
AllocKind thingKind = aheader->getAllocKind();
size_t thingSize = aheader->getThingSize();
@ -5642,12 +5652,12 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason)
{
/* GC shouldn't be running in parallel execution mode */
MOZ_ASSERT(!InParallelSection());
MOZ_ALWAYS_TRUE(!InParallelSection());
JS_AbortIfWrongThread(rt);
/* If we attempt to invoke the GC while we are running in the GC, assert. */
MOZ_ASSERT(!rt->isHeapBusy());
MOZ_ALWAYS_TRUE(!rt->isHeapBusy());
/* The engine never locks across anything that could GC. */
MOZ_ASSERT(!rt->currentThreadHasExclusiveAccess());
@ -6359,8 +6369,33 @@ JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
if (rt->gc.isInsideUnsafeRegion())
MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
}
JS::AutoAssertNoAlloc::AutoAssertNoAlloc(JSRuntime *rt)
: gc(nullptr)
{
disallowAlloc(rt);
}
void JS::AutoAssertNoAlloc::disallowAlloc(JSRuntime *rt)
{
JS_ASSERT(!gc);
gc = &rt->gc;
gc->disallowAlloc();
}
JS::AutoAssertNoAlloc::~AutoAssertNoAlloc()
{
if (gc)
gc->allowAlloc();
}
#endif
JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject *obj)
: AutoSuppressGCAnalysis()
{
MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapMajorCollecting());
}
#ifdef JSGC_HASH_TABLE_CHECKS
void
js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt)

View File

@ -695,10 +695,8 @@ class ArenaLists
/* The background finalization must have stopped at this point. */
JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE ||
backgroundFinalizeState[i] == BFS_JUST_FINISHED);
for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next) {
uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader);
memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
}
for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next)
aheader->unmarkAll();
}
}

View File

@ -325,37 +325,9 @@ class ZoneCellIterUnderGC : public ZoneCellIterImpl
}
};
/* In debug builds, assert that no allocation occurs. */
class AutoAssertNoAlloc
{
#ifdef JS_DEBUG
GCRuntime *gc;
public:
AutoAssertNoAlloc() : gc(nullptr) {}
explicit AutoAssertNoAlloc(JSRuntime *rt) : gc(nullptr) {
disallowAlloc(rt);
}
void disallowAlloc(JSRuntime *rt) {
JS_ASSERT(!gc);
gc = &rt->gc;
gc->disallowAlloc();
}
~AutoAssertNoAlloc() {
if (gc)
gc->allowAlloc();
}
#else
public:
AutoAssertNoAlloc() {}
explicit AutoAssertNoAlloc(JSRuntime *) {}
void disallowAlloc(JSRuntime *rt) {}
#endif
};
class ZoneCellIter : public ZoneCellIterImpl
{
AutoAssertNoAlloc noAlloc;
JS::AutoAssertNoAlloc noAlloc;
ArenaLists *lists;
AllocKind kind;

View File

@ -663,6 +663,12 @@ JSObject::finish(js::FreeOp *fop)
fop->free_(elements);
}
}
// It's possible that unreachable shapes may be marked whose listp points
// into this object. In case this happens, null out the shape's pointer here
// so that a moving GC will not try to access the dead object.
if (shape_->listp == &shape_)
shape_->listp = nullptr;
}
/* static */ inline bool

View File

@ -71,8 +71,12 @@ WeakMapBase::unmarkCompartment(JSCompartment *c)
void
WeakMapBase::markAll(JSCompartment *c, JSTracer *tracer)
{
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
m->markIteratively(tracer);
JS_ASSERT(tracer->eagerlyTraceWeakMaps() != DoNotTraceWeakMaps);
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
m->trace(tracer);
if (m->memberOf)
gc::MarkObject(tracer, &m->memberOf, "memberOf");
}
}
bool

View File

@ -91,7 +91,7 @@ class WeakMapBase {
virtual void finish() = 0;
// Object that this weak map is part of, if any.
JSObject *memberOf;
HeapPtrObject memberOf;
// Compartment that this weak map is part of.
JSCompartment *compartment;

View File

@ -772,10 +772,13 @@ RegExpCompartment::sweep(JSRuntime *rt)
bool keep = shared->marked() && !IsStringAboutToBeFinalized(shared->source.unsafeGet());
for (size_t i = 0; i < ArrayLength(shared->compilationArray); i++) {
RegExpShared::RegExpCompilation &compilation = shared->compilationArray[i];
if (keep && compilation.jitCode)
keep = !IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet());
if (compilation.jitCode &&
IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet()))
{
keep = false;
}
}
if (keep) {
if (keep || rt->isHeapCompacting()) {
shared->clearMarked();
} else {
js_delete(shared);

View File

@ -199,7 +199,7 @@ class RegExpShared
void trace(JSTracer *trc);
bool marked() const { return marked_; }
void clearMarked() { JS_ASSERT(marked_); marked_ = false; }
void clearMarked() { marked_ = false; }
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
};

View File

@ -897,9 +897,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FNINFO("ClampToUint8",
JSNativeThreadSafeWrapper<js::ClampToUint8>,
&js::ClampToUint8JitInfo, 1, 0),
JS_FNINFO("Memcpy",
JSNativeThreadSafeWrapper<js::Memcpy>,
&js::MemcpyJitInfo, 5, 0),
JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0),
JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0),

View File

@ -49,6 +49,11 @@ public:
ClearWrapper();
}
void ObjectMoved(JSObject *obj, const JSObject *old)
{
UpdateWrapper(obj, old);
}
private:
virtual ~SandboxPrivate() { }

View File

@ -310,7 +310,7 @@ sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
}
static void
sandbox_finalize(JSFreeOp *fop, JSObject *obj)
sandbox_finalize(js::FreeOp *fop, JSObject *obj)
{
nsIScriptObjectPrincipal *sop =
static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
@ -324,6 +324,15 @@ sandbox_finalize(JSFreeOp *fop, JSObject *obj)
DestroyProtoAndIfaceCache(obj);
}
static void
sandbox_moved(JSObject *obj, const JSObject *old)
{
nsIScriptObjectPrincipal *sop =
static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
MOZ_ASSERT(sop);
static_cast<SandboxPrivate *>(sop)->ObjectMoved(obj, old);
}
static bool
sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
{
@ -443,22 +452,42 @@ sandbox_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleV
#define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
static const JSClass SandboxClass = {
static const js::Class SandboxClass = {
"Sandbox",
XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
JS_NULL_CLASS_SPEC,
{
nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
sandbox_moved /* objectMovedOp */
},
JS_NULL_OBJECT_OPS
};
// Note to whomever comes here to remove addProperty hooks: billm has promised
// to do the work for this class.
static const JSClass SandboxWriteToProtoClass = {
static const js::Class SandboxWriteToProtoClass = {
"Sandbox",
XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
sandbox_addProperty, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
JS_NULL_CLASS_SPEC,
{
nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
sandbox_moved /* objectMovedOp */
},
JS_NULL_OBJECT_OPS
};
static const JSFunctionSpec SandboxFunctions[] = {
@ -471,7 +500,7 @@ static const JSFunctionSpec SandboxFunctions[] = {
bool
xpc::IsSandbox(JSObject *obj)
{
const JSClass *clasp = GetObjectJSClass(obj);
const Class *clasp = GetObjectClass(obj);
return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass;
}
@ -861,11 +890,11 @@ xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prin
compartmentOptions.setAddonId(addonId);
const JSClass *clasp = options.writeToGlobalPrototype
? &SandboxWriteToProtoClass
: &SandboxClass;
const Class *clasp = options.writeToGlobalPrototype
? &SandboxWriteToProtoClass
: &SandboxClass;
RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, clasp,
RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
principal, compartmentOptions));
if (!sandbox)
return NS_ERROR_FAILURE;

View File

@ -991,6 +991,20 @@ XPCWrappedNative::FlatJSObjectFinalized()
Release();
}
void
XPCWrappedNative::FlatJSObjectMoved(JSObject *obj, const JSObject *old)
{
JS::AutoAssertGCCallback inCallback(obj);
MOZ_ASSERT(mFlatJSObject == old);
nsWrapperCache *cache = nullptr;
CallQueryInterface(mIdentity, &cache);
if (cache)
cache->UpdateWrapper(obj, old);
mFlatJSObject = obj;
}
void
XPCWrappedNative::SystemIsBeingShutDown()
{

View File

@ -572,6 +572,17 @@ WrappedNativeFinalize(js::FreeOp *fop, JSObject *obj, WNHelperType helperType)
wrapper->FlatJSObjectFinalized();
}
static void
WrappedNativeObjectMoved(JSObject *obj, const JSObject *old)
{
nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
if (!p)
return;
XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
wrapper->FlatJSObjectMoved(obj, old);
}
static void
XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj)
{
@ -658,7 +669,9 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
nullptr, // outerObject
nullptr, // innerObject
nullptr, // iteratorObject
true, // isWrappedNative
true, // isWrappedNative
nullptr, // weakmapKeyDelegateOp
WrappedNativeObjectMoved
},
// ObjectOps
@ -1165,6 +1178,7 @@ XPCNativeScriptableShared::PopulateJSClass()
mJSClass.base.trace = XPCWrappedNative::Trace;
mJSClass.base.ext.isWrappedNative = true;
mJSClass.base.ext.objectMovedOp = WrappedNativeObjectMoved;
}
/***************************************************************************/

View File

@ -2141,6 +2141,7 @@ public:
nsresult RescueOrphans();
void FlatJSObjectFinalized();
void FlatJSObjectMoved(JSObject *obj, const JSObject *old);
void SystemIsBeingShutDown();

View File

@ -1402,7 +1402,7 @@ nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
}
nscoord start = aStart;
nscoord frameWidth = aFrame->GetSize().width;
nscoord frameISize = aFrame->ISize(aLineWM);
if (!IsBidiLeaf(aFrame))
{
@ -1436,7 +1436,7 @@ nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
iCoord,
aContinuationStates,
frameWM,
frameWidth);
frameISize);
index++;
frame = reverseOrder ?
childList[childList.Length() - index - 1] :
@ -1448,7 +1448,7 @@ nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
}
aStart += iCoord;
} else {
aStart += frameWidth;
aStart += frameISize;
}
LogicalRect logicalRect(aLineWM, aFrame->GetRect(), aLineWidth);

View File

@ -33,23 +33,40 @@ function execTests() {
}
var eatSpace;
var deleteImmediately;
function getPrefs() {
function getPrefs(branch) {
const prefSvcContractID = "@mozilla.org/preferences-service;1";
const prefSvcIID = Components.interfaces.nsIPrefService;
return Components.classes[prefSvcContractID].getService(prefSvcIID)
.getBranch("layout.word_select.");
.getBranch(branch);
}
function setPref(branch, pref, newValue) {
getPrefs(branch).setBoolPref(pref, newValue);
return newValue;
}
function restorePref(branch, pref, newValue) {
try {
getPrefs(branch).clearUserPref(pref);
} catch(ex) {}
}
function setEatSpace(newValue) {
getPrefs().setBoolPref("eat_space_to_next_word", newValue);
eatSpace = newValue;
eatSpace = setPref("layout.word_select.", "eat_space_to_next_word", newValue);
}
function restoreEatSpace() {
try {
getPrefs().clearUserPref("eat_space_to_next_word");
} catch(ex) {}
restorePref("layout.word_select.", "eat_space_to_next_word");
}
function setDeleteImmediately(newValue) {
deleteImmediately = setPref("bidi.edit.", "delete_immediately", newValue);
}
function restoreDeleteImmediately() {
restorePref("bidi.edit.", "delete_immediately");
}
function doCommand(cmd) {
@ -86,7 +103,7 @@ function execTests() {
function testDelete(node, offset, text, richtext) {
doCommand("cmd_deleteCharForward");
var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset;
var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
if(typeof node == 'function'){
node = node();
}
@ -99,7 +116,7 @@ function execTests() {
function testBackspace(node, offset, text) {
doCommand("cmd_deleteCharBackward");
var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset;
var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
is(sel.anchorNode, node, msg);
is(sel.anchorOffset, offset, msg);
@ -195,10 +212,38 @@ function execTests() {
// Tests for Bug 419406
var s = "helloשלום";
setDeleteImmediately(true);
setupTest(s, 4);
testRight(editor.firstChild, 5);
testDelete(editor.firstChild, 5, "helloלום");
setDeleteImmediately(false);
setupTest(s, 4);
testRight(editor.firstChild, 5);
testDelete(editor.firstChild, 5, "helloשלום");
// Tests for bug 1034337
s = "اهلاhello";
setDeleteImmediately(true);
setupTest(s, 4);
// first delete an ltr character to make sure that the caret is ltr
testDelete(editor.firstChild, 4, "اهلاello");
testBackspace(editor.firstChild, 3, "اهلello");
setDeleteImmediately(false);
setupTest(s, 4);
// first delete an ltr character to make sure that the caret is ltr
testDelete(editor.firstChild, 4, "اهلاello");
testBackspace(editor.firstChild, 4, "اهلاello");
restoreDeleteImmediately();
// Tests for Bug 462188
setupTest("You should not see this text.", 29);
testDeletePrevWord(editor.firstChild, 24, "You should not see this ");

View File

@ -294,16 +294,18 @@ void TestBasicElements()
void TestStringEscaping()
{
// This test uses hexadecimal character escapes because UTF8 literals cause
// problems for some compilers (see bug 1069726).
const char* expected = "\
{\n\
\"ascii\": \"~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\
\"مرحبا هناك\": true,\n\
\"բարեւ չկա\": -123,\n\
\"你好\": 1.234,\n\
\"γεια εκεί\": \"سلام\",\n\
\"halló þarna\": \"0x1234\",\n\
\"こんにちは\": {\n\
\"привет\": [\n\
\"ascii\": \"\x7F~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\
\"\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83\": true,\n\
\"\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1\": -123,\n\
\"\xE4\xBD\xA0\xE5\xA5\xBD\": 1.234,\n\
\"\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF\": \"\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\",\n\
\"hall\xC3\xB3 \xC3\xBE" "arna\": \"0x1234\",\n\
\"\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\": {\n\
\"\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82\": [\n\
]\n\
}\n\
}\n\
@ -323,14 +325,14 @@ void TestStringEscaping()
w.StringProperty("ascii", buf);
// Test lots of unicode stuff. Note that this file is encoded as UTF-8.
w.BoolProperty("مرحبا هناك", true);
w.IntProperty("բարեւ չկա", -123);
w.DoubleProperty("你好", 1.234);
w.StringProperty("γεια εκεί", "سلام");
w.PointerProperty("halló þarna", (void*)0x1234);
w.StartObjectProperty("こんにちは");
w.BoolProperty("\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83", true);
w.IntProperty("\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1", -123);
w.DoubleProperty("\xE4\xBD\xA0\xE5\xA5\xBD", 1.234);
w.StringProperty("\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF", "\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85");
w.PointerProperty("hall\xC3\xB3 \xC3\xBE" "arna", (void*)0x1234);
w.StartObjectProperty("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF");
{
w.StartArrayProperty("привет");
w.StartArrayProperty("\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82");
w.EndArray();
}
w.EndObject();

View File

@ -319,7 +319,7 @@ nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resu
}
nsresult
nsJARChannel::LookupFile()
nsJARChannel::LookupFile(bool aAllowAsync)
{
LOG(("nsJARChannel::LookupFile [this=%x %s]\n", this, mSpec.get()));
@ -387,6 +387,11 @@ nsJARChannel::LookupFile()
}
}
if (!aAllowAsync) {
mJarFile = nullptr;
return NS_OK;
}
mOpeningRemote = true;
#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
@ -798,7 +803,7 @@ nsJARChannel::Open(nsIInputStream **stream)
mJarFile = nullptr;
mIsUnsafe = true;
nsresult rv = LookupFile();
nsresult rv = LookupFile(false);
if (NS_FAILED(rv))
return rv;
@ -835,7 +840,7 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
// Initialize mProgressSink
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
nsresult rv = LookupFile();
nsresult rv = LookupFile(true);
if (NS_FAILED(rv))
return rv;
@ -910,7 +915,7 @@ nsJARChannel::GetJarFile(nsIFile **aFile)
NS_IMETHODIMP
nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry)
{
nsresult rv = LookupFile();
nsresult rv = LookupFile(false);
if (NS_FAILED(rv))
return rv;

Some files were not shown because too many files have changed in this diff Show More